Layout: headers, footers, columns, booklet, page manager
At a glance
Section titled “At a glance”The Layout module contains the page-furniture engines behind the Document facade: headers, footers, multi-column layout, saddle-stitch booklet imposition, and structural page operations. It is small and stable: six classes, all @since 1.0.0.
Install
Section titled “Install”composer require nextpdf/core:^3Conceptual overview
Section titled “Conceptual overview”Layout (src/Layout/, six classes, @since 1.0.0) is the engine layer under the HasLayout concern. Your application calls facade methods on Document; the trait routes each call to one of these engines. The manifest marks the module as standard risk and internal stability, with Core as its only dependent. Use it through the facade, not by constructing the classes directly.
HeaderFooter renders the repeating header and footer. It stores the title, description, logo, font, margin, and color state for each band. It emits Portable Document Format (PDF) content-stream operators on demand through renderHeader() and renderFooter(). The default footer prints a right-aligned "page / total" string. setHeaderCallback() and setFooterCallback() replace the default layout with a caller-supplied closure. getHeaderContentHeight() reports the vertical space the header consumes, so the page body can start below it. When the document is in tagged-PDF mode, HasPages suppresses the automatic header upstream because header content sits outside the structure tree.
ColumnLayout manages multi-column flow. setEqualColumns(int $count, float $totalWidth, float $gap = 5) divides the available width into equal columns. setColumnsArray() accepts explicit ColumnDefinition positions and widths. Use selectColumn() to choose a column, or nextColumn() to advance. getCurrentColumnX() / getCurrentColumnWidth() return the active column geometry. An invalid column count, negative gap, or non-positive computed column width raises PageLayoutException.
BookletLayout reorders pages for saddle-stitch (center-fold) binding. reorderPages() pads the page list to a multiple of four because a booklet sheet holds four page slots, then imposes pages outside-in so the folded, stapled sheets read in order. getMarginAdjustments() returns the inner (spine) and outer (edge) margins for each side at a given position. getSheetCount() reports how many double-sided sheets a page count needs. Reordering changes content placement only. The underlying PDF page sequence stays linear, consistent with the page tree, which defines the ordering of pages in the document (ISO 32000-2 §7.7).
PageManager provides structural page operations separate from content rendering. movePage(), copyPage(), and deletePage() operate on a PageData array by reference. Page regions (addPageRegion(), isInRegion(), getRegionOffset()) define no-write zones. Page groups (startPageGroup(), getGroupPageNo()) support per-section page numbering. PageRegion and ColumnDefinition are the two value carriers these engines use. The Writer module serializes the resulting pages into a page tree whose Kids entry is an array of indirect references to the immediate children of a page tree node (ISO 32000-2 §7.7.3.2).
API surface
Section titled “API surface”| Symbol | Kind | Stability | Since |
|---|---|---|---|
HeaderFooter::setHeaderData(string, string, string, float): self | method | stable | 1.0.0 |
HeaderFooter::setHeaderFont(string, float): self / setHeaderMargin(float): self | method | stable | 1.0.0 |
HeaderFooter::setFooterFont(string, float): self / setFooterMargin(float): self | method | stable | 1.0.0 |
HeaderFooter::setHeaderCallback(Closure): self / setFooterCallback(Closure): self | method | stable | 1.0.0 |
HeaderFooter::getHeaderContentHeight(): float | method | stable | 1.0.0 |
HeaderFooter::renderHeader(float, float, float, float, int, int): string | method | stable | 1.0.0 |
HeaderFooter::renderFooter(float, float, float, float, int, int, string): string | method | stable | 1.0.0 |
ColumnLayout::setEqualColumns(int, float, float): self | method | stable | 1.0.0 |
ColumnLayout::setColumnsArray(array): self / resetColumns(): self | method | stable | 1.0.0 |
ColumnLayout::selectColumn(int): self / nextColumn(): bool | method | stable | 1.0.0 |
ColumnLayout::getCurrentColumnX(float): float / getCurrentColumnWidth(float): float | method | stable | 1.0.0 |
BookletLayout::setBooklet(bool, float, float): void | method | stable | 1.0.0 |
BookletLayout::reorderPages(array): array | method | stable | 1.0.0 |
BookletLayout::getMarginAdjustments(int): array{left: float, right: float} | method | stable | 1.0.0 |
BookletLayout::getSheetCount(int): int | method | stable | 1.0.0 |
PageManager::movePage(int, int, array): void / copyPage(int, array): void / deletePage(int, array): void | method | stable | 1.0.0 |
PageManager::addPageRegion(float, float, float, float): void / isInRegion(float, float): bool | method | stable | 1.0.0 |
PageManager::getRegionOffset(float, float, float, float): float | method | stable | 1.0.0 |
PageManager::startPageGroup(): void / getGroupPageNo(int): int | method | stable | 1.0.0 |
PageRegion / ColumnDefinition | value carrier | stable | 1.0.0 |
Code sample — Quick start
Section titled “Code sample — Quick start”<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();$doc->setTitle('Header and Footer');
$doc->setHeaderData( title: 'NextPDF Example', description: 'Header and Footer Demonstration',);$doc->setHeaderFont('helvetica', 10);$doc->setHeaderMargin(5);$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->save(__DIR__ . '/output/13-header-footer.pdf');Source: examples/13-header-footer.php. The header renders on each addPage() call; the footer renders when the page flushes.
Code sample — Production
Section titled “Code sample — Production”A footer callback controls the page-number text, and the column engine drives a two-column body. You reach both engines through the facade.
<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();$doc->setTitle('Two-Column Report');
$doc->setFooterCallback(static function (Document $d): void { $d->setFont('helvetica', '', 8); $d->text(180.0, 285.0, 'Page ' . ($d->getPage() + 1));});
$doc->addPage();$doc->setEqualColumns(2, gap: 8);$doc->selectColumn(0);$doc->setFont('helvetica', '', 10);$doc->multiCell(0, 6, 'Left column flows here.');$doc->selectColumn(1);$doc->multiCell(0, 6, 'Right column flows here.');
$doc->save(__DIR__ . '/output/two-column-report.pdf');Source: pattern from examples/13-header-footer.php.
Edge cases & gotchas
Section titled “Edge cases & gotchas”setEqualColumns()rejects a column count below 1, a negative gap, or any layout whose computed column width is not positive. It raisesPageLayoutExceptioninstead of returning a degraded layout.selectColumn()ignores an out-of-range index and keeps the current column; it never throws for an invalid index.nextColumn()returnsfalsewhen it is already on the last column.BookletLayout::reorderPages()pads to a multiple of four with blank pages cloned from the last page’s dimensions. An empty page list returns an empty array. Reordering affects placement only;movePage()indices still refer to logical order.PageManager::movePage(),copyPage(), anddeletePage()silently do nothing for an out-of-range index; they validate withisset()and return without modifying the array. Verify the index yourself when a missing page is a caller error.getHeaderContentHeight()returns0.0when the header is disabled or has neither title nor description. The page body then starts at the top margin.- In tagged-PDF mode, the automatic header is suppressed upstream. Build a structure-aware layout for accessible documents.
Performance
Section titled “Performance”Header and footer rendering appends operators to the active page buffer in O(furniture content); cost scales with the title, description, and separator written, not with document size. Column math is O(1) per call. BookletLayout::reorderPages() is O(n) in page count, with a one-time padding pass; the imposition loop touches each padded slot once. PageManager region tests are O(regions) per point, and page operations are O(n) array splices. These engines retain no per-page state across the document, so they do not add memory growth on long documents. The dominant memory cost is the accumulated content stream, which the streaming-and-memory concept covers. The HyperText Markup Language (HTML) pipeline latency and memory budget gate is documented in PERFORMANCE-BUDGETS; it is scoped to the HTML render path and does not gate these layout engines directly. The performance_budget of 1500 ms / 64 MB is the canvas envelope for the quick-start, not a per-call contract.
Security notes
Section titled “Security notes”These engines consume caller-supplied strings and a caller-supplied logo path. The logo path flows through the image engine, which validates the file before embedding it. renderHeader() and renderFooter() escape title, description, and page-number text through the centralized PDF string escaper before they reach the content stream, so caller text cannot break out of the literal-string grammar. A header or footer callback runs caller code with the same trust as the rest of the document; treat any external data it reads accordingly. PageManager page operations move references to existing PageData; they do not parse untrusted bytes.
Conformance
Section titled “Conformance”| Claim | Standard | Clause | Evidence |
|---|---|---|---|
| Page reordering for booklet output changes placement only; the page tree still defines the linear ordering of pages in the document. | ISO 32000-2 | §7.7 | |
The serialized page tree node’s Kids entry is an array of indirect references to the immediate children of that node. | ISO 32000-2 | §7.7.3.2 |
The table paraphrases each clause and keeps glossary terms pinned; it does not reproduce normative text.
See also
Section titled “See also”- Core/HasLayout trait — the facade concern that composes the layout engines.
- Core/HasPages trait — page size and margins used by column math.
- Writer — the PDF object and page-tree emitter that serializes laid-out pages.
- Streaming and memory — why furniture engines hold no per-page state, and how the memory-bound rendering path works.
- HTML / Streaming constraints (ADR-001) — the rationale for the single-pass streaming scope.