Zum Inhalt springen

Layout: Kopf- und Fußzeilen, Spalten, Broschürenlayout, Seitenmanager

Das Layoutmodul enthält die Seitenausstattungs-Engines, an die die Document-Fassade delegiert: Rendering von Kopf- und Fußzeilen, mehrspaltiges Layout, Broschüren-Imposition mit Rückenheftung und strukturelle Seitenoperationen. Es ist ein kleines, stabiles Modul mit sechs Klassen, alle @since 1.0.0.

Terminal-Fenster
composer require nextpdf/core:^3

Layout (src/Layout/, sechs Klassen, @since 1.0.0) ist die Engine-Schicht unterhalb des HasLayout-Concerns. Anwendungscode ruft die Fassadenmethoden von Document auf; der Trait leitet jeden Aufruf an eine dieser Engines weiter. Das Modul ist im Manifest mit dem Risiko standard und der Stabilität internal gekennzeichnet, wobei Core das einzige abhängige Modul ist. Sie erreichen es über die Fassade, nicht durch direktes Konstruieren der Klassen.

HeaderFooter rendert die wiederkehrenden Kopf- und Fußzeilen. Es verwaltet Titel, Beschreibung, Logo, Schrift, Rand und Farbzustand für jedes Band. Bei Bedarf erzeugt es PDF-Content-Stream-Operatoren über renderHeader() und renderFooter(). Die Standardfußzeile gibt eine rechtsbündige "page / total"-Zeichenkette aus. setHeaderCallback() und setFooterCallback() ersetzen das Standard-Layout durch eine vom Aufrufer bereitgestellte Closure. getHeaderContentHeight() gibt den vertikalen Platz zurück, den die Kopfzeile beansprucht, damit der Seitenkörper darunter beginnen kann. Wenn sich das Dokument im Tagged-PDF-Modus befindet, wird die automatische Kopfzeile bereits in HasPages unterdrückt, weil Kopfzeileninhalt außerhalb des Strukturbaums liegt.

ColumnLayout verwaltet den mehrspaltigen Fluss. setEqualColumns(int $count, float $totalWidth, float $gap = 5) teilt eine verfügbare Breite in gleich große Spalten auf. setColumnsArray() akzeptiert explizite ColumnDefinition-Positionen und -Breiten. Der Cursor wählt mit selectColumn() eine Spalte aus oder rückt mit nextColumn() vor. getCurrentColumnX() / getCurrentColumnWidth() liefern die Geometrie der aktiven Spalte. Eine ungültige Spaltenanzahl, ein negativer Abstand oder eine berechnete Spaltenbreite, die nicht positiv ist, lösen PageLayoutException aus.

BookletLayout ordnet Seiten für die Rückenheftung (Mittelfalz) neu an. reorderPages() füllt die Seitenliste auf ein Vielfaches von vier auf (ein Broschürenbogen fasst vier Seitenplätze) und bringt die Seiten anschließend von außen nach innen in die Impositionsreihenfolge, damit die gefalteten und gehefteten Bögen in der richtigen Reihenfolge lesbar sind. getMarginAdjustments() liefert für eine gegebene Seitenposition den inneren (Bund-) und äußeren (Kanten-)Rand. getSheetCount() gibt zurück, wie viele doppelseitige Bögen eine Seitenzahl benötigt. Die Neuanordnung ändert nur die Platzierung des Inhalts. Die zugrunde liegende PDF-Seitenabfolge bleibt linear und konsistent mit dem Seitenbaum, der die Reihenfolge der Seiten im Dokument definiert (ISO 32000-2 §7.7).

PageManager stellt strukturelle Seitenoperationen getrennt vom Inhalts-Rendering bereit. movePage(), copyPage() und deletePage() operieren per Referenz auf einem PageData-Array. Seitenregionen (addPageRegion(), isInRegion(), getRegionOffset()) definieren Sperrzonen, in die nicht geschrieben wird. Seitengruppen (startPageGroup(), getGroupPageNo()) unterstützen eine abschnittsweise Seitennummerierung. PageRegion und ColumnDefinition sind die beiden Wertträger, die die Engines verwenden. Das Writer-Modul serialisiert die resultierenden Seiten in einen Seitenbaum, dessen Kids-Eintrag ein Array indirekter Referenzen auf die unmittelbaren Kinder eines Seitenbaumknotens ist (ISO 32000-2 §7.7.3.2).

SymbolArtStabilitätSeit
HeaderFooter::setHeaderData(string, string, string, float): selfMethodestabil1.0.0
HeaderFooter::setHeaderFont(string, float): self / setHeaderMargin(float): selfMethodestabil1.0.0
HeaderFooter::setFooterFont(string, float): self / setFooterMargin(float): selfMethodestabil1.0.0
HeaderFooter::setHeaderCallback(Closure): self / setFooterCallback(Closure): selfMethodestabil1.0.0
HeaderFooter::getHeaderContentHeight(): floatMethodestabil1.0.0
HeaderFooter::renderHeader(float, float, float, float, int, int): stringMethodestabil1.0.0
HeaderFooter::renderFooter(float, float, float, float, int, int, string): stringMethodestabil1.0.0
ColumnLayout::setEqualColumns(int, float, float): selfMethodestabil1.0.0
ColumnLayout::setColumnsArray(array): self / resetColumns(): selfMethodestabil1.0.0
ColumnLayout::selectColumn(int): self / nextColumn(): boolMethodestabil1.0.0
ColumnLayout::getCurrentColumnX(float): float / getCurrentColumnWidth(float): floatMethodestabil1.0.0
BookletLayout::setBooklet(bool, float, float): voidMethodestabil1.0.0
BookletLayout::reorderPages(array): arrayMethodestabil1.0.0
BookletLayout::getMarginAdjustments(int): array{left: float, right: float}Methodestabil1.0.0
BookletLayout::getSheetCount(int): intMethodestabil1.0.0
PageManager::movePage(int, int, array): void / copyPage(int, array): void / deletePage(int, array): voidMethodestabil1.0.0
PageManager::addPageRegion(float, float, float, float): void / isInRegion(float, float): boolMethodestabil1.0.0
PageManager::getRegionOffset(float, float, float, float): floatMethodestabil1.0.0
PageManager::startPageGroup(): void / getGroupPageNo(int): intMethodestabil1.0.0
PageRegion / ColumnDefinitionWertträgerstabil1.0.0
<?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');

