October DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsWindows FixRecommendedWindows errors stealing your time? Find the fix fastScan stability, cleanup and performance issues.Fix NowOctober DealsAmazon USDeal season is back - check today's better picksAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
GitHub Projects

How to Convert HTML to PDF in Python and Track the Work with GitHub Projects

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

Use a document renderer in Python, then use GitHub Projects to manage the implementation. For CSS-focused reports, WeasyPrint is the shortest path: HTML(filename="report.html").write_pdf("report.pdf"). If your page depends on browser JavaScript or browser-only layout, use Playwright and its page.pdf() API instead. GitHub Projects does not render HTML or create PDFs; it organizes the issues, decisions, tests and documentation around your converter.

Choose the renderer before you create the project

The right library depends on what “HTML” means in your application. A static report with conventional CSS is a good WeasyPrint candidate. A page that must execute JavaScript, use browser APIs or match Chromium’s layout should be tested with Playwright.

Concern WeasyPrint Playwright
Rendering model HTML/CSS document renderer Real browser page and print pipeline
Python API HTML(...).write_pdf(...) page.pdf()
Environment Python package plus operating-system libraries such as Pango; requirements vary by platform Python package plus browser binaries
Media behavior CSS is interpreted by WeasyPrint’s document engine page.pdf() uses print media by default; call page.emulate_media(media="screen") when screen styles are required
Best first test Representative CSS, fonts, images and page breaks Representative JavaScript, network-loaded assets and browser-specific layout

Neither choice is universally faster or more faithful. Render a representative document, not just a toy page, before committing to one.

Prepare a reproducible Python environment

WeasyPrint prerequisites

The current WeasyPrint first-steps documentation lists Python 3.10 or later and dependencies including Pango, pydyf, CFFI, tinyhtml5, tinycss2, cssselect2, Pyphen, Pillow and fontTools. Operating-system packages differ, so follow the current installation instructions for your platform rather than assuming that pip installs every native dependency.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
python -m venv .venv
# macOS/Linux
. .venv/bin/activate
# Windows PowerShell
# .venvScriptsActivate.ps1
python -m pip install --upgrade pip
python -m pip install weasyprint
weasyprint --info

weasyprint --info is a useful environment check. Commit a short setup note to the repository so another contributor can reproduce the same prerequisites.

Playwright prerequisites

python -m venv .venv
. .venv/bin/activate
python -m pip install playwright
playwright install chromium

The package and browser installation are separate steps. In CI, install the browser in the job that performs conversion and cache it only when your runner policy permits that.

Convert a local HTML file with WeasyPrint

Minimal conversion

This is the smallest useful implementation documented by WeasyPrint:

from weasyprint import HTML

HTML(filename="report.html").write_pdf("report.pdf")

Run it with python convert.py. A successful run writes report.pdf in the current directory. Treat the output path as an explicit build artifact rather than silently overwriting a production document.

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

Use a string and make relative assets resolve

When HTML is generated by Python, pass a string and set a base URL so relative stylesheets, images and fonts have a known origin.

from pathlib import Path
from weasyprint import HTML

html = Path("templates/report.html").read_text(encoding="utf-8")
HTML(string=html, base_url=str(Path("templates").resolve())).write_pdf("build/report.pdf")

Create the build directory before writing it, and use UTF-8 consistently. If you pass a URL instead, confirm that every asset is reachable from the conversion environment; a browser that can reach an asset on your laptop may not be able to reach it in CI.

Control paper size, orientation and margins with CSS

@page {
  size: A4 portrait;
  margin: 2cm;
}

@page landscape-report {
  size: A4 landscape;
  margin: 1.5cm;
}

.landscape-page {
  page: landscape-report;
}

h1, h2 {
  break-after: avoid;
}

table, figure {
  break-inside: avoid;
}

WeasyPrint documents @page as the control for page format, orientation and margins. Keep print-specific rules in a dedicated stylesheet and inspect the PDF at the target paper size; a layout that looks correct on screen can still produce an orphaned heading or an unwanted table split.

