A PDF-fájl a lényegét tekintve (at heart) egymásra mutató objektumok gyűjteménye. Vegyük el a tömörítést, a kereszthivatkozás-könyvelést (cross-reference bookkeeping) és a bájt eltolásokat (byte offsets), és ami megmarad, az egy gráf: típusos értékek kis halmaza, amelyeket hivatkozások (references) kötnek össze, és egyetlen objektumban gyökereznek, amelyet az olvasó (reader) tudja, hogyan kell megtalálni. Minden, amit egy PDF ki tud fejezni, egy bekezdésnyi szövegtől kezdve a beágyazott betűtípuson át a digitális aláírásig, nyolc primitív objektum típusból és abból a szabályból épül fel, amely lehetővé teszi, hogy az egyik objektum hivatkozzon a másikra. Tanulja meg ezeket, és a formátum többi része rejtély helyett (rather than mystery) kompozícióként olvasható
Ez a PDF logikai rétege (logical layer), amely az ISO 32000-1 7.3-as klauzulájában van meghatározva, és egy szinttel a fizikai fájl elrendezés felett helyezkedik el (a fejléc, a törzs, a kereszthivatkozási tábla és a trailer, amely saját témája a PDF fájlstruktúra műszaki áttekintésének). A logikai modell az, amit ezek a bájtok az elemzés (parsed) után jelentenek. Egy megjelenítő (viewer) visszafelé olvassa a fájlt, hogy megtalálja a trailert, követi azt a gyökérig, és onnan a dokumentum objektumokra hivatkozó objektumokként (objects referencing objects) bontakozik ki. Ez az a rész, amiről gondolkodik (reason about), amikor egy hibás formátumú oldalt hibakeres (debug), egy elemzőt ír, vagy rábíz egy könyvtárra, hogy összeállítson egy dokumentumot
Nyolc objektum típus, és semmi más
A PDF pontosan nyolc alapvető objektum típust határoz meg. A dokumentumban lévő minden érték ezek egyike, és ez az, ami a formátumot kezelhetővé (tractable) teszi a kiterjedtsége ellenére
A Logikai értékek (Booleans) a true és false kulcsszavak. Zászlókat (flags) kapcsolnak be és ki, például, hogy egy megjegyzés (annotation) kinyomtatásra kerül-e
A Számok (Numbers) kétféle változatban léteznek, amelyeket a specifikáció egy típusként kezel: egész számok, mint a 42, és valós számok, mint a 3.14 vagy -0.002. A PDF-ben nincs exponenciális jelölés, így soha nem fog 1e6-ot látni egy megfelelő fájlban. A koordináták, a betűméretek és a forgatási szögek mind számok
A Karakterláncok (Strings) bájtok sorozatait tartalmazzák, zárójelekbe, (Hello), vagy hegyes zárójelekbe írva hexadecimálisan, <48656C6C6F>. Mindkét jelölés azonos tartalmat kódol; a hexadecimális (hex) egy kiskapu (escape hatch) a zárójelekben kínos (awkward) bájtok számára. A karakterláncok szöveget hordoznak, de elsősorban bájtok, ami abban a pillanatban számít, amikor bármit kezel az ASCII-n túl
A Nevek (Names) perjel (slash) által bevezetett atomi tokenek: /Type, /Pages, /MediaBox. A név nem karakterlánc; ez egy azonosító (identifier), amelyet szótár kulcsként vagy felsorolásos értékként (enumerated value) használnak, és két név csak akkor egyenlő, ha bájtról bájtra egyeznek. A perjel szintaxis, nem a név része. Ez megbotránkoztatja az újoncokat, akik a /Times-Roman-t és a (Times-Roman) karakterláncot felcserélhetőként (interchangeable) kezelik; a formátum nem teszi
A Tömbök (Arrays) rendezett, heterogén listák szögletes zárójelben: a [0 0 612 792] egy oldal téglalap, és egy tömb szabadon keverheti a típusokat, beleértve a más objektumokra mutató hivatkozásokat is. A Szótárak (Dictionaries) a munkalovak (workhorse). A << és >> közé írt szótár a név kulcsokat tetszőleges típusú értékekhez rendeli, és a PDF szinte minden értelmes struktúrája (oldal, katalógus, betűtípus, megjegyzés) egy szótár, amelynek egy /Type kulcsa deklarálja (declaring), hogy mi az
A Folyamok (Streams) olyan szótárak, amelyek a stream és az endstream kulcsszavak közötti nyers bájtok "farkával" (tail of raw bytes) rendelkeznek. A szótár leírja a bájtokat (a hosszukat és az olyan szűrőket, mint a FlateDecode, amelyek tömörítik őket), a bájtok pedig hordozzák a terjedelmes hasznos terhet (bulky payload): oldal tartalom utasítások, beágyazott betűtípus-programok, képek. A folyam az a hely, ahová a PDF mindazt teszi, ami túl nagy vagy túl bináris ahhoz, hogy soron belül (inline) üljön
A nyolcadik típus a Null objektum (null object), a null kulcsszó. Ez egy valós érték, amely különbözik a kulcs hiányától. Egy null-ra beállított szótárbejegyzést úgy kezelünk, mintha nem lenne jelen, és egy nem létező objektumra feloldódó (resolves) hivatkozás is hiba helyett null-t ad. Ez a megbocsátó viselkedés (forgiving behavior) szándékos: hagyja, hogy egy sérült fájl leromoljon (degrade) ahelyett, hogy megtagadná a megnyitást. Nincs kilencedik típus; minden, amit a PDF kifejez, abból fakad, hogyan kombinálódik ez a nyolc
Közvetlen értékek, indirekt objektumok és hivatkozások
E nyolc típus bármelyike kétféleképpen jelenhet meg. Egy közvetlen (direct) objektumot a helyére írnak, mint a 612 egy MediaBox tömbön belül. Egy indirekt (indirect) objektum identitást kap, hogy más objektumok mutathassanak rá: két egész számot, egy objektum számot (object number) és egy generációs számot (generation number), a definíciót obj és endobj-be csomagolva:
12 0 obj
<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>
endobj
Ez a 12. objektum, 0. generáció, egy betűtípus-szótár (font dictionary). Bárhol máshol a fájlban egy másik objektum egy indirekt hivatkozással utal rá: ugyanaz a két szám, amelyet az R kulcsszó követ, 12 0 R. A hivatkozás egy mutató (pointer). Amikor egy oldal erőforrás szótára azt mondja: /Font << /F1 12 0 R >>, a 12. objektumot nevezi meg a /F1 erőforrás neve mögötti betűtípusként anélkül, hogy a betűtípus definícióját bemásolná az oldalba
A generációs szám (generation number) a törlések és az újrafelhasználás céljából létezik. Amikor egy objektumot felszabadítanak (freed) és a helyét újra felhasználják, a generáció növekszik (increments), így egy elavult (stale) 12 0 R nem tud feloldódni a 12. hely új bérlőjére (new tenant). A frissen írt fájlok szinte mindegyike a 0. generáció, de egy erősen szerkesztett fájl magasabb számokat hordozhat, és egy olyan elemző (parser), amely figyelmen kívül hagyja a generációt, végül rossz objektumot fog olvasni
Az indirekció (indirection) az, ami a PDF-et hatékonnyá és szerkeszthetővé teszi. Egy betűtípus, kép vagy színtér egyszer definiálható (defined once), és száz oldalról hivatkozható. Egy apró változtatás új revízióként (new revision) hozzáfűzhető, amely egyetlen objektumot helyettesít, ahelyett, hogy a fájlt újraírná. A kereszthivatkozási tábla az az index, amely egy objektum számot bájt eltolássá (byte offset) alakít, így az olvasó pásztázás (scanning) nélkül ugrik egyenesen a 12 0 obj-ra, de ez fizikai optimalizálás. Logikailag mindössze annyit kell tudnia, hogy a 12 0 R azt jelenti: "a 12 0-ként azonosított objektum"
A katalógus: ahol minden dokumentum kezdődik
A hivatkozások feloldásának valahol el kell kezdődnie, és ez a valahol a trailer /Root bejegyzése, amely a dokumentum katalógusra (document catalog) mutat: az objektumgráf gyökerére, egy /Type /Catalog-gal rendelkező szótárra. Az olvasó ezt éri el először, mert a trailer található meg először, és onnan a dokumentum minden más része elérhető a hivatkozások követésével
A katalógus (catalog) csak két szigorúan kötelező bejegyzést hordoz: a /Type-ot (típusát) és a /Pages-t (oldalakat), amely egy indirekt hivatkozás az oldalfa (page tree) gyökerére. A többi opcionális, és a dokumentum szintű viselkedést írja le a tartalom helyett: az /Outlines a könyvjelző fára mutat, a /Names karakterlánccal kulcsolt név fákat (name trees) tartalmaz, a /Metadata egy XMP metaadat-folyamra hivatkozik, a /PageMode és a /PageLayout pedig azt javasolja (suggest), hogyan kellene egy megjelenítőnek (viewer) megnyitnia a dokumentumot. Ezek egyike sem szükséges egy oldal megjelenítéséhez; konfigurálják (configure) az oldalak körüli élményt. A katalóguson logó (hanging off) könyvjelző-, metaadat- és megjegyzés-struktúrákkal (annotation structures) a PDF metaadatokról, könyvjelzőkről és megjegyzésekről szóló cikk foglalkozik
Az alábbi diagram megmutatja, hol helyezkedik el az objektum törzse a környező fájlban. A katalógus és az oldalfa ezen a törzsön belül él, mint közönséges indirekt objektumok; a körülöttük lévő fejléc, kereszthivatkozási tábla és trailer (scaffolding) az a fizikai állványzat (scaffolding), amely lehetővé teszi, hogy az olvasó megtalálja őket

