Ir al contenido

Paginar HTML extenso en varias páginas

Distribuye contenido extenso en varias páginas con saltos de página automáticos. Añade un esquema para que el lector pueda saltar entre secciones. Esta recipe toma como referencia examples/12-bookmarks-and-toc.php.

Ventana de terminal
composer require nextpdf/core:^3

La restricción corresponde al paquete nextpdf/core. El ejemplo se ejecuta en PHP 8.4.

setAutoPageBreak(true, $margin) indica al motor que inicie una página nueva cada vez que el contenido vaya a sobrepasar el umbral del margen inferior. El motor fragmenta en ese límite el texto extenso escrito mediante multiCell() o writeHtml(). El módulo CSS Fragmentation (css_break_3) está calificado como Verified en la matriz de compatibilidad y respalda el comportamiento de los saltos para la pipeline HTML.

bookmark($title, $level) añade un elemento de esquema que apunta a la posición actual. Un elemento de esquema del PDF asocia un destino para que el usuario pueda saltar directamente a una página (ISO 32000-2). El motor escribe ese destino como la entrada Dest del elemento (ISO 32000-2). El argumento level anida los elementos en una tabla de contenidos jerárquica dentro de la barra lateral del lector.

La pipeline funciona en una sola pasada (ADR-001). La paginación se decide a medida que se emite el flujo, no mediante un árbol de maquetación retenido.

  • 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 tabla completa de PHPDoc se genera a partir del código fuente.

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

Este ejemplo es autónomo y puede ejecutarse con el harness. Construye un documento de varios capítulos con un esquema anidado y saltos de página automáticos, y reproduce 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 esperado:

Wrote paginate-long-html.pdf
  • Desactivarlo y olvidarlo. Cuando el salto de página automático está desactivado, el motor recorta el contenido que sobrepasa el margen inferior en lugar de distribuirlo. Volver a activarlo antes de emitir contenido extenso.
  • Contenido indivisible. Un único bloque más alto que la altura útil de la página puede provocar UnsplittableContentException. Entre las causas están una fila de tabla muy alta o una imagen grande. Dividir el contenido de origen.
  • Marcador antes del contenido. Llamar a bookmark() en la posición a la que debe apuntar el destino. Colocarlo justo antes del encabezado que se escribe a continuación, en la página deseada.
  • El encabezado y el pie reservan espacio. Un encabezado o un pie de impresión reduce la altura útil del contenido, y el umbral de salto lo tiene en cuenta. Al desactivar ambos, como hace el ejemplo, se obtiene la altura completa del cuerpo.
  • Anidamiento del esquema. level es la profundidad de anidamiento. Un hijo en level: 1 debe seguir a un padre en level: 0. De lo contrario, el lector aplana el árbol del esquema.

El motor decide la paginación durante la única pasada de emisión. El costo es lineal respecto de la longitud del contenido, O(n). El presupuesto es wall_ms: 2000, peak_mb: 96. El tiempo de pared es ligeramente mayor que en las recipes de una sola página, debido al ensamblaje del xref de varias páginas y del esquema. El modelo de streaming mantiene la memoria acotada, y el esquema es una lista plana pequeña.

Extracto de la matriz de compatibilidad de CSS (solo filas Verified)

Sección titulada «Extracto de la matriz de compatibilidad de CSS (solo filas Verified)»

Solo se reproducen las filas Verified de la matriz de compatibilidad de CSS auditada en cuanto a veracidad.

Módulo del W3CNivelEstadoEvidencia
CSS Fragmentation (css_break_3)3Verifiedsrc/Html/Fragmentation/, tests/Unit/Html/PagedMedia/
CSS Table (css_tables_3)3Verifiedsrc/Html/Table/ + PDF de referencia
CSS Cascading and Inheritance (css_cascade_3)3Verifiedsrc/Html/Cascade/

@page proporciona selectores de página con nombre que forman parte de CSS Paged Media. Consultar la matriz para conocer la calificación actual de ese módulo antes de depender de él.

Restricciones del streaming de una sola pasada (ADR-001)

Sección titulada «Restricciones del streaming de una sola pasada (ADR-001)»

El motor emite los saltos de página a medida que fluye el stream. No existe un árbol retenido para redistribuir, así que una decisión de salto es definitiva una vez tomada. Cierto contenido necesita su número de página definitivo después de la maquetación, por ejemplo una referencia cruzada. Ese contenido está restringido, así que conviene escribirlo teniendo presente esa limitación.

La paginación pertenece al controlador de saltos de página, no al analizador. El analizador no emite operadores de transición de página en crudo; solicita un salto mediante el contrato del controlador.

Presupuesto de memoria para documentos extensos

Sección titulada «Presupuesto de memoria para documentos extensos»

El modelo de streaming mantiene el búfer de la página actual más la lista plana del esquema, no todas las páginas a la vez. Un documento muy extenso se mantiene dentro del límite de ADR-020 porque el motor descarga las páginas terminadas. Las tablas y los contenedores flex siguen obedeciendo el límite de 5,000 nodos por contexto.

Un documento hostil no puede forzar un uso de memoria ilimitado. Los límites de elementos y de anidamiento (ADR-001) y el presupuesto de nodos por contexto (ADR-020) acotan el trabajo. Validar la longitud y la estructura del contenido extenso proporcionado por el usuario. El motor renderiza un título de esquema controlado por un atacante como texto, sin interpretarlo nunca.

AfirmaciónEspecificaciónCláusulareference_id
Cada elemento de esquema puede asociarse con un destino para que el usuario salte directamente a él.ISO 32000-2iso32000_2_sec12#x1.x5.p4
La entrada Dest de un elemento de esquema nombra el destino que se muestra cuando el elemento se activa.ISO 32000-2iso32000_2_sec12#x1.x11.p30

Esta recipe muestra cómo NextPDF distribuye contenido extenso y construye un esquema. CSS Fragmentation está calificado como Verified en la matriz de compatibilidad.

No aplica.