Format datoteke PDF

9 minutno branje Zadnja sprememba:

Kako je zgrajen pdf
Kazalo

Izvirnik objavljen na fiziki 25/4/2016

1. Uvod


Letno proizvedem vsaj sto pdf datotek, zato me je nekoč zanimalo kaj to sploh je. Format sem nekoliko raziskala.


Portable Document Format (kratica PDF) je odprt standard za izmenjavo elektronskih dokumentov, ki je bil ustvarjen z namenom od platforme neodvisnega prikazovanja dokumenta. Datoteka PDF ima pripono .pdf in vsebuje popoln zapis dokumenta, vključno z besedilom, prelomom, pisavami, grafičnimi elementi, metapodatki in drugimi podatki, nujnimi za enoznačen prikaz.

Vir: Wikipedija

Leta 2008 je bil PDF potrjen kot standard ISO 32000-1:2008


Če vas tema zanima, priporočam ogled dela Angeja Albertinija (glej vire), ki opravlja občudovanja vredne raziskave na področju formatov datotek. Med drugim sodeluje tudi pri PoC||GTFO, ki je res poseben časopis, ne le vsebinsko. Pdf datoteka v katero je zapakiran ima vedno obilo skritih funkcij. Zadnja izdaja 0x11 se je, med drugim, kar sama sebe servirala:D



2. Prvi pogled


Odprimo naključno pdf datoteko s tekstovnim urejevalnikom.


pdf vim

Neko strukturo lahko razberemo. Poglejmo test.pdf še z Origamijem, natančneje njegovim grafičnim vmestnikom Pdf Walkerjem.


pdf origami

pdf1 origami

Origami nam veliko pove o pdf datoteki. Vidimo glavno strukturo datoteke in tudi detajle. test.pdf pošljimo še čez pdf-parser, ki nam da sledeče podatke test.txt. Poglejmo si torej kako je narejena pdf datoteka in kako jo naredimo v tekstovnem urejevalniku.



3. Struktura


Osnovna struktura pdf-ja je predstavljena na spodnji sliki:


pdf format



3.1 Glava


Glava pdf datoteke je prva vrstica datoteke. Poglejmo si hexdump test.pdf:

$ xxd test.pdf | head -n 1
00000000: 2550 4446 2d31 2e35 0a25 d0d4 c5d8 0a33  %PDF-1.5.%.....3

Pdf datoteka se začne z magičnim nizom 25 50 44 46, torej s %PDF. Sledi ena izmed verzij: -1.0, -1.1, -1.2, -1.3, -1.4, -1.5, -1.6 -1.7. Znak % se v pdf datoteki uporablja za komentar.



3.2 Telo


V telesu so zapisani objekti, ki predstavljajo vsebino dokumenta. Zapisani so fonti, strani, slike, tekstovni tok, multimedijski elementi itd. Pdf podpira osem osnovnih tipov objekov: logične vrednosti, cela in realna števila, nize, imena, polja, slovarje, tokove in ničelni objekt. Poglejmo si nekaj tipov objektov.


:: Imena definiramo s poševnico, ki ji sledijo ASCII znaki od 0x21 do 0x7E. Primer:

/Name

:: Slovarje definiramo s pomočjo <<ključ vrednost>>. Lahko jih gnezdimo. Presledki so zanemarjeni. Primer:

<< /Index 1 << /Item true >> >>

:: Vse objekte v pdf dokumentu lahko označimo kot posredne objekte. Zapišemo jih med obj in endobj, kjer mora endobj stati v svoji vrstici. Primer:

0 1 obj
5
endobj

Tako smo shranili vrednost 5 kot objekt 1, generacije 0, in ga lahko kasneje prikličemo. Sklic zapišemo s pomočjo črke R. Na prejšnji posreden objekt se lahko skličemo z:

0 1 R

Primer uporabe v slovarju:

<< /Root 0 1 R >>

:: Polja zapišemo s pomočjo oglatih oklepajev. Vrednosti so ločene s presledki. Primer:

[1 3 7 9]

:: Nizi so objekti in jih, kot vse ostale objekte, zapišemo med obj in endobj. Niz nato zapišemo med stream in endstream. Nizi lahko vsebujejo karkoli. Tudi izvršljive datoteke in ostale formate. Ne smejo pa vsebovati ključa endstream. Parametri niza so shranjeni pred stream. Obvezen parameter nizov je dolžina. Primer:

1 0 obj
<< /Length 10 >>
stream
1234567890
endstream
endobj

