Document 12 of 14 · spikes/render/README.md
A feasibility check for the riskiest claims in ARCHITECTURE.md, run before
any template was built. Not the product; the invoice here is a stand-in with
the runtime's four jobs cut down to sixty lines.
./fetch-fonts.sh # the two OFL faces, cut to static instances
node render.mjs # prints invoice.html to Letter and A4 through the DevTools protocol
python3 check.py invoice-letter.pdf invoice-a4.pdf
render.mjs drives headless Chromium the way the Cloudflare Browser Run
endpoint does: navigate, wait for html[data-ready], then print with
preferCSSPageSize and printBackground. No npm dependencies; Node 22's
global WebSocket is the CDP client.
| Claim | Result |
|---|---|
| One HTML file prints Letter and A4 by switching a class | Letter 612 by 792 pt, A4 594.96 by 841.92 pt, from the same document. The @page size cannot be selected by a class, so the runtime swaps a <style id="page-size"> element with the @page rule; template.css carries only the default. |
| The renderer never captures fallback fonts | data-ready is set only after document.fonts.ready; document.fonts.check confirmed both faces before the print. |
| The overflow guard measures real boxes | Clean at the defaults; a 60-character client name flags client_name. It also flagged the note box at first, because the box was one line too short for its own default text. That is the guard doing its job on the designer. |
| Fonts embed in the PDF | Only with static instances. With the variable TTFs Chromium wrote 24 Type 3 fonts (glyphs as outlines, no font program, 149 KB). With static instances cut by fonttools it wrote Type 0 fonts with FontFile2 streams and the file fell to 39 KB. Templates ship static instances per weight; the site shell may use the variable face on screen. |
Fonts and PDFs are not committed; the script regenerates them.