SDK · Template model and renderer

PDF template designer SDK

A template here is a document model, not a PDF with boxes in it. Your users design one visually against the data fields you declare; the SDK measures it, paginates it, applies a brand and renders a vector PDF — in the browser, in Node, or behind a service you run.

Four pages, made by the engine in a browser: selectable text, vector charts, a table header repeated across a break, and the fonts the theme declared embedded in the file.

§ 01Four things called a template

“PDF template” means four different things. This is the fourth.

Half the confusion in this category is vocabulary. Before anything else, here is where BroadPaper sits — and where it genuinely is not the answer.

A fillable PDF form An AcroForm or XFA file with named fields, filled by a library and flattened. Fixed geometry: the boxes are where they are, and a value too long for one is a value too long for one. Not this. If that is your requirement, use a form-filling library.
An HTML template printed to PDF Handlebars or JSX rendered to markup and put through headless Chromium. Flexible, and the browser owns the page breaks — which is why “page 3 of 7”, repeated table headers and keeping a heading with its paragraph are all a fight. Not this either, though BroadPaper can use Chromium as a backend.
A coordinate layout text(x, y) and rect() in jsPDF or PDFKit. Exact, fast, and yours to measure — everything that depends on knowing the height of what you are about to draw is code you write. Excellent for a receipt. Painful past page one.
A document model BroadPaper A tree — page setup, regions, sections, blocks — with bindings, conditions and repeaters, stored as JSON and laid out at render time. One template makes a two-page document for one record and a fourteen-page one for the next, and re-brands with one option.
§ 02What is in one

The parts a template is made of.

Page and regions

Size, orientation, margins, and the fixed regions — header and footer — that recur on every page and are where page numbers, running titles and regulatory footers live.

Sections and blocks

Section, row, column, then blocks: heading, text, field, KPI, table, key/value list, chart, image, logo, divider, signature, page number, callout, card, button. A tree your users cannot break, rather than a canvas they can.

Bindings

A reference to a field you declared, optionally through a formatting pipe — portfolio.value | currency. Checked against your schema as it is written, with no eval anywhere in the evaluation path.

Conditions

A block, or a whole section, that appears only when the data says so — which is how one template serves the client who has a mortgage and the one who does not, without an empty heading over nothing.

Repeaters and tables

Bound to a list. Tables carry sorting, limits, totals, column widths, conditional formatting and a header row that repeats after every page break.

Charts

Drawn as SVG and translated to vector paths, with labels lifted out and drawn as real text — so they stay sharp at any zoom and searchable in the finished file. Interactive on screen, identical in print.

Theme tokens

A template that names tokens rather than fixed colours re-brands with one option. Fonts are declared once in theme.fonts, and the same bytes measure the layout and draw the file.

Break controls

Explicit page breaks, keep-together, keep-with-next, orphan and widow control. The vocabulary of typesetting rather than of CSS printing, because the paginator is ours.

It is stable, deterministic in key order and round-trips losslessly through JSON.stringify — typically 20–60 KB for a multi-page report. Unknown block types and unknown props survive a round trip, so a template saved by a newer host is not damaged by an older one reading it. The document model →

§ 03The expensive part

Pagination is the product. Everything else is drawing.

Anybody can put text on a page. The cost is in the second page: knowing the height of something before you place it, keeping a heading with what it introduces, carrying a table's header across a break, and printing “page 3 of 7” when the 7 depends on choices not yet made.

BroadPaper never lets the browser or the PDF engine decide. A section is measured once as a continuous galley; a pure paginator assigns clip windows to pages; both backends are handed pages that are already decided. Point the canvas at the same measurer the PDF uses, with the theme's fonts embedded, and the break your user approved on screen is the break in the file.

Long documents are where this stops being theory. A table of ten thousand rows is measured a batch at a time because a measuring page is not infinite; a page's tree is rendered once and cropped per page rather than rebuilt; and the page count is known before anything is drawn, so a service can refuse a document that is too long without paying for it first.

One galley, measured once
cut 1
cut 2
3 pages, already decided
1
2
3
render-guard.ts Node
import { layoutPaginated } from "@broadpaper/forme";

// Laying out and drawing are separate calls, because the page count is
// known before the expensive half. A service can refuse a document that
// is too long without having drawn any of it.
const layout = await layoutPaginated({ template, data, theme, dataSources, registry });
if (layout.paged.totalPages > MAX_PAGES) throw new TooLong(layout.paged.totalPages);

// draw() takes an optional page range, which is how one layout becomes
// several files that still agree about the breaks and the totals.
const { pdf } = await layout.draw();
§ 04The finished file

