Documentation page: CSS @page Rules: Page Size, Margins, Named Pages and Margin Boxes. This page is also available as Markdown at css-page-rules-page-size-margins-named-pages-and-margin-boxes.md.

CSS @page Rules: Page Size, Margins, Named Pages and Margin Boxes

The CSS @page rule sets up the printed page from the style sheet: its size and orientation, its margins, different pages for the first, left and right pages or for parts of the document, and running headers and footers with page numbers in the page margins. With the v42 layout engine, PD4ML follows @page as CSS Paged Media 3 defines it and as Chrome prints it, so a document styled for printing in a browser comes out the same way without any PD4ML-specific API calls or tags.

@page {
  size: A4;
  margin: 20mm 15mm;

  @top-right    { content: "Annual Report 2026"; font-size: 9pt; color: #666 }
  @bottom-center { content: "Page " counter(page) " of " counter(pages) }
}

@page :first {
  margin-top: 60mm;
  @top-right { content: none }          /* no running header on the title page */
}

@page wide { size: A4 landscape }
.appendix-table { page: wide }          /* this section gets landscape pages */

Available from PD4ML v4.2.0, with the v42 layout engine: call useHtmlRenderer(Constants.LayoutEngine.v42) before readHTML(). The v40 engine reads only size and the page margins from @page.

What is supported

Feature Syntax Notes
Page size size: A4, size: 150mm 100mm, size: letter landscape keywords, lengths, orientation
Page margins margin, margin-top, margin-right, margin-bottom, margin-left lengths, percentages
First page @page :first the first page of the document
Left and right pages @page :left, @page :right the first page is a right page
Blank pages @page :blank pages PD4ML leaves empty on purpose
Named pages @page wide { ... } with page: wide on elements page breaks where the name changes
Margin boxes @top-left, @top-center, ..., @bottom-right-corner (16 boxes) text and page counters in the margins
Page counters counter(page), counter(pages) with counter styles: counter(page, upper-roman)

Page size

Value Page
size: auto the page size set through the API, or A4 (the default)
size: A5 a page size keyword, portrait
size: A4 landscape, size: landscape A4 a keyword in the given orientation
size: landscape the API's (or default) page size, turned to landscape
size: 210mm 297mm width and height
size: 6in one length: a square page

The page size keywords of CSS are A5, A4, A3, B5, B4, JIS-B5, JIS-B4, letter, legal and ledger (11 x 17 in). PD4ML also knows its own named formats: A0 to A10, ISOB0 to ISOB5, halfletter, tabloid, note and ARCHA to ARCHE1. Keywords are case-insensitive.

Lengths can be given in mm, cm, in, pt, pc, px (1/96 in), Q, em and rem (16 px each). They are physical sizes: size: 150mm 100mm gives a 150 x 100 mm PDF page whatever setHtmlWidth() is.

A size that is not valid - an unknown keyword, a zero or negative length, three values - is ignored, as in a browser.

Page margins

@page { margin: 25mm 20mm 30mm }                /* top, left and right, bottom */
@page { margin: 10mm; margin-left: 35mm }       /* longhands override the shorthand */
@page { margin: 5% }                            /* left/right: of the page width; top/bottom: of its height */

The margin shorthand takes one to four values as everywhere in CSS. Percentages of margin-left and margin-right are of the page width, of margin-top and margin-bottom of the page height. Negative margins are taken as 0, and auto leaves the margin as the API (or default) sets it. Margins are physical lengths, like size.

The content of the document is laid out in the box the margins leave: its width is the layout width of the document. A pd4ml:page.header or pd4ml:page.footer takes its room from inside that box, below the top margin and above the bottom margin; @page margin boxes, below, go into the margins themselves.

Page selectors: first, left, right and blank pages, page names

@page        { margin: 20mm }
@page :first { margin-top: 70mm }                     /* room for a title block */
@page :left  { margin-left: 25mm; margin-right: 15mm }
@page :right { margin-left: 15mm; margin-right: 25mm } /* binding margin on the inside */
@page cover:first { margin: 0 }                        /* a name and a pseudo-class together */
@page :left, :right { margin-bottom: 25mm }            /* selector lists work */
  • :first is the first page of the document.
  • :left and :right: pages alternate, and the first page is a right page (a left-to-right document starts on the right side of a spread). Page 1, 3, 5, ... are right pages, 2, 4, 6, ... left pages.
  • :blank is a page PD4ML leaves empty on purpose: the filler page <pd4ml:page.break next="even"> or next="odd" adds to make the next page even or odd.
  • A page name (@page chapter) matches the pages of elements with page: chapter (see Named pages below). It can be combined with the pseudo-classes: @page chapter:left matches the left pages among them, and @page cover:first the first page of the document if it is a cover page. (:first is always the first page of the document, not the first page of each run of named pages.)

Several rules can match one page. Their declarations cascade as style rules do: a more specific rule wins - a page name counts more than :first or :blank, which count more than :left or :right - then the later rule; !important wins over the rest. A margin box set in one rule and a property of it in another combine the same way.

The whole document is laid out at one width: that of its first page. Pages that :first, :left or :right give other side margins get those margins - the content is placed between them - but the content keeps the layout width of the document, so it may stick out on the narrower side. Different top and bottom margins are not affected: they only change how much content fits on a page. To have parts of a document laid out at another width, use named pages.

Named pages

The page property gives a part of the document pages of its own kind:

@page        { size: A4; margin: 15mm }
@page wide   { size: A4 landscape; margin: 10mm }
@page cover  { margin: 0 }

.cover    { page: cover }
.big-data { page: wide }
<body>
  <section class="cover"> ... title page, edge to edge ... </section>
  <section> ... report text, A4 portrait ... </section>
  <section class="big-data"> ... a wide table, on landscape pages ... </section>
  <section> ... more text, A4 portrait again ... </section>
</body>
  • A block whose page name differs from that of the content before it starts on a new page, and the content after it, back with another name, does too. Elements without a page property (page: auto, the default) have the name of their parent.
  • Those pages take the @page rules of their name in addition to the unnamed ones: here the big-data section is printed on landscape A4 pages with 10 mm margins, as many pages as it needs.
  • The content of a named page is laid out at that page's own width, as Chrome does: a table with width: 100% in the landscape section fills the landscape width, and text lines there are longer.
  • A name that has no @page rule still forces the page breaks, with the unnamed page's size and margins.

A named page gets a layout width of its own when the named element is a child of <body> (a <section>, <div>, <table> directly in the body), the usual way to structure a document. A named element nested deeper - inside a wrapper <div> - still starts its own pages with the named page size and margins, but its content keeps the layout width of the content around it.

Margin boxes

The page margins hold sixteen margin boxes, three along each edge and one in each corner, for running headers, footers and page numbers:

 top-left-corner |   top-left  |  top-center  |  top-right  | top-right-corner
-----------------+-------------------------------------------+-----------------
  left-top       |                                           |  right-top
                 |                                           |
  left-middle    |              page content                 |  right-middle
                 |                                           |
  left-bottom    |                                           |  right-bottom
-----------------+-------------------------------------------+-----------------
 bottom-left-    | bottom-left | bottom-center | bottom-right | bottom-right-
 corner          |             |               |              | corner

A margin box is declared inside an @page rule, and is shown when its content has something to show:

@page {
  margin: 25mm 20mm;
  @top-left      { content: "ACME Corp." ; font-weight: bold }
  @top-right     { content: "Confidential"; color: #c00 }
  @bottom-left   { content: "Printed 2026-11-01" }
  @bottom-right  { content: "Page " counter(page) " of " counter(pages) }
  @right-middle  { content: "DRAFT"; font-size: 18pt; color: #ccc }
}
@page :first {
  @top-left  { content: none }       /* not on the title page */
  @top-right { content: none }
}

Content

content takes a list of:

Value Shows
"text" or 'text' the text
counter(page) the page number
counter(pages) the number of pages of the document
counter(page, upper-roman) the number in a counter style: decimal, decimal-leading-zero, lower-roman, upper-roman, lower-alpha/lower-latin, upper-alpha/upper-latin, lower-greek
none, normal no box

counter(page) is the number PD4ML prints on the page: it starts at setInitialPageNumber() and follows a page counter reset of <pd4ml:page.break pageCounter="...">, the same number as $[page] in a pd4ml:page.footer.

Placement and size

  • The corner boxes fill the corners of the page: the width of the left or right margin by the height of the top or bottom margin.
  • The top and bottom boxes share the top and bottom margin between the corners. With a centre box, it is centred and gets the room its content needs; the left and right boxes take the rest on either side. Without a centre box, the left and right boxes share the edge by the room their contents need. A box alone takes the whole edge. (CSS Paged Media 3, 5.3.2.)
  • The left and right boxes share the side margins between the corners in the same way, top to bottom.

The default alignment is that of the spec: top-left and bottom-left text is left-aligned, *-center centred, top-right and bottom-right right-aligned; the left and right side boxes are centred; a corner's text sits towards the page content. Top and bottom boxes are centred vertically in the margin; left-top/right-top sit at the top, left-bottom/right-bottom at the bottom. text-align and vertical-align (top, middle, bottom) change this.

Styling

A margin box takes the usual text and box properties: font-family, font-size, font-weight, font-style, color, text-transform, letter-spacing, background, border (border-top: 1px solid gives a rule above a footer), padding and margin. Unset text properties are inherited from the root element (<html>), not from <body> - as in a browser, so a font set only on body does not reach the margin boxes:

html { font-family: "Helvetica", sans-serif }            /* reaches the margin boxes */
@page { @bottom-center { content: counter(page); font-size: 8pt } }

A margin box takes no room from the page content: it sits in the margin, and content taller than the margin overflows it. Make the margins large enough for the margin boxes.

Margin boxes in the output formats

  • PDF: drawn on every page; in tagged PDF (PDF/UA, PDF/A-1a/2a/3a) they are page artifacts, as running headers should be.
  • Images (renderAsImages()): drawn as in PDF.
  • DOCX and RTF: the page size and margins are taken over into the document's sections; margin boxes are not (Word has its own header and footer model). Use pd4ml:page.header/pd4ml:page.footer for headers and footers that must appear in Word output too.

Media types

@page rules inside @media apply when the media type matches, like the rest of the style sheet. PD4ML applies screen (and all, pdf, pd4ml) rules by default, not print. A document that keeps its print styles in @media print { @page { ... } }, or in a <link media="print"> style sheet, needs print media switched on before readHTML():

pd4ml.setParam(Constants.PD4ML_MEDIA_TYPE_PRINT, "add");       // print rules as well as screen rules
// or "override": print rules instead of screen rules
pd4ml.readHTML(url);

Together with the PD4ML API

API With @page
setPageSize(...) wins over size: a page size set through the API is used, the style sheet's is not. @page margins still apply.
setPageMargins(...) wins over the @page margins. The @page size still applies.
setHtmlWidth(...) scales the document to the content width of the first page, @page size and margins included. @page lengths stay physical.
<pd4ml:page.break pageFormat="..."> sets the page size for the pages after it: from there on, @page sizes do not change it. @page margins still apply.
setPageHeader(), setPageFooter(), pd4ml:page.header, pd4ml:page.footer work together with margin boxes: they take room from the content area, inside the @page margins.
setInitialPageNumber(), <pd4ml:page.break pageCounter="..."> set the numbers counter(page) shows.
layout() gives page sizes and element positions with the @page rules applied (see DOM API: Modifying the HTML Document Before Conversion).

A style sheet added from Java code with the DOM API can carry @page rules too:

Element style = pd4ml.getDocument().createElement("style");
style.setTextContent("@page { size: letter; margin: 0.75in;"
        + " @bottom-right { content: counter(page) } }");
pd4ml.getDocument().getHead().appendChild(style);

Complete example

<!DOCTYPE html>
<html>
<head>
<style>
  html { font: 10.5pt/1.4 "Georgia", serif }

  @page {
    size: A4;
    margin: 25mm 20mm;
    @top-left      { content: "Quarterly Report"; font-size: 8.5pt; color: #555 }
    @top-right     { content: "Q3 2026"; font-size: 8.5pt; color: #555 }
    @bottom-center { content: counter(page) " / " counter(pages); font-size: 8.5pt }
  }
  @page :first {
    margin-top: 80mm;
    @top-left  { content: none }
    @top-right { content: none }
  }
  @page :left  { margin-left: 25mm; margin-right: 15mm }
  @page :right { margin-left: 15mm; margin-right: 25mm }
  @page appendix {
    size: A4 landscape;
    margin: 15mm;
    @bottom-center { content: "Appendix - page " counter(page, upper-roman) }
  }

  .appendix { page: appendix }
  table { width: 100%; border-collapse: collapse }
  td, th { border: 1px solid #999; padding: 2pt 4pt }
</style>
</head>
<body>
  <h1>Quarterly Report</h1>
  <p>...</p>
  <section class="appendix">
    <h2>Appendix: figures by region</h2>
    <table> ... a wide table, laid out across the landscape page ... </table>
  </section>
  <p>Closing remarks ...</p>
</body>
</html>
PD4ML pd4ml = new PD4ML();
pd4ml.useHtmlRenderer(Constants.LayoutEngine.v42);
pd4ml.readHTML(new File("report.html").toURI().toURL());
pd4ml.writePDF(new FileOutputStream("report.pdf"));      // no page setup in code

Limitations

  • :first, :left and :right pages with other side margins than the first page keep the document's layout width (see above). Top and bottom margins have no such limit.
  • Named pages get a layout width of their own for elements directly in <body> only.
  • Margin boxes show strings and page counters. Not supported: width/height on margin boxes, string() and string-set, running elements (position: running(), element()), attr(), target-counter() and leaders.
  • @page rules of the v40 layout engine are limited to size and margins.

See also