Skip to content

Add repeating page headers and footers

Configure a header (a title and description) and a footer once. The layout engine renders them on every page, including pages created by automatic page breaks. Set the data before the first addPage(). You do not draw the header or footer on each page. This recipe follows examples/13-header-footer.php.

Terminal window
composer require nextpdf/core:^3

No optional extension is required. The header and footer application programming interface (API) on the Layout concern has been stable since 1.0.0. It runs on the 8.1–8.4 backport matrix.

A header and footer are page furniture. The layout engine draws them in the reserved top and bottom bands of each page as it flushes the page. setHeaderData() records the content. setHeaderFont(), setHeaderMargin(), setFooterFont(), and setFooterMargin() set the typography and distance from the page edge. setPrintHeader(false) and setPrintFooter(false) turn the furniture off for documents that do not need it.

Header and footer geometry is measured against the page boundary. The page object dictionary (ISO 32000-2 §7.7.3.3) defines the MediaBox entry as the medium boundary and the CropBox entry as the visible region the page is clipped to. Section 14.11.2 defines the semantics of those page boundaries. The header margin you set is the offset from that boundary. Header and footer marks are not separate objects; they are part of each page’s Contents stream and are emitted per page (§7.7.3.3).

The API surface is generated from PHPDoc. This recipe uses these methods:

  • setHeaderData(string $title = '', string $description = '', string $logo = '', float $logoWidth = 0): static — sets header content.
  • setHeaderFont(string $family, float $size = 10): static / setFooterFont(string $family, float $size = 8): static — sets header and footer typography.
  • setHeaderMargin(float $margin): static / setFooterMargin(float $margin): static — sets the distance from the page edge, in millimetres.
  • setPrintHeader(bool $enabled): static / setPrintFooter(bool $enabled): static — toggles the furniture.
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();
$doc->setHeaderData(title: 'Quarterly Report', description: 'Confidential');
$doc->setHeaderFont('helvetica', 10);
$doc->setFooterFont('helvetica', 8);
$doc->addPage();
$doc->setFont('helvetica', '', 11);
$doc->multiCell(0, 7, 'Body text. The header and footer appear on this page '
. 'and on every page added afterwards, with no per-page code.');
$doc->addPage();
$doc->multiCell(0, 7, 'Page 2 — the furniture repeats automatically.');
$doc->save(getenv('NEXTPDF_COOKBOOK_OUTPUT') ?: __DIR__ . '/header-footer.pdf');

This complete, harness-ready example honours NEXTPDF_COOKBOOK_OUTPUT and does not pin its own entropy.

<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();
$doc->setTitle('Header and Footer');
// Configure the header once, before the first page. The layout engine
// draws it on every page, including auto-break pages.
$doc->setHeaderData(
title: 'NextPDF Example',
description: 'Header and Footer Demonstration',
);
$doc->setHeaderFont('helvetica', 10);
$doc->setHeaderMargin(5);
// Configure the footer. The footer band carries the page number.
$doc->setFooterFont('helvetica', 8);
$doc->setFooterMargin(10);
$doc->addPage();
$doc->setFont('helvetica', 'B', 16);
$doc->cell(0, 12, 'Document with Header and Footer', newLine: true);
$doc->ln(5);
$doc->setFont('helvetica', '', 11);
$doc->multiCell(0, 7, 'This document has a header with a title and description '
. 'that repeats on every page. The footer shows the page number.');
$doc->addPage();
$doc->setFont('helvetica', '', 11);
$doc->multiCell(0, 7, 'This is page 2. The header and footer appear without '
. 'any additional code on each new page.');
$out = getenv('NEXTPDF_COOKBOOK_OUTPUT') ?: __DIR__ . '/header-footer.pdf';
$doc->save($out);
echo "Created header-footer.pdf\n";
  • Set the furniture before the first page. Calling setHeaderData() after addPage() does not redraw earlier pages, so configure it before the first addPage().
  • Header margin versus content top margin. The header margin is the header’s distance from the page edge. It is independent of the body’s top margin. The two can overlap if the body top margin is smaller than the header band, so leave headroom.
  • Suppress furniture per document, not per page. setPrintHeader(false) applies to the whole document. There is no built-in per-page toggle. For a cover page without furniture, use a separate document or make a deliberate layout choice.
  • Logo path. The $logo argument to setHeaderData() is a local file path. The image loader rejects a URL scheme (see the images recipe), so use a local file.

Header and footer rendering runs per page. Its cost scales with the furniture content (a few text runs), not with body size. It adds negligible overhead to each page flush. The 2000 ms / 64 MB budget covers a multi-hundred-page document with furniture on every page.

The header title and description are rendered as document text. If they include user-controlled data, such as a tenant name, bound the length and sanitise it the same way you treat body text. This recipe performs no parsing and no network access.

StatementSpecClausereference_id
MediaBox page-object-dictionary entry defines the page medium boundary header/footer geometry is measured against.ISO 32000-2§7.7.3.3
CropBox page-object-dictionary entry is the visible region the page is clipped to.ISO 32000-2§7.7.3.3
Header/footer marks are part of each page’s Contents stream.ISO 32000-2§7.7.3.3

Reproducibility profile — structural. The trailer /ID and the /CreationDate / /ModDate atoms vary per save. The harness strips those atoms, then compares the qpdf-normalised structure. This recipe describes how NextPDF produces the structure. It does not assert ISO 32000-2 conformance as a blanket claim.

Not applicable. Repeating headers and footers are a Core capability, with no Premium gate.