Metadata: generazione del pacchetto XMP e lettura in streaming
In breve
Sezione intitolata “In breve”Il modulo Metadata è il livello XMP del motore. Costruisce il pacchetto XMP che un PDF trasporta come flusso di metadati. Legge un pacchetto esistente senza caricare in memoria l’intero documento. Emette l’estensione XMP per la traccia di audit del motore.
Installazione
Sezione intitolata “Installazione”composer require nextpdf/core:^3Panoramica concettuale
Sezione intitolata “Panoramica concettuale”Un PDF trasporta i metadati a livello di documento come pacchetto XMP in un flusso di metadati associato al catalogo del documento — ISO 32000-2 §14.3. Questo modulo è responsabile della produzione e del consumo di quel pacchetto. La superficie è volutamente piccola e mirata: tre classi sotto NextPDF\Metadata\Xmp.
XmpMetadataBuilder produce il pacchetto. Serializza un insieme di proprietà in un documento XMP ben formato, racchiuso nelle istruzioni di elaborazione standard <?xpacket?>. Utilizza il GUID canonico del pacchetto e il byte-order mark stabiliti dalla specifica XMP. L’output è la stringa di byte che il Writer incorpora come flusso di metadati, cioè la rappresentazione XMP all’interno del PDF descritta dal §14.3.
XmpStreamReader consuma un pacchetto. È progettato per input ostili. Prima dell’analisi, la sorgente viene trasmessa in streaming in blocchi da 64 KB verso un file temporaneo con limiti definiti. Durante questa scrittura viene applicato un limite aggregato di byte. Il caricatore di entità di libxml viene azzerato per la durata dell’analisi e poi ripristinato. Un DOCTYPE provoca un rifiuto netto. Espone iterateProperties(), un generatore che produce tuple (namespaceUri, localName, textContent) per ciascun elemento foglia senza materializzare l’intero albero: in qualsiasi momento, nel parser sono attivi solo l’elemento corrente e il relativo nodo di testo. Un pacchetto sovradimensionato solleva PacketTooLargeException; in caso di XML malformato, DOCTYPE o input non UTF-8 viene sollevata InvalidConfigException.
XmpAuditFieldEmitter è l’estensione specifica del motore. Esegue il rendering di un AuditReport in un campo XMP personalizzato sotto lo spazio dei nomi nextpdfAudit, così che l’audit di conformità di un documento viaggi con il file come XMP conforme agli standard anziché come side-car. L’AuditReport sottoposto a rendering non è prodotto dall’emettitore stesso: l’arricchimento si attiva quando un rendering viene eseguito con CssRenderingMode::Audit, con un auditCollector fornito dal chiamante e configurato tramite Config(auditCollector: ...). Il collector è guidato dal chiamante: il chiamante lo alimenta e l’emettitore esegue il rendering di tutto ciò che è stato raccolto. È più recente della superficie XMP di base (@since 5.4.0). Il costruttore e il lettore sono @since 2.0.0.
Superficie API
Sezione intitolata “Superficie API”| Classe | Membri principali | Ruolo |
|---|---|---|
XmpMetadataBuilder | build(): string, XPACKET_GUID, XPACKET_BOM | Serializza un insieme di proprietà in un pacchetto XMP (@since 2.0.0) |
XmpStreamReader | iterateProperties(mixed $source, int $byteCap = DEFAULT_BYTE_CAP): \Generator, DEFAULT_BYTE_CAP | Lettore XMP in streaming, con limiti definiti e rifiuto del DOCTYPE (@since 2.0.0) |
PacketTooLargeException | estende NextPdfException | Sollevata quando un pacchetto XMP supera il limite di byte (@since 2.0.0) |
XmpAuditFieldEmitter | render(?AuditReport $report): string, NAMESPACE_URI | Esegue il rendering della traccia di audit come campo XMP personalizzato (@since 5.4.0) |
Eseguire composer docs:generate-api-php -- --module=Metadata per ottenere la tabella PHPDoc completa.
Esempio di codice — Avvio rapido
Sezione intitolata “Esempio di codice — Avvio rapido”Trasmettere in streaming le proprietà di un pacchetto XMP esistente con un limite di byte esplicito.
<?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);}Esempio di codice — Produzione
Sezione intitolata “Esempio di codice — Produzione”Leggere un pacchetto in modo difensivo, mappando gli errori tipizzati del modulo su un esito a livello applicativo invece di lasciare emergere i guasti grezzi del parser.
<?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; }}Casi limite e insidie
Sezione intitolata “Casi limite e insidie”XmpStreamReaderrifiuta categoricamente qualsiasi DOCTYPE. È una difesa contro XXE, non una finezza di convalida: un pacchetto che richiede un DOCTYPE non viene accettato; sanificare a monte.- Il limite di byte è predefinito a 1 GiB (
DEFAULT_BYTE_CAP). Tale valore predefinito è un tetto massimo, non una raccomandazione. Passare unbyteCapstringente per gli input non attendibili. iterateProperties()è un generatore. Consumarlo una sola volta; iterarlo due volte non ne ripete l’esecuzione.- Il lettore azzera il caricatore di entità di libxml durante l’analisi e poi lo ripristina. Non eseguirlo contemporaneamente ad altre analisi basate su libxml che, nella stessa richiesta, dipendono dal caricatore di entità.
XmpAuditFieldEmitter::render(null)è valido e produce un rendering vuoto; unAuditReportnull significa «nessun audit», non un errore.
Prestazioni
Sezione intitolata “Prestazioni”Il costruttore è lineare rispetto al numero di proprietà. La memoria del lettore è dominata dalla singola sequenza di testo più lunga, non dalla dimensione del documento, poiché nel parser è attivo solo l’elemento corrente: i pacchetti di grandi dimensioni vengono trasmessi in streaming anziché caricati. Il carico di lavoro di riferimento predefinito rientra in un budget di 1500 ms di tempo reale / 64 MB di picco. Il profilo di riproducibilità è structural: un pacchetto XMP registra le marche temporali di modifica. Due generazioni degli stessi metadati logici differiscono in quei campi pur mantenendo una struttura identica.
Note sulla sicurezza
Sezione intitolata “Note sulla sicurezza”XmpStreamReader è un parser per XML non attendibile ed è irrobustito di conseguenza. La suddivisione in blocchi in streaming con un limite di byte applicato circoscrive un attacco di denial of service per amplificazione della memoria. Un DOCTYPE viene rifiutato per chiudere XXE. LIBXML_NONET blocca la risoluzione delle entità via rete. L’input non UTF-8 viene rifiutato. Impostare comunque un byteCap appropriato al deployment per qualsiasi pacchetto di origine esterna, invece di affidarsi al valore predefinito di un gigabyte. Trattare i valori delle proprietà XMP come stringhe non attendibili quando rientrano nell’applicazione. Consultare il modello di minaccia del motore in /modules/core/security/.
Conformità
Sezione intitolata “Conformità”Il pacchetto prodotto da XmpMetadataBuilder è la rappresentazione del flusso di metadati XMP all’interno del PDF definita in ISO 32000-2 §14.3 (). La forma di serializzazione XMP in sé è regolata dalla specifica XMP (ISO 16684-1), che non rientra nel corpus di citazioni verificabili. Tale requisito è citato per numero, non ancorato a uno specifico chunk. Sono fatti di implementazione prodotti da src/Metadata/Xmp/ e verificati da tests/Unit/Metadata/Xmp/. La conformità end-to-end dei metadati per un profilo (PDF/A, PDF/UA) è convalidata dall’oracle e dalle suite golden descritte in /modules/core/conformance/.
Vedere anche
Sezione intitolata “Vedere anche”- Modulo Document — l’albero DPart a cui DPM si associa.
- Modulo Audit — produce l’
AuditReportdi cui l’emettitore esegue il rendering. - Modulo Writer — incorpora il pacchetto come flusso di metadati.
- Panoramica sulla conformità
- Modello di sicurezza del motore