# 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.

```css
@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 */
```

{% hint style="version" %}
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`.
{% endhint %}

## 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

```css
@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

```css
@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](#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.

{% hint style="warning" %}
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.
{% endhint %}

## Named pages

The `page` property gives a part of the document pages of its own kind:

```css
@page        { size: A4; margin: 15mm }
@page wide   { size: A4 landscape; margin: 10mm }
@page cover  { margin: 0 }

.cover    { page: cover }
.big-data { page: wide }
```

```html
<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.

{% hint style="info" %}
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.
{% endhint %}

## 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:

```css
@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:

```css
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()`:

```java
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](modifying-the-html-document-before-conversion)). |

A style sheet added from Java code with the DOM API can carry `@page` rules too:

```java
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

```html
<!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>
```

```java
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

* [PDF Header and Footer Generation](pdf-header-and-footer-generation) -- `setPageHeader()`/`setPageFooter()` and `pd4ml:page.header`/`pd4ml:page.footer`.
* [Dynamic Page Format Changes Within a Document](dynamic-page-format-changes-within-a-document) -- `<pd4ml:page.break pageFormat="...">`.
* [PD4ML Proprietary CSS Media Types, Properties, and Tags](pd4ml-proprietary-css-media-types-properties-and-tags)
* [DOM API: Modifying the HTML Document Before Conversion](modifying-the-html-document-before-conversion) -- `layout()` included.
