Most billing systems reach for a PDF drawing library and end up hand-placing coordinates for every line of every table. Ours prints HTML. The markup that renders the on-screen document preview is the same markup handed to headless Chromium, which prints it to A4. Layout stays in CSS, pagination stays in the browser's fragmentation engine, and the file the customer opens cannot drift from what the issuer saw.
One document, two builders
An issued document renders only from its frozen snapshot: business details, customer details, VAT rate and logo as they were at issue time, never live settings. The authenticated path builds the HTML in the client, where locale, accent color and the QR code for the verification link already live. The public share link has no session to read those from, so a server-side mirror of the same builder produces byte-comparable markup with server-resolved labels. Both call one render function, so the two paths can't disagree about what a document looks like.
Everything inlined, nothing fetched
The HTML that reaches Chromium is self-contained. Hebrew fonts are base64 @font-face sources, the business logo is a data URI, the QR image is a data URL. That is not only a packaging convenience. The render input is client-supplied, so the page runs behind request interception that continues data: and about:blank and aborts everything else. Without it, a crafted would execute inside a browser sitting on the server's own network: an SSRF hole with a PDF for an exfiltration channel.
Two print passes
Page margins are set as real print margins (20mm top and bottom) rather than body padding, because CSS fragmentation applies a block's padding before the first fragment and after the last one only. Padding would leave interior pages flush against the paper edge.
The signature and QR block should sit at the bottom of its actual last page. Its exact height is not knowable ahead of the render, since text extraction cannot see the QR at all. So the position is found empirically: a binary search over the block's margin-top, eight iterations, each one re-printing the page and inspecting the result. Page count comes from pdf-lib, and the last page's text content from pdf.js, to detect a wasted trailing page. The search converges toward the natural margin from whichever side the first render landed on. The winning margin is left applied to the DOM, and a second print adds the per-page footer through Chromium's own footer template, where page number and total are substituted per page.
Concurrency and guards
One Puppeteer browser is launched lazily and shared; each request gets a page that is closed in a finally. A three-slot FIFO semaphore caps concurrent renders, handing a freed slot directly to the next waiter so a fresh caller cannot slip past the cap. Imported historical documents carry no snapshot or signature and are rejected server-side, by document id, not just hidden in the UI. The public route is rate-limited per IP, since each hit is a full browser render. Every generated file is logged with its byte size and wall-clock duration.
Content by Slate

Top comments (0)