Skip to content

Releases

v0.6.2

Choose a tag to compare

@github-actions github-actions released this 05 Nov 14:30
Immutable release. Only release title and notes can be modified.

Fixed

  • A mask, a clip or a filter region in object bounding box units applies to an <image>, and to a group that
    holds one. WeasyPrint has no bounding box for an image: the mask was left out and the image drawn opaque.
    The flat SVG writes the alpha of an image, since 0.6.1 each blur, as such a mask: weasysvg-convert, which
    pandoc draws the SVG of a DOCX with, drew the shadow of the OpenStreetMap logo as a dark rectangle.
  • An element under a soft mask draws in a group, which the mask cuts once. PDFium and Poppler applied the mask
    again inside the cell of a pattern: a gradient fill under a mask of 0.5 showed at 0.25, an SVG pattern fill
    vanished. In PDF the soft mask of an image with alpha replaced the mask: a masked image drew unmasked. WeasyPrint
    alone has these gaps too.
  • feColorMatrix and feComponentTransfer recolor the pixels of an image too, as they recolor the paint of a
    shape, within MAX_PIXEL_WORK. cairo turns a soft mask white through such a filter: the shading of the
    flat SVG of Tux was missing.

Full changelog: v0.6.1...v0.6.2

v0.6.1

Choose a tag to compare

@github-actions github-actions released this 05 Nov 12:36
Immutable release. Only release title and notes can be modified.

Changed

  • In weasysvg.portable() and the flat SVG, a blur becomes an image of its color with the blurred coverage as
    its alpha, no longer levels of constant opacity. Word drew each level as a ring: the shadow of the
    OpenStreetMap logo and the glare of Tux showed rings. An element that paints a gradient shows it through
    that image as a soft mask. The flat SVG of Tux shrinks from 12.9 MB to 3.7 MB: each level of its blurred
    gradients had cairo write the gradient again. band_px sets the levels of gradients only.
  • In weasysvg.portable(), a fill with a gradient of one color that fades fills each opacity level with that color,
    where a level clipped the shading. A stroke keeps the shading: it paints through a pattern, and Poppler drew such
    levels there washed out, over everything after them. cairo wrote a clip path, a group and a gradient per level, now one path. Word drew
    the clipped levels of the OpenStreetMap lens wrongly: its digits came out sharp and opaque, where Chromium fades
    them.
  • The flat SVG keeps the stops of a gradient that change it, within 0.1 % of a color channel and 0.001 of opacity:
    cairo samples a shading into 257 stops. It rounds the coordinates of a path to two places, of a drawing eight
    times as large. With both, the flat SVG of the OpenStreetMap logo shrinks from 6.6 MB to 2.9 MB, Tux from 3.7 MB
    to 0.46 MB.

Fixed

  • The flat SVG of weasysvg-convert reports why pdftocairo wrote nothing. Killed by the kernel, it said only
    "pdftocairo failed: "; under a memory limit, pdftocairo wrote an empty or cut file, exited with 0 and the flat
    SVG passed. Now each case raises its own ConvertError: the signal, out of memory anywhere in the error output
    (also an abort of Poppler or of C++), no output, and only the first line of the error output, which runs to
    megabytes out of memory.
  • pdftocairo runs with at most 2 GB of address space on POSIX (PDFTOCAIRO_MEMORY). cairo draws a soft mask of a
    rotated radial shading with gigabytes: one such lens of the OpenStreetMap logo took 8.3 GB and 39 s, and the
    whole logo was killed at 21 GB. It fails in a few seconds now. The flattening of weasysvg keeps no such soft
    mask today, and stays under 50 MB.

Full changelog: v0.6.0...v0.6.1

v0.6.0

Choose a tag to compare

@github-actions github-actions released this 04 Nov 14:09
Immutable release. Only release title and notes can be modified.

Added

  • In weasysvg.portable(), no opacity level of a gradient is narrower than band_px CSS px of the drawing as
    shown, 1 by default, where its opacity changes most slowly. A small gradient gets fewer levels: a level per
    1/64 was a third of a pixel over 20 px, and the flat SVG drew each one in full. weasysvg-convert --band-px
    and to_svg(band_px=) set it; 0 keeps a level per 1/64.
  • The levels of a blur in weasysvg.portable() follow band_px too: where the softest blurred edge rises
    fastest, its contours lie at least band_px apart. A blur of 4 px takes 10 levels, not 64. The flat SVG of
    the corpus shrinks from 261 MB to 25 MB and flattens in 6.4 s, not 84 s: Tux from 244 MB, which librsvg
    refused to draw, to 12.9 MB, the Inkscape logo from 3.0 MB to 1.0 MB. Against the PDF none drifts further.
  • Both measure a length as shown along its own direction: a transform that stretches one axis and squeezes
    the other keeps the levels along the stretched one.

