Flussi e filtri
ISO 32000-2 §7.4 Evidence: Standard-backed
In sintesi
Sezione intitolata “In sintesi”La maggior parte dei byte di un PDF reale si trova nei flussi: contenuto di pagina, font, immagini, perfino il flusso dei riferimenti incrociati. Quasi nessuno di quei byte è memorizzato in forma grezza: prima passa attraverso uno o più filtri. Questa pagina illustra i filtri più comuni, a cosa serve ciascuno, dove possono creare problemi e perché NextPDF fissa la propria compressione affinché lo stesso input produca sempre gli stessi byte.
Perché è importante
Sezione intitolata “Perché è importante”Un flusso e il suo filtro sono un contratto: «questi byte sono compressi con deflate e poi codificati in base-85: decodificarli in quest’ordine per ottenere i dati reali». Se la voce /Filter non corrisponde a ciò che i byte sono realmente, se il valore /Length è errato, oppure se due filtri sono elencati nell’ordine sbagliato, il flusso non è decodificabile e l’oggetto che trasportava è perduto. Un lettore non prova a indovinare in modo euristico; esegue ciò che il dizionario gli indica.
Esiste anche un secondo costo, più silenzioso. Se il compressore di una libreria non è deterministico — build di zlib diversa, livello diverso, confini interni dei blocchi diversi — due esecuzioni che dovrebbero produrre un PDF identico generano invece due file diversi. Questo interrompe la riproducibilità a livello di byte. A sua volta, una riproducibilità compromessa indebolisce i test golden-file, la verifica delle build firmate e qualunque pipeline che confronti l’output. I filtri determinano sia se il PDF è corretto sia se il PDF è lo stesso.
In breve
Sezione intitolata “In breve”- Un oggetto flusso è un dizionario più un blocco di byte, racchiuso tra
stream…endstream, con un/Lengthe di solito un/Filter. - La voce
/Filternomina il filtro di decodifica — oppure un array di filtri applicati come pipeline, in ordine. - I filtri si dividono in due famiglie: compressione (FlateDecode, LZWDecode, RunLengthDecode, DCTDecode, JPXDecode, JBIG2Decode) e trasporto ASCII (ASCIIHexDecode, ASCII85Decode), oltre allo speciale filtro Crypt per la cifratura.
- Il filtro che si incontra più spesso è FlateDecode — zlib/deflate. È il filtro predefinito per il contenuto, i font e il flusso dei riferimenti incrociati.
- NextPDF fissa il proprio output Flate a un livello e a un formato prestabiliti, in modo che gli stessi byte di input si comprimano sempre negli stessi byte di output.
Come lo affronta NextPDF
Sezione intitolata “Come lo affronta NextPDF”NextPDF emette gli oggetti flusso attraverso un unico helper di buffer e comprime tramite un unico compressore fissato — intenzionalmente.
BinaryBuffer::writeStream() (src/Support/BinaryBuffer.php) avvolge il contenuto del flusso nel relativo dizionario, scrivendo sempre un /Length pari alla lunghezza effettiva in byte e unendo le eventuali voci aggiuntive fornite dal chiamante, come /Filter. Non esiste alcun percorso in cui la lunghezza dichiarata possa non corrispondere ai byte scritti, perché la lunghezza è ricavata dalla stessa stringa di contenuto.
La compressione passa attraverso PinnedZlibCompressor (src/Writer/PinnedZlibCompressor.php). Questa classe esiste per un solo motivo. gzcompress senza un livello esplicito si affida al valore predefinito del runtime zlib, che storicamente è variato tra build diverse. L’intestazione zlib di 2 byte codifica perfino il livello in modo indiretto, quindi «il valore predefinito» non è un output stabile. Il compressore fissa il livello al massimo previsto da RFC 1951 ed emette sempre deflate avvolto in zlib (intestazione RFC 1950 + trailer Adler-32), esattamente ciò che /Filter /FlateDecode si aspetta. Un guasto irreversibile di zlib diventa un’eccezione tipizzata anziché un ripiego silenzioso su output non compresso: un flusso non viene mai emesso grezzo in modo silenzioso.
Lo stesso flusso dei riferimenti incrociati è un esempio concreto di questo meccanismo: CrossReferenceStream (src/Core/CrossReferenceStream.php) costruisce una tabella binaria, la comprime e la emette come oggetto flusso con /Type /XRef, un array di larghezze di campo /W e /Filter /FlateDecode. L’indice che consente a un lettore di trovare ogni oggetto è, a sua volta, un flusso filtrato.
| Filtro | Famiglia | A cosa serve | Dove va storto |
|---|---|---|---|
| FlateDecode | Compressione | zlib/deflate; il filtro predefinito per contenuto, font e flussi xref | Una build di zlib non deterministica fa differire byte per byte PDF «identici» |
| LZWDecode | Compressione | Vecchia compressione Lempel–Ziv–Welch | Obsoleto; soppiantato da Flate, ma ancora visibile occasionalmente in vecchi file |
| DCTDecode | Compressione | Immagini colour/grayscale codificate in JPEG | Con perdita — ricodificare un’immagine già in DCT la degrada ulteriormente |
| JPXDecode | Compressione | Dati immagine wavelet JPEG 2000 | Non consentito da alcuni profili di archiviazione; il supporto diffuso è disomogeneo |
| JBIG2Decode | Compressione | Compressione di immagini bilivello (1 bit) | Non deve essere usato con le immagini in linea; le modalità con perdita possono alterare le scansioni |
| RunLengthDecode | Compressione | Run-length orientato ai byte | Aiuta solo con dati che contengono lunghe sequenze di un singolo byte; può ingrandire altri dati |
| ASCIIHexDecode | Trasporto | Dati binari come cifre esadecimali | Raddoppia la dimensione; solo per canali sicuri a 7 bit, mai per ridurre la dimensione |
| ASCII85Decode | Trasporto | Dati binari come ASCII in base-85 | Circa il 25% di sovraccarico; una comodità di trasporto, non una compressione |
| Crypt | Sicurezza | Applica il gestore di sicurezza del documento | Un flusso di riferimenti incrociati non deve usare un filtro Crypt |
L’insieme dei filtri standard del PDF, per famiglia, con il guasto che ciascuno può comportare. NextPDF scrive FlateDecode per il contenuto, i font e il flusso dei riferimenti incrociati; i filtri di trasporto ASCII servono per i canali a 7 bit, mai per ridurre la dimensione.
Cosa dicono le prove
Sezione intitolata “Cosa dicono le prove”Il meccanismo dei filtri è definito da Spec: ISO 32000-2, §7.4 ISO 32000-2 §7.4 . I filtri di un flusso sono specificati dalla voce /Filter nel suo dizionario, e i filtri possono essere concatenati per formare una pipeline che fa passare il flusso attraverso due o più trasformazioni di decodifica in sequenza. L’esempio fornito dallo standard stesso è LZW seguito da ASCII base-85, decodificati in quell’ordine. Un writer codifica un flusso per comprimerlo o per renderlo sicuro a 7 bit. Un reader invoca i filtri di decodifica corrispondenti per recuperare i dati originali. Evidence: Standard-backed
La tabella dei filtri dello standard classifica ciascun filtro. FlateDecode decomprime i dati codificati con zlib/deflate, riproducendo il testo o i dati binari originali. DCTDecode riproduce campioni di immagine che approssimano l’originale tramite JPEG — la parola «approssimano» segnala che il filtro è con perdita. Anche LZWDecode, RunLengthDecode, JBIG2Decode, JPXDecode e il filtro Crypt sono definiti lì, con JBIG2 esplicitamente vietato per le immagini in linea.
Il flusso dei riferimenti incrociati applica a se stesso il meccanismo proprio del formato: è un oggetto flusso (/Type /XRef,
Spec: ISO 32000-2, §7.5.8 ISO 32000-2 §7.5.8 ) il cui array /W
indica la larghezza in byte di ciascun campo di voce nel flusso decodificato. Lo
standard richiede che non sia cifrato e che non usi un filtro Crypt.
Il CrossReferenceStream di NextPDF rispetta esattamente questo schema — FlateDecode,
/W esplicito, nessuna cifratura.
Esempio pratico
Sezione intitolata “Esempio pratico”Un flusso di contenuto di pagina compresso con Flate. Questa è di gran lunga la forma più comune: un dizionario con /Length e /Filter, poi i byte compressi tra stream ed endstream.
<?php
declare(strict_types=1);
use NextPDF\Writer\PinnedZlibCompressor;
// The marking operators a page content stream carries, uncompressed.$content = "BT /F1 12 Tf 72 712 Td (Hello) Tj ET\n";
// NextPDF compresses through the pinned compressor: fixed level,// fixed zlib-wrapped format. The same $content always yields the// same $compressed bytes, on any supported PHP/zlib build.$compressed = PinnedZlibCompressor::compress($content);
// Emitted as a stream object. /Length is the real byte length of// $compressed; /Filter names the decode the reader must apply.// N 0 obj// << /Length <strlen($compressed)> /Filter /FlateDecode >>// stream// <$compressed bytes>// endstream// endobjUn lettore esegue l’operazione inversa: legge /Length byte, li fa passare attraverso FlateDecode perché lo indica /Filter, e riottiene gli operatori originali. Fissando il compressore, quel round trip non è solo corretto. È identico ogni volta, ed è questo il presupposto dei controlli golden-file e delle build firmate.
Errore comune
Sezione intitolata “Errore comune”L’insidia è trattare i filtri ASCII come compressione. ASCIIHexDecode e ASCII85Decode rendono un flusso più grande — all’incirca il doppio e all’incirca il 25% rispettivamente. Esistono per spostare dati binari attraverso un canale sicuro solo per testo a 7 bit, non per risparmiare spazio. Scegliere ASCII85 per «rimpicciolire» un PDF ottiene l’effetto opposto. La seconda metà dello stesso errore è credere che FlateDecode sia senza perdita per le immagini «gratuitamente». Flate è senza perdita, ma se l’immagine era già codificata in DCT (JPEG), avvolgerla di nuovo o transcodificarla attraverso un filtro con perdita la degrada a prescindere da ciò che Flate fa attorno a essa. La pipeline dei filtri preserva esattamente ciò che le viene fornito — incluso un artefatto di ri-compressione fornito per errore.
Limiti e confini
Sezione intitolata “Limiti e confini”Questa pagina illustra come i filtri vengono dichiarati e applicati, non l’algoritmo a livello di bit interno a ciascuno. La garanzia di determinismo riguarda specificamente l’output Flate di NextPDF per i flussi che scrive. Vale tra versioni minori di PHP e build di zlib conformi allo standard, ma lo standard consente esplicitamente a un encoder deflate di scegliere confini interni dei blocchi diversi; quindi un output byte-identico tra implementazioni di zlib genuinamente diverse (per esempio una zlib di serie rispetto a zlib-ng) non è garantito. Per questo motivo l’ambiente di build è fissato.
NextPDF sceglie FlateDecode e i filtri di trasporto ASCII per i dati che emette. Non è un transcodificatore di immagini. Non promette di re-impacchettare un flusso JPEG2000 o JBIG2 arbitrario in ingresso, e i compromessi delle immagini con perdita sono una proprietà dei dati di origine, non qualcosa che un writer possa annullare.
Mini-FAQ
Sezione intitolata “Mini-FAQ”Perché FlateDecode è ovunque? È senza perdita, generico, ben supportato e si adatta bene al contenuto composto da testo e operatori della maggior parte dei PDF. È il valore predefinito sicuro per i flussi di contenuto, i font incorporati e il flusso dei riferimenti incrociati.
Posso disattivare la compressione? Si può omettere /Filter e memorizzare i byte grezzi; un lettore li accetterà. Il file diventa più grande e nient’altro migliora; raramente c’è un motivo al di fuori del debug.
Perché fissare il livello di compressione? Affinché l’output sia riproducibile. Un livello non fissato (o una build di zlib) può cambiare i byte compressi senza cambiare il contenuto decompresso — corretto, ma non identico, il che vanifica la verifica a livello di byte.
Documenti correlati
Sezione intitolata “Documenti correlati”- Cos’è davvero un PDF — il modello a oggetti in cui si collocano i flussi descritti in questa pagina.
- Font: la parte difficile — i programmi di font incorporati sono flussi filtrati, con le rispettive modalità di guasto.
- PDF 2.0: cosa è cambiato — come la baseline 2.0 tratta i flussi e il flusso dei riferimenti incrociati che NextPDF usa come impostazione predefinita.
Glossario
Sezione intitolata “Glossario”- Oggetto flusso — un dizionario più un blocco di byte tra
streamedendstream, che trasporta un/Lengthe di solito un/Filter. - Filtro — una trasformazione di decodifica con nome che un lettore applica ai byte di un flusso (per esempio
FlateDecode). - Pipeline di filtri — un array di filtri applicati in sequenza; l’ordine dell’array è l’ordine di decodifica.
- FlateDecode — il filtro zlib/deflate; la compressione predefinita per contenuto, font e flussi di riferimenti incrociati.
- DCTDecode — il filtro per immagini JPEG; con perdita, quindi ricodificare degrada di nuovo l’immagine.
- Filtro di trasporto ASCII — ASCIIHexDecode / ASCII85Decode; rende i dati sicuri a 7 bit a costo della dimensione — non è compressione.
- Compressione deterministica — produzione di un output compresso byte-identico per input identico, ottenuta fissando il livello e il formato del compressore.