L'anatomia di un file PDF
In sintesi
Sezione intitolata “In sintesi”Apri un qualsiasi PDF in un editor di testo semplice e la prima cosa che vedi è
rassicurante: un header %PDF-1.x o %PDF-2.0. L’ultima cosa che vedi è %%EOF.
Tutto ciò che sta tra queste due righe è una piccola macchina ordinata per
trovare gli oggetti per numero. Questa pagina è una dissezione. Apriamo il file,
diamo un nome a ciascun organo e mostriamo come si collegano.
È il compagno strutturale di due pagine vicine. What a PDF actually is tratta il file come un grafo di oggetti; incremental updates coprono come cresce nel tempo. Questa pagina resta vicina ai byte — le regioni fisiche che un parser attraversa, nell’ordine in cui stanno su disco.
Perché è importante
Sezione intitolata “Perché è importante”Quasi mai serve sapere questo per usare un PDF. Serve il giorno in cui uno va storto. Un file si apre in un visualizzatore e non in un altro; un validatore segnala una «tabella dei riferimenti incrociati danneggiata»; un documento firmato improvvisamente non si verifica più. Nessuno di questi è un mistero, una volta che sai leggere l’anatomia. Sono un numero che non corrisponde più a una posizione, una regione nel posto sbagliato, o una coda che punta al nulla.
Conoscere il layout trasforma «il PDF è corrotto» in una diagnosi su cui puoi agire. È la differenza tra alzare le spalle davanti a una scatola nera e indicare il byte esatto che mente.
In breve
Sezione intitolata “In breve”Un PDF conforme ha quattro parti fisiche, in questo ordine nel file (Spec: ISO 32000-2, §7.5.1ISO 32000-2 §7.5.1):
- Un header — una riga,
%PDF-2.0, che indica la versione. - Un corpo — il grosso del file: una sequenza di oggetti indiretti numerati.
- Una sezione dei riferimenti incrociati — un indice dal numero di oggetto all’offset di byte dove quell’oggetto si trova. I PDF classici usano una tabella di testo; PDF 2.0 usa un xref stream compresso.
- Un trailer — un piccolo dizionario che indica il punto di ingresso,
seguito da
startxref, un offset e%%EOF.
Il colpo di scena: un lettore non parte dall’alto. Parte dal basso, legge
startxref per trovare l’indice, e usa quell’indice per raggiungere direttamente
qualsiasi oggetto. Il file è scritto dall’inizio alla fine ma letto dalla fine
all’inizio.
Come NextPDF lo affronta
Sezione intitolata “Come NextPDF lo affronta”Percorriamo le quattro regioni in ordine, con i byte davanti a noi.
L’header è una riga. NextPDF scrive %PDF-2.0, e per convenzione una seconda
riga di commento fatta di byte a bit alto, così che i naive strumenti di
trasferimento trattino il file come binario, non come testo. Quella seconda riga
è il motivo per cui un PDF aperto come testo semplice mostra un piccolo guazzabuglio
subito dopo la versione.
Il corpo è dove vive il documento. Ogni oggetto indiretto è un numero, una
generazione, la keyword obj, un valore e endobj
(Spec: ISO 32000-2, §7.3.10ISO 32000-2 §7.3.10). Il valore è una di poche
forme — ma due portano quasi tutto il peso:
- Un dizionario,
<< /Key value … >>, è una mappa da nomi a valori. L’albero delle pagine, il catalogo, i descrittori di font: tutti dizionari. - Uno stream è un dizionario seguito da
stream, un blocco di byte arbitrari eendstream. Il contenuto delle pagine, i font incorporati e le immagini sono stream, quasi sempre compressi. (I loro filtri sono una storia a sé, raccontata in streams and filters.)
Gli oggetti puntano l’uno all’altro tramite riferimento indiretto — 2 0 R
significa «oggetto 2, generazione 0». È il cablaggio che trasforma un elenco
piatto di oggetti in un grafo.
La sezione dei riferimenti incrociati è la parte che la maggior parte delle
persone non immagina correttamente. Nella forma classica è testo semplice: la
keyword xref, poi sottosezioni di righe a larghezza fissa di 20 byte
(Spec: ISO 32000-2, §7.5.4ISO 32000-2 §7.5.4). Ogni riga è un offset di byte
a dieci cifre, una generazione a cinque cifre e un singolo flag — n per in uso,
f per libero — riempito fino a esattamente venti byte, così che un lettore possa
posizionarsi su qualsiasi voce con la sola aritmetica. PDF 2.0 sostituisce questo
con un xref stream: lo stesso indice, ma binario e compresso dentro un oggetto
stream marcato /Type /XRef (Spec: ISO 32000-2, §7.5.8ISO 32000-2 §7.5.8).
Più piccolo, e capace di descrivere oggetti impacchettati dentro object stream.
Il trailer è l’indice del file (Spec: ISO 32000-2, §7.5.5ISO 32000-2 §7.5.5). Indica /Root — il catalogo del documento, l’unico oggetto
da cui pende tutto il resto — e /Size, il conteggio degli oggetti. Poi arriva
la stretta di mano che rende possibile la lettura all’indietro: startxref, un
offset di byte su una riga a sé, e %%EOF. Un lettore si posiziona alla fine,
legge quell’offset, salta direttamente alla sezione dei riferimenti incrociati, e
parte.
- HeaderUna riga, %PDF-2.0, che indica la versione. Di solito segue un commento marcatore binario.
- CorpoOggetti indiretti numerati — dizionari e stream — referenziati con N G R.
- Sezione dei riferimenti incrociatiUna tabella di testo di voci da 20 byte, o uno stream /Type /XRef compresso in PDF 2.0.
- TrailerIndica /Root e /Size, poi startxref + offset + %%EOF.
- Ordine di letturaUn lettore parte da %%EOF, segue startxref fino all'indice, poi raggiunge direttamente ogni oggetto.
C’è una quinta regione che un file di lunga vita fa crescere: un aggiornamento
incrementale. Una modifica non viene scritta sul posto. Gli oggetti modificati,
una nuova sezione dei riferimenti incrociati e un nuovo trailer vengono aggiunti
dopo il primo %%EOF, e quel nuovo trailer porta /Prev — l’offset della
sezione dei riferimenti incrociati precedente
(Spec: ISO 32000-2, §7.5.6ISO 32000-2 §7.5.6). Le sezioni formano una catena
all’indietro; per qualsiasi numero di oggetto, vince la voce più recente. Poiché
i byte originali non si spostano mai, il controllo crittografico sull’intervallo
di byte che una firma effettivamente copre regge ancora dopo un aggiornamento. Un
successivo aggiornamento incrementale può comunque cambiare ciò che un validatore
riporta sul documento nel suo insieme — se le modifiche successive alla firma sono
consentite, e cosa si ritiene che la firma certifichi — ma non può alterare i byte
firmati stessi. Quella proprietà è l’intero argomento di incremental
updates.
Esempio pratico
Sezione intitolata “Esempio pratico”Ecco l’intera anatomia in un solo file minimo. I numeri sotto xref sono offset
di byte, e devono essere esatti — punta un carattere oltre il punto in cui un
oggetto inizia e un lettore rigoroso si arrende.
%PDF-2.01 0 obj<< /Type /Catalog /Pages 2 0 R >>endobj2 0 obj<< /Type /Pages /Kids [3 0 R] /Count 1 >>endobj3 0 obj<< /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] >>endobjxref0 40000000000 65535 f0000000009 00000 n0000000058 00000 n0000000115 00000 ntrailer<< /Size 4 /Root 1 0 R >>startxref186%%EOFLeggilo nel modo in cui lo fa un parser. Ultima riga: %%EOF. Sopra di essa:
startxref 186, quindi posizionati al byte 186, dove inizia xref. La tabella
dice che l’oggetto 1 vive al byte 9. Il /Root 1 0 R del trailer punta lì — il
catalogo — e dal catalogo percorri /Pages fino all’albero delle pagine e trovi
la singola pagina. L’oggetto 0 è sempre la testa della free-list con generazione
65535, un fossile del primo design del formato che ogni lettore si aspetta
ancora di vedere.
Equivoco comune
Sezione intitolata “Equivoco comune”La trappola è leggere un PDF come una storia — dall’alto in basso, in ordine. Non
è una storia; è un indice con un puntatore all’indietro. I numeri di oggetto non
devono essere sequenziali nel file, gli oggetti possono comparire in qualsiasi
ordine fisico, e un lettore non si affida mai alla loro posizione. L’unica mappa
autorevole è la sezione dei riferimenti incrociati, e l’unico modo per trovare
quella mappa è l’offset startxref proprio in fondo.
La conseguenza sorprende le persone. Un PDF con un corpo impeccabile e una sola
cifra sbagliata in startxref è illeggibile — il lettore non riesce a trovare
l’indice. Un PDF con i suoi oggetti in ordine sparso ma con una sezione dei
riferimenti incrociati corretta va perfettamente bene. La posizione fisica non
porta alcun significato. La posizione registrata lo porta tutto.
Limiti e confini
Sezione intitolata “Limiti e confini”Questa pagina descrive la struttura fisica, non il contenuto della pagina. Come i
segni atterrano su una pagina — operatori del content stream, mostrare testo,
stato grafico — è un argomento separato. Descrive anche un file ben formato. I
PDF del mondo reale sono spesso un po’ rotti e sopravvivono solo perché i
visualizzatori indulgenti ricostruiscono la tabella dei riferimenti incrociati
scandagliando alla ricerca delle keyword obj. Quel recupero è un comportamento
del visualizzatore, non qualcosa che il formato garantisce.
| Edition | Availability |
|---|---|
| Core | NextPDF è un writer. Registra ogni offset dal buffer di output nel momento in cui ciascun oggetto è emesso, così che i file che produce abbiano una sezione dei riferimenti incrociati che corrisponde al corpo per costruzione. |
| Pro | L’analisi, la ricostruzione o la riparazione di una tabella dei riferimenti incrociati danneggiata in un file che NextPDF non ha scritto è fuori dall’ambito in ogni edizione. |
| Enterprise | Per l’ispezione della struttura di un file esistente, usa un parser o un validatore dedicato; NextPDF garantisce la correttezza di ciò che scrive, non di ciò che legge. |
Mini-FAQ
Sezione intitolata “Mini-FAQ”Perché un PDF ha una riga marcatore binario dopo l’header? Alcuni strumenti di trasferimento più datati storpiavano un file che credevano fosse testo semplice. Il commento a bit alto fa apparire il file inequivocabilmente binario, così che sopravviva al viaggio immutato.
L’xref stream è solo una tabella più piccola? Per lo più, con un potere in più. Oltre a essere compresso, un xref stream può descrivere oggetti memorizzati dentro gli object stream — voci che la classica tabella di testo da 20 byte non ha modo di esprimere.
Posso avere sia una tabella che uno stream in un solo file? Una singola revisione usa l’una o l’altro. Ma un file ibrido può abbinare una tabella classica per i vecchi lettori a un xref stream per quelli nuovi, così che ogni tipo di lettore trovi un indice che comprende.
Documentazione correlata
Sezione intitolata “Documentazione correlata”- What a PDF actually is — le stesse quattro parti viste come un grafo di oggetti anziché come un layout fisico.
- Incremental updates and why they matter — come la quinta regione aggiunta fa crescere un file e protegge le firme.
- Streams and filters — cosa c’è dentro gli oggetti stream del corpo e come sono compressi.
- PDF 2.0: what changed — perché l’xref stream è la struttura predefinita che NextPDF scrive.
Glossario
Sezione intitolata “Glossario”- Header — la prima riga,
%PDF-2.0, che indica la versione; di solito seguita da un commento marcatore binario. - Oggetto indiretto — un oggetto numerato nel corpo, scritto
N G obj … endobj, doveNè il numero di oggetto eGla generazione. - Dizionario — una mappa
<< /Key value … >>da nomi a valori; la forma di oggetto più comune. - Stream — un dizionario più un blocco di byte tra
streamedendstream, usato per contenuto, font e immagini. - Tabella dei riferimenti incrociati (xref) — l’indice dal numero di oggetto
all’offset di byte; una tabella di testo da 20 byte per voce nella forma
classica, uno stream
/Type /XRefin PDF 2.0. - Trailer — il dizionario che indica
/Roote/Size, individuato tramite l’offsetstartxrefalla fine del file. - Aggiornamento incrementale — oggetti modificati, una nuova sezione dei
riferimenti incrociati e un nuovo trailer aggiunti dopo
%%EOF, con/Prevche si concatena alla sezione precedente.