Fixed

  • The flat SVG of weasysvg-convert writes each clip path once. cairo wrote the clip of a shape again for every
    paint inside it, and a gradient with a transparent stop is painted once per opacity level: the OpenStreetMap
    logo came out at 29 MB, now 9.6 MB, and Word slowed down on it.

Full changelog: v0.5.0...v0.6.0

v0.5.0

Choose a tag to compare

@github-actions github-actions released this 03 Nov 17:39
Immutable release. Only release title and notes can be modified.

Added

  • In weasysvg.portable(), a gradient with transparent stops or a fill opacity needs no soft mask. Its color stays
    one shading; the opacity becomes its level sets along the gradient, strips or rings, each painted at a constant
    opacity, within 1/64. cairo writes them as SVG gradients under clips, and Word draws the faded gradients of
    draw.io. A radial gradient whose circles do not hold each other keeps its soft mask.
  • In weasysvg.portable(), a blur and a drop shadow need no soft mask: the levels of the blurred coverage
    are filled one over the other, each with a constant opacity, within 1/64 of the blur. The contours come from
    the blurred coverage of the shape (levelsets.py), and a rect takes its exact blur, at any size. cairo
    keeps them vector in a flat SVG, and Word draws the shadows of draw.io, Mermaid and blockdiag.
  • A clip path that one PDF clip could not express (a <use>, several children, an even-odd child, bounding box
    units) becomes one path of all its fills, each subpath turned so that nonzero fills their union. It was an alpha
    soft mask, which Word does not draw and a printer may rasterize. Text, images, a child with a clip of its own
    and a path that crosses itself keep the soft mask. Tux and the Inkscape logo now clip without one.
  • In weasysvg.portable(), the blur of a <use> of one shape takes the exact blur of that shape, in any paint:
    the glare of Tux, a shape filled with a gradient, needed a soft mask.
  • In weasysvg.portable(), a mask of one gray or translucent shape is a clip to the shape and an opacity of the
    element. A mask without a white area, or empty, hides the element without a soft mask.
  • The flat SVG of weasysvg-convert writes the opacity of a group as an opacity. cairo writes it as a mask of
    one rect over the whole drawing, and Word draws only some masks.

Fixed

  • Clip path children that turn opposite ways unite, as in Chromium. WeasyPrint added them to one nonzero path,
    which cut a hole where they overlap.
  • The flat SVG of weasysvg-convert keeps the shape of each glyph. cairo writes a glyph as its outline hinted at
    the size it is drawn: a bold label of 24 px came out wider and heavier in every viewer. The flat SVG is now
    read from a PDF eight times as large, its size still the SVG's own.
  • A rect without a width or a height casts no shadow and no blur, as in Chromium: in a portable PDF its stroke
    made a dot.

Full changelog: v0.4.0...v0.5.0

v0.4.0

Choose a tag to compare

@github-actions github-actions released this 03 Nov 13:00
Immutable release. Only release title and notes can be modified.

Added

  • weasysvg-convert, a command line converter from SVG to PDF, PNG or a flat SVG. It accepts the options
    of rsvg-convert that pandoc passes, so a service can install it under that name: pandoc then draws
    <foreignObject>, filters and masks in a PDF and in the PNG fallback of a DOCX. Only data: URLs are
    fetched. The PNG needs the convert extra (PDFium), the flat SVG needs pdftocairo of Poppler.
  • weasysvg.portable(): inside it, a mask of opaque white areas, then opaque black ones, is a clip path
    instead of a soft mask. cairo turns a soft mask into an image when it writes SVG, and lost the D2 cuts
    under the edge labels. The flat SVG of weasysvg-convert keeps them vector, and Word draws them.

Full changelog: v0.3.1...v0.4.0

v0.3.1

Choose a tag to compare

@github-actions github-actions released this 22 Oct 13:33
Immutable release. Only release title and notes can be modified.

Fixed

  • In a <foreignObject>, collapsible white space on either side of a <br> goes, as at the end and the start
    of any line in Chromium. draw.io writes HTML labels indented, with a newline before each <br/>: WeasyPrint
    kept a space at the end of each line, which moved centered text left by half a space, and white space after
    the last <br/> made an empty line, which moved a label centered in a flex box up. A label of pdf-exporter
    sat 10.8 px left and 5 px above Chromium, and now matches it.

Full changelog: v0.3.0...v0.3.1

v0.3.0

Choose a tag to compare

@github-actions github-actions released this 22 Oct 09:52
Immutable release. Only release title and notes can be modified.

