PDF je v svojem jedru besedilni vsebnik. Če večino datotek odprete v heksadecimalnem urejevalniku, je začetek berljiv: komentar o različici, nato niz oštevilčenih objektov, na samem dnu pa majhno kazalo in kazalec, ki bralniku pove, kje naj začne. Če odstranimo stiskanje, je format dovolj dostopen, da lahko delujoč dokument vpišete v urejevalnik besedil in ga odprete v pregledovalniku. Če to storite enkrat, se o zgradbi PDF-ja naučite več kot z vsakim branjem specifikacije, saj morate objekte povezati ročno, datoteka pa se ne bo odprla, dokler povezav ne nastavite pravilno.
Ta vodič opisuje izdelavo najmanjšega PDF-ja, ki dejansko nekaj izriše: eno stran z besedilom "Hello, World!" v vgrajeni pisavi na papirju velikosti US Letter. Končana datoteka potrebuje natanko pet objektov in nekaj vrstic tehničnih podatkov okoli njih. Najprej bomo napisali objekte, nato pa sestavili glavo, tabelo navzkrižnih sklicev in napovednik, ki jih povežejo v datoteko, primerno za bralnike.
Pet objektov, ki jih zahteva pregledovalnik
Bralnik ne pregleduje PDF-ja od vrha navzdol v iskanju vsebine. Začne pri napovedniku, sledi sklicu na katalog dokumenta in od tam prehodi verigo objektov. Vsak objekt na tej verigi mora obstajati, sicer se odpiranje ponesreči. Za enostranski dokument je veriga kratka, vsak člen pa opravlja eno nalogo:
- Catalog je koren dokumenta. To je objekt, na katerega kaže napovednik, njegov edini zahtevani vnos tukaj pa je sklic na drevo strani.
- Pages je vozel drevesa strani. Navaja strani v dokumentu in poroča o tem, koliko jih je.
- Page opisuje posamezno fizično stran: njeno velikost, vire, s katerimi se izrisuje, in kateri tok vsebine jo izrisuje.
- Content stream (tok vsebine) vsebuje operaterje risanja, to so postfiksni ukazi, ki postavijo besedilo in grafiko na to stran.
- Font (pisava) deklarira pisavo, na katero se sklicuje tok vsebine. Če uporabite eno od 14 standardnih pisav, vam ni treba vgrajevati ničesar.
Vsak objekt je oštevilčen in naslovljiv. Posredni objekt je zapisan kot N 0 obj ... endobj, kjer je N številka objekta, 0 pa je številka generacije (v novi datoteki je vedno 0). Kjer koli drugje v datoteki na ta objekt pokažete s sklicem: 5 0 R pomeni "objekt 5." Ti sklici predstavljajo povezave. Katalog vsebuje sklic 2 0 R za dostop do drevesa strani, drevo strani vsebuje sklic nazaj na stran in tako naprej. Če se zmotite pri številki, bo bralnik sledil neveljavnemu kazalcu v prazno.
Imena, slovarji in tokovi
Skoraj vse podatke prenašajo trije sintaktični elementi. *Ime* se začne s poševnico: /Type, /Page, /F0. Imena so identifikatorji, občutljivi na velike in male črke, in ne nizi, PDF pa jih uporablja za ključe slovarjev in označevanje vrste objekta. *Slovar* je niz parov ključ-vrednost, ovitih v dvojne lomljene oklepaje, kjer je vsak ključ ime: << /Type /Page /MediaBox [0 0 612 792] >>. Vrednosti so lahko številke, imena, polja v oglatih oklepajih, sklici ali gnezdeni slovarji. Večina PDF objektov so slovarji.
*Tok* (stream) je slovar, ki mu sledi blok bajtov med ključnima besedama stream in endstream. Tam se nahajajo operaterji za izrisovanje strani, v dejanskih datotekah pa tudi stisnjene slike in vgrajene pisave. Slovar toka opisuje te bajte; v produkcijski datoteki mora vsebovati vnos /Length z natančnim številom bajtov ter pogosto tudi filter /Filter, kot je /FlateDecode, ko so podatki stisnjeni. Pri določanju dolžine /Length se bomo zanesli na zunanje orodje, saj ročno štetje bajtov nima izobraževalne vrednosti, hkrati pa prinaša veliko tveganje za napako za en bajt, ki pokvari datoteko.
Zapisovanje objektov
Spodaj je navedenih vseh pet objektov po vrstnem redu. Pred branjem toka vsebine upoštevajte podrobnosti o koordinatah: PDF meri točke od spodnjega levega kota strani navzgor, pri čemer je ena točka enaka 1/72 palca, os Y pa raste navzgor. Stran velikosti US Letter meri 612 krat 792 točk, zato se koordinata 50 700 nahaja blizu zgornjega levega kota in ne spodaj.
1 0 obj
<< /Type /Catalog
/Pages 2 0 R
>>
endobj
2 0 obj
<< /Type /Pages
/Kids [3 0 R]
/Count 1
>>
endobj
3 0 obj
<< /Type /Page
/Parent 2 0 R
/MediaBox [0 0 612 792]
/Resources << /Font << /F0 4 0 R >> >>
/Contents 5 0 R
>>
endobj
4 0 obj
<< /Type /Font
/Subtype /Type1
/BaseFont /Helvetica
>>
endobj
5 0 obj
<< /Length 44 >>
stream
BT
/F0 36 Tf
50 700 Td
(Hello, World!) Tj
ET
endstream
endobj
Če preberemo sklice, je zgradba jasna. Objekt 1 (katalog) usmerja svoj vnos /Pages na objekt 2. Objekt 2 (drevo strani) navaja objekt 3 v polju /Kids in določa /Count 1. Objekt 3 (stran) s sklicem /Parent kaže nazaj na objekt 2 (zahtevano je, da se drevo in stran sklicujeta drug na drugega), določa svojo velikost z /MediaBox, izpostavlja pisavo pod lokalnim imenom /F0 v /Resources in določa objekt 5 kot svojo vsebino. Objekt 4 je pisava: /BaseFont /Helvetica izbere eno od 14 standardnih pisav, ki jih ima vsak skladen bralnik že vgrajene, zato uvoz ni potreben. Objekt 5 je tok vsebine.
Kaj dejansko pomeni tok vsebine
Telo toka je majhen program v PDF-jevem jeziku za opis strani, ki deluje po postfiksnem načelu: najprej so navedeni operandi, nato pa operater, ki jih uporabi. Opravilo izvede pet vrstic. Ukaza BT in ET odpreta in zapreta besedilni objekt; vse, kar določa položaj ali prikazuje besedilo, mora biti zapisano med njima. /F0 36 Tf nastavi trenutno pisavo na vir /F0 z velikostjo 36 točk (Tf pomeni "nastavi pisavo in velikost besedila"). Ukaz 50 700 Td premakne položaj besedila na (50, 700) v koordinatah strani. (Hello, World!) Tj izriše niz, ki ga PDF zapiše kot dobesedno besedilo v oklepajih, pri čemer operater Tj besedilo izriše na trenutnem položaju. Če izpustite BT/ET, bo strog bralnik zavrnil operaterje za besedilo; če pozabite nastaviti pisavo pred uporabo Tj, pa trenutna pisava za risanje ne bo določena.
Vnos /Length 44 v slovarju toka označuje število bajtov med ključnima besedama stream in endstream in mora biti povsem natančen. To vrednost je bolje prepustiti zunanjemu orodju kot pa ročno šteti nove vrstice, še posebej zato, ker izbira konca vrstic v urejevalniku (LF ali CRLF) spremeni končni seštevek bajtov.
Glava, tabela navzkrižnih sklicev (xref) in napovednik
Objekti predstavljajo vsebino. Trije strukturni deli pa jih povežejo v datoteko. Prvi del je glava, prva vrstica, ki poimenuje format in različico:
%PDF-1.7
Znak % označuje komentar v sintaksi PDF, vendar bralnik ta specifičen komentar obravnava kot podpis formata in iz njega prebere različico. Dejanski pisalnik takoj za tem zapiše drugo vrstico komentarja z bajti visoke vrednosti (high-bit bytes), kar je namig za orodja za prenos datotek, da je datoteka binarna in se ne sme spreminjati kot navadno besedilo.
Na koncu datoteke se nahaja tabela navzkrižnih sklicev (cross-reference table oz. xref), ki deluje kot kazalo in omogoča naključen dostop. Beleži bajtni odmik vsakega objekta od začetka datoteke, tako da lahko bralnik skoči neposredno na objekt 3 brez predhodnega branja objektov 1 in 2. Tabela je strogo določena: vnosi imajo fiksno širino 20 bajtov (vključno s koncem vrstice) in so formatirani kot 10-mestni odmik, 5-mestna generacija, ključna beseda (n za aktivne, f za proste objekte) in dvobajtni zaključek vrstice. Pravilna tabela za naših šest vnosov (objekt 0 je vedno začetek prostega seznama) izgleda takole:
xref
0 6
0000000000 65535 f
0000000009 00000 n
0000000058 00000 n
0000000115 00000 n
0000000235 00000 n
0000000308 00000 n
trailer
<< /Size 6
/Root 1 0 R
>>
startxref
408
%%EOF
Ti odmiki so najbolj občutljiv del ročnega pisanja PDF-ja. Vsak odmik predstavlja natančen bajtni položaj, kjer se začne pripadajoči N 0 obj, in vsak odmik se spremeni takoj, ko dodate znak kjer koli nad njim. Napovednik (trailer) je vstopna točka, ki jo bralnik uporabi najprej in nazadnje: /Root 1 0 R poimenuje katalog, /Size 6 določa število objektov, startxref 408 pa označuje bajtni odmik same besede xref. Bralnik odpre datoteko, skoči na njen konec, prebere startxref, poiše tabelo navzkrižnih sklicev ter prek nje doseže katalog in vse ostale deli. Oznaka %%EOF označuje zadnji bajt.
Naj orodje popravi odmike bajtov
Odmiki zgoraj so le ilustrativni; v praksi bodo napačni takoj, ko boste končali s tipkanjem, saj so odvisni od natančne bajtne postavitve vaše datoteke. Namesto ponovnega izračunavanja raje zapišite strukturo z začasnimi vrednostmi in pustite, da zunanje orodje ponovno zgradi tabelo navzkrižnih sklicev in določi dolžine tokov. Brezplačno in večplatformsko orodje pdftk to stori v enem koraku:
pdftk hello-draft.pdf output hello.pdf
Orodje razčleni vaše objekte, ponovno izračuna vsak bajtni odmik, vpiše pravilne vrednosti /Length, zapiše veljavno tabelo xref in napovednik ter ustvari datoteko hello.pdf. Če jo odprete v katerem koli pregledovalniku, boste dobili eno stran z napisom "Hello, World!" v 36-točkovni pisavi Helvetica blizu vrha. Orodje Qpdf opravi enako delo, prav tako pa bo veliko pregledovalnikov sproti popravilo rahlo poškodovano datoteko. Namen uporabe orodja tukaj ni lenoba, temveč dejstvo, da aritmetika odmikov nima konceptualne teže in prinaša največ napak, tako da avtomatizacija poskrbi, da se lahko osredotočite na spoznavanje same strukture.
Zašto je to primerno tudi za obsežne dokumente
Nič v poročilu na sto straneh ne spremeni osnovne oblike, ki ste jo pravkar ustvarili. Katalog se še vedno nahaja v korenu, drevo strani še vedno združuje strani in vsaka stran še vedno kaže na svoje vire in tok vsebine. Kar raste, je obseg, ne hrbtenica dokumenta: drevo strani se razveji, da lahko bralnik preskoči celotna poddrevesa, tokovi vsebine nosijo na stotine operaterjev namesto petih, pisave se vgradijo kot samostojni objekti tokov s tabelami širin in kodiranji, slike pa pridejo kot tokovi s specifičnimi filtri. Sodobne datoteke prav tako pogosto pakirajo več objektov v stisnjene tokove objektov ter zamenjajo navadno tabelo xref s tokom navzkrižnih sklicev, zaradi česar odpiranje dejanskega PDF-ja v urejevalniku običajno prikaže le neberljive znake. Model pod tem pa je enak tistemu v vaši ročno ustvarjeni datoteki. Če vas zanima širši graf objektov ter kako se povezujejo katalog, drevo strani in slovarji virov, si preberite podroben pregled strukture dokumentov PDF, pregled zgradbe datotek PDF pa obravnava postopne posodobitve in veriženje napovednikov.
Od ročnega pisanja do uporabe knjižnice
Ročno pisanje objektov je le učna vaja in ne produkcijska tehnika. Takoj ko potrebujete prave pisave, prelomljeno besedilo, slike ali več kot le eno enostavno stran, postane bajtno računanje, ki ga je za vas popravil pdftk, vaše glavno delo. V takem primeru potrebujete namensko knjižnico. Zapiše se enakih pet objektov, vendar knjižnica sama izračuna vsak odmik, upravlja slovarje pisav in virov ter stisne tokove vsebine, ne da bi vi morali slediti enemu samemu bajtu. V okoljih Delphi in C++Builder knjižnica HotPDF Component celotno datoteko zvede na nekaj klicev: nastavite dokument, pokličete BeginDoc, nastavite pisavo s SetFont in izpišete besedilo s TextOut ter nato s klicem EndDoc zapišete katalog, drevo strani, tabelo xref in napovednik. Poznavanje objektov v ozadju pa vam pomaga razumeti, zakaj dokument morda ne izgleda tako, kot ste pričakovali.