A PDF layout and rendering engine — and an HTTP service around it — that turns a JSON request into a PDF binary. Built on react-pdf and the Yoga layout engine.
yarn && yarn start
curl -X POST http://localhost:9000/ \
-H "Content-Type: application/json" \
-o hello.pdf \
-d '{"title":"Hello","pages":[{"children":[{"text":"Hello, World!"}]}]}'See docs/getting-started.md for the full quickstart.
Creating a PDF is hard. Editors handle fixed content well, but anything dynamic — a variable-length product list, changing titles and names — quickly runs out of road. This service aims to be:
- Data-driven for both content and structure.
- Easy to modify on the fly — no recompilation, no template language to learn beyond JSON + a templating syntax.
- Standards-based layout — flexbox via Yoga.
- Service-shaped — POST a JSON body, get a PDF binary back.
The killer feature here is re-entrant headers and footers on variable-length content. If you have a table whose header row should repeat at the top of every page the table spans, none of the headless-browser converters handle that cleanly. They're also fragile under upstream Chromium updates — when something breaks, you're at the mercy of a third party. This service owns the rendering path so it can be debugged and fixed in-house.
The end goal is a graphical template editor. That only works if the layout is data, not code — otherwise a developer is always in the loop for visual changes. Keeping data and structure separable also lets a backend produce the data while a designer owns the structure, with neither blocking the other.
Human-targeted docs live in docs/:
- Getting started — install, run, first PDF.
- HTTP API — endpoints, request envelope, errors, limits.
- Element reference — every element type and property (page, view, text, image, link, list, shadow).
- Templating —
{{...}}syntax, scope rules, sandbox limits. - Styling — class precedence, supported CSS, common patterns.
- Fonts — bundled defaults, on-demand Google Fonts loading.
- Deployment — Docker, environment variables, security notes.
The memory-bank/ directory contains a terser, LLM-targeted snapshot of the same material; humans will get more out of docs/.
src/
index.ts entrypoint (winston + http listener)
Server.ts routing (GET /, GET /fonts, OPTIONS, POST /)
PdfController.ts POST handler — buffer body, validate, render, stream PDF back
validatePdfRequest.ts AJV against src/resources/PdfRequest.json
fontManagement.ts registry + Google Fonts loader
wire/ PdfRequest + ElementDeclaration types (schema source of truth)
factory/ ElementFactory + per-type Elements/
helpers/ small utilities
fonts/ bundled font files (Roboto, Teko, Noto Sans)
docs/ human-targeted documentation
memory-bank/ LLM-targeted project snapshot
MIT — see LICENSE.
See Contributing.md and the Code of Conduct. Issues and pull requests are tracked on GitHub.
- react-pdf — the underlying renderer.
- Yoga layout engine — the flexbox implementation react-pdf uses.
- PDF format reference — for when you need to understand what's coming out the other end.