Skip to content

ShowGlyphs, NewFont, radial shadings; straight colour under a soft mask - #48

Merged
tannevaled merged 1 commit into
mainfrom
feat/vector-canvas-support
Oct 19, 2026
Merged

tannevaled merged 1 commit into
mainfrom
feat/vector-canvas-support

Conversation

@tannevaled

Copy link
Copy Markdown
Contributor

What

Three additions a layout engine needs to print what it laid out as vectors, and one conformance fix:

  • Page.ShowGlyphs(x, y, []Glyph) — glyph ids at positions the caller computed, each Glyph carrying the text it stands for (its /ToUnicode entry, ISO 32000-1 §9.10.3). One TJ array per baseline (§9.4.3); a numeric correction only where a position departs from the font's /W advance by ≥ 1/1000 em, computed from absolute positions so the pen never drifts (tested: 40 glyphs each 0.3/1000 em off give 9 corrections, final error < 1/1000 em). A change of Y starts a new segment. A glyph id the font does not have, a non-finite position or a zero font size is refused before anything is written.
  • NewFont(*opentype.Font) — embed a font another component already parsed (go-webengine measures with *opentype.Fonts and does not keep their bytes). Same cmap requirement as LoadFont; subset embedding as before.
  • Radial shadings — Shading.Radial with R0, R1: a type 3 shading (§8.7.4.5.4). Radii must be finite and ≥ 0, not both 0 ("nothing shall be painted").
  • Fix: straight colour under a soft mask. DrawImage wrote image samples premultiplied (as image.Image.RGBA and *image.RGBA.Pix hold them) next to an /SMask without /Matte — and "if this entry is absent, the image data shall not be preblended" (§11.6.5.3, Table 145). Half-transparent pixels came out darkened towards black. They are now divided back by their alpha. Opaque images are byte-identical.

Why

go-slides draws each slide's PDF page as one bitmap (10 MiB for 100 slides against marp-cli's 0.8 MiB). go-webengine is getting a vector painter (paint.PaintVector, pdfcanvas) that draws the same layout into pdfkit; these are the pdfkit pieces it needs. With them the 100-slide deck is 758 KiB, text selectable.

Bibliography (opened)

ISO 32000-1:2008 (Adobe's copy, PDF32000_2008.pdf): §9.4.3 Text-Showing Operators (TJ), §9.10.3 ToUnicode CMaps, §8.7.4.5.3–4 Type 2 and Type 3 shadings, §11.6.5.3 Soft-Mask Images (Matte).

Tests

glyphs_test.go: TJ encoding (no numbers at the font's own advances; one correction for a 2 pt shift; no drift), segments on Y change, ToUnicode text recorded, refusals leave the page untouched, NewFont embeds a parsed font that rsc.io/pdf finds (FontFile2 + ToUnicode mapping H and i), radial shading type and coords, radii refusals, straight samples for *image.RGBA and *image.NRGBA. Coverage 100 %; gofmt and vet clean.

MINOR: new API → v0.15.0 after merge.

🤖 Generated with Claude Code

… soft mask

A layout engine that prints what it laid out needs to put its own glyphs
where it measured them, in the fonts it measured with. pdfkit could only
re-derive positions from a string (Text, TextShaped) and load a font from
bytes the engine no longer has.

- Page.ShowGlyphs(x, y, []Glyph): glyph ids at caller-computed positions,
  each with the text it stands for (the /ToUnicode entry, ISO 32000-1
  9.10.3). One TJ array per baseline (9.4.3), a correction only where a
  position departs from /W by a thousandth of an em, from absolute
  positions so the pen never drifts. Refuses, before writing, a glyph id
  the font lacks and a non-finite position.
- NewFont(*opentype.Font): embed a font another component already parsed.
- Shading.Radial, R0, R1: type 3 radial shadings (8.7.4.5.4); radii must be
  finite, >= 0, not both 0.
- DrawImage: the colour samples under an /SMask are now written straight.
  image.Image's RGBA and *image.RGBA's Pix are premultiplied; with no /Matte
  the samples "shall not be preblended" (11.6.5.3, Table 145), so a
  half-transparent edge came out darkened towards black.

Coverage stays at 100 %.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@tannevaled
tannevaled merged commit 5c3686b into main Oct 19, 2026
1 check passed
@tannevaled
tannevaled deleted the feat/vector-canvas-support branch October 19, 2026 08:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant