Technical Article

Linearizacija PDF-a i Fast Web View: Kako funkcioniše

Postavite skenirani izveštaj od 80 MB iza linka, otvorite ga u pregledaču i gledajte šta se dešava: čitač stoji na praznom ekranu sve dok ne stigne veliki deo tih bajtova, a zatim odjednom iscrtava prvu stranicu. Skočite na stranicu 40 i, kod loše napravljene datoteke, celo preuzimanje može početi ponovo. Frustrirajući deo je to što je čitač zapravo želeo samo prvu stranicu. Linearizacija je strukturni odgovor na taj problem. Ona reorganizuje PDF tako da čitač može da prikaže početnu stranicu iz malog uvodnog dela datoteke i preuzme ostatak na zahtev, zbog čega Adobe ovu funkciju reklamira pod nazivom "Fast Web View".

Ništa od ovoga nije drugačiji format datoteke. Linearizovani PDF je običan PDF koji će kompatibilan čitač otvoriti bez ikakvog posebnog rukovanja. Trik je u potpunosti u tome kako su bajtovi poređani i u dve dodatne strukture koje datoteka nosi. Standard ISO 32000-1 specifikuje ceo ovaj raspored u Aneksu F, a kada vidite izgled, ovakvo ponašanje prestaje da izgleda kao magija i počinje da izgleda kao svesna razmena redosleda datoteke za brže početno iscrtavanje.

Šta linearizacija zapravo reorganizuje

Normalan PDF može rasporediti svoje objekte u skoro bilo kom redosledu. Tabela unakrsnih referenci na kraju datoteke je ono što omogućava taj rad: čitač ide na kraj, čita pokazivač startxref, učitava xref i odatle može locirati svaki objekat prema njegovom pomaku (offset). Taj dizajn je odličan za lokalne datoteke, gde pozicioniranje na kraj ne košta ništa, ali je loš za datoteku koja se strimuje preko mreže, gde je kraj upravo onaj deo koji stiže poslednji. Da bi prikazao prvu stranicu, konvencionalni čitač treba objekat stranice, njen tok sadržaja (content stream), fontove na koje se referencira i sve slike koje crta, a u neuređenoj datoteci oni mogu biti bilo gde, uključujući i poslednji megabajt.

Linearizacija popravlja redosled. Objekti potrebni za prikaz prve stranice sakupljeni su u neprekidni blok blizu početka, odmah nakon malog zaglavlja, tako da stižu rano u toku bajtova. Sve ostalo, preostale stranice i resursi koje dele, sledi u predvidljivom redosledu. Druga, kompletna tabela unakrsnih referenci i dalje se nalazi na kraju za čitače koji ignorišu optimizaciju, ali linearizovana datoteka takođe postavlja unakrsne reference prve stranice i parametre koji su potrebni čitaču za strimovanje na samom početku. Čitač više ne mora da stigne do kraja datoteke pre nego što može bilo šta da iscrta.

Skup objekata prve stranice i rečnik parametara linearizacije

Prvi objekat u linearizovanoj datoteci, nakon %PDF zaglavlja, jeste rečnik parametara linearizacije. To je ono što čitač za strimovanje traži kako bi odlučio da li je optimizacija prisutna i kako da je koristi. Rečnik beleži dužinu cele datoteke, bajt pomak gde počinje glavni odeljak unakrsnih referenci, broj objekta prve stranice, kao i lokaciju i dužinu toka nagoveštaja koji sledi. Pomoću tih brojeva, čitač već iz prvih kilobajta zna koliko mora da preuzme da bi prikazao prvu stranicu i gde da traži indeks koji mu omogućava da skoči na drugo mesto.

Aneks F je strog po pitanju toga šta ovde znači "prva stranica". Odeljak prve stranice mora da sadrži sam objekat stranice, njene tokove sadržaja i resurse na koje se ti tokovi referenciraju, tako da je stranica samodovoljna čim se taj početni deo preuzme. Zajednički resursi, kao što je font koji se koristi na svakoj stranici ili logotip koji se ponavlja u zaglavlju, tretiraju se posebno: pojavljuju se dovoljno rano da posluže prvoj stranici, ali su označeni kao zajednički kako ih čitač ne bi ponovo preuzimao kada kasnije bude prikazivao stranicu 30. Ta razlika između privatnih objekata stranice i zajedničkih objekata je deo koji većina neprofesionalnih "optimizatora" pogreši, a ta greška dovodi do datoteke koja tvrdi da je linearizovana, ali i dalje koči.

