Skip to main content
SeleniumDecoded

Printing Pages to PDF

Generate PDFs of any page with the W3C print command: orientation, scale, page ranges, margins, backgrounds, and how to assert on the PDF's contents in your tests.

Selenium 4 Stable Updated 9 Sept 2026 · Verified against Selenium 4.48.0

Invoices, reports, tickets and certificates are usually reviewed as PDFs, and “the print layout is broken” is a bug that screenshots never catch. The W3C WebDriver specification includes a print command, exposed in Selenium 4, that renders the current page to PDF the same way the browser’s Print dialog would, including @media print styles. It works headless, which is where you will run it in CI.

Basic Usage

Print the current page to a PDF file
Selenium 4 Stable
import org.openqa.selenium.Pdf;
import org.openqa.selenium.PrintsPage;
import org.openqa.selenium.print.PrintOptions;
driver.get("https://app.example.com/invoices/INV-1042");
PrintOptions options = new PrintOptions();
Pdf pdf = ((PrintsPage) driver).print(options);
byte[] bytes = Base64.getDecoder().decode(pdf.getContent());
Files.write(Path.of("invoice-1042.pdf"), bytes);
import base64
from selenium.webdriver.common.print_page_options import PrintOptions
driver.get("https://app.example.com/invoices/INV-1042")
options = PrintOptions()
pdf_base64 = driver.print_page(options)
with open("invoice-1042.pdf", "wb") as f:
f.write(base64.b64decode(pdf_base64))
const fs = require('fs');
await driver.get('https://app.example.com/invoices/INV-1042');
const pdfBase64 = await driver.printPage({});
fs.writeFileSync('invoice-1042.pdf', Buffer.from(pdfBase64, 'base64'));
using OpenQA.Selenium;
driver.Navigate().GoToUrl("https://app.example.com/invoices/INV-1042");
var options = new PrintOptions();
PrintDocument pdf = driver.Print(options);
pdf.SaveAsFile("invoice-1042.pdf");

Chrome and Edge require headless mode for the print command (headed Chrome returns an error); Firefox prints in both modes.

OptionValuesDefault
Orientationportrait, landscapeportrait
Scale0.1 to 2.01.0
Backgroundprint background colours and imagesfalse
Shrink to fitscale content to page widthtrue
Page sizewidth and height in centimetres21.59 x 27.94 (US Letter)
Marginstop, bottom, left, right in centimetres1.0 each
Page rangese.g. "1-3", "5"all
Configuring the output
Selenium 4 Stable
PrintOptions options = new PrintOptions();
options.setOrientation(PrintOptions.Orientation.LANDSCAPE);
options.setScale(0.9);
options.setBackground(true);
options.setShrinkToFit(true);
options.setPageSize(new PageSize(29.7, 21.0)); // A4 landscape, height x width in cm
options.setPageMargin(new PageMargin(1.5, 1.5, 1.0, 1.0)); // top, bottom, left, right
options.setPageRanges("1-2");
Pdf pdf = ((PrintsPage) driver).print(options);
options = PrintOptions()
options.orientation = "landscape"
options.scale = 0.9
options.background = True
options.shrink_to_fit = True
options.page_height = 21.0 # cm
options.page_width = 29.7
options.margin_top = 1.5
options.margin_bottom = 1.5
options.margin_left = 1.0
options.margin_right = 1.0
options.page_ranges = ["1-2"]
pdf_base64 = driver.print_page(options)
const pdfBase64 = await driver.printPage({
orientation: 'landscape',
scale: 0.9,
background: true,
shrinkToFit: true,
pageWidth: 29.7,
pageHeight: 21.0,
margin: { top: 1.5, bottom: 1.5, left: 1.0, right: 1.0 },
pageRanges: ['1-2'],
});
var options = new PrintOptions
{
Orientation = PrintOrientation.Landscape,
ScaleFactor = 0.9,
OutputBackgroundImages = true,
ShrinkToFit = true,
PageDimensions = new PrintOptions.PageSize { Width = 29.7, Height = 21.0 },
PageMargins = new PrintOptions.Margins { Top = 1.5, Bottom = 1.5, Left = 1.0, Right = 1.0 },
};
options.AddPageRangeToPrint("1-2");
PrintDocument pdf = driver.Print(options);

