# Chrome CDP Page.printToPDF A4 settings: paperWidth and paperHeight in inches

## TL;DR

Chrome DevTools Protocol `Page.printToPDF` has no A4 preset and no `format` field. Its `paperWidth` and `paperHeight` parameters are numbers in inches, and the defaults are US Letter (8.5 by 11). For A4, pass `paperWidth: 8.2677` and `paperHeight: 11.6929` (210 by 297 mm divided by 25.4), set `preferCSSPageSize: false`, and set the four margins explicitly in inches too. Verify the result by reading the PDF page box: exact A4 measures 595.28 by 841.89 points.

## The query

```text
chrome cdp printtopdf paperwidth paperheight a4 settings
```

The symptom behind it: a PDF produced over raw CDP (or through a CDP session in Puppeteer or Playwright) comes out at US Letter size, or at a wildly wrong size after millimetre values are passed, even though the caller asked for A4.

## Fix: raw CDP call

1. Navigate to the page and wait for load plus any async rendering to finish. Success check: the document title or a ready marker element is present before you print.
2. Send `Page.printToPDF` with A4 dimensions in inches. A complete portrait A4 parameter set:

```json
{
  "landscape": false,
  "printBackground": true,
  "scale": 1,
  "paperWidth": 8.2677,
  "paperHeight": 11.6929,
  "marginTop": 0.4,
  "marginBottom": 0.4,
  "marginLeft": 0.4,
  "marginRight": 0.4,
  "pageRanges": "",
  "preferCSSPageSize": false
}
```

   Success check: the call returns without a protocol error. A parameter type error here almost always means a value was sent as a string or in millimetres.
3. Save the returned payload. By default the response carries the PDF as base64 in its data field. Decode it to a .pdf file. Success check: the file starts with the %PDF- header bytes and opens in a PDF viewer.
4. Verify the page box before shipping the file (step below). Never trust the request parameters alone.

## Fix: Node with puppeteer-core CDPSession

```js
const fs = require('fs');
// page is a puppeteer-core Page connected to Chrome or Chromium
const client = await page.createCDPSession();
const result = await client.send('Page.printToPDF', {
  landscape: false,
  printBackground: true,
  scale: 1,
  paperWidth: 8.2677,
  paperHeight: 11.6929,
  marginTop: 0.4,
  marginBottom: 0.4,
  marginLeft: 0.4,
  marginRight: 0.4,
  pageRanges: '',
  preferCSSPageSize: false
});
fs.writeFileSync('out.pdf', Buffer.from(result.data, 'base64'));
console.log('wrote out.pdf');
```

Expected output: `wrote out.pdf`, and the file passes the page-box check below.

## Verify the output is really A4

PDF page boxes are measured in points (72 points per inch), so A4 is 595.28 by 841.89 pt and Letter is 612 by 792 pt.

1. With poppler-utils: run `pdfinfo out.pdf` and read the Page size line. Expected: `Page size: 595.28 x 841.89 pts (A4)`.
2. Or with PyMuPDF in Python: open the file, read the first page rect, and print its width and height. Expected: about 595.3 by 841.9.
3. If you see 612 by 792, the size parameters never took effect and Chrome fell back to its Letter default. If you see a page thousands of points wide, millimetres were passed where inches were expected.

## Size reference (inches and points)

- A4 portrait: 210 by 297 mm, 8.2677 by 11.6929 in, 595.28 by 841.89 pt
- A4 landscape: swap width and height, and set landscape to true
- A5 portrait: 148 by 210 mm, 5.8268 by 8.2677 in, 419.53 by 595.28 pt
- US Letter portrait: 8.5 by 11 in, 612 by 792 pt (the CDP default)
- US Legal portrait: 8.5 by 14 in, 612 by 1008 pt

Note on precision: the rounded pair 8.27 by 11.69 is widely used and Chrome treats it as A4, but it yields a page box of about 595.44 by 841.68 pt. Use the full-precision pair 8.2677 by 11.6929 when a downstream system checks the page box exactly.

## When to use this

- You call `Page.printToPDF` directly over a DevTools Protocol websocket.
- You print through a CDP session object (Puppeteer `createCDPSession`, Playwright `newCDPSession`, chromedp, chrome-remote-interface) and the output is Letter instead of A4.
- A wrapper library forwards raw printToPDF parameters unchanged.

## When not to use this

- Puppeteer high-level `page.pdf()` calls: that wrapper has its own `format: 'A4'` option plus `width` and `height` options that accept unit strings such as '8.27in'. Prefer those there; the raw names in this skill are the underlying CDP parameters the wrapper translates to.
- Playwright high-level `page.pdf()`: it also exposes `format: 'A4'` and is Chromium-only. Use the raw parameters only when you dropped down to a CDP session yourself.
- Pages that define their own print size in CSS and should keep it: set `preferCSSPageSize: true` instead and fix the CSS size rule, because with that flag on, the CSS size wins over paperWidth and paperHeight.

## Variant phrasings

### printToPDF returns Letter instead of A4

Same fix. Omitting paperWidth and paperHeight silently keeps the 8.5 by 11 inch default regardless of system locale or printer settings.

### paperWidth 210 paperHeight 297 gives a huge or clamped page

210 and 297 are millimetres. The protocol reads them as inches, so the requested sheet is over 17 feet tall and Chrome clamps or errors. Always convert: inches = millimetres / 25.4.

### A4 pages but content is cut off at the edges

That is margins, not paper size. The default margins are 1 cm (about 0.4 in) each side. Set marginTop, marginBottom, marginLeft, and marginRight explicitly, in inches, and keep header and footer templates inside the top and bottom margins when displayHeaderFooter is on.

### CSS page rule overrides the CDP paper size

If the document carries a CSS at-page size rule and `preferCSSPageSize` is true, Chrome uses the CSS size and ignores paperWidth and paperHeight. Either set the flag to false or change the CSS rule; do not fight it by changing both.

## Why it happens

The CDP print API was designed around the US printing stack: dimensions and margins are plain numbers in inches, there is no named-format enum at the protocol level, and the defaults are Letter with 1 cm margins. Named sizes like A4 only exist in the higher-level wrappers (Puppeteer, Playwright) that translate a format name into these inch values before sending the command. Anyone working at the raw protocol layer has to do that translation themselves, and the two classic mistakes are passing millimetres and assuming the browser locale changes the default.

## Edge cases

- Landscape: set landscape to true AND swap the dimensions (paperWidth 11.6929, paperHeight 8.2677). Setting only one of the two gives inconsistent results across Chrome versions.
- Scale does not change the paper box. Scale shrinks or grows the rendered content on the same sheet; a wrong Page size reading in pdfinfo is never a scale problem.
- Large PDFs: for big documents prefer the stream transfer mode in newer protocol versions and read the stream in chunks instead of one base64 string, which can hit message size limits.
- Header and footer templates are HTML strings rendered in an isolated context with their own inline styles; they do not inherit page CSS, and they only appear when displayHeaderFooter is true and the margins leave room for them.
- Chromium and Chrome behave the same here; the parameters are stable across current Chrome, Chromium, and headless shell builds. Very old Chrome versions (before roughly version 61) ignored printToPDF parameters entirely.

## Compatibility

- Chrome DevTools Protocol, Page domain, `Page.printToPDF`: current stable protocol (tot) and 1-3 branch.
- Works from any CDP client: raw websocket, puppeteer-core CDPSession, Playwright CDPSession, chromedp (which exposes the same fields on its print params), chrome-remote-interface.
- Verification tools: pdfinfo (poppler-utils) or PyMuPDF, any recent version.
