Ir al contenido

Crear un documento de varias páginas con saltos de página automáticos

Permite crear un documento cuyo contenido se extienda por muchas páginas y agregar contenido de forma incremental. Con setAutoPageBreak() activado, el motor de maquetación inicia automáticamente una página nueva cuando el cursor llega al margen inferior. Después de save(), se lee el número final de páginas con getNumPages(). Esta receta sigue examples/05-multi-page.php.

Durante save(), el motor escribe las marcas de cada página en un flujo de contenido. ISO 32000-2 §7.7.3.3 define el Contents de una página como un único flujo o como un arreglo de flujos concatenados en orden. Por tanto, la salida de varias páginas es una secuencia de objetos de página, no un único búfer.

Ventana de terminal
composer require nextpdf/core:^3

No se requiere ninguna extensión opcional. Esta receta funciona con la matriz de backport de PHP 8.1-8.4. Tanto getNumPages() como setAutoPageBreak() son estables desde la versión 1.0.0.

Un documento de NextPDF es un árbol de páginas. A medida que se agrega contenido, avanza un cursor interno (getY()). Cuando los saltos de página automáticos están activados, el motor comprueba el espacio vertical restante antes de cada bloque de contenido. Si el bloque no cabe en el espacio disponible sobre el margen inferior, el motor vacía la página actual y llama automáticamente a addPage(). El margen inferior que se pasa a setAutoPageBreak() es el umbral que dispara el salto.

Los atributos de página, como el cuadro de medios, son heredables. ISO 32000-2 §7.7.3.4 especifica que un atributo omitido en un objeto de página se resuelve a partir de un nodo ascendiente del árbol de páginas. NextPDF establece un tamaño de página coherente en todo el documento, de modo que cada página generada comparte la misma geometría y no es necesario repetirlo página por página.

La superficie de la API se genera automáticamente a partir de PHPDoc. Esta receta se basa en estos métodos:

  • Document::createStandalone(): self — construye un documento aislado.
  • setAutoPageBreak(bool $enabled, float $margin = 20): static — activa los saltos de página automáticos. $margin es el umbral del margen inferior, en milímetros.
  • addPage(?PageSize $size = null, Orientation $orientation = Orientation::Portrait): static — inicia la primera página, y cualquier página explícita.
  • multiCell(...): static / cell(...): static — emite bloques de texto fluido o fijo. La comprobación del salto de página mide estos bloques.
  • getNumPages(): int — el número de páginas después de la maquetación.
<?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";

Este es el ejemplo completo, listo para el arnés de pruebas. Debe respetar NEXTPDF_COOKBOOK_OUTPUT, que establece el arnés, por lo que no se debe enviar el PDF a STDOUT. No establece entropía propia. Cuando el arnés lo ejecuta, DeterministicMode fija el reloj, el /ID y la marca.

<?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";
  • Salto de página automático desactivado. Con setAutoPageBreak(false, …), el contenido que sobrepasa el margen inferior se recorta a la página en vez de fluir, y el documento conserva una sola página. Debe activarse para el contenido fluido.
  • Un único bloque más alto que la página. El motor divide internamente un multiCell cuyo texto supera la altura imprimible. Sin embargo, un único bloque indivisible más alto que el área utilizable, por ejemplo una imagen alta, se coloca una sola vez y se desborda. Debe dividirse manualmente.
  • El primer addPage() sigue siendo obligatorio. cell() llama a addPage() bajo demanda cuando no existe ninguna página. Aun así, se debe llamar a addPage() de forma explícita para que el tamaño y la orientación de la primera página sean deterministas.
  • Unidades del margen. El margen de setAutoPageBreak() se expresa en milímetros en el sistema de unidades predeterminado, no en puntos.

getNumPages() es O(1). Lee un contador y no vuelve a maquetar. La memoria escala con el contenido retenido, no con el número de páginas. El motor vacía las páginas terminadas al búfer de salida a medida que se completan: el modelo de streaming de una sola pasada (ADR-001). El presupuesto de 2000 ms / 64 MB cubre documentos de unos cientos de páginas de texto en el host de referencia.

Esta receta escribe únicamente el texto que proporciona el código. No realiza análisis de entrada, acceso a la red ni deserialización. Todo texto de origen externo debe tratarse como no confiable y acotarse en longitud antes de renderizarlo. El motor no impone automáticamente un límite de tamaño de contenido a nivel de aplicación.

DeclaraciónEspecificaciónCláusulareference_id
El Contents de una página es un único flujo o un arreglo ordenado y concatenado de flujos.ISO 32000-2§7.7.3.3
Un atributo de página heredable que se omite en un objeto de página se resuelve a partir de un nodo ascendiente del árbol de páginas.ISO 32000-2§7.7.3.4
El /ID del tráiler es un identificador de archivo de dos cadenas de bytes (obligatorio en PDF 2.0).ISO 32000-2§7.5.5

Perfil de reproducibilidad — estructural (por qué no a nivel de bits). Cada documento guardado lleva un /ID en el tráiler cuyas dos cadenas de bytes son un identificador de archivo (ISO 32000-2 §7.5.5, arriba). El segundo elemento no es estable entre ejecuciones, así que los bytes en bruto difieren entre ejecuciones incluso con contenido idéntico. El arnés compara la estructura normalizada por qpdf, que elimina /ID, /CreationDate y /ModDate. Esta recipe describe cómo NextPDF produce la estructura. No afirma la conformidad con ISO 32000-2 como una declaración general.

No aplica. La composición de varias páginas con saltos de página automáticos es una capacidad del nivel Core, sin restricción de Premium.