Zum Inhalt springen

Ein mehrseitiges Dokument mit automatischen Seitenumbrüchen erstellen

Erstellen Sie ein Dokument, dessen Inhalt sich über viele Seiten erstreckt. Fügen Sie Inhalt nach und nach hinzu. Ist setAutoPageBreak() aktiviert, beginnt die Layout-Engine automatisch eine neue Seite, sobald der Cursor den unteren Rand erreicht. Lesen Sie nach save() die endgültige Seitenzahl mit getNumPages() aus. Dieses Recipe folgt examples/05-multi-page.php.

Beim Aufruf von save() schreibt die Engine die Marken jeder Seite in einen Content-Stream. ISO 32000-2 §7.7.3.3 definiert die Contents einer Seite als einen einzelnen Stream oder als ein der Reihenfolge nach verkettetes Array von Streams. Eine mehrseitige Ausgabe besteht also aus einer Folge von Seitenobjekten, nicht aus einem einzelnen Puffer.

Terminal-Fenster
composer require nextpdf/core:^3

Es ist keine optionale Erweiterung erforderlich. Dieses Recipe läuft auf der Backport-Matrix für PHP 8.1–8.4. Sowohl getNumPages() als auch setAutoPageBreak() sind seit 1.0.0 stabil.

Ein NextPDF-Dokument ist ein Seitenbaum. Während Sie Inhalt hinzufügen, rückt ein interner Cursor (getY()) vor. Sind automatische Seitenumbrüche aktiviert, prüft die Engine vor jedem Inhaltsblock den verbleibenden vertikalen Platz. Passt der Block nicht mehr in den Bereich oberhalb des unteren Rands, schließt die Engine die aktuelle Seite ab und ruft für Sie addPage() auf. Der untere Rand, den Sie an setAutoPageBreak() übergeben, ist die Auslöseschwelle.

Seitenbezogene Attribute wie die Media-Box sind vererbbar. ISO 32000-2 §7.7.3.4 legt fest, dass ein in einem Seitenobjekt weggelassenes Attribut von einem Vorgängerknoten im Seitenbaum aufgelöst wird. NextPDF legt eine einheitliche Seitengröße für das gesamte Dokument fest, sodass jede erzeugte Seite dieselbe Geometrie verwendet und Sie sie nicht pro Seite erneut angeben müssen.

Die API-Oberfläche wird automatisch aus PHPDoc generiert. Dieses Recipe stützt sich auf die folgenden Methoden:

  • Document::createStandalone(): self — erstellt ein isoliertes Dokument.
  • setAutoPageBreak(bool $enabled, float $margin = 20): static — aktiviert automatische Seitenumbrüche. $margin ist der Auslöser für den unteren Rand in Millimetern.
  • addPage(?PageSize $size = null, Orientation $orientation = Orientation::Portrait): static — legt die erste Seite und jede explizit angeforderte weitere Seite an.
  • multiCell(...): static / cell(...): static — geben fließende oder fest positionierte Textblöcke aus. Die Seitenumbruchprüfung misst diese Blöcke.
  • getNumPages(): int — liefert die Anzahl der Seiten nach dem Layout.
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();
$doc->setAutoPageBreak(true, margin: 25);
$doc->addPage();
$doc->setFont('helvetica', '', 11);
for ($i = 1; $i <= 60; $i++) {
$doc->multiCell(0, 7, "Line {$i}: content flows until the page is full, "
. 'then the engine starts a new page automatically.');
}
$doc->save(getenv('NEXTPDF_COOKBOOK_OUTPUT') ?: __DIR__ . '/multi-page.pdf');
echo 'Pages: ' . $doc->getNumPages() . "\n";

Dies ist das vollständige, für den Harness vorbereitete Beispiel. Es berücksichtigt die Umgebungsvariable NEXTPDF_COOKBOOK_OUTPUT, die der Harness setzt; geben Sie das PDF daher nicht über STDOUT aus. Es legt keine eigene Entropie fest. Bei Ausführung durch den Harness fixiert DeterministicMode die Uhr, /ID und das Branding.