Generate from an HTTP page

from weasyprint import HTML

HTML(url="https://example.com/report").write_pdf("report.pdf")

Remote conversion introduces network and trust boundaries. Prefer a local, versioned template for deterministic builds. If you must fetch remote content, set timeouts and allow only the hosts your application needs at the surrounding application or network layer.

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.

Use Playwright when a browser engine is required

Playwright’s Python API starts a browser, loads the page and creates a PDF. The PDF operation uses print CSS media by default.

from pathlib import Path
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch()
    page = browser.new_page()
    page.goto("file:///" + str(Path("report.html").resolve()), wait_until="networkidle")
    page.pdf(path="report.pdf", format="A4", print_background=True)
    browser.close()

For an HTTP page, replace the file:///... URL with the page URL. If the design is written for screen media, emulate it before calling page.pdf():

page.emulate_media(media="screen")
page.pdf(path="report.pdf", format="A4", print_background=True)

Wait for the condition that actually means “ready”: a selector, an application-specific completion flag or network idle. Network idle alone may be insufficient for pages that render after a background task.

Make the converter testable

Keep a small fixture and a representative fixture

  • Minimal fixture: headings, paragraphs and one page break to detect a broken installation.
  • Representative fixture: your real fonts, images, long tables, links, lists and any JavaScript-driven content.
  • Failure fixture: a missing image, an unreachable URL and an intentionally long table so error handling is visible.

Check output properties

  • Assert that the PDF file exists and is larger than zero bytes.
  • Open it with a PDF parser in tests and verify the expected page count or text, rather than relying only on a successful process exit.
  • For visual regressions, store a reviewed baseline or render pages to images and compare them with a deliberately chosen tolerance.
  • Record the renderer version, Python version, operating system and installed fonts with the build metadata. Font changes can alter line wrapping and page count.

Do not claim pixel identity across renderers. Decide which properties matter—text presence, page count, margins, key visual regions—and test those properties explicitly.

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

Organize the implementation in GitHub Projects

Create a project for the conversion effort and use issues for decisions and verifiable work. The project is a planning and visibility layer; the Python code and its tests remain in the repository.

Suggested issue sequence

  1. Select the renderer. Record whether the input needs browser JavaScript, which CSS features are required and what the representative fixture contains.
  2. Create the minimal HTML fixture. Include a known title, a page break and one local asset.
  3. Implement conversion. Add the WeasyPrint or Playwright adapter and a command or function with a clear input and output path.
  4. Define page and asset handling. Decide paper size, margins, fonts, image policy, URL access and how missing assets fail.
  5. Add an output check. Verify file creation and representative content; add a visual review step if layout is critical.
  6. Document environment setup. Include Python version, native libraries or browser binaries, and the exact verification command.
  7. Review security. Assess untrusted HTML/CSS, remote requests, file access, resource limits and isolation before accepting user input.

Useful project fields

Field Example values Why it helps
Status Backlog, Ready, In progress, Review, Done Shows flow without reading every issue
Renderer WeasyPrint, Playwright, undecided Makes the main technical choice visible
Risk Low, Medium, High Highlights security, asset and compatibility work
Validation Not started, Automated, Visual review Prevents “code complete” from being mistaken for “output verified”

Define “done” in each issue

An issue should name its input fixture, expected PDF properties, test command and documentation change. Link pull requests to the issue and move the item only when those checks pass. This keeps renderer experiments and production-ready work separate.

Troubleshoot common failures

Import or shared-library errors with WeasyPrint

Cause: a missing or incompatible native dependency. Fix: follow the current operating-system installation guide, activate the intended virtual environment and run weasyprint --info. Do not solve a system-library error by randomly pinning Python packages.

Images, CSS or fonts are missing

