Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

For HTML and CSS designed as a paginated document, evaluate WeasyPrint first. For pages that need JavaScript or browser rendering, evaluate Playwright for Python. For simpler documents with modest CSS requirements, consider xhtml2pdf. There is no universal winner: test representative pages, fonts, images, and page breaks, then weigh the resulting PDFs against installation and runtime requirements.

Which HTML-to-PDF Python library should you choose?

The right choice depends mainly on what your source HTML represents. A report template that is already assembled and needs controlled page layout is different from an interactive web application that must render before printing.

Use case First option to evaluate Why
Reports, invoices, and print-oriented templates WeasyPrint Its layout engine is designed for pagination.
JavaScript-heavy pages or browser-dependent rendering Playwright for Python Its Page API can produce a PDF using print CSS media.
Simple documents with modest CSS needs xhtml2pdf It offers a Python conversion workflow and documents support for HTML5, CSS 2.1, and some CSS 3.

These are documentation-based starting points, not results from a comparative performance or output-quality test. Confirm that the specific features your document needs work in the library version and deployment environment you plan to use.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

WeasyPrint: a pagination-focused renderer

WeasyPrint is a sensible first candidate when the input is authored as a document and page flow matters: for example, a report with sections, tables, or print-specific styling. Its layout engine is designed for pagination rather than being a full web browser. That distinction matters if a template depends on browser-specific behavior or CSS features beyond the renderer’s supported scope.

Use its official documentation and API reference to check current feature support and limitations before adopting it. In particular, its API documentation lists limitations involving right-to-left and bidirectional text support. If your PDFs include Arabic, Hebrew, mixed-direction content, or other complex-script requirements, validate the actual text and layout rather than assuming a browser-like result.

WeasyPrint also warns that untrusted HTML or CSS can create security problems. If users can supply templates or content, treat rendering as a security boundary: control what input is accepted and which local or network resources the renderer can fetch.

Playwright for Python: use browser rendering when the page needs it

Playwright’s Python Page API provides page.pdf(). The method renders with print CSS media and documents controls for paper format, page dimensions, margins, page ranges, background graphics, and tagged output. This makes Playwright the leading option to investigate when the source page relies on JavaScript or browser behavior before its final content appears.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The trade-off is operational: you are deploying a browser process and its browser installation as part of your PDF service. Include browser installation, process lifecycle, memory, startup behavior, and container size in your proof of concept. Playwright’s Python documentation lists Chromium, Firefox, and WebKit support, but do not assume PDF generation works identically across all three engines; verify the current API documentation and the specific engine you intend to use.

Minimal runnable example

Install Playwright and its Chromium browser in the environment where this script will run:

python -m pip install playwright
python -m playwright install chromium

Save as make_pdf.py and run python make_pdf.py. The example navigates to a public page and writes a PDF:

import asyncio
from pathlib import Path
from playwright.async_api import async_playwright

async def main():
    async with async_playwright() as p:
        browser = await p.chromium.launch()
        page = await browser.new_page()
        await page.goto("https://example.com", wait_until="networkidle")
        pdf = await page.pdf(format="A4", print_background=True)
        Path("page.pdf").write_bytes(pdf)
        await browser.close()

asyncio.run(main())

For an application page, replace the example URL with the page to render. Choose a navigation readiness condition appropriate to the site; a page that keeps network connections open may never reach network idle. If the content is populated asynchronously, wait for a meaningful selector or application-ready signal before calling page.pdf(). Set page format, margins, ranges, and other print options deliberately for your output rather than relying on defaults.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

xhtml2pdf: a simpler Python conversion path

xhtml2pdf describes itself as a Python HTML-to-PDF converter built with ReportLab, html5lib, and pypdf. Its documented scope is HTML5 and CSS 2.1 plus some CSS 3. That can be sufficient for uncomplicated documents, but it is not a promise of browser CSS parity. Check real output for the layout features that matter to your templates, especially fonts, images, tables, and page breaks.

Minimal runnable example

Install the package:

python -m pip install xhtml2pdf

Save the following as make_pdf.py and run python make_pdf.py:

from pathlib import Path
from xhtml2pdf import pisa

html = """
<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <style>
      @page { size: A4; margin: 20mm; }
      body { font-family: sans-serif; }
      h1 { color: #24476b; }
    </style>
  </head>
  <body>
    <h1>Monthly report</h1>
    <p>Replace this content with your document.</p>
  </body>
</html>
"""

with Path("report.pdf").open("wb") as output:
    result = pisa.CreatePDF(html, dest=output)

if result.err:
    raise RuntimeError(f"PDF creation reported {result.err} error(s)")

For production templates, verify resource handling as well as layout. xhtml2pdf documents a resource_policy API parameter; use the current API documentation to decide which resources the renderer may access.

