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

Use a real browser when your HTML depends on modern JavaScript; use a publishing engine when paged-media control matters; use a command-line WebKit renderer for simpler, headless jobs. Puppeteer and Playwright expose page.pdf() and render with print CSS by default. wkhtmltopdf provides a headless command-line path, while Prince and WeasyPrint target document publishing and HTML-to-PDF workflows. No cited source establishes a universal winner, so choose against your page’s scripts, layout, deployment and accessibility requirements.

Choose the conversion path first

HTML-to-PDF conversion is not one algorithm. The renderer determines which CSS features, scripts, fonts and page-layout rules survive in the PDF.

Approach What the documentation establishes Best fit Watch for
Chromium automation Puppeteer and Playwright generate PDFs through a browser page; print CSS is the default media type. Applications whose pages already work in Chrome, including JavaScript charts and client-side data. Wait for application state and assets, then inspect print-specific page breaks and colors.
wkhtmltopdf A headless command-line HTML-to-PDF tool based on Qt WebKit; it runs without a display service and is licensed LGPLv3. Scripts and servers that need a simple executable and do not require the newest browser behavior. Its WebKit rendering basis may differ from a current browser; the project page does not establish a current release or maintenance comparison.
Prince Converts HTML, Markdown and XML to PDF, with CSS, JavaScript, server-side integration and paged-media features. Books, reports and branded documents needing generated content, page regions, footnotes and controlled pagination. Its guide is vendor documentation, not an independent fidelity or performance benchmark.
WeasyPrint Describes itself as free, open-source software for producing PDF documents from HTML and offers paid professional support. Teams wanting an open-source publishing-oriented component. Confirm supported CSS, fonts and deployment behavior for your templates before committing.

These projects are documented differently, and the cited pages do not provide controlled measurements of speed, fidelity, memory use or total cost. Treat those as properties to test in your own workload.

Browser conversion with Puppeteer

Puppeteer’s Page.pdf() documentation says it “Generates a PDF of the page with the print CSS media type.” A current Puppeteer API page identifies version 25.12.0. The following complete script loads a URL, waits for network activity, emulates screen styles when requested, and writes a PDF.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
import puppeteer from 'puppeteer';

const browser = await puppeteer.launch({headless: true});
try {
  const page = await browser.newPage();
  await page.setViewport({width: 1440, height: 900, deviceScaleFactor: 1});
  await page.goto('https://example.com', {waitUntil: 'networkidle0', timeout: 90000});

  // Use print CSS by default. Uncomment to render screen media rules instead.
  // await page.emulateMediaType('screen');

  await page.pdf({
    path: 'output.pdf',
    format: 'A4',
    printBackground: true,
    margin: {top: '16mm', right: '14mm', bottom: '16mm', left: '14mm'},
    preferCSSPageSize: true
  });
} finally {
  await browser.close();
}

Make the page deterministic

  • Use waitUntil: 'networkidle0' only when the page eventually becomes quiet. Analytics, WebSockets or polling can prevent that condition; in those cases wait for a specific selector or a bounded delay instead.
  • Wait for application content explicitly, for example await page.waitForSelector('.invoice-total'), and await fonts with page.evaluate(() => document.fonts.ready).
  • Set authentication, cookies or headers before navigation when the source is private. Keep credentials out of logs and generated files.
  • Use printBackground: true when colored panels or backgrounds are part of the document. Puppeteer notes that PDF generation modifies colors for printing by default; CSS such as -webkit-print-color-adjust: exact can force color treatment when appropriate.

Print CSS that controls pagination

@page {
  size: A4;
  margin: 16mm 14mm;
}

@media print {
  .screen-only { display: none !important; }
  h1, h2 { break-after: avoid; }
  table, figure { break-inside: avoid; }
  a { color: inherit; text-decoration: none; }
}

preferCSSPageSize: true lets an @page rule win over the API’s paper size. If you omit it, the API’s format and margin settings determine the sheet geometry.

Browser conversion with Playwright

Playwright’s Page API exposes the same basic workflow and documents options for paper formats, margins, background printing, outlines and tagged output. Tagged output defaults to false; enabling it is not proof that the resulting PDF conforms to an accessibility standard.

import { chromium } from 'playwright';

const browser = await chromium.launch();
try {
  const page = await browser.newPage({viewport: {width: 1440, height: 900}});
  await page.goto('https://example.com', {waitUntil: 'networkidle', timeout: 90000});
  await page.waitForLoadState('domcontentloaded');
  await page.evaluate(() => document.fonts.ready);
  await page.pdf({
    path: 'output.pdf',
    format: 'Letter',
    printBackground: true,
    margin: {top: '0.65in', right: '0.55in', bottom: '0.65in', left: '0.55in'},
    outline: true,
    tagged: true,
    preferCSSPageSize: true
  });
} finally {
  await browser.close();
}

To target screen rules instead of the default print rules, call await page.emulateMedia({media: 'screen'}) before page.pdf(). Validate the result rather than assuming a tagged setting establishes reading order, structure or conformance.

Headless command-line conversion with wkhtmltopdf

wkhtmltopdf describes its tools as headless command-line renderers based on Qt WebKit that “run entirely ‘headless’ and do not require a display or display service.” A basic conversion is:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
wkhtmltopdf --print-media-type --margin-top 16mm --margin-right 14mm --margin-bottom 16mm --margin-left 14mm https://example.com output.pdf

Use a local file when the HTML and assets are packaged together:

wkhtmltopdf --enable-local-file-access invoice.html invoice.pdf

Only enable local-file access for trusted input; it broadens what the renderer can read from the machine. Because this engine uses Qt WebKit rather than a current Chromium page, compare its output with your production templates before standardizing on it.

