Skip to content

Emit selectable Type0 text for TrueType-font widgets in AddWidgetVector - #3

Merged
tannevaled merged 1 commit into
mainfrom
feat/widget-truetype-selectable
Aug 3, 2026
Merged

Emit selectable Type0 text for TrueType-font widgets in AddWidgetVector#3
tannevaled merged 1 commit into
mainfrom
feat/widget-truetype-selectable

Conversation

@tannevaled

Copy link
Copy Markdown
Contributor

What

Implements the new painter.FacePainter seam on vectorPainter, so a widget tree whose active toolkit font is a TrueType/OpenType face (SetFont(NewTrueTypeFont(...))) now renders through AddWidgetVector as real, selectable Type0 PDF text — not a rasterised image or the wrong fallback font.

  • TextFace embeds the face's own sfnt bytes (glyph shapes and advances match the on-screen layout), sizes the run at the face's pixel size, and drops to the baseline by the face ascent.
  • Embedded faces are memoised per tree (each subset once); a face whose bytes don't parse degrades to the plain-Text fallback font rather than vanishing.

Previously only the built-in bitmap font produced selectable vector text; TrueType faces had no path to the text seam.

Proof (rsc.io/pdf round-trip)

TestAddWidgetVectorTrueTypeSelectable reparses the output with the independent rsc.io/pdf reader and asserts a TrueType-font widget label ("Hi") is:

  • a Type0 font with a CIDFontType2 (embedded TrueType) descendant,
  • shown with a Tj operator, its glyph codes <0001><0002> present,
  • recoverable as real text — the /ToUnicode CMap maps those codes back to "Hi",
  • not an image — no Do operator and no XObject resource on the page.

On-screen rendering unchanged

The change is confined to the vector painter's new TextFace method; the raster AddWidget / PixelPainter path is untouched, so on-screen output is pixel-identical.

Deps / coverage

Bumps go-widgets/toolkit -> v0.79.0 and go-widgets/painter -> v0.2.0 for the seam. Statement coverage stays 100% (embed, cache hit, early-return and unparseable-face fallback all covered). Builds on all 6 64-bit arches + wasm/windows/darwin.

🤖 Generated with Claude Code

vectorPainter renders bitmap-font widget text as selectable PDF text via
the painter Text primitive, but a widget tree whose active toolkit font
is a TrueType/OpenType face (SetFont(NewTrueTypeFont(...))) had no way to
reach it — the run either rasterised or fell back to the wrong font at
the wrong size.

Implement the new painter.FacePainter seam on vectorPainter: TextFace
embeds the FACE's own sfnt bytes (so glyph shapes AND advances match the
on-screen layout) and emits the run as real Type0 text at the face's
pixel size, dropping to the baseline by the face ascent. Embedded faces
are memoised per tree so each is subset once; a face whose bytes do not
parse degrades to the plain-Text fallback font rather than vanishing.

An rsc.io/pdf round-trip proves a TrueType-font widget label ("Hi") is
now a Type0 / CIDFontType2 embedded face shown with Tj — with its glyph
codes present and the /ToUnicode CMap mapping them back to "Hi" (real
selectable text) — and that no image XObject is placed (not rasterised).

Bumps go-widgets/toolkit to v0.79.0 and go-widgets/painter to v0.2.0 for
the seam. 100% coverage retained.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@tannevaled
tannevaled merged commit a1dfd59 into main Aug 3, 2026
1 check passed
@tannevaled
tannevaled deleted the feat/widget-truetype-selectable branch August 3, 2026 11:15
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