Ir al contenido

Maquetación: encabezados, pies de página, columnas, folleto y gestor de páginas

El módulo Layout reúne los motores de maquetación de página en los que delega la fachada Document: renderizado de encabezado y pie de página, diseño multicolumna, imposición de folletos a caballete y operaciones estructurales de página. Es un módulo pequeño y estable: seis clases, todas @since 1.0.0.

Ventana de terminal
composer require nextpdf/core:^3

Layout (src/Layout/, seis clases, @since 1.0.0) es la capa de motores situada bajo el concern HasLayout. El código de aplicación llama a los métodos de fachada en Document; el trait enruta cada llamada a uno de estos motores. El módulo está marcado en el manifiesto con riesgo standard y estabilidad internal, con Core como único dependiente. Se accede a él a través de la fachada, no mediante la construcción directa de las clases.

HeaderFooter renderiza el encabezado y el pie de página recurrentes. Conserva el estado de título, descripción, logotipo, fuente, margen y color de cada banda. Produce operadores de flujo de contenido PDF bajo demanda mediante renderHeader() y renderFooter(). El pie de página predeterminado imprime una cadena "page / total" alineada a la derecha. setHeaderCallback() y setFooterCallback() sustituyen la maquetación predeterminada por un closure suministrado por quien llama. getHeaderContentHeight() indica el espacio vertical que consume el encabezado para que el cuerpo de la página pueda empezar debajo de él. Cuando el documento está en modo PDF etiquetado, el encabezado automático se suprime en un nivel superior, en HasPages, porque el contenido del encabezado queda fuera del árbol de estructura.

ColumnLayout gestiona el flujo multicolumna. setEqualColumns(int $count, float $totalWidth, float $gap = 5) divide un ancho disponible en columnas iguales. setColumnsArray() acepta posiciones y anchos explícitos de ColumnDefinition. El cursor selecciona una columna con selectColumn() o avanza con nextColumn(). getCurrentColumnX() / getCurrentColumnWidth() devuelven la geometría de la columna activa. Un número de columnas inválido, una separación entre columnas negativa o un ancho de columna calculado que no sea positivo provocan PageLayoutException.

BookletLayout reordena las páginas para la encuadernación a caballete (pliego central). reorderPages() rellena la lista de páginas hasta un múltiplo de cuatro (un pliego de folleto contiene cuatro posiciones de página) y luego impone las páginas de fuera hacia dentro para que los pliegos doblados y grapados se lean en orden. getMarginAdjustments() devuelve, por cada lado, los márgenes interno (lomo) y externo (borde) de una posición dada. getSheetCount() indica cuántos pliegos a doble cara requiere un recuento de páginas. La reordenación solo cambia la ubicación del contenido. La secuencia de páginas PDF subyacente permanece lineal, en consonancia con el árbol de páginas, que define la ordenación de las páginas en el documento (ISO 32000-2 §7.7).

PageManager proporciona operaciones estructurales de página separadas del renderizado de contenido. movePage(), copyPage() y deletePage() operan sobre un arreglo de PageData por referencia. Las regiones de página (addPageRegion(), isInRegion(), getRegionOffset()) definen zonas en las que no se debe escribir. Los grupos de páginas (startPageGroup(), getGroupPageNo()) admiten una numeración de páginas por sección. PageRegion y ColumnDefinition son los dos contenedores de valores que usan los motores. El módulo Writer serializa las páginas resultantes en un árbol de páginas cuya entrada Kids es un arreglo de referencias indirectas a los hijos inmediatos de un nodo del árbol de páginas (ISO 32000-2 §7.7.3.2).

SímboloTipoEstabilidadDesde
HeaderFooter::setHeaderData(string, string, string, float): selfmétodoestable1.0.0
HeaderFooter::setHeaderFont(string, float): self / setHeaderMargin(float): selfmétodoestable1.0.0
HeaderFooter::setFooterFont(string, float): self / setFooterMargin(float): selfmétodoestable1.0.0
HeaderFooter::setHeaderCallback(Closure): self / setFooterCallback(Closure): selfmétodoestable1.0.0
HeaderFooter::getHeaderContentHeight(): floatmétodoestable1.0.0
HeaderFooter::renderHeader(float, float, float, float, int, int): stringmétodoestable1.0.0
HeaderFooter::renderFooter(float, float, float, float, int, int, string): stringmétodoestable1.0.0
ColumnLayout::setEqualColumns(int, float, float): selfmétodoestable1.0.0
ColumnLayout::setColumnsArray(array): self / resetColumns(): selfmétodoestable1.0.0
ColumnLayout::selectColumn(int): self / nextColumn(): boolmétodoestable1.0.0
ColumnLayout::getCurrentColumnX(float): float / getCurrentColumnWidth(float): floatmétodoestable1.0.0
BookletLayout::setBooklet(bool, float, float): voidmétodoestable1.0.0
BookletLayout::reorderPages(array): arraymétodoestable1.0.0
BookletLayout::getMarginAdjustments(int): array{left: float, right: float}métodoestable1.0.0
BookletLayout::getSheetCount(int): intmétodoestable1.0.0
PageManager::movePage(int, int, array): void / copyPage(int, array): void / deletePage(int, array): voidmétodoestable1.0.0
PageManager::addPageRegion(float, float, float, float): void / isInRegion(float, float): boolmétodoestable1.0.0
PageManager::getRegionOffset(float, float, float, float): floatmétodoestable1.0.0
PageManager::startPageGroup(): void / getGroupPageNo(int): intmétodoestable1.0.0
PageRegion / ColumnDefinitioncontenedor de valoresestable1.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');

