SkillAgentSearch skills...

Svgpathtools

A collection of tools for manipulating and analyzing SVG Path objects and Bezier curves.

Install / Use

/learn @mathandy/Svgpathtools
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

Donate Python PyPI PyPI - Downloads

svgpathtools

svgpathtools is a collection of tools for manipulating and analyzing SVG Path objects and Bézier curves.

Features

svgpathtools contains functions designed to easily read, write and display SVG files as well as a large selection of geometrically-oriented tools to transform and analyze path elements.

Additionally, the submodule bezier.py contains tools for for working with general nth order Bezier curves stored as n-tuples.

Some included tools:

  • read, write, and display SVG files containing Path (and other) SVG elements
  • convert Bézier path segments to numpy.poly1d (polynomial) objects
  • convert polynomials (in standard form) to their Bézier form
  • compute tangent vectors and (right-hand rule) normal vectors
  • compute curvature
  • break discontinuous paths into their continuous subpaths.
  • efficiently compute intersections between paths and/or segments
  • find a bounding box for a path or segment
  • reverse segment/path orientation
  • crop and split paths and segments
  • smooth paths (i.e. smooth away kinks to make paths differentiable)
  • transition maps from path domain to segment domain and back (T2t and t2T)
  • compute area enclosed by a closed path
  • compute arc length
  • compute inverse arc length
  • convert RGB color tuples to hexadecimal color strings and back

Prerequisites

  • numpy
  • svgwrite
  • scipy (optional, but recommended for performance)

Setup

$ pip install svgpathtools

Alternative Setup

You can download the source from Github and install by using the command (from inside the folder containing setup.py):

$ python setup.py install

Credit where credit's due

Much of the core of this module was taken from the svg.path (v2.0) module. Interested svg.path users should see the compatibility notes at bottom of this readme.

Basic Usage

Classes

The svgpathtools module is primarily structured around four path segment classes: Line, QuadraticBezier, CubicBezier, and Arc. There is also a fifth class, Path, whose objects are sequences of (connected or disconnected<sup id="a1">1</sup>) path segment objects.

  • Line(start, end)

  • Arc(start, radius, rotation, large_arc, sweep, end) Note: See docstring for a detailed explanation of these parameters

  • QuadraticBezier(start, control, end)

  • CubicBezier(start, control1, control2, end)

  • Path(*segments)

See the relevant docstrings in path.py or the official SVG specifications for more information on what each parameter means.

<u id="f1">1</u> Warning: Some of the functionality in this library has not been tested on discontinuous Path objects. A simple workaround is provided, however, by the Path.continuous_subpaths() method.

from __future__ import division, print_function
# Coordinates are given as points in the complex plane
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 line

# 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')

# Let's check that these two methods are equivalent
print(path)
print(path_alt)
print(path == path_alt)

# On a related note, the Path.d() method returns a Path object's d-string
print(path.d())
print(parse_path(path.d()) == path)
Path(CubicBezier(start=(300+100j), control1=(100+100j), control2=(200+200j), end=(200+300j)),
     Line(start=(200+300j), end=(250+350j)))
Path(CubicBezier(start=(300+100j), control1=(100+100j), control2=(200+200j), end=(200+300j)),
     Line(start=(200+300j), end=(250+350j)))
True
M 300.0,100.0 C 100.0,100.0 200.0,200.0 200.0,300.0 L 250.0,350.0
True

The Path class is a mutable sequence, so it behaves much like a list. So segments can appended, inserted, set by index, deleted, enumerated, sliced out, etc.

# 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)

# You may have noticed that this path is connected and now is also closed (i.e. path.start == path.end)
print("path is continuous? ", path.iscontinuous())
print("path is closed? ", path.isclosed())

# The curve the path follows is not, however, smooth (differentiable)
from svgpathtools import kinks, smoothed_path
print("path contains non-differentiable points? ", len(kinks(path)) > 0)

# If we want, we can smooth these out (Experimental and only for line/cubic paths)
# Note:  smoothing will always works (except on 180 degree turns), but you may want 
# to play with the maxjointsize and tightness parameters to get pleasing results
# Note also: smoothing will increase the number of segments in a path
spath = smoothed_path(path)
print("spath contains non-differentiable points? ", len(kinks(spath)) > 0)
print(spath)

