Tegyünk egy 80 MB-os szkennelt jelentést egy hivatkozás mögé, nyissuk meg böngészőben, és figyeljük meg, mi történik: a megjelenítő üres felületen ül, amíg a bájtok nagy része meg nem érkezik, majd egyszerre kirajzolja az első oldalt. Ugorjunk a 40. oldalra, és egy rosszul felépített fájlnál akár az egész letöltés újraindulhat. A bosszantó az egészben az, hogy az olvasó mindössze az első oldalt akarta. A linearizálás erre a problémára adott szerkezeti válasz. Úgy rendezi át a PDF-et, hogy a megjelenítő a fájl egy rövid előtagjából kirajzolhassa a nyitóoldalt, a többit pedig igény szerint töltse le – ezért árulja az Adobe a funkciót „Fast Web View” néven
Mindebben semmi nem másik fájlformátum. Egy linearizált PDF hétköznapi PDF, amelyet a szabványt követő olvasó minden külön kezelés nélkül megnyit. A trükk teljes egészében a bájtok sorrendjében és két többletszerkezetben rejlik, amelyet a fájl magával visz. Az ISO 32000-1 az F függelékben adja meg az egész elrendezést, és ha egyszer láttuk a felépítést, a viselkedés megszűnik varázslatnak látszani, és tudatos alkuvá válik: a fájlsorrendet cseréljük az első kirajzolás késleltetéséért
Mit rendez át valójában a linearizálás
Egy szokásos PDF szinte tetszőleges sorrendben szórhatja szét az objektumait. A fájl végén álló kereszthivatkozási tábla teszi ezt működőképessé: az olvasó a végére pozicionál, kiolvassa a startxref mutatót, betölti az xref-et, és onnantól minden objektumot megtalál az eltolása alapján. Ez a felépítés kiváló helyi fájloknál, ahol a végére ugrás semmibe sem kerül, és rossz egy hálózaton át érkező fájlnál, ahol éppen a vég az a rész, amely utoljára érkezik meg. Az első oldal kirajzolásához a hagyományos olvasónak szüksége van az oldalobjektumra, annak tartalomstreamjére, a hivatkozott betűkészletekre és minden rajzolt képre, egy rendezetlen fájlban pedig ezek bárhol lehetnek, akár az utolsó megabájtban is
A linearizálás rendbe teszi a sorrendet. Az első oldal megjelenítéséhez szükséges objektumok összefüggő blokkba kerülnek a fájl elejére, közvetlenül egy rövid fejrész után, így korán érkeznek meg a bájtfolyamban. Minden más – a maradék oldalak és a közösen használt erőforrásaik – kiszámítható sorrendben követi őket. A végén továbbra is ott marad egy második, teljes kereszthivatkozási tábla azoknak az olvasóknak, amelyek nem törődnek az optimalizálással, de a linearizált fájl az elejére is kihelyez egy első oldalhoz tartozó kereszthivatkozást és azokat a paramétereket, amelyekre a streamelő olvasónak szüksége van. Az olvasónak többé nem kell elérnie a fájl végét ahhoz, hogy bármit is rajzolhasson
Az első oldal objektumkészlete és a linearizálási paraméterszótár
A linearizált fájl legelső objektuma a %PDF fejléc után a linearizálási paraméterszótár. A streamelő olvasó ezt keresi, hogy eldöntse, jelen van-e az optimalizálás, és hogyan használható. A szótár rögzíti a teljes fájl hosszát, azt a bájteltolást, ahol a fő kereszthivatkozási szakasz kezdődik, az első oldal objektumszámát, valamint az utána következő hint stream helyét és hosszát. Ezekből a számokból az olvasó pusztán a nyitó kilobájtokból tudja, mennyit kell letöltenie az első oldal megjelenítéséhez, és hol keresse azt az indexet, amellyel máshová ugorhat
Az F függelék szigorú abban, mit jelent itt az „első oldal”. Az első oldalhoz tartozó szakasznak tartalmaznia kell magát az oldalobjektumot, a tartalomstreamjeit és az azokból hivatkozott erőforrásokat, hogy az oldal önmagában elegendő legyen, amint ez az előtag letöltődött. A közösen használt erőforrásokat – egy minden oldalon szereplő betűkészletet, egy fejlécben ismétlődő emblémát – külön kezeli: elég korán jelennek meg ahhoz, hogy az első oldalt kiszolgálják, de megosztottként vannak megjelölve, így az olvasó nem tölti le őket újra, amikor később a 30. oldalt rajzolja ki. Az oldalhoz kötött és a megosztott objektumok közötti különbségtétel az a rész, amelyet a saját fejlesztésű „optimalizálók” a leggyakrabban elrontanak, és ennek elrontásából születik az a fájl, amely linearizáltnak vallja magát, mégis megakad
Hint streamek: az index, amely olcsóvá teszi az oldalugrást
Az első oldal gyors megjelenítése az értéknek csak a fele. A másik fele az, hogy tetszőleges oldalra ugorhatunk anélkül, hogy a közte lévő mindent letöltenénk, és éppen ezt biztosítják a hint streamek. Egy linearizált fájl oldaleltolás-hinttáblát és megosztott objektumok hinttábláját hordozza, streamként tárolva, amelyre a paraméterszótár hivatkozik. Az oldaleltolás-tábla minden oldalról rögzíti, hol kezdődnek az objektumai a fájlban, és meddig tartanak. A megosztott objektumok táblája ugyanezt teszi a több oldalon használt erőforrásokkal
Ezekkel a táblákkal az az olvasó, amely a 40. oldalt kéri, nem elemzi végig a fájlt sorban. Megnézi a hinttáblában, melyik bájttartományt foglalja el a 40. oldal, pontosan azt kéri le a kiszolgálótól, és kirajzolja az oldalt, amint azok a bájtok megérkeztek, ugyanezzel a mechanizmussal behúzva minden olyan megosztott erőforrást, amely még nincs meg neki. A hint stream lényegében a dokumentumra terített közvetlen elérésű térkép, és ez az oka annak, hogy egy jól linearizált 500 oldalas fájl lassú kapcsolaton is fürgének hat, egy azonos méretű, nem optimalizált pedig nem
Miért kell a kiszolgálónak is együttműködnie
A linearizálás feltételezi, hogy az átvitel a fájl tetszőleges szeleteit képes kézbesíteni, és ezt a feltevést érdemes ellenőrizni, mielőtt a gyenge eredményért a formátumot okolnánk. A mechanizmus a HTTP bájtkiszolgálás: az olvasó tartománykéréseket ad ki, a kiszolgáló pedig 206 Partial Content válaszokkal felel rájuk. Ha a kiszolgáló nem hirdeti meg az Accept-Ranges: bytes fejlécet, vagy ha egy előtte álló proxy vagy CDN teljes átvitellé olvasztja a tartománykéréseket, az olvasónak nincs módja önmagában letölteni a 40. oldalt, és visszaesik a teljes fájl letöltésére. A PDF-en belüli szerkezet ilyenkor tökéletesen helyes és teljesen kárba vész
Ez az a hiba, amelyet a leggyakrabban félrediagnosztizálnak úgy, hogy „a linearizálás nem működik”. A fájllal semmi baj; a kézbesítési úttal van. Mielőtt újraépítenénk egy dokumentumot, ellenőrizzük egy feltételes kéréssel, hogy a kiszolgáló valóban részleges tartalmat ad-e vissza arra az URL-re, amelyet az olvasó megüt. Sok statikus tárhely alapból ezt teszi, sok rosszul beállított alkalmazáskiszolgáló és gyorsítótárréteg pedig nem
A növekményes frissítések csendben elrontják a linearizálást
Itt jön az a megkötés, amely meglepi azokat, akik helyesen állítanak elő linearizált fájlokat, majd értetlenül állnak, miért párolog el az optimalizálás. A linearizálás egyetlen, gondosan rendezett elrendezésen múlik, amelynek indexe elöl áll. Egy növekményes frissítés ezt eleve megsérti. Amikor egy eszköz aláírást ad hozzá, űrlapmezőt tölt ki vagy jegyzetet fűz a fájlhoz növekményes mentéssel, nem írja újra a fájlt. A megváltozott objektumokat, egy új kereszthivatkozási szakaszt és egy új trailert fűz a végére, az eredeti bájtokat érintetlenül hagyva. Ez a hozzáfűzés a növekményes frissítés egész lényege: gyors, és megőrzi a korábbi változatot auditáláshoz vagy aláírás-ellenőrzéshez
A mellékhatás az, hogy a fájl legfrissebb kereszthivatkozási adatai most a végén vannak, a gondosan elhelyezett első oldalhoz tartozó blokk után, az elején álló linearizálási paraméterszótár pedig olyan elrendezést ír le, amely már nem felel meg a fájlnak. A szabványt követő olvasó észleli az eltérést, és a dokumentumot szokásos, nem linearizált PDF-ként kezeli. A Fast Web View eltűnt, jóllehet az eredeti linearizált szerkezet még mindig ott ül a fájl első felében. Ha több frissítést fűzünk hozzá, mindegyik újabb változatot rak a végére, és tovább nő a szakadék az elavult elülső index és a valós állapot között
Ha a munkafolyamatnak szerkesztésre és Fast Web View-ra is szüksége van, a szabály közvetlenül a szerkezetből következik: amíg a dokumentum alakul, szerkesszünk növekményesen, majd a végén linearizáljunk újra egyszer. Az elrendezést a teljes újraírás állítja helyre. HotPDF fogalmakkal: a folyamatban lévő szerkesztés a BeginIncrementalUpdate és a SaveIncrementalUpdate hívásokon megy át, amelyek egy különbözetet fűznek hozzá, a záró lépés pedig betölti a teljes dokumentumot, és frissen sorosítja a LoadFromFile, majd a SaveLoadedDocument hívással, ami eldobja a felhalmozott régi változatokat, és egyetlen tiszta elrendezést ad ki. Ugyanez az alku jelenik meg az objektumstreameknél is: az UseObjectStreams és az UseXRefStream együttes bekapcsolása tömöríti a kereszthivatkozást, és szorosan pakolja az objektumokat, ami a fájlméretnek jót tesz, de mint minden szerkezeti döntést, ezt is a záró újraíráskor kell alkalmazni, nem egy hozzáfűzött változatra ráaggatva
// Menet közbeni szerkesztések: különbözetet fűz hozzá, a korábbi változatokat érintetlenül hagyja.
// Ettől a fájl NEM lesz linearizált.
Pdf.BeginIncrementalUpdate('report.pdf');
Pdf.AddPage;
Pdf.CurrentPage.TextOut(72, 760, 0, 'Addendum');
Pdf.SaveIncrementalUpdate('report.pdf');
// Záró lépés: a teljes újrasorosítás egyetlen tiszta elrendezést ad,
// eldobva az egymásra rakott változatokat. A kimeneten futtassuk újra a linearizálót.
Pdf.LoadFromFile('report.pdf');
Pdf.SaveLoadedDocument('report-final.pdf');
A HotPDF nem kínál egyetlen hívásból álló „linearizáló” eljárást, ezért a gyakorlati minta az, hogy tiszta, teljesen újraírt fájlt állítunk elő, és egy erre való optimalizálót futtatunk rajta. A parancssori eszközök közvetlenül elvégzik az átrendezést. A qpdf egyetlen kapcsolóval írja át a fájlt linearizált alakra:
qpdf --linearize report-final.pdf report-web.pdf
Hogyan derül ki, hogy egy fájl linearizált-e
Ne bízzunk a fájlnévben, sem abban az eszközben, amely az előállítást állítja magáról; ellenőrizzük a bájtokat. A legközvetlenebb próba a fájl eleje: nyissuk meg, és keressük a linearizálási paraméterszótárt a fejléc utáni első objektumként, a /Linearized kulccsal. Olvasói oldalon gyors megoldás az Acrobat Dokumentum tulajdonságai párbeszédpanele, amely csak akkor jelenti a „Fast Web View: Yes” értéket, ha a szerkezet valóban jelen van és naprakész
Szkriptelt ellenőrzéshez a qpdf a szerkezet meglétéről és épségéről is beszámol, ami azért számít, mert egy fájl hordozhat olyan linearizálási szótárt, amely már nem tükrözi az elrendezését – pontosan ezt az állapotot hagyja maga után egy növekményes frissítés:
# Jelenti a "File is linearized" üzenetet, és az elrendezéshez viszonyítva ellenőrzi a hint táblákat
qpdf --check report-web.pdf
# Részletesen kiírja a linearizálási paramétereket és a hintadatokat
qpdf --show-linearization report-web.pdf
Az ellenőrző lépés az, amely megszolgálja a helyét. Az a próba, amely csak a szótár meglétét igazolja, örömmel megáldja azt a fájlt is, amelynek indexe rossz eltolásokra mutat; az a vizsgálat mondja meg, hogy az optimalizálás kibírja-e egy valódi olvasó tartománykéréseit, amely a hinttáblákat a tényleges objektumpozíciókhoz méri
A linearizálás továbbra is megéri minden weben kiszolgált nagy dokumentumnál, kiváltképp egyenetlen kapcsolaton dolgozó mobilolvasóknál, és néhány százaléknyi fájlméretbe kerül az előre hozott index miatt. Két dolgot érdemes fejben tartani: a PDF-en belüli szerkezetnek és a rajta kívüli bájtkiszolgálásnak egyaránt rendben kell lennie, és minden utólagos szerkesztés visszacsinálja az optimalizálást, amíg újra nem írjuk a fájlt. Kezeljük az újralinearizálást a folyamat utolsó lépéseként, miután minden más változtatás lezárult. Az itt leírt kereszthivatkozási, objektumstream- és növekményesfrissítés-viselkedés része annak a szerkezeti modellnek, amelyet a HotPDF Delphi Component valósít meg Delphi és C++Builder alatt; a tágabb fájlelrendezési háttérhez lásd, hogyan épül fel egy PDF, a növekményes frissítés és a nagy fájlok kódbeli munkafolyamatához pedig azt, hogyan dolgozzunk fel nagy PDF-eket Delphiből