Asserting on the PDF

A saved file is only useful if the test checks it. Extract the text with a PDF library and assert on it, or count pages, or compare to a baseline with a tolerance.

Extract text and assert
Selenium 4 Medium
// Apache PDFBox: org.apache.pdfbox:pdfbox
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.text.PDFTextStripper;
byte[] bytes = Base64.getDecoder().decode(pdf.getContent());
try (PDDocument doc = Loader.loadPDF(bytes)) {
assertEquals(2, doc.getNumberOfPages());
String text = new PDFTextStripper().getText(doc);
assertTrue(text.contains("Invoice INV-1042"));
assertTrue(text.contains("Total due: $1,240.00"));
}
# pip install pypdf
import io
from pypdf import PdfReader
reader = PdfReader(io.BytesIO(base64.b64decode(pdf_base64)))
assert len(reader.pages) == 2
text = "".join(page.extract_text() for page in reader.pages)
assert "Invoice INV-1042" in text
assert "Total due: $1,240.00" in text
// npm install pdf-parse
const pdfParse = require('pdf-parse');
const data = await pdfParse(Buffer.from(pdfBase64, 'base64'));
assert.strictEqual(data.numpages, 2);
assert.ok(data.text.includes('Invoice INV-1042'));
assert.ok(data.text.includes('Total due: $1,240.00'));
// NuGet: PdfPig
using UglyToad.PdfPig;
using var doc = PdfDocument.Open(Convert.FromBase64String(pdf.AsBase64EncodedString));
Assert.That(doc.NumberOfPages, Is.EqualTo(2));
var text = string.Join("", doc.GetPages().Select(p => p.Text));
Assert.That(text, Does.Contain("Invoice INV-1042"));
Assert.That(text, Does.Contain("Total due: $1,240.00"));

Text extraction merges words unpredictably across columns, so assert on distinctive substrings rather than whole sentences.

Waiting Before Printing

The print command captures the current DOM. If the page is still fetching data, you print a spinner. Wait for the content, and for fonts and images if the layout depends on them:

Ensure the page is ready to print
Selenium 4 Stable
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(15));
wait.until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector("[data-testid='invoice-total']")));
wait.until(d -> Boolean.TRUE.equals(((JavascriptExecutor) d).executeScript(
"return document.fonts.status === 'loaded' && [...document.images].every(i => i.complete)")));
Pdf pdf = ((PrintsPage) driver).print(new PrintOptions());
wait = WebDriverWait(driver, 15)
wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, "[data-testid='invoice-total']")))
wait.until(lambda d: d.execute_script(
"return document.fonts.status === 'loaded' && [...document.images].every(i => i.complete)"))
pdf_base64 = driver.print_page(PrintOptions())
await driver.wait(until.elementLocated(By.css("[data-testid='invoice-total']")), 15000);
await driver.wait(() => driver.executeScript(
"return document.fonts.status === 'loaded' && [...document.images].every(i => i.complete)"), 15000);
const pdfBase64 = await driver.printPage({});
var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(15));
wait.Until(d => d.FindElement(By.CssSelector("[data-testid='invoice-total']")).Displayed);
wait.Until(d => (bool)((IJavaScriptExecutor)d).ExecuteScript(
"return document.fonts.status === 'loaded' && [...document.images].every(i => i.complete)"));
var pdf = driver.Print(new PrintOptions());
  • Chrome’s --print-to-pdf flag produces a PDF at launch without WebDriver; fine for one-off documents, useless for authenticated pages.
  • CDP Page.printToPDF offers header and footer templates that the W3C command lacks; Chromium only.
  • Emulating print media before a screenshot: BiDi’s emulation module or CDP Emulation.setEmulatedMedia("print") lets you screenshot the print layout for visual comparison without producing a PDF.

Summary

  • print is a W3C command available in Selenium 4 on all major browsers; Chromium needs headless.
  • Options cover orientation, scale, page size, margins, ranges and backgrounds.
  • Wait for content, fonts and images before printing, then assert on the PDF with a parsing library.
  • Use CDP only when you need header/footer templates.

Copy-paste recipes for this topic

Related lessons