{ "info": { "author": "Andy Port", "author_email": "AndyAPort@gmail.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 4 - Beta", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python :: 2", "Programming Language :: Python :: 3", "Topic :: Multimedia :: Graphics :: Editors :: Vector-Based", "Topic :: Scientific/Engineering", "Topic :: Scientific/Engineering :: Image Recognition", "Topic :: Scientific/Engineering :: Information Analysis", "Topic :: Scientific/Engineering :: Mathematics", "Topic :: Scientific/Engineering :: Visualization", "Topic :: Software Development :: Libraries :: Python Modules" ], "description": "svgpathtools\n============\n\nsvgpathtools is a collection of tools for manipulating and analyzing SVG\nPath objects and B\u00e9zier curves.\n\nFeatures\n--------\n\nsvgpathtools contains functions designed to **easily read, write and\ndisplay SVG files** as well as *a large selection of\ngeometrically-oriented tools* to **transform and analyze path\nelements**.\n\nAdditionally, the submodule *bezier.py* contains tools for for working\nwith general **nth order Bezier curves stored as n-tuples**.\n\nSome included tools:\n\n- **read**, **write**, and **display** SVG files containing Path (and\n other) SVG elements\n- convert B\u00e9zier path segments to **numpy.poly1d** (polynomial) objects\n- convert polynomials (in standard form) to their B\u00e9zier form\n- compute **tangent vectors** and (right-hand rule) **normal vectors**\n- compute **curvature**\n- break discontinuous paths into their **continuous subpaths**.\n- efficiently compute **intersections** between paths and/or segments\n- find a **bounding box** for a path or segment\n- **reverse** segment/path orientation\n- **crop** and **split** paths and segments\n- **smooth** paths (i.e. smooth away kinks to make paths\n differentiable)\n- **transition maps** from path domain to segment domain and back (T2t\n and t2T)\n- compute **area** enclosed by a closed path\n- compute **arc length**\n- compute **inverse arc length**\n- convert RGB color tuples to hexadecimal color strings and back\n\nPrerequisites\n-------------\n\n- **numpy**\n- **svgwrite**\n\nSetup\n-----\n\nIf not already installed, you can **install the prerequisites** using\npip.\n\n.. code:: bash\n\n $ pip install numpy\n\n.. code:: bash\n\n $ pip install svgwrite\n\nThen **install svgpathtools**:\n\n.. code:: bash\n\n $ pip install svgpathtools\n\nAlternative Setup\n~~~~~~~~~~~~~~~~~\n\nYou can download the source from Github and install by using the command\n(from inside the folder containing setup.py):\n\n.. code:: bash\n\n $ python setup.py install\n\nCredit where credit's due\n-------------------------\n\nMuch of the core of this module was taken from `the svg.path (v2.0)\nmodule `__. Interested svg.path\nusers should see the compatibility notes at bottom of this readme.\n\nBasic Usage\n-----------\n\nClasses\n~~~~~~~\n\nThe svgpathtools module is primarily structured around four path segment\nclasses: ``Line``, ``QuadraticBezier``, ``CubicBezier``, and ``Arc``.\nThere is also a fifth class, ``Path``, whose objects are sequences of\n(connected or disconnected\\ `1 <#f1>`__\\ ) path segment objects.\n\n- ``Line(start, end)``\n\n- ``Arc(start, radius, rotation, large_arc, sweep, end)`` Note: See\n docstring for a detailed explanation of these parameters\n\n- ``QuadraticBezier(start, control, end)``\n\n- ``CubicBezier(start, control1, control2, end)``\n\n- ``Path(*segments)``\n\nSee the relevant docstrings in *path.py* or the `official SVG\nspecifications `__ for more\ninformation on what each parameter means.\n\n1 Warning: Some of the functionality in this library has not been tested\non discontinuous Path objects. A simple workaround is provided, however,\nby the ``Path.continuous_subpaths()`` method. `\u21a9 <#a1>`__\n\n.. code:: ipython2\n\n from __future__ import division, print_function\n\n.. code:: ipython2\n\n # Coordinates are given as points in the complex plane\n from svgpathtools import Path, Line, QuadraticBezier, CubicBezier, Arc\n seg1 = CubicBezier(300+100j, 100+100j, 200+200j, 200+300j) # A cubic beginning at (300, 100) and ending at (200, 300)\n seg2 = Line(200+300j, 250+350j) # A line beginning at (200, 300) and ending at (250, 350)\n path = Path(seg1, seg2) # A path traversing the cubic and then the line\n\n # We could alternatively created this Path object using a d-string\n from svgpathtools import parse_path\n path_alt = parse_path('M 300 100 C 100 100 200 200 200 300 L 250 350')\n\n # Let's check that these two methods are equivalent\n print(path)\n print(path_alt)\n print(path == path_alt)\n\n # On a related note, the Path.d() method returns a Path object's d-string\n print(path.d())\n print(parse_path(path.d()) == path)\n\n\n.. parsed-literal::\n\n Path(CubicBezier(start=(300+100j), control1=(100+100j), control2=(200+200j), end=(200+300j)),\n Line(start=(200+300j), end=(250+350j)))\n Path(CubicBezier(start=(300+100j), control1=(100+100j), control2=(200+200j), end=(200+300j)),\n Line(start=(200+300j), end=(250+350j)))\n True\n M 300.0,100.0 C 100.0,100.0 200.0,200.0 200.0,300.0 L 250.0,350.0\n True\n\n\nThe ``Path`` class is a mutable sequence, so it behaves much like a\nlist. So segments can **append**\\ ed, **insert**\\ ed, set by index,\n**del**\\ eted, **enumerate**\\ d, **slice**\\ d out, etc.\n\n.. code:: ipython2\n\n # Let's append another to the end of it\n path.append(CubicBezier(250+350j, 275+350j, 250+225j, 200+100j))\n print(path)\n\n # Let's replace the first segment with a Line object\n path[0] = Line(200+100j, 200+300j)\n print(path)\n\n # You may have noticed that this path is connected and now is also closed (i.e. path.start == path.end)\n print(\"path is continuous? \", path.iscontinuous())\n print(\"path is closed? \", path.isclosed())\n\n # The curve the path follows is not, however, smooth (differentiable)\n from svgpathtools import kinks, smoothed_path\n print(\"path contains non-differentiable points? \", len(kinks(path)) > 0)\n\n # If we want, we can smooth these out (Experimental and only for line/cubic paths)\n # Note: smoothing will always works (except on 180 degree turns), but you may want \n # to play with the maxjointsize and tightness parameters to get pleasing results\n # Note also: smoothing will increase the number of segments in a path\n spath = smoothed_path(path)\n print(\"spath contains non-differentiable points? \", len(kinks(spath)) > 0)\n print(spath)\n\n # Let's take a quick look at the path and its smoothed relative\n # The following commands will open two browser windows to display path and spaths\n from svgpathtools import disvg\n from time import sleep\n disvg(path) \n sleep(1) # needed when not giving the SVGs unique names (or not using timestamp)\n disvg(spath)\n print(\"Notice that path contains {} segments and spath contains {} segments.\"\n \"\".format(len(path), len(spath)))\n\n\n.. parsed-literal::\n\n Path(CubicBezier(start=(300+100j), control1=(100+100j), control2=(200+200j), end=(200+300j)),\n Line(start=(200+300j), end=(250+350j)),\n CubicBezier(start=(250+350j), control1=(275+350j), control2=(250+225j), end=(200+100j)))\n Path(Line(start=(200+100j), end=(200+300j)),\n Line(start=(200+300j), end=(250+350j)),\n CubicBezier(start=(250+350j), control1=(275+350j), control2=(250+225j), end=(200+100j)))\n path is continuous? True\n path is closed? True\n path contains non-differentiable points? True\n spath contains non-differentiable points? False\n Path(Line(start=(200+101.5j), end=(200+298.5j)),\n CubicBezier(start=(200+298.5j), control1=(200+298.505j), control2=(201.057124638+301.057124638j), end=(201.060660172+301.060660172j)),\n Line(start=(201.060660172+301.060660172j), end=(248.939339828+348.939339828j)),\n CubicBezier(start=(248.939339828+348.939339828j), control1=(249.649982143+349.649982143j), control2=(248.995+350j), end=(250+350j)),\n CubicBezier(start=(250+350j), control1=(275+350j), control2=(250+225j), end=(200+100j)),\n CubicBezier(start=(200+100j), control1=(199.62675237+99.0668809257j), control2=(200+100.495j), end=(200+101.5j)))\n Notice that path contains 3 segments and spath contains 6 segments.\n\n\nReading SVGSs\n~~~~~~~~~~~~~\n\n| The **svg2paths()** function converts an svgfile to a list of Path\n objects and a separate list of dictionaries containing the attributes\n of each said path.\n| Note: Line, Polyline, Polygon, and Path SVG elements can all be\n converted to Path objects using this function.\n\n.. code:: ipython2\n\n # Read SVG into a list of path objects and list of dictionaries of attributes \n from svgpathtools import svg2paths, wsvg\n paths, attributes = svg2paths('test.svg')\n\n # Update: You can now also extract the svg-attributes by setting\n # return_svg_attributes=True, or with the convenience function svg2paths2\n from svgpathtools import svg2paths2\n paths, attributes, svg_attributes = svg2paths2('test.svg')\n\n # Let's print out the first path object and the color it was in the SVG\n # We'll see it is composed of two CubicBezier objects and, in the SVG file it \n # came from, it was red\n redpath = paths[0]\n redpath_attribs = attributes[0]\n print(redpath)\n print(redpath_attribs['stroke'])\n\n\n.. parsed-literal::\n\n Path(CubicBezier(start=(10.5+80j), control1=(40+10j), control2=(65+10j), end=(95+80j)),\n CubicBezier(start=(95+80j), control1=(125+150j), control2=(150+150j), end=(180+80j)))\n red\n\n\nWriting SVGSs (and some geometric functions and methods)\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThe **wsvg()** function creates an SVG file from a list of path. This\nfunction can do many things (see docstring in *paths2svg.py* for more\ninformation) and is meant to be quick and easy to use. Note: Use the\nconvenience function **disvg()** (or set 'openinbrowser=True') to\nautomatically attempt to open the created svg file in your default SVG\nviewer.\n\n.. code:: ipython2\n\n # Let's make a new SVG that's identical to the first\n wsvg(paths, attributes=attributes, svg_attributes=svg_attributes, filename='output1.svg')\n\n.. figure:: https://cdn.rawgit.com/mathandy/svgpathtools/master/output1.svg\n :alt: output1.svg\n\n output1.svg\n\nThere will be many more examples of writing and displaying path data\nbelow.\n\nThe .point() method and transitioning between path and path segment parameterizations\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nSVG Path elements and their segments have official parameterizations.\nThese parameterizations can be accessed using the ``Path.point()``,\n``Line.point()``, ``QuadraticBezier.point()``, ``CubicBezier.point()``,\nand ``Arc.point()`` methods. All these parameterizations are defined\nover the domain 0 <= t <= 1.\n\n| **Note:** In this document and in inline documentation and doctrings,\n I use a capital ``T`` when referring to the parameterization of a Path\n object and a lower case ``t`` when referring speaking about path\n segment objects (i.e. Line, QaudraticBezier, CubicBezier, and Arc\n objects).\n| Given a ``T`` value, the ``Path.T2t()`` method can be used to find the\n corresponding segment index, ``k``, and segment parameter, ``t``, such\n that ``path.point(T)=path[k].point(t)``.\n| There is also a ``Path.t2T()`` method to solve the inverse problem.\n\n.. code:: ipython2\n\n # Example:\n\n # Let's check that the first segment of redpath starts \n # at the same point as redpath\n firstseg = redpath[0] \n print(redpath.point(0) == firstseg.point(0) == redpath.start == firstseg.start)\n\n # Let's check that the last segment of redpath ends on the same point as redpath\n lastseg = redpath[-1] \n print(redpath.point(1) == lastseg.point(1) == redpath.end == lastseg.end)\n\n # This next boolean should return False as redpath is composed multiple segments\n print(redpath.point(0.5) == firstseg.point(0.5))\n\n # If we want to figure out which segment of redpoint the \n # point redpath.point(0.5) lands on, we can use the path.T2t() method\n k, t = redpath.T2t(0.5)\n print(redpath[k].point(t) == redpath.point(0.5))\n\n\n.. parsed-literal::\n\n True\n True\n False\n True\n\n\nBezier curves as NumPy polynomial objects\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n| Another great way to work with the parameterizations for ``Line``,\n ``QuadraticBezier``, and ``CubicBezier`` objects is to convert them to\n ``numpy.poly1d`` objects. This is done easily using the\n ``Line.poly()``, ``QuadraticBezier.poly()`` and ``CubicBezier.poly()``\n methods.\n| There's also a ``polynomial2bezier()`` function in the pathtools.py\n submodule to convert polynomials back to Bezier curves.\n\n**Note:** cubic Bezier curves are parameterized as\n\n.. math:: \\mathcal{B}(t) = P_0(1-t)^3 + 3P_1(1-t)^2t + 3P_2(1-t)t^2 + P_3t^3\n\nwhere :math:`P_0`, :math:`P_1`, :math:`P_2`, and :math:`P_3` are the\ncontrol points ``start``, ``control1``, ``control2``, and ``end``,\nrespectively, that svgpathtools uses to define a CubicBezier object. The\n``CubicBezier.poly()`` method expands this polynomial to its standard\nform\n\n.. math:: \\mathcal{B}(t) = c_0t^3 + c_1t^2 +c_2t+c3\n\n where\n\n.. math::\n\n \\begin{bmatrix}c_0\\\\c_1\\\\c_2\\\\c_3\\end{bmatrix} = \n \\begin{bmatrix}\n -1 & 3 & -3 & 1\\\\\n 3 & -6 & -3 & 0\\\\\n -3 & 3 & 0 & 0\\\\\n 1 & 0 & 0 & 0\\\\\n \\end{bmatrix}\n \\begin{bmatrix}P_0\\\\P_1\\\\P_2\\\\P_3\\end{bmatrix}\n\n``QuadraticBezier.poly()`` and ``Line.poly()`` are `defined\nsimilarly `__.\n\n.. code:: ipython2\n\n # Example:\n b = CubicBezier(300+100j, 100+100j, 200+200j, 200+300j)\n p = b.poly()\n\n # p(t) == b.point(t)\n print(p(0.235) == b.point(0.235))\n\n # What is p(t)? It's just the cubic b written in standard form. \n bpretty = \"{}*(1-t)^3 + 3*{}*(1-t)^2*t + 3*{}*(1-t)*t^2 + {}*t^3\".format(*b.bpoints())\n print(\"The CubicBezier, b.point(x) = \\n\\n\" + \n bpretty + \"\\n\\n\" + \n \"can be rewritten in standard form as \\n\\n\" +\n str(p).replace('x','t'))\n\n\n.. parsed-literal::\n\n True\n The CubicBezier, b.point(x) = \n\n (300+100j)*(1-t)^3 + 3*(100+100j)*(1-t)^2*t + 3*(200+200j)*(1-t)*t^2 + (200+300j)*t^3\n\n can be rewritten in standard form as \n\n 3 2\n (-400 + -100j) t + (900 + 300j) t - 600 t + (300 + 100j)\n\n\nThe ability to convert between Bezier objects to NumPy polynomial\nobjects is very useful. For starters, we can take turn a list of B\u00e9zier\nsegments into a NumPy array\n\nNumpy Array operations on B\u00e9zier path segments\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n`Example available\nhere `__\n\nTo further illustrate the power of being able to convert our Bezier\ncurve objects to numpy.poly1d objects and back, lets compute the unit\ntangent vector of the above CubicBezier object, b, at t=0.5 in four\ndifferent ways.\n\nTangent vectors (and more on NumPy polynomials)\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n.. code:: ipython2\n\n t = 0.5\n ### Method 1: the easy way\n u1 = b.unit_tangent(t)\n\n ### Method 2: another easy way \n # Note: This way will fail if it encounters a removable singularity.\n u2 = b.derivative(t)/abs(b.derivative(t))\n\n ### Method 2: a third easy way \n # Note: This way will also fail if it encounters a removable singularity.\n dp = p.deriv() \n u3 = dp(t)/abs(dp(t))\n\n ### Method 4: the removable-singularity-proof numpy.poly1d way \n # Note: This is roughly how Method 1 works\n from svgpathtools import real, imag, rational_limit\n dx, dy = real(dp), imag(dp) # dp == dx + 1j*dy \n p_mag2 = dx**2 + dy**2 # p_mag2(t) = |p(t)|**2\n # Note: abs(dp) isn't a polynomial, but abs(dp)**2 is, and,\n # the limit_{t->t0}[f(t) / abs(f(t))] == \n # sqrt(limit_{t->t0}[f(t)**2 / abs(f(t))**2])\n from cmath import sqrt\n u4 = sqrt(rational_limit(dp**2, p_mag2, t))\n\n print(\"unit tangent check:\", u1 == u2 == u3 == u4)\n\n # Let's do a visual check\n mag = b.length()/4 # so it's not hard to see the tangent line\n tangent_line = Line(b.point(t), b.point(t) + mag*u1)\n disvg([b, tangent_line], 'bg', nodes=[b.point(t)])\n\n\n.. parsed-literal::\n\n unit tangent check: True\n\n\nTranslations (shifts), reversing orientation, and normal vectors\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n.. code:: ipython2\n\n # Speaking of tangents, let's add a normal vector to the picture\n n = b.normal(t)\n normal_line = Line(b.point(t), b.point(t) + mag*n)\n disvg([b, tangent_line, normal_line], 'bgp', nodes=[b.point(t)])\n\n # and let's reverse the orientation of b! \n # the tangent and normal lines should be sent to their opposites\n br = b.reversed()\n\n # Let's also shift b_r over a bit to the right so we can view it next to b\n # The simplest way to do this is br = br.translated(3*mag), but let's use \n # the .bpoints() instead, which returns a Bezier's control points\n br.start, br.control1, br.control2, br.end = [3*mag + bpt for bpt in br.bpoints()] # \n\n tangent_line_r = Line(br.point(t), br.point(t) + mag*br.unit_tangent(t))\n normal_line_r = Line(br.point(t), br.point(t) + mag*br.normal(t))\n wsvg([b, tangent_line, normal_line, br, tangent_line_r, normal_line_r], \n 'bgpkgp', nodes=[b.point(t), br.point(t)], filename='vectorframes.svg', \n text=[\"b's tangent\", \"br's tangent\"], text_path=[tangent_line, tangent_line_r])\n\n.. figure:: https://cdn.rawgit.com/mathandy/svgpathtools/master/vectorframes.svg\n :alt: vectorframes.svg\n\n vectorframes.svg\n\nRotations and Translations\n~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n.. code:: ipython2\n\n # Let's take a Line and an Arc and make some pictures\n top_half = Arc(start=-1, radius=1+2j, rotation=0, large_arc=1, sweep=1, end=1)\n midline = Line(-1.5, 1.5)\n\n # First let's make our ellipse whole\n bottom_half = top_half.rotated(180)\n decorated_ellipse = Path(top_half, bottom_half)\n\n # Now let's add the decorations\n for k in range(12):\n decorated_ellipse.append(midline.rotated(30*k))\n\n # Let's move it over so we can see the original Line and Arc object next\n # to the final product\n decorated_ellipse = decorated_ellipse.translated(4+0j)\n wsvg([top_half, midline, decorated_ellipse], filename='decorated_ellipse.svg')\n\n.. figure:: https://cdn.rawgit.com/mathandy/svgpathtools/master/decorated_ellipse.svg\n :alt: decorated\\_ellipse.svg\n\n decorated\\_ellipse.svg\n\narc length and inverse arc length\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nHere we'll create an SVG that shows off the parametric and geometric\nmidpoints of the paths from ``test.svg``. We'll need to compute use the\n``Path.length()``, ``Line.length()``, ``QuadraticBezier.length()``,\n``CubicBezier.length()``, and ``Arc.length()`` methods, as well as the\nrelated inverse arc length methods ``.ilength()`` function to do this.\n\n.. code:: ipython2\n\n # First we'll load the path data from the file test.svg\n paths, attributes = svg2paths('test.svg')\n\n # Let's mark the parametric midpoint of each segment\n # I say \"parametric\" midpoint because Bezier curves aren't \n # parameterized by arclength \n # If they're also the geometric midpoint, let's mark them\n # purple and otherwise we'll mark the geometric midpoint green\n min_depth = 5\n error = 1e-4\n dots = []\n ncols = []\n nradii = []\n for path in paths:\n for seg in path:\n parametric_mid = seg.point(0.5)\n seg_length = seg.length()\n if seg.length(0.5)/seg.length() == 1/2:\n dots += [parametric_mid]\n ncols += ['purple']\n nradii += [5]\n else:\n t_mid = seg.ilength(seg_length/2)\n geo_mid = seg.point(t_mid)\n dots += [parametric_mid, geo_mid]\n ncols += ['red', 'green']\n nradii += [5] * 2\n\n # In 'output2.svg' the paths will retain their original attributes\n wsvg(paths, nodes=dots, node_colors=ncols, node_radii=nradii, \n attributes=attributes, filename='output2.svg')\n\n.. figure:: https://cdn.rawgit.com/mathandy/svgpathtools/master/output2.svg\n :alt: output2.svg\n\n output2.svg\n\nIntersections between Bezier curves\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n.. code:: ipython2\n\n # Let's find all intersections between redpath and the other \n redpath = paths[0]\n redpath_attribs = attributes[0]\n intersections = []\n for path in paths[1:]:\n for (T1, seg1, t1), (T2, seg2, t2) in redpath.intersect(path):\n intersections.append(redpath.point(T1))\n\n disvg(paths, filename='output_intersections.svg', attributes=attributes,\n nodes = intersections, node_radii = [5]*len(intersections))\n\n.. figure:: https://cdn.rawgit.com/mathandy/svgpathtools/master/output_intersections.svg\n :alt: output\\_intersections.svg\n\n output\\_intersections.svg\n\nAn Advanced Application: Offsetting Paths\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nHere we'll find the `offset\ncurve `__ for a few paths.\n\n.. code:: ipython2\n\n from svgpathtools import parse_path, Line, Path, wsvg\n def offset_curve(path, offset_distance, steps=1000):\n \"\"\"Takes in a Path object, `path`, and a distance,\n `offset_distance`, and outputs an piecewise-linear approximation \n of the 'parallel' offset curve.\"\"\"\n nls = []\n for seg in path:\n for k in range(steps):\n t = k / float(steps)\n offset_vector = offset_distance * seg.normal(t)\n nl = Line(seg.point(t), seg.point(t) + offset_vector)\n nls.append(nl)\n connect_the_dots = [Line(nls[k].end, nls[k+1].end) for k in range(len(nls)-1)]\n if path.isclosed():\n connect_the_dots.append(Line(nls[-1].end, nls[0].end))\n offset_path = Path(*connect_the_dots)\n return offset_path\n\n # Examples:\n path1 = parse_path(\"m 288,600 c -52,-28 -42,-61 0,-97 \")\n path2 = parse_path(\"M 151,395 C 407,485 726.17662,160 634,339\").translated(300)\n path3 = parse_path(\"m 117,695 c 237,-7 -103,-146 457,0\").translated(500+400j)\n paths = [path1, path2, path3]\n\n offset_distances = [10*k for k in range(1,51)]\n offset_paths = []\n for path in paths:\n for distances in offset_distances:\n offset_paths.append(offset_curve(path, distances))\n\n # Note: This will take a few moments\n wsvg(paths + offset_paths, 'g'*len(paths) + 'r'*len(offset_paths), filename='offset_curves.svg')\n\n.. figure:: https://cdn.rawgit.com/mathandy/svgpathtools/master/offset_curves.svg\n :alt: offset\\_curves.svg\n\n offset\\_curves.svg\n\nCompatibility Notes for users of svg.path (v2.0)\n------------------------------------------------\n\n- renamed Arc.arc attribute as Arc.large\\_arc\n\n- Path.d() : For behavior similar\\ `2 <#f2>`__\\ to svg.path (v2.0),\n set both useSandT and use\\_closed\\_attrib to be True.\n\n2 The behavior would be identical, but the string formatting used in\nthis method has been changed to use default format (instead of the\nGeneral format, {:G}), for inceased precision. `\u21a9 <#a2>`__\n\nLicence\n-------\n\nThis module is under a MIT License.\n\n\n\n", "description_content_type": "", "docs_url": null, "download_url": "http://github.com/mathandy/svgpathtools/tarball/1.3.3", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/mathandy/svgpathtools", "keywords": "svg,svg path,svg.path,bezier,parse svg path,display svg", "license": "MIT", "maintainer": "", "maintainer_email": "", "name": "svgpathtools", "package_url": "https://pypi.org/project/svgpathtools/", "platform": "OS Independent", "project_url": "https://pypi.org/project/svgpathtools/", "project_urls": { "Download": "http://github.com/mathandy/svgpathtools/tarball/1.3.3", "Homepage": "https://github.com/mathandy/svgpathtools" }, "release_url": "https://pypi.org/project/svgpathtools/1.3.3/", "requires_dist": [ "numpy", "svgwrite" ], "requires_python": "", "summary": "A collection of tools for manipulating and analyzing SVG Path objects and Bezier curves.", "version": "1.3.3" }, "last_serial": 4038313, "releases": { "1.0": [ { "comment_text": "", "digests": { "md5": "0f10b73d03994cfd1a6c0e1d959f534e", "sha256": "01d6e3b5b21241ea602abd2856c9939ad90e5e258e20b1008bab375a2939ae48" }, "downloads": -1, "filename": "svgpathtools-1.0.tar.gz", "has_sig": false, "md5_digest": "0f10b73d03994cfd1a6c0e1d959f534e", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 39802, "upload_time": "2016-07-06T05:45:19", "url": "https://files.pythonhosted.org/packages/4a/d8/227847ae031c6d593443df1aeb41f043abb98abe4d4409c22a9e709e0529/svgpathtools-1.0.tar.gz" } ], "1.0.1": [ { "comment_text": "", "digests": { "md5": "6b1ee164923afd2f6f482f0cf47f3020", "sha256": "28c1bb6b650e191e951304f4e5e77e569d7fd8fb94b34f5b76d682bf208916a9" }, "downloads": -1, "filename": "svgpathtools-1.0.1.tar.gz", "has_sig": false, "md5_digest": "6b1ee164923afd2f6f482f0cf47f3020", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 32593, "upload_time": "2016-07-06T06:03:45", "url": "https://files.pythonhosted.org/packages/13/42/bd4d86ea5bfc880d8ea913acfa48c9bf4682f15e346ecce7cf1cce0b81bb/svgpathtools-1.0.1.tar.gz" } ], "1.2.1": [ { "comment_text": "", "digests": { "md5": "54512ce3d734157554a833d232fe396d", "sha256": "e14b7ecede1bd21caaa2af4b0eaf80ea79b125ab09b73bc228dc6d75f9782615" }, "downloads": -1, "filename": "svgpathtools-1.2.1.tar.gz", "has_sig": false, "md5_digest": "54512ce3d734157554a833d232fe396d", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 33700, "upload_time": "2016-07-24T00:21:07", "url": "https://files.pythonhosted.org/packages/89/3a/5c3a08bfcc85f34b56700ade20a0c63c3313480f898dbebb1203db62d1f0/svgpathtools-1.2.1.tar.gz" } ], "1.2.2": [ { "comment_text": "", "digests": { "md5": "0600cbd628b2098d7e6cabf3ecbf1f82", "sha256": "f41ab9446bf8ffca8466c613ae59d610aeef3f29a818bf9db044b4bd3912c5f8" }, "downloads": -1, "filename": "svgpathtools-1.2.2.tar.gz", "has_sig": false, "md5_digest": "0600cbd628b2098d7e6cabf3ecbf1f82", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 34047, "upload_time": "2016-10-16T05:39:15", "url": "https://files.pythonhosted.org/packages/28/50/623d5e4730d87c89fe98bececed497e0c3418d7e43bc64ce7cd24af02c4c/svgpathtools-1.2.2.tar.gz" } ], "1.2.3": [ { "comment_text": "", "digests": { "md5": "21f38ba469807d19bc42bb0a7d87d122", "sha256": "e7252042766b16e1e7d9f29d3d9722bfcde310522a248e0f79e0477681a6eb6d" }, "downloads": -1, "filename": "svgpathtools-1.2.3.tar.gz", "has_sig": false, "md5_digest": "21f38ba469807d19bc42bb0a7d87d122", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 34047, "upload_time": "2016-10-16T07:42:52", "url": "https://files.pythonhosted.org/packages/af/30/c50ebafd0bb4d6061a228866a4ad2cfbcadf0d272c9213b33495cac90330/svgpathtools-1.2.3.tar.gz" } ], "1.2.4": [ { "comment_text": "", "digests": { "md5": "807698fce5cd20006a6fd34387b2ccb6", "sha256": "a9cd254c93e827d66a06afcf0fb0203a1f428e59aa0ebe2f667e9b409c8d73bf" }, "downloads": -1, "filename": "svgpathtools-1.2.4.tar.gz", "has_sig": false, "md5_digest": "807698fce5cd20006a6fd34387b2ccb6", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 34042, "upload_time": "2016-10-29T03:51:51", "url": "https://files.pythonhosted.org/packages/74/16/9986c0aa258bdf18d5aa9cd0c959611461a0ce28a0d88851cf3f8d2501e9/svgpathtools-1.2.4.tar.gz" } ], "1.2.5": [ { "comment_text": "", "digests": { "md5": "ac89e22a696336c8cc124bd8154f4617", "sha256": "0b1cc413ccfc7640d1a0f889b76f806dbf44599648050389dd55acd7c49b9dae" }, "downloads": -1, "filename": "svgpathtools-1.2.5-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "ac89e22a696336c8cc124bd8154f4617", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 56025, "upload_time": "2016-10-29T08:12:37", "url": "https://files.pythonhosted.org/packages/59/61/cf646da0c628a041e94a95f0a1e3cb9ba946b74a27cc900a333b68fc92f3/svgpathtools-1.2.5-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "bc916a3da4c915d8e2c4057582fd48e1", "sha256": "aac150ad362fc5437e5d66a43527483605a7b0f8fd5483e35250512e12551028" }, "downloads": -1, "filename": "svgpathtools-1.2.5.tar.gz", "has_sig": false, "md5_digest": "bc916a3da4c915d8e2c4057582fd48e1", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 2097569, "upload_time": "2016-10-29T08:13:01", "url": "https://files.pythonhosted.org/packages/17/57/e736c4e961abb94d3b034b984ab68196a9f28303695101b56ecb85d55e92/svgpathtools-1.2.5.tar.gz" } ], "1.2.6": [ { "comment_text": "", "digests": { "md5": "d7c3b775e71a9fc4888c8bff3f32ee87", "sha256": "37e4ab980556f7144cd162cc876501c2957ca5d57dcf468019d7fcfe0cf9d921" }, "downloads": -1, "filename": "svgpathtools-1.2.6-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "d7c3b775e71a9fc4888c8bff3f32ee87", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 56330, "upload_time": "2017-02-21T07:24:19", "url": "https://files.pythonhosted.org/packages/78/f0/b54c41d4d659980d040424df722084b1c43248afeb857be909bec19cfc4b/svgpathtools-1.2.6-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "dd67c27cc95f8ee2686086c4d7fc6a4e", "sha256": "1c4ca59a4c562d734a369fc8fad9d2bce0cbb47371e4af5c0e1d6059ca88efd8" }, "downloads": -1, "filename": "svgpathtools-1.2.6.tar.gz", "has_sig": false, "md5_digest": "dd67c27cc95f8ee2686086c4d7fc6a4e", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 2098539, "upload_time": "2017-02-21T07:24:24", "url": "https://files.pythonhosted.org/packages/7a/e2/acb5b34e212ee45bc5a56e05cd51a2c86ad0860a5ed71f99b406d2a7e2a3/svgpathtools-1.2.6.tar.gz" } ], "1.3.1": [ { "comment_text": "", "digests": { "md5": "2903a98a4788f4cd14a346202fd411bf", "sha256": "a865b63d621488ad201be28fde03bbffca311f89ac0062190a992073525e5ddc" }, "downloads": -1, "filename": "svgpathtools-1.3.1-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "2903a98a4788f4cd14a346202fd411bf", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 56792, "upload_time": "2017-03-01T07:49:37", "url": "https://files.pythonhosted.org/packages/41/06/a458f655141a1758d540d038e293b4dcd3c090062fa9c3eefe9de708bbb6/svgpathtools-1.3.1-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "6554950e0583a956f3828eb43e4ccd77", "sha256": "212cf13b9d7e81b0fd15563464920dddb9f01bb55ddcf3d634f457180eb1ed13" }, "downloads": -1, "filename": "svgpathtools-1.3.1.tar.gz", "has_sig": false, "md5_digest": "6554950e0583a956f3828eb43e4ccd77", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 2099174, "upload_time": "2017-03-01T07:49:42", "url": "https://files.pythonhosted.org/packages/2b/bc/47e5f7e07f931fe77ec661b38ac6407422840316435fbc97c4c6ec2c823f/svgpathtools-1.3.1.tar.gz" } ], "1.3.2": [ { "comment_text": "", "digests": { "md5": "d14bf6ecc085bb97fb095b81e2bf0092", "sha256": "0d22cb70d275eb865433ebc6a856776372360ce924b412d85c158356b03dca41" }, "downloads": -1, "filename": "svgpathtools-1.3.2-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "d14bf6ecc085bb97fb095b81e2bf0092", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 56423, "upload_time": "2017-11-27T23:35:48", "url": "https://files.pythonhosted.org/packages/cb/20/d47da85982500ba50b8c38a0a43d8c80b4161b93cfcfd598085636c65e41/svgpathtools-1.3.2-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "fdf65c102fb9e6bf3ffbae4a0b97b095", "sha256": "e6221aea684d8048523706e45736b4cc40af32a029ea9259f1d36c678dbcbb1a" }, "downloads": -1, "filename": "svgpathtools-1.3.2.tar.gz", "has_sig": false, "md5_digest": "fdf65c102fb9e6bf3ffbae4a0b97b095", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 2100640, "upload_time": "2017-11-27T23:36:03", "url": "https://files.pythonhosted.org/packages/7f/6d/7dca381369b341c92dbccd56ba2a9715659d408b54ff91b6637d426e22a2/svgpathtools-1.3.2.tar.gz" } ], "1.3.3": [ { "comment_text": "", "digests": { "md5": "29c51ee1bbca55130d27b04ee15f26e4", "sha256": "7f7bdafe2c03b312178460104705e1d554d8cf36c898bec41bdce9fed3504746" }, "downloads": -1, "filename": "svgpathtools-1.3.3-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "29c51ee1bbca55130d27b04ee15f26e4", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 50693, "upload_time": "2018-07-07T03:08:19", "url": "https://files.pythonhosted.org/packages/71/96/cc91050f3b53c2cea0eda18f371d0584e7f43713ce606738384e8001a877/svgpathtools-1.3.3-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "253714213424e73b67a73c1fd73b714e", "sha256": "e4b3784ae41b725fbce6a33a8981210967b16d0b557cb5d98c0ed0c81f0f89b9" }, "downloads": -1, "filename": "svgpathtools-1.3.3.tar.gz", "has_sig": false, "md5_digest": "253714213424e73b67a73c1fd73b714e", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 2100979, "upload_time": "2018-07-07T03:08:24", "url": "https://files.pythonhosted.org/packages/59/53/a66c970db2abe96152b28fc990bab9a4b93f0dcab21e70db287166910415/svgpathtools-1.3.3.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "29c51ee1bbca55130d27b04ee15f26e4", "sha256": "7f7bdafe2c03b312178460104705e1d554d8cf36c898bec41bdce9fed3504746" }, "downloads": -1, "filename": "svgpathtools-1.3.3-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "29c51ee1bbca55130d27b04ee15f26e4", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 50693, "upload_time": "2018-07-07T03:08:19", "url": "https://files.pythonhosted.org/packages/71/96/cc91050f3b53c2cea0eda18f371d0584e7f43713ce606738384e8001a877/svgpathtools-1.3.3-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "253714213424e73b67a73c1fd73b714e", "sha256": "e4b3784ae41b725fbce6a33a8981210967b16d0b557cb5d98c0ed0c81f0f89b9" }, "downloads": -1, "filename": "svgpathtools-1.3.3.tar.gz", "has_sig": false, "md5_digest": "253714213424e73b67a73c1fd73b714e", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 2100979, "upload_time": "2018-07-07T03:08:24", "url": "https://files.pythonhosted.org/packages/59/53/a66c970db2abe96152b28fc990bab9a4b93f0dcab21e70db287166910415/svgpathtools-1.3.3.tar.gz" } ] }