Ir al contenido

Motores de layout personalizados e interceptación de texto durante el layout

NextPDF no expone una interfaz conectable para motores de layout. El contrato público de extensión del layout es TextPreprocessorInterface, que intercepta texto durante el layout. También están disponibles los eventos de ciclo de vida del contenido, que permiten observar lo que produce el layout.

Ventana de terminal
composer require nextpdf/core:^3

La canalización de layout es interna. Abarca el layout de glifos, la creación de subconjuntos de fuentes, la salida del CMap ToUnicode y el árbol de estructura, y NextPDF no permite reemplazarla. La estabilidad de la salida en bytes y la conformidad con PDF etiquetado dependen de una sola compilación controlada.

Lo que NextPDF expone es el punto previo al layout: TextPreprocessorInterface. Una implementación recibe texto en bruto y devuelve un resultado segmentado antes de que ese texto entre en el layout de glifos, la creación de subconjuntos de fuentes, el CMap ToUnicode o el árbol de estructura. Esta es la forma admitida de cambiar el contenido de texto sin tocar el motor de layout.

El contrato incluye una regla estricta en su PHPDoc de origen: una implementación no debe cambiar el funcionamiento del layout. No debe añadir caracteres que afecten al layout, como el salto de línea, el retorno de carro o el tabulador, y debe mantener el orden lógico de lectura. El preprocesador declara un intercambio de contenido; no toma decisiones de layout. Respetar esta regla es necesario para no romper la salida estable ni la accesibilidad.

Para observar el resultado del layout —no para cambiarlo— se deben usar los eventos de ciclo de vida del contenido en Disparadores de acciones y escuchadores de eventos. ContentRenderedEvent se dispara después de dibujar el contenido en una página. FontLoadedEvent se dispara una vez por familia y estilo de fuente.

NextPDF\Contracts\TextPreprocessorInterface (estable, desde la 1.9.0):

MétodoDevuelvePropósito
process(string $text)TextPreprocessResultTransforma el texto en bruto antes de la canalización de renderizado; devuelve un resultado segmentado con metadatos de redacción.

El NextPDF\Contracts\TextPreprocessResult devuelto es un objeto de valor congelado. La firma de su constructor y sus propiedades públicas son estables, y no cambian en una versión menor ni de parche. Se pueden añadir nuevos métodos.

Este pequeño preprocesador enmascara un token fijo. No añade caracteres que afecten al layout y mantiene el orden de lectura.

<?php
declare(strict_types=1);
use NextPDF\Contracts\TextPreprocessorInterface;
use NextPDF\Contracts\TextPreprocessResult;
use NextPDF\Contracts\TextSegment;
final class TokenMaskingPreprocessor implements TextPreprocessorInterface
{
public function process(string $text): TextPreprocessResult
{
$masked = \str_replace('SECRET-TOKEN', '••••••••••••', $text);
return new TextPreprocessResult([
new TextSegment($masked, redacted: $masked !== $text),
]);
}
}

Un preprocesador de producción mantiene las reglas de coincidencia en un solo lugar. Falla de forma segura ante un patrón incorrecto y nunca registra el texto original.

<?php
declare(strict_types=1);
use NextPDF\Contracts\TextPreprocessorInterface;
use NextPDF\Contracts\TextPreprocessResult;
use NextPDF\Contracts\TextSegment;
use Psr\Log\LoggerInterface;
final class PatternRedactionPreprocessor implements TextPreprocessorInterface
{
/**
* @param non-empty-string $pattern A valid PCRE pattern for sensitive spans
*/
public function __construct(
private readonly string $pattern,
private readonly LoggerInterface $logger,
) {}
public function process(string $text): TextPreprocessResult
{
$result = \preg_replace($this->pattern, '[REDACTED]', $text);
if ($result === null) {
// Fail closed: never emit unredacted text on a pattern error.
$this->logger->error('Redaction pattern failed; substituting empty text');
return new TextPreprocessResult([new TextSegment('', redacted: true)]);
}
return new TextPreprocessResult([
new TextSegment($result, redacted: $result !== $text),
]);
}
}
  • Sin reemplazo del layout. No hay contrato para reemplazar el layout de cajas, el salto de líneas ni la paginación. La petición de conectar un motor de layout de terceros queda fuera del alcance por diseño.
  • Cumplimiento de la regla. Añadir \n, \r o \t en process() corrompe el layout y rompe la salida estable. El motor confía en la regla; no vuelve a comprobar tu salida en busca de caracteres que afecten al layout.
  • Orden de lectura. Reordenar los segmentos rompe el orden de lectura del PDF etiquetado y la conformidad con PDF/UA.
  • Una sola responsabilidad. El preprocesador declara un intercambio de contenido. Usar los eventos de ciclo de vida para observar, y no introducir efectos secundarios a través de process().

process() se ejecuta una vez por cada serie de texto en la ruta crítica del layout. Debe mantenerse ligero en memoria. Compilar los patrones una sola vez en el constructor, no en cada llamada. Los eventos de ciclo de vida del contenido no tienen coste cuando no hay ningún escuchador asociado.

TextPreprocessorInterface es el lugar admitido para eliminar contenido sensible antes de que llegue al flujo de contenido, los subconjuntos de fuentes o los metadatos. Como se ejecuta antes de la creación de subconjuntos y del CMap ToUnicode, los glifos redactados nunca entran en el archivo. Un fallo del preprocesador debe tratarse como falla-segura, y debe usarse texto vacío o enmascarado en lugar de emitir el original.

Ninguna afirmación normativa de firma ni de archivado se aplica a esta página. La regla de orden de lectura alinea el contrato con las necesidades del PDF etiquetado. La conformidad en el nivel de etiquetas se cubre en la referencia de accesibilidad.

NextPDF Pro ofrece estrategias de preprocesamiento de texto para producción, incluida la redacción de PII ajustada a los tipos de documento más comunes. En Core se implementa TextPreprocessorInterface directamente, o se usa una compilación verificada de una edición de pago a través del mismo contrato público.

El glosario define text preprocessor y extension point; consultar el glosario publicado para cada definición canónica.