Soubor PDF je ve svém jádru sbírkou objektů, které na sebe navzájem odkazují. Odstraňte kompresi, účetnictví křížových odkazů a bytové offsety a to, co zůstane, je graf: malá množina typovaných hodnot, propojených pomocí odkazů, s kořenem v jediném objektu, který čtečka umí najít. Vše, co může PDF vyjádřit, od odstavce textu přes vložené písmo až po digitální podpis, je postaveno z osmi primitivních typů objektů a pravidla, které umožňuje jednomu objektu odkazovat na jiný. Naučte se je a zbytek formátu se bude číst spíše jako kompozice než tajemství
Toto je logická vrstva PDF, definovaná v ISO 32000-1 bod 7.3, a nachází se o úroveň výše nad fyzickým rozvržením souboru (hlavička, tělo, tabulka křížových odkazů a trailer, což je samostatné téma v technickém přehledu struktury souboru PDF). Logický model je to, co tyto bajty znamenají po parsování. Prohlížeč čte soubor pozpátku, aby našel trailer, sleduje jej ke kořeni a odtud se dokument rozvíjí jako objekty odkazující na objekty. Toto je část, o které uvažujete, když ladíte chybnou stránku, píšete parser nebo důvěřujete knihovně, že sestaví dokument
Osm typů objektů a nic jiného
PDF definuje přesně osm základních typů objektů. Každá hodnota v dokumentu je jednou z nich, což udržuje formát zvládnutelný navzdory jeho rozsahu
Booleovské hodnoty (Booleans) jsou klíčová slova true a false. Zapínají a vypínají příznaky, jako například zda se tiskne anotace
Čísla (Numbers) přicházejí ve dvou variantách, se kterými specifikace zachází jako s jedním typem: celá čísla jako 42 a reálná čísla jako 3.14 nebo -0.002. PDF nemá žádný zápis exponentu, takže v odpovídajícím souboru nikdy neuvidíte 1e6. Souřadnice, velikosti písma a úhly natočení jsou všechno čísla
Řetězce (Strings) obsahují sekvence bajtů, zapsané buď v závorkách, (Hello), nebo v lomených závorkách jako hexadecimální, <48656C6C6F>. Oba zápisy kódují identický obsah; hexadecimální je záchranná brzda pro bajty, které jsou uvnitř závorek nepraktické. Řetězce nesou text, ale jsou to především bajty, na čemž záleží v okamžiku, kdy zpracováváte cokoli mimo ASCII
Názvy (Names) jsou atomické tokeny uvedené lomítkem: /Type, /Pages, /MediaBox. Název není řetězec; je to identifikátor, který se používá jako klíč slovníku nebo výčtová hodnota, a dva názvy jsou si rovny, pouze pokud se shodují bajt po bajtu. Lomítko je syntaxe, nikoliv součást názvu. To zmate nováčky, kteří považují /Times-Roman a řetězec (Times-Roman) za zaměnitelné; formát nikoliv
Pole (Arrays) jsou uspořádané, heterogenní seznamy v hranatých závorkách: [0 0 612 792] je obdélník stránky a pole může volně kombinovat typy, včetně odkazů na jiné objekty. Slovníky (Dictionaries) jsou pracantem. Zapsaný mezi << a >>, slovník mapuje klíče názvů na hodnoty jakéhokoli typu a téměř každá smysluplná struktura v PDF, stránka, katalog, písmo, anotace, je slovník s klíčem /Type deklarujícím, co to je
Proudy (Streams) jsou slovníky s ohonem surových bajtů mezi klíčovými slovy stream a endstream. Slovník popisuje bajty (jejich délku a případné filtry, jako je FlateDecode, které je komprimují), a bajty nesou objemný náklad: instrukce pro obsah stránky, vložené programy písem, obrázky. Proud je místo, kam PDF ukládá cokoli, co je příliš velké nebo příliš binární na to, aby to bylo vloženo (inline)
Osmým typem je nulový objekt (null object), klíčové slovo null. Je to skutečná hodnota, odlišná od chybějícího klíče. S položkou slovníku nastavenou na null se zachází, jako by nebyla přítomna, a odkaz, který se překládá na neexistující objekt, také vrací null namísto chyby. Toto shovívavé chování je záměrné: umožňuje poškozenému souboru se degradovat, namísto toho, aby se odmítl otevřít. Neexistuje žádný devátý typ; vše, co PDF vyjadřuje, vychází z toho, jak se těchto osm kombinuje
Přímé hodnoty, nepřímé objekty a odkazy
Kterýkoli z těchto osmi typů se může objevit dvěma způsoby. Přímý objekt je zapsán na místě, jako 612 uvnitř pole MediaBox. Nepřímému objektu je přidělena identita, aby na něj mohly ukazovat další objekty: dvě celá čísla, číslo objektu a číslo generace, které obalují definici v obj a endobj:
12 0 obj
<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>
endobj
Toto je objekt 12, generace 0, slovník písma. Kdekoliv jinde v souboru na něj jiný objekt odkazuje nepřímým odkazem: stejnými dvěma čísly následovanými klíčovým slovem R, 12 0 R. Odkaz je ukazatel. Když slovník prostředků (resources) stránky říká /Font << /F1 12 0 R >>, pojmenuje objekt 12 jako písmo za názvem prostředku /F1, aniž by zkopíroval definici písma do stránky
Číslo generace existuje pro mazání a opětovné použití. Když je objekt uvolněn a jeho slot je znovu použit, generace se zvýší, takže zastaralý odkaz 12 0 R se nemůže přeložit na nového nájemce slotu 12. Čerstvě zapsané soubory jsou téměř všechny generace 0, ale silně upravovaný soubor může nést vyšší čísla a parser, který ignoruje generaci, nakonec načte špatný objekt
Nepřímé odkazy (Indirection) jsou to, co dělá PDF efektivním a upravitelným. Jedno písmo, obrázek nebo barevný prostor lze definovat jednou a odkazovat na něj ze sta stránek. Malou změnu lze připojit jako novou revizi, která nahradí jeden objekt, spíše než aby se přepisoval celý soubor. Tabulka křížových odkazů je index, který mění číslo objektu na bytový offset, takže čtečka přeskočí rovnou k 12 0 obj bez skenování, ale to je fyzická optimalizace. Logicky stačí vědět, že 12 0 R znamená „objekt identifikovaný jako 12 0.“
Katalog: kde začíná každý dokument
Řešení odkazů musí někde začít a to někde je položka /Root v traileru, která ukazuje na katalog dokumentu: kořen grafu objektů, slovník s /Type /Catalog. Čtečka se k němu dostane jako první, protože nejprve najde trailer a odtud je jakákoli další část dokumentu dosažitelná sledováním odkazů
Katalog nese pouze dvě striktně vyžadované položky: svůj /Type a /Pages, nepřímý odkaz na kořen stromu stránek. Zbytek je volitelný a popisuje chování v celém dokumentu spíše než obsah: /Outlines ukazuje na strom záložek, /Names obsahuje stromy názvů s řetězcovými klíči, /Metadata odkazuje na datový proud metadat XMP a /PageMode a /PageLayout navrhují, jak by měl prohlížeč otevřít dokument. Žádná z nich není nutná pro vykreslení stránky; konfigurují zážitek kolem stránek. Struktury záložek, metadat a anotací, které visí na katalogu, se zabývají v článku o metadatech, záložkách a anotacích PDF
Níže uvedený diagram ukazuje, kde se tělo objektů nachází v okolním souboru. Katalog a strom stránek žijí uvnitř tohoto těla jako běžné nepřímé objekty; hlavička, tabulka křížových odkazů a trailer kolem nich jsou fyzickým lešením, které umožňuje čtečce je lokalizovat

