PDF page raster
A produced PDF can be turned into one PNG per page, so a page can be reviewed as an image: by a human outside a PDF viewer, or by a model with vision. The rasterizer is a copy of pdf.js shipped inside the binary and driven in the Chromium the renderer already requires — no native dependency, no cgo, and nothing added to the cross-compiled build.
The path a page takes
Rasterizer.RasterizePDFputs the PDF bytes in a blob store keyed by an unguessable ID, exactly as a document to render is stored.- It borrows one browser from the render pool — one borrow for the whole
document, not one per page, because
--render-pool-max-totaldefaults to 1. - It navigates that page to the harness on the internal loopback server, which fetches the PDF back from the blob store by ID.
- Per page, the harness draws into a canvas at the document origin and the
driver captures it with
Page.captureScreenshotclipped to the canvas box, then hands that page to the caller before drawing the next. - The browser goes back to the pool and the blob store entry is dropped.
Pages are handed over rather than returned as a slice, so a caller that writes each image out — into a bundle directory, or into a tar on an HTTP response — holds one PNG at a time instead of the whole document's worth. A caller that refuses a page stops the capture: there is no point drawing the rest into a destination that has stopped accepting them.
The bytes travel over the loopback listener rather than through
Runtime.evaluate or a data: URL, both of which would push megabytes through
the CDP connection (--factory-rpc-buffer-size is 8096 by default).
The harness is served by the internal server (see
Internal callback server), not from the public
static directory: the validate and debug paths never build the public HTTP
server, so a harness served from /static/ would be missing exactly where it is
needed most.
Scale and page size
The harness scales the pdf.js viewport by scale = dpi / 72, PDF user space
being 72 dpi. The default is 96 dpi (scale 4/3), which is the browser's own
1:1 and renders an A4 page at roughly 794 by 1123 pixels.
Token cost of an image grows with the square of the scale while legibility for a model stops improving well before 150 dpi, so raising the scale is rarely worth it. Total pixels per page are capped; a page above the cap is rendered at a reduced scale, and the reduction is reported per page so a manifest can carry it. Truncation is never silent.
Raster scale is a rendering concern only. It is not the PrintToPDF scale, and
changing it never changes how the document paginates.
The pinned rasterizer
The bundle lives in app/pdfrender/server/rasterassets/ and is embedded with
go:embed; only harness.html is committed there. The minified modules are
copied out of tester/node_modules/pdfjs-dist by make raster-assets, which
every target that compiles the server depends on.
pdfjs-dist is pinned in tester/package.json, so the version is chosen in
one place and pnpm-lock.yaml verifies its integrity — the tester's own copy
and the server's harness cannot drift apart, and there is no second version
file or checksum list to keep in step by hand. The Apache-2.0 notice travels
inside the minified modules, so nothing separate has to be shipped or served.
This does make the Go build depend on pnpm install — not on the tester's
bundler. Parcel is still never on the path to a binary, so the binary and the
tester bundle stay independently cacheable, and CI pays nothing extra: the
check job already installs tester/node_modules for the tester checks.
pdf.js and PDFium do not agree pixel for pixel, so the pinned version is the rasterizer of record. A page image is reproducible against that version and no other — worth knowing before anyone compares two page images byte for byte.