Changed

  • Each SVG image is one Form XObject: an <img>, an inline <svg>, a CSS image and an <image> inside an
    SVG. Its /BBox is the viewport, and the page paints it with Do under the matrix that places it, so a
    PDF reader such as PDFBox or PyMuPDF finds each diagram with its size and position. WeasyPrint drew the
    operators straight into the page. The form is no transparency group: it renders as before. The same
    image drawn again at the same size, on the pages or within one group, reuses its form. The
    form_xobject argument of install() turns it off.
  • An SVG background or list marker is clipped at its viewport, as in Chromium: content past it showed.
    An inline <svg> with overflow: visible still shows its overflow, and its form box grows to let it through.

Fixed

  • SVG text in the generic monospace family alone takes 13px as its medium size, as in Chromium and Firefox:
    MathJax writes accented letters as <text font-family="monospace"> with no size, and they came out 1.23
    times too large. A size from a keyword reads the monospace table of Chromium (large is 16px), and a size
    relative to one (2em, 50%, smaller) scales by 13/16 where the family turns monospace and back where
    it leaves. A family list (monospace, serif) and an absolute length keep their size.
  • A tagged PDF (pdf/ua-1, pdf/a-2a) of an SVG with a <foreignObject> renders: WeasyPrint failed on
    the tags of its HTML, which belong to no box of the page. The Figure of the image marks the whole SVG.
  • A hole in a blurred shape keeps its size: it grew by one sample on each side, so the ring of the lens in
    the OpenStreetMap logo came out thinner and its shadow lighter than in Chromium.
  • The shapes of a theme-aware draw.io export are white, as in Chromium, not black. weasysvg reads the CSS of
    an SVG as Chromium does when it prints: @supports blocks apply when their condition holds, custom
    properties and var() substitute, also in presentation attributes, and light-dark() takes its light
    color. A value that var() makes invalid is unset, and an invalid color declaration or attribute is
    ignored, so the attribute or the parent value applies. WeasyPrint painted each of these black. The css
    argument of install() turns it off.

Removed

  • Python 3.10, which reaches its end of life in October 2026. Pillow 13.0.0 requires Python 3.11, and it
    fixes the vulnerabilities that Dependabot reports for Pillow 12.3.0 in the lock file.
  • PyPy. No known user runs weasysvg on it, and its CI job took five minutes, the longest of all.

Full changelog: v0.2.0...v0.3.0

v0.2.0

Choose a tag to compare

@github-actions github-actions released this 21 Oct 19:10
Immutable release. Only release title and notes can be modified.

Added

  • paint-order on shapes, markers, text and text decoration lines, as Chromium paints it: the stroke of
    the boxes in the OSI model from Wikimedia (paint-order: stroke) shows its outer half only. The
    paint_order argument of install() turns it off.

Changed

  • Text decoration lines take their thickness and place from the font tables, as WeasyPrint draws
    text-decoration on HTML text, so SVG text and HTML text in one PDF get the same lines. The underline and
    the overline run straight through the glyphs: WeasyPrint skips no ink. This departs from Chromium on
    purpose (a tenth of the font size, ink skipped) and matches resvg; text-decoration-thickness: auto
    allows both.

Fixed

  • A dash that runs through the start point of a closed shape is one dash, with its line join, as in
    Chromium. PDFium drew two dashes that met there: a light seam across the stroke and a notch on its
    outer edge, and no miter at the corner of a rectangle. Every dash keeps its place.
  • A translucent stroke over a fill shows the fill under it, as in Chromium. WeasyPrint filled and stroked
    in one operator, a knockout object in PDF, so the stroke showed the white behind the shape.
  • Text laid out one character at a time draws its decoration lines character by character, as Chromium,
    Safari and Firefox do: each character carries them over its advance and its letter-spacing, and the room
    textLength, x and dx add between characters stays bare. Text with several y or dy values gets its
    lines too, under each character on its own baseline. On a textPath the last character carries its
    letter-spacing as well.
  • An arc of a zero radius in path data draws the straight line SVG asks for: WeasyPrint sent it off by the
    current point, which broke every slice of a Highcharts pie.
  • The text after a child of a <tspan> is drawn: WeasyPrint dropped it, so Don<wbr/>e in a Polarion chart
    label read "Don".
  • Text in a <foreignObject> sits on the line where Chromium and Safari put it: Blink rounds the font
    metrics to whole px and paints the baseline on a whole px. Mermaid labels sat up to 1 px lower.
  • The noise of feTurbulence is computed at 2 px per CSS px: at 1 it came out blurred once printed or
    viewed at 2x.
  • A blurred rect leaves no seam where its blurred edges meet its middle.
  • The space that ends a text moves no anchor: text-anchor="end" put the text a space too far left.
  • A wide arc in path data is drawn as curves of at most 90 degrees: WeasyPrint drew a half circle as one
    curve, which bulged the band of the flag of Brazil.
  • text-anchor places each text chunk of SVG 2 as one: a chunk starts only at a character with an absolute
    x or y, and the text after a positioned <tspan> belongs to it. WeasyPrint anchored that text with the
    <text>, so the second line of a label in a Wikimedia OSI model diagram sat off its center.
  • A blurred filled shape other than a plain rect fades as in Chromium: its coverage is blurred and drawn
    as a mesh of shaded triangles. The levels of the nearest edge left rings in the tail, made a shape
    thinner than a few deviations dark and sharp (the lens ring of the OpenStreetMap logo, the chest of
    Tux) and left white glints where the grown paint had holes (the Inkscape logo).
  • The blurred edge of a plain rect follows the Gaussian to about one level of 255, its tail included:
    one shading over 8 deviations showed bands in PDFium, and the paint stopped at 2.15 deviations.
  • Any blurred content made of paths fades as in Chromium: a group, a stroke, several shapes and colors.
    Its coverage per color is read back from the PDF operators, blurred and drawn as a mesh of shaded
    triangles, each color mixed in by its share. The levels of the nearest edge drew a mitered corner on a
    blurred square (resvg filters-filter--on-the-root-svg) and a stroke in steps with a hard inner edge.
    Text, images, gradients, patterns, masks, blend modes and dashes keep the levels.
  • The gradient of a blurred shape pads past the box of the shape: the pattern WeasyPrint makes of it
    repeated there, which cut the shadow of the map in the OpenStreetMap logo with hard straight edges.

