Repository navigation
Rows that cross a page continue on it. Tall table and grid rows split across pages instead of leaving the rest of the page empty, flex rows end below their deepest column, and the standard fonts can be embedded without a conformance claim, subset and at half the memory. All packages share the line: engine (crates.io forme-pdf), every @formepdf/* npm package, formepdf (PyPI), forme-go (tag v0.29.0), and the VS Code extension.
Addresses #189, #190, #191, #195, #197 and #201.
Security
quick-xml0.41 (RUSTSEC-2026-0194): an SVG element with many attributes was parsed in quadratic time, so a crafted SVG could stall a render (20,000 attributes took 2.7 s, now 50 ms).miniz_oxide0.8 drops the unmaintainedadler(RUSTSEC-2025-0056). Output is byte-identical (#195)@formepdf/mcprequires@modelcontextprotocol/sdk1.32.0 or later, the first release outside five advisories in the MCP TypeScript SDK (GHSA-6qxp-vccf-f47h, GHSA-22jm-h49p-29qw, GHSA-6prh-2h8m-c8cw, GHSA-46jr-4wx8-v453, GHSA-rvq5-wwqv-78pq). It allowed 1.27.0 and later
Added
embedStandardFontsembeds Helvetica, Times and Courier as their metric-compatible substitutes from@formepdf/fonts-standardwithout claiming PDF/UA or PDF/A, so an ordinary document looks the same in every viewer (poppler-based viewers draw non-embedded Helvetica bold as regular). The substitutes are subset and layout does not change. It is a<Document>prop in every adapter, an option of@formepdf/html, of Python'srender_htmlandDocument,EmbedStandardFonts()in Go, and--embed-standard-fontsonforme-html. Without the fonts registered, nothing is embedded and a warning says so (#197)
Changed: these move existing documents
- Tall table and grid rows split across pages (#201). A table row or CSS grid row taller than the space left on the page starts in that space and continues on the next page, its cells side by side below the repeated table header, the way a browser prints it. It used to move to the next page whole, which could leave most of a page empty, and a row taller than a page overflowed it. A short row still moves whole: if any cell cannot begin where it is under its own widow and orphan control, the row moves with it.
wrap: falseon a row (break-inside: avoidon a<tr>) keeps it whole as before - A flex row that crosses a page ends below its deepest column (#201). When the column that carried the row onto the next page was also the deepest, as a long column usually is, the row ended at the next-deepest one and what followed was drawn over it. The catalog demo template lost all eight products on its second page to this, and the report template's last card drew its accent bar short. A regression from the 0.24.0 parallel-fragmentation work
- A table row counts the vertical margins of its cells' children (#201). It measured them without, so a cell whose content had margins drew a box too short for it and overlapped the next row: an HTML invoice's discount row overprinted the subtotal above it. Near a page end, the row broke inside a cell that had measured as fitting and the render panicked with "table cell broke but no rollback checkpoint was taken"
- A Page's
backgroundColoris painted (#189), full-bleed and behind all content, on every page the Page's content runs onto. It was ignored without a warning
Fixed
- SVG and other leaf drawings restore opacity and transforms before painting the elements that follow (#190)
- Greedy line breaking no longer draws a trailing line break as a missing glyph, in plain or styled text (#191)
- The Liberation substitutes embedded for Helvetica, Times and Courier under PDF/UA, PDF/A or PDF 2.0 are subset to the characters used instead of embedded whole, about 400 KB a face: a one-page invoice with PDF/UA goes from 653 KB to 51 KB (#197). A document with charts or form fields keeps regular Helvetica whole, since their text is drawn outside the subset's view
- Fonts passed to
@formepdf/htmland Python'srender_htmlcost about half the WASM memory: they were decoded, encoded back to base64 for the engine and decoded again, with every copy kept for the whole render. TwelvestandardFonts()faces took a render to 27 MiB; now 14 MiB (#197). The engine gainsrender_with_options_and_fonts(and its layout twin), taking fonts as bytes - Page background images are marked as artifacts in tagged output. Drawn unmarked, they made a PDF/UA document fail validation (content neither tagged nor an artifact)
SVG and greedy-breaking fixes contributed by @amruthpillai in #192 and #193.