Skip to content

v0.13.0 - bookmark outline fixes + invoice discount column

Choose a tag to compare

@danmolitor danmolitor released this 31 Aug 16:38
· 96 commits to main since this release

Three defects in bookmark handling — one of which shipped real PDF corruption (duplicate outline entries) — plus a per-line discount column in the invoice template. Also the release where the VS Code extension joins the shared version line.

Fixed

Engine — three bookmark defects, one shared root cause.

  • Duplicate PDF outline entries. A bookmarked container that both overflows a page and carries visual styling wrote its outline entry twice — two identical /Outlines entries and /Count 2 for a single bookmark prop. A genuine document defect, not a reporting one. The overflow path emits a zero-height marker so unstyled views can't lose their bookmark; styled views also built a wrapper carrying the same bookmark, and collect_bookmarks recurses — so it found both. Wrappers no longer carry bookmark; the marker is the sole carrier on every path.
  • Bookmarks on content that fits its page now appear in LayoutInfo. The marker was only emitted on the overflow path, so consumers walking layout output for Bookmark nodes silently missed most bookmarks. The PDF was never wrong here — the outline was always complete — this was a LayoutInfo blind spot. PDF output is byte-identical after the fix (verified by hash: 8f41f7e9fb58b3cb before and after).
  • The marker reports nodeType: "Bookmark" instead of "None". The unset node type fell back to kind.to_string() and leaked the DrawCommand::None variant name — a value never present in ElementNodeType.

All three came from the same divergence: two hand-maintained copies of the marker-building code. Both paths now share one bookmark_marker() helper.

⚠️ Behavior notes

  • Outline destination moves for one narrow case. Markers now sit at the view's outer top edge, so every container path resolves a bookmark to the same coordinate. Only an unstyled, overflowing, breakable view with top padding or border shifts (Letter, 36pt margin, padding 20: destination Y 736.00 → 756.00). Styled views are unchanged.
  • Consumers matching nodeType === 'None' (which the types never permitted) must switch to 'Bookmark'. "None" was an accident of the fallback, not a semantic role.

Added

@formepdf/templates — per-line discount on the invoice template. A decimal fraction off the line total, matching taxRate's units rather than introducing a second convention. Applies before tax. The items table grows a fifth column; discounted lines render -15% in green, undiscounted ones an em dash. invoiceExample expanded from 5 to 19 line items (three pages), so the fixture actually exercises header repetition and page breaks — the template's main job.

VS Code extension — version jump 0.10.5 → 0.13.0

The extension now tracks the monorepo version. It bundles the engine and @formepdf/renderer wholesale, so its independently-drifting number told you nothing about what was in the VSIX. No features were skipped; 0.11.x and 0.12.x simply never shipped as extension releases. Also picks up the sidebar fixes: bookmarked containers no longer show None in the component tree, and bookmarks on fitting content appear at all.

How these were found

Dogfooding, continued from v0.12.1: structural regression baselines (via pdf-testkit) extended to the shipped templates. The catalog template's bookmarks tripped all three engine defects — the duplicate-entry bug had survived because every pre-existing bookmark test asserted contains, which two identical entries satisfy perfectly well. The new test asserts counts.

Full changelog

engine · @formepdf/core · @formepdf/templates · vscode

All other @formepdf/* packages got version-alignment bumps to 0.13.0. No functional changes.