<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();
$doc->setTitle('Multi-Page Document');
// Enable automatic page breaks. The 25 mm bottom margin is the trigger:
// when the cursor would cross it, the engine flushes the page and adds
// a new one before the next block is drawn.
$doc->setAutoPageBreak(true, margin: 25);
$doc->addPage();
$doc->setFont('helvetica', 'B', 18);
$doc->cell(0, 12, 'Multi-Page Document Example', newLine: true);
$doc->ln(5);
for ($chapter = 1; $chapter <= 3; $chapter++) {
$doc->setFont('helvetica', 'B', 14);
$doc->cell(0, 10, "Chapter {$chapter}: Lorem Ipsum", newLine: true);
$doc->setFont('helvetica', '', 11);
for ($para = 1; $para <= 5; $para++) {
$text = "Paragraph {$para} of Chapter {$chapter}. "
. 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. '
. 'Sed do eiusmod tempor incididunt ut labore et dolore magna '
. 'aliqua. Ut enim ad minim veniam, quis nostrud exercitation '
. 'ullamco laboris nisi ut aliquip ex ea commodo consequat.';
$doc->multiCell(0, 7, $text);
$doc->ln(3);
}
$doc->ln(5);
}
// The harness sets NEXTPDF_COOKBOOK_OUTPUT; honour it. STDOUT stays free
// for progress text only.
$out = getenv('NEXTPDF_COOKBOOK_OUTPUT') ?: __DIR__ . '/multi-page.pdf';
$doc->save($out);
echo 'Created multi-page.pdf with ' . $doc->getNumPages() . " pages\n";
  • Automatischer Seitenumbruch deaktiviert. Mit setAutoPageBreak(false, …) wird Inhalt jenseits des unteren Rands auf der Seite abgeschnitten, statt umgebrochen; das Dokument behält dabei eine einzige Seite. Aktivieren Sie ihn für fließende Inhalte.
  • Ein einzelner Block, der höher als die Seite ist. Die Engine teilt eine multiCell, deren Text die druckbare Höhe überschreitet, intern auf. Ein einzelner unteilbarer Block, der jedoch höher als der nutzbare Bereich ist, etwa ein hohes Bild, wird einmal platziert und läuft über. Brechen Sie ihn selbst um.
  • Der erste addPage() ist weiterhin erforderlich. cell() ruft bei Bedarf addPage() auf, wenn noch keine Seite vorhanden ist. Rufen Sie dennoch addPage() explizit auf, damit Größe und Ausrichtung der ersten Seite deterministisch sind.
  • Maßeinheiten des Rands. Der Rand von setAutoPageBreak() wird im Standard-Einheitensystem in Millimetern angegeben, nicht in Punkten.

getNumPages() ist O(1). Es liest einen Zähler und berechnet das Layout nicht neu. Der Speicherbedarf skaliert mit dem zwischengespeicherten Inhalt, nicht mit der Seitenzahl. Die Engine schreibt abgeschlossene Seiten in den Ausgabepuffer, sobald sie fertiggestellt sind — das Single-Pass-Streaming-Modell (ADR-001). Das Budget von 2000 ms / 64 MB deckt Dokumente mit einigen Hundert Textseiten auf dem Referenzhost ab.

Dieses Recipe schreibt nur den Text, den Ihr Code liefert. Es führt kein Parsen von Eingaben, keinen Netzwerkzugriff und keine Deserialisierung durch. Behandeln Sie jeglichen extern bezogenen Text als nicht vertrauenswürdig und begrenzen Sie seine Länge vor dem Rendern. Die Engine erzwingt keine Inhaltsgrößenbegrenzung auf Anwendungsebene.

AussageSpezifikationAbschnittreference_id
Der Contents-Eintrag einer Seite ist ein einzelner Stream oder ein geordnetes, verkettetes Array von Streams.ISO 32000-2§7.7.3.3
Ein vererbbares Seitenattribut, das in einem Seitenobjekt weggelassen wird, wird von einem Vorgängerknoten im Seitenbaum aufgelöst.ISO 32000-2§7.7.3.4
Die /ID im Trailer ist ein Dateiidentifikator aus zwei Byte-Strings (in PDF 2.0 erforderlich).ISO 32000-2§7.5.5

Reproduzierbarkeitsprofil — strukturell (warum nicht bitweise). Jedes gespeicherte Dokument trägt im Trailer eine /ID, deren zwei Byte-Strings einen Dateiidentifikator bilden (ISO 32000-2 §7.5.5, siehe oben). Das zweite Element ist nicht über Durchläufe hinweg stabil; daher unterscheiden sich die rohen Bytes zwischen den Durchläufen selbst bei identischem Inhalt. Der Harness vergleicht die qpdf-normalisierte Struktur, die /ID, /CreationDate und /ModDate entfernt. Dieses Recipe beschreibt, wie NextPDF die Struktur erzeugt. Es behauptet nicht pauschal ISO 32000-2-Konformität.

Nicht zutreffend. Mehrseitige Komposition mit automatischen Seitenumbrüchen ist eine Core-Fähigkeit, ohne Premium-Gate.