Technical Article

Logički model PDF objekata: Tipovi, reference i struktura

PDF datoteka je u svojoj suštini kolekcija objekata koji ukazuju jedni na druge. Kada uklonite kompresiju, vođenje evidencije unakrsnih referenci i ofsete bajtova, ono što preostaje jeste graf: mali skup tipiziranih vrednosti, povezanih referencama, sa korenom u jednom objektu koji čitač zna kako da pronađe. Sve što PDF može da izrazi, od pasusa teksta preko ugrađenog fonta do digitalnog potpisa, izgrađeno je od osam primitivnih tipova objekata i pravila koje omogućava jednom objektu da se pozove na drugi. Naučite njih, i ostatak formata se čita kao kompozicija a ne kao misterija.

Ovo je logički sloj PDF-a, definisan u odeljku 7.3 standarda ISO 32000-1, i on se nalazi jedan nivo iznad fizičkog rasporeda datoteke (zaglavlje, telo, tabela unakrsnih referenci i trailer, što je posebna tema obrađena u našem tehničkom pregledu strukture PDF datoteka). Logički model predstavlja ono što ti bajtovi znače nakon što se analiziraju. Pregledač čita datoteku unazad kako bi pronašao trailer, prati ga do korena, i odatle se dokument odvija kao niz objekata koji referenciraju druge objekte. To je deo o kojem razmišljate kada otklanjate greške na neispravnoj stranici, pišete parser ili se oslanjate na biblioteku za sklapanje dokumenta.

Osam tipova objekata, i ništa više

PDF definiše tačno osam osnovnih tipova objekata. Svaka vrednost u dokumentu je jedan od njih, što je ono što format čini upravljivim uprkos njegovom obimu.

Booleans (Logički tipovi) su ključne reči true i false. Oni uključuju i isključuju zastavice, kao što je podatak da li se napomena štampa.

Numbers (Brojevi) dolaze u dva oblika koje specifikacija tretira kao jedan tip: celi brojevi (integers) kao što je 42 i realni brojevi (reals) kao 3.14 ili -0.002. PDF nema eksponentnu notaciju, tako da u usaglašenoj datoteci nikada nećete videti 1e6. Koordinate, veličine fontova i uglovi rotacije su brojevi.

Strings (Tekstualni nizovi) drže sekvence bajtova, upisane ili u zagradama, (Hello), ili u šiljastim zagradama kao heksadecimalne vrednosti, <48656C6C6F>. Obe notacije kodiraju identičan sadržaj; heksadecimalni zapis je rešenje za bajtove koje je nezgodno pisati unutar zagrada. Nizovi prenose tekst, ali su prvenstveno bajtovi, što postaje važno onog trenutka kada radite sa bilo čim izvan ASCII opsega.

Names (Nazivi) su atomski tokeni uvedeni kosom crtom: /Type, /Pages, /MediaBox. Naziv nije tekstualni niz; to je identifikator koji se koristi kao ključ rečnika ili nabrojana vrednost, a dva naziva su jednaka samo ako se poklapaju bajt po bajt. Kosa crta je deo sintakse, a ne samog naziva. Ovo sapliće početnike koji tretiraju /Times-Roman i string (Times-Roman) kao zamenljive pojmove; format ih ne tretira tako.

Arrays (Nizovi) su uređene, heterogene liste u uglastim zagradama: [0 0 612 792] je pravougaonik stranice, a niz može slobodno mešati tipove, uključujući i reference na druge objekte. Dictionaries (Rečnici) su glavna radna snaga. Napisani između oznaka << i >>, rečnik mapira ključeve naziva na vrednosti bilo kog tipa, i skoro svaka značajna struktura u PDF-u (stranica, katalog, font, napomena) jeste rečnik sa ključem /Type koji deklariše šta je taj objekat.

Streams (Tokovi) su rečnici sa dodatkom sirovih bajtova između ključnih reči stream i endstream. Rečnik opisuje te bajtove (njihovu dužinu i sve filtere poput FlateDecode koji ih kompresuju), dok bajtovi nose obiman sadržaj: instrukcije o sadržaju stranice, ugrađene programe fontova, slike. Tok je mesto gde PDF smešta sve što je preveliko ili previše binarno da bi stajalo unutar samog objekta.

