json2pdf

Render PDFs (statements, tax reports, terms and conditions) from a JSON document and a pre-compiled template, in pure Go. The same renderer runs on the server or in the browser as a TinyGo WASM module.

Why

Documents like statements are important to the customer but cheap to regenerate. Storing the rendered PDF, or rendering it on the server on every request, is load and storage the main system need not carry. If the client holds the template (cached once) and fetches only the JSON, it can render the PDF itself.

Template format

A template is PDF content-stream syntax with %% directives for the dynamic parts. The runtime is a linker, not a layout engine: it substitutes text, aligns %%Text slots using core-14 font metrics, paginates %%Table rows, and writes the PDF object table.

Directive Meaning
%%json2pdf 1 Format version; must be line 1
%%Page A4 [landscape] or %%Page <w> <h> Page size in points (A3, A4, A5, Letter, Legal)
%%Font <res> <name> Bind a resource name (F1) to a core-14 font (Helvetica-Bold)
%%Master <name> Start a page master; rendering starts from first
%%Table <path> top= bottom= rowHeight= continue=<master> … %%EndTable Repeat the enclosed row for each element of the array at path; rows that do not fit continue on the named master
%%Text [align=left|right|center] x= y= font= size= <text> One line placed by alignment, which needs font metrics

Any other line is raw content copied to the page. {{path}} substitutes a value from the data (dotted paths; inside a table row the row is searched first, then the root). {{path|money}} formats a number as 1,234.50. {{page}} and {{pages}} are the page numbers. Inside a table row, coordinates are relative to the row origin.

Text is WinAnsi encoded, which covers Latin-1, £, € and typographic punctuation.

Pagination rule

Condition Outcome
Next row origin (top - rowHeight × n) ≥ bottom Row is placed on this page
Otherwise, rows remain Page is finished; rendering continues on the table's continue master, which must contain a %%Table for the same path
Rows remain but the continuation master has no table for the path Error (rows are never dropped silently)
Array is empty Table emits nothing

Usage

go install git.bytestone.uk/hum3/json2pdf/cmd/json2pdf@latest
json2pdf render -template examples/statement/template.ps -data examples/statement/data.json -o statement.pdf