Two pagination models
A web page is a scroll. A PDF is a stack of fixed rectangles. Prince's public user guide (Prince 14 Paged Media, https://www.princexml.com/doc/14/paged/) treats that stack as the primary layout. You name pages, select :first, :left, :right, :recto, :verso, :blank, and :nth(). You put generated content into page-margin boxes. You float work to the top or bottom of a page, defer a float, attach footnotes, copy running heads with string-set, and force chapters onto a recto with break-before. Crop marks and bleed exist because the output is assumed to be trimmed paper, not a tab.
Chromium's print path is the opposite assumption. Layout happens in a viewport. Then the engine paginates the already-flowed document into paper sizes. @page size and margins work. Chrome 131 added CSS page-margin boxes with generated content (Chrome Developers, Add content to the margins of web pages when printed). That is a real improvement over the old browser-chrome header era. It is still generated content in sixteen boxes, not Prince's running-element, named-page, footnote, and page-float stack. If the document was designed as a web app, Chromium is the honest printer. If the document was designed as a book, Chromium is a compromise.
What Prince docs actually specify
Prince lists CSS Paged Media Module Level 3 as supported (https://www.princexml.com/doc/css-refs/). Default @page is Letter, 54pt margin. size accepts keywords plus portrait/landscape or raw lengths. margin-inside and margin-outside exist for a gutter. Page-margin boxes include the usual sixteen plus Prince-specific regions (@page-float-top, @page-float-bottom, @prince-overlay, @footnote).
Headers are not a second HTML string. They are content: counter(page), counter(pages), string(name), element(), or flow() inside @top / @bottom / corners. string-set copies heading text into running heads; a page-policy (first, last, first-except) picks which value wins on a dictionary-style spread. Named pages (page: chapter on a block, then @page chapter) change numbering style mid-document. -prince-page-group: start makes :first mean the first page of each chapter, not the first page of the file. :blank clears headers on the leftover verso after break-before: right.
Fragmentation is first-class: break-before / break-after / break-inside, plus widows and orphans (Prince initial value is 1, not the CSS default of 2). Flex and Grid are supported as of recent Prince versions, with documented holes (flex fragmentation, some Grid alignment). JavaScript is not interactive; scripts run before layout finishes. That is a typesetter that happens to parse HTML, not a headless Chrome with extra CSS.
@page chapter:right {
size: A4;
margin-inside: 3cm; @top-left { content: string(chapter-title); } @bottom-right { content: counter(page) " / " counter(pages); }
}
h1 { page: chapter; string-set: chapter-title content(); break-before: right; }
What Chromium actually does
Chromium print applies print media, then slices the flow into paper. You get format (letter, A4, and the usual set), landscape, scale, CSS-length margins, pageRanges, printBackground, and preferCSSPageSize. Headers and footers have two practical routes:
- CSS @page margin boxes (Chrome 131+): generated content only. counter(page) and counter(pages) work. You do not drop arbitrary DOM into @top-left. Flex/Grid inside a margin box is not the layout model.
- headerTemplate / footerTemplate: a separate HTML fragment painted into the reserved margin. Chromium does not inherit document CSS. Placeholders (pageNumber, totalPages, date, title) are classes or, on RelayPDF, named tokens. If margin.top is smaller than the fragment, the header clips. The engine will not grow the margin for you.
That is why invoice templates that look fine in a browser tab still need a print stylesheet, extra top/bottom margin, and a waitUntil that matches their fonts and XHR. Chromium will run the page JavaScript, load webfonts, and honor Flex/Grid the way the tab does. It will not restart page numbering in lower-roman for a preface, float a figure to the next recto, or keep a footnote cluster in @footnote. preferCSSPageSize true honors @page size; it does not turn Blink into Prince.
RelayPDF Chromium options use the same field names as the docs (https://relaypdf.com/docs/options): format letter / legal / tabloid / ledger / a0-a6, printBackground default true, waitUntil default networkidle0 (load, domcontentloaded, networkidle2 also accepted), timeout max 60000 ms, headerTemplate / footerTemplate with pageNumber, totalPages, date, title. URL sources must be public; private, loopback, and metadata hosts are rejected.
curl https://api.relaypdf.com/v1/pdf
-H "Authorization: Bearer pdf_live_..."
-H "Content-Type: application/json"
-d '{"html":"<h1>Invoice 1042</h1>","filename":"invoice.pdf",
"options":{"format":"letter","printBackground":true,
"preferCSSPageSize":true,"waitUntil":"networkidle0"}}'
--output invoice.pdf
Side by side
| Concern | Prince (CSS Paged Media) | Chromium print |
|---|---|---|
| Layout unit | Named pages and spreads | Viewport, then paper slices |
| @page size / margins | Full; inside/outside gutters | Yes; preferCSSPageSize for size |
| Running heads | string-set, running(), flow() | Margin boxes or headerTemplate HTML |
| Named pages / :blank / :recto | Documented, first-class | Limited or absent |
| Footnotes / page floats / bleed | Documented (Prince extensions) | Not the print API |
| JS / modern CSS in the body | Scripts before layout; Flex/Grid holes | Same as the tab |
| Wait for network | Not the model | waitUntil, timeout |
| Typical host | Self-license or a Prince host | Self-run Chrome or /v1/pdf |
When Prince is the right engine
Keep Prince, licensed locally or behind a Prince-hosting API, when the stylesheet is the product. Textbooks, statutes, catalogs, and journals are written against @page selectors. If you already have string-set chapter titles, page: frontmatter with lower-roman, break-before: recto, and a :blank rule, porting that to headerTemplate is a rewrite, not a flag. Same for page floats, @footnote, or crop marks and bleed.
A princexml alternative in that case is another Paged Media engine (Prince itself on a different host, or a different typesetter), not whatever prints HTML. Chromium will produce a PDF. The page breaks will not match the book you signed off.
When Chromium is enough
Most SaaS PDFs are not books. They are the HTML you already ship: invoices, receipts, dashboards, tickets, one-off reports. The CSS is Flex and Grid. Charts arrive after a network idle. Fonts are webfonts. Headers are a logo plus Page n of m. That document should be printed by the same engine that rendered the preview. Chromium is that engine.
Use a hosted Chromium printer when you do not want a browser process and a queue on every app node. RelayPDF is one: POST exactly one of html, url, markdown, or templateId as UTF-8 JSON (not Base64) to https://api.relaypdf.com/v1/pdf. Response is binary (default), url (24h download), or async with optional callbackUrl. Office files are a different route (POST /v1/convert). Merging existing PDFs is POST /v1/pdf/merge. See https://relaypdf.com/html-to-pdf and https://relaypdf.com/docs/pdf.
If the question is DocRaptor or RelayPDF as products, do not clone the table from this post. The live compare is https://relaypdf.com/compare/docraptor: both do HTML/URL to PDF; RelayPDF also treats Markdown, screenshots, Office, PDF tools, barcodes, zip, CLI, and MCP as first-class. That is an endpoint-surface note, not a claim that Chromium implements Prince @page machinery.
Limits
Chromium timeout on RelayPDF is 60 seconds. render_failed is not billed. You cannot fetch private URLs. You cannot assume @page named pages, Prince floats, or footnote regions will do anything useful. Chrome 131 margin boxes help for simple running text; they do not replace a typesetter. headerTemplate is a second document with its own CSS budget.
Prince is not a free-form browser. Interactive CSS is out of scope. Scripting after layout is a special case in their JavaScript chapter, not wait for networkidle0. Flex fragmentation is a documented exception. If your HTML is an app that paints into a canvas after three round trips, Prince is the wrong process even if you like its @page syntax.
Neither engine is described here as archival, healthcare, OCR, or signature infrastructure.
Pricing (verified)
RelayPDF prepaid wallet (https://relaypdf.com/pricing, checked 23 Aug 2026): HTML, URL, Markdown PDF or screenshot $0.015; template PDF $0.015; AI generate $0.05; LibreOffice $0.04; wkhtmltopdf $0.025; tools $0.005. Failures never billed. Credit does not expire. $5 signup credit, no card. Pack bonuses 5/6/10/15 percent. Prince and DocRaptor list prices are on their sites, not here.
Pick the engine, then the host
If the stylesheet is CSS Paged Media, stay on a Paged Media engine. If the stylesheet is a web page, print with Chromium. For the Chromium path, POST the HTML you already have: https://relaypdf.com/html-to-pdf. For the hosted-Prince versus hosted-Chromium product surface, https://relaypdf.com/compare/docraptor. Options reference: https://relaypdf.com/docs/options.