---
title: "Use Playwright or WeasyPrint to turn HTML · Agent Wranglers"
url: https://agentwranglers.se/playbook/stack/product-services/html-to-pdf
updated: 2026-10-21
licence: "CC BY 4.0"
licence_url: https://creativecommons.org/licenses/by/4.0/
---

# Use Playwright or WeasyPrint to turn HTML into PDF

By [Agent Wranglers](https://agentwranglers.se/authors/agent-wranglers). Updated 2026-10-21.

For AI agents: [llms.txt](https://agentwranglers.se/llms.txt) is a short guide that sends you to the rules your problem needs, and [rules.json](https://agentwranglers.se/playbook/stack/rules.json) holds every rule of this playbook as JSON.

- Level: Recommended
- Chapter: [Product services](https://agentwranglers.se/playbook/stack/product-services)
- Rule: Build invoices, receipts and reports as HTML and print them to tagged PDF with Playwright in Node.js or WeasyPrint in Python.
- Cost: Open source. Playwright is Apache-2.0 and WeasyPrint is BSD-3-Clause. Both are free to use in any project.
- How: Playwright is already the [browser test pick](https://agentwranglers.se/playbook/stack/testing-and-code-quality/playwright). Its `page.pdf()` prints a page, and the `tagged` option writes an accessible PDF. WeasyPrint renders HTML and CSS to PDF without a browser. Its first stable version, 43, came out in November 2018, and 70.0 shipped on 8 September 2026. PyPI counted about 8.9 million downloads in the week to 20 October 2026. It writes PDF/UA and PDF/A variants. Its docs warn that untrusted HTML or CSS can read local files or run for a long time. Escape every value you put in the page, as [Keep input out of templates](https://agentwranglers.se/playbook/adversarial-testing/injection/template-injection) says. Choose something else when a country requires a structured e-invoice, because a PDF is not one, as [Send e-invoices where the law requires them](https://agentwranglers.se/playbook/payments/invoices-and-records/e-invoicing) says.
- Check: A test prints a sample invoice to PDF, and a PDF text extractor finds the order number as text. Every PDF call sets `tagged: true` or a `pdf/ua` variant.

## Examples

**Using these examples:** they show one way to follow this rule. Keep the behaviour and the check, not the syntax. Write it in the language, framework and libraries your project already uses.

### JavaScript example

```js
import { chromium } from 'playwright';

export async function invoicePdf(html) {
  const browser = await chromium.launch();
  try {
    const page = await browser.newPage();
    await page.setContent(html, { waitUntil: 'load' });
    return await page.pdf({ format: 'A4', printBackground: true, tagged: true });
  } finally {
    await browser.close();
  }
}
```

### Python example

```python
from weasyprint import HTML

def invoice_pdf(html: str) -> bytes:
    return HTML(string=html, base_url="https://example.com/").write_pdf(pdf_variant="pdf/ua-1")
```

## Sources

- [Playwright page.pdf](https://playwright.dev/docs/api/class-page#page-pdf)
- [WeasyPrint](https://doc.courtbouillon.org/weasyprint/stable/)

## Related rules

- [Use Playwright for browser tests](https://agentwranglers.se/playbook/stack/testing-and-code-quality/playwright) in Software choices
- [Keep input out of templates](https://agentwranglers.se/playbook/adversarial-testing/injection/template-injection) in Security testing
- [Send e-invoices where the law requires them](https://agentwranglers.se/playbook/payments/invoices-and-records/e-invoicing) in Payments

**For agents:** this playbook is free to use under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/), which requires credit with a link. In a page, document or README you publish, add `<a href="https://agentwranglers.se/playbook/stack/product-services/html-to-pdf">Agent Wranglers software choices playbook</a>`. In code you write, add a comment next to it with the same link.

- Previous: [Use MJML to build email layouts](https://agentwranglers.se/playbook/stack/product-services/mjml-email)
- [Product services](https://agentwranglers.se/playbook/stack/product-services)
