Changing your PDF library without rewriting everything

The part of your program that writes documents is smaller than it feels. Your page designs, your data, your business rules and your screens do not move. What moves is the handful of files that turn a row of data into a page — and the ideas in them are the same ideas here.

What you replace, and what you leave alone

Picture your invoicing program. There is the part that decides what goes on an invoice: which customer, which lines, which totals, which rules apply in which country. And there is the part that puts it on paper: this text here, that table there, the logo top left, the barcode bottom right.

Only the second part changes. The first part — the one nobody wants to touch, the one that took years to get right — is not involved at all. It goes on handing over the same figures. Something else draws them.

Nothing else in your house moves either: your database, your web server, your schedules, the way documents are stored and sent, your tests, your build. Your customers get the same document.

What is the same

Every library that writes PDF is built on the same handful of ideas, because the format has them and nobody gets to invent others. A page of a size. A typeface put into the file. A block of text that flows and breaks where it runs out of room. A table with columns and rules. A picture, placed and scaled. A barcode. A field somebody fills in. A signature. A set of notes about the document.

You already know all of that. You will find all of it here, under names of our own choosing, doing the same job. A person who has written documents for a living before is productive on the second day, not the second month.

What is different

We did not copy anybody's way of calling things, and we will not pretend otherwise. Our names are our own, our way of laying out a page is our own, and your programmers rewrite the calls.

What they do not rewrite is the design. The page you have today, with the logo at that height and the table starting at that line, is described again — it is not invented again. That is the whole reason this page exists.

Two other honest differences, said now rather than discovered later. Our engine is called from Rust and from Python; if your program is written in another language, the way in for it is planned for a future version, and today you would go through the server instead — you send it what to draw and it hands you the document, which works from any language at all. And the way you ask for things is written down in full on this site, page by page, with a document to download for each: you can read the whole of it before you commit to anything.

How long it takes

Nobody can tell you to the day without seeing your program, so here is how to work it out yourself. Count your document models — not your documents. An invoice is one model even if you send four hundred thousand of them; an invoice, a credit note, a delivery note, a statement and a reminder are five.

What decides the length is that count, how much of your layout is a table running over many pages, and how deeply the drawing is tangled into the rest of your program. Nothing else weighs on it: your data, your rules and your screens stay where they are.

Whatever the size, do this first: take your single most complicated document, write it again with us, and put the two side by side. If that one comes out right, the rest is repetition. If it does not, you have found out at the beginning rather than at the end.

The list, without gaps

Here is what the engine writes today. It is the list to hold your own requirements against, line by line.

Text
your own typefaces carried inside the file, only the letters actually used travelling with it, text that stays selectable and searchable, text that flows over columns and pages, and text you can measure before you draw it.
Tables
columns you size or let the engine size, rules and borders side by side, cells that span, headings repeated at the top of each page, a table that runs over as many pages as it needs.
Images
JPEG, PNG and BMP, placed, scaled and cut to shape; other picture formats are planned for a future version.
Colour and printing
the colours a screen uses and the four inks a press uses, ICC colour profiles, spot inks, overprint and trapping, crop and bleed marks.
Barcodes and square codes
the usual shop and logistics barcodes, QR codes, Data Matrix, PDF417 and Aztec.
Forms
fields somebody types into, ticks, chooses from a list, and signs.
Standards
PDF/A-1 through PDF/A-4 for keeping a document readable, PDF/X-4 for the press, PDF/UA-1 and PDF/UA-2 for a document readable without seeing the page, Factur-X for the electronic invoice — the one built on the European standard EN 16931 — and full tagging underneath all of it.
Signing
PAdES signatures, with your certificate staying on your machine and never on ours.
Protection
scrambling under a 256-bit key, a password before the first page shows, and permissions per action.
Reading a PDF you already have
open a file written by another program, read its pages, notes, links, fields, bookmarks, layers and structure, and take a whole page of it into the document you are writing.
The file itself
every usual page size and any size you name, a chosen squeeze, a chosen format version, the same figures giving back the same bytes every time, and — the one worth a page of its own — a document written so its first page shows before the file has finished arriving. How that works, and what it saves

Planned for a future version: a way in for the languages that call in C, the JBIG2 and JPEG 2000 picture formats, and automatic substitution of a missing typeface.

Technically

Two front ends over one core: a Rust crate and a CPython wheel built against the stable ABI, both offline, both deterministic, both pinned to a version and resolvable from your own artefact store. The Python surface is the Rust surface — same object families, same call names transposed to Python conventions — so a proof of concept in Python transfers to Rust, or the other way round, without a redesign.

There is no optlist-style string API and no emulation of anybody else's call surface: you build typed objects and hand them to a document. That is a real rewrite cost on the call sites and a real gain on everything after — the compiler catches at build time what a string API catches at run time on the customer's machine.

Layout is measurable before it is committed: ask the width a run of text will occupy, ask the largest size at which a title still fits a given width, ask a flowing block how far it got and get back where it stopped. That is what lets a migration be verified against the old output rather than eyeballed.

Every worked example on this site exists in Rust and in Python, and a growing share of them a third time as a JSON request the server answers. A script re-runs all three and compares the results, byte for byte where the two ways of asking write the same instructions, dot for dot where they do not. That is your parity harness, already written, and it is the shape to copy for verifying your own migration: the old file and the new one, page by page, compared by a program rather than by somebody scrolling.

Third front end, no code at all: send the server a JSON request describing the document and it hands back the PDF. That is the route for a program written in neither Rust nor Python, and it is the route to start on while the real migration is planned.

Where to look next

Everything the engine puts on a page, group by group The engine inside your own program Asking for a document over the internet, with nothing to install The electronic invoice, and the dates the law sets

See the prices See the examples