Writer: serializzatore PDF 2.0 + xref
In breve
Sezione intitolata “In breve”Il modulo Writer serializza un documento come byte PDF. Seleziona la strategia di versione, scrive il grafo degli oggetti ed emette la struttura di cross-reference e il trailer.
Installazione
Sezione intitolata “Installazione”composer require nextpdf/core:^3Panoramica concettuale
Sezione intitolata “Panoramica concettuale”PdfWriter è il punto di ingresso. Il metodo write() accetta un value object DocumentData e restituisce il PDF completo come stringa di byte. Il writer assembla il grafo degli oggetti, assegna i numeri di oggetto, registra gli offset di byte e scrive infine la struttura di cross-reference.
Il writer usa una sola strategia di serializzazione per chiamata. L’interfaccia PdfSerializationStrategy definisce quattro metodi: writeHeader(), getCatalogVersion(), writeXrefAndTrailer() e usesXrefStream(). Tre strategie la implementano. Pdf20StreamStrategy scrive l’header %PDF-2.0, imposta la versione del catalogo a /2.0 ed emette uno stream di cross-reference. Pdf17TableStrategy scrive %PDF-1.7 e una table di cross-reference classica. Pdf14TableStrategy scrive %PDF-1.4 e una table di cross-reference. PdfWriter seleziona la strategia con un match su DocumentData::$outputProfile. La strategia predefinita è Pdf20StreamStrategy.
L’enum PdfOutputProfile contiene le tre versioni di destinazione: Pdf20, Pdf17 e Pdf14. L’enum espone headerVersion(), catalogVersion(), allowsObjectStreams() e usesXrefStream(). Una modalità di conformità archivistica sovrascrive il profilo scelto prima della selezione della strategia. Pdf14FeatureGuard rifiuta le funzionalità PDF 2.0 quando il profilo è Pdf14.
Uno stream di cross-reference associa ogni numero di oggetto al relativo offset di byte — ISO 32000-2 §7. Gli aggiornamenti incrementali accodano nuovi oggetti alla fine del file — ISO 32000-2 §7.5.6. Il writer applica l’escape a ogni stringa letterale tramite il punto di estensione canonico PdfStringEscaper::escapeLiteral(), che segue la tabella di escape normativa in ISO 32000-2 §7.3.4.2 (ADR-015).
Il writer supporta l’output deterministico. setDeterministicMode() fissa gli identificatori di oggetto e l’ordine delle chiavi del dizionario. setReproducibleClock() fissa il timestamp del documento. Con entrambe le impostazioni fissate, lo stesso input produce un output identico a livello di byte. Il metodo writeChunked() restituisce un generatore che produce il PDF in chunk a dimensione fissa. Streaming/StreamingPdfWriter scrive una pagina alla volta sullo stream fornito dal chiamante, per i documenti che superano il budget di memoria.
Linearizer riscrive un PDF completato in un layout linearizzato. Colloca la prima pagina all’inizio, così un visualizzatore può mostrarla prima che il download completo sia terminato. shadowValidate() verifica la riscrittura senza modificare l’input.
Attenzione.
PdfWriter.phpeLinearizer.phpsono critici per gli offset di byte e per il grafo degli oggetti (zone di pericolo del manifest). Non modificare la numerazione degli oggetti né l’aritmetica degli offset xref senza la golden suite del Writer.
Superficie API
Sezione intitolata “Superficie API”| Classe | Metodi principali | Ruolo |
|---|---|---|
PdfWriter | write(DocumentData): string, writeChunked(DocumentData, int): Generator, setDeterministicMode(), setReproducibleClock(), setOutputColorProfile(), getLastXrefOffset(), getFileId() | Serializzatore primario |
PdfSerializationStrategy (interfaccia) | writeHeader(), getCatalogVersion(), writeXrefAndTrailer(), usesXrefStream() | Contratto della strategia di versione |
Pdf20StreamStrategy | writeHeader() → %PDF-2.0, getCatalogVersion() → /2.0, usesXrefStream() → true | Strategia xref-stream per PDF 2.0 |
Pdf17TableStrategy | writeHeader() → %PDF-1.7, table xref | Strategia xref-table per PDF 1.7 |
Pdf14TableStrategy | writeHeader() → %PDF-1.4, table xref | Strategia xref-table per PDF 1.4 |
PdfOutputProfile (enum) | Pdf20, Pdf17, Pdf14; headerVersion(), catalogVersion(), allowsObjectStreams() | Selettore della versione di destinazione |
PdfXrefWriter | generateFileId(), finalizeTrailerAndXref() | File ID + finalizzazione di trailer/xref |
Linearizer | linearize(string): string, shadowValidate(string): array | Riscrittura per Fast Web View |
Streaming\StreamingPdfWriter | open(), newPage(), close() | Writer in streaming a passaggio singolo |
Eseguire composer docs:generate-api-php -- --module=Writer per ottenere la tabella PHPDoc completa.
Esempio di codice — Avvio rapido
Sezione intitolata “Esempio di codice — Avvio rapido”Sorgente: examples/02-pdf-factory.php.
<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Writer\PdfWriter;
$writer = new PdfWriter();$pdfBytes = $writer->write($documentData);
file_put_contents('out.pdf', $pdfBytes);Il profilo predefinito è PDF 2.0. L’output inizia con %PDF-2.0 e termina con uno stream di cross-reference.
Esempio di codice — Produzione
Sezione intitolata “Esempio di codice — Produzione”Questo esempio fissa il determinismo e un clock costante per ottenere un output identico a livello di byte, quindi esegue lo streaming in chunk a dimensione fissa.
<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use DateTimeImmutable;use NextPDF\Writer\PdfWriter;use NextPDF\Writer\ReproducibleClock;
$pinned = new DateTimeImmutable('2026-01-01T00:00:00Z');
$writer = new PdfWriter();$writer->setDeterministicMode($pinned, 'nextpdf-fixed-file-id');$writer->setReproducibleClock(new ReproducibleClock($pinned));
$out = fopen('php://output', 'wb');foreach ($writer->writeChunked($documentData, chunkSize: 65536) as $chunk) { fwrite($out, $chunk);}fclose($out);Casi limite e insidie
Sezione intitolata “Casi limite e insidie”- Per ogni chiamata a
write()viene usata una sola strategia. A ogni chiamata il writer reimposta la strategia in base al profilo. Una chiamata precedente non lascia in eredità la propria versione. - Una modalità di conformità archivistica sovrascrive il profilo richiesto. Una build PDF/A-3 forza PDF 1.7. Una build PDF/A-4 forza PDF 2.0.
- Un output identico a livello di byte richiede entrambi i vincoli. Impostare la modalità deterministica e un clock riproducibile. Un solo vincolo da solo non è sufficiente.
writeChunked()produce un generatore. Consumarlo per intero. Una lettura parziale produce un PDF troncato e non valido.Linearizerriscrive gli offset di cross-reference. Eseguire primashadowValidate()in una pipeline che non può tollerare una riscrittura non riuscita.Pdf14TableStrategyèfinal readonly. Il percorso PDF 1.4 rifiuta le funzionalità PDF 2.0 tramitePdf14FeatureGuardanziché degradarle.
Prestazioni
Sezione intitolata “Prestazioni”La serializzazione è lineare rispetto al numero di oggetti e alla dimensione totale in byte. Lo stream di cross-reference aggiunge un passaggio sulla tabella degli oggetti. writeChunked() mantiene il documento assemblato ma lo produce in porzioni delimitate, quindi il picco di memoria è la dimensione del documento più un chunk. Streaming\StreamingPdfWriter non mantiene l’intero documento: è il percorso adatto per input più grandi del budget di memoria. Per il carico di lavoro di riferimento, il budget è di 1500 ms di tempo reale e 64 MB di picco. La linearizzazione aggiunge un secondo passaggio completo e un passaggio di misurazione. Prevederne esplicitamente il budget.
Note sulla sicurezza
Sezione intitolata “Note sulla sicurezza”Il writer serializza in memoria un grafo di oggetti attendibile. Le minacce principali riguardano i suoi input. Ogni stringa letterale passa attraverso PdfStringEscaper::escapeLiteral() (ADR-015), il punto canonico di escape, quindi i byte di controllo incorporati non possono uscire da un token di stringa. La cifratura è collegata tramite PdfEncryptionWriter e la voce di trailer /Encrypt. La cifratura a chiave pubblica viene rifiutata con un’eccezione esplicita anziché degradata in modo silenzioso. Le modalità deterministica e a clock riproducibile rimuovono dall’output i canali laterali basati su timestamp e ordinamento. Vedere /modules/core/security/ per il modello di minaccia del documento e il confine di attendibilità della cifratura.
Conformità
Sezione intitolata “Conformità”Il Writer produce strutture di file PDF 2.0: l’header %PDF-2.0, una versione del catalogo /2.0, uno stream di cross-reference e l’escape delle stringhe letterali secondo la tabella di escape ISO 32000-2 §7.3.4.2. Si tratta di fatti relativi all’implementazione. L’evidenza proviene da src/Writer/Pdf20StreamStrategy.php, src/Writer/PdfSerializationStrategy.php e dalla selezione della strategia in src/Writer/PdfWriter.php, verificata da tests/Unit/Writer/ (192 test, incluse le suite Pdf20StreamStrategy, PdfXrefWriter e Linearizer*) e dalla baseline tests/Golden/PdfWriter/PdfWriterGoldenBaselineSmokeTest.
Questa non è un’affermazione di piena conformità a PDF 2.0. La piena conformità a ISO 32000-2 è una proprietà di un documento completo convalidato da un oracolo esterno, non del solo serializzatore. La conformità end-to-end viene asserita solo dove un oracolo la conferma: tests/Integration/Accessibility/VeraPdfUa2GoldenTest convalida una fixture generata con veraPDF per PDF/UA-2 e tests/Standards/Profile/PdfRConformanceTest copre il profilo PDF/R. Il golden test veraPDF viene saltato quando il binario veraPDF è assente dal runner, quindi è un gate basato su oracolo e opt-in, non incondizionato. Impostare VERAPDF_BINARY per eseguirlo. La selezione del profilo archivistico (PDF/A-3 → PDF 1.7, PDF/A-4 → PDF 2.0) è determinata da ADR-011 e dalla modalità di conformità, ed è convalidata dalle suite di conformità in /modules/core/conformance/. Al di fuori dei profili supportati da oracolo, indicare che il Writer «produce strutture PDF 2.0; la conformità è convalidata da veraPDF per il profilo PDF/UA-2» anziché asserire una conformità senza riserve.