Poglejmo si še sintakso tesktovnega niza. Najprej zapišemo zaporedje parametrov, čemur sledi operator. Tekst pišemo med BT (begin text object) in ET (end text object). Font določimo s Tf (text font), ki ima ime fonta in velikost za obvezna parametra. Pozicijo teksta določimo s Td, ki ravno tako potrebuje dva parametra: x in y koordinato. Vsebino teksta prikažemo s Tj. Dolžina je število vseh znakov od BT do ET (vključno ET in BT), s presledki, znaki za novo vrstico itd. Primer zapisa teksta:

3 0 obj
<< /length 38 >>
stream
BT
/F1 100 Tf
10 400 Td
(Sneži!) Tj
ET
endstream
endobj

Pdf vedno vsebuje objekt 0, ki je ničelen.


Telo pdf datoteke je zaporedje posrednih objektov, ki predstavljajo vsebino dokumenta. Primer:

1 0 obj
3
endobj

2 0 obj
<< /Index 1 >>
endobj

Več o tipih objektov si lahko preberete v Adobejevem Pdf referenčnem priročniku.



3.3 xref tabela


“Cross reference” ali xref tabela vsebuje podatke o objektih, koliko jih je, kje se nahajajo. Omogoča nam naključni dostop do objektov. Tako nam ni potrebno brati cele datoteke, da bi vedeli, kje se kakšen objekt nahaja.

Začne se z xref v eni vrstici, ter nadaljuje z začetnim indeksom in številom objektov v naslednji vrstici. Primer:

xref
0 3

Sledijo objekti, vsak v svoji vrstici. Vsaka vrstica je dolga 20 bytov (1 char je 1 byte), zato je potreben na koncu vrstice presledek. Format zgleda nekako tako:

xxxxxxxxxx yyyyy a 
10 št.     5 št. črka presledek

Znak za novo vrstico je na Linux/BSD 1 byte, na Windowsih pa 2 byta. Torej skupno 20 bytov. Sklepamo, da presledka na Windowsih ni potrebno dodati.

Prvih 10 števk določa odmik objekta od začetka dokumenta. Pove nam torej število znakov od začetka dokumenta do objekta, z všetimi presledki, znaki za novo vrstico itd. Naslednjih pet števk predstavlja generacijsko številko, ki je navadno 0, razen za objekt 0, katerega generacijska številka je 65535. Na koncu vrtice je črka f (free) ali n (not free, v uporabi). Primer xref tabele:

xref
0 6
0000000003 65535 f 
0000000017 00000 n 
0000000081 00000 n 
0000000000 00007 f 
0000000331 00000 n 
0000000409 00000 n 

V zgornjem primeru je bil objekt številka 3 izbrisan.


Sedaj moramo nastaviti še startxref kazalec, da nam kaže odmik od začetka dokumenta do xref tabele. Odmik zapišemo v decimalni obliki. Pove nam število vseh znakov od začetka dokumenta do začetka xref tabele. Ne pozabimo na presledke, nove vrstice ipd. Primer:

startxref
569



3.4 Rep


Bralniki pdf dokumentov začnejo brati datoteko pri repu, ki določa kako naj najdejo xref tabelo in določene posebne objekte. Zadnja vrstica dokumenta je %%EOFpostscript. Rep je slovar, ki določa /Root ime, ki se nanaša na objekt, ki bo v telesu dokumenta. Primer:

%trailer
<< /Root 1 0 R >>



4. Pozdravljen svet


Odprimo sedaj novo datoteko primer.pdf v tekstovnem urejevalniku in napišimo “Pozdravljen svet!” pdf. Napisati moramo torej glavo, telo, xref tabelo, startxref kazalec in rep datoteke.



4.1 Glava


Glava je prva vrtica dokumenta. Verzija je poljubna.

%PDF-1.3



4.2 Telo


Objekt 1 je katalog, ki se nanaša na objekt Pages:

1 0 obj
<< /Type /Catalog /Pages 2 0 R >>
endobj

Objekt 2 je tudi katalog in definira svoje otroke:

2 0 obj
<< /Type /Pages
/Kids [ 3 0 R ]
/Count 1 >>
endobj

Objekt 3 je ponovno katalog. Definira velikost strani in pisavo uporabljeno v objektu 2:

3 0 obj
<< /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792]
/Resources << /Font << /F1 <<
/Type /Font /Subtype /Type1 /BaseFont /Arial >> >> >>
/Contents 4 0 R
>>
endobj

Objekt 4 vsebuje naš tekstovni niz Pozdravljen svet!, njegovo postavitev na strani, velikost in prej definirane fonte.

4 0 obj
<< /Length 48 >>
stream
BT
/F1 75 Tf
10 400 Td
(Pozdravljen svet!) Tj
ET
endstream
endobj



4.3 xref tabela


Vim ima zelo priročno funkcijo g CTRL+g , ki nam pomaga določiti odmik objektov v dokumentu. Pomagamo si lahko tudi z mutool clean primer.pdf, ki nam popravi odmike in dolžine.