Osmi tip je null objekat, sa ključnom rečju null. To je stvarna vrednost, različita od odsustva ključa. Unos u rečnik koji je podešen na null tretira se kao da nije prisutan, a referenca koja se razrešava na nepostojeći objekat takođe daje null umesto greške. To tolerantno ponašanje je namerno: omogućava oštećenoj datoteci da se delimično prikaže umesto da odbije otvaranje. Ne postoji deveti tip; sve što PDF izražava dolazi iz kombinacije ovih osam.

Direktne vrednosti, indirektni objekti i reference

Bilo koji od ovih osam tipova može se pojaviti na dva načina. Direktan objekat se piše na licu mesta, kao što je broj 612 unutar MediaBox niza. Indirektnom objektu se daje identitet kako bi drugi objekti mogli da ukazuju na njega: dva cela broja (broj objekta i broj generacije) obavijajući definiciju rečima obj i endobj:

12 0 obj
<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>
endobj

Ovo je objekat 12, generacije 0, odnosno rečnik fonta. Na bilo kom drugom mestu u datoteci, drugi objekat se poziva na njega pomoću indirektne reference: ista dva broja praćena ključnom rečju R, odnosno 12 0 R. Referenca je pokazivač. Kada rečnik resursa stranice kaže /Font << /F1 12 0 R >>, on imenuje objekat 12 kao font iza naziva resursa /F1, bez kopiranja definicije fonta na samu stranicu.

Broj generacije postoji zbog brisanja i ponovne upotrebe. Kada se objekat oslobodi i njegov slot ponovo iskoristi, broj generacije se uvećava kako se stara referenca 12 0 R ne bi razrešila na novog stanara slota 12. Sveže napisane datoteke su skoro sve generacije 0, ali intenzivno uređivana datoteka može nositi veće brojeve, i parser koji ignoriše generaciju će na kraju pročitati pogrešan objekat.

Indirekcija je ono što čini PDF efikasnim i podložnim uređivanju. Jedan font, slika ili prostor boja mogu se definisati jednom i referencirati sa sto stranica. Mala promena se može dodati kao nova revizija koja zamenjuje jedan objekat, umesto da se ponovo piše cela datoteka. Tabela unakrsnih referenci je indeks koji pretvara broj objekta u bajt-ofset, tako da čitač skače direktno na 12 0 obj bez skeniranja, ali to je fizička optimizacija. Logički, sve što treba da znate jeste da 12 0 R označava „objekat identifikovan kao 12 0“.

Katalog: mesto gde svaki dokument počinje

Razrešavanje referenci mora negde da počne, a to mesto je unos /Root u trailer-u, koji ukazuje na katalog dokumenta (document catalog): koren grafa objekata, rečnik sa /Type /Catalog. Čitač prvo stiže do njega jer se trailer pronalazi prvi, a odatle je svaki drugi deo dokumenta dostupan prateći reference.

Katalog sadrži samo dva striktno obavezna unosa: svoj /Type i /Pages, što je indirektna referenca na koren stabla stranica. Ostali unosi su opcioni i opisuju ponašanje na nivou celog dokumenta, a ne sam sadržaj: /Outlines ukazuje na stablo obeleživača (bookmarks), /Names drži stabla naziva sa ključevima u vidu teksta, /Metadata referencira XMP tok metapodataka, a /PageMode i /PageLayout sugerišu kako pregledač treba da otvori dokument. Nijedan od njih nije potreban za iscrtavanje stranice; oni samo konfigurišu doživljaj oko stranica. Strukture obeleživača, metapodataka i napomena koje vise sa kataloga obrađene su u članku o PDF metapodacima, obeleživačima i napomenama.

Dijagram ispod pokazuje gde se telo objekata nalazi u odnosu na ostatak datoteke. Katalog i stablo stranica žive unutar tog tela kao obični indirektni objekti; zaglavlje, tabela unakrsnih referenci i trailer oko njih su fizička skela koja čitaču omogućava da ih locira.

Dijagram četiri fizička odeljka PDF datoteke: zaglavlje verzije, telo koje sadrži objekte dokumenta uključujući katalog i stablo stranica, tabela unakrsnih referenci sa ofsetima objekata i trailer koji ukazuje na koren

Stablo stranica: balansirana hijerarhija stranica

