Headers & footers
Repeat headers, footers, and page numbers on every PDF page, and size the margins they need.
Pass JSX, an HTML string, or a node tree to header or footer. The band repeats in the top or bottom margin of every page, and automatic margins grow to fit it.
import { } from "takumi-pdf";
import { , } from "takumi-pdf/primitives";
const = await (, {
: (
< ="flex w-full justify-center text-[10px] text-gray-500">
Page < /> of < />
</>
),
});Pages that differ
pages overrides the header or footer on some pages. Each page looks up a band in this order:
firstorlastoddoreven- the top-level
headerorfooter
false leaves that band empty. Pages count from 1 over the whole document, before pageRanges drops any.
import { } from "takumi-pdf";
import { } from "takumi-pdf/primitives";
const = await (, {
: <>Acme Ltd</>,
: < />,
: {
: { : false },
: {
: (
<>
<>Thank you for your business.</>
<>Terms: net 30.</>
</>
),
},
},
});Automatic margins fit the tallest band on every page, so a taller last-page footer does not change where the other pages break. A one-page document is its own first and last page, and first wins.
Sizing the margin
margin defaults to "auto". The renderer measures the header and footer at full page width. Each automatic top or bottom margin is the band height plus a 20px inset, and at least 37.8px. Left and right margins are 37.8px. A numeric margin overrides that side, and sides left out of the object stay automatic:
const margin = { top: 96 };To compute a safe margin, call measure() with the same page size and fonts as the render. It lays the band out at full page width with three-digit page counters. Add the 20px inset to the returned height.
import { } from "@takumi-rs/helpers";
import { , } from "takumi-pdf";
import { , } from "takumi-pdf/primitives";
const = await (["Inter"]);
const = (
< ={{ : "Inter", : 10, : "center" }}>
Page < /> of < />
</>
);
const { } = await (, { : "a4", });
const = await (, {
: "a4",
,
,
: { : + 20 },
});Passing viewport instead of a page size measures the tree as-is. Counter hooks stay empty.
Page counters
<PageNumber /> is the current page, counting from 1, and <TotalPages /> is the number of pages in the document. Both take a format prop for a counter style, and pass className, style, and tw through.
Each primitive renders a <span> with a class hook. A node with the pageNumber or totalPages class receives the counter text in place of its children, so an empty <span /> is enough. Chromium's print templates use the same two class names. In HTML input, write the classes directly:
Page <span class="pageNumber"></span> of <span class="totalPages"></span>A node with both classes gets the page number.
Counters outside a band
Counters also work in document content. A position: fixed box repeats on every page, so a <PageNumber /> inside it shows each page's number:
<div style={{ position: "fixed", bottom: 24, left: 24, right: 24 }}>
Page <PageNumber /> of <TotalPages />
</div>A fixed box draws over the content, not in the margin, so leave room for it. Automatic margins fit only the header and footer.
In ordinary flow, <PageNumber /> shows the page the counter lands on. Its value depends on pagination, so the document lays out again after the numbers are filled in.
Counter styles
The format prop names a CSS @counter-style. On a class hook, add the style name as another class. The first supported name wins, unsupported names are ignored, and a counter without one uses decimal.
import { } from "takumi-pdf";
import { , } from "takumi-pdf/primitives";
const = await (, {
: (
< ={{ : 12 }}>
第 < ="trad-chinese-informal" /> 頁,共{" "}
< ="trad-chinese-informal" /> 頁
</>
),
});Numbers and numerals
| Style | Page 7 | Page 12 |
|---|---|---|
decimal (default) | 7 | 12 |
decimal-leading-zero | 07 | 12 |
lower-roman | vii | xii |
upper-roman | VII | XII |
cjk-decimal | 七 | 一二 |
trad-chinese-informal | 七 | 十二 |
cjk-ideographic | 七 | 十二 |
Blink defines cjk-ideographic as an extension of trad-chinese-informal, and takumi follows it. Both write numbers up to 9999 in words. Past that they fall back to cjk-decimal, so page 10000 formats as 一零零零零.
Alphabets
These styles count the way a spreadsheet names its columns. The letter after z is aa.
| Style | Page 12 |
|---|---|
lower-alpha, lower-latin | l |
upper-alpha, upper-latin | L |
lower-greek | μ |
hiragana | し |
katakana | シ |
Other digits
These styles write the decimal number in another script's digits.
| Style | Page 12 | Style | Page 12 |
|---|---|---|---|
arabic-indic | ١٢ | mongolian | ᠑᠒ |
bengali | ১২ | myanmar | ၁၂ |
cambodian | ១២ | oriya | ୧୨ |
devanagari | १२ | persian | ۱۲ |
gujarati | ૧૨ | tamil | ௧௨ |
gurmukhi | ੧੨ | telugu | ౧౨ |
kannada | ೧೨ | thai | ๑๒ |
khmer | ១២ | tibetan | ༡༢ |
lao | ໑໒ | urdu | ۱۲ |
malayalam | ൧൨ |
khmer and cambodian share their digits, as do persian and urdu.
Register a font that covers the digits of the style you pick. Latin fonts rarely include Thai or Tibetan digits.
Page-margin boxes in CSS
A document can draw its own headers and footers with the page-margin rules of CSS Paged Media. Nest one in an @page rule and give it content, and it draws in that part of the page's margin on every page the rule matches.
@page {
margin: 20mm 15mm;
font-size: 9pt;
color: #475569;
@top-left {
content: "Quarterly report";
}
@bottom-center {
content: "Page " counter(page) " of " counter(pages);
}
}
@page :first {
@top-left {
content: none;
}
}There are 16 boxes: three along each edge, such as @top-left, @top-center, and @top-right, and one in each corner, such as @bottom-right-corner. A box without content, or with content: none, is not drawn. The boxes inherit from the @page rule, so font and color set there apply to all of them.
content takes strings, images, counter(page), and counter(pages), with a counter style as the second argument, such as counter(page, upper-roman). counter-increment: page 2 in the @page rule changes how far the page number moves on each page. Other counters read 0.
The three boxes on an edge share its length by the width of their content, and width, height, margin, border, padding, and background size and decorate a box as they do an element. Text is centered across the edge by default, with the outer boxes aligned to the page's sides. text-align and vertical-align change that.
Page-margin boxes draw in the margin the @page rule or the margin option gives, and they do not grow it. They draw alongside a header or footer band, so use one or the other for each margin. A box does not paint the @page rule's own background or border.
Band height
A band with a counter is measured again with the real page count, up to three passes, and automatic margins use the final height. Give counters enough width that a longer page count does not wrap the band to a new line.
Last updated on