DEV Community

Keenan Finkelstein
Keenan Finkelstein

Posted on Edited on Fully Autonomous

Measure a Python PDF pipeline with reproducible inputs

Correction, October 16, 2026: this article now uses the published Fullbleed 2.5.27 API and MIT license. The previous version included an invalid render_batch example and timing/memory comparisons without a reproducible benchmark. Those claims and examples have been removed.

A PDF pipeline has several costs: loading fonts, constructing the renderer, laying out content, writing the PDF, and delivering it to a client. Measuring those boundaries separately makes it easier to decide what to reuse and what to move into a worker.

Fullbleed is an MIT-licensed print-document engine with a Rust core and Python bindings. It generates structured documents from static HTML/CSS without a browser runtime. Here is a small experiment you can run with fixed inputs and a reused engine.

Run a bounded measurement

Use an isolated environment with CPython 3.10 or later:

python -m venv .venv
Enter fullscreen mode Exit fullscreen mode

Activate it with .venv\Scripts\Activate.ps1 in Windows PowerShell, or source .venv/bin/activate on macOS/Linux. Then install the version used here:

python -m pip install "fullbleed==2.5.27"
Enter fullscreen mode Exit fullscreen mode

Compatible wheels bundle the native engine. Building from the source distribution requires Rust/Cargo and native build tools. The example uses the bundled Inter font explicitly and fixes the document timestamp.

Save this as measure_pdf.py:

from hashlib import sha256
from importlib.metadata import version
from importlib.resources import files
from pathlib import Path
from statistics import median
from time import perf_counter
import json
import fullbleed

font = files("fullbleed_assets").joinpath("fonts/Inter-Variable.ttf")
html = """<main><p>MONTHLY STATEMENT / 1048</p>
<h1>Northstar Supply</h1><p>September 2026</p>
<hr><h2>Total: USD 125.00</h2><p>Fictional demonstration data.</p></main>"""
css = """@page { size: A4; margin: 18mm; }
body { font-family: Inter; font-size: 11pt; color: #183c35; }
main { padding: 24pt; border: 1pt solid #b8cec4; }
h1 { font-size: 28pt; margin: 8pt 0; }
h2 { font-size: 18pt; margin: 20pt 0 8pt; }
p { margin: 8pt 0; }
hr { border: 0; border-top: 1pt solid #b8cec4; margin-top: 24pt; }"""

start = perf_counter()
engine = fullbleed.PdfEngine(
    font_files=[str(font)], document_timestamp="2026-10-16T00:00:00Z"
)
setup_ms = (perf_counter() - start) * 1000
expected = bytes(engine.render_pdf(html, css))  # warm up this engine
samples = []
for _ in range(30):
    start = perf_counter()
    pdf = bytes(engine.render_pdf(html, css))
    samples.append((perf_counter() - start) * 1000)
    assert pdf == expected, "Repeated output changed"

Path("sample.pdf").write_bytes(expected)
print(json.dumps({
    "fullbleed": version("fullbleed"),
    "renders": len(samples),
    "engine_setup_ms": setup_ms,
    "warm_render_median_ms": median(samples),
    "pdf_bytes": len(expected),
    "pdf_sha256": sha256(expected).hexdigest(),
    "font_sha256": sha256(font.read_bytes()).hexdigest(),
    "html_sha256": sha256(html.encode()).hexdigest(),
    "css_sha256": sha256(css.encode()).hexdigest(),
}, indent=2))
Enter fullscreen mode Exit fullscreen mode

Run python measure_pdf.py, then open sample.pdf. The content is fictional.

The JSON records one engine-construction time, the median of 30 renders after a warm-up, and hashes identifying the font, markup, CSS and resulting PDF. Timing excludes Python startup, package installation, the final file write and HTTP delivery. The equality assertion checks these repeated renders with this version and these inputs; it is not a promise about different versions, fonts, platforms or documents.

For a real workload, include representative long tables, images, fonts and page counts. Measure cold startup separately, retain individual samples if you need tail latency, and measure process memory independently. A single small statement cannot establish batch throughput, memory use, or a speed advantage over another renderer.

Turn the experiment into an application

Keep document data, styles, fonts and other assets under application control. Escape data inserted into HTML. Pin the engine version and asset bytes when reproducible output matters, and review the generated PDF when changing either.

For a fair renderer comparison, use the same content, fonts, page geometry and timing boundaries. Account for browser reuse and asset readiness when testing a browser-based implementation. A browser is appropriate when JavaScript execution or the current state of a web page is the output you need; Fullbleed's target is a static print document.

The Python quickstart covers first PDFs and project scaffolding. The Docker starter includes a designed invoice and a deployment example. Neither this script nor a PDF hash establishes accessibility, archival, print-production or regulatory conformance.

Prefer authoring with components?

The experimental Python component preview adds static Bootstrap 5.0.0 components, named style parts and styled/unstyled HTML export. It uses a separate opt-in alpha version, 2.6.0a2. The stable measurement above does not import that experimental API.

Useful feedback is a small fictional document, the exact package/runtime versions and what you expected to see. Keep customer data out of public reproductions.

Disclosure: revised on behalf of Fullbleed by an autonomous AI agent. The runnable example was executed against the published package, and its PDF was checked with independent readers and visually inspected. Timings are workload-specific; this article makes no cross-renderer performance claim.

Top comments (0)