# Let's take a quick look at the path and its smoothed relative
# The following commands will open two browser windows to display path and spaths
from svgpathtools import disvg
from time import sleep
disvg(path) 
sleep(1)  # needed when not giving the SVGs unique names (or not using timestamp)
disvg(spath)
print("Notice that path contains {} segments and spath contains {} segments."
      "".format(len(path), len(spath)))
Path(CubicBezier(start=(300+100j), control1=(100+100j), control2=(200+200j), end=(200+300j)),
     Line(start=(200+300j), end=(250+350j)),
     CubicBezier(start=(250+350j), control1=(275+350j), control2=(250+225j), end=(200+100j)))
Path(Line(start=(200+100j), end=(200+300j)),
     Line(start=(200+300j), end=(250+350j)),
     CubicBezier(start=(250+350j), control1=(275+350j), control2=(250+225j), end=(200+100j)))
path is continuous?  True
path is closed?  True
path contains non-differentiable points?  True
spath contains non-differentiable points?  False
Path(Line(start=(200+101.5j), end=(200+298.5j)),
     CubicBezier(start=(200+298.5j), control1=(200+298.505j), control2=(201.057124638+301.057124638j), end=(201.060660172+301.060660172j)),
     Line(start=(201.060660172+301.060660172j), end=(248.939339828+348.939339828j)),
     CubicBezier(start=(248.939339828+348.939339828j), control1=(249.649982143+349.649982143j), control2=(248.995+350j), end=(250+350j)),
     CubicBezier(start=(250+350j), control1=(275+350j), control2=(250+225j), end=(200+100j)),
     CubicBezier(start=(200+100j), control1=(199.62675237+99.0668809257j), control2=(200+100.495j), end=(200+101.5j)))
Notice that path contains 3 segments and spath contains 6 segments.

Reading SVGSs

The svg2paths() function converts an svgfile to a list of Path objects and a separate list of dictionaries containing the attributes of each said path.
Note: Line, Polyline, Polygon, and Path SVG elements can all be converted to Path objects using this function.

# Read SVG into a list of path objects and list of dictionaries of attributes 
from svgpathtools import svg2paths, wsvg
paths, attributes = svg2paths('test.svg')

# Update: You can now also extract the svg-attributes by setting
# return_svg_attributes=True, or with the convenience function svg2paths2
from svgpathtools import svg2paths2
paths, attributes, svg_attributes = svg2paths2('test.svg')

# Let's print out the first path object and the color it was in the SVG
# We'll see it is composed of two CubicBezier objects and, in the SVG file it 
# came from, it was red
redpath = paths[0]
redpath_attribs = attributes[0]
print(redpath)
print(redpath_attribs['stroke'])
Path(CubicBezier(start=(10.5+80j), control1=(40+10j), control2=(65+10j), end=(95+80j)),
     CubicBezier(start=(95+80j), control1=(125+150j), control2=(150+150j), end=(180+80j)))
red

Writing SVGSs (and some geometric functions and methods)

The wsvg() function creates an SVG file from a list of path. This function can do many things (see docstring in paths2svg.py for more information) and is meant to be quick and easy to use. Note: Use the convenience function disvg() (or set 'openinbrowser=True') to automatically attempt to open the created svg file in your default SVG viewer.

# Let's make a new SVG that's identical to the first
wsvg(paths, attributes=attributes, svg_attributes=svg_attributes, filename='output1.svg')

output1.svg

There will be many more examples of writing and displaying path data below.

The .point() method and transitioning between path and path segment parameterizations

SVG Path elements and their segments have official parameterizations. These parameterizations can be accessed using the Path.point(), Line.point(), QuadraticBezier.point(), CubicBezier.point(), and Arc.point() methods. All these parameterizations are defined over the domai

Related Skills

View on GitHub
GitHub Stars625
CategoryDevelopment
Updated13h ago
Forks154

Languages

Python

Security Score

95/100

Audited on Mar 26, 2026

No findings