Getting Started
Install
Requirements
Python 3.12 or newer. pdfun ships as a compiled Rust extension; wheels are published for Linux (x86_64), macOS (arm64), and Windows (x86_64). No system libraries to install.
Running without installation
Try the CLI against a file without adding pdfun to your environment:
Or with pipx:
Adding pdfun to your project (uv)
Upgrade:
Adding pdfun to your project (pip)
Upgrade:
From source
Your first PDF
That's it. hello.pdf is a one-page document with the heading rendered in the default font.
Styling with CSS
Put a <style> block inline or a <link rel="stylesheet">. Most of CSS 2.1 layout works — see CSS Support for the full list.
from pdfun import HtmlDocument
html = """
<style>
@page { size: A4; margin: 2cm; }
body { font-family: Helvetica; font-size: 11pt; line-height: 1.4; }
h1 { color: #1a237e; border-bottom: 2pt solid #1a237e; padding-bottom: 4pt; }
.note { background: #fff3e0; padding: 8pt; border-left: 3pt solid #ff9800; }
</style>
<h1>Quarterly Report</h1>
<p class="note">Figures are preliminary and subject to revision.</p>
"""
HtmlDocument(string=html).write_pdf("report.pdf")
Table of contents
Pass toc=True (or a custom title) to prepend an auto-generated ToC built from <h1>–<h6>:
Headings without id attributes get them auto-assigned, and the ToC entries are clickable internal links in the PDF.
Images
Relative paths are resolved against base_url (defaults to the current working directory):
HtmlDocument(
string='<img src="logo.png" style="width: 200pt">',
base_url="/path/to/assets",
).write_pdf("out.pdf")
PNG and JPEG are supported.
Low-level Layout API
When you need to place text and shapes without HTML, use the layout API directly:
from pdfun import PdfDocument, Layout, TextRun
doc = PdfDocument()
layout = Layout(doc)
layout.add_text("Invoice", font_size=24.0, spacing_after=12.0)
layout.add_paragraph(runs=[
TextRun("Amount due: ", font_name="Helvetica-Bold"),
TextRun("$1,234.56"),
])
layout.finish()
doc.save("invoice.pdf")
The Layout takes care of page breaks and line wrapping; PdfDocument owns the page list and font table. For raw placement (no layout), grab a Page directly via doc.add_page() and call page.draw_text(x, y, ...), page.draw_rect(...), etc.
CLI
See pdfun render --help for flags.
Examples
Runnable versions of the above live in the repo's examples/ directory.