Techninis straipsnis

PDF loginis objektų modelis: tipai, nuorodos ir struktūra

Iš esmės PDF failas yra vienas į kitą nurodančių objektų rinkinys. Atmetus suspaudimą, kryžminių nuorodų apskaitą ir baitų poslinkius, lieka grafas – nedidelis tipizuotų reikšmių rinkinys, sujungtas nuorodomis, kurio pradžia yra viename objekte, kurį skaitytuvas moka surasti. Viskas, ką gali išreikšti PDF (nuo teksto pastraipos iki įterpto šrifto ar skaitmeninio parašo), yra sukurta iš aštuonių primityvių objektų tipų ir taisyklės, leidžiančios vienam objektui nurodyti kitą. Išmokite juos, ir likęs formatas taps dėsningu deriniu, o ne paslaptimi

Tai yra loginis PDF sluoksnis, apibrėžtas standarto ISO 32000-1 7.3 skyriuje, ir jis yra vienu lygiu aukščiau nei fizinis failo išdėstymas (antraštė, kūnas, kryžminių nuorodų lentelė ir pabaigos blokas, kuris atskirai aptariamas techninėje PDF failo struktūros apžvalgoje). Loginis modelis yra tai, ką šie baitai reiškia juos išanalizavus. Skaitytuvas nuskaito failą iš galo, kad surastų trailer dalį, eina pagal ją į šaknį, ir iš ten dokumentas atsiveria kaip vienas kitą nurodantys objektai. Tai yra dalis, apie kurią galvojate, kai šalinate neteisingo puslapio klaidas, rašote analizatorių arba patikite bibliotekai surinkti dokumentą

Aštuoni objektų tipai ir nieko daugiau

PDF apibrėžia tiksliai aštuonis bazinius objektų tipus. Kiekviena dokumento reikšmė yra viena iš jų, ir tai leidžia formatui išlikti valdomam, nepaisant jo apimties

Loginės reikšmės (Booleans) yra raktiniai žodžiai true ir false. Jos įjungia ir išjungia tam tikras žymas, pavyzdžiui, ar anotacija turėtų būti spausdinama

Skaičiai (Numbers) būna dviejų rūšių, kurias specifikacija laiko vienu tipu: sveikieji skaičiai (pavyzdžiui, 42) ir realieji skaičiai (pavyzdžiui, 3.14 arba -0.002). PDF nenaudoja eksponentinio žymėjimo, žodžiu, standarto reikalavimus atitinkančiame faile niekada nepamatysite įrašo 1e6. Koordinatės, šriftų dydžiai ir posūkio kampai – visi yra skaičiai

Eilutės (Strings) saugo baitų sekas, kurios užrašomos arba skliausteliuose (Hello), arba kampiniuose skliaustuose kaip šešioliktainės reikšmės <48656C6C6F>. Abu užrašymo būdai koduoja tą patį turinį; šešioliktainis formatas yra išeitis baitams, kuriuos nepatogu rašyti skliausteliuose. Eilutės neša tekstą, tačiau pirmiausia jos yra baitai, o tai tampa svarbu, kai dirbama su koduotėmis už ASCII ribų

Pavadinimai (Names) yra atominiai ženklai, prasidedantys pasvirusiu brūkšniu: /Type, /Pages, /MediaBox. Pavadinimas nėra eilutė; tai identifikatorius, naudojamas kaip žodyno raktas arba išvardyta reikšmė, ir du pavadinimai yra lygūs tik tuo atveju, jei sutampa baitas po baito. Pasviręs brūkšnys yra tik sintaksė, o ne paties pavadinimo dalis. Tai dažnai suklaidina naujokus, kurie mano, kad /Times-Roman ir eilutė (Times-Roman) yra pakeičiami vienas kitu; formatas jų taip netraktuoja

Masyvai (Arrays) yra tvarkingi, įvairialypiai sąrašai laužtiniuose skliaustuose: [0 0 612 792] yra puslapio stačiakampis, o masyvas gali laisvai maišyti tipus, įskaitant nuorodas į kitus objektus. Žodynai (Dictionaries) yra pagrindinis darbo įrankis. Užrašomi tarp << ir >>, žodynai susieja pavadinimų raktų su bet kokio tipo reikšmėmis. Beveik kiekviena prasminga PDF struktūra (puslapis, katalogas, šriftas, anotacija) yra žodynas su raktu /Type, nurodančiu, kas tai yra

Srautai (Streams) yra žodynai, po kurių seka neapdorotų baitų uodega tarp raktinių žodžių stream ir endstream. Žodynas aprašo baitus (jų ilgį ir bet kokius filtrus, pavyzdžiui, FlateDecode, kurie juos suspaudžia), o patys baitai neša pagrindinį svorį: puslapio turinio instrukcijas, įterptų šriftų programas, paveikslėlius. Srautas yra vieta, kur PDF patalpina viską, kas yra per didelio dydžio arba per daug dvejetainio formato, kad tilptų tiesiogiai tekste