Strom stránek: vyvážená hierarchie stránek
Od položky /Pages se dokument větví do stromu stránek, kde se volba PDF pro graf oproti plochému seznamu vyplácí. Stránky nejsou uloženy jako jednoduchá sekvence; visí ze stromu, jehož vnitřními uzly jsou uzly stromu stránek (/Type /Pages) a jehož listy jsou objekty stránek (/Type /Page). Vnitřní uzel vypisuje své potomky v poli /Kids a zaznamenává v /Count, kolik listových stránek žije pod ním. Každý uzel kromě kořenového nese odkaz /Parent zpět nahoru, takže lze stromem procházet oběma směry
2 0 obj % kořen stromu stránek
<< /Type /Pages /Kids [3 0 R 4 0 R] /Count 3 >>
endobj
3 0 obj % listová stránka
<< /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 % vnitřní uzel seskupující další dvě stránky
<< /Type /Pages /Parent 2 0 R /Kids [6 0 R 7 0 R] /Count 2 >>
endobj
Zde je objekt 2 kořenem, pod nímž jsou tři stránky: listová stránka 3, plus další dvě dosažitelné přes vnitřní uzel 4. /Count kořene rovno 3 se musí rovnat celkovému počtu listů pod ním, a počet, který nesouhlasí se skutečnou strukturou, je běžným způsobem, jak se ručně upravený soubor pokazí. Smyslem stromu je lokalita přístupu. Čtečka otevírající stránku 900 tisícistránkového dokumentu neprochází 900 objektů; sestoupí přes několik uzlů, protože dobře vytvořený strom zůstává mělký a vyvážený. Sestavování takového stromu ručně je natolik zdlouhavé, že stojí za to ho vidět od začátku do konce, což ukazuje návod na vytvoření dokumentu PDF od nuly
Strom získává na hodnotě také díky dědičnosti (inheritance). Hrstka atributů stránky, /Resources, /MediaBox, /CropBox a /Rotate, může být nastavena na vnitřním uzlu a vynechána na jednotlivých stránkách, které pak zdědí hodnotu nejbližšího předka. Nastavte /MediaBox jednou na kořeni a každý list získá stejnou velikost stránky, aniž by ji musel opakovat; stránka, která se potřebuje lišit, deklaruje svou vlastní. Toto je jediné místo v objektovém modelu, kde význam hodnoty závisí na poloze objektu ve stromu, nejen na jeho vlastním obsahu
Co vlastně obsahuje listová stránka
Objekt stránky je spojovacím bodem mezi strukturálním modelem a viditelným obsahem. Její položka /Contents odkazuje na jeden nebo více datových proudů obsahu, operátory kreslení, které na stránku malují text a grafiku. Její slovník /Resources vyjmenovává písma, obrázky a barevné prostory, na které se tyto operátory spoléhají, přičemž každá položka je nepřímým odkazem na objekt sdílený napříč stránkami. /MediaBox udává obdélník stránky v bodech (1/72 palce) a položky jako /Rotate a /CropBox upravují, jak se prezentuje
Tato dělba práce je vlastně celý model v miniatuře. Slovník stránky je strukturou: typované položky a odkazy, které říkají, co stránka je a čím se kreslí. Proud obsahu (content stream) jsou instrukce: samostatný, komprimovatelný blok dat, který říká, jak kreslit. Písmo skryté za /F1 je sdílený prostředek, definovaný jednou a odkazovaný tam, kde se používá. Slovník, proud a odkaz spolupracují na vykreslení jedné stránky a stejné vzory se škálují na celý dokument. Operátory datového proudu obsahu uvnitř tohoto bloku dat se probírají samostatně v částech věnovaných textu a písmům a grafice a vizuálním prvkům
Proč se vyplatí tento model znát
Většina vývojářů se s objektovým modelem setká až ve chvíli, kdy se něco pokazí: stránka se vykreslí prázdná, protože její odkaz /Contents nikam nevede, text se zobrazí jako rámečky, protože prostředek písma nebyl nikdy vložen, nástroj hlásí /Count, který neodpovídá stránkám, které dokáže najít. Každý z těchto případů je výpovědí o grafu, a číst přímo z grafu je lepší než hádat. Osm typů a pravidlo odkazů tvoří dostatečně malý slovník na to, abyste si ho udrželi v hlavě, a jakmile začnete vnímat PDF jako objekty odkazující na objekty, přestanou být poškozené soubory neprůhlednými
Přesto je ruční psaní tohoto modelu málokdy tou správnou volbou jindy než při učení. Udržování konzistence posunů křížových odkazů, čísel generací, počtů ve stromu stránek a délek datových proudů napříč úpravami je přesně ten druh účetnictví, kvůli kterému existují knihovny, aby jej zvládly. V produkci se vyzrálá vývojářská PDF knihovna stará o správu objektového grafu a nechá vás přemýšlet v rovině stránek a obsahu. Znalost modelu se však stále vyplácí: rozumíte totiž tomu, co knihovna na pozadí vytváří a proč