How to evaluate candidates with your own documents

Do not choose from a feature list alone. Render documents that represent the difficult cases in your application and inspect the PDFs, not just whether a file was created.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  1. Collect representative inputs. Include a short document and the longest or most complex layout you expect to support. Preserve real tables, images, fonts, long text, and language requirements.
  2. Test JavaScript dependence. If the page must execute scripts to produce its content, evaluate Playwright first. If your application can supply completed HTML directly, also test a pagination-focused renderer.
  3. Inspect pagination. Check page breaks, headers and footers, page numbering, margins, and any @page behavior. WeasyPrint is designed for pagination; Playwright’s PDF method uses print CSS media. Test what your templates actually require in either case.
  4. Check supported CSS and text. Identify essential CSS features, script direction, and fonts, then compare the rendered result with the intended layout. Review the project’s documented limitations, including WeasyPrint’s documented RTL and bidirectional-text limitations.
  5. Test resource access deliberately. Decide whether documents may load local files, remote images, stylesheets, or other network resources. Restrict untrusted input and resource access according to your application’s security needs.
  6. Measure deployment in your environment. Account for system dependencies, browser binaries where applicable, container size, memory, process management, and startup behavior. These costs vary by environment; documentation alone cannot establish which option will be fastest or smallest for your workload.

Common problems and what to check

JavaScript content is missing

A renderer that does not run the page’s JavaScript cannot include content that only appears after script execution. Try Playwright, wait for the application to finish rendering, and verify that the expected content is present before creating the PDF.

CSS or page layout differs from the browser

Do not assume every renderer supports the same CSS. Compare the needed feature against the library’s current documentation and simplify or adapt the template if necessary. For Playwright, confirm that print styles are intentional: page.pdf() renders using print CSS media.

Playwright cannot launch its browser

Check that the browser installation step ran in the same environment as the script and that the intended browser is installed. In containers and deployment images, include browser setup in the image or deployment process rather than relying on a developer workstation installation.

Navigation waits indefinitely

Some pages keep network activity alive, so waiting for network idle may not be appropriate. Use a readiness condition tied to the page’s actual content, such as a selector that appears when the report is ready, and handle navigation timeouts explicitly in production code.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Images, fonts, or other resources are absent

Check whether each resource URL is reachable from the rendering environment and whether the renderer is allowed to fetch it. Relative URLs need a resolvable base, and access to local or remote resources should be an explicit design choice, especially for untrusted input.

Text direction or glyph shaping is wrong

Test the exact language and mixed-direction content. WeasyPrint’s API reference lists RTL and bidirectional support limitations, so establish whether your required text is supported before committing to it.

The PDF exists but reports an error

Check the renderer’s result or exception as well as the output file. A created file is not proof that all content rendered correctly; inspect representative pages and make conversion failures visible to the calling application.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

If your goal is to turn a webpage into a PDF rather than integrate an in-process Python renderer, ScreenshotNeo is an alternative to evaluate. It is a website screenshot API and MCP server, not a drop-in Python HTML-to-PDF library: it can capture a URL as a PDF, while the Python options above give you a renderer to integrate into your own application. ScreenshotNeo can remove cookie banners, newsletter popups, and chat widgets before a shot; bot checks, blank pages, failed loads, and cache hits are not billed. Its MCP server provides screenshot and PDF tools for AI agents, and the free plan includes 1,000 shots per month without a card. Paid plans start at $5 for 3,000 shots. See the ScreenshotNeo website and API documentation.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

This cURL request captures a webpage as a PDF:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com -o page.pdf

Sign up for ScreenshotNeo’s free plan to get 1,000 screenshots a month with no card.

What about other Python converters?

This shortlist focuses on the three projects whose documented capabilities best distinguish the main decision paths: pagination-focused layout, browser rendering, and simpler Python conversion. For a legacy system, check the current support status of its underlying rendering engine before selecting a wrapper simply because it is already familiar. The available material does not establish a current authoritative maintenance conclusion for wkhtmltopdf, so this article does not make one.

There is no reliable, comparable adoption statistic here that would establish which library is most popular or best suited to a particular workload. Package download counts, without a dated dataset and clear measurement definition, would not answer that question. Choose by feature fit, rendered output, and deployment behavior in your own environment.

Frequently Asked Questions

Can I use Playwright’s PDF method with Firefox or WebKit?

Playwright documents multiple browser engines, but PDF method availability and behavior should be checked against its current API documentation for the engine you plan to use.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Is ScreenshotNeo a Python library?

No. It is a website screenshot API and MCP server; it can capture webpages as PDFs, but it is not a drop-in replacement for an in-process HTML-to-PDF renderer.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.