PDF datoteka je, u svojoj biti, zbirka objekata koji pokazuju jedni na druge. Uklonite kompresiju, vođenje evidencije unakrsnih referenci i pomake bajtova, i ono što ostaje jest graf: mali skup tipiziranih vrijednosti, povezanih referencama, s korijenom u jednom objektu koji preglednik zna pronaći. Sve što PDF može izraziti, od odlomka teksta preko ugrađenog fonta do digitalnog potpisa, izgrađeno je od osam primitivnih tipova objekata i pravila koje omogućuje jednom objektu da upućuje na drugi. Naučite njih, i ostatak formata čitat će se kao kompozicija, a ne kao misterij.
Ovo je logički sloj PDF-a, definiran u normi ISO 32000-1, stavak 7.3, i nalazi se jednu razinu iznad fizičkog rasporeda datoteke (zaglavlje, tijelo, tablica unakrsnih referenci i trailer, što je zasebna tema u tehničkom pregledu strukture PDF datoteka). Logički model je ono što ti bajtovi znače nakon što se analiziraju. Preglednik čita datoteku unatrag kako bi pronašao trailer, prati ga do korijena, a odatle se dokument odvija kao niz objekata koji upućuju na druge objekte. To je dio o kojem razmišljate kada otklanjate pogreške na neispravnoj stranici, pišete parser ili povjeravate knjižnici sastavljanje dokumenta.
Osam tipova objekata i ništa drugo
PDF definira točno osam osnovnih tipova objekata. Svaka vrijednost u dokumentu je jedna od njih, što format čini pristupačnim unatoč njegovom opsegu.
Booleove vrijednosti (Booleans) su ključne riječi true and false. One uključuju i isključuju zastavice, kao što je npr. ispisuje li se bilješka (annotation).
Brojevi dolaze u dva oblika koje specifikacija tretira kao jedan tip: cijeli brojevi (integers) poput 42 i realni brojevi (reals) poput 3.14 ili -0.002. PDF nema eksponentnu notaciju, pa nikada nećete vidjeti 1e6 u usklađenoj datoteci. Koordinate, veličine fontova i kutovi rotacije su brojevi.
Nizovi znakova (Strings) drže sekvence bajtova, zapisane ili u zagradama, (Hello), ili u šiljastim zagradama kao heksadecimalni kod, <48656C6C6F>. Obje notacije kodiraju identičan sadržaj; heksadecimalni zapis je pomoćno rješenje za bajtove koji su nezgodni unutar zagrada. Nizovi znakova nose tekst, ali su prvenstveno bajtovi, što postaje važno onog trenutka kada radite s bilo čime izvan ASCII koda.
Nazivi (Names) su atomski tokeni uvedeni kosom crtom: /Type, /Pages, /MediaBox. Naziv nije niz znakova; to je identifikator, koristi se kao ključ rječnika ili nabrojana vrijednost, a dva naziva su jednaka samo ako se podudaraju bajt po bajt. Kosa crta je dio sintakse, a ne dio samog naziva. To buni početnike koji tretiraju /Times-Roman i niz znakova (Times-Roman) kao međusobno zamjenjive; format ih ne tretira tako.
Polja (Arrays) su poredani, heterogeni popisi u uglatim zagradama: [0 0 612 792] je pravokutnik stranice, a polje može slobodno miješati tipove, uključujući reference na druge objekte. Rječnici (Dictionaries) su glavno radno sredstvo. Zapisani između << and >>, rječnik preslikava ključeve naziva u vrijednosti bilo kojeg tipa, a gotovo svaka značajna struktura u PDF-u – stranica, katalog, font, bilješka – rječnik je s ključem /Type koji deklarira što je to.
Tokovi (Streams) su rječnici s dodatkom sirovih bajtova između ključnih riječi stream i endstream. Rječnik opisuje bajtove (njihovu duljinu i sve filtre poput FlateDecode koji ih komprimiraju), a bajtovi nose teški korisni teret: upute za sadržaj stranice, ugrađene programe fontova, slike. Tok je mjesto gdje PDF stavlja sve što je preveliko ili previše binarno da bi stajalo u samom retku (inline).
Osmi tip je null objekt, ključna riječ null. To je stvarna vrijednost, različita od odsutnosti ključa. Unos rječnika postavljen na null tretira se kao da nije prisutan, a referenca koja se razrješava u nepostojeći objekt također daje null umjesto pogreške. To popustljivo ponašanje je namjerno: omogućuje oštećenoj datoteci da degradira umjesto da se odbije otvoriti. Ne postoji deveti tip; sve što PDF izražava dolazi od načina na koji se ovih osam kombinira.
Izravne vrijednosti, neizravni objekti i reference
Bilo koji od tih osam tipova može se pojaviti na dva načina. Izravni (direct) objekt piše se na licu mjesta, poput broja 612 unutar polja MediaBox. Neizravnom (indirect) objektu daje se identitet kako bi drugi objekti mogli pokazivati na njega: dva cijela broja, broj objekta i broj generacije, koji omataju definiciju u obj i endobj:
12 0 obj
<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>
endobj
Ovo je objekt 12, generacija 0, rječnik fonta. Bilo gdje drugdje u datoteci, drugi objekt upućuje na njega neizravnom referencom: ista dva broja iza kojih slijedi ključna riječ R, 12 0 R. Referenca je pokazivač. Kada rječnik resursa stranice kaže /Font << /F1 12 0 R >>, on imenuje objekt 12 kao font iza naziva resursa /F1, bez kopiranja definicije fonta na samu stranicu.
Broj generacije postoji za brisanja i ponovnu upotrebu. Kada se objekt oslobodi i njegovo mjesto ponovno upotrijebi, generacija se povećava tako da zastarjeli 12 0 R ne može razriješiti novog stanara mjesta 12. Svježe zapisane datoteke gotovo su sve generacije 0, ali jako uređivana datoteka može nositi veće brojeve, a parser koji ignorira generaciju na kraju će pročitati pogrešan objekt.
Neizravnost je ono što PDF čini učinkovitim i prikladnim za uređivanje. Jedan font, slika ili prostor boja mogu se definirati jednom i referencirati sa stotinu stranica. Mala promjena može se dodati kao nova revizija koja zamjenjuje pojedinačni objekt umjesto prepisivanja cijele datoteke. Tablica unakrsnih referenci je indeks koji pretvara broj objekta u pomak bajta, tako da preglednik skače izravno na 12 0 obj bez skeniranja, ali to je fizička optimizacija. Logički gledano, sve što trebate znati jest da 12 0 R znači "the object identified as 12 0."
Katalog: gdje počinje svaki dokument
Razrješavanje referenci mora negdje početi, a to je unos /Root u traileru, koji pokazuje na katalog dokumenta (document catalog): korijen grafa objekata, rječnik s /Type /Catalog. Preglednik prvo dolazi do njega jer se trailer pronalazi prvi, a odatle je svaki drugi dio dokumenta dostupan praćenjem referenci.
Katalog nosi samo dva strogo obvezna unosa: svoj /Type i /Pages, neizravnu referencu na korijen stabla stranica. Ostali su neobavezni i opisuju ponašanje na razini cijelog dokumenta, a ne sadržaj: /Outlines pokazuje na stablo oznaka (bookmarks), /Names drži stabla naziva s ključevima u obliku niza znakova, /Metadata upućuje na XMP tok metapodataka, a /PageMode i /PageLayout sugeriraju kako bi preglednik trebao otvoriti dokument. Ništa od toga nije potrebno za prikazivanje stranice; oni konfiguriraju doživljaj oko stranica. Strukture oznaka, metapodataka i bilješki koje vise s kataloga obrađene su u članku o PDF metapodacima, oznakama i bilješkama.
Dijagram u nastavku pokazuje gdje tijelo objekta sjedi u okolnoj datoteci. Katalog i stablo stranica žive unutar tog tijela kao obični neizravni objekti; zaglavlje (header), tablica unakrsnih referenci (xref) i trailer oko njih fizička su konstrukcija koja pregledniku omogućuje da ih locira.