Cause: relative URLs have no usable base, the asset is unreachable, or the font is not installed. Fix: use an absolute base_url for string input, verify paths inside the runner, package required fonts and inspect the generated PDF rather than assuming a warning means the page is correct.

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

Playwright reports that a browser is missing

Cause: the Python package is installed but browser binaries are not. Fix: run playwright install chromium in the same environment used by the converter and repeat the launch test.

The PDF is blank or captures an unfinished page

Cause: conversion occurred before application rendering completed, or the selected media is wrong. Fix: wait for a meaningful selector or application-ready signal; for Playwright, remember that PDF output defaults to print media and explicitly emulate screen media when appropriate.

Layout changes between machines

Cause: different fonts, renderer versions, browser binaries or operating systems. Fix: pin application dependencies, document native requirements, provide fonts where licensing permits and run the representative fixture in the same CI environment used for releases.

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

Handle untrusted HTML as a security boundary

WeasyPrint’s documentation warns that untrusted HTML or CSS can create security problems. Do not treat arbitrary user input as safe merely because the output is a PDF. Sanitize input according to your application’s policy, restrict network access and file visibility, limit document size and conversion time, and isolate the worker from credentials and sensitive host paths. Apply equivalent controls to browser-based conversion, including sandboxing and a narrow outbound network policy. Log failures without storing secrets embedded in URLs or headers.

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

Performance, reliability and cost decisions

  • Reuse a warm worker only after confirming that renderer state, cookies and temporary files cannot leak between jobs.
  • Bound concurrency to the memory and CPU available on the runner; browser processes and large image-heavy documents can be expensive.
  • Cache deterministic inputs using a content hash, but invalidate the cache when templates, fonts, renderer versions or relevant assets change.
  • Separate retryable network failures from deterministic template errors. Retrying malformed HTML rarely helps.
  • Measure your own representative documents. The available documentation does not establish a universal speed or fidelity winner between WeasyPrint and Playwright.

Or skip the browser setup

If the HTML is publicly reachable and you need a hosted capture rather than maintaining a rendering worker, ScreenshotNeo provides a website screenshot API and MCP server. It can return PNG, JPEG, WebP or PDF output; use the response-format options in the API documentation for a PDF response.

One GET request is enough to start a capture:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com/report -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://example.com/report"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://example.com/report' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

Before the capture, ScreenshotNeo accepts the cookie or consent banner like a visitor and removes more than 60 known consent platforms, newsletter popups and chat widgets; each step can be turned off. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads and cache hits cost nothing, and the response identifies the page verdict and billing status with X-Page-Verdict and X-Billed headers. Its MCP server exposes take_screenshot, get_page_info and capture_pdf for Claude, Cursor and other MCP clients. The Free plan includes 1,000 screenshots each month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.

FAQ

Can one project support both renderers?

Yes. Keep a shared input and output contract, then implement separate WeasyPrint and Playwright adapters behind it. This lets a fixture decide which renderer is appropriate without duplicating project tracking.

Should generated PDFs be committed to Git?

Usually no for every build. Commit a small reviewed sample only when it documents a contract; publish routine PDFs as build artifacts and regenerate them from versioned templates.

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

How do I decide whether a visual diff is too large?

Set the threshold from business impact: strict for invoices or regulated forms, looser for prose reports. Have a human review intentional template changes and record the reason beside the approved baseline.

Frequently Asked Questions

Can one project support both renderers?

Yes. Keep a shared input and output contract, then implement separate WeasyPrint and Playwright adapters behind it.

Should generated PDFs be committed to Git?

Usually no for every build; publish routine PDFs as build artifacts and regenerate them from versioned templates.

How do I decide whether a visual diff is too large?

Set the threshold from business impact and review intentional template changes against an approved baseline.

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

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.

Leave a Reply

Your email address will not be published. Required fields are marked *

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

Read next

Recommended PC Tool
Recommended PC Tool
Crashes, No Sound, or Screen Glitches?Free driver scan
Windows Errors? Fix Them Before They SpreadFree repair scan

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.