Aštuntasis tipas yra tuščiasis objektas (null object) – raktinis žodis null. Tai yra tikra reikšmė, besiskirianti nuo to atvejo, kai rakto tiesiog nėra. Žodyno įrašas, kurio reikšmė nustatyta į null, traktuojamas taip, tarsi jo nebūtų, o nuoroda, rodanti į neegzistuojantį objektą, taip pat grąžina null, o ne klaidą. Toks tolerantiškas elgesys yra sąmoningas: jis leidžia apgadintam failui veikti ribotu režimu, užuot visiškai atsisakius atsidaryti. Devintojo tipo nėra; viskas, ką išreiškia PDF, atsiranda derinant šiuos aštuonis tipus

Tiesioginės reikšmės, netiesioginiai objektai ir nuorodos

Bet kuris iš šių aštuonių tipų gali būti pateikiamas dviem būdais. Tiesioginis (direct) objektas užrašomas vietoje, pavyzdžiui, 612 MediaBox masyve. Netiesioginiam (indirect) objektui suteikiama tapatybė, kad kiti objektai galėtų į jį nurodyti: du sveikieji skaičiai (objekto numeris ir kartos numeris), o pats apibrėžimas apgaubiamas raktiniais žodžiais obj ir endobj:

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

Tai yra 12 objektas, 0 karta, šrifto žodynas. Bet kurioje kitoje failo vietoje kitas objektas nurodo į jį naudodamas netiesioginę nuorodą (indirect reference) – tuos pačius du skaičius ir raktinį žodį R: 12 0 R. Ši nuoroda veikia kaip rodyklė. Kai puslapio išteklių žodyne parašyta /Font << /F1 12 0 R >>, ji nurodo 12 objektą kaip šriftą už ištekliaus pavadinimo /F1, nekopijuojant šrifto apibrėžimo į patį puslapį

Kartos numeris egzistuoja objektų šalinimui ir pakartotiniam naudojimui. Kai objektas yra pašalinamas ir jo vieta panaudojama vėl, kartos numeris padidinamas, kad pasenusi nuoroda 12 0 R negalėtų nurodyti į naują 12-os vietos gyventoją. Naujai sugeneruoti failai beveik visada naudoja 0 kartą, tačiau dažnai redaguotas failas gali turėti didesnius skaičius, o analizatorius, kuris ignoruoja kartos numerį, galiausiai perskaitys neteisingą objektą

Netiesioginis nurodymas yra tai, kas daro PDF efektyvų ir redaguojamą. Vienas šriftas, paveikslėlis ar spalvų erdvė gali būti aprašyta vieną kartą ir naudojama šimte puslapių. Nedidelis pakeitimas gali būti pridėtas kaip nauja revizija, pakeičianti tik vieną objektą, užuot perrašius visą failą. Kryžminių nuorodų lentelė yra indeksas, kuris objekto numerį paverčia poslinkiu baitais, todėl skaitytuvas iškart šoka prie 12 0 obj, bet tai tik fizinė optimizacija. Logiškai jums tereikia žinoti, kad 12 0 R reiškia „objektą, identifikuojamą kaip 12 0“

Katalogas: kur prasideda kiekvienas dokumentas

Nuorodų sprendimas turi kažkur prasidėti, ir ta pradžia yra pabaigos bloko (trailer) įrašas /Root, kuris nurodo į dokumentų katalogą (document catalog) – objektų grafo šaknį, žodyną su reikšme /Type /Catalog. Skaitytuvas jį pasiekia pirmiausia, nes pabaigos blokas yra surandamas pirmiausia, o iš jo sekančiomis nuorodomis pasiekiama bet kuri kita dokumento dalis

Katalogas turi tik du griežtai privalomus įrašus: /Type ir /Pages (netiesioginę nuorodą į puslapių medžio šaknį). Visi kiti įrašai yra neprivalomi ir aprašo bendrą dokumento elgseną, o ne turinį: /Outlines nurodo į žymių (bookmarks) medį, /Names laiko pavadinimų medžius, /Metadata nurodo į XMP metaduomenų srautą, o /PageMode bei /PageLayout siūlo, kaip skaitytuvas turėtų atidaryti dokumentą. Nė vienas iš šių įrašų nėra reikalingas puslapio atvaizdavimui; jie tik konfigurioja aplinką aplink puslapius. Žymių, metaduomenų bei anotacijų struktūros, kabančios nuo katalogo, nagrinėjamos straipsnyje apie PDF metaduomenis, žymes ir anotacijas

Žemiau pateikta diagrama rodo, kur objektų kūnas yra pačiame faile. Katalogas ir puslapių medis gyvena šiame kūne kaip įprasti netiesioginiai objektai; antraštė, kryžminių nuorodų lentelė ir pabaigos blokas aplink juos yra fiziniai pastoliai, leidžiantys skaitytuvui juos surasti

Diagrama, rodanti keturias fizines PDF failo dalis: versijos antraštę, kūną su dokumento objektais (įskaitant katalogą ir puslapių medį), kryžminių nuorodų lentelę su objektų poslinkiais ir pabaigos bloką (trailer), nurodantį į šaknį

Puslapių medis: subalansuota puslapių hierarchija