Stablo stranica: uravnotežena hijerarhija stranica
Od /Pages se dokument grana u stablo stranica, gdje se PDF-ov izbor grafa umjesto ravnog popisa isplati. Stranice se ne pohranjuju kao jednostavan slijed; one vise na stablu čiji su unutarnji čvorovi čvorovi stabla stranica (/Type /Pages), a čiji su listovi objekti stranica (/Type /Page). Unutarnji čvor navodi svoju djecu u polju /Kids i bilježi, u /Count, koliko stranica (listova) živi ispod njega. Svaki čvor osim korijena nosi referencu /Parent natrag prema gore, tako da se stablom može kretati u oba smjera.
2 0 obj % root of the page tree
<< /Type /Pages /Kids [3 0 R 4 0 R] /Count 3 >>
endobj
3 0 obj % a leaf page
<< /Type /Page /Parent 2 0 R
/MediaBox [0 0 612 792]
/Resources << /Font << /F1 12 0 R >> >>
/Contents 5 0 R >>
endobj
4 0 obj % an interior node grouping two more pages
<< /Type /Pages /Parent 2 0 R /Kids [6 0 R 7 0 R] /Count 2 >>
endobj
Ovdje je objekt 2 korijen, s tri stranice ispod sebe: listom stranice 3 i još dvije dostupne preko unutarnjeg čvora 4. /Count od 3 na korijenu mora biti gore navedeni ukupni broj listova ispod njega, a broj koji se ne slaže sa stvarnom strukturom čest je način na koji ručno uređivana datoteka pođe po zlu. Svrha stabla je lokalnost pristupa. Preglednik koji otvara stranicu 900 u dokumentu od tisuću stranica ne prolazi kroz 900 objekata; on se spušta kroz nekoliko čvorova, jer dobro oblikovano stablo ostaje plitko i uravnoteženo. Ručna izrada takvog stabla dovoljno je složena da ju vrijedi vidjeti od početka do kraja, što vodič o izradi PDF dokumenta od nule i čini.
Stablo se opravdava i kroz nasljeđivanje. Nekoliko atributa stranice – /Resources, /MediaBox, /CropBox i /Rotate – može se postaviti na unutarnji čvor i izostaviti sa samih stranica, koje potom nasljeđuju vrijednost najbližeg pretka. Postavite /MediaBox jednom na korijen i svaki list dobiva istu veličinu stranice bez ponavljanja; stranica koja se mora razlikovati deklarira vlastitu. To je jedino mjesto u modelu objekata gdje značenje vrijednosti ovisi o položaju objekta u stablu, a ne samo o njegovom sadržaju.
Što zapravo sadrži list stranice
Objekt stranice (Page object) je točka spajanja između strukturnog modela i vidljivog sadržaja. Njegov unos /Contents upućuje na jedan ili više tokova sadržaja (content streams), operatore crtanja koji iscrtavaju tekst i grafiku na stranici. Njegov rječnik resursa /Resources imenuje fontove, slike i prostore boja na koje se ti operatori oslanjaju, pri čemu je svaki unos neizravna referenca na objekt koji se dijeli među stranicama. /MediaBox daje pravokutnik stranice u točkama (1/72 inča), a unosi poput /Rotate i /CropBox prilagođavaju način na koji je prikazan.
Ta podjela rada je cijeli model u malom. Rječnik stranice je struktura: tipizirani unosi i reference koji govore što stranica jest i čime crta. Tok sadržaja su upute: zaseban, kompresibilan blok (blob) koji govori kako crtati. Font iza /F1 je zajednički resurs, definiran jednom i usmjeren na njega svugdje gdje se koristi. Rječnik, tok i referenca surađuju na renderiranju jedne stranice, a isti se obrasci prenose na cijeli dokument. Operatori toka sadržaja unutar tog bloka pokriveni su zasebno za tekst i fontove te za grafiku i vizualne elemente.
Zašto vrijedi poznavati ovaj model
Većina razvojnih programera susreće se s modelom objekata tek kada se nešto pokvari: stranica se prikazuje prazna jer njezina referenca /Contents visi, tekst se prikazuje kao kvadratići jer resurs fonta nikada nije ugrađen, alat javlja /Count koji se ne podudara sa stranicama koje može pronaći. Svaki od tih slučajeva je izjava o grafu, a izravno čitanje grafa je bolje od nagađanja. Tih osam tipova i pravilo referenciranja dovoljno su mali vokabular da ih držite u glavi, a kada jednom vidite PDF kao skup objekata koji pokazuju na druge objekte, neispravne datoteke prestaju biti neprozirne.
Međutim, ručno pisanje modela rijetko je ispravan izbor izvan učenja. Održavanje pomaka unakrsnih referenci, brojeva generacija, broja stabla stranica i duljina tokova dosljednima kroz uređivanja vrsta je administracije koju knjižnica preuzima na sebe. U produkciji, a zrela knjižnica za razvoj PDF-a upravlja grafom objekata, ostavljajući vas da razmišljate o stranicama i sadržaju. Poznavanje modela se i dalje isplati: razumijete što knjižnica gradi ispod svega, i zašto.