Skip to content

Add go-widgets → PDF bridge (raster + vector) - #2

Merged
tannevaled merged 1 commit into
mainfrom
feat/widgets-to-pdf
Aug 2, 2026
Merged

Add go-widgets → PDF bridge (raster + vector)#2
tannevaled merged 1 commit into
mainfrom
feat/widgets-to-pdf

Conversation

@tannevaled

Copy link
Copy Markdown
Contributor

Summary

Adds a go-widgets → PDF bridge so any github.com/go-widgets/toolkit widget tree can be "printed"/exported to a PDF. A read-only dependency on toolkit + painter is added; go-widgets is not modified.

API

  • (*Page) AddWidget(root toolkit.Widget, rect Rect, opts *WidgetOptions) errorraster. Lays the tree out at the target size, renders it through a painter.NewPixelPainter + root.Draw, and places the RGBA result as an image XObject at rect. Prints any UI exactly as it draws on screen.
  • (*Page) AddWidgetVector(root toolkit.Widget, rect Rect, opts *WidgetOptions) errorvector. A painter.Painter (+ painter.Clipper) implementation that emits PDF vector operators (fills, strokes, rounded-corner Béziers, 1×1 pixels, clip) instead of pixels. Text drawn through the toolkit's built-in bitmap font reaches the painter's Text primitive and becomes real, selectable Type0 PDF text. Requires WidgetOptions.Font.
  • WidgetOptions{Theme, Scale, Font} with defaults (DefaultLight, DefaultWidgetScale = 2).

Both levels achieved

Raster (must) and vector (stretch) are both implemented — the painter.Painter interface is only 7 methods + optional 2-method Clipper, small enough to implement cleanly, and the toolkit's bitmap font already delegates to p.Text for any non-PixelPainter back-end, which the vector painter turns into selectable PDF text.

Proof

Tests build a real widget tree (Container + prominent Button + Label), export to PDF, and re-open with the independent rsc.io/pdf reader as an oracle:

  • raster: asserts the image XObject /Subtype /Image, its Width/Height, /Im0 Do in the content stream, and that decoded samples contain painted (non-black) pixels;
  • vector: asserts a Type0 font resource plus text-show (Tj), fill (f), stroke (S), Bézier (c) and clip (W) operators in the content stream.

Gate

  • Pure-Go, CGO_ENABLED=0, go 1.26.4.
  • Exact 100% statement coverage.
  • go vet clean, gofmt clean (new files).
  • Builds on the 6 64-bit arches (amd64, arm64, riscv64, loong64, ppc64le, s390x) + js/wasm + darwin + windows.

🤖 Generated with Claude Code

Bridge any github.com/go-widgets/toolkit widget tree onto a PDF page so a
UI can be "printed"/exported to PDF.

- AddWidget (raster): lays the tree out at the target size, renders it
  through a go-widgets/painter PixelPainter into an RGBA buffer, and places
  the result as an image XObject at the target rect. Prints any UI exactly
  as it draws on screen.
- AddWidgetVector (vector): a painter.Painter (+ Clipper) implementation
  that emits PDF vector operators (fills, strokes, rounded-corner Béziers,
  1x1 pixels, clip) instead of pixels. Text drawn through the toolkit's
  built-in bitmap font reaches the painter's Text primitive and becomes
  real, selectable Type0 PDF text. Needs a WidgetOptions.Font.
- WidgetOptions{Theme, Scale, Font} with sensible defaults (DefaultLight,
  DefaultWidgetScale=2). Read-only dependency on toolkit + painter; no
  changes to go-widgets.

Tests build a real widget tree (Container + prominent Button + Label),
export to PDF and re-open with rsc.io/pdf as an independent oracle,
asserting the image XObject dimensions + painted pixels (raster) and the
Type0 font + text/fill/stroke/clip operators (vector). Exact 100%
statement coverage; go vet clean; builds on the 6 64-bit arches + wasm +
darwin + windows.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@tannevaled
tannevaled merged commit 39b42f5 into main Aug 2, 2026
1 check passed
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