An SVG path editor
Project description
🎨 SVG Path Editor
A small, high-precision library for editing, transforming, and optimizing SVG paths programmatically in Python.
It is a port of svg-path-editor-lib 1.0.3 to Python with significant improvements:
- Decimal-based geometry: all coordinates are stored as
decimal.Decimal, with high-precision SymPy-based computations where appropriate. This preserves the decimal values stored in an SVG path and avoids the binary round-off errors that occur withfloat. - In-place and out-of-place operations: most geometric operations are available in a mutating (
scale,rotate, …) and a non-mutating (scaled,rotated, …) variant. list-like path modification API: path-level manipulations (insert, remove, change type, …) are exposed as methods onSvgPath.- Typed and documented: extensive type hints and docstrings for good IDE support and static analysis.
The full documentation is available on Read the Docs, and the pytest-based test suite with a 100% test coverage is defined in the tests directory.
📦 Installation
This package is available on PyPI and can be installed with pip:
pip install svg-path-editor
🚀 Basic Usage
from svg_path_editor import SvgPath
path = SvgPath("M-15 14s5 7.5 15 7.5 15-7.5 15-7.5 z")
# `SvgPath` implements `__str__` with fairly readable (non-minified) output
# M -15 14 s 5 7.5 15 7.5 s 15 -7.5 15 -7.5 z
print(path)
# Custom decimals and minified output (`decimals=None`, `minify=False` by default)
# M-15 14s5 7.5 15 7.5 15-7.5 15-7.5z
print(path.as_string(decimals=1, minify=True))
# `SvgPath` also implements `__format__`, with `m` denoting `minify=True`
print(f"{path:.1m} or {path:m.1}")
📐 Geometric Operations
Geometric operations are available in both in-place and out-of-place variants.
Out-of-place
path = SvgPath("M-15 14s5 7.5 15 7.5 15-7.5 15-7.5 z")
# Out-of-place scale
# M -30 28 s 10 15 30 15 s 30 -15 30 -15 z
print(path.scaled(kx=2, ky=2))
# Out-of-place translate
# M -14 14.5 s 5 7.5 15 7.5 s 15 -7.5 15 -7.5 z
print(path.translated(dx=1, dy=0.5))
# Out-of-place rotate around (0, 0)
# M -14 -15 s -7.5 5 -7.5 15 s 7.5 15 7.5 15 z
print(path.rotated(ox=0, oy=0, degrees=90))
In-place
path = SvgPath("M-15 14s5 7.5 15 7.5 15-7.5 15-7.5 z")
# In-place scale
# M -30 28 s 10 15 30 15 s 30 -15 30 -15 z
path.scale(kx=2, ky=2)
print(path)
# In-place translate
# M -29 28.5 s 10 15 30 15 s 30 -15 30 -15 z
path.translate(dx=1, dy=0.5)
print(path)
# In-place rotate
# M -28.5 -29 s -15 10 -15 30 s 15 30 15 30 z
path.rotate(ox=0, oy=0, degrees=90)
print(path)
🔁 Absolute vs. Relative Commands
Commands can be stored as either absolute (M, L, C, …) or relative (m, l, c, …).
Conversion is available in-place via a property and out-of-place via a method.
path = SvgPath("M-15 14s5 7.5 15 7.5 15-7.5 15-7.5 z")
# In-place: `SvgPath.relative` mutates the instance
path.relative = False
# M -15 14 S -10 21.5 0 21.5 S 15 14 15 14 Z
print(path)
# Out-of-place: `SvgPath.with_relative()` returns a new path
relative = path.with_relative(True)
# m -15 14 s 5 7.5 15 7.5 s 15 -7.5 15 -7.5 z
print(relative)
🧱 Path Modification
SvgPath exposes several methods that modify the structure of a path in place, including parts of the list API:
from svg_path_editor import Point, SvgPath
from svg_path_editor.svg import QuadraticBezierCurveTo
path = SvgPath("M0 0L10 0V10Z")
# Deep copy
clone = path.clone()
# M 0 0 L 10 0 V 10 Z
print(clone)
# In-place removal of the `L` command
path.remove(path.path[1])
# M 0 0 V 10 Z
print(path)
# In-place insertion of a quadratic Bézier curve where the `L` command was
path.insert(1, QuadraticBezierCurveTo([5, -5, 10, 0], relative=False))
# M 0 0 Q 5 -5 10 0 V 10 Z
print(path)
# In-place command type change from `V` to `L` (equivalent, but longer)
path.change_type(2, "L")
# M 0 0 Q 5 -5 10 0 L 10 10 Z
print(path)
# In-place move of a particular point
path.set_location(path.target_locations[-2], to=Point(5, 10))
# M 0 0 Q 5 -5 10 0 L 5 10 Z
print(path)
# The clone is unaffected by these changes
print(clone)
🧱 Higher-Level Path Operations
These functions operate on paths out-of-place:
from svg_path_editor import SvgPath, change_path_origin, reverse_path
path = SvgPath("M-15 14s5 7.5 15 7.5 15-7.5 15-7.5 z")
# Reverse path direction
# M 15 14 S 10 21.5 0 21.5 S -15 14 -15 14 Z
print(reverse_path(path))
# Change the origin (starting command) within a subpath
# M 0 21.5 c 10 0 15 -7.5 15 -7.5 L -15 14 s 5 7.5 15 7.5
print(change_path_origin(path, new_origin_index=2))
🧮 Decimal-Based Geometry
Internally, all coordinates and numeric parameters are stored as decimal.Decimal:
- Constructors and geometric methods accept
int,float,str, orDecimal; values are converted toDecimalimmediately. - Arithmetic (translation, scaling, rotation, etc.) is performed in terms of
Decimalto retain the decimal representation in an SVG path and avoid introducing binary round-off errors. - The decimal precision can be controlled via Python’s
decimalcontext.
from decimal import localcontext
from svg_path_editor import SvgPath
path = SvgPath("M0 0h10v10z")
# Default precision: 28 places
# Rotation computed with SymPy for high-precision trigonometric functions
rotated = path.rotated(0, 0, -45)
# M 0 0 l 7.071067811865475244008443621 -7.071067811865475244008443621 l 7.071067811865475244008443621 7.071067811865475244008443621 z
print(rotated)
# Precision can be reduced when printing
# M 0 0 l 7.07107 -7.07107 l 7.07107 7.07107 z
print(f"{rotated:.5}")
# The precision can be controlled using `getcontext`/`localcontext`
# Since `Decimal` is a floating-point format, the precision specifies the overall
# number of digits, not just the number of decimal places!
with localcontext() as ctx:
ctx.prec = 6
rotated = path.rotated(0, 0, -45)
# The same output as before, even without precision reduction
# M 0 0 l 7.07107 -7.07107 l 7.07107 7.07107 z
print(rotated)
🧹 Path Optimization
optimize_path rewrites a path into an equivalent but more compact form and is also out-of-place:
from svg_path_editor import SvgPath, optimize_path
path = SvgPath("M-15 14s5 7.5 15 7.5 15-7.5 15-7.5 z")
optimized = optimize_path(
path,
# Remove redundant M/Z or degenerate L/H/V.
remove_useless_commands=True,
# Remove empty closed subpaths (M immediately followed by Z).
remove_orphan_dots=True,
# Convert eligible C/Q to S/T.
use_shorthands=True,
# Replace L with H/V where possible.
use_horizontal_and_vertical_lines=True,
# Choose relative/absolute per command to minimize size.
use_relative_absolute=True,
# Try reversing path direction if it reduces output length.
# This may change the appearance of stroked paths!
use_reverse=True,
# Convert final line segments that return to start into Z.
# This may change the appearance of stroked paths!
use_close_path=True,
)
# More readable form
# M -15 14 s 5 7.5 15 7.5 S 15 14 15 14 z
print(optimized)
# Minified form
# M-15 14s5 7.5 15 7.5S15 14 15 14z
print(f"{optimized:m}")
🧪 Testing
The project includes pytest-based tests that cover the entire code base with a 100% code coverage.
The development dependencies can be installed via the dev optional group:
pip install .[dev]
All tests (including coverage reporting) can then be run from the project root:
pytest --cov
📜 License
This library is licensed under the terms of the Mozilla Public License 2.0, provided in License.
The original TypeScript library is licensed under the Apache License, Version 2.0, provided in LicenseYqnn.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file svg_path_editor-3.1.0.tar.gz.
File metadata
- Download URL: svg_path_editor-3.1.0.tar.gz
- Upload date:
- Size: 35.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a6482ce33fd929c9f14e42df36d3f857252ac61f2abf007be94045a0781574e4
|
|
| MD5 |
508eac4cacee091b9b99ce615ee88ab0
|
|
| BLAKE2b-256 |
d8e8d70fed85531ec397cc986658855bd297b3aa14dda2e1fca996ed60b886b1
|
Provenance
The following attestation bundles were made for svg_path_editor-3.1.0.tar.gz:
Publisher:
publish-to-pypi.yml on KurtBoehm/svg-path-editor
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
svg_path_editor-3.1.0.tar.gz -
Subject digest:
a6482ce33fd929c9f14e42df36d3f857252ac61f2abf007be94045a0781574e4 - Sigstore transparency entry: 788562184
- Sigstore integration time:
-
Permalink:
KurtBoehm/svg-path-editor@322f193337e4592d8af4a818d64691040821bfda -
Branch / Tag:
refs/tags/3.1.0 - Owner: https://github.com/KurtBoehm
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-to-pypi.yml@322f193337e4592d8af4a818d64691040821bfda -
Trigger Event:
release
-
Statement type:
File details
Details for the file svg_path_editor-3.1.0-py3-none-any.whl.
File metadata
- Download URL: svg_path_editor-3.1.0-py3-none-any.whl
- Upload date:
- Size: 28.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dc6d3d9283f5bdf71918c605102de26a1807edd2770dbcf666443c39f74c0daa
|
|
| MD5 |
b1faec408a39ac74d1ba88135561e605
|
|
| BLAKE2b-256 |
17e7328497ca268b760271ace758078ca043f841d5b185cf6d8ef0aa88e49695
|
Provenance
The following attestation bundles were made for svg_path_editor-3.1.0-py3-none-any.whl:
Publisher:
publish-to-pypi.yml on KurtBoehm/svg-path-editor
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
svg_path_editor-3.1.0-py3-none-any.whl -
Subject digest:
dc6d3d9283f5bdf71918c605102de26a1807edd2770dbcf666443c39f74c0daa - Sigstore transparency entry: 788562189
- Sigstore integration time:
-
Permalink:
KurtBoehm/svg-path-editor@322f193337e4592d8af4a818d64691040821bfda -
Branch / Tag:
refs/tags/3.1.0 - Owner: https://github.com/KurtBoehm
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-to-pypi.yml@322f193337e4592d8af4a818d64691040821bfda -
Trigger Event:
release
-
Statement type: