Salta ai contenuti

Impaginare contenuti HTML lunghi su più pagine

Distribuire contenuti lunghi su più pagine tramite interruzioni di pagina automatiche. Aggiungere una struttura per consentire al lettore di spostarsi tra le sezioni. Questa ricetta segue examples/12-bookmarks-and-toc.php.

Terminal window
composer require nextpdf/core:^3

Il vincolo fa riferimento al pacchetto nextpdf/core. L’esempio è eseguibile con PHP 8.4.

setAutoPageBreak(true, $margin) indica al motore di iniziare una nuova pagina ogni volta che il contenuto supererebbe la soglia del margine inferiore. Il motore frammenta il testo lungo scritto tramite multiCell() o writeHtml() in corrispondenza di quel limite. Il modulo CSS Fragmentation (css_break_3) è classificato come Verificato nella matrice di supporto e gestisce il comportamento di interruzione per la pipeline HTML.

bookmark($title, $level) aggiunge un elemento della struttura che punta alla posizione corrente. Un elemento della struttura PDF associa una destinazione, così l’utente può passare direttamente a una pagina (ISO 32000-2). Il motore scrive quella destinazione come voce Dest dell’elemento (ISO 32000-2). L’argomento level annida gli elementi in un sommario gerarchico nella barra laterale del lettore.

La pipeline rimane a passaggio singolo (ADR-001). L’impaginazione viene decisa durante l’emissione del flusso, non da un albero di layout mantenuto in memoria.

  • setAutoPageBreak(bool $enabled, float $margin = 20): staticNextPDF\Core\Concerns\HasPages.
  • bookmark(string $title, int $level = 0, float $y = -1): staticNextPDF\Core\Concerns\HasNavigation.
  • multiCell(...) / writeHtml(string $html): staticNextPDF\Core\Concerns\HasTextOutput.

La tabella PHPDoc completa è generata dal codice sorgente.

<?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->bookmark('Section 1', level: 0);
$doc->setFont('helvetica', '', 11);
for ($i = 1; $i <= 80; $i++) {
$doc->multiCell(0, 7, "Paragraph {$i} of a long flowing document.");
}
$doc->save(__DIR__ . '/out.pdf');

Questo esempio è autonomo ed eseguibile tramite l’harness. Crea un documento con più capitoli, una struttura annidata e interruzioni di pagina automatiche, e ricalca examples/12-bookmarks-and-toc.php.

<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();
$doc->setTitle('Bookmarks and Navigation');
$doc->setPrintHeader(false);
$doc->setPrintFooter(false);
$doc->setAutoPageBreak(true, margin: 25);
$chapters = [
'Chapter 1: Introduction' => ['What is NextPDF?', 'Key Features'],
'Chapter 2: Getting Started' => ['Installation', 'Your First PDF'],
'Chapter 3: Advanced Topics' => ['Worker-safe Architecture', 'Streaming Output'],
];
$body = 'NextPDF is a modern PDF 2.0 library for PHP. This paragraph is '
. 'repeated so the content overflows the page and the engine inserts '
. 'an automatic page break at the bottom-margin threshold.';
foreach ($chapters as $chapter => $sections) {
$doc->addPage();
$doc->bookmark($chapter, level: 0);
$doc->setFont('helvetica', 'B', 18);
$doc->cell(0, 12, $chapter, newLine: true);
$doc->ln(3);
foreach ($sections as $section) {
$doc->bookmark($section, level: 1);
$doc->setFont('helvetica', 'B', 14);
$doc->cell(0, 10, $section, newLine: true);
$doc->setFont('helvetica', '', 11);
for ($i = 0; $i < 12; $i++) {
$doc->multiCell(0, 7, $body);
}
$doc->ln(4);
}
}
$out = getenv('NEXTPDF_COOKBOOK_OUTPUT');
$doc->save($out !== false ? $out : __DIR__ . '/paginate-long-html.pdf');
echo "Wrote paginate-long-html.pdf\n";

STDOUT previsto:

