Invoices
Paged invoices, and the PDF/A-3 container an EU e-invoice needs.
An invoice PDF serves two readers. A person reads the layout. A tax authority's software reads structured data attached inside the same file. takumi-pdf writes both halves.
A plain invoice
Line items flow across pages. A footer carries the page counter. measure returns the band's height, so the bottom margin always clears it:
import { } from "@takumi-rs/helpers";
import { , } from "takumi-pdf";
const = (
< ="flex w-full justify-between px-12 text-[10px] text-[#6b7280]">
<>Invoice {.}</>
< ="flex">
Page < ="pageNumber" /> of < ="totalPages" />
</>
</>
);
const = await (["Inter"]);
const { } = await (, { : "a4", });
const = await (< ={} />, {
: "a4",
: { : 48, : + 32, : 48, : 48 },
,
,
});Mark each line row break-inside: avoid so a row never straddles a page. The Tailwind class is break-inside-avoid:
import "takumi-pdf";
< ="flex break-inside-avoid gap-3 pt-3 text-xs">
< ="flex-1">{.}</>
< ="w-[100px] text-right">{.}</>
</>;Wrap the totals block the same way. It is the part a reader most expects to see whole.
Electronic invoices
Factur-X and ZUGFeRD are the same file twice: a readable invoice, plus the invoice as XML attached inside it. The standards pin how that file is packaged.
| The standard requires | The option |
|---|---|
| A PDF/A-3 container | pdfa: "3b" |
| The XML attached by name | attachments |
An fx: XMP block | metadata.xmp |
| A modification date per file | modificationDate |
| Embedded subset fonts | always on |
Each attachment's modificationDate falls back to metadata.creationDate. Setting the document date once covers both.
import { } from "takumi-pdf";
const = await (, {
: "en",
: "3b",
: "ua1",
: {
: "Invoice INV-2026-0042",
: "2026-08-06",
: [],
},
: [
{
: "factur-x.xml",
: ,
: "text/xml",
: "Factur-X 1.0 MINIMUM invoice data",
: "data",
},
],
});The renderer validates while it writes. A document that cannot conform fails with the violated rule, instead of producing a file a tax portal rejects.
Takumi does not build the invoice XML. Use a library for the profile, or emit the elements the profile lists. The e-invoice example writes a MINIMUM profile by hand.
Validating the result
Two validators cover the two halves. Both need Java.
veraPDF checks the container:
verapdf --flavour 3b --format text invoice.pdf
verapdf --flavour ua1 --format text invoice.pdfMustang checks the Factur-X half, both the XML and the packaging:
java -jar Mustang-CLI.jar --action validate --source invoice.pdfMustang finds the profile through the fx: XMP block. Takumi writes the values and the schema description PDF/A demands, so the two cannot drift. It does not check the values: a wrong profile name surfaces in Mustang, not at render time.
Accessible invoices
Public-sector buyers often require an accessible invoice. tagged: "ua1" validates the structure tree against PDF/UA-1 during the render. See PDF/A for what it needs.
Reproducible bytes
A fixed metadata.creationDate makes the output byte-identical across runs. The same invoice hashes to the same value, which suits archival storage and golden tests.
Last updated on