CONSTRUX
Engineering

Writing a PDF by hand, and what it caught

Printing a web page is not an answer when the document carries a content hash. Building the writer found the report putting raw minor units in front of an adjudicator.

PDF is the format an adjudicator, an insurer or a court asks for, and it was the one thing the exporter could not produce.

"Print the web page" is not an answer when the document carries a content hash. A browser's print pipeline re-flows the content — different fonts, different widths, different breaks — so what was hashed and what was printed are not the same artefact. The hash is the whole point of the document; a process that quietly changes the bytes underneath it produces a file that fails its own verification.

So backend/src/export/pdf.ts writes the file directly: objects, content streams, and the byte-offset cross-reference table that is the only part of the format a reader is genuinely strict about.

Text uses the standard 14 fonts, which every reader has and none of which need embedding — that is what makes it possible with no dependency. It is also why Adobe's published AFM character widths are in the repository as data. Without real widths, lines break in the wrong place and text runs off the page, and an approximation is not good enough for a document going in front of a tribunal.

Every page carries the client's name, the document reference, the page number against the total, and the content hash — so a page separated from its bundle still says what it belongs to.

The thing worth reporting is what building it found. Rendering the payment report properly exposed that it was putting raw minor units in front of the reader: a figure a hundred times too large, in the one document whose whole purpose is to be relied upon. The formatter existed. The report was not calling it.

← All engineering notes