Full changelog: v0.1.0...v0.2.0

v0.1.0

Choose a tag to compare

@github-actions github-actions released this 21 Oct 09:47
Immutable release. Only release title and notes can be modified.

Added

  • weasysvg.install() patches WeasyPrint 70.x to render SVG close to Chromium: <foreignObject> with
    the list markers of Chromium and the white space of empty inline boxes collapsed as there,
    conditional processing, vector filter chains (blur, exact for a plain rect, color matrices and
    transfer functions included), masks set before the path, pattern and gradient fixes, mix-blend-mode, unclipped
    opacity groups, marker orientation, text baselines from font metrics, text-anchor per text chunk, filters, masks and clips on text, circles of the right length, dots of zero-length dashes in place in PDFium, negative dash offsets of odd dash arrays, decorations placed as in Chromium with underlines that skip the ink of the glyphs (laid out whole or character by character), font-size
    keywords, relative font sizes inherited as computed, ex from the x-height of the font, and Chromium font selection. No SVG content is rasterized: the PDF stays vector.
  • feTurbulence, drawn as an image of the noise, and feComposite arithmetic.
  • systemLanguage reads the lang of the document where the image sits, else install(language=...),
    en by default: a German document shows the German branch of a <switch>.
  • Arabic laid out one character at a time, on a path or down a column, takes its joining forms and runs
    right to left, numbers in it read left to right.
  • Upright CJK in vertical text hangs from the vertical origin of its font (VORG, vmtx), as in Chromium
    and resvg, and rotate turns it about that point.
  • Python 3.10 to 3.14 and PyPy 3.11, the versions WeasyPrint 70 supports.
  • Releases on PyPI (pip install weasysvg), with publish attestations, and on GitHub, with signed
    build provenance of the same files.
  • Upstream tracking for WeasyPrint upgrades (UPGRADING.md): probes of the gaps weasysvg covers, a
    snapshot of the internals it hooks, a bench baseline, and a weekly run against the newest WeasyPrint.
  • A visual regression corpus of 115 SVGs with Chromium and resvg references, and a bench that
    compares weasysvg with Chromium.
  • corpus/COMPARISON.md and the interactive corpus/comparison.html on GitHub Pages: every corpus SVG
    in Chromium, resvg, WeasyPrint alone and with weasysvg, each panel at 2x as lossless WebP, sharp when zoomed.
  • Each corpus SVG names its reference (Chromium, resvg, or both). The verdicts say
    whether a rendering is better than it, matches it, comes close or differs, and what differs, in a
    table with an icon per status. The comparison page shows the SVG in your own browser too, and a swipe
    between any two renderers.
  • make review: the renderings of each corpus SVG compared region by region at twice the resolution of the
    references, with sheets of the cells that differ, to judge the verdicts at full size.
  • The visual image has Trebuchet MS, the first font of Mermaid, as Safari has it: Mermaid labels fit their
    boxes in the references.
  • Chromium prints the references with the Accept-Language list of a desktop Chrome in English
    (en-US,en): systemLanguage matches English as there.
  • corpus/fonts: the fonts the resvg tests ask for. The references, weasysvg, resvg and the browser panel of the comparison page all use them.