Quelle: examples/13-header-footer.php. Die Kopfzeile wird bei jedem addPage() gerendert; die Fußzeile wird gerendert, wenn der Seitenpuffer geleert wird.

Ein Fußzeilen-Callback erzeugt den Seitennummerntext, und die Spalten-Engine steuert einen zweispaltigen Seitenkörper. Beide Engines werden über die Fassade angesprochen.

<?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');

Quelle: Muster aus examples/13-header-footer.php.

  • setEqualColumns() weist eine Spaltenanzahl unter 1, einen negativen Abstand oder ein Layout mit nicht positiver berechneter Spaltenbreite zurück. Jeder dieser Fälle löst PageLayoutException aus, statt ein degradiertes Layout zurückzugeben.
  • selectColumn() ignoriert einen Index außerhalb des gültigen Bereichs und behält die aktuelle Spalte bei; für einen ungültigen Index wirft es nie eine Ausnahme. nextColumn() liefert false, wenn die aktuelle Spalte bereits die letzte ist.
  • BookletLayout::reorderPages() füllt mit leeren Seiten, die aus den Abmessungen der letzten Seite geklont werden, auf ein Vielfaches von vier auf. Eine leere Seitenliste liefert ein leeres Array. Die Neuanordnung betrifft nur die Platzierung; die Indizes von movePage() beziehen sich weiterhin auf die logische Reihenfolge.
  • PageManager::movePage(), copyPage() und deletePage() tun bei einem Index außerhalb des gültigen Bereichs stillschweigend nichts — sie prüfen mit isset() und kehren zurück, ohne das Array zu verändern. Prüfen Sie den Index selbst, wenn eine fehlende Seite ein Aufruferfehler ist.
  • getHeaderContentHeight() liefert 0.0, wenn die Kopfzeile deaktiviert ist oder weder Titel noch Beschreibung hat. Der Seitenkörper beginnt dann am oberen Rand.
  • Im Tagged-PDF-Modus wird die automatische Kopfzeile bereits vorher unterdrückt. Erstellen Sie ein strukturbewusstes Layout für barrierefreie Dokumente.

Das Rendering von Kopf- und Fußzeile hängt in O(Ausstattungsinhalt) Operatoren an den aktiven Seitenpuffer an — die Kosten sind proportional zum geschriebenen Titel, zur Beschreibung und zum Trenner, nicht zur Dokumentgröße. Die Spaltenberechnung ist O(1) pro Aufruf. BookletLayout::reorderPages() ist O(n) in der Seitenanzahl, mit einem einmaligen Auffülldurchlauf; die Impositionsschleife berührt jeden aufgefüllten Platz einmal. PageManager-Regionstests sind O(Regionen) pro Punkt; Seitenoperationen sind O(n)-Array-Splices. Keine dieser Engines speichert dokumentweit Zustand pro Seite, daher tragen sie bei langen Dokumenten nicht zum Speicherwachstum bei. Der dominierende Speicherbedarf entsteht durch den angesammelten Content-Stream, den das Konzept Streaming und Speicher behandelt. Das Latenz- und Speicherbudget-Gate der HTML-Pipeline ist in PERFORMANCE-BUDGETS dokumentiert; es ist auf den HTML-Render-Pfad beschränkt und steuert diese Layout-Engines nicht direkt. Das performance_budget von 1500 ms / 64 MB ist die Canvas-Hülle für den Schnellstart, keine Zusage pro Aufruf.

Diese Engines verarbeiten Zeichenketten und einen Logo-Pfad, die der Aufrufer bereitstellt. Der Logo-Pfad läuft über die Bild-Engine, die die Datei vor dem Einbetten validiert. renderHeader() und renderFooter() escapen Titel, Beschreibung und Seitennummerntext über den zentralen PDF-String-Escaper, bevor sie den Content-Stream erreichen, sodass Aufrufertext nicht aus der Grammatik der literalen Zeichenketten ausbrechen kann. Ein Kopf- oder Fußzeilen-Callback führt Aufrufercode mit demselben Vertrauen aus wie der Rest des Dokuments — behandeln Sie alle externen Daten, die er liest, entsprechend. PageManager-Seitenoperationen verschieben Referenzen auf vorhandene PageData; sie parsen keine nicht vertrauenswürdigen Bytes.

AussageStandardAbschnittNachweis
Die Seitenneuanordnung für die Broschürenausgabe ändert nur die Platzierung; der Seitenbaum definiert weiterhin die lineare Reihenfolge der Seiten im Dokument.ISO 32000-2§7.7
Der Kids-Eintrag des serialisierten Seitenbaumknotens ist ein Array indirekter Referenzen auf die unmittelbaren Kinder dieses Knotens.ISO 32000-2§7.7.3.2

Die Klauseln sind paraphrasiert und glossargebunden; es wird kein normativer Text wiedergegeben.