Tokovi nagoveštaja: indeks koji skakanje na stranice čini jeftinim

Brz prikaz prve stranice je samo polovina vrednosti. Druga polovina je skakanje na bilo koju stranicu bez preuzimanja svega između, a to je ono što pružaju tokovi nagoveštaja. Linearizovana datoteka nosi tabelu nagoveštaja pomaka stranica i tabelu nagoveštaja zajedničkih objekata, koje su sačuvane kao tok na koji se referencira iz rečnika parametara. Tabela pomaka stranica beleži, za svaku stranicu, gde njeni objekti počinju u datoteci i koliko dugo traju. Tabela zajedničkih objekata radi isto za resurse koji se koriste na više stranica.

Uz ove tabele, čitač koji želi stranicu 40 ne analizira datoteku sekvencijalno. On konsultuje tabelu nagoveštaja da bi saznao opseg bajtova koji zauzima stranica 40, traži od servera tačno taj opseg i prikazuje stranicu kada ti bajtovi stignu, povlačeći sve zajedničke resurse koje već nema preko istog mehanizma. Tok nagoveštaja je, u suštini, mapa za nasumičan pristup postavljena preko dokumenta, i to je razlog zašto dobro linearizovana datoteka od 500 stranica deluje brzo na sporoj vezi, dok neoptimizovana datoteka iste veličine to ne čini.

Zašto server mora da sarađuje

Linearizacija podrazumeva da transportni sloj može da isporuči proizvoljne delove datoteke, i tu pretpostavku vredi proveriti pre nego što okrivite format za loše rezultate. Mehanizam je HTTP byte-serving: čitač šalje zahteve za opsegom (range requests), a server na njih odgovara sa 206 Partial Content. Ako server ne oglašava Accept-Ranges: bytes, ili ako proksi ili CDN ispred njega spajaju zahteve za opsegom u pune prenose, čitač nema načina da preuzme stranicu 40 zasebno i vraća se na preuzimanje cele datoteke. Strukturna ispravnost unutar PDF-a je tada savršena, ali potpuno uzaludna.

Inkrementalna ažuriranja tiho narušavaju linearizaciju

Evo ograničenja koje iznenađuje ljude koji ispravno generišu linearizovane datoteke, a zatim se pitaju zašto optimizacija nestaje. Linearizacija zavisi od jednog, pažljivo uređenog rasporeda sa indeksom na početku. Inkrementalno ažuriranje to narušava po dizajnu. Kada alat dodaje potpis, popunjava polje obrasca ili dodaje napomenu (anotaciju) putem inkrementalnog čuvanja, on ne prepisuje datoteku. On dodaje izmenjene objekte, novi odeljak unakrsnih referenci i novi trejler (trailer) na kraj, ostavljajući originalne bajtove netaknutim. To dodavanje je cela poenta inkrementalnih ažuriranja: brzo je i čuva prethodnu reviziju radi revizije ili validacije potpisa.

Sporedni efekat je da datoteka sada ima najnovije podatke unakrsnih referenci na samom kraju, nakon pažljivo postavljenog bloka prve stranice, a rečnik parametara linearizacije na početku opisuje raspored koji više ne odgovara datoteci. Kompatibilan čitač detektuje ovo neslaganje i tretira dokument kao običan, nelinearizovan PDF. Fast Web View se gubi, iako se originalna linearizovana struktura i dalje nalazi u prvoj polovini datoteke. Ako dodate nekoliko ažuriranja, svako od njih slaže novu reviziju na kraj i jaz između zastarelog indeksa na početku i stvarnog stanja se povećava.

