Metadatos: construcción de paquetes XMP y lectura por flujo
De un vistazo
Sección titulada «De un vistazo»El módulo Metadata es la capa XMP del motor. Construye el paquete XMP que un PDF transporta como flujo de metadatos. Lee un paquete existente sin cargar todo el documento en memoria. Emite la extensión XMP para el rastro de auditoría del motor.
Instalación
Sección titulada «Instalación»composer require nextpdf/core:^3Visión conceptual
Sección titulada «Visión conceptual»Un PDF transporta los metadatos de nivel de documento como un paquete XMP en un flujo de metadatos adjunto al catálogo del documento: ISO 32000-2 §14.3. Este módulo se encarga de producir y consumir ese paquete. Su superficie es deliberadamente pequeña y específica: tres clases bajo NextPDF\Metadata\Xmp.
XmpMetadataBuilder produce el paquete. Serializa un conjunto de propiedades en un documento XMP bien formado, envuelto en las instrucciones de procesamiento <?xpacket?> estándar. Utiliza el GUID de paquete canónico y la marca de orden de bytes que fija la especificación XMP. La salida es la cadena de bytes que el Writer incrusta como el flujo de metadatos: la representación XMP en el PDF descrita en §14.3.
XmpStreamReader consume un paquete. Está diseñado para entradas hostiles. El origen se procesa por flujo en fragmentos de 64 KB hacia un archivo temporal acotado antes del análisis. Durante esa escritura se aplica un tope agregado de bytes. El cargador de entidades de libxml se anula durante el análisis y se restaura después. Un DOCTYPE se rechaza de forma tajante. Expone iterateProperties(), un generador que produce tuplas (namespaceUri, localName, textContent) por cada elemento hoja sin materializar el árbol completo: en cada momento, solo el elemento actual y su nodo de texto permanecen vivos en el analizador. Un paquete sobredimensionado lanza PacketTooLargeException; el XML malformado, un DOCTYPE o una entrada no UTF-8 lanzan InvalidConfigException.
XmpAuditFieldEmitter es la extensión específica del motor. Representa un AuditReport en un campo XMP personalizado dentro del espacio de nombres nextpdfAudit, de modo que la auditoría de conformidad de un documento viaja con el archivo como XMP conforme a los estándares, y no como un archivo paralelo. El propio emisor no produce el AuditReport que representa: el enriquecimiento se activa cuando una representación se ejecuta bajo CssRenderingMode::Audit, con un auditCollector proporcionado por el llamador y configurado mediante Config(auditCollector: ...). El llamador dirige el recopilador: lo alimenta y el emisor representa lo que haya recopilado. Es más reciente que la superficie XMP del núcleo (@since 5.4.0). El constructor y el lector son @since 2.0.0.
Superficie de la API
Sección titulada «Superficie de la API»| Clase | Miembros clave | Función |
|---|---|---|
XmpMetadataBuilder | build(): string, XPACKET_GUID, XPACKET_BOM | Serializa un conjunto de propiedades como un paquete XMP (@since 2.0.0) |
XmpStreamReader | iterateProperties(mixed $source, int $byteCap = DEFAULT_BYTE_CAP): \Generator, DEFAULT_BYTE_CAP | Lector XMP acotado, por flujo y que rechaza DOCTYPE (@since 2.0.0) |
PacketTooLargeException | extiende NextPdfException | Se lanza cuando un paquete XMP supera el tope de bytes (@since 2.0.0) |
XmpAuditFieldEmitter | render(?AuditReport $report): string, NAMESPACE_URI | Representa el rastro de auditoría como un campo XMP personalizado (@since 5.4.0) |
Ejecutar composer docs:generate-api-php -- --module=Metadata para obtener la tabla PHPDoc completa.
Ejemplo de código — Inicio rápido
Sección titulada «Ejemplo de código — Inicio rápido»Leer por flujo las propiedades de un paquete XMP existente con un tope de bytes explícito.
<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Metadata\Xmp\XmpStreamReader;
$reader = new XmpStreamReader();
foreach ($reader->iterateProperties(file_get_contents('/srv/in/xmp.xml'), byteCap: 1_048_576) as [$ns, $name, $value]) { printf("%s:%s = %s\n", $ns, $name, $value);}Ejemplo de código — Producción
Sección titulada «Ejemplo de código — Producción»Leer un paquete de forma defensiva y asignar los fallos tipados del módulo a un resultado de nivel de aplicación, en lugar de dejar que escapen errores brutos del analizador.
<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Exception\InvalidConfigException;use NextPDF\Metadata\Xmp\PacketTooLargeException;use NextPDF\Metadata\Xmp\XmpStreamReader;use Psr\Log\LoggerInterface;
final readonly class XmpIngestService{ public function __construct( private XmpStreamReader $reader, private LoggerInterface $logger, ) {}
/** * @param resource|string $source A stream resource or XMP byte string. * * @return array<string, string> Flattened "ns:name" => value map. */ public function ingest(mixed $source): array { $properties = [];
try { // Cap untrusted XMP at 4 MB regardless of the 1 GiB default. foreach ($this->reader->iterateProperties($source, byteCap: 4_194_304) as [$ns, $name, $value]) { $properties["{$ns}:{$name}"] = $value; } } catch (PacketTooLargeException $e) { $this->logger->warning('XMP packet exceeded ingest cap; rejected.', ['error' => $e->getMessage()]);
return []; } catch (InvalidConfigException $e) { $this->logger->warning('XMP packet malformed or unsafe; rejected.', ['error' => $e->getMessage()]);
return []; }
return $properties; }}Casos límite y trampas
Sección titulada «Casos límite y trampas»XmpStreamReaderrechaza de plano cualquier DOCTYPE. Es una defensa contra XXE, no un detalle de validación: un paquete que necesita un DOCTYPE no se acepta; debe sanearse aguas arriba.- El tope de bytes es 1 GiB de forma predeterminada (
DEFAULT_BYTE_CAP). Ese valor predeterminado es un límite máximo, no una recomendación. Pasar unbyteCapajustado para entradas no confiables. iterateProperties()es un generador. Consumirlo una sola vez; iterarlo dos veces no vuelve a reproducirlo.- El lector anula el cargador de entidades de libxml durante el análisis y lo restaura. No debe ejecutarse de forma concurrente con otros análisis basados en libxml en la misma solicitud que dependan del cargador de entidades.
XmpAuditFieldEmitter::render(null)es válido y produce una representación vacía; unAuditReportnulo significa «sin auditoría», no un error.
Rendimiento
Sección titulada «Rendimiento»El constructor escala linealmente con el número de propiedades. La memoria del lector está dominada por la secuencia de texto individual más larga, no por el tamaño del documento, porque solo el elemento actual permanece vivo en el analizador: los paquetes grandes se transmiten por flujo en lugar de cargarse. La carga de trabajo de referencia predeterminada se mantiene dentro de un presupuesto de 1500 ms de tiempo de pared / 64 MB de pico. El perfil de reproducibilidad es structural: un paquete XMP registra marcas de tiempo de modificación. Dos construcciones de los mismos metadatos lógicos difieren en esos campos, aunque la estructura sea idéntica.
Notas de seguridad
Sección titulada «Notas de seguridad»XmpStreamReader es un analizador para XML no confiable y está endurecido en consecuencia. La fragmentación por flujo con un tope de bytes aplicado acota una denegación de servicio por amplificación de memoria. Un DOCTYPE se rechaza para bloquear XXE. LIBXML_NONET bloquea la resolución de entidades por red. Se rechaza la entrada no UTF-8. Aun así, debe establecerse un byteCap apropiado para el despliegue en cualquier paquete de origen externo, en lugar de confiar en el valor predeterminado de un gigabyte. Los valores de propiedades XMP deben tratarse como cadenas no confiables cuando vuelvan a entrar en la aplicación. Consultar el modelo de amenazas del motor en /modules/core/security/.
Conformidad
Sección titulada «Conformidad»El paquete que produce XmpMetadataBuilder corresponde a la representación del flujo de metadatos XMP en el PDF definida en ISO 32000-2 §14.3 (). La forma de serialización XMP en sí se rige por la especificación XMP (ISO 16684-1), que no forma parte del corpus de citas verificable. Ese requisito se referencia por número, pero no se ancla a un fragmento. Se trata de hechos de implementación producidos por src/Metadata/Xmp/ y verificados por tests/Unit/Metadata/Xmp/. La conformidad de metadatos de extremo a extremo para un perfil (PDF/A, PDF/UA) la validan el oráculo y las suites de referencia descritos en /modules/core/conformance/.
Véase también
Sección titulada «Véase también»- Módulo Document — el árbol DPart con el que se empareja DPM.
- Módulo Audit — produce el
AuditReportque el emisor representa. - Módulo Writer — incrusta el paquete como un flujo de metadatos.
- Visión general de conformidad
- Modelo de seguridad del motor