Nuo nuorodos /Pages dokumentas šakojasi į puslapių medį, kur pasiteisina PDF sprendimas naudoti grafą, o ne plokščią sąrašą. Puslapiai nėra saugomi kaip paprasta seka; jie kabo medyje, kurio vidiniai mazgai yra puslapių medžio mazgai (/Type /Pages) ir kurio lapai – puslapių objektai (/Type /Page). Vidinis mazgas išvardija savo vaikus /Kids masyve ir nurodo lauke /Count, kiek puslapių lapų yra po juo. Kiekvienas mazgas, išskyrus šaknį, turi /Parent nuorodą atgal, todėl medis gali būti pereinamas bet kuria kryptimi

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

Čia 2 objektas yra šaknis su trimis puslapiais po juo: lapas 3 bei dar du pasiekiami per vidinį mazgą 4. Šaknies /Count reikšmė 3 privalo sutapti su bendru puslapių skaičiumi po juo. Šio skaičiaus neatitikimas realiai struktūrai yra dažna rankiniu būdu redaguojamų failų klaida. Medžio tikslas yra prieigos greitis. Skaitytuvas, atidarydamas tūkstančio puslapių dokumento 900-ąjį puslapį, neina per 900 objektų; jis nusileidžia per kelis mazgus, nes teisingai suformuotas medis išlieka negilus ir subalansuotas. Tokio medžio kūrimas rankomis yra pakankamai sudėtingas, kad jį būtų verta pamatyti nuo pradžios iki galo, kaip tai daroma vadove apie PDF dokumento kūrimą nuo nulio

Medis atveria antrą privalumą per paveldėjimą. Keletas puslapio atributų (/Resources, /MediaBox, /CropBox ir /Rotate) gali būti nustatyti vidiniame mazge ir praleisti atskiruose puslapiuose, kurie tada paveldi artimiausio protėvio reikšmę. Nustatykite /MediaBox vieną kartą šaknyje, ir kiekvienas puslapis gaus tą patį dydį be jo kartojimo; puslapis, kurio dydis skiriasi, deklaruoja savo reikšmę. Tai vienintelė vieta objektų modelyje, kur reikšmės prasmė prikalauso nuo objekto pozicijos medyje, o ne tik nuo jo paties turinio

Ką iš tikrųjų turi puslapio lapas

Puslapio objektas yra jungiamasis taškas tarp struktūrinio modelio ir matomo turinio. Jo įrašas /Contents nurodo į vieną ar kelis turinio srautus (content streams) – piešimo operatorius, kurie išdėsto tekstą ir grafiką puslapyje. Jo /Resources (išteklių) žodynas įvardija šriftus, paveikslėlius ir spalvų erdves, kuriais remiasi šie operatoriai, kur kiekvienas įrašas yra netiesioginė nuoroda į objektą, bendrai naudojamą keliuose puslapiuose. /MediaBox nustato puslapio stačiakampį taškais (1/72 colio dalimis), o tokie įrašai kaip /Rotate ir /CropBox suderina jo pateikimą

Šis atsakomybių padalijimas yra visas modelis miniatiūroje. Puslapio žodynas yra struktūra: tipizuoti įrašai ir nuorodos, sakantys, kas yra puslapis ir su kuo jis piešia. Turinio srautas yra instrukcijos: atskiras, suspaudžiamas dvejetainis blokas, sakantis, kaip piešti. Šriftas už /F1 yra bendras išteklius, aprašytas vieną kartą ir naudojamas ten, kur reikia. Žodynas, srautas ir nuoroda veikia kartu, kad atvaizduotų vieną puslapį, o tas pats modelis pritaikomas visam dokumentui. Srauto operatoriai atskirai aprašomi straipsniuose apie tekstą bei šriftus ir apie grafiką bei vizualinius elementus

Kodėl verta žinoti šį modelį

Dauguma programuotojų su objektų modeliu susiduria tik tada, kai kas nors sugenda: puslapis atvaizduojamas tuščias, nes jo nuoroda /Contents yra pažeista; tekstas rodomas kaip tušti kvadratėliai, nes šrifto išteklius nebuvo įterptas; arba įrankis praneša apie /Count skaičių, kuris nesutampa su surandamais puslapiais. Kiekvienas iš šių atvejų yra teiginys apie objektų grafą, o tiesioginis grafo perskaitymas yra daug pranašesnis už spėliojimą. Aštuoni tipai ir nuorodų taisyklė sudaro nedidelį žodyną, kurį lengva prisiminti, o kai pamatysite PDF kaip objektus, rodančius į kitus objektus, sugadinti failai nustos būti nepermatomi

Kita vertus, objektų rašymas ranka, išskyrus mokymąsi, gamyboje retai pasiteisina. Kryžminių nuorodų poslinkių, kartos numerių, puslapių medžio skaitiklių ir srautų ilgių derinimas redagavimo metu yra ta apskaita, kuria pasirūpinti privalo biblioteka. Gamybiniame darbe brandi PDF programavimo biblioteka valdo objektų grafą, leisdama jums galvoti tik apie puslapius bei turinį. Tačiau žinoti modelį vis tiek apsimoka: taip geriau suprantate, ką biblioteka sukuria po kapotu ir kodėl