Page geometry is a CSS problem. Put an @page rule in your stylesheet and the renderer uses it for the sheet size and the printable area.
@page {
size: Letter portrait;
margin: 0.75in 0.5in;
}A working example in Python, using WeasyPrint to turn HTML and CSS into a PDF:
from weasyprint import HTML, CSS
page_css = CSS(string="""
@page { size: Letter portrait; margin: 0.75in 0.5in; }
""")
HTML(string="<h1>Quarterly report</h1><p>Body copy.</p>").write_pdf(
"report.pdf", stylesheets=[page_css]
)Verify it by reading the page box the PDF actually stores:
from pypdf import PdfReader
box = PdfReader("report.pdf").pages[0].mediabox
print(float(box.width), float(box.height))
# 612.0 792.0, which is LetterGiving one section a different size
The size descriptor takes a named page size, an orientation keyword, or both together. CSS Paged Media Level 3 defines the names as a3, a4, a5, b4, b5, jis-b4, jis-b5, ledger, legal, and letter, and states that those names can be used with landscape or portrait to indicate both size and orientation. Two explicit lengths work too, for example 8.5in 11in.
A named @page rule plus the page property gives one part of the document its own geometry. That is the usual fix for a wide table or an appendix that will not fit portrait:
@page { size: Letter portrait; margin: 0.75in 0.5in; }
@page wide { size: Letter landscape; margin: 0.5in; }
.appendix { page: wide; break-before: page; }That produces a 612 by 792 first page and a 792 by 612 second page. WeasyPrint already starts a new sheet when the page name changes, so break-before: page here is explicit rather than strictly required. Page names are case sensitive.
The headless Chrome caveat
If you render through Puppeteer instead, Chrome ignores your @page size until you tell it not to. page.pdf() defaults format to letter and preferCSSPageSize to false, which per Puppeteer's own docs "will scale the content to fit the paper size". Pass preferCSSPageSize: true to give the CSS @page size priority over the width, height, and format options.
await page.pdf({
path: "report.pdf",
preferCSSPageSize: true,
printBackground: true,
});Margins are a second, separate setting there. Puppeteer's margin option defaults to no margins at all and is independent of the margin inside your @page rule, so pick one place to own them rather than setting both.
Back to All Questions