Free tools Windows power users keep installed
One-click scans. No signup required.
Use WeasyPrint to convert a local HTML file to a PDF in Python. Install it in the environment that will run your script, then pass the file to HTML and call write_pdf:
from weasyprint import HTML
HTML(filename="input.html").write_pdf("output.pdf")
This creates output.pdf beside your script (unless you provide another path). The rest of this guide covers installation, relative assets, print CSS, security, batch conversion, troubleshooting, and an API alternative when you do not want to manage a browser or rendering environment.
1. Install WeasyPrint in the right Python environment
Install the package with the interpreter that will execute your conversion code:
python -m pip install weasyprint
The current WeasyPrint 70.0 documentation lists Python 3.10 or newer, Pango 1.44 or newer, and additional Python and native dependencies. On Linux, the distribution package can be the simplest route. If pip installation fails, check the operating-system installation instructions and confirm that the required native libraries are available.
#1 Best Overall
Verify the environment before converting:
python --version
weasyprint --info
Run these commands inside the same virtual environment, container, or deployment image used by your application. Dependency requirements can change, so consult the current WeasyPrint installation documentation for your operating system and record the versions you deploy.
2. Convert one local HTML file
Minimal script
from weasyprint import HTML
HTML(filename="input.html").write_pdf("output.pdf")
The filename can also be supplied positionally, as in HTML("input.html"). Using filename= makes the source explicit and is easier to extend when you later add options.
Use absolute paths when the working directory is uncertain
from pathlib import Path
from weasyprint import HTML
source = Path("/srv/reports/input.html").resolve()
target = Path("/srv/reports/output.pdf").resolve()
HTML(filename=str(source)).write_pdf(str(target))
print(f"Wrote {target}")
Resolve paths deliberately in web workers, scheduled jobs, and containers: a process’s current directory may not be the directory containing your Python file.
3. Make CSS, images, and fonts resolve correctly
An HTML document often references styles.css, images, or fonts with relative URLs. Keep the source file and its asset tree together, use correct relative references, and inspect the PDF rather than assuming that a browser-like environment will find every resource.
<link rel="stylesheet" href="css/print.css">
<img src="images/logo.png" alt="Company logo">
For a document at /app/report/input.html, the paths above point to /app/report/css/print.css and /app/report/images/logo.png. If assets are generated, write them before calling write_pdf. For remote resources, ensure the rendering environment can reach them and that authentication, certificates, and firewall rules permit access. The official documentation confirms filename input, but does not guarantee every possible relative-resource arrangement; test your actual layout.
Rank #2
Use print-specific CSS
@media print {
.screen-only { display: none; }
a { color: black; text-decoration: none; }
}
@page {
size: A4;
margin: 18mm 16mm;
}
h1, h2 { break-after: avoid; }
Print CSS controls pagination and removes controls that make sense only on screen. Test long tables, headings near page bottoms, images, fonts, and page breaks with representative documents.
4. Convert HTML held in a string
When your application generates markup instead of reading a file, pass the string to HTML(string=...) and provide a base_url that lets relative assets resolve:
from pathlib import Path
from weasyprint import HTML
html = """
<!doctype html>
<html><head>
<link rel="stylesheet" href="css/print.css">
</head><body>
<h1>Monthly report</h1>
<p>Generated by Python.</p>
</body></html>
"""
HTML(string=html, base_url=str(Path("/srv/reports").resolve())).write_pdf(
"/srv/reports/monthly-report.pdf"
)
Without a suitable base URL, a relative stylesheet or image may not be found. Sanitize or template generated HTML according to your application’s trust model.
5. What WeasyPrint supports—and where it stops
WeasyPrint is an HTML/CSS-to-PDF renderer, not a promise that every arbitrary web page will look exactly like it does in Chrome or Firefox. Its documentation states that generated-document validity is not guaranteed for every combination of HTML, CSS, and PDF features; the features you use must fit the specifications and the implementation’s limits. Validate a representative output before committing to a layout.
The API reference lists hyperlinks, bookmarks, attachments, forms, text, raster graphics, and vector graphics among content that PDFs can contain. That is a capability description, not a guarantee that every source document transfers perfectly. JavaScript-driven pages may need to be rendered to static HTML first, because this workflow does not establish browser-level execution of arbitrary client-side code.
6. Security for untrusted HTML and CSS
The WeasyPrint first-steps documentation warns: “Using WeasyPrint with untrusted HTML or untrusted CSS may lead to various security problems.” Treat user-submitted markup as hostile. Run conversion in an isolated worker or container, restrict network access and filesystem visibility, limit document size and conversion time, and control which URLs or resource types can be fetched. Do not expose a conversion endpoint that can freely read local files or reach internal services.
For trusted, internally generated reports, still validate URLs and resource paths. Log failures without including secrets embedded in URLs or headers.
The Tool Desk
Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →7. Convert many files efficiently
For repeated conversions, keep a long-lived Python process instead of starting a fresh interpreter for every document. The WeasyPrint documentation identifies this as a way to avoid paying startup costs on each conversion; it does not establish a quantified speed improvement.
from pathlib import Path
from weasyprint import HTML
input_dir = Path("reports/html")
output_dir = Path("reports/pdf")
output_dir.mkdir(parents=True, exist_ok=True)
for source in input_dir.glob("*.html"):
destination = output_dir / f"{source.stem}.pdf"
HTML(filename=str(source)).write_pdf(str(destination))
print(f"{source.name} -> {destination.name}")
For production queues, add bounded concurrency, per-document timeouts at the worker layer, retries only for transient resource failures, and output checks such as file existence and a nonzero size. Do not run unlimited conversions in one process without monitoring memory and disk use.
8. Troubleshooting common failures
ModuleNotFoundError: No module named 'weasyprint'
Your script is using a different interpreter from the one where you installed the package. Run python -m pip install weasyprint with that interpreter, activate the intended virtual environment, and check python -c "import weasyprint; print(weasyprint.__version__)".
Native-library or Pango errors
Install the operating-system dependencies listed for your platform in the current WeasyPrint installation guide. Confirm Pango is at least the version required by the documentation, then rerun weasyprint --info.
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 →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Missing images, CSS, or fonts
Check the spelling and case of every path, the process working directory, and the document’s base URL. Prefer absolute, known-good paths while diagnosing. Confirm that the worker can read local files and reach approved remote resources.
Blank or incomplete pages
Open the generated PDF and inspect the HTML independently. Look for invalid markup, CSS that hides content in print, external resources that time out, and oversized elements that cannot fit the page. Reduce the document to a small reproduction, then add styles and assets back incrementally.
Layout differs from the browser
Check print media rules and unsupported CSS features. Replace fragile screen-only layout with print-oriented styles, explicit page sizes and margins, and tested page-break rules. Do not claim pixel-perfect browser parity without validating the exact documents you produce.
Conversion is slow or consumes too much memory
Reduce unnecessary remote assets, resize very large images, split exceptionally large documents, and reuse a long-lived worker. Measure your own workload; the available documentation supplies no universal timing or memory figure.
Best Value
9. A hosted option when you do not want browser setup
Or skip the browser setup:
If your real input is a publicly reachable web page rather than a local file, ScreenshotNeo can return a screenshot or PDF through one request. It accepts cookie and consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. Every response identifies the page verdict and billing status with X-Page-Verdict and X-Billed headers.
See the complete parameter list in the ScreenshotNeo documentation. The same endpoint supports full-page capture with lazy images, CSS-selector elements, dark mode, device presets or custom viewports, retina scale, PDF paper size, margins, landscape mode and page ranges, custom CSS and JavaScript, clicks, waits, blocking rules, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, chosen cache TTLs, signed links, asynchronous webhooks, bulk capture of up to 100 URLs per call, usage reporting, and an OpenAPI specification.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
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)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
The Free plan includes 1,000 shots per month with no card. Paid plans start at $5 for 3,000 shots; yearly billing provides two months free, and every feature is included on every plan. Sign up free for ScreenshotNeo.
10. Practical validation checklist
- Install and verify WeasyPrint in the deployment environment.
- Convert a representative HTML file and open the PDF.
- Check page size, margins, page breaks, fonts, images, links, and bookmarks where relevant.
- Test missing or slow resources and decide how your worker handles them.
- Isolate untrusted HTML/CSS and restrict filesystem and network access.
- For batch jobs, reuse a long-lived process and monitor resource consumption.
Frequently Asked Questions
Can I convert an HTML file without installing a browser?
Yes. WeasyPrint renders HTML/CSS directly from Python and its documented minimal call is HTML(filename="input.html").write_pdf("output.pdf"). You still need WeasyPrint’s Python and native dependencies.
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Scan for outdated or missing drivers - takes under a minute3Clear out junk files and repair common Windows errorsWhy does my PDF not match Chrome exactly?
WeasyPrint has its own HTML/CSS implementation and documented limits. Use print CSS and validate the exact layouts, fonts, assets, and page breaks your application generates.
Is WeasyPrint safe for user-uploaded HTML?
Not by default. The project warns that untrusted HTML or CSS can create security problems. Isolate conversion, restrict resources, and enforce size and time limits.
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.