xref
0 5
0000000000 65535 f 
0000000010 00000 n 
0000000060 00000 n 
0000000120 00000 n 
0000000292 00000 n 



4.4 Rep


Vsebuje ključ /Home, katerega vrednost je objekt 1, in ključ /Size katerega vrednost je 5, saj imamo, vključno z ničelnim, vsega 5 objektov.

trailer
<< /Root 1 0 R /Size 5 >>

Ostane nam še kazalec startxref in EOF (end of file).

startxref
391
%%EOF



Celotna koda zgleda torej takole:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
%PDF-1.3

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 << /F1 <<
/Type /Font /Subtype /Type1 /BaseFont /Arial >> >> >>
/Contents 4 0 R
>>
endobj

4 0 obj
<< /Length 48 >>
stream
BT
/F1 75 Tf
10 400 Td
(Pozdravljen svet!) Tj
ET
endstream
endobj

xref
0 5
0000000000 65535 f 
0000000010 00000 n 
0000000060 00000 n 
0000000120 00000 n 
0000000292 00000 n 

trailer
<< /Root 1 0 R /Size 5 >>

startxref
391
%%EOF

Shranimo jo v datoteko primer.pdf in odprimo s poljubnim bralnikom pdf dokumentov.


Tako. Sedaj pa dovolj igračkanja, saj me čaka resno delo. Začenja se sezona testov in grem “štjpat” te pdf datoteke v ogromnih količinah. Seveda ne na roke, kot tukaj, ampak s pomočjo meni ljubega LaTeXa.

¯\_(ツ)_/¯



Viri



Dodatki

Kako vstavimo sliko v pdf dokument?


Kako vstavimo niz smo si že ogledali. Na podoben način vstavimo sliko. Najprej moramo vključiti objekt v /Resources:

/Resources 
<<
  /XObject << /Im0 5 0 R >>
>>

Nato dodamo sliko kot na primer objekt 5 0 obj.

5 0 obj
<<
/BitsPerComponent 8
/ColorSpace /DeviceRGB
/Filter [
  /ASCIIHexDecode
  /DCTDecode
]
/Height številka
/Subtype /Image
/Type /XObject
/Width številka
/Length število znakov niza
>>
stream
slika
endstream
endobj

Sedaj potrebujemo le še sliko v ASCII hexadecimalnem formatu, saj je v tem primeru uporabljen ASCIIHex decode filter. Zapis vstavimo namesto slika v zgornjem objektu. Do zapisa pridemo lahko na več načinov. Na primer:

xxd -ps slika.jpg | sed ':a;N;$!ba;s/\n//g'

Primer slike vključene v pdf: pdf-slika.pdf. Kodo lahko pogledate tako, da si datoteko shranite in odprete v tekstovnem urejevalniku.



Kako vključimo JavaScript v pdf dokument?


Da bi vključili JavaScript v naš dokument, moramo dodati tri objekte.


Prvi objekt bo posreden objekt in bo vseboval informacijo o našem JavaScript objektu:

9 0 obj
<<
/JavaScript 10 0 R
>>
endobj

Drugi objekt bo tipa ime in nam bo omogočil poimenovanje naše JavaScript kode. Kazal bo na dejansko JavaScript kodo.

10 0 obj 
<<
/Names [(My Code) 11 0 R]
>>
endobj

Tretji objekt bo vseboval JavaScript kodo:

11 0 obj
<<
/JS (
app.alert({cMsg: "Danes bo lep dan!", nIcon: 3, nType: 1, cTitle: "Adobe Misli"});
app.launchURL("https://www.youtube.com/watch?v=v2AC41dglnM");
)
/S
/JavaScript
>>
endobj

Sedaj moramo dopolniti še xref tabelo tako, da bo vsebovala naše nove tri objekte. V spodaj priloženem primeru izgleda sedaj xref tabela tako:

xref
0 12
0000000000 65535 f 
0000000010 00000 n 
0000000062 00000 n 
0000000134 00000 n 
0000000311 00000 n 
0000000397 00000 n 
0000000416 00000 n 
0000205726 00000 n 
0000205749 00000 n 
0000205820 00000 n 
0000205861 00000 n 
0000205910 00000 n 
trailer <<
  /Root 1 0 R
  /Size 12
>>
startxref
206101

V objektu številka 1 (katalogu) moramo k imenom prišteti še naše novo ime in smo zaključili.

1 0 obj
<<
  /Pages 2 0 R
  /Names 9 0 R
>>
endobj

Primer JavaScripta vključenega v pdf: pdf-js.pdf. Za popolno doživetje pdf shranite in odprite z Adobe Acrobat Readerjem. Kodo lahko pogledate tako, da datoteko odprete v tekstovnem urejevalniku.



EOF