What the PDF guarantees.

  • Vector, not pixels. Text is text, charts are paths, rules are strokes. Nothing is screenshotted on the way out.
  • Your fonts, embedded. Declared once in the theme; the same TrueType bytes measure and draw.
  • Accessible on request. tagged, pdfUa and pdfA are options, with the title, author, subject and language written from your metadata.
  • Reproducible. Pass now and the same inputs give the same bytes — which is what makes a document testable.
  • Split, when it has to be. One layout can be drawn as several files that still agree about the breaks and the totals, so page 251 still says “251 of 1,300”.
  • Made where you choose. Browser, Node, a container behind HTTP, or a .NET client calling that container.
render.ts Browser or Node
import { renderPdfPaginated } from "@broadpaper/forme";
import { createRegistry } from "@broadpaper/blocks";

const { pdf, pages, warnings } = await renderPdfPaginated({
  template,                    // the JSON your user designed
  data,                        // this record, from your database
  theme,                       // colours, type and the embedded fonts
  dataSources,                 // the schema it was designed against
  registry: createRegistry(),

  // Reproducible: the same inputs give the same bytes.
  now: asAt,
  locale: "en-GB",
  currency: "GBP",

  // Accessibility and archiving, when procurement asks.
  tagged: true,
  pdfUa: true,
  pdfA: "2b",
  metadata: { title: "Annual statement", author: theme.name, lang: "en-GB" }
});
Page one of an exported PDF: a cover, a summary paragraph and a line chart of portfolio value drawn as vector paths
Fig. 1aPage 1, as the engine produced it.
Page two of the same PDF: an allocation donut chart, a holdings table whose header row repeats after the page break, and a risk section
Fig. 1bPage 2. The holdings table carries its header across the break.
§ 05Licensing

Free to evaluate, with no trial clock.

Everything works unlicensed — every block, both PDF backends, the render service, unlimited documents and unlimited developers. The only difference is that generated PDFs carry an evaluation mark.

There are two licences and they unlock identical software. They differ in who the application you build may be shown to: your own organisation, or your customers. That is a contractual distinction, deliberately not a technical one, because detecting it would need the telemetry this product exists without. Licences are signed certificates verified offline — no seats, no activation, no phone-home.

How licensing works →

Is this for you?

A good fit

  • Documents whose length depends on the data: statements, reviews, packs, schedules
  • Anywhere the document has to be accessible, archivable or reproducible for an audit
  • Products where the same template has to serve many brands
  • Reports with tables long enough that page breaks are a design problem
  • Teams who need the same file produced on a server and in a browser, breaking in the same places

Probably overkill

  • Filling in a fixed, existing PDF form — use a form-filling library
  • One page at fixed coordinates, with no wrapping and no growth
  • Editing or annotating somebody else's finished PDF
  • Documents that are really tables of data, where a spreadsheet is what people actually wanted

Questions

Is a BroadPaper template a PDF?
No. It is a JSON document model — page setup, regions, blocks, bindings and conditions — that becomes a PDF when it is rendered with data. That is why one template can produce a two-page document for one record and a fourteen-page one for the next, and why re-branding it does not mean re-drawing it.
Can it fill in an existing PDF form?
No, and it is worth being plain about it. If your requirement is “take this AcroForm and populate the fields”, a form-filling library is the right tool and BroadPaper is not. BroadPaper composes a document from a design and data; it does not edit somebody else's finished file.
Can templates use application data?
That is what they are for. You declare data sources with a typed schema; the designer shows those fields; a template references them by path. Expressions, conditions and table bindings are all checked against the schema as the user writes them, so a template that could not render is an error on screen rather than a blank in a file.
Does the PDF have selectable text?
Yes. Text is drawn as text with the fonts your theme declares embedded, charts are vector paths with their labels as real text, and rules and logos are vector. Nothing is rasterised on the way out, which is also why a page of charts does not weigh a megabyte.
Can it produce tagged, PDF/UA or PDF/A files?
Yes — tagged, pdfUa and pdfA are options on the render call, and the title, author, subject and language come from the template and your metadata. PDF/UA-1 needs an embeddable font, so declare one in the theme rather than relying on the standard faces.
Is the output deterministic?
Pass now and the same template, data and theme produce the same bytes. That is what makes a document diffable in tests and reproducible in an audit — and it is why the render options carry a clock at all.
When should I use a code-first PDF library instead?
When your developers own every layout and it rarely changes. The jsPDF guide walks through where the line actually falls; it is further along than most people expect.

Look at the file, not the feature list.

Four pages, made by the engine in a browser. Open it, select the text, zoom into a chart.