Wrote paginate-long-html.pdf
  • Disabilitarla e dimenticarsene. Quando l’interruzione di pagina automatica è disabilitata, il motore ritaglia il contenuto oltre il margine inferiore anziché distribuirlo. Riabilitarla prima di un contenuto lungo.
  • Contenuto non suddivisibile. Un singolo blocco più alto dell’altezza utile della pagina può generare UnsplittableContentException. Una riga di tabella molto alta o un’immagine di grandi dimensioni possono causarla. Suddividere il contenuto di origine.
  • Inserire il segnalibro prima del contenuto. Chiamare bookmark() nel punto verso cui si desidera far puntare la destinazione. Posizionarlo immediatamente prima del titolo che verrà scritto subito dopo, nella pagina desiderata.
  • Intestazione e piè di pagina riservano spazio. Un’intestazione o un piè di pagina di stampa riduce l’altezza utile del contenuto, e la soglia di interruzione ne tiene conto. Disabilitandoli entrambi, come fa l’esempio, si ottiene l’altezza completa del corpo.
  • Annidamento della struttura. level è la profondità di annidamento. Un elemento figlio a level: 1 deve seguire un elemento padre a level: 0. In caso contrario, il lettore appiattisce l’albero della struttura.

Il motore decide l’impaginazione durante un unico passaggio di emissione. Il costo è lineare rispetto alla lunghezza del contenuto, O(n). Il budget è wall_ms: 2000, peak_mb: 96. Il tempo effettivo risulta leggermente superiore rispetto alle ricette a pagina singola a causa dell’assemblaggio della tabella xref multipagina e della struttura. Il modello di streaming mantiene limitato l’uso della memoria e la struttura resta un piccolo elenco piatto.

Estratto della matrice di supporto CSS (solo righe Verificato)

Sezione intitolata “Estratto della matrice di supporto CSS (solo righe Verificato)”

Sono riportate solo le righe Verificato della matrice di supporto CSS sottoposta a verifica.

Modulo W3CLivelloStatoEvidenza
CSS Fragmentation (css_break_3)3Verificatosrc/Html/Fragmentation/, tests/Unit/Html/PagedMedia/
CSS Table (css_tables_3)3Verificatosrc/Html/Table/ + PDF golden
CSS Cascading and Inheritance (css_cascade_3)3Verificatosrc/Html/Cascade/

@page definisce i selettori di pagina denominata e fa parte di CSS Paged Media. Consultare la matrice per conoscere la classificazione corrente di tale modulo prima di farvi affidamento.

Vincoli dello streaming a passaggio singolo (ADR-001)

Sezione intitolata “Vincoli dello streaming a passaggio singolo (ADR-001)”

Il motore emette le interruzioni di pagina man mano che il flusso procede. Non esiste alcun albero mantenuto in memoria da ridistribuire, quindi una decisione di interruzione è definitiva una volta presa. Alcuni contenuti richiedono il numero di pagina definitivo dopo il layout, ad esempio un riferimento incrociato. Tale contenuto è soggetto a vincoli, pertanto va creato tenendo presente questa limitazione.

L’impaginazione è di competenza del controller delle interruzioni di pagina, non del parser. Il parser non emette operatori grezzi di transizione di pagina; richiede l’interruzione tramite il contratto del controller.

Budget di memoria per documenti di grandi dimensioni

Sezione intitolata “Budget di memoria per documenti di grandi dimensioni”

Il modello di streaming mantiene il buffer della pagina corrente e l’elenco piatto della struttura, non tutte le pagine contemporaneamente. Un documento molto lungo rimane entro il limite massimo dell’ADR-020 perché il motore scarica le pagine completate. Le tabelle e i contenitori flex rispettano comunque il limite di 5,000 nodi per contesto.

Un documento ostile non può forzare un utilizzo illimitato della memoria. I limiti sugli elementi e sull’annidamento (ADR-001) e il budget di nodi per contesto (ADR-020) delimitano il lavoro. Convalidare la lunghezza e la struttura del contenuto lungo fornito dall’utente. Il motore rende come testo un titolo della struttura controllato da un utente malintenzionato e non lo interpreta mai.

DichiarazioneSpecificaClausolareference_id
A ogni elemento della struttura può essere associata una destinazione, così l’utente può passarvi direttamente.ISO 32000-2iso32000_2_sec12#x1.x5.p4
La voce Dest di un elemento della struttura indica la destinazione visualizzata all’attivazione dell’elemento.ISO 32000-2iso32000_2_sec12#x1.x11.p30

Questa ricetta mostra come NextPDF distribuisce contenuti lunghi e crea una struttura. CSS Fragmentation è classificato come Verificato nella matrice di supporto.

Non applicabile.