Free tools Windows power users keep installed
One-click scans. No signup required.
Use Playwright’s page.pdf() method instead of window.print(). It creates a PDF buffer or writes a file directly, so no user-facing print dialog opens. By default, the PDF uses print CSS; call page.emulateMedia({ media: 'screen' }) first when you need the page’s screen styling.
Generate a PDF directly with page.pdf()
The smallest working Node.js example launches Chromium, navigates to a URL, and saves the generated document:
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('https://example.com', { waitUntil: 'load' });
await page.pdf({ path: 'page.pdf', format: 'A4' });
await browser.close();
})();
path is optional. Without it, page.pdf() returns a PDF buffer that you can send from an HTTP route, attach to a job, or store in object storage. A relative path is resolved from the process working directory. Use Chromium for the PDF workflow and install the browser binary that matches your Playwright package:
npm install playwright
npx playwright install chromium
Keep browser shutdown in a finally block in production so a navigation or PDF error does not leave processes running:
#1 Best Overall
const { chromium } = require('playwright');
async function makePdf(url, outputPath) {
const browser = await chromium.launch();
try {
const page = await browser.newPage();
await page.goto(url, { waitUntil: 'load', timeout: 60000 });
await page.pdf({ path: outputPath, format: 'A4', printBackground: true });
} finally {
await browser.close();
}
}
makePdf('https://example.com', 'page.pdf').catch((error) => {
console.error(error);
process.exitCode = 1;
});
Choose the PDF settings that match the document
Pass the options that describe the paper and the content you need. The current Page API reference is authoritative for defaults and version-specific additions.
| Option | Use it for | Important detail |
|---|---|---|
path |
Saving a file | Omit it to receive a buffer. |
format |
Standard paper such as A4 or Letter | Choose one format, or use explicit dimensions. |
width, height |
Custom paper size | Include units in the value when using dimensions. |
margin |
Controlling printable whitespace | Set top, right, bottom and left margins independently. |
pageRanges |
Exporting selected pages | Use the documented page-range syntax, such as a single page or a range. |
printBackground |
Keeping background colors and images | Enable it when the design depends on backgrounds; otherwise they may be omitted. |
preferCSSPageSize |
Honoring CSS @page dimensions |
Useful when the site defines its own page size. |
scale |
Fitting content without changing CSS | Adjust the rendered scale carefully because it changes text and spacing. |
displayHeaderFooter |
Adding generated headers or footers | Use with the header and footer template options. |
headerTemplate, footerTemplate |
Page numbers, dates or labels | Templates support Playwright’s documented page-number and date placeholders. |
For example, this export keeps backgrounds, adds margins, and prints only pages 1 through 3:
await page.pdf({
path: 'invoice.pdf',
format: 'A4',
margin: { top: '18mm', right: '14mm', bottom: '18mm', left: '14mm' },
printBackground: true,
pageRanges: '1-3'
});
Do not combine a paper format and a CSS page size casually. If the document contains an @page rule, decide whether the browser’s format or the stylesheet should win, then set preferCSSPageSize accordingly.
Print CSS or screen CSS?
page.pdf() uses print media by default. That is normally what you want for a document: navigation can disappear, columns can reflow, and print-only elements can appear. To preserve the appearance used in a browser window, emulate screen media before generating the PDF:
Rank #2
await page.goto('https://example.com', { waitUntil: 'load' });
await page.emulateMedia({ media: 'screen' });
await page.pdf({ path: 'screen-styled.pdf', format: 'A4', printBackground: true });
Pages often need explicit print rules. A typical stylesheet hides interactive controls and avoids splitting a card across pages:
@media print {
.site-nav,
.cookie-settings,
.share-buttons {
display: none !important;
}
.card {
break-inside: avoid;
}
}
@page {
margin: 16mm;
}
Printed colors can be adjusted by the browser. If exact colors are essential, consider -webkit-print-color-adjust in the page’s print stylesheet and verify the resulting PDF rather than assuming screen colors will be identical.
Wait for the page’s real content before printing
Navigation completion is not the same as application readiness. A page may still be rendering a chart, loading fonts, inserting lazy images, or waiting for an API response after goto() resolves. There is no universal wait condition that works for every site.
Use an application signal
Prefer a selector or state that means the content is ready:
Rank #3
await page.goto('https://example.com/report', { waitUntil: 'domcontentloaded' });
await page.locator('[data-report-ready="true"]').waitFor({ state: 'visible', timeout: 30000 });
await page.pdf({ path: 'report.pdf', format: 'A4' });
Wait for fonts and images when they affect layout
await page.evaluate(async () => {
if (document.fonts) await document.fonts.ready;
const images = Array.from(document.images);
await Promise.all(images.map((img) => {
if (img.complete) return Promise.resolve();
return new Promise((resolve) => {
img.addEventListener('load', resolve, { once: true });
img.addEventListener('error', resolve, { once: true });
});
}));
});
A fixed delay can help with a known animation, but it is less reliable than waiting for the application’s own ready signal. If you use waitUntil: 'networkidle', treat it as a site-specific choice: analytics, polling and long-lived connections can prevent a stable idle point.
Return a buffer instead of writing a file
When a web service should stream the PDF to a caller, omit path and set the response headers yourself:
const pdf = await page.pdf({ format: 'Letter', printBackground: true });
// Express example
res.set({
'Content-Type': 'application/pdf',
'Content-Disposition': 'attachment; filename="page.pdf"'
});
res.send(pdf);
For large documents, write the buffer to storage immediately and avoid retaining many PDFs in memory at once. Reuse a controlled browser process or a small pool for jobs, but create an isolated page or browser context for each URL so cookies and page state do not leak between requests.
Headers, footers and page design
Generated headers and footers are separate from the page’s DOM. Enable them explicitly and use the documented template placeholders:
PC 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 & 11Crashes, 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 minuteRank #4
await page.pdf({
path: 'manual.pdf',
format: 'A4',
displayHeaderFooter: true,
headerTemplate: '<div style="font-size:9px;width:100%;text-align:center">Internal manual</div>',
footerTemplate: '<div style="font-size:9px;width:100%;text-align:center">Page <span class="pageNumber"></span> of <span class="totalPages"></span></div>',
margin: { top: '22mm', bottom: '20mm', left: '15mm', right: '15mm' }
});
Reserve enough top and bottom margin for these templates. Header and footer markup does not automatically inherit the page’s application styles, so use inline styles in the templates.
Common failures and fixes
- A print dialog opens. Your code is calling
window.print()or clicking a print button that does so. Replace that flow withpage.pdf(); the dialog-testing example in Playwright’s dialogs guide is for observingwindow.print(), not for creating a PDF. page.pdf is not a function. Check that the object is a PlaywrightPage, not a different browser automation library, and update the package if your installed version is unusually old.- The browser executable is missing. Run the matching
npx playwright install chromiumcommand in the same environment as the application, including your CI image. - The PDF is blank or missing late content. Wait for a page-specific ready selector, fonts and images. A successful navigation only proves that the initial document loaded.
- Backgrounds or colors are absent. Set
printBackground: trueand inspect the page’s print CSS. If colors still differ, evaluate-webkit-print-color-adjustfor the relevant elements. - Content is clipped or unexpectedly scaled. Check paper format, explicit width and height, margins,
scale, and whetherpreferCSSPageSizeis allowing an@pagerule to override your format. - Images or fonts shift between runs. Wait for the actual assets, use stable test data, and avoid printing while transitions or carousels are still moving.
- Only some pages are needed. Set
pageRangeswith the documented syntax and verify the resulting page count, especially when content reflows. - The job hangs. Give
gotoand readiness waits explicit timeouts, log the URL and stage that failed, and always close the browser infinally.
JavaScript and Python bindings
The JavaScript API is shown above. Python projects use the same PDF concepts through the Playwright binding:
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
page.goto('https://example.com', wait_until='load')
page.pdf(path='page.pdf', format='A4', print_background=True)
browser.close()
In either language, keep the media choice, readiness condition and PDF options together in the job that owns them. That makes a failed export diagnosable and prevents a global setting from silently changing unrelated documents.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.What this does—and does not—test
Calling page.pdf() is document generation, not a test that a user successfully opened a system print dialog. If your test requirement is specifically “clicking this control invokes window.print(),” use Playwright’s dialog event handling and assert that the call occurred. If the requirement is a downloadable PDF, skip that browser-dialog flow and generate the bytes with page.pdf().
The Tool Desk
Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Best Value
Or skip the browser setup
ScreenshotNeo provides a website capture API and an MCP server for AI agents. It can capture a page as PNG, JPEG, WebP or PDF, and its capture_pdf MCP tool is useful when an agent needs document output without you maintaining Playwright browsers.
For a direct request, the documented cURL form is:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Equivalent Python and Node.js requests are:
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)
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 body = Buffer.from(await res.arrayBuffer());
Read the current request and PDF details in the ScreenshotNeo documentation. 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 disabled. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed, 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 to Claude, Cursor and other MCP clients.
Every feature is included on every plan. The free plan includes 1,000 shots per month with no card; paid plans are Starter ($5 for 3,000), Growth ($15 for 15,000), Pro ($39 for 60,000), Scale ($99 for 250,000) and Business ($249 for 1,000,000). Yearly billing provides two months free. If you want clean captures without installing browsers, create a free ScreenshotNeo account.
Frequently Asked Questions
Can a PDF generated by Playwright be sent directly from an API endpoint?
Yes. Omit the path option, receive the returned buffer, set Content-Type: application/pdf, and send the bytes in your framework’s response.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Does generating a PDF require a physical printer or operating-system printer configuration?
No. Playwright writes PDF bytes in the browser process; it does not submit a job to an installed printer.
Where should I check for option changes?
Use the Page API reference that matches the Playwright version installed in your project, because defaults and available options can change between versions.
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.

