Use WeasyPrint when your HTML is a document template and you want a direct Python API; use Playwright when the output depends on JavaScript and real browser layout. WeasyPrint can render a string, file, or URL with HTML.write_pdf(). Playwright controls a browser, so it requires both the Python package and browser binaries. xhtml2pdf is another Python-library option, while wkhtmltopdf is mainly a legacy integration to keep deliberately isolated.
This guide shows a minimal working conversion, explains installation and deployment trade-offs, and covers security controls for user-supplied HTML.
Choose the rendering model first
HTML-to-PDF conversion is not one operation. A document renderer interprets HTML and CSS directly; browser automation loads a page in an actual browser engine and prints it. Your choice should follow the content you need to reproduce.
| Route | Consider it when | Operational cost and cautions |
|---|---|---|
| WeasyPrint | You generate reports, invoices, letters, or other mostly static HTML/CSS. | Install Python plus platform-native libraries. Restrict URL fetching and isolate untrusted input. |
| Playwright with Chromium | The page needs JavaScript, browser APIs, or the same layout behavior users see in a browser. | Install the package, browser binaries, and any required system dependencies; manage browser processes. |
| xhtml2pdf | You want a Python library based on ReportLab and your templates fit its supported HTML/CSS model. | The project documents Python 3.10+ as tested and recommends the pycairo extra for its Cairo backend. |
| wkhtmltopdf | An existing legacy system already depends on it. | The official downloads page lists 0.12.6 (released June 11, 2020) and warns not to process untrusted HTML. Do not make it an unexamined default for new work. |
No official source here establishes a universal fidelity winner. Render a representative template containing your real fonts, images, tables, page breaks, and scripts, then compare the resulting PDFs and deployment burden.
The Tool Desk
Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →#1 Best Overall
- Read Before You Buy — No Video Output: These adapters support charging and USB 2.0 data transfer, but cannot transmit video signals. Except for standard USB webcams (which use USB data only), they are not compatible with HDMI/DisplayPort cables, video-capable USB-C hubs, or docking stations with video output.
- Convert USB-A Ports to USB-C: Designed to connect USB-C earphones, cables, flash drives, card readers, and other USB-C accessories to standard USB-A ports. Plug-and-play with no drivers or software required.
- Aluminum Alloy Housing: Built with a sturdy aluminum alloy shell that aids in heat dissipation and protects against daily wear and scratches. Designed to maintain a stable and secure connection.
- Compact & Travel-Friendly: The ultra-compact design allows the adapter to stay plugged into your device without blocking adjacent ports or adding bulk, reducing wear and tear on your original USB ports.
- 12-Month Warranty: Backed by a 12-month manufacturer warranty for peace of mind. Designed to meet strict quality control standards for reliable everyday performance.
Convert HTML with WeasyPrint
Install the package and native prerequisites
Install WeasyPrint in your virtual environment:
python -m pip install weasyprint
That command may not be sufficient. The current WeasyPrint installation documentation describes Python and Pango requirements and different setup procedures for Linux, macOS, and Windows. Follow the instructions for the operating system and pin the versions in the image or machine that will run your service.
Minimal string-to-PDF example
Once installed, the smallest useful conversion is:
from weasyprint import HTML
html = """
<h1>Report</h1>
<p>Generated from Python.</p>
"""
HTML(string=html).write_pdf("report.pdf")
The documented API is to create an HTML object and call HTML.write_pdf() to produce one PDF file. The output path can be relative or absolute. For a bytes response in a web application, write to an in-memory buffer instead of a filename:
from io import BytesIO
from weasyprint import HTML
buffer = BytesIO()
HTML(string=html).write_pdf(buffer)
pdf_bytes = buffer.getvalue()
Render a file or URL
Use the corresponding HTML input form when your source is not a string:
from weasyprint import HTML
HTML(filename="templates/report.html").write_pdf("report.pdf")
HTML(url="https://example.com/report").write_pdf("remote-report.pdf")
Relative images, stylesheets, and fonts need a sensible base URL. When constructing from a string, provide one so relative links resolve:
from weasyprint import HTML
HTML(string=html, base_url="/srv/app/templates").write_pdf("report.pdf")
Load web fonts deliberately
For CSS @font-face, the documentation demonstrates sharing a FontConfiguration between the HTML and CSS objects:
Rank #2
- 5-in-1 USB-C Hub: Experience comprehensive connectivity featuring a Power Delivery input, two USB-A 2.0 ports, a USB-A 3.0 port, and an HDMI port. (Note: The USB-C power delivery input port is only for connecting an external wall charger to power your laptop and cannot power peripheral devices.)
- 90W Pass-Through Charging: Achieve optimal charging with 90W pass-through power to your laptop, supported by a total input of 100W, with the hub reserving 10W for operational efficiency. (Note: Wall charger not included.)
- Quick Data Transfers: Accelerate your productivity with rapid data transfers using a high-speed 5Gbps USB 3.0 port and two 480Mbps USB 2.0 ports.
- 4K HDMI Display: Enhance your visual experience with a hub capable of delivering 4K resolution at 30Hz in both mirror and extend modes. Please note that this hub is compatible with MacBook (macOS 12 and newer), Windows 10 and 11, ChromeOS, and laptops equipped with DP Alt Mode and Power Delivery. Note: This device is not compatible with Linux.
- What You Get: Anker USB-C Hub (5-in-1, 4K HDMI), welcome guide, 18-month warranty, and our friendly customer service.
from weasyprint import HTML, CSS
from weasyprint.text.fonts import FontConfiguration
font_config = FontConfiguration()
css = CSS(filename="styles/print.css", font_config=font_config)
HTML(filename="templates/report.html").write_pdf(
"report.pdf",
stylesheets=[css],
font_config=font_config,
)
Install the font files in the runtime image or serve them from an allowed location. A PDF that looks different in production often has a missing font or an asset URL that the renderer cannot reach.
Use Playwright when a browser is part of the requirement
Install Python and browser binaries
Playwright is browser automation, not a small HTML parser. Follow the official Python library setup:
python -m pip install playwright
playwright install chromium
The second command downloads the browser binaries. Containers may also need the documented system dependencies. Playwright exposes synchronous and asynchronous APIs; choose one style and manage browser and page lifetimes explicitly.
Recommended Free Tools
Minimal synchronous conversion
from pathlib import Path
from playwright.sync_api import sync_playwright
html = """
Browser-rendered report
JavaScript can run before printing.
"""
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
page.set_content(html, wait_until="load")
page.pdf(path="report.pdf")
browser.close()
For a deployed page, use page.goto(url, wait_until="networkidle") or another condition that matches your application, then call page.pdf(). Consult the current Playwright documentation and Page API reference for print settings supported by the version you pin. Do not assume that installing Playwright installs branded Google Chrome; its browser documentation distinguishes bundled browser builds from branded browsers.
Control browser lifecycle
- Reuse a browser process for a controlled batch, but create a fresh context or page for isolated jobs.
- Close pages, contexts, and the browser in a
finallyblock when conversion errors are possible. - Set application-level timeouts and terminate jobs that never finish loading.
- In asynchronous code, use
async_playwright()and await navigation and PDF operations; do not mix sync and async APIs in one execution path.
xhtml2pdf as a Python-library alternative
xhtml2pdf converts HTML through ReportLab. Its project documentation says Python 3.10+ is tested and guaranteed to work and recommends installing the Cairo extra:
Rank #3
- Sleek 7-in-1 USB-C Hub: Features an HDMI port, two USB-A 3.0 ports, and a USB-C data port, each providing 5Gbps transfer speeds. It also includes a USB-C PD input port for charging up to 100W and dual SD and TF card slots, all in a compact design.
- Flawless 4K@60Hz Video with HDMI: Delivers exceptional clarity and smoothness with its 4K@60Hz HDMI port, making it ideal for high-definition presentations and entertainment. (Note: Only the HDMI port supports video projection; the USB-C port is for data transfer only.)
- Double Up on Efficiency: The two USB-A 3.0 ports and a USB-C port support a fast 5Gbps data rate, significantly boosting your transfer speeds and improving productivity.
- Fast and Reliable 85W Charging: Offers high-capacity, speedy charging for laptops up to 85W, so you spend less time tethered to an outlet and more time being productive.
- What You Get: Anker USB-C Hub (7-in-1), welcome guide, 18-month warranty, and our friendly customer service.
python -m pip install "xhtml2pdf[pycairo]"
Verify the current backend requirements for your operating system. Choose this route when its supported markup and CSS match your templates; otherwise, prototype the exact document before committing to it.
Prepare production templates
Make assets deterministic
- Use absolute or controlled relative URLs for images, stylesheets, and fonts.
- Bundle required assets in the deployment image when possible instead of depending on an external website.
- Specify page size, margins, and print-oriented CSS with
@page. - Include a character set declaration and test non-ASCII text, long tables, and page breaks.
- Record the renderer, Python, native-library, browser, and font versions with your build.
Separate document data from markup
Generate HTML from structured values using a template engine and escaping appropriate to HTML. Do not concatenate untrusted values into style blocks, attributes, or scripts. A PDF conversion service should have a bounded input size, render timeout, and output-size limit.
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 minuteSecurity: treat HTML and CSS as untrusted
WeasyPrint documents that URL fetching can access local files through file://; untrusted HTML/CSS can probe local files or embed attachments. Its guidance is to isolate the rendering process with sandboxing and provide a restrictive custom URL fetcher that blocks or filters access. It also warns about long renderings and resource exhaustion.
Apply controls at the service boundary:
- Run conversion in a low-privilege worker or container with a read-only, minimal filesystem.
- Disable access to local paths and private network ranges unless a specific, reviewed feature requires them.
- Allow only approved schemes, hosts, MIME types, and response sizes for remote resources.
- Enforce CPU, memory, wall-clock, page-count, and output-size limits.
- Keep browser automation jobs isolated; never expose a powerful host session to arbitrary page scripts.
The official wkhtmltopdf downloads page gives an especially direct warning: “Do not use wkhtmltopdf with any untrusted HTML – be sure to sanitize any user-supplied HTML/JS, otherwise it can lead to complete takeover of the server it is running on!” Treat that warning as a reason to sandbox legacy integrations, not as a reason to pass raw user content to the binary.
Reliability, performance, and cost decisions
Rendering speed
Direct renderers generally avoid the startup and memory cost of a full browser, while Playwright adds browser lifecycle overhead. The actual result depends on document size, fonts, images, scripts, and network access. Measure your own representative workload rather than relying on a generic benchmark.
Rank #4
- Dual Converters, Infinite Potential:Includes 2× USB C male to USB A female adapters and 2× USB A male to USB C female adapters. Perfect for a wide range of uses—tablets with Bluetooth keyboards, expand USB ports on macbook, and more. Two different converters for all your daily needs
- Next-Level 10Gbps & 3A Charging: No more slow 480Mbps, this usb to usb c adapter has a transfer speed of up to 10Gbps, allowing you to do more transferring in less time. This usb adapter fits both USB A and USB C charger, supporting up to 3A fast charging
- Upgraded Exquisite Craftsmanship: With an aluminum alloy housing and metal connector, the usbc to usb adapter is extremely durable and sturdy. Rigorously tested to withstand more than 10,000 times of plugging and unplugging, ensuring long-lasting performance
- Broad Compatible: The usb c to usb adapter widely supports all USB C/ USB A devices like laptops, tablets, cellphones, car chargers, and phone chargers. Such as compatible with MacBook Pro/Air 2023/2022, Thunderbolt 4/3 Devices,Apple MagSafe Watch 9/8/7/SE/Ultra, iPad Pro 2022/2021, Samsung Galaxy S23/S20/S10, and iPhone 17/16/15 Pro. Plug and play
- Please Note: To reach 10Gbps speed, keep the cable under 3.3 ft. For USB A Male to USB C adapters, try flipping the USB C connector. USB C Male to USB A adapters support bidirectional 10Gbps transfer within 3.3 ft
Repeatable output
Pin Python packages, native libraries, browser binaries, fonts, and templates. Capture a small set of reference PDFs in CI and inspect page count, text, images, and important layout boundaries after upgrades.
Scaling jobs
Use a queue for untrusted or expensive conversions. Limit concurrent browser pages, recycle workers after a defined number of jobs, and monitor timeout and memory failures. Cache identical, immutable inputs only when the cache key includes the template, data, asset versions, and renderer versions.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Troubleshooting common failures
ImportError or missing Pango/Cairo libraries
Your Python package is present but native dependencies are not. Recheck the platform-specific WeasyPrint installation page, or install the xhtml2pdf Cairo extra and its system prerequisites.
Playwright reports that an executable is missing
Run playwright install chromium in the same environment used by the application. In a container, install the browser during image build and verify that the runtime user can read it.
Images or CSS are absent
Check the HTML base URL, URL scheme, filesystem permissions, certificate validation, and your allowlist. Log failed resource requests without exposing sensitive document contents.
Best Value
- 5-in-1 Connectivity: Equipped with a 4K HDMI port, a 5 Gbps USB-C data port, two 5 Gbps USB-A ports, and a USB C 100W PD-IN port. Note: The USB C 100W PD-IN port supports only charging and does not support data transfer devices such as headphones or speakers.
- Powerful Pass-Through Charging: Supports up to 85W pass-through charging so you can power up your laptop while you use the hub. Note: Pass-through charging requires a charger (not included). Note: To achieve full power for iPad, we recommend using a 45W wall charger.
- Transfer Files in Seconds: Move files to and from your laptop at speeds of up to 5 Gbps via the USB-C and USB-A data ports. Note: The USB C 5Gbps Data port does not support video output.
- HD Display: Connect to the HDMI port to stream or mirror content to an external monitor in resolutions of up to 4K@30Hz. Note: The USB-C ports do not support video output.
- What You Get: Anker 332 USB-C Hub (5-in-1), welcome guide, our worry-free 18-month warranty, and friendly customer service.
JavaScript content is blank
Use Playwright, wait for the application’s actual readiness condition, and ensure the required API requests are allowed. A static renderer will not execute browser JavaScript.
Fonts wrap differently in production
Install the same font files and configure @font-face consistently. Confirm that the renderer can read the font URLs and that the PDF worker is using the intended font configuration.
Jobs hang or consume excessive memory
Set navigation and render timeouts, cap input and output sizes, block unnecessary resources, and terminate the isolated worker when limits are exceeded. Investigate pathological CSS, enormous images, recursive resources, and scripts that never become idle.
Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server. One GET request can return a PNG, JPEG, WebP, or PDF, which is useful when the source is already a publicly reachable web page and you do not want to package a browser.
Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchPC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11cURL (see the ScreenshotNeo documentation):
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Python:
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
Node.js:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));
- Cookie banners, newsletter popups, and chat widgets are removed before the shot; each cleanup step can be turned off.
- Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed as clean shots; response headers identify the page verdict and billing status.
- An MCP server provides
take_screenshot,get_page_info, andcapture_pdftools for Claude, Cursor, and other MCP clients. - The Free plan includes 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots. Every feature is available on every plan.
Create a free ScreenshotNeo account to try the 1,000 monthly shots without a card.
Frequently Asked Questions
Can WeasyPrint execute JavaScript before creating the PDF?
No. Use Playwright or another browser automation route when the document depends on JavaScript execution, browser APIs, or client-side rendering.
Can I return the generated PDF directly from a Python web endpoint?
Yes. Render into a BytesIO buffer, then return its bytes with a PDF content type and a suitable download disposition.
Which option should I select for arbitrary user HTML?
None is safe by default. Isolate the renderer, restrict filesystem and network access, validate input, and enforce resource limits before accepting untrusted markup.
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.

