Skip to main content

Fast HTML to PDF — Rust-powered, 10x faster than WeasyPrint

Project description

ferropdf

Fast HTML-to-PDF for Python — Rust-powered, up to 13x faster than WeasyPrint.

CI PyPI Python License: MIT


Why ferropdf?

Most Python HTML-to-PDF libraries are slow. ferropdf is a native Rust engine exposed as a Python package via PyO3 — it renders complex invoices, reports, and dashboards in milliseconds, not seconds.

Feature ferropdf WeasyPrint
Speed (invoice) ~25ms ~330ms
GIL Released during render Held
Async-safe Yes (FastAPI, Django) No
Font subsetting Automatic Manual
Install pip install ferropdf System deps required

Benchmarks on Linux x86_64, median of 20 runs, reusable engine with font cache.


Install

pip install ferropdf

Pre-built wheels for Linux (x86_64, aarch64), macOS (x86_64, ARM), and Windows (x86_64).
Python 3.8 – 3.13 supported.


Quick start

One-shot rendering

import ferropdf

# HTML string → PDF bytes
pdf = ferropdf.from_html("<h1>Hello, World!</h1>")

# With options
pdf = ferropdf.from_html(
    "<h1>Invoice</h1><p>Total: $1,234</p>",
    options=ferropdf.Options(page_size="Letter", margin="25mm"),
)

# Write directly to disk
ferropdf.write_pdf("<h1>Report</h1>", "report.pdf")

# From an HTML file
pdf = ferropdf.from_file("templates/invoice.html")

Reusable engine (recommended for servers)

from ferropdf import Engine, Options

engine = Engine(Options(page_size="A4", margin="20mm"))

# Font database is cached — subsequent renders are faster
pdf1 = engine.render("<h1>Invoice #1</h1>")
pdf2 = engine.render("<h1>Invoice #2</h1>")

API reference

Options

ferropdf.Options(
    page_size="A4",         # A4, Letter, Legal, A3, Tabloid, ...
    margin="20mm",          # CSS margin (mm, pt, px)
    base_url=None,          # Resolve relative paths in CSS
    title=None,             # PDF metadata
    author=None,            # PDF metadata
)

Engine

engine = ferropdf.Engine(options=None)
engine.render(html: str) -> bytes         # Render HTML to PDF bytes
engine.render_file(path: str) -> bytes    # Render from file

The engine caches fonts internally — create once, render many times.

Functions

Function Description
from_html(html, base_url=None, options=None) -> bytes Render HTML string to PDF
from_file(path, options=None) -> bytes Render HTML file to PDF
write_pdf(html, output_path, base_url=None, options=None) Render and write to disk

Exceptions

All exceptions inherit from ferropdf.FerroError (itself a RuntimeError):

  • ParseError — HTML/CSS parsing failure
  • LayoutError — Layout computation failure
  • FontError — Font loading/resolution failure
  • RenderError — PDF generation failure

Framework integration

Django

# views.py
from ferropdf.contrib.django import PdfResponse

def invoice(request, pk):
    context = {"invoice_id": pk, "items": get_items(pk)}
    return PdfResponse("invoice.html", context, request=request)

PdfResponse renders a Django template to PDF. Pass inline=False to force download instead of browser preview.

FastAPI

# main.py
from ferropdf.contrib.fastapi import pdf_response

@app.get("/invoice/{id}/pdf")
async def invoice_pdf(id: int):
    html = templates.get_template("invoice.html").render(invoice_id=id)
    return await pdf_response(html, filename=f"invoice-{id}.pdf")

pdf_response is async — rendering runs in a thread executor with the GIL released, so it won't block your event loop.


CSS support

ferropdf uses industry-standard libraries for parsing and layout — not a hand-rolled engine.

Layout

Feature Status
Block layout Supported
Flexbox (flex-direction, flex-wrap, gap, justify-content, align-items) Supported
Tables (<table>, <thead>, <tbody>, <tr>, <td>) Supported
width, height (px, %, em) Supported
margin, padding (px, mm, em, auto) Supported
box-sizing: border-box Supported
CSS Grid Not yet
float Not yet

