Skip to content
getnextpdf.com

Headers, footers, and multi-page documents

One page is rarely enough. In this tutorial you write a document that grows to three pages on its own. You also give every page a repeating header, and a footer that shows the page number.

You will write one script, 01-multipage.php. It produces a three-page travel journal as a PDF (Portable Document Format) file:

  • The journal entries flow from page to page automatically. You never say where a page ends; the engine decides.
  • A header is a strip at the top of a page that repeats on every page. Yours shows the document title.
  • A footer is the matching strip at the bottom. Yours shows the page number as 1 / 3, 2 / 3, and 3 / 3.

Everything runs with the nextpdf/core package alone: no font files, no extra extensions, and no network.

Two ideas do all the work in this script.

The first is the automatic page break. As the engine writes text, it tracks a cursor: the spot on the page where the next line will land. setAutoPageBreak(true, margin: 25) tells the engine to watch that cursor. When it comes within 25 points (about a third of an inch) of the bottom edge, the engine closes the page and starts a fresh one.

The second is the page furniture: the header and the footer. You describe them once, before the first page. setHeaderData() records the title and a short description for the top strip. The font and margin calls choose the lettering and the distance from the page edge. The engine then draws this furniture on every page it creates, including the pages it adds by itself.

Create 01-multipage.php in your project folder:

<?php
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
// Make sure the output folder exists next to this script.
@mkdir(__DIR__ . '/out');
$document = Document::createStandalone();
$document->setTitle('NextPDF Travel Journal');
// Configure the header and footer BEFORE the first page.
// The engine repeats them on every page for you.
$document->setHeaderData(title: 'NextPDF Travel Journal', description: 'A multi-page tutorial document');
$document->setHeaderFont('helvetica', 10);
$document->setHeaderMargin(5);
$document->setFooterFont('helvetica', 8);
$document->setFooterMargin(10);
// Turn on automatic page breaks. When the text cursor gets within
// 25 points of the bottom edge, the engine starts a new page.
$document->setAutoPageBreak(true, margin: 25);
$document->addPage();
$document->setFont('helvetica', 'B', 18);
$document->cell(0, 12, 'Travel journal: three days on the coast', newLine: true);
$document->ln(5);
$days = [
1 => 'We followed the shoreline north and counted seventeen lighthouses.',
2 => 'Rain moved in before noon, so we sketched the harbor from a cafe window.',
3 => 'On the last morning the fog lifted and the whole bay turned silver.',
];
foreach ($days as $day => $highlight) {
$document->setFont('helvetica', 'B', 14);
$document->cell(0, 10, "Day {$day}", newLine: true);
$document->setFont('helvetica', '', 11);
for ($entry = 1; $entry <= 12; $entry++) {
$text = "Entry {$entry} of day {$day}. {$highlight} "
. 'The trail hugged the cliffs for most of the afternoon, and every '
. 'turn opened another view of the water. We stopped often to take '
. 'notes, compare maps, and argue happily about where to eat dinner. '
. 'By the time we reached the guesthouse, our boots were soaked and '
. 'our notebooks were full.';
$document->multiCell(0, 7, $text);
$document->ln(3);
}
$document->ln(5);
}
// Ask the engine how many pages the layout produced, then save.
$pages = $document->getNumPages();
$document->save(__DIR__ . '/out/travel-journal.pdf');
echo "Wrote out/travel-journal.pdf with {$pages} pages\n";

Run it with php 01-multipage.php. The script prints one line:

Wrote out/travel-journal.pdf with 3 pages

Open out/travel-journal.pdf and scroll through it. The title strip sits at the top of all three pages, and the page numbers count up at the bottom right.

The writing loop never mentions pages. It writes each day heading with cell(), which puts one line in an invisible box. It writes the entries with multiCell(), which wraps long text onto as many lines as it needs. Thirty-six entries do not fit on one page, so the automatic page break fires twice. That is how the journal becomes exactly three pages.

The header and footer were configured before the first addPage() call. That order matters: a page that has already been drawn does not update afterwards. Configure the furniture first, and every page comes out consistent.

You never drew the page number. The footer prints it by default; you only chose its font and its distance from the page edge. While the engine writes pages, it cannot know the final count yet. So it leaves a placeholder in each footer and fills in the true total when you save.

Two small details round the script off. @mkdir creates the out folder, and the @ sign keeps PHP quiet when the folder already exists. getNumPages() asks the engine how many pages the layout produced, so the final message can report the real count.

  • A “failed to open stream” error on the require line means the script could not find vendor/autoload.php. Run it inside the project folder that contains vendor/.
  • Everything sits on a single page, and the end of the text is cut off? Then automatic page breaks are off. Keep the setAutoPageBreak(true, margin: 25) line, and keep it above the writing loop.
  • A mistyped font family throws an exception that names the font it could not find. Check the spelling of helvetica, and see Fonts and tagging for deeper font problems.
  • For anything else, the troubleshooting hub maps symptoms to causes, and the error reference explains the engine’s general exceptions and how to recover from each.

Your documents can now grow to any length and still look finished. In the next tutorial you write your content in HTML (Hypertext Markup Language) and let the engine turn it into pages.

When you want finer control over today’s topics, two recipes go deeper: