# WeasyPrint alternative

WeasyPrint alternative: WeasyPrint has great print CSS but no JavaScript and partial flexbox and grid. See when Chrome rendering fits better.

**Quick answer.** If your HTML needs JavaScript, modern flexbox or grid, use a Chromium renderer. With html-pdf.com it is one call.

```bash
curl https://html-pdf.com/v1/pdf \
  -H "Authorization: Bearer $HTML_PDF_KEY" \
  -H "Content-Type: application/json" \
  -d '{"html": "<h1>Hello</h1>", "format": "A4"}' \
  -o report.pdf
```

WeasyPrint is a BSD-licensed Python library that turns HTML and CSS into PDF with its own layout engine. It is one of the best tools for print-focused CSS. It is also not a browser, which matters when your HTML depends on JavaScript or modern layout.

This page is a fair look at both sides.

## What WeasyPrint does well

These are the areas where WeasyPrint is strong.

- **Paged media CSS.** `@page` rules, named pages, margin boxes for running headers and footers, `counter(page)` and `counter(pages)`, page-based cross references, and footnotes.
- **Typesetting features** aimed at books, reports, and documentation.
- **No browser to install.** Pure Python plus system libraries (Pango), so it runs offline and in locked-down environments.
- **Permissive license.** BSD, free for commercial use.

```css
@page {
  size: A4;
  margin: 20mm;
  @bottom-center { content: "Page " counter(page) " of " counter(pages); }
}
```

## Where WeasyPrint falls short

These are the limits you are most likely to hit.

- **No JavaScript.** Scripts don't run, so charts (Chart.js, D3, ECharts), client-rendered tables, and SPA pages come out empty. You would have to pre-render charts to SVG or images.
- **Partial flexbox.** The docs describe flex support as working for simple cases but not deeply tested.
- **Basic grid.** `display: grid`, grid areas, and `fr` work. Features like `auto-fill`/`auto-fit`, subgrid, and intrinsic sizing aren't supported.
- **Different from the browser.** Because it is a separate engine, a page that looks right in Chrome can lay out differently. You debug in the PDF rather than DevTools.
- **System dependencies.** Pango and related libraries need to be installed in Docker images and on some platforms.

## Comparison

This table compares WeasyPrint, self-hosted Chromium and the html-pdf.com API side by side.

| | WeasyPrint | Playwright/Puppeteer (self-hosted) | html-pdf.com API |
|---|---|---|---|
| Engine | Own layout engine | Chromium | Chromium (hosted) |
| JavaScript | No | Yes | Yes |
| Flexbox / grid | Partial / basic | Full | Full |
| Margin boxes (`@top-center` etc.) | Yes | Yes (Chrome 131+) | Yes, or `header_html` / `footer_html` |
| Running headers, page counters | Yes, in CSS | Header/footer templates | `header_html` / `footer_html` |
| Footnotes, cross references | Yes | No | No |
| Works offline | Yes | Yes | No |
| License / cost | BSD, free | Free + servers | 1 credit per page |

## When WeasyPrint is the better choice

WeasyPrint is the right pick in these cases.

- **Print-CSS-heavy documents** like books, manuals, and reports that rely on footnotes and page-based cross references. Chrome handles margin boxes and page counters, but not these.
- **Offline or air-gapped** systems, or when content must never leave your servers.
- **Static HTML** with no JavaScript and simple layouts.

If that is your case, WeasyPrint is likely a better fit than any Chrome-based tool.

## When Chrome rendering fits better

Chrome is the better choice when your PDF should match what the browser shows.

- The HTML is the same page your app shows in the browser, built with flexbox, grid, or Tailwind.
- You need **charts or other JavaScript** to run before printing.
- You want to convert a **live URL** as it looks in Chrome.
- You don't want to maintain Pango or Chrome in your images.

Chrome supports `@page` size and margins, `break-before`, `break-after`, and `break-inside`. Since Chrome 131 it also renders `@page` margin boxes such as `@bottom-center { content: counter(page) }`. That means most WeasyPrint running headers and page numbers work as-is, and header and footer templates are an alternative.

## Python example

This example renders a Chart.js report with a CSS grid and page numbers.

