Ferri PDF for Python
An HTML and CSS to PDF renderer for print, written in pure Rust. No browser and no JavaScript.
pip install ferri-pdf
Wheels are prebuilt for CPython 3.10 and later on Linux, macOS and Windows.
Rendering
import ferri_pdf
output = ferri_pdf.render("<h1>Hello</h1>")
with open("hello.pdf", "wb") as file:
file.write(output.pdf)
render takes a str, or bytes decoded by the HTML encoding rules. It returns the PDF bytes, page_count, the located headings and the diagnostics for recovered input. Invalid markup, unsupported CSS and missing resources never raise; they are recovered as the specifications say and listed in diagnostics.
The interpreter lock is released while rendering, so threads render in parallel.
Options
options = ferri_pdf.RenderOptions(
base="templates/invoice",
stylesheets=["@page { size: A4; margin: 2cm }"],
serif=["Georgia"],
jpeg_quality=85,
image_dpi=150,
allow_http=False,
)
output = ferri_pdf.render(html, options)
| Option | Effect |
|---|---|
base |
Directory that relative URLs resolve against (default: the working directory) |
base_url |
Document URL that relative URLs resolve against, instead of base |
stylesheets |
Author stylesheets applied after the document's own |
user_stylesheets |
User-origin stylesheets |
paper |
Paper size (width, height) in points when CSS sets none (default: A4) |
color_scheme |
"light" or "dark" for prefers-color-scheme |
contrast |
"no-preference", "less", "more" or "custom" for prefers-contrast |
reduced_motion |
"no-preference" or "reduce" for prefers-reduced-motion |
serif, sans_serif, monospace |
Installed families for each generic font |
fallback |
Installed families tried for missing glyphs |
image_dpi |
Downsample raster images to this resolution |
jpeg_quality |
Re-encode opaque photos as JPEG at this quality, 1 to 100 |
include_layout |
Return the measured layout as output.layout |
allow_content_loss |
Return the PDF even when a resource limit left content out |
allow_http |
Fetch HTTP(S) resources with the built-in client (default: True) |
file_root |
Refuse local files outside this directory |
fetch |
Callable that loads every URL other than local files and data URLs |
Rendering untrusted HTML? Set allow_http=False and file_root so a document cannot reach your network or read files outside its own assets, or route requests through fetch and decide there.
Fetching resources yourself
fetch receives each absolute URL that is not a file: or data: URL: HTTP(S), and any scheme your base_url uses, such as app://. It replaces the built-in HTTP client. Return the body as bytes, a ferri_pdf.Resource to add a content type or the final URL after redirects, or None when the resource is unavailable.
import httpx
import ferri_pdf
client = httpx.Client(headers={"Authorization": f"Bearer {token}"}, timeout=10)
def fetch(url: str) -> ferri_pdf.Resource | None:
if not url.startswith("https://assets.example.com/"):
return None
response = client.get(url, follow_redirects=True)
response.raise_for_status()
return ferri_pdf.Resource(
response.content,
content_type=response.headers.get("content-type"),
url=str(response.url),
)
output = ferri_pdf.render(html, ferri_pdf.RenderOptions(fetch=fetch))
Relative URLs inside a fetched resource resolve against its url. An Exception raised by fetch, like None, leaves that resource unavailable and is reported in diagnostics; rendering carries on. KeyboardInterrupt, SystemExit and a return value of the wrong type stop further fetches and are raised from render. Local files still load from disk, within file_root when set.
Several documents in one PDF
output = ferri_pdf.render_parts([
ferri_pdf.Part(cover_html, base="cover"),
ferri_pdf.Part(body_html, base="body"),
])
for part in output.parts:
print(part.first_page, part.page_count)
Each part starts on a new page. Parts can also be plain str or bytes, which use the options' base.
Errors
ferri_pdf.RenderError is raised when rendering fails. Its subclass ferri_pdf.ContentLostError is raised when a resource limit left content out of the PDF; its diagnostics list the losses and part names the one-based part of a multipart render. Pass allow_content_loss=True to get the PDF anyway. Invalid option values raise ValueError.
Licence
MIT. The bundled fonts, hyphenation patterns and specification data keep their own licences; see the repository.
Metadata
Release files for ferri-pdf 0.1.8
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| ferri_pdf-0.1.8.tar.gz | 9.7 MB | With provenance | Details for ferri_pdf-0.1.8.tar.gz |
Built distributions (wheels)
Total release size: 174.7 MB
Release files / ferri_pdf-0.1.8.tar.gz
| Download URL | ferri_pdf-0.1.8.tar.gz |
|---|---|
| Size | 9.7 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e49e200d56351d99fdceac90060ea6a13c6569f140f2b87e0a93219fc7d93626
|
|
BLAKE2b-256 checksum How to use checksums |
82fc4b1d181e76b6a3d322d57dd8fc139a7c19831d13b99cb06d66e2f4f6787a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 1, 2026.
Transparency logRelease files / ferri_pdf-0.1.8-cp310-abi3-win_arm64.whl
| Download URL | ferri_pdf-0.1.8-cp310-abi3-win_arm64.whl |
|---|---|
| Size | 18.8 MB |
| Tags | CPython 3.10 Windows ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
9b0d99c5a3ebef5e52b4bfad4d6a4aa11a54be863ebbf615b9e40dae8e98e3ac
|
|
BLAKE2b-256 checksum How to use checksums |
3694f429a80b587cc6e049f9d374913b8055de37f667b0a2947c4c48c9d7415f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 1, 2026.
Transparency logRelease files / ferri_pdf-0.1.8-cp310-abi3-win_amd64.whl
| Download URL | ferri_pdf-0.1.8-cp310-abi3-win_amd64.whl |
|---|---|
| Size | 20.0 MB |
| Tags | CPython 3.10 Windows x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
76f2c202bfad1b17e771e9483da0dfc062df994a6e46edb4148c5ff5741763de
|
|
BLAKE2b-256 checksum How to use checksums |
46148107a3ebcd3d9cd905698fe346b44cadacde13b4547dc042a28b57ca129b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 1, 2026.
Transparency logRelease files / ferri_pdf-0.1.8-cp310-abi3-musllinux_1_2_x86_64.whl
| Download URL | ferri_pdf-0.1.8-cp310-abi3-musllinux_1_2_x86_64.whl |
|---|---|
| Size | 22.2 MB |
| Tags | CPython 3.10 Linux musl 1.2+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
15cc86ca520a57c11d3d0a3b7103cf85f86df91df9a1cf2dd7c65590605e315a
|
|
BLAKE2b-256 checksum How to use checksums |
36a913ae844923287e171795533709cc60f47df339e81717991af70a846ec68e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 1, 2026.
Transparency logRelease files / ferri_pdf-0.1.8-cp310-abi3-musllinux_1_2_aarch64.whl
| Download URL | ferri_pdf-0.1.8-cp310-abi3-musllinux_1_2_aarch64.whl |
|---|---|
| Size | 21.5 MB |
| Tags | CPython 3.10 Linux musl 1.2+ ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
35cb128fcc147cecbfa11ee5ffb337dc5f9000a75a91606515286cb24499e2ac
|
|
BLAKE2b-256 checksum How to use checksums |
446230548c18dbf5ea200c770a2a134d80bbea71acd86ac51c5c9cf8fc63d546
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 1, 2026.
Transparency logRelease files / ferri_pdf-0.1.8-cp310-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
| Download URL | ferri_pdf-0.1.8-cp310-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl |
|---|---|
| Size | 21.3 MB |
| Tags | CPython 3.10 Linux glibc 2.17+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
aaf0d601a1aac7c28e848af40645febd18b944b74ed9d1de6d67ae79b3afdf0d
|
|
BLAKE2b-256 checksum How to use checksums |
8ecd2fbc3f97c5fa12eba2dcf07ab1c0463699d6635b7277f3996b13006d5a74
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 1, 2026.
Transparency logRelease files / ferri_pdf-0.1.8-cp310-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl
| Download URL | ferri_pdf-0.1.8-cp310-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl |
|---|---|
| Size | 21.2 MB |
| Tags | CPython 3.10 Linux glibc 2.17+ ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
c30e036bb9228bbc30a753dc61ffc08b0de264f62ab94e8976bb279814b23337
|
|
BLAKE2b-256 checksum How to use checksums |
d9d94329b4b793b3e9edb24448128c69e958721effc68a78ed8a2ae54a212784
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 1, 2026.
Transparency logRelease files / ferri_pdf-0.1.8-cp310-abi3-macosx_11_0_arm64.whl
| Download URL | ferri_pdf-0.1.8-cp310-abi3-macosx_11_0_arm64.whl |
|---|---|
| Size | 19.7 MB |
| Tags | CPython 3.10 abi3 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
90c7e4165be69a199de2eaa107db9e41021950ae0bdfbb78238d87b19f482ba2
|
|
BLAKE2b-256 checksum How to use checksums |
4cdf2fc4b8c49b286ea16dd4fa472ab35a7c7fe681409de97f2c3d1bea5c687e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 1, 2026.
Transparency logRelease files / ferri_pdf-0.1.8-cp310-abi3-macosx_10_12_x86_64.whl
| Download URL | ferri_pdf-0.1.8-cp310-abi3-macosx_10_12_x86_64.whl |
|---|---|
| Size | 20.4 MB |
| Tags | CPython 3.10 abi3 macOS 10.12+ x86-64 |
|
SHA-256 checksum How to use checksums |
93d51bcee6a568383d61756d9652314b4165d82bc78500d2bd9f66a416a08d9d
|
|
BLAKE2b-256 checksum How to use checksums |
8bbc418be56e321901cee8901b36d5b2b727c0f01074b86a7a3cc71873821535
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 1, 2026.
Transparency logSecurity for ferri-pdf 0.1.8
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 9 files in this release have been uploaded with provenance metadata.
Attestation for ferri_pdf-0.1.8.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 1, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.