# Changelog

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