```python
import os
import requests

html = """
<!doctype html>
<html><head>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
  body { font-family: system-ui, sans-serif; }
  .grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 12px; }
</style>
</head><body>
  <h1>Monthly report</h1>
  <div class="grid"><div>Revenue: $12,400</div><div>Orders: 318</div></div>
  <canvas id="c" width="600" height="300"></canvas>
  <script>
    new Chart(document.getElementById("c"), {
      type: "bar",
      data: { labels: ["Jan", "Feb", "Mar"], datasets: [{ label: "Sales", data: [12, 19, 7] }] },
      options: { animation: false }
    });
    window.chartReady = true;
  </script>
</body></html>
"""

resp = requests.post(
    "https://html-pdf.com/v1/pdf",
    headers={"Authorization": f"Bearer {os.environ['HTML_PDF_KEY']}"},
    json={
        "html": html,
        "format": "A4",
        "margin": {"top": "15mm", "right": "12mm", "bottom": "20mm", "left": "12mm"},
        "footer_html": '<div style="font-size:9px;width:100%;text-align:center">'
                       'Page <span class="pageNumber"></span> of <span class="totalPages"></span></div>',
        "wait_for_expression": "window.chartReady === true",
        "max_pages": 20,
    },
    timeout=40,
)
resp.raise_for_status()

with open("report.pdf", "wb") as f:
    f.write(resp.content)
```

`wait_for_expression` holds the print until the chart has drawn. Turning off Chart.js animation avoids capturing a half-drawn chart. The bottom margin leaves room for `footer_html`.

## Mapping WeasyPrint habits

Use this table to translate common WeasyPrint calls to html-pdf.com options.

| WeasyPrint | html-pdf.com |
|---|---|
| `HTML(string=html).write_pdf("out.pdf")` | `{"html": html}` and save `resp.content` |
| `HTML(url).write_pdf(...)` | `{"url": "https://..."}` (public pages) |
| `@page { size: A4 landscape }` | Keep it with `prefer_css_page_size`, or `format` + `landscape` |
| `@bottom-center { content: counter(page) }` | works as-is (Chrome 131+), or `footer_html` with `pageNumber` / `totalPages` |
| `stylesheets=[CSS(...)]` | `"css": "..."` |
| `media_type="print"` | `"media": "print"` |

## Or skip the server

If you would rather not run any renderer, use the hosted API. html-pdf.com is an HTML/URL to PDF API on headless Chromium. `POST https://html-pdf.com/v1/pdf` with a Bearer key and `{"html": "..."}` or `{"url": "https://..."}` returns a PDF with selectable text, as binary, base64, or a 24-hour link.

1 credit = 1 PDF page. You get 100 free credits once, then packs from $10 for 1,000 pages down to $0.005 per page, and purchased credits never expire. AI assistants can also use it through the [remote MCP server](https://html-pdf.com/pdf-mcp-server).

[Get a free API key](https://html-pdf.com/login) · [API docs](https://html-pdf.com/docs) · [HTML to PDF in Python](https://html-pdf.com/html-to-pdf-python) · [Page breaks](https://html-pdf.com/html-to-pdf-page-breaks) · [Templates](https://html-pdf.com/templates)

## FAQ

### What is the best alternative to WeasyPrint?

If your HTML uses JavaScript, charts, or modern flexbox and grid, a Chromium renderer like Playwright, Puppeteer or the html-pdf.com API is the closest fit. For footnote-heavy print documents, WeasyPrint is still a strong choice.

### Does WeasyPrint support JavaScript?

No. WeasyPrint does not run scripts, so charts and client-rendered content come out empty unless you pre-render them to SVG or images.

### Can Chrome do CSS page numbers like WeasyPrint?

Yes. Since Chrome 131, `@page` margin boxes such as `@bottom-center { content: "Page " counter(page) " of " counter(pages) }` work, and you can use `prefer_css_page_size` to keep your CSS page size. You can also use `footer_html` instead.

### Is WeasyPrint free?

Yes, it is BSD-licensed and free for commercial use. html-pdf.com charges 1 credit per PDF page, with 100 free credits to start and no card needed.