Publishing-oriented conversion with Prince or WeasyPrint

Prince

The Prince user guide covers HTML, Markdown and XML input, CSS styling, JavaScript, server-side integration and paged-media controls. Its documentation specifically addresses page regions and generated content such as page numbers, headers, footers, list markers and footnotes. This makes it a candidate for long reports where pagination is a design requirement, not an afterthought. Follow the installation and licensing terms for your deployment and render a representative document set before production use.

WeasyPrint

WeasyPrint presents itself as free, open-source software for producing PDF documents from HTML and also lists paid professional support. Its open-source status does not by itself tell you which CSS, JavaScript or font behaviors your templates will receive, so test the exact components you use.

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

Make HTML print-ready before rendering

  • Define the sheet: Set @page size and margins, then verify the physical paper choice in the API or command.
  • Separate screen and print UI: Hide navigation, cookie controls, chat launchers and interactive widgets in @media print.
  • Control breaks: Apply break-before, break-after and break-inside to headings, tables, cards and figures. Keep repeated table headers with thead { display: table-header-group; } where the renderer supports it.
  • Load assets explicitly: Use absolute or correctly resolved URLs, await fonts and images, and ensure the server permits the renderer’s requests.
  • Preserve useful links: Do not remove link destinations merely to match screen styling; verify that links remain usable in the PDF viewer.
  • Plan for long content: Test tables with many rows, code blocks, very long words, right-to-left text and unusually large images.

Accessibility and document quality

A PDF can look correct and still be difficult to navigate. Inspect heading hierarchy, reading order, table headers, link annotations, selectable text, language metadata and contrast. Playwright’s tagged option is a tool setting, not an accessibility certification. Use an appropriate PDF accessibility review for your jurisdiction and audience.

Review every generated file

  1. Open the PDF at 100% and compare its page count with the intended document.
  2. Check for clipped text, orphaned headings, unexpected blank pages and rows split in unusable places.
  3. Confirm fonts, images, SVGs, backgrounds and colors are present.
  4. Activate links and inspect bookmarks or outlines when you requested them.
  5. Check reading order and table semantics with an accessibility tool or manual keyboard and screen-reader review.
  6. Repeat with the smallest and largest realistic inputs, not just a short demo page.

Troubleshooting common failures

The PDF is blank or missing client-rendered content

The capture happened before JavaScript finished. Wait for a meaningful selector, await the application’s data promise where possible, and ensure the network request is not failing due to authentication or CORS.

Colors look washed out

Print color adjustment is active. Enable background printing and, for Chromium, use -webkit-print-color-adjust: exact on the relevant elements; still check whether the result is suitable for physical printing.

Fonts or images disappear

Check URL resolution, certificates, access permissions and response status. Wait for document.fonts.ready and image completion, and bundle assets or serve them from an endpoint the renderer can reach.

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

Pages break in the wrong places

Set explicit @page dimensions, margins and break rules. Remove fixed-height containers that cannot expand, and test tables and figures with break-inside: avoid.

wkhtmltopdf cannot load a local asset

For trusted local input, add --enable-local-file-access and use correct file paths. Do not enable it for untrusted HTML.

A tagged PDF still fails an accessibility review

Tagging is only one part of accessibility. Correct the source structure, language, labels, reading order and table markup, then review the produced artifact with an appropriate checker.

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

Or skip the browser setup

ScreenshotNeo provides a website capture API and MCP server. Give it a public URL and request a PDF or image without installing Chromium, managing fonts or writing wait logic. Its cleanup step accepts the cookie or consent banner like a visitor and removes more than 60 known consent platforms, newsletter popups and chat widgets; each cleanup action can be turned off. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed, and response headers identify the page verdict and billing status. AI agents can call its MCP tools—take_screenshot, get_page_info and capture_pdf—from Claude, Cursor or another MCP client.

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

For API details and PDF options, see the ScreenshotNeo documentation. A cURL request follows the documented endpoint pattern:

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

The same request in Python:

import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://example.com"}, timeout=90)
r.raise_for_status()
open("page.pdf", "wb").write(r.content)

And Node.js:

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://example.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
require('fs').writeFileSync('page.pdf', Buffer.from(await res.arrayBuffer()));

ScreenshotNeo includes full-page capture with lazy images loaded, paper size, margins, landscape mode and page ranges for PDFs, plus custom CSS and JavaScript, selector waits, delays or network-idle waits, request blocking, headers, cookies, user agents, authorization, timezone and geolocation. It also supports signed links, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, caching with a chosen TTL, usage data and an OpenAPI specification. The free plan includes 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.

How to choose for your deployment

  • Choose Puppeteer or Playwright when browser compatibility and JavaScript execution are central, and you can operate a browser runtime.
  • Choose wkhtmltopdf when a headless command is the operational priority and your templates work with its WebKit behavior.
  • Choose Prince when paged-media features such as generated page furniture and footnotes are core requirements.
  • Choose WeasyPrint when its supported HTML/CSS subset fits your templates and an open-source component is preferred.
  • Choose a hosted capture API when you want URL-based conversion without maintaining browser infrastructure, and verify the provider’s handling of private pages, failures and billing.

Frequently Asked Questions

Can I convert HTML to PDF entirely offline?

Yes. A local browser installation, wkhtmltopdf, Prince or WeasyPrint can render local files, subject to that tool’s asset-loading and licensing requirements.

Should I use print CSS or screen CSS?

Use print CSS for paper-oriented output. Emulate screen media only when the PDF intentionally must match the on-screen design.

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

Does a successful PDF render guarantee accessibility?

No. Inspect structure, reading order, links, tables, language and contrast in the final artifact; a tagged-output option alone is not conformance evidence.

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.