Od unoga /Pages dokument se grana u stablo stranica, gde se PDF-ov izbor grafa umesto ravne liste isplaćuje. Stranice se ne čuvaju kao jednostavan niz; one vise sa stabla čiji su unutrašnji čvorovi čvorovi stabla stranica (/Type /Pages), a listovi su objekti stranica (/Type /Page). Unutrašnji čvor navodi svoju decu u nizu /Kids i beleži, u unosu /Count, koliko se stranica-listova nalazi ispod njega. Svaki čvor osim korena nosi referencu /Parent nagore, tako da se kroz stablo može prolaziti u oba smera.

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

Ovde je objekat 2 koren, sa tri stranice ispod njega: stranica-list 3, i još dve dostupne preko unutrašnjeg čvora 4. Korenov /Count od 3 mora biti negativan? Ne, jednak ukupnom broju listova ispod njega, a broj koji se ne slaže sa stvarnom strukturom je čest način na koji ručno uređivana datoteka otkaže. Poenta stabla je lokalnost pristupa. Čitač koji otvara stranicu 900 u dokumentu od hiljadu stranica ne prolazi kroz 900 objekata; on se spušta kroz samo nekoliko čvorova, jer dobro formirano stablo ostaje plitko i balansirano. Ručna izrada takvog stabla je dovoljno komplikovana da je vredi videti od početka do kraja, što vodič za izgradnju PDF dokumenta od nule upravo i radi.

Stablo opravdava svoju ulogu i kroz nasleđivanje (inheritance). Nekoliko atributa stranice, kao što su /Resources, /MediaBox, /CropBox i /Rotate, mogu se postaviti na unutrašnji čvor i izostaviti sa pojedinačnih stranica, koje potom nasleđuju vrednost najbližeg pretka. Postavite /MediaBox jednom na koren i svaki list dobija istu veličinu stranice bez ponavljanja; stranica koja treba da se razlikuje deklariše sopstvene vrednosti. Ovo je jedino mesto u modelu objekata gde značenje vrednosti zavisi od pozicije objekta u stablu, a ne samo od njegovog sopstvenog sadržaja.

Šta stranica-list zapravo sadrži

Objekat stranice je tačka spajanja između strukturnog modela i vidljivog sadržaja. Njegov unos /Contents referencira jedan ili više tokova sadržaja, odnosno operatore crtanja koji oslikavaju tekst i grafiku na stranici. Njegov rečnik /Resources imenuje fontove, slike i prostore boja na koje se ti operatori oslanjaju, pri čemu je svaki unos indirektna referenca na objekat koji se deli među stranicama. Unos /MediaBox daje pravougaonik stranice u tačkama (1/72 inča), dok unosi kao što su /Rotate i /CropBox podešavaju način na koji se ona prikazuje.

Ta podela posla predstavlja ceo model u malom. Rečnik stranice je struktura: tipizirani unosi i reference koji govore šta je stranica i čime crta. Tok sadržaja (content stream) čine instrukcije: poseban, kompresibilan blok koji govori kako crtati. Font iza oznake /F1 je deljeni resurs, definisan jednom i usmeren svuda gde se koristi. Rečnik, tok i referenca sarađuju na iscrtavanju jedne stranice, a isti obrasci se primenjuju i na ceo dokument. Operatori toka sadržaja unutar tog bloka pokriveni su odvojeno za tekst i fontove i za grafiku i vizuelne elemente.

Zašto vredi poznavati ovaj model

Većina programera se susreće sa modelom objekata tek kada nešto otkaže: stranica se iscrtava kao prazna jer referenca /Contents ne ukazuje na važeći objekat, tekst se prikazuje kao prazni kvadratići jer resurs fonta nikada nije ugrađen, ili alat prijavljuje /Count koji se ne podudara sa stranicama koje može pronaći. Svaka od tih situacija je tvrdnja o grafu, a direktno čitanje grafa je daleko bolje od pogađanja. Osam tipova i pravilo o referenciranju čine dovoljno mali rečnik koji možete držati u glavi, i kada jednom vidite PDF kao niz objekata koji ukazuju jedni na druge, neispravne datoteke prestaju da budu neprozirne.

Ipak, ručno pisanje modela je retko kada pravi izbor izvan procesa učenja. Održavanje ofseta unakrsnih referenci, brojeva generacija, brojača stabla stranica i dužina tokova usaglašenim tokom uređivanja je vrsta administracije koju biblioteka treba da vodi. U produkciji, zrela biblioteka za PDF razvoj upravlja grafom objekata dok vama ostavlja da razmišljate o stranicama i sadržaju. Poznavanje modela se i dalje isplati: razumete šta biblioteka gradi u pozadini, i zašto.