svgpathtools turns SVG path strings into Python objects you can edit
A collection of tools for manipulating and analyzing SVG Path objects and Bezier curves.
At a glance
- What is it?
- svgpathtools is a MIT licensed Python library that parses SVG path data into Line, QuadraticBezier, CubicBezier and Arc objects, then gives you the geometry a path never had as strings: curvature, tangents, intersections, area, arc length and smoothing. It is a geometric tool first and a file format reader second, with one rough edge around discontinuous paths.
- Who is it for?
- svgpathtools is the library to reach for when a script has to reason about the shape of an SVG path rather than merely move it, since curvature, arc length, intersections, offsetting and smoothing are implemented as methods on the segment objects instead of being left to you.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 16 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Coordinates are complex numbers, which makes the arithmetic readable
The first thing to understand about svgpathtools is that a point is a complex number, not a tuple. That single decision shapes the whole API. Constructing a segment means writing 300+100j rather than a pair of numbers, and the arithmetic that follows, such as midpoints and tangents, comes from complex number operations instead of hand-written vector code.
The module is organised around four segment classes, Line, QuadraticBezier, CubicBezier and Arc, plus a fifth, Path, whose objects are sequences of connected or disconnected segment objects. The constructors are explicit:
from svgpathtools import Path, Line, QuadraticBezier, CubicBezier, Arc
seg1 = CubicBezier(300+100j, 100+100j, 200+200j, 200+300j) # A cubic beginning at (300, 100) and ending at (200, 300)
seg2 = Line(200+300j, 250+350j) # A line beginning at (200, 300) and ending at (250, 350)
path = Path(seg1, seg2) # A path traversing the cubic and then the lineThe Arc class takes the SVG arc parameters in their native form, start, radius, rotation, large_arc, sweep and end, and the documentation points at the docstring and the official SVG specifications for what each of them means. Zero-radius arcs, which SVG treats as a special case, even have a dedicated example file in the examples directory.
A separate submodule, bezier.py, handles general nth order Bezier curves stored as n-tuples, so the library reaches past the three segment types SVG itself defines.
A Path is a mutable sequence, so editing geometry does not need a parser
Path behaves much like a list, and the documentation is explicit that it is a mutable sequence. Segments can be appended, inserted, set by index, deleted, enumerated and sliced out. The point of that is not convenience but mechanism: you can take a path apart and put it back together in code, without ever touching the path string again.
# Let's append another to the end of it
path.append(CubicBezier(250+350j, 275+350j, 250+225j, 200+100j))
print(path)
# Let's replace the first segment with a Line object
path[0] = Line(200+100j, 200+300j)
print(path)That opens up the operations a vector editor would normally do by hand: replacing a straight edge with a curve, cropping a path, splitting one into several, reversing the orientation of a segment or of the whole path, and finding a bounding box for a path or for one segment.
Beyond the moving and the reshaping, the library also carries the measurements. It computes tangent vectors and right-hand rule normal vectors, curvature, the area enclosed by a closed path, arc length and inverse arc length, and it converts Bezier segments to numpy.poly1d polynomial objects, plus polynomials back into their Bezier form. There are transition maps from the path domain to the segment domain and back, named T2t and t2T, which is what you need when a parameter value has to survive a change of representation.
There are also two utilities that look out of place and are not: converting RGB colour tuples to hexadecimal strings and back.
parse_path and d() give a round trip between strings and objects
Reading and writing SVG files is the other half of the package. The read, write and display functions handle SVG files containing Path elements and other SVG elements, and the display function, disvg, opens the path in a browser window, which is a quick way to look at what your script produced.
The parser is the entry point for real files, and it produces the same object you would have built by hand:
# We could alternatively created this Path object using a d-string
from svgpathtools import parse_path
path_alt = parse_path('M 300 100 C 100 100 200 200 200 300 L 250 350')The inverse direction is the d method, which returns a Path object's d-string, and the documentation demonstrates the round trip explicitly by parsing a path back out of its own string and comparing the two objects for equality. That comparison is the interesting part, because it is what tells you the parser and the writer agree on the geometry rather than merely producing something that looks similar.
The output of the writer is normalised rather than preserving the original formatting: the sample in the documentation comes back as M 300.0,100.0 C 100.0,100.0 200.0,200.0 200.0,300.0 L 250.0,350.0, with decimals on every coordinate. If you are diffing generated files against checked-in ones, expect noise from that normalisation.
For the smooth workflow, the examples directory contains ready-made scripts, including one for computing many points quickly using numpy arrays, one for determining whether an SVG path is contained in another path, and one for the distance between two SVG paths.
Smoothing removes kinks but multiplies the segment count
Smoothing is the feature that goes beyond what a path parser normally does. Two functions do the work: kinks returns the points on a path where the curve is not differentiable, and smoothed_path returns a path with those kinks removed.
spath = smoothed_path(path)
print("spath contains non-differentiable points? ", len(kinks(spath)) > 0)The caveats are worth more than the feature. Smoothing is described as experimental and only works for line and cubic paths, so a path containing quadratic segments or arcs is out of scope. It always works except on 180 degree turns, which is exactly the case where smoothing a corner into a curve is ambiguous. The results depend on two parameters, maxjointsize and tightness, and the documentation expects you to experiment with them to get a result you like.
The structural cost is real: smoothing increases the number of segments in a path, because removing a kink means replacing a join with curve geometry. The demonstration prints both counts at the end so you can see the difference. Any downstream code that assumes a fixed segment count, or that indexes into a path by segment position, has to cope with the fact that a smoothed path is a different object with a different shape.
The package also includes the display helper for inspecting the before and after side by side:
from svgpathtools import disvgDiscontinuous paths are the acknowledged rough edge
A footnote in the documentation states the main limitation plainly: some of the functionality in this library has not been tested on discontinuous Path objects. A workaround is provided, and it is the continuous_subpaths method, which also appears in the feature list as the way to break discontinuous paths into their continuous subpaths.
The distinction is not academic. A Path object can hold segments that do not connect to each other, which happens with real SVG files containing subpaths. Operations that assume continuity, such as computing a normal vector at a point or smoothing across a join, produce meaningless results on such a path, and the library's answer is that you split the path first.
That makes the recommended workflow clear: parse the file, call continuous_subpaths, work on the pieces, and reassemble if you need to. A script that skips that step on a file with several subpaths will not raise an error in every case, it will simply return wrong geometry, which is the more expensive kind of failure.
The library also computes intersections between paths and segments efficiently, and the example scripts in the repository include determining whether one SVG path is contained in another, which is a question that a general geometry library is well placed to answer and that a path parser alone cannot.
Install from PyPI, and reconcile the scipy dependency yourself
Setup is a single command, and the documentation also offers the source route:
$ pip install svgpathtoolsThe package needs numpy and svgwrite, and the README lists scipy as optional but recommended for performance, while the setup.py in the repository lists numpy, svgwrite and scipy together as install_requires. Those two statements do not agree, so if you are assembling a minimal or offline environment, install scipy yourself rather than relying on the metadata to make that call for you.
The version in setup.py is 1.8.0, the package requires Python 3.8 or newer, and the classifiers run from Python 3.8 through 3.13. The licence is MIT, and the repository also carries a SECURITY.md and a CONTRIBUTING.md. There are no GitHub releases at all, so the distribution channel is PyPI and the tags in the repository are not the artefact you install.
Two details in the packaging are worth knowing if you vendor the source. The download URL in setup.py points at a wheel named svgpathtools-1.8.0-py3-none-any.whl, and the long description is read straight out of README.md at build time, so a build from a checkout without that file present will fail rather than produce a package with an empty description.
The examples include an HTML file that runs the library through pyodide, which is the clearest indication that the package is expected to work in a browser through WebAssembly as well as in a normal Python environment.
Editorial conclusion
svgpathtools is the library to reach for when a script has to reason about the shape of an SVG path rather than merely move it, since curvature, arc length, intersections, offsetting and smoothing are implemented as methods on the segment objects instead of being left to you. It is the wrong tool for a quick one-off coordinate edit, where a regex over the d attribute is faster, and it is the wrong tool for paths that are not connected, unless you call continuous_subpaths first. Verify first that your Python is 3.8 or newer, decide whether scipy is genuinely optional in your environment given that setup.py lists it as a requirement, and check the compatibility notes at the bottom of the README if you are moving code off svg.path.
Frequently asked questions
How do I install svgpathtools?
Run pip install svgpathtools from PyPI, or download the source and run python setup.py install from inside the folder containing setup.py. Python 3.8 or newer is required.
What dependency does svgpathtools need for performance?
The README lists scipy as optional but recommended for performance, although setup.py lists numpy, svgwrite and scipy together as install requirements. Install scipy yourself if you want the performance benefit.
How do I smooth the kinks out of an SVG path in svgpathtools?
Use kinks to find the non-differentiable points and smoothed_path to remove them, tuning the maxjointsize and tightness parameters as needed. Smoothing is experimental and works for line and cubic paths, always except on 180 degree turns, and it increases the number of segments.
Does svgpathtools handle paths with separate subpaths?
Partly. The documentation warns that some functionality has not been tested on discontinuous Path objects and points to Path.continuous_subpaths() as the workaround for breaking a path into its continuous pieces.
What geometry can svgpathtools compute for a path?
Tangent and normal vectors, curvature, the area enclosed by a closed path, arc length and inverse arc length, intersections between paths and segments, bounding boxes, and transition maps between the path domain and the segment domain.
Is svgpathtools related to the svg.path module?
Yes. Much of the core was taken from the svg.path v2.0 module, and the README points interested svg.path users to the compatibility notes at the bottom.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/mathandy-svgpathtools)Was this useful?