Skip to content

PDFs are vector: bin/pdf.mjs + the button's print route in Chromium, 3× Flate raster in Safari - #17

Merged
grunion-ai merged 3 commits into
mainfrom
pdf-vector
Sep 8, 2026
Merged

PDFs are vector: bin/pdf.mjs + the button's print route in Chromium, 3× Flate raster in Safari#17
grunion-ai merged 3 commits into
mainfrom
pdf-vector

Conversation

@grunion-ai

Copy link
Copy Markdown
Owner

What

Both PDF exits rasterised. The PDF button painted each slide to a 2× canvas and stored JPEG .92; agents screenshot slides and stitched PNGs with ImageMagick. Soft text on every rescale, no link annotations, and the editor's HUD shipped inside a LinkedIn carousel once.

  • bin/pdf.mjs deck.html [out.pdf] drives the deck's own print pipeline (beforeprint#print) through Chromium's print engine with @page{size:W H px} and zoom 1. One slide-sized page per slide, text stays text, fonts embed, every href is a /Link annotation, the HUD is hidden by the deck's @media print. Self-gating, exit 1: pages == slides, ratio == W/H, #hud computed display none under print media, link count == linked rows.
  • The PDF button takes the same route in Chromium and removes the injected rule on afterprint, so a plain ⌘P is still the Letter paper path. Safari ignores px @page, so it keeps the in-file rasteriser at 3× with lossless FlateDecode pixels via CompressionStream (JPEG .92 only for a page whose deflate exceeds 6 MB, or an engine without it).
  • SKILL.md, README, llms.txt: agents make PDFs with bin/pdf.mjs only; screenshot stitching is an anti-pattern. decklet-pdf bin. library.html and deck.html rebuilt.

Why

A rasterised page blurs on every rescale (LinkedIn's document post, a viewer's zoom), carries no text and no links, and the HUD leak was only ever prevented by a hand-added style override in a per-deck script.

Evidence

Same seven-card 1080×1350 carousel, both ways:

raster (before) print engine (after)
size 1,609 KB 367 KB
text pixels embedded fonts, selectable
/Link annotations 0 3
HUD manual CSS override hidden by @media print, gated

WebKit run of the in-file rasteriser: 12 pages, 960×540 pt, all FlateDecode, page 2 decodes with pdftoppm.

Verification

node --test test/**/*.test.mjs
# tests 174  # pass 174  # fail 0  # skipped 0

New: test/pdf.test.mjs (4). Updated: gate's button-route and writer assertions, editor's arrow test hooks getImageData (the Flate path never calls toDataURL).

🤖 Generated with Claude Code

… the button takes that route in Chromium, Safari's raster is 3× Flate

Both exits rasterised: the PDF button painted each slide to a 2× canvas and stored JPEG .92; agents screenshot slides and stitched
PNGs with ImageMagick — soft text on every rescale, no links, and the editor's HUD shipped inside a LinkedIn carousel once.

- bin/pdf.mjs deck.html [out.pdf]: the deck's own print pipeline (beforeprint → #print) through Chromium's print engine with
  @page{size:W H px}, zoom 1. One slide-sized page per slide, text stays text, fonts embed, every href is a /Link annotation,
  HUD hidden by the deck's @media print. Self-gating (exit 1): pages == slides, ratio == W/H, #hud display none under print
  media, links == linked rows. 367 KB vs 1.6 MB on a seven-card carousel.
- The PDF button: Chromium (navigator.userAgentData) injects the px @page + zoom rule and calls print(); afterprint removes it
  so a plain ⌘P is still the Letter paper path. Safari ignores px @page, so it keeps the in-file rasteriser at 3× with lossless
  FlateDecode pixels via CompressionStream (JPEG .92 only for a page whose deflate exceeds 6 MB, or an engine without it).
  Verified in WebKit: 12 pages, 960×540 pt, decodes.
- Gate: test/pdf.test.mjs (inspect · pdf.mjs pass · pdf.mjs refuses a visible HUD · button route). editor arrow test hooks
  getImageData (the Flate path never calls toDataURL). SKILL.md / README / llms.txt: agents make PDFs with bin/pdf.mjs only;
  screenshot stitching is an anti-pattern. library.html and deck.html rebuilt.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@grunion-ai grunion-ai closed this Sep 8, 2026
grunion-ai and others added 2 commits September 8, 2026 12:35
…ilds with nspell; a build without it drops the SPELL0 flags and fails the self-hosting gate)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@grunion-ai grunion-ai reopened this Sep 8, 2026
@grunion-ai
grunion-ai merged commit 36491fb into main Sep 8, 2026
2 checks passed
@grunion-ai
grunion-ai deleted the pdf-vector branch September 8, 2026 19:42
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