Egy PDF 1. oldala nem az 1-es objektum. Ez a különbség a leggyakoribb forrása a rossz oldal kinyerésével kapcsolatos hibáknak a PDF-értelmezőkben, és a megoldás a specifikáció elolvasása a fájlbájtok helyett
Objektumok, hivatkozások és a katalógus
Egy PDF fájl számozott objektumok gyűjteménye. Mindegyik egyedi objektumszámot és generációs számot hordoz, N G obj formátumban írva, ahol a G szinte mindig 0 azokban a fájlokban, amelyeket nem frissítettek növekményesen. Az objektumok az N G R jelöléssel hivatkoznak egymásra, így a 3 0 R azt jelenti: "a 3-as objektum aktuális verziója". A trailer egy gyökérkatalógus objektumra mutat, amelynek /Pages bejegyzése az oldalfához vezet. Minden, ami navigálható egy PDF-ben, ebből a gyökérből indul ki, nem pedig a fájl törzsének első bájtjából
A kereszthivatkozási tábla (vagy a PDF 1.5+ verziójában a kereszthivatkozási adatfolyam) az objektumszámokat a fájl eltolásaihoz (offset) rendeli. Feladata a véletlenszerű hozzáférés, nem pedig a sorrendezés. Egy író, amely növekményesen épít fel egy dokumentumot, magasabb számokkal fűzhet új objektumokat a végére, miközben ezek az objektumok logikailag megelőzik a meglévőket az oldalak sorrendjében. Ez nem hiba; ez tervezési sajátosság
Az oldalfa (ISO 32000-1 §7.7.3)
Az oldalak sorrendje az oldalfában található. A gyökérkatalógus tartalmaz egy /Pages hivatkozást, amely egy /Pages típusú csomópontra mutat. Ennek a csomópontnak a /Kids tömbje olvasási sorrendben sorolja fel a gyermekeit. Minden gyermek vagy egy /Page típusú levélcsomópont, vagy egy másik köztes /Pages csomópont, amely tartalmazza a saját /Kids tömbjét. Az 1. oldal a Kids tömbök mélységi, balról jobbra történő bejárása során elért első levél. Minden köztes csomóponton a /Count bejegyzés gyorsítótárazza (cache) a leszármazott levéloldalak teljes számát, így egy megjelenítő ugorhat az 500. oldalra anélkül, hogy az egész fát bejárná
Így néz ki egy minimális háromoldalas fa nyers PDF szintaxissal:
16 0 obj
<<
/Type /Pages
/Count 3
/Kids [20 0 R 1 0 R 4 0 R]
/MediaBox [0 0 612 792]
>>
endobj
20 0 obj
<< /Type /Page /Parent 16 0 R /Contents 21 0 R /Resources 22 0 R >>
endobj
1 0 obj
<< /Type /Page /Parent 16 0 R /Contents 2 0 R /Resources 3 0 R >>
endobj
4 0 obj
<< /Type /Page /Parent 16 0 R /Contents 5 0 R /Resources 6 0 R >>
endobj
A Kids tömb így olvasható: [20 0 R, 1 0 R, 4 0 R]. A logikai 1. oldal a 20-as objektum, a logikai 2. oldal az 1-es objektum, a logikai 3. oldal a 4-es objektum. Bármely kód, amely az objektumszámokon 1-től felfelé iterál, 1, 4, 20 sorrendben találkozik velük, és a 2. oldal, 3. oldal, 1. oldal sorozatot fogja előállítani. Az eredményül kapott dokumentum összekevert sorrendben jelenik meg, ami teljesen normálisnak tűnhet egy olyan megjelenítőben, amely követi a fát, és katasztrofálisan rossznak egy olyanban, amely nem
Öröklődés
A köztes csomópontok hordozhatnak olyan tulajdonságokat, amelyeket a leszármazottaik örökölnek. A leggyakoribb örökölt bejegyzések a /MediaBox (oldalméretek), /CropBox, /Resources (betűtípusok és képek) és a /Rotate. Egy levéloldal, amelyből hiányzik a /MediaBox, nem hibás; felveszi az értéket a legközelebbi ős csomóponttól, amely definiálja azt. Egy olyan oldal, amely definiálja a /MediaBox értéket, felülírja azt, amit a szülő mond, de csak arra az egy oldalra vonatkozóan
Ez az elemzés (parsing) szempontjából számít. Ha egy /Page objektumot elszigetelten olvasunk, és feltételezzük, hogy tulajdonságai teljesek, téves méreteket fogunk jelenteni minden olyan oldalnál, amely az öröklődésre támaszkodik. Egy helyesen működő olvasó végigmegy a /Parent láncon, összegyűjtve a még nem látott tulajdonságokat, és a gyökérnél megáll
Beágyazott fák
A specifikációban semmi sem korlátozza a fát egyetlen szintre. Egy nagy dokumentum csoportosíthatja az oldalakat olyan köztes csomópontok alá, amelyek lazán megfelelnek a fejezeteknek:
2 0 obj % root Pages node, Count = 8
<< /Type /Pages /Count 8 /Kids [3 0 R 4 0 R] >>
endobj
3 0 obj % first chapter, 5 pages
<< /Type /Pages /Parent 2 0 R /Count 5
/Kids [10 0 R 11 0 R 12 0 R 13 0 R 14 0 R]
/MediaBox [0 0 612 792] >>
endobj
4 0 obj % second chapter, 3 pages
<< /Type /Pages /Parent 2 0 R /Count 3
/Kids [20 0 R 21 0 R 22 0 R]
/MediaBox [0 0 612 792] >>
endobj
A bejárási algoritmus ugyanaz: a Kids bejárása sorrendben, rekurzív belépés bármely /Pages csomópontba, és a /Page levélcsomópontok összegyűjtése. A /Count értékek lehetővé teszik a megjelenítő számára, hogy átugorjon egy teljes részfát, amikor egy azon túli oldalra ugrik; ezért kell ezeknek a számoknak pontosnak lenniük. Néhány PDF-szerkesztő az 1990-es évek végéről és a 2000-es évek elejéről nem számolta újra ezeket a helyben történő szerkesztések után, így egy védekező (defensive) értelmező ellenőrzi a /Count értékét a tényleges levélszámmal szemben, ahelyett, hogy megbízna benne a tömb allokációjakor
Hol merül fel ez a gyakorlatban
Az oldalsorrend-hiba leggyakrabban két forgatókönyvben jelentkezik. Az első egy egyéni értelmező, amely /Page típusú objektumokat keres a fa követése helyett. Megtalál minden oldalt, de objektumszám szerinti sorrendben, nem pedig olvasási sorrendben. A megoldás mindig ugyanaz: induljunk a trailerből, oldjuk fel a gyökérkatalógust, kövessük a /Pages bejegyzést, és járjuk be a Kids tömböket
A második forgatókönyv egy növekményes frissítésű (incremental-update) fájl. Amikor egy PDF-szerkesztő a teljes fájl újraírása nélkül fűz hozzá módosításokat, az új oldalobjektumok magas objektumszámokat kapnak, míg a logikai pozíciójukat továbbra is az eredeti fában lévő Kids tömb szabályozza. Egy oldal, amely eredetileg az 5-ös objektum volt, kicserélődik a 143-as új objektumra, de a Kids tömb most a 143-asra hivatkozik ott, ahol korábban az 5-ösre, így a logikai sorrend megmarad. Az objektumszám szerinti végighaladás a csereoldalt a sorozat rossz pozíciójába helyezné
A linearizált (web-optimalizált) PDF-ek egy harmadik variációt adnak hozzá: a fájl fizikailag úgy van átrendezve, hogy az első oldal tartalma a fájl eleje közelében jelenjen meg a lassú kapcsolaton keresztüli gyors megjelenítés érdekében. A sorrend szempontjából továbbra is az oldalfa szerkezete az irányadó, de a kereszthivatkozási tábla az átrendezett eltolásokra mutat. Egy olyan értelmező, amely a fájlpozícióra támaszkodik az xref tábla helyett, még egy linearizált fájl első oldalát is félre fogja olvasni
A HotPDF Component belsőleg kezeli az oldalfa bejárását, az öröklődés feloldását és a növekményes frissítésű xref egyesítést. A saját oldalobjektumaival való közvetlen munkavégzés azt jelenti, hogy a Kids-tömb szerinti sorrendezés már alkalmazva van; az oldalindexek logikai oldalakra mutatnak, nem objektumszámokra
A frissített oldalfa-rész a fan-outot teljesítménydöntésként, a belső csomópontok örökölt attribútumait, a flatteninget és a megtévesztő /Count értéket külön hibaforrásként kezeli