Az oldalfa: az oldalak kiegyensúlyozott hierarchiája
A /Pages-ből (oldalakból) a dokumentum az oldalfába ágazik (branches), ahol a PDF gráfnak egy lapos listával szembeni (over a flat list) választása kifizetődik. Az oldalakat nem egyszerű szekvenciaként (simple sequence) tárolják; egy fáról lógnak, amelynek belső csomópontjai (interior nodes) az oldalfa csomópontok (page tree nodes) (/Type /Pages), és amelynek levelei az oldal objektumok (page objects) (/Type /Page). Egy belső csomópont a gyermekeit (children) egy /Kids tömbben sorolja fel, és a /Count-ban rögzíti, hogy hány levél oldal (leaf pages) él alatta. A gyökér (root) kivételével minden csomópont egy /Parent (szülő) hivatkozást hordoz visszafelé (back up), így a fa mindkét irányba járható (walks in either direction)
2 0 obj % az oldalfa gyökere
<< /Type /Pages /Kids [3 0 R 4 0 R] /Count 3 >>
endobj
3 0 obj % egy levél oldal
<< /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 % egy belső csomópont, amely további két oldalt csoportosít
<< /Type /Pages /Parent 2 0 R /Kids [6 0 R 7 0 R] /Count 2 >>
endobj
Itt a 2. objektum a gyökér, alatta három oldallal: a 3. levél oldal, valamint további kettő, amely a 4. belső csomóponton keresztül érhető el. A gyökér 3-as /Count értékének meg kell egyeznie az alatta lévő összes levéllel, és egy olyan számlálás, amely nem egyezik meg a tényleges struktúrával, az egyik gyakori módja (common way) annak, ahogy egy kézzel szerkesztett fájl elromlik (goes wrong). A fa lényege a hozzáférés lokalitása (locality of access). Egy ezer oldalas dokumentum 900. oldalát megnyitó olvasó nem jár végig (walk) 900 objektumot; leszáll (descends) egy maroknyi csomóponton, mert egy jól formázott (well-formed) fa sekély (shallow) és kiegyensúlyozott marad. Egy ilyen fa kézzel történő felépítése elég pepecs (fiddly) ahhoz, hogy érdemes legyen elejétől a végéig (end to end) látni, amit a PDF dokumentum nulláról történő felépítéséről szóló bemutató végigvesz (does)
A fa második fenntartását (second keep) az öröklődésen (inheritance) keresztül keresi meg. Egy maroknyi oldal-attribútum (page attributes), a /Resources, a /MediaBox, a /CropBox és a /Rotate beállítható egy belső csomóponton (interior node), és lehagyható (left off) az egyes oldalakról, amelyek aztán a legközelebbi ős (ancestor) értékét öröklik (inherit). Állítsa be egyszer a /MediaBox-ot a gyökéren, és minden levél (leaf) ugyanazt az oldalméretet (page size) kapja meg anélkül, hogy megismételné azt; egy olyan oldal, amelynek el kell térnie, deklarálja (declares) a sajátját. Ez az egyetlen hely (one place) az objektummodellben, ahol egy érték jelentése egy objektum fában elfoglalt helyzetétől függ, nem csak a saját tartalmától
Mit is tartalmaz valójában egy levél oldal (leaf page)
Egy oldal objektum a csatlakozási pont (join point) a strukturális modell és a látható tartalom között. A /Contents bejegyzése (entry) egy vagy több tartalomfolyamra (content streams) hivatkozik, a rajzoló operátorokra (drawing operators), amelyek szöveget és grafikákat festenek (paint) az oldalra. A /Resources (erőforrások) szótára megnevezi a betűtípusokat, a képeket és a színtereket (color spaces), amelyekre ezek az operátorok támaszkodnak, minden bejegyzés egy indirekt hivatkozás egy oldalak között megosztott (shared across pages) objektumra. A /MediaBox megadja az oldal téglalapját (page rectangle) pontokban (1/72 hüvelyk), és az olyan bejegyzések, mint a /Rotate (forgatás) és a /CropBox (vágódoboz) módosítják annak megjelenítését
Ez a munkamegosztás (division of labor) az egész modell kicsinyítve (in miniature). Az oldal szótár a struktúra (structure): típusos bejegyzések és hivatkozások (typed entries and references), amelyek megmondják, hogy mi az oldal, és mivel rajzol (draws with). A tartalomfolyam utasítások (instructions): egy különálló, tömöríthető paca (blob), amely megmondja, hogyan kell rajzolni. A /F1 mögötti betűtípus egy megosztott erőforrás (shared resource), amelyet egyszer definiálnak, és bárhol rámutatnak (pointed at), ahol használják. A szótár, a folyam (stream) és a hivatkozás (reference) együttműködik egyetlen oldal megjelenítésében, és ugyanezek a minták skálázódnak a teljes dokumentumra (scale to the whole document). Az ebben a pacában (blob) lévő tartalomfolyam-operátorokat külön tárgyaljuk (covered separately) a szövegről és a betűtípusokról, valamint a grafikákról és a vizuális elemekről szóló cikkben
Miért érdemes ismerni ezt a modellt
A legtöbb fejlesztő csak akkor találkozik az objektummodellel, amikor valami eltörik (breaks): egy oldal üresen jelenik meg (renders blank), mert a /Contents hivatkozása lóg (dangles), a szöveg dobozként (boxes) jelenik meg, mert egy betűtípus-erőforrás sosem lett beágyazva (never embedded), egy eszköz (tool) olyan /Count-ot jelent (reports), amely nem egyezik meg a megtalálható (can find) oldalakkal. Ezek mindegyike egy állítás (statement) a gráfról, és a gráf közvetlen olvasása (reading directly) felülmúlja a találgatást (beats guessing). A nyolc típus és a hivatkozási szabály elég kis szókincs (vocabulary) ahhoz, hogy a fejében tartsa, és ha egy PDF-et objektumokra mutató objektumokként lát, a hibás formátumú (malformed) fájlok többé nem lesznek átláthatatlanok (opaque)
Ettől függetlenül (That said) a modell kézi (by hand) írása a tanuláson túl ritkán a megfelelő döntés. A kereszthivatkozás-eltolások, a generációs számok, az oldalfa-számlálások és a folyamhosszok konzisztens (consistent) tartása a szerkesztések (edits) során olyan könyvelés, amelynek kezelésére a könyvtár létezik. Élesben (In production) egy érett (mature) PDF fejlesztői könyvtár kezeli az objektum gráfot, miközben önnek hagyja (leaving you), hogy oldalakban és tartalmakban gondolkodjon. A modell ismerete még mindig kifizetődik (pays off): megérti, hogy mit épít fel a könyvtár alatta (underneath), és miért