For static, document-shaped HTML and CSS, use WeasyPrint: install its Python package and required native libraries, create an HTML object, then call write_pdf(). If the page needs JavaScript or browser APIs, use Puppeteer to print it with Chromium instead. In either case, set up asset URLs and print CSS deliberately, verify the rendered pages, and isolate untrusted input.
Choose a renderer that matches the page
HTML-to-PDF conversion is not one uniform operation. A report or invoice is usually a paged document: its layout should flow across pages, repeat headers where needed, and handle margins and page breaks. A JavaScript application, by contrast, may need to run in a browser before its content exists. The first decision is whether you need a document renderer or a browser.
| Option | Best fit | Important trade-off |
|---|---|---|
| WeasyPrint | Python projects producing documents from HTML and CSS | Uses a paged-media rendering model, not a full browser; check its support for the CSS features your document uses. |
| Puppeteer | Pages that need JavaScript, browser APIs, or Chromium-compatible rendering | Requires a compatible Chromium installation and explicit decisions about print versus screen styles. |
| wkhtmltopdf | Legacy applications that depend on its existing output | Its stable 0.12.6 series was released June 11, 2020; treat it as a compatibility choice rather than the default for a new system. |
For a Python-first service that produces invoices, certificates, or reports from templates, start with WeasyPrint. Choose Puppeteer if the page is effectively a web app that must render in a browser before printing. There is no single renderer that guarantees identical output for every CSS feature: test the actual templates, fonts, images, and page breaks you rely on.
Convert HTML to PDF with WeasyPrint in Python
WeasyPrint is suited to HTML and CSS designed as paged documents. It exposes a Python API as well as a command-line program. Installation may require native Pango-related libraries in addition to the Python package; the exact system packages vary by operating system and deployment image.
Do these 3 things before closing this tab:
1Fix the driver behind crashes, sound loss and screen glitches2Repair Windows errors before they cause bigger problems3Scan for outdated or missing drivers - takes under a minute#1 Best Overall
Install and check the environment
- Install Python and the Pango-related native dependencies required for your operating system.
- Install the Python package:
pip install weasyprint. - Check the detected environment with
weasyprint --info. Resolve missing-library errors before wiring conversion into an application.
The command below assumes the native prerequisites are already installed. Keep the input HTML and its assets together, or use absolute URLs for remote assets.
Runnable Python example
from pathlib import Path
from weasyprint import HTML
source = Path("invoice.html")
output = Path("invoice.pdf")
HTML(filename=str(source), base_url=str(source.resolve().parent)).write_pdf(output)
print(f"Wrote {output.resolve()}")
Setting base_url gives relative references such as images/logo.png a location to resolve against. Without a deliberate base URL, local images, stylesheets, or fonts may be missing even when the HTML itself converts. You can also use absolute asset URLs, provided the conversion environment is permitted to reach them.
Use print CSS to control pages
Specify paper size, margins, and page-specific layout rather than expecting screen dimensions to map neatly to paper. For example:
Rank #2
@page {
size: A4;
margin: 18mm;
}
@media print {
.screen-only { display: none; }
h1, h2 { break-after: avoid; }
table, figure { break-inside: avoid; }
}
Adapt page size and margins to the document and the region where it will be printed. Break-avoidance rules are preferences, not a guarantee that a large element can fit on one page. For long tables, test how rows split and whether the header remains useful. Check the CSS support of your chosen WeasyPrint release for advanced layout properties rather than assuming every browser feature is implemented.
Use the command line for a quick conversion
For a file-based job, the installed executable can convert a source file directly:
weasyprint invoice.html invoice.pdf
For a Python application, the API is usually easier to integrate with template rendering, logging, and application-level validation. Either path still needs access to the fonts and other resources referenced by the document.
Use Puppeteer when the page needs a browser
WeasyPrint is not a substitute for running a JavaScript application. If the content appears only after scripts execute, or the layout depends on browser APIs and Chromium behavior, Puppeteer can load the page in a browser and create a PDF with Page.pdf(). Puppeteer uses the print CSS media type by default. If you need the screen stylesheet, explicitly select it with page.emulateMediaType('screen').
Install Puppeteer
In a new Node.js project, install Puppeteer:
npm install puppeteer
Use a compatible Chromium installation in the runtime environment. Whether Chromium is installed or downloaded as part of your setup depends on how Puppeteer is installed and deployed; check that the browser can launch in the same container or host that runs the conversion.
Free tools Windows power users keep installed
One-click scans. No signup required.
Runnable browser-to-PDF example
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({ headless: true });
try {
const page = await browser.newPage();
await page.goto('https://example.com/report', {
waitUntil: 'networkidle0',
timeout: 60000,
});
// Use this when the PDF should match screen CSS instead of print CSS.
// await page.emulateMediaType('screen');
await page.evaluate(() => document.fonts.ready);
await page.pdf({
path: 'report.pdf',
format: 'A4',
printBackground: true,
margin: { top: '18mm', right: '18mm', bottom: '18mm', left: '18mm' },
});
} finally {
await browser.close();
}
})();
Replace the example URL with the page you control. Wait for the application’s data and assets, not just initial navigation: some pages continue loading content after the network becomes quiet. If the page has a reliable completion signal, wait for that selector or application state before printing. Waiting for document.fonts.ready helps avoid capturing before web fonts finish loading; still inspect the PDF for missing or substituted fonts.
Rank #4
Make the output reliable and safe
Check assets, pagination, and PDF behavior
- Relative resources: give WeasyPrint an intentional base URL, or use absolute asset URLs. Ensure the service can reach remote assets if it needs them.
- Fonts: make fonts available to the renderer and wait for browser font loading when using Puppeteer. Verify the resulting PDF rather than assuming a successful conversion means every font embedded or displayed as intended.
- Page breaks: inspect headings at page bottoms, table splits, oversized images, and blank pages. Adjust the source content and print CSS, then re-render.
- Visual styling: Puppeteer prints using print media by default. Select screen media only when that is the intended appearance; WeasyPrint also requires CSS features to be supported by its renderer.
- Document requirements: check whether the chosen renderer meets your needs for hyperlinks, bookmarks, attachments, forms, PDF/A, or PDF/UA. WeasyPrint documents support for these capabilities, but verify the specific output requirement and workflow you need.
Do not treat user HTML as harmless
HTML and CSS can reference files and network resources, and scripts may be active in browser-based workflows. WeasyPrint warns about security problems with untrusted sources. The wkhtmltopdf project warns that unsafe HTML or JavaScript can lead to server takeover. Treat submitted markup, stylesheets, and URLs as hostile input.
- Sanitize or reject user-supplied HTML and CSS according to your application’s needs.
- Run conversion in an isolated process or container with limited filesystem permissions and no unnecessary secrets.
- Restrict outbound network access and local-file access; do not let arbitrary input choose resources the service can reach.
- For Puppeteer, avoid executing untrusted pages in a privileged environment, and limit browser permissions and runtime resources.
- Apply timeouts and resource limits so a slow, oversized, or resource-intensive page cannot monopolize a worker.
Do not use wkhtmltopdf for untrusted HTML unless you have addressed the project’s security warning and have appropriate isolation. Its older WebKit behavior may still matter when preserving a legacy output, but that is a reason to test a migration carefully, not to assume current browser compatibility.
Or skip the browser setup
If the input is a public webpage and you need a rendered capture rather than a locally controlled HTML-to-PDF pipeline, ScreenshotNeo offers a screenshot API and MCP server. It can return screenshots or PDFs; its PDF options include paper size, margins, landscape, and page ranges. The example below is specifically a screenshot request saved as WebP, not a PDF request. Use the documentation for the PDF request configuration.
Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Repair Windows errors before they cause bigger problemsFix Now →Best Value
cURL example:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Python example:
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
Node.js example:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
See the ScreenshotNeo API documentation for request options. Before a capture, it accepts cookie or consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each of those steps can be turned off. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and responses identify the page verdict and billing status in headers. Its MCP server exposes take_screenshot, get_page_info, and capture_pdf for AI agents and MCP clients.
The free plan includes 1,000 screenshots per month without a card; paid plans start at $5 for 3,000. These are plan allowances, not a replacement for testing whether a remote page is suitable for a formal document workflow. Sign up for ScreenshotNeo’s free plan.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Troubleshoot common conversion failures
| Symptom | Likely cause | What to check |
|---|---|---|
WeasyPrint fails to import or weasyprint --info reports missing libraries |
Native dependencies are missing or unavailable in the runtime environment. | Install the Pango-related dependencies for that operating system, then rerun the environment check in the same container or host as the app. |
| Images, CSS, or fonts are absent | Relative paths have no usable base URL, or assets cannot be reached. | Set base_url, use valid absolute URLs, and verify filesystem and network access from the conversion process. |
| PDF content is blank or incomplete | The page may depend on JavaScript or data that has not loaded. | Use Puppeteer for browser-dependent pages and wait for the application’s data-ready signal before printing. |
| Browser PDF looks different from the visible page | Page.pdf() uses print media by default, and print CSS can change layout. |
Decide whether print or screen styles are intended; if screen styling is required, call page.emulateMediaType('screen') before generating the PDF. |
| Text uses unexpected fonts | Fonts may not be installed, accessible, or loaded at capture time. | Make fonts available to the renderer, wait for browser font readiness, and inspect the generated PDF. |
| Tables or sections split awkwardly | Page-break rules, available page space, and renderer support affect pagination. | Revise print CSS and content dimensions, then test representative long and short documents. |
| Conversion hangs or consumes excessive resources | A page may be waiting indefinitely on resources, or input may be too costly to render. | Set navigation and job timeouts, constrain resource access, and apply process-level resource limits. |
Validate before shipping
A successful call only establishes that a PDF file was produced; it does not establish that the document is readable or compliant. Add representative documents to your release checks, including long tables, page-boundary headings, missing or slow assets, and the fonts used in production. Inspect page count and visual pagination, confirm links and bookmarks when required, and test attachments or forms if your workflow depends on them. For accessibility or archival targets such as PDF/UA or PDF/A, validate against the requirement with an appropriate checker rather than relying on the file extension.
There is no performance figure that applies to all these renderers and inputs. Runtime depends on page complexity, assets, fonts, JavaScript execution, and deployment resources; benchmark your own templates under realistic concurrency. For predictable service behavior, cache only where the source and freshness rules allow it, cap concurrent jobs, and record failures with enough detail to distinguish renderer errors from unavailable assets.
Frequently asked questions
Can I convert an HTML string without saving it to a file?
WeasyPrint’s Python API accepts HTML content as well as a filename. When the content references relative assets, supply a base URL so those references resolve from the intended location.
Does choosing an open-source renderer guarantee an open-source output PDF?
No. The renderer’s licensing and the rights to the HTML, fonts, images, and other assets are separate considerations. Check the relevant licenses for your application and its inputs.
Is there a universal best library for HTML-to-PDF?
No. Use a paged-media renderer for document-oriented HTML/CSS and a browser engine for pages that require browser execution. Validate the CSS and PDF features that matter to your own output.
Quick Recap
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.