Ako vaš radni tok zahteva i izmene i Fast Web View, pravilo direktno proizilazi iz strukture: menjajte inkrementalno dok je dokument u fazi izmene, a zatim ga jednom linearizujte na kraju. Potpuno prepisivanje je ono što vraća ispravan raspored. U terminima HotPDF-a, to znači da se izmena u toku vrši preko BeginIncrementalUpdate i SaveIncrementalUpdate, koji dodaju deltu, dok završni korak učitava ceo dokument i serijalizuje ga iznova pomoću LoadFromFile, nakon čega sledi SaveLoadedDocument, što odbacuje nagomilane stare revizije i generiše jedan čist raspored. Isti kompromis se javlja i kod tokova objekata (object streams): omogućavanje UseObjectStreams zajedno sa UseXRefStream komprimuje unakrsne reference i pakuje objekte tesno, što smanjuje veličinu datoteke ali se, kao i svaka strukturna odluka, mora primeniti tokom tog konačnog prepisivanja, umesto da se kalemi na dodatu reviziju.

// In-flight edits: append a delta, keep prior revisions intact.
// This leaves the file NOT linearized.
Pdf.BeginIncrementalUpdate('report.pdf');
Pdf.AddPage;
Pdf.CurrentPage.TextOut(72, 760, 0, 'Addendum');
Pdf.SaveIncrementalUpdate('report.pdf');

// Finishing step: full re-serialization produces one clean layout,
// dropping the stacked revisions. Re-run your linearizer on the output.
Pdf.LoadFromFile('report.pdf');
Pdf.SaveLoadedDocument('report-final.pdf');

HotPDF ne pruža funkciju za linearizaciju jednim pozivom, tako da je praktičan šablon da se generiše čista, potpuno prepisana datoteka, a zatim da se na njoj pokrene namenski optimizator. Alati komandne linije direktno rešavaju reorganizaciju. Alat qpdf prepisuje datoteku u linearizovani oblik pomoću jedne zastavice:

qpdf --linearize report-final.pdf report-web.pdf

Kako utvrditi da li je datoteka linearizovana

Nemojte verovati nazivu datoteke ili alatu koji tvrdi da ju je napravio; proverite bajtove. Najdirektnija provera je na početku datoteke: otvorite je i potražite rečnik parametara linearizacije kao prvi objekat nakon zaglavlja, koji sadrži ključ /Linearized. Prečica za korisnike je Acrobat-ov dijalog Document Properties, koji prikazuje "Fast Web View: Yes" samo kada je struktura zaista prisutna i ažurna.

Za skriptovane provere, qpdf prijavljuje i prisustvo i integritet strukture, što je važno jer datoteka može sadržati rečnik linearizacije koji više ne odražava njen stvarni raspored, što je upravo stanje koje inkrementalno ažuriranje ostavlja za sobom:

# Reports "File is linearized" and validates hint tables against the layout
qpdf --check report-web.pdf

# Dumps the linearization parameters and hint data in detail
qpdf --show-linearization report-web.pdf

Korak validacije je onaj koji zaista opravdava trud. Provera koja samo potvrđuje da rečnik postoji rado će odobriti datoteku čiji indeks pokazuje na pogrešne pomake; provera koja usklađuje tabele nagoveštaja sa stvarnim pozicijama objekata je ono što vam govori da li će optimizacija izdržati zahteve za opsegom stvarnog čitača.

Linearizaciju i dalje vredi primeniti na svaki veliki dokument koji se isporučuje preko veba, posebno za mobilne čitače na nestabilnim vezama, a ona košta nekoliko procenata veličine datoteke zbog indeksa koji se postavlja na početak. Dve stvari koje treba imati na umu jesu da struktura unutar PDF-a i isporuka bajtova van njega moraju biti ispravni, kao i da svaka naknadna izmena poništava optimizaciju sve dok ponovo ne prepišete datoteku. Tretirajte ponovnu linearizaciju kao poslednji korak u radnom procesu, nakon što su sve ostale izmene završene. Ponašanje unakrsnih referenci, toka objekata i inkrementalnog ažuriranja koje je ovde opisano deo je strukturnog modela koji implementira HotPDF Component za Delphi i C++Builder; za širu pozadinu o rasporedu datoteke pogledajte kako je PDF strukturiran, a za radni tok sa inkrementalnim ažuriranjem i velikim datotekama u kodu pogledajte obradu velikih PDF-ova iz Delphi-ja.