from weasyprint import HTML, CSS
html = """
<h1>Invoice 1042</h1>
<p>Total: $120.00</p>
"""
page_css = CSS(string="@page { size: Letter; margin: 1in } h1 { color: #333 }")
# base_url resolves relative paths such as <img src="logo.png">
pdf_bytes = HTML(string=html, base_url=".").write_pdf(stylesheets=[page_css])
with open("invoice.pdf", "wb") as f:
f.write(pdf_bytes)Install it with pip install weasyprint. When you call write_pdf() without a target, it returns the PDF as bytes, so you can write it to disk or return it from a web handler. Extra CSS goes in through the stylesheets option as a list of CSS objects.
Caveats
Set base_url when you pass HTML as a string. The WeasyPrint docs describe it as the base used to resolve relative URLs, and relative image or stylesheet paths can fail without it when the input is a string.
Check the system requirements before debugging your code. WeasyPrint's documentation lists Python 3.10 or newer and Pango 1.44.0 or newer. Pango is a system library, not a pip package, so a minimal Docker or CI image often needs it installed separately. If the import or the first render fails with a library-loading error, that is the first thing to check.
Back to All Questions