Skip to main content

A fast, lossless SVG optimizer that removes unnecessary data and produces clean, lightweight vector graphics.

Project description

SVG Polish

A fast, lossless, type-safe SVG optimizer for Python.

svg_polish shrinks SVG files — strips editor metadata, collapses redundant attributes, dedups gradients, optimises path data and transforms — while guaranteeing that the output renders identically to the input. The library is fully typed (py.typed), thread-safe, secure-by-default against XML attacks, and ships with a single short API: optimize().

PyPI Python Coverage License


Why SVG Polish

Before After
style="fill:#ff0000;stroke:#000000" fill="#f00"
Inkscape / Sketch / Adobe namespaces Removed
Verbose path commands Collapsed and rounded
Duplicate gradients Deduplicated
Hostile XXE / billion-laughs payload Rejected before parsing

Install

Requires Python 3.10+.

pip install svg-polish

The install is pure Python and depends only on defusedxml. A svg-polish[fast] extra is reserved for the v1.x lxml-backed XML engine (~3–5× faster on large files); v1.0 ships only the defusedxml.minidom backend.

Quick start

Python

from svg_polish import optimize

optimized = optimize('<svg xmlns="http://www.w3.org/2000/svg">…</svg>')

That's the whole pitch. Read from disk, write to disk:

from pathlib import Path
from svg_polish import optimize, optimize_path

with open("input.svg", encoding="utf-8") as f:
    svg = f.read()

optimized = optimize(svg)

with open("output.svg", "w", encoding="utf-8") as f:
    f.write(optimized)

# or, in one line via the path-aware helper:
Path("output.svg").write_text(optimize_path("input.svg"), encoding="utf-8")

Pass OptimizeOptions for tuning:

from svg_polish import optimize, OptimizeOptions

opts = OptimizeOptions(
    digits=3,
    shorten_ids=True,
    enable_viewboxing=True,
    strip_comments=True,
)
optimized = optimize(svg, opts)

For metrics, use optimize_with_stats:

from svg_polish import optimize_with_stats

result = optimize_with_stats(svg)
print(f"saved {result.saved_bytes} B ({result.saved_ratio:.1%}) in {result.duration_ms:.1f} ms")

For async web frameworks:

from svg_polish import optimize_async

async def handler(svg: str) -> str:
    return await optimize_async(svg)

Command line

svg-polish -i input.svg -o output.svg
cat input.svg | svg-polish > output.svg
svg-polish -i input.svg -o output.svgz       # gzip-compressed output

Aggressive settings:

svg-polish -i input.svg -o output.svg \
  --enable-viewboxing \
  --enable-id-stripping \
  --enable-comment-stripping \
  --shorten-ids \
  --indent=none

Run svg-polish --help for the full flag list.

Public API

Symbol Purpose
optimize(svg, options=None) Canonical entry point. Alias of optimize_string.
optimize_string(svg, options=None) str/bytes in, str out.
optimize_bytes(svg, options=None) bytes in, UTF-8 bytes out.
optimize_path(path, options=None) Read from a filesystem path.
optimize_async(svg, options=None) await-able wrapper via asyncio.to_thread.
optimize_with_stats(svg, options=None) Returns an OptimizeResult with metrics.
OptimizeOptions Frozen dataclass — the only configuration shape.
OptimizeResult Optimised SVG + stats + duration.
ScourStats Per-pass counters.

Exceptions: SvgPolishError (base) → SvgParseError, SvgPathSyntaxError, SvgTransformSyntaxError, SvgOptimizeError, SvgSecurityError, InvalidOptionError. See docs/api.md for the full reference.

What it does

  • Removes editor metadata (Inkscape, Sodipodi, Illustrator, Sketch).
  • Strips default attribute values and empty attributes.
  • Converts colors to the shortest equivalent form.
  • Deduplicates <linearGradient> / <radialGradient> definitions.
  • Collapses <g> wrappers and merges sibling groups.
  • Optimises <path> d data (relative coords, h/v/s shortcuts, segment merging).
  • Optimises transform, patternTransform, gradientTransform.
  • Reduces numeric precision to a configurable digit count.
  • Optionally shortens IDs, strips comments, converts to viewBox, embeds rasters.
  • Custom serialiser produces tight, deterministic output.

All passes are lossless by default — see docs/performance.md for the opt-in decimal_engine="float" mode that trades reproducibility for ~3-5× faster numeric arithmetic.

Security

Inputs are parsed through defusedxml, so XML external entity attacks, billion-laughs, and external DTD fetches are rejected before they touch the optimiser. Inputs over 100 MB are refused by default. See SECURITY.md for the threat model and docs/security.md for usage patterns.

Documentation

Development

git clone https://github.com/g-battaglia/svg_polish.git
cd svg_polish
uv sync

uv run poe test          # full test suite (~660 tests)
uv run poe test-cov      # with coverage HTML report
uv run poe lint          # ruff lint
uv run poe format        # ruff format
uv run poe typecheck     # mypy strict
uv run poe check         # all of the above

uv run poe bench         # save a performance baseline
uv run poe bench-compare # compare current run against the baseline

Origin

svg_polish is a fork of Scour, originally created by Jeff Schiller and Louis Simard in 2010, later maintained by Tobias Oberstein and Patrick Storz. Upstream Scour has been dormant since August 2021. This v1.0 release is a ground-up modernisation:

  • Python 3.10+ only; no six, no Python 2 compatibility shims.
  • Single typed public surface (OptimizeOptions, OptimizeResult, optimize_*).
  • Modular passes (one file per transformation) instead of a 4 700-line monolith.
  • Thread-safe Decimal precision contexts.
  • Secure-by-default XML parsing with defusedxml.
  • 100% line coverage, mypy --strict clean, ruff clean.

License

Apache License 2.0 — see LICENSE.

svg_regex.py is derived from code by Enthought, Inc. (BSD 3-Clause). Full attribution in NOTICE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

svg_polish-1.0.0.tar.gz (210.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

svg_polish-1.0.0-py3-none-any.whl (122.1 kB view details)

Uploaded Python 3

File details

Details for the file svg_polish-1.0.0.tar.gz.

File metadata

  • Download URL: svg_polish-1.0.0.tar.gz
  • Upload date:
  • Size: 210.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for svg_polish-1.0.0.tar.gz
Algorithm Hash digest
SHA256 56df761f7f0beb7c41060616e7eb5ddc990ce78e4a3323f04d62790ab431273d
MD5 14b5ab2e35442d3b3c339f04cb13b6df
BLAKE2b-256 7dc5e7987aa81660268e7cc64db5360ca28ef6e729cd225a28443d9da246a2ec

See more details on using hashes here.

File details

Details for the file svg_polish-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: svg_polish-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 122.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for svg_polish-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 a513b3f584b64b293c2535d0231eb8bb27e977f193f8f1e5f59cdf7fc59ae61d
MD5 01718a40aefaec5872f4e96226a4f90e
BLAKE2b-256 593e3de48cd6c5c0c94924fb28127bd392ba99175f8a3f03bc43d9eb694bd61c

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page