Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.
To create a reliable multipage PDF from HTML, define a print stylesheet first, then use a renderer that matches your content. Put page dimensions and margins in @page, hide screen-only controls in @media print, and control section boundaries with break-before, break-after, and break-inside. You can render the result with Puppeteer or Playwright when your page needs browser JavaScript, use WeasyPrint for a Python server workflow, or submit HTML to a hosted converter when you do not want to operate a renderer.
1. Prepare HTML that can paginate
Pagination is easiest when the document has a predictable structure. Use semantic elements such as header, main, section, h1, h2, tables and lists. Keep navigation, cookie notices, live chat, buttons and other screen controls in elements that can be hidden for print output.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Quarterly report</title>
<link rel="stylesheet" href="print.css">
</head>
<body>
<nav class="screen-only">Dashboard | Export | Help</nav>
<main>
<section class="chapter">
<h1>Quarterly report</h1>
<p>Report content…</p>
</section>
<section class="chapter">
<h2>Operations</h2>
<p>More content…</p>
</section>
</main>
</body>
</html>
2. Write a print stylesheet
Print CSS, rather than the screen layout alone, determines how a browser presents a PDF. The @media print block applies only during printing or PDF generation. The @page rule sets the sheet size, orientation and margins.
Windows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallCrashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minute/* print.css */
@page {
size: A4 portrait;
margin: 18mm 16mm 20mm;
}
@media print {
.screen-only,
button,
.chat-widget,
.cookie-banner {
display: none !important;
}
html, body {
margin: 0;
padding: 0;
color: #111;
background: #fff;
font: 10.5pt/1.45 system-ui, sans-serif;
}
h1, h2, h3 {
color: #000;
break-after: avoid;
page-break-after: avoid; /* compatibility alias */
}
.chapter {
break-before: page;
page-break-before: always; /* compatibility alias */
}
.chapter:first-child {
break-before: auto;
page-break-before: auto;
}
figure, table, pre, blockquote {
break-inside: avoid;
page-break-inside: avoid;
}
img {
max-width: 100%;
}
a {
color: inherit;
text-decoration: none;
}
}
break-before controls whether a page, column or region break occurs before an element. Use break-after for the following boundary and break-inside: avoid for compact blocks. The older page-break-* properties are aliases that improve compatibility with older implementations. An avoid value is a preference, not an absolute guarantee: if a block is taller than the remaining page (or taller than a whole page), it must be split.
#1 Best Overall
Choosing paper, margins and orientation
- Paper: choose a named size such as
A4orLetter, or provide dimensions such as210mm 297mm. - Orientation: use
portraitfor reports andlandscapefor wide tables. - Margins: leave enough space for headers, footers and printer-safe output. Browser PDF output can use the CSS margins when the renderer is configured to prefer CSS page size.
- Backgrounds: request background printing explicitly in renderers that expose that option; otherwise colored panels and background images may disappear.
Keeping headings and tables usable
Apply break-after: avoid to headings so a heading does not sit alone at the bottom of a page. Put break-inside: avoid on cards, figures and short tables. For long tables, repeat the header row and allow rows to split when necessary:
thead { display: table-header-group; }
tfoot { display: table-footer-group; }
.long-table tr { break-inside: auto; }
Do not force a page break before every subsection. A forced break is appropriate for a chapter or major part, while normal flow gives better results for ordinary paragraphs.
3. Generate a PDF with Puppeteer
Puppeteer is a browser-automation route for HTML that depends on JavaScript, web fonts, client-side data or browser layout. Its page.pdf() method generates a PDF with the print CSS media type. The method waits for fonts by default.
Free tools Windows power users keep installed
One-click scans. No signup required.
- Install Node.js and create a project:
mkdir html-pdf && cd html-pdf && npm init -y. - Install Puppeteer:
npm install puppeteer. - Save your HTML as
report.htmland stylesheet asprint.css. - Create
make-pdf.js:
const puppeteer = require('puppeteer');
const path = require('node:path');
(async () => {
const browser = await puppeteer.launch({ headless: true });
try {
const page = await browser.newPage();
await page.goto(`file://${path.resolve('report.html')}`, {
waitUntil: 'networkidle0'
});
await page.emulateMediaType('print');
await page.pdf({
path: 'report.pdf',
format: 'A4',
printBackground: true,
preferCSSPageSize: true,
margin: { top: '18mm', right: '16mm', bottom: '20mm', left: '16mm' }
});
} finally {
await browser.close();
}
})();
Run node make-pdf.js. For a web page, replace the file:// URL with an HTTPS URL. Use a sufficiently specific waitUntil condition and add an application-level wait for data that appears after the initial network activity. If your CSS contains @page, keep preferCSSPageSize: true so the CSS page size can win over the API default.
Rank #2
4. Generate a PDF with Playwright
Playwright exposes the same basic browser workflow and documents options for paper format, explicit dimensions, margins, page ranges, preferCSSPageSize, printBackground and scale.
- Install it with
npm init -y && npm install playwright. - Install a browser binary when prompted, or run
npx playwright install chromium. - Save this as
playwright-pdf.js:
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch();
try {
const page = await browser.newPage();
await page.goto('http://localhost:3000/report.html', {
waitUntil: 'networkidle'
});
await page.pdf({
path: 'report.pdf',
format: 'A4',
printBackground: true,
preferCSSPageSize: true,
margin: { top: '18mm', right: '16mm', bottom: '20mm', left: '16mm' },
scale: 1
});
} finally {
await browser.close();
}
})();
Playwright’s PDF API uses print media by default. If the page is protected by authentication, establish the session in the browser context before navigation, and do not put credentials in a public URL.
5. Generate a PDF in Python with WeasyPrint
WeasyPrint is a Python-oriented HTML-to-PDF library. Its HTML object can be created from a filename, URL, file object or string, then written with write_pdf(). Its render() method returns a document with individual page objects, which is useful when you need page-level inspection.
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Fix the driver behind crashes, sound loss and screen glitches3Clear out junk files and repair common Windows errorsfrom weasyprint import HTML
HTML(filename='report.html').write_pdf('report.pdf')
# Or render first when you need page information:
document = HTML(filename='report.html').render()
print(f'pages: {len(document.pages)}')
document.write_pdf('report.pdf')
Install WeasyPrint according to the operating-system instructions for your Python environment, then run the script. A library renderer is convenient for server-side, mostly static HTML, but browser-specific JavaScript behavior is not the same as in Chromium. If the page requires client-side execution, render it in a browser first or choose a browser-based workflow.
6. Use a hosted HTML-to-PDF API
A managed service removes browser installation, patching and process supervision from your application. DocRaptor documents an HTML-to-PDF API powered by Prince and accepts either HTML content or a document URL. Treat this as an operational choice, not a universal performance winner: the available documentation does not establish neutral benchmarks against Puppeteer, Playwright or WeasyPrint.
Before selecting a hosted service, verify how it reaches private pages, handles authentication, stores submitted HTML, reports failures and charges for unsuccessful jobs. Keep sensitive data out of public URLs and use the provider’s documented authentication method.
7. A renderer-selection checklist
| Situation | Practical first choice | Reason |
|---|---|---|
| Interactive page, JavaScript data, browser fonts or Chromium-specific layout | Puppeteer or Playwright | They render the page in a real browser and expose PDF settings. |
| Python service with controlled HTML and no client-side execution requirement | WeasyPrint | It provides a direct Python API and page objects after rendering. |
| No desire to operate a renderer | Hosted API such as DocRaptor | The conversion process is managed outside your application. |
These options expose different feature surfaces; the documentation does not provide a neutral, independently verified ranking of speed or CSS compatibility.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
8. Diagnose pagination and output problems
Content is clipped or the page count is surprising
- Check
@pagesize and margins against the renderer’s format and margin options. - Look for fixed-height containers, absolute positioning and transforms that were designed for a screen.
- Remove an unnecessary forced break and inspect whether a large element is taller than one page.
Background colors or images are missing
Enable the renderer’s background-printing option (printBackground: true in the browser examples). Also confirm that the image URL is reachable from the rendering process and that the asset is not blocked by authentication or a content-security policy.
Rank #4
Fonts are wrong or text reflows
Wait until fonts have loaded before creating the PDF. In a browser workflow, navigate with a suitable wait condition and, when necessary, wait for a known font-loading or application-ready signal. Bundle fonts or make them reachable from the renderer; a local browser and a server may not have the same installed fonts.
A heading is separated from its paragraph
Use break-after: avoid (and its page-break-after alias) on the heading. If the following block cannot fit in the remaining space, the browser may still move or split content.
Dynamic content is absent
Do not generate the PDF immediately after navigation. Wait for the selector that signals completion, an application-defined ready flag or a controlled delay. Network-idle alone may not cover timers, WebSockets or late data requests.
Local files work but deployment fails
Check absolute and relative URLs, filesystem permissions, sandbox restrictions, missing browser binaries and outbound network access. Log the final URL, HTTP status, console errors and the renderer’s exception without logging secrets.
Best Value
- Python Programming Language design with distressed logo for Python Software Engineers and Developers.
- Vintage and Distressed Python Programming Language design.
- Lightweight, Classic fit, Double-needle sleeve and bottom hem
9. Inspect every generated document
Open representative PDFs rather than trusting a successful process exit. Check the first, middle and last pages for clipped edges, blank pages, missing backgrounds, orphaned headings, table overflow, broken links, incorrect orientation and unloaded fonts. Compare output after changing one variable at a time: CSS margins, renderer margins, scale, background printing or a break rule. Browser and library pagination can differ, so validate with the renderer you will run in production.
Or skip the browser setup
ScreenshotNeo can return a PDF from one GET request, with PDF paper size, margins, landscape mode and page ranges available as options. It also supports full-page capture, custom CSS and JavaScript, waiting for a selector, delay or network idle, custom headers and cookies, and signed asynchronous jobs. Before capture it accepts cookie or consent banners and removes more than 60 known consent platforms, newsletter popups and chat widgets; each cleanup step can be turned off. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads and cache hits cost nothing, and response headers identify the page verdict and billing status. Its MCP server provides take_screenshot, get_page_info and capture_pdf for Claude, Cursor and other MCP clients.
See the parameter details in the ScreenshotNeo documentation. Replace the URL in these examples with your page:
Recommended Free Tools
cURL
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)
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}`);
The Free plan includes 1,000 shots per month with no card. Starter is $5 for 3,000 shots, Growth $15 for 15,000, Pro $39 for 60,000, Scale $99 for 250,000 and Business $249 for 1,000,000; yearly billing gives two months free, and every feature is on every plan. Create a free ScreenshotNeo account.
Frequently Asked Questions
Can CSS guarantee that an element never splits across pages?
No. break-inside: avoid is a preference. An element that cannot fit on one page may still be split.
Should I set margins in CSS, the PDF API, or both?
You can define them in both, but make the relationship intentional. With browser renderers, use preferCSSPageSize when the stylesheet should control page dimensions and avoid accidentally applying two different margin sets.
Why does a PDF contain an extra blank page?
Common causes are a forced break after the final section, content wider than the printable area, or a fixed-height element overflowing. Remove the final break and inspect layout dimensions in the target renderer.
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 →Can I use a URL that requires login?
Yes, when the renderer can establish an authenticated session, cookie or authorization header. Configure that access securely and verify that private assets such as fonts and images are reachable.
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.