Fuente: examples/13-header-footer.php. El encabezado se renderiza en cada addPage(); el pie de página se renderiza al vaciar la página.

Un callback de pie de página se encarga del texto del número de página, y el motor de columnas hace fluir el cuerpo en dos columnas. A ambos motores se accede a través de la fachada.

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

Fuente: patrón de examples/13-header-footer.php.

  • setEqualColumns() rechaza un número de columnas inferior a 1, una separación entre columnas negativa o una maquetación cuyo ancho de columna calculado no sea positivo. Cada uno de estos casos provoca PageLayoutException en lugar de devolver una maquetación degradada.
  • selectColumn() ignora un índice fuera de rango y mantiene la columna actual; nunca lanza una excepción por un índice incorrecto. nextColumn() devuelve false cuando ya se encuentra en la última columna.
  • BookletLayout::reorderPages() rellena hasta un múltiplo de cuatro con páginas en blanco clonadas a partir de las dimensiones de la última página. Una lista de páginas vacía devuelve un arreglo vacío. La reordenación afecta solo a la ubicación; los índices de movePage() siguen refiriéndose al orden lógico.
  • Ante un índice fuera de rango, PageManager::movePage(), copyPage() y deletePage() no hacen nada y no emiten error: validan con isset() y retornan sin modificar el arreglo. Conviene verificar el índice cuando una página ausente sea un error de quien llama.
  • getHeaderContentHeight() devuelve 0.0 cuando el encabezado está deshabilitado o no tiene ni título ni descripción. El cuerpo de la página empieza entonces en el margen superior.
  • En modo PDF etiquetado, el encabezado automático se suprime en un nivel superior. Se debe construir una maquetación consciente de la estructura para documentos accesibles.

El renderizado de encabezado y pie de página añade operadores al búfer de la página activa en O(contenido del mobiliario): el costo es proporcional al título, la descripción y el separador que se escriben, no al tamaño del documento. El cálculo de columnas es O(1) por llamada. BookletLayout::reorderPages() es O(n) en número de páginas, con una sola pasada de relleno; el bucle de imposición toca cada posición rellenada una vez. PageManager realiza pruebas de región en O(regiones) por punto; las operaciones de página son empalmes de arreglo O(n). Ninguno de estos motores retiene estado por página a lo largo del documento, así que no contribuyen al crecimiento de memoria en documentos largos. El costo de memoria dominante es el flujo de contenido acumulado, que cubre el concepto de streaming y memoria. La compuerta del presupuesto de latencia y memoria del pipeline HTML está documentada en PERFORMANCE-BUDGETS; su alcance es la ruta de renderizado HTML y no controla directamente estos motores de maquetación. El performance_budget de 1500 ms / 64 MB corresponde al envoltorio del canvas para el inicio rápido, no a un contrato por llamada.

Estos motores consumen cadenas y una ruta de logotipo que suministra quien llama. La ruta del logotipo fluye a través del motor de imágenes, que valida el archivo antes de incrustarlo. renderHeader() y renderFooter() escapan el título, la descripción y el texto del número de página mediante el escapador centralizado de cadenas PDF antes de que lleguen al flujo de contenido, de modo que el texto de quien llama no puede salirse de la gramática de cadenas literales. Un callback de encabezado o de pie de página ejecuta código de quien llama con la misma confianza que el resto del documento; por lo tanto, cualquier dato externo que lea debe tratarse en consecuencia. Las operaciones de página de PageManager mueven referencias a PageData existentes; no analizan bytes no confiables.

AfirmaciónEstándarCláusulaEvidencia
La reordenación de páginas para la salida en folleto solo cambia la ubicación; el árbol de páginas sigue definiendo la ordenación lineal de las páginas en el documento.ISO 32000-2§7.7
La entrada Kids del nodo del árbol de páginas serializado es un arreglo de referencias indirectas a los hijos inmediatos de ese nodo.ISO 32000-2§7.7.3.2

Las cláusulas están parafraseadas y vinculadas al glosario; no se reproduce ningún texto normativo.