Fast HTML to PDF — Rust-powered, 10x faster than WeasyPrint
Project description
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 failureLayoutError— Layout computation failureFontError— Font loading/resolution failureRenderError— 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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ferropdf-0.2.5.tar.gz.
File metadata
- Download URL: ferropdf-0.2.5.tar.gz
- Upload date:
- Size: 90.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: maturin/1.12.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b8b4dbfba96fd77bf5480c77b8ba5ad38b7b800aedd2c2cac3090d37fabcac72
|
|
| MD5 |
964ca86234e17a798c738c7f4d3ca2c9
|
|
| BLAKE2b-256 |
daf9d9182c50ec5a141332ea8017604119a6ca50b16a5730586275eb7eb3a36d
|
File details
Details for the file ferropdf-0.2.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: ferropdf-0.2.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 2.4 MB
- Tags: CPython 3.13, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: maturin/1.12.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a3fa9406f2e7b2a317fcee0330dd4bbd27fd91b153e895633f38d0329b414458
|
|
| MD5 |
e47c7e64f3e56d39d7dfffd6a444a5a5
|
|
| BLAKE2b-256 |
75ddce548628ffb4a08ae28720070bcb706b94f8132fc6f4a5b28c04f7b4b2de
|
File details
Details for the file ferropdf-0.2.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.
File metadata
- Download URL: ferropdf-0.2.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
- Upload date:
- Size: 2.3 MB
- Tags: CPython 3.13, manylinux: glibc 2.17+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: maturin/1.12.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0533676b50d177383d3b798afb1587354d2e07b58c0c36045cf1d4bc00b9e749
|
|
| MD5 |
7dc3d90c83746eb89d86918848280a6d
|
|
| BLAKE2b-256 |
1bab8eba946320fa5d8621fb03d66899d6fcbf7108b7bc5ef9ab291238a936b0
|
File details
Details for the file ferropdf-0.2.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: ferropdf-0.2.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 2.4 MB
- Tags: CPython 3.12, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: maturin/1.12.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ca595c78ac021907ac3dfbcf154736cc016891dd67d6c288942c64d890b1b06c
|
|
| MD5 |
c5291f81c7d31202fc9b135769b4cdd7
|
|
| BLAKE2b-256 |
26655928973048b5530172bce4ef74aed87e41a1cd8ceead9a8c718e56ea45e3
|
File details
Details for the file ferropdf-0.2.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.
File metadata
- Download URL: ferropdf-0.2.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
- Upload date:
- Size: 2.3 MB
- Tags: CPython 3.12, manylinux: glibc 2.17+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: maturin/1.12.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e8830f8649dfc22bca2ccef239c3fba034c3f65b9b1eaf54206d36311dcda0d6
|
|
| MD5 |
36af9e2e5ecda9563f497ee22bc12fb9
|
|
| BLAKE2b-256 |
81a079911dbb6da3de2d94916e612a4f66af03c2bbda5d140213949cf52d94d1
|
File details
Details for the file ferropdf-0.2.5-cp311-cp311-win_amd64.whl.
File metadata
- Download URL: ferropdf-0.2.5-cp311-cp311-win_amd64.whl
- Upload date:
- Size: 2.3 MB
- Tags: CPython 3.11, Windows x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: maturin/1.12.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1437baa8f806eee75dcc4b73a68a3a7fdac8cb9b644e1c2a112218f7de293233
|
|
| MD5 |
b3f16117c84ad96e59050a62c7094f05
|
|
| BLAKE2b-256 |
cc7b36766d3b52e56140f1bdcc62935bfd5bff9d0b680e51e37ba58308882ece
|
File details
Details for the file ferropdf-0.2.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: ferropdf-0.2.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 2.4 MB
- Tags: CPython 3.11, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: maturin/1.12.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7276eeb09b82fc945d8ca576384bb209672d7254c4d12cd04ceeadf2811befd5
|
|
| MD5 |
d340071d1983a890e79d6a57b59efcc5
|
|
| BLAKE2b-256 |
3a661bd7c5490b3b326db02ca09eaebbe84140e67ebb176f8271413eab06c278
|
File details
Details for the file ferropdf-0.2.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.
File metadata
- Download URL: ferropdf-0.2.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
- Upload date:
- Size: 2.3 MB
- Tags: CPython 3.11, manylinux: glibc 2.17+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: maturin/1.12.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5bc0f9095a2859cc03d58b3806c101cd4fc4df3c19c77a754d6b22f7b52aad6e
|
|
| MD5 |
ea45c79d926b128b0ca0d008a59c9494
|
|
| BLAKE2b-256 |
43f6b94759d361f77e4c9012357b1b6eb7623251081a6e4a9a782b20b0d95fbb
|
File details
Details for the file ferropdf-0.2.5-cp311-cp311-macosx_11_0_arm64.whl.
File metadata
- Download URL: ferropdf-0.2.5-cp311-cp311-macosx_11_0_arm64.whl
- Upload date:
- Size: 2.1 MB
- Tags: CPython 3.11, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: maturin/1.12.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ee317942f59430dc3128ae2f4101d3f15074d8d072ff25e741ac9bba6813a9b5
|
|
| MD5 |
85d45eb7f4f36bfc3437b668d6f0e8ec
|
|
| BLAKE2b-256 |
6168460bb92d1e674f0cfbe80e939b6dd51b7d52a01280915432314819d1b561
|
File details
Details for the file ferropdf-0.2.5-cp311-cp311-macosx_10_12_x86_64.whl.
File metadata
- Download URL: ferropdf-0.2.5-cp311-cp311-macosx_10_12_x86_64.whl
- Upload date:
- Size: 2.2 MB
- Tags: CPython 3.11, macOS 10.12+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: maturin/1.12.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
68d97b72daf42fac45250f3d4523fdbd2980c1bf678fbc35778a4746da021380
|
|
| MD5 |
526de04419a8c557a9bc80c7b51e37f0
|
|
| BLAKE2b-256 |
bec2495a845dd6498d1fb7595d8277c814f31df5b2fd699ecb54847b61c19867
|
File details
Details for the file ferropdf-0.2.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: ferropdf-0.2.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 2.4 MB
- Tags: CPython 3.10, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: maturin/1.12.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
11a5d697ff0384ba226ea54802b2a96921abc5e8eea252f7e516f8ccf32fe552
|
|
| MD5 |
7fb53c18f8b68ecfde33546893710ba8
|
|
| BLAKE2b-256 |
fbeb1a16485b20a77853d7916f474b0aba6050121660ff0b9bcd22b3c3c533f6
|
File details
Details for the file ferropdf-0.2.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.
File metadata
- Download URL: ferropdf-0.2.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
- Upload date:
- Size: 2.3 MB
- Tags: CPython 3.10, manylinux: glibc 2.17+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: maturin/1.12.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b7501fc3070416fe24da632b4fc2b1f4ffea4c2c9a892c789f7db12dd521e9c9
|
|
| MD5 |
f493922a6f861623c3c02fd96a87a811
|
|
| BLAKE2b-256 |
5c2c99422626d5841fcb4281259241ca4accfd26e7e2fcd170b837fbedf4ab0b
|
File details
Details for the file ferropdf-0.2.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: ferropdf-0.2.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 2.4 MB
- Tags: CPython 3.9, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: maturin/1.12.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
97b2e70aebe93d81e82ff31a147ef0749f2b0836589e2fa802dfd8150a337bcb
|
|
| MD5 |
b48436129a0b795a5f96e1e3d0d0b93b
|
|
| BLAKE2b-256 |
23140c9a2a328a1625c1cabb5089a68a5bb9a0bfd8a839d6aa7c9158c0512612
|
File details
Details for the file ferropdf-0.2.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.
File metadata
- Download URL: ferropdf-0.2.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
- Upload date:
- Size: 2.3 MB
- Tags: CPython 3.9, manylinux: glibc 2.17+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: maturin/1.12.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b22cd1ed0cc33d57d55a15c22660e56eda29aaa0f79a5e2d247c30b35503b330
|
|
| MD5 |
f014f48a30d23eb4986662427a037d30
|
|
| BLAKE2b-256 |
efd34671a8159964a4c8fe8f5b18ae01584f3397ef5f22ea6410e21363dba435
|
File details
Details for the file ferropdf-0.2.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: ferropdf-0.2.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 2.4 MB
- Tags: CPython 3.8, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: maturin/1.12.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
79e97b0799a659331defca810f1aed34b92c4d1b457125595ed99c8da3223440
|
|
| MD5 |
fcffc3b0dd660894ddaec006c0bc0f81
|
|
| BLAKE2b-256 |
6fcde6984167da0ed21d314999c579b48a8f2963cde124086ecbdb10e56d78b2
|
File details
Details for the file ferropdf-0.2.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.
File metadata
- Download URL: ferropdf-0.2.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
- Upload date:
- Size: 2.3 MB
- Tags: CPython 3.8, manylinux: glibc 2.17+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: maturin/1.12.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
07b2868081c7f30d14a3fe248b4eecc79aa490c6ab3ba4164d550e2dc325c16a
|
|
| MD5 |
5647ea444f100a778bee67f40274a729
|
|
| BLAKE2b-256 |
09aafdd033360db776ad84dd6e75eb797c274a0bfcb67d2702cee2be33e0b144
|