NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
PyPI · #1778 most downloaded on PyPI
A pure-Python library for reading and converting SVG
Last release 1 months ago
14 Aug 2026
Ships unpredictably
gaps range from 2 weeks to 2.7 years
Most releases are documented
notes for 22 of 25 stable releases
3 versions withdrawn
withdrawn after publishing
18 years old
29 releases · first in 2008
Modernized type annotations across src/svglib/ and tests/ to use PEP 585 builtin generics (list[...], dict[...], tuple[...], set[...]) instead of typi
src/svglib/ and tests/ to use PEP
585 builtin generics (list[...], dict[...], tuple[...], set[...])
instead of typing.List/Dict/Tuple/Set, and simplified a few
emptiness/membership checks (not points instead of len(points) == 0,
a set literal instead of a list for in checks). No behavior change.UserWarning from test_convert_pdf_png that came from
ReportLab's own renderPM.drawImage, not from svglib: it reopens a
<image> file referenced by path and converts it straight to RGB without
normalizing palette-with-transparency PNGs first, unlike svglib's own
handling of base64-embedded images. The referenced PNG (a dedicated
tRNS-chunk test fixture) still renders correctly; there was nothing to fix
in svglib's output.font-size no longer has PX_TO_PT applied twice. The viewport group
already carries the conversion, but String.fontSize and the advance widths
used to position text fragments were converted to points as well. Text
therefore rendered at 0.75x the size of the geometry in the same document,
and multi-tspan runs were laid out in the wrong unit (issue #502). Both now
stay in user units, like every other coordinate below the transform.
String.fontSize in the shape tree is in user units accordingly; multiply by
the enclosing group's transform to get the rendered size.One column per quarter.
svglib 2.2.0 Latest
Latest
Compare
Removed the deprecated _LinearGradientShape and _RadialGradientShape aliases. Use LinearGradientShape and RadialGradientShape instead.
<circle>, <ellipse>, and <polygon> clip
shapes, in addition to paths and rectangles, and clipping masks referenced
from within a <use> element.getBounds(), so drawings that
contain gradient fills can be measured — for example when used as a flowable
by rst2pdf (issue #466)._LinearGradientShape and
_RadialGradientShape to LinearGradientShape and RadialGradientShape.
They appear in the rendered Drawing tree and were never meant to be
private, so the leading underscore was misleading._LinearGradientShape and _RadialGradientShape
aliases. Use LinearGradientShape and RadialGradientShape instead.The visibility property is now respected on shapes. Elements with
visibility="hidden" (or collapse), set directly or inherited from an
ancestor, were still rendered (issue #359).
display: none set through a style attribute is now respected. Only the
display presentation attribute was read, so <g style="display:none"> and
<rect style="display:none"/> were still rendered (issue #401).
Embedded <image> geometry is no longer truncated to whole points. The
x, y, width, and height of an SVG <image> were each passed through
int(), so a raster placed at fractional coordinates landed up to ~1pt away
from a vector element with identical coordinates. Because width/height were
truncated too, the error grew across the image rather than being a constant
shift (issue #490).
font-family values are now split per CSS instead of on whitespace. An
unquoted multi-word name such as font-family: Cascadia Code was split into
Cascadia and Code, so a font registered under its full name was never
found (issue #374). Commas inside a quoted name (font-family: "Foo, Bar", Arial) no longer split the list either; that previously handed shlex an
unbalanced quote and raised ValueError: No closing quotation, aborting the
conversion of a valid value.
Replaced locale.getdefaultlocale() (used for <switch> systemLanguage
matching) with a small environment-variable lookup. getdefaultlocale() is
deprecated and is removed in Python 3.15; the replacement keeps the same
behaviour, emits no deprecation warning on 3.12–3.14, and adds unit tests for
the language-matching path.
Clipping paths now support the clip-rule attribute instead of incorrectly
using fill-rule (issue #485).
fill="url(...)" and gradient xlink:href values are now matched correctly
when the referenced id is quoted (url('#name'), url("#name")) or
contains a right parenthesis; the previous regex rejected or truncated
these forms.
Rendering to reportlab.graphics.renderSVG.SVGCanvas (as used by
easy-thumbnails[svg]) no longer raises
AttributeError: 'SVGCanvas' object has no attribute 'beginPath' when
drawing a clipped gradient; that canvas doesn't implement beginPath(),
so the gradient's clip path is now skipped on it instead of erroring.
svglib.py; the package now passes
mypy --strict on its own source, with a single documented per-module
override for the untyped reportlab/cssselect2 base classes it subclasses.[tool.mypy] configuration in pyproject.toml and now enforce mypy
in CI (new type-check.yml workflow) and via a local pre-commit hook, so type
regressions fail before merge.TypedDict instead of an opaque Dict[str, Any], and the internal
group/parent parameters are typed as Optional[Group].svglib.py,
bringing documentation coverage to 100%.mypy --strict, CodeQL, and the pytest matrix, and only produced recurring
noise on pull requests.uniconv sample tests. They contained no assertions
and shelled out to UniConvertor, an abandoned Python 2 tool, so they had been
permanently skipped.Supply-chain hygiene release — no code changes.
Supply-chain hygiene release — no code changes.
The 2.0.1 PyPI attestation was generated by a manual workflow_dispatch run from refs/heads/main that raced ahead of the release event. As a result the Sigstore certificate embedded in the PEP 740 attestation identified refs/heads/main as the source rather than refs/tags/v2.0.1, making it impossible to verify the package against the tagged commit. This release is published exclusively via the release: [published] trigger so the attestation identity is refs/tags/v2.0.2.
Supply-chain hygiene release — no code changes.
Supply-chain hygiene release — no code changes.
This release replaces 2.0.0, which was published directly with uv publish and therefore lacked the PEP 740 provenance attestation that was present in 1.6.0. Releases must be triggered via the GitHub Actions release workflow (publish-to-pypi.yml), which uses PyPI Trusted Publishing (OIDC) to produce a SLSA Level 3 attestation. Publishing locally — even with uv publish --trusted-publishing — relies on a local OAuth identity and does not meet that bar.
pillow>=9.0.0 as a direct dependency; it was previously an undeclared transitive dependency pulled in by reportlab (#463).Fixes #463, #464.
Breaking change — output sizes will differ from 1.x.
Identical to 2.0b1 in terms of code; this release promotes the beta to a stable release and converts the project documentation files from RST to Markdown format.
Breaking change — output sizes will differ from 1.x.
svglib now correctly maps SVG user units to ReportLab points using the
standard SVG/CSS conversion factor: 1 px = 0.75 pt (96 dpi). Previous
releases treated 1 user unit as 1 pt, which is 33 % too large. Any SVG
whose width/height or viewBox uses user units or px will
produce a PDF that is 75 % of its previous linear dimensions (same
proportions, correct physical size).
Migration — if you need to preserve the old apparent size, scale the
returned Drawing object before use:
```python from svglib.svglib import svg2rlg
drawing = svg2rlg("file.svg")
factor = 4 / 3 # 1 / 0.75 drawing.width *= factor drawing.height *= factor drawing.scale(factor, factor) ```
rem, vw, vh, vmin,
vmax, and q (quarter-millimetre) in convertLength (#449).rem now resolves against the root <svg> element's font-size
(falling back to the CSS default of 16 px when not set).rlpycairo to the bitmaps extra, so Cairo is no longer part of
the default installation path.px units now produce
identical output, as required by the SVG spec (§5.9.2). convertLength
now returns user units (px) instead of ReportLab points; a new
convertLengthToPt helper is used where absolute point values are needed
(font sizes, canvas dimensions) (#439).viewBox but no explicit width/height: the
viewport scale now defaults to PX_TO_PT (0.75) instead of 1, preventing
content from being cropped.linearGradient and radialGradient paint servers
(#442).<switch> elements with conditional rendering (#441).fill-rule handling for paths by setting ReportLab fillMode
directly (#440).FontMap (#433, #435).register_font().fc-match calls by resolving the executable path and passing
arguments after --.Identical to 2.0b1 in terms of code; this release promotes the beta to a
stable release and converts the project documentation files from RST to
Markdown format.
Breaking change — output sizes will differ from 1.x.
svglib now correctly maps SVG user units to ReportLab points using the
standard SVG/CSS conversion factor: 1 px = 0.75 pt (96 dpi). Previous
releases treated 1 user unit as 1 pt, which is 33 % too large. Any SVG
whose width/height or viewBox uses user units or px will
produce a PDF that is 75 % of its previous linear dimensions (same
proportions, correct physical size).
Migration — if you need to preserve the old apparent size, scale the
returned Drawing object before use:
```python
from svglib.svglib import svg2rlg
drawing = svg2rlg("file.svg")
factor = 4 / 3 # 1 / 0.75
drawing.width *= factor
drawing.height *= factor
drawing.scale(factor, factor)
```
rem, vw, vh, vmin,vmax, and q (quarter-millimetre) in convertLength (#449).rem now resolves against the root <svg> element's font-sizerlpycairo to the bitmaps extra, so Cairo is no longer part ofpx units now produceconvertLengthconvertLengthToPt helper is used where absolute point values are neededviewBox but no explicit width/height: thePX_TO_PT (0.75) instead of 1, preventinglinearGradient and radialGradient paint servers<switch> elements with conditional rendering (#441).fill-rule handling for paths by setting ReportLab fillModeFontMap (#433, #435).register_font().fc-match calls by resolving the executable path and passing--.⚠️ Breaking change — output sizes will differ from 1.x.
⚠️ Breaking change — output sizes will differ from 1.x.
svglib now correctly maps SVG user units to ReportLab points using the standard SVG/CSS conversion factor: 1 px = 0.75 pt (96 dpi). Previous releases treated 1 user unit as 1 pt, which is 33% too large. Any SVG whose width/height or viewBox uses user units or px will produce a PDF that is 75% of its previous linear dimensions (correct physical size).
Migration — to restore 1.x dimensions:
drawing = svg2rlg("file.svg")
factor = 4 / 3 # 1 / 0.75
drawing.width *= factor
drawing.height *= factor
drawing.scale(factor, factor)
font-size="13" and font-size="13px" now render identically (#439)preserveAspectRatio — meet, slice, none, and all alignment tokens now honoured (#448)rem, vw, vh, vmin, vmax, q added to convertLength (#449)rem uses root font-size — resolves against the root <svg> font-size attribute, falling back to 16 pxrlpycairo moved to svglib[bitmaps]; PDF output no longer requires Cairo (#421)<svg> default size — inner <svg> without explicit width/height now defaults to 100% of parent viewport (SVG 1.1 §5.1.2)⚠️ Breaking change — output sizes will differ from 1.x.
svglib now correctly maps SVG user units to ReportLab points using the standard SVG/CSS conversion factor: 1 px = 0.75 pt (96 dpi). Previous releases treated 1 user unit as 1 pt, which is 33% too large. Any SVG whose width/height or viewBox uses user units or px will produce a PDF that is 75% of its previous linear dimensions (correct physical size).
Migration — to restore 1.x dimensions:
drawing = svg2rlg("file.svg")
factor = 4 / 3 # 1 / 0.75
drawing.width *= factor
drawing.height *= factor
drawing.scale(factor, factor)font-size="13" and font-size="13px" now render identically (#439)preserveAspectRatio — meet, slice, none, and all alignment tokens now honoured (#448)rem, vw, vh, vmin, vmax, q added to convertLength (#449)rem uses root font-size — resolves against the root <svg> font-size attribute, falling back to 16 pxrlpycairo moved to svglib[bitmaps]; PDF output no longer requires Cairo (#421)<svg> default size — inner <svg> without explicit width/height now defaults to 100% of parent viewport (SVG 1.1 §5.1.2)Breaking change — output sizes will differ from 1.x.
svglib now correctly maps SVG user units to ReportLab points using the
standard SVG/CSS conversion factor: 1 px = 0.75 pt (96 dpi). Previous
releases treated 1 user unit as 1 pt, which is 33 % too large. Any SVG
whose width/height or viewBox uses user units or px will
produce a PDF that is 75 % of its previous linear dimensions (same
proportions, correct physical size).
Migration — if you need to preserve the old apparent size, scale the
returned Drawing object before use:
from svglib.svglib import svg2rlg
drawing = svg2rlg("file.svg")
# Restore 1.x dimensions (1 user unit → 1 pt, non-spec):
factor = 4 / 3 # 1 / 0.75
drawing.width *= factor
drawing.height *= factor
drawing.scale(factor, factor)
rem, vw, vh, vmin,
vmax, and q (quarter-millimetre) in convertLength (#449).rem now resolves against the root <svg> element's font-size
(falling back to the CSS default of 16 px when not set).rlpycairo to the bitmaps extra, so Cairo is no longer part of
the default installation path.px units now produce
identical output, as required by the SVG spec (§5.9.2). convertLength
now returns user units (px) instead of ReportLab points; a new
convertLengthToPt helper is used where absolute point values are needed
(font sizes, canvas dimensions) (#439).viewBox but no explicit width/height: the
viewport scale now defaults to PX_TO_PT (0.75) instead of 1, preventing
content from being cropped.linearGradient and radialGradient paint servers
(#442).<switch> elements with conditional rendering (#441).fill-rule handling for paths by setting ReportLab fillMode
directly (#440).FontMap (#433, #435).register_font().fc-match calls by resolving the executable path and passing
arguments after --.Replace setup.py with pyproject.toml, rework GitHub workflows (#405).
rlpycairo as a dependency, which made Cairo part of the default
installation path.Final fix to conversion from shorthand quadratic to cubic bézier (#372).
Add support for ex units (assuming for em/2).
ex units (assuming for em/2).image/jpg embedded images (in addition to image/jpeg).@import rules in stylesheets. The rules are simply ignored
(#285).No source code changes, only fixed a leftover set_trace() in the released code (#352).
Include tests/samples/others/*.svg in source distribution (#341).
tests/samples/others/*.svg in source distribution (#341).Support mmult import from reportlab < 3.5.61 (#316).
<symbol> nodes now only appears in documents when they are
referenced, not on their initial appearance (#328).<rect>, the value is used for both.href attribute added in SVG 2 (#330).Revert to scripts usage instead of console_scripts during setup (#313).
dropped support for Python 3.6 and added official support for Python 3.10.
honor cascading when applying CSS rules
when converting percentage values in embedded SVGs, consider the direct svg parent node
fixed references to content when placed middle or end of SVG documents
<defs> content when placed middle or end of
SVG documents (#225)disabled external entity loading by default (#229 - CVE-2020-10799)
WARNING: this is the last release supporting Python 2!
fixed license mention in the svg2pdf script
fixed rendering of circular arcs in some edge cases
<rect> as a clipping sourcekept PDF standard fonts untouched
countless improvements to be hopefully listed in more detail in 0.9.0
added support for the stroke-opacity property
stroke-opacity propertyThis release introduces *many* contributions by Claude Paroz, who stepped forward to give this project a long needed overhaul after ca. six years of t
This release introduces many contributions by Claude Paroz, who stepped forward to give this project a long needed overhaul after ca. six years of taking a nap, for which I'm really very grateful! Thanks, Claude!
minidom with lxmllogging supportfrozen last version maintained at https://bitbucket.org/deeplook/svglib/
Sadly, no condensed changelog exists prior to version 0.6.3.
Nothing published for this version
Nothing published for this version
Nothing published for this version
Your coding agent can read these notes before it upgrades. Set up the MCP server →