# Changelog

## 2.2.0 (2026-08-14)

### Tooling and housekeeping

- Modernized type annotations across `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.
- Silenced a `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.

### Fixed

- `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.

## 2.1.0 (2026-08-01)

### Added

- Declared support for **Python 3.14** (added the classifier and CI matrix
  entry across Ubuntu, macOS, and Windows, alongside the existing 3.9–3.13).
- **Clipping paths** now support `<circle>`, `<ellipse>`, and `<polygon>` clip
  shapes, in addition to paths and rectangles, and clipping masks referenced
  from within a `<use>` element.
- Transforms are now applied to clipping-path shapes, fixing clip paths that
  relied on previously unsupported transforms (issue #366).
- The gradient shape classes now implement `getBounds()`, so drawings that
  contain gradient fills can be measured — for example when used as a flowable
  by rst2pdf (issue #466).

### Changed

- Renamed the gradient shape classes `_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.

### Removed

- Removed the deprecated `_LinearGradientShape` and `_RadialGradientShape`
  aliases. Use `LinearGradientShape` and `RadialGradientShape` instead.

### Fixed

- 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.

### Type safety and internal quality (no behavior change)

- Completed static type annotations for `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.
- Pinned a `[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.
- Tightened internal gradient handling types: parsed gradient definitions now
  use a `TypedDict` instead of an opaque `Dict[str, Any]`, and the internal
  group/parent parameters are typed as `Optional[Group]`.
- Added docstrings to the remaining undocumented functions in `svglib.py`,
  bringing documentation coverage to 100%.

### Tooling and housekeeping

- Removed the DeepSource configuration. Its checks were redundant with ruff,
  `mypy --strict`, CodeQL, and the pytest matrix, and only produced recurring
  noise on pull requests.
- Removed the long-defunct `uniconv` sample tests. They contained no assertions
  and shelled out to UniConvertor, an abandoned Python 2 tool, so they had been
  permanently skipped.
- Fixed and revived the Windows CI workflow. It was a disabled placeholder that
  compiled GTK/Cairo from source (a ~20–40 minute step); it now runs a real
  CPython 3.9/3.13 matrix in well under a minute by dropping the source build
  and skipping the network-heavy sample tests on Windows (they already run on
  Ubuntu and macOS). The redundant manual Windows workflow was removed.

## 2.0.2 (2026-06-18)

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`.

## 2.0.1 (2026-06-17)

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.

- Declare `pillow>=9.0.0` as a direct dependency; it was previously an
  undeclared transitive dependency pulled in by reportlab (#463).

## 2.0.0 (2026-06-16)

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.

## 2.0b1 (2026-05-26)

**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")
# 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)
```

- Add support for SVG 2 length units: `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).
- Warn when loading SVGs created with Inkscape < 0.92, which used a
  non-standard 90 dpi reference resolution (#452).
- Move `rlpycairo` to the `bitmaps` extra, so Cairo is no longer part of
  the default installation path.
- Fix inconsistent unit handling: bare numbers and `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).
- Fix SVGs with a `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.
- Add support for SVG `linearGradient` and `radialGradient` paint servers
  (#442).
- Add support for SVG `<switch>` elements with conditional rendering (#441).
- Fix `fill-rule` handling for paths by setting ReportLab `fillMode`
  directly (#440).
- Fix case-insensitive font family lookup in `FontMap` (#433, #435).
- Fix fontconfig handling when resolved fonts use non-normal weight or style.
- Improve missing font warnings by pointing users to `register_font()`.
- Harden `fc-match` calls by resolving the executable path and passing
  arguments after `--`.
- Fixed a SVG path interpretation bug with two successive M or m commands
  (#414).

## 1.6.0 (2025-09-25)

- Replace setup.py with pyproject.toml, rework GitHub workflows (#405).
- Drop support for Python 3.8.
- Modernize codebase, added type annotations, expanded docstrings, etc.
- Add a Makefile.
- Add publishing workflows.
- Remove dunder constants and use importlib.metadata to get the version number.
- Add `rlpycairo` as a dependency, which made Cairo part of the default
  installation path.

## 1.5.1 (2023-01-07)

- Final fix to conversion from shorthand quadratic to cubic bézier (#372).

## 1.5.0 (2023-01-05)

- Add support for `ex` units (assuming for em/2).
- Add support for `image/jpg` embedded images (in addition to `image/jpeg`).
- Avoid crash on `@import` rules in stylesheets. The rules are simply ignored
  (#285).
- Fix conversion from shorthand quadratic to cubic bézier (#364).

## 1.4.1 (2022-08-05)

- No source code changes, only fixed a leftover set_trace() in the released code
  (#352).

## 1.4.0 (2022-08-05)

- Include `tests/samples/others/*.svg` in source distribution (#341).
- Render leading and trailing text content in text nodes (#343).
- Better integration with cssselect2 to support a wider range of CSS selectors
  (#346).

## 1.3.0 (2022-05-18)

- Support mmult import from reportlab < 3.5.61 (#316).
- Make zero-length Line, PolyLine and Polygon appear in PDFs when their stroke
  value is non-zero (#319).
- `<symbol>` nodes now only appears in documents when they are
  referenced, not on their initial appearance (#328).
- If only rx or ry is defined on a `<rect>`, the value is used for both.
- Support non-prefixed `href` attribute added in SVG 2 (#330).
- Fixed parsing of empty SVG style nodes (#325).
- Handle content of multiple SVG style nodes (#326).
- Better font caching for some font names (#338).

## 1.2.1 (2022-01-27)

- Revert to scripts usage instead of console_scripts during setup (#313).

## 1.2.0 (2022-01-20)

- dropped support for Python 3.6 and added official support for Python 3.10.
- improved support for scientific notation in paths (#277)
- if some path has a color with alpha value, it also sets the default fill or
  stroke opacity of the resulting object.
- matrix transforms on groups are combined with other transforms instead of
  replacing them.

## 1.1.0 (2021-04-10)

- honor cascading when applying CSS rules (#253)
- ignore !important statement in CSS values (#227)
- svg2rlg accept pathlib.Path input (#97)
- better handling of font variants (bold, italic)
- a demo for using svglib inside a Streamlit application in /demos
- add a svg2pdf man page
- dropped official support for Python 3.5 and modernized to 3.6+
- added official support for Python 3.9.

## 1.0.1 (2020-08-26)

- avoid stroking clipping paths (#238)
- when converting percentage values in embedded SVGs, consider the direct svg
  parent node (#246)
- fixed rounded rects artifacts when rx/ry values are too high (#250)
- avoid stroking rects when strokeWidth is 0 (#250)

## 1.0.0 (2020-03-22)

- dropped Python 2 support
- fixed references to `<defs>` content when placed middle or end of
  SVG documents (#225)
- fixed elliptic arcs reading when arc flags are condensed (#232)

## 0.9.4 (2020-03-22)

- disabled external entity loading by default (#229 - CVE-2020-10799)

## 0.9.3 (2019-11-02)

- WARNING: this is the last release supporting Python 2!
- added support for more color values (hex with alpha, rgba(), etc.)
  (#213, #115)
- handle text positioning when x, y, dx, dy have a list of values
- fixed styles precedence issue (#211)

## 0.9.2 (2019-07-12)

- fixed license mention in the svg2pdf script (#194)
- support the whole range of HTML color names for color styles (#203)
- fixed a division by zero error when width/height are missing in main viewBox
  (#195)

## 0.9.1 (2019-06-22)

- fixed rendering of circular arcs in some edge cases (#189)
- support for percentage attribute values has been added (#141)
- SVG viewbox is now properly scaled to its width/height attributes (#121)
- embedded external SVG files or file fragments are now rendered (#175)
- support `<rect>` as a clipping source
- prevented crash when a relative file path is used in a memory-only SVG
  source (#173)
- fixed image translation (by y value instead of x)

## 0.9.0 (2018-12-08)

- fixed svgz output on Python 3
- kept PDF standard fonts untouched (#89)
- added basic support for non-standard fonts (#89, #107)
- allowed list of font names
- better merge style attributes from parent nodes (#119)
- fixed crash with strings in transform parameters
- handled PNGs embedded in SVG sources (#93)
- improved scaling of embedded SVGs (#124)
- added millimeter unit support
- fixed crash in elliptical arc calculation (#117)
- added experimental support for CSS style sheets (#111)
- allowed decimal percentage values in rgb colors

## 0.9.0b0 (2018-08-19)

- countless improvements to be hopefully listed in more detail in 0.9.0

## 0.8.1 (2017-04-22)

- added support for the `stroke-opacity` property
- added basic em unit support for text placement
- added respecting absolute coordinates for tspan
- fixed crash with empty path definitions
- symbol definitions are considered when referenced in nodes
- fixed compatibility with recent ReportLab versions

## 0.8.0 (2017-01-23)

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!

- moved repository to https://github.com/deeplook/svglib
- skipped version 0.7.0 to indicate tons of fixes regarding the points below
- added support for elliptical arcs
- fixed open/closed path issues
- fixed clip path issues
- fixed text issues
- replaced `minidom` with `lxml`
- added `logging` support
- added a few more sample SVG files
- migrated test suite from unittest to pytest
- improved test documentation

## 0.6.3 (2010-03-02)

- frozen last version maintained at https://bitbucket.org/deeplook/svglib/

Sadly, no condensed changelog exists prior to version 0.6.3.