Typography

Feature Status
font-family (system fonts, fallbacks) Supported
font-size (px, pt, mm, em, rem) Supported
font-weight (normal, bold, 100–900) Supported
font-style (normal, italic) Supported
line-height Supported
text-align (left, center, right) Supported
@font-face Not yet

Visual

Feature Status
color, background-color (hex, rgb, rgba) Supported
border (width, style, color) Supported
border-radius Supported
opacity Not yet
box-shadow Not yet

Page

Feature Status
Multi-page documents Supported
Page sizes (A0–A10, Letter, Legal, Tabloid, B-series) Supported
Configurable margins Supported
PDF metadata (title, author) Supported
@page rules Planned

Architecture

ferropdf is built as a modular Rust workspace with 6 crates:

HTML string
  ↓  ferropdf-parse      (html5ever + cssparser)
DOM tree + Stylesheets
  ↓  ferropdf-style       (Mozilla's selectors crate — cascade, specificity, inheritance)
Style tree
  ↓  ferropdf-layout      (Taffy flexbox engine + cosmic-text shaping)
Layout tree
  ↓  ferropdf-page        (pagination into discrete pages)
Pages
  ↓  ferropdf-render      (pdf-writer — font subsetting, compression, embedding)
PDF bytes
Crate Role Key dependency
ferropdf-core Shared types: DOM, styles, geometry, errors
ferropdf-parse HTML & CSS parsing html5ever, cssparser
ferropdf-style CSS cascade, specificity, inheritance selectors (Mozilla)
ferropdf-layout Box layout + text shaping taffy, cosmic-text
ferropdf-page Pagination across pages
ferropdf-render PDF generation, font embedding pdf-writer, subsetter

Python bindings are via PyO3 + maturin.


Performance

Font subsetting + caching means:

  • First render: loads system fonts + builds cache (~100ms)
  • Subsequent renders: 15–30ms for typical documents
  • PDF sizes: small, because only used glyphs are embedded

The Engine class keeps the font cache alive — ideal for web servers handling many requests.


Examples

The examples/ directory includes:

  • basic.py — Hello world, styled report card, invoice
  • FastAPI app — Invoice, report, receipt, dashboard, letter endpoints
  • Django app — Same templates with Django views

Run the FastAPI example:

cd examples/fastapi_app
pip install fastapi uvicorn jinja2
uvicorn main:app --reload
# Visit http://localhost:8000/invoice/42/pdf

Development

# Clone and setup
git clone https://github.com/MoncefMak/ferropdf.git
cd ferropdf
python -m venv .venv && source .venv/bin/activate
pip install maturin pytest

# Build and install locally
maturin develop --release

# Run tests
cargo test --no-default-features   # Rust tests
pytest tests/ -v                   # Python tests

# Lint
cargo fmt --all --check
cargo clippy --workspace -- -D warnings
cargo audit

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ferropdf-0.2.8.tar.gz (94.5 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

ferropdf-0.2.8-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (2.5 MB view details)

Uploaded CPython 3.13manylinux: glibc 2.17+ x86-64

ferropdf-0.2.8-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (2.3 MB view details)

Uploaded CPython 3.13manylinux: glibc 2.17+ ARM64

ferropdf-0.2.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (2.5 MB view details)

Uploaded CPython 3.12manylinux: glibc 2.17+ x86-64

ferropdf-0.2.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (2.3 MB view details)

Uploaded CPython 3.12manylinux: glibc 2.17+ ARM64

ferropdf-0.2.8-cp311-cp311-win_amd64.whl (2.3 MB view details)

Uploaded CPython 3.11Windows x86-64

ferropdf-0.2.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (2.5 MB view details)

Uploaded CPython 3.11manylinux: glibc 2.17+ x86-64

ferropdf-0.2.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (2.3 MB view details)

Uploaded CPython 3.11manylinux: glibc 2.17+ ARM64

ferropdf-0.2.8-cp311-cp311-macosx_11_0_arm64.whl (2.2 MB view details)

Uploaded CPython 3.11macOS 11.0+ ARM64

ferropdf-0.2.8-cp311-cp311-macosx_10_12_x86_64.whl (2.3 MB view details)

Uploaded CPython 3.11macOS 10.12+ x86-64

ferropdf-0.2.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (2.5 MB view details)

Uploaded CPython 3.10manylinux: glibc 2.17+ x86-64

ferropdf-0.2.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (2.3 MB view details)

Uploaded CPython 3.10manylinux: glibc 2.17+ ARM64

ferropdf-0.2.8-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (2.5 MB view details)

Uploaded CPython 3.9manylinux: glibc 2.17+ x86-64

ferropdf-0.2.8-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (2.3 MB view details)

Uploaded CPython 3.9manylinux: glibc 2.17+ ARM64

ferropdf-0.2.8-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (2.5 MB view details)

Uploaded CPython 3.8manylinux: glibc 2.17+ x86-64

ferropdf-0.2.8-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (2.3 MB view details)

Uploaded CPython 3.8manylinux: glibc 2.17+ ARM64

File details

Details for the file ferropdf-0.2.8.tar.gz.

File metadata

  • Download URL: ferropdf-0.2.8.tar.gz
  • Upload date:
  • Size: 94.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: maturin/1.12.6

File hashes

Hashes for ferropdf-0.2.8.tar.gz
Algorithm Hash digest
SHA256 60e22aa8f113b138e49e1dccb33613876ac133a8241a1c554679885092d4089b
MD5 dbb7e2aba66e44785854e07cbc88d812
BLAKE2b-256 d03362b51a1425c0af083ce67cff06818403722694fc7c89d7a1166e2b99b1bf

See more details on using hashes here.

File details

Details for the file ferropdf-0.2.8-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for ferropdf-0.2.8-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 10d09bd823d3ce7ad8d7176b80ab098278d2392f9cd1f77e1199d486b259e836
MD5 8043f27740eef9f70ca918237cd60858
BLAKE2b-256 52e7d197c35ec812811107cb00e658750d36716ff4328971aa7b02df9a19c18e

See more details on using hashes here.

File details

Details for the file ferropdf-0.2.8-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for ferropdf-0.2.8-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 4498212cb1c10e2a8c9ee141c329dbadf745ed816d4d376d224d8623ceb646ec
MD5 564ed9f394d49939b8157eca505b7bb3
BLAKE2b-256 e1c2bf5e64a02fec756703ae8139abdd8e6dbe168a532f008942b72d7a2f7eee

See more details on using hashes here.

File details

Details for the file ferropdf-0.2.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for ferropdf-0.2.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 460a5111bbada7268c2d99afea1dbf7066b37acf665823485ed915ca715adb52
MD5 061bcd9e253e100d7deb8939fe4d4332
BLAKE2b-256 29fde6d50ccd6425e104af716ca159e71e0f63ef175c37eb7bebffe295eb23f5

See more details on using hashes here.

File details

Details for the file ferropdf-0.2.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for ferropdf-0.2.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 9a1c150ea8471183ccf3f7a99caf11e171fbf3f6ddf5b1b4e35abda7ff407b26
MD5 8fb169cb7ee79d64dd15e0eb4c2fa8b6
BLAKE2b-256 1456c40109f0b17815a60197621844909b2f84a85d814131ef90496dd164ecae

See more details on using hashes here.

File details

Details for the file ferropdf-0.2.8-cp311-cp311-win_amd64.whl.

File metadata

File hashes

Hashes for ferropdf-0.2.8-cp311-cp311-win_amd64.whl
Algorithm Hash digest
SHA256 da68b3e393732449aac5b368117eda67aba3f7aa6015dbb9f81c00db5668f598
MD5 66abc931927529fa90dd237652d17dd5
BLAKE2b-256 c13cb0196d2c5dd4c7c347ad5954cea3818ad2bddda46163259d8842e9cd1ac2

See more details on using hashes here.

File details

Details for the file ferropdf-0.2.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for ferropdf-0.2.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 eabae022c3aa19c926001774d71299da658a5e72852ae51a9610b8b83c78e36a
MD5 0b5ddabd1c9eb72546a0691488ab0e7d
BLAKE2b-256 42a14b53588856caba50df897d2927100370887c9d43f0a707619c8eed3ff0ba

See more details on using hashes here.

File details

Details for the file ferropdf-0.2.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for ferropdf-0.2.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 72e12df9727364196abd41f2f2fe0ecb5db0aaa04fe7b0915581999f8efa9d43
MD5 3ed1b0dcd185e9429367a08c3258774d
BLAKE2b-256 6b4a917702b9b3a3049ae128a9f429f551cfdec264648ca6627e4af7dfdc2589

See more details on using hashes here.

File details

Details for the file ferropdf-0.2.8-cp311-cp311-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for ferropdf-0.2.8-cp311-cp311-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 b18e953e43e0adff2de6b8a8873944f3e95d0b6317cfecef1c131466aa10391c
MD5 3894570e7b0d50009221bcf88b271ce4
BLAKE2b-256 5f01b11b828688f02890577a35f1095c74ad9a306c4c0350be545cc555e8e677

See more details on using hashes here.

File details

Details for the file ferropdf-0.2.8-cp311-cp311-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for ferropdf-0.2.8-cp311-cp311-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 68a05e854e9f2193be609fe1afe0745e624412a3dc6474e1f6a0daa52ae605c0
MD5 06a1e5b4ff41931c6943eaf6729aa072
BLAKE2b-256 8f21bdaab4d77dfa4a57802f51ae91714b353a69a573d2ec8fa2391e22b75c3f

See more details on using hashes here.

File details

Details for the file ferropdf-0.2.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for ferropdf-0.2.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 852fd400d026a5424be23af93ce5b9fc891b4b973a2f6ed3ecd8c4c6adf85e70
MD5 c30e8b43c2055bc6fe9eef6eefc6bc32
BLAKE2b-256 58dcb4b46fcdc8abe167f7e788611e53503b847e100362da5a984f2574668e1e

See more details on using hashes here.

File details

Details for the file ferropdf-0.2.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for ferropdf-0.2.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 8a8de1ae43a7139e06d9ee64197ae9b81014d03856229c450336c60e6ad6ef95
MD5 aaf3f2a1c88aa456481b1fab5fa4e350
BLAKE2b-256 ae6d58e100b748f05afa28c1bd51899fcb4b4c7c98c229b06dcf26e6dd5da321

See more details on using hashes here.

File details

Details for the file ferropdf-0.2.8-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for ferropdf-0.2.8-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 9752b82e1c77acf8316c96c11b3fa104b00b6085ff72792e43c2211415ad2657
MD5 58ebae0e93b7315d81901b9a25d7443a
BLAKE2b-256 9109e82a662b37fce2a7547263d8aa43d50bc6c69845db8a0a79c8fa32a1d180

See more details on using hashes here.

File details

Details for the file ferropdf-0.2.8-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for ferropdf-0.2.8-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 5b11748d19fc9384d4a4b90b85f5c9d0ab4fc5ed77e8a3a6e9e630a560a63650
MD5 e6c6d8d6f45632ce91c3fa54604a456e
BLAKE2b-256 7c73fe517b9726dbc63a88300f16e0a5bf9f58ba34c40c3b449b73abeb643a2c

See more details on using hashes here.

File details

Details for the file ferropdf-0.2.8-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for ferropdf-0.2.8-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 b60fad9c140f56f62195d6341283eba859a537dbc56dd0e76516722bfd56942d
MD5 d121199b6f9de16cf96455098ea92e2a
BLAKE2b-256 5ff1321e88e60cd599d99fa1398c1cb2928026a0008635b1897251f8b41463d3

See more details on using hashes here.

File details

Details for the file ferropdf-0.2.8-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for ferropdf-0.2.8-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 0eb85f32093b1e2cbe4c9c196334ff513b8acad989b4a4f6f1d4742c5a6e2049
MD5 038acdd259bab094146e6a4c15ddc6c8
BLAKE2b-256 241673c80586626f47fb651f2d1d5343a9143e4153ee6ff2e720b6393aca2138

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page