Blog/Engineering

An Open HTML-to-PDF Rendering Test Suite

Download reproducible HTML and PDF fixtures for pagination, SVG charts, fonts, mixed page sizes, and visual regression testing.

Engineering··4 min read

An HTML-to-PDF comparison is easier to evaluate when readers can inspect the inputs and outputs. A screenshot of a successful invoice does not show whether a renderer can repeat table headers, handle a row taller than a page, or honor different page dimensions in one document.

This article introduces a downloadable fixture suite with HTML sources, generated PDFs, a rendering manifest, and a regression harness. The examples and synthetic data are MIT-licensed in the archive. Download the example projects, fixtures, and results.

What the suite contains

FixtureWhat it exercisesWhat to inspect
72-row service reportMultipage table paginationRepeated headers, intact short rows, final total
Deliberately enlarged reportA known layout regressionThree pages becoming four
Oversized table rowA row taller than printable heightHow the engine fragments unavoidable overflow
SVG chartVector shapes and labelsMissing elements, clipping, readable labels
Font fallbackAn unavailable first-choice fontFallback glyphs, changed metrics, accented text
Mixed page sizesPortrait document with landscape appendixPer-page width and height
Three invoice layoutsSmall business-document variantsCurrency, line items, missing invoice number
Clean and degraded scansRecognition-sensitive inputText-layer recovery and failed phrase checks

The oversized row is intentionally impossible to keep whole on one page. Its purpose is to show a limit, not to declare a renderer defective for breaking it. Similarly, the fallback fixture intentionally requests an unavailable font. It tests observable fallback behavior rather than successful embedding of a custom font.

Reproduce the baseline

Install Node 22 or newer, Chromium, and Poppler. Extract the archive and run:

npm ci
npm run fixtures
npm run compare
npm test

Set CHROMIUM_PATH if your Chromium executable is elsewhere. The source uses inline content and system fonts; no remote CSS or image host is required. Record your font environment when comparing results across machines.

render.mjs writes the HTML and PDF fixtures and records SHA-256 hashes, file sizes, page counts, and page dimensions. compare.mjs renders the shared report using Puppeteer and React PDF. regression.mjs verifies a separately generated clean report and rejects the intentionally broken one.

The rendering manifest records the original local run with Chromium 151.0.7922.173 and Node 26.7.0. PDF hashes identify those particular output files; they are not expected to stay constant when a renderer changes metadata or object ordering.

Inspect artifacts before interpreting scores

Download the standard report, broken report, SVG chart, font fallback output, and mixed-page document.

For the mixed-page fixture, the manifest records each page's actual dimensions. For the report, text extraction checks the first item, last item, and grand total. Raster comparisons then detect layout differences that those semantic checks miss.

The recorded clean comparison had zero changed pixels at the test settings. The broken candidate had four pages instead of three and exceeded the visual threshold. The CI article explains why the negative test is part of the suite.

What has been compared so far

The corpus has a Chromium baseline across the HTML fixtures. The shared service report also has a separately authored React PDF implementation. The React PDF versus Puppeteer article discloses its timing method, font differences, and header behavior.

This release does not contain measured Prince, wkhtmltopdf, or Gotenberg results. It does not rank untested engines. The fixture sources are supplied so another engine can be evaluated without relying on undocumented marketing screenshots.

API-based OCR and basic compression results were measured on RelayPDF staging and are labeled separately from the local renderer results. They demonstrate concrete outcomes on the provided files, including OCR failure on a degraded scan and compression that slightly enlarges image-based PDFs.

Add an engine without changing the question

Use the same HTML source and record every adaptation required by the engine. If an engine needs a different stylesheet for a feature, publish that stylesheet rather than silently replacing the input.

Record the engine version, operating environment, fonts, page options, and whether startup is included in timing. Separate local rendering time from network service latency. Run enough repetitions for the claim you intend to make and publish individual measurements, not only a selected best run.

Use distinct criteria for visual layout, searchable text, file size, and specialized conformance. One combined score can hide a tradeoff that matters more than the average to a particular reader.

Use the corpus in your own application

Replace the generic service rows with your difficult production-shaped examples, using synthetic or appropriately permitted data. Add a long description, an empty section, a large chart, and non-English text in a font you actually deploy.

Keep an approved baseline under review and compare candidate outputs against it. Generating both baseline and candidate from the same changed template can produce a green test that proves nothing about regression.

For specific layout problems, the page-break guide, custom-font guide, and chart guide explain the corresponding controls. For managed rendering of the same HTML, see HTML-to-PDF and the print options reference.

Ready to generate?

One API for HTML, Markdown, URLs, and Office. REST, SDK, CLI, or MCP.