Techninis straipsnis

PDF linearizavimas ir Fast Web View: kaip tai veikia

Padėkite 80 MB skenuotą ataskaitą už nuorodos, atverkite ją naršyklėje ir stebėkite, kas vyksta: peržiūros programa spokso į tuščią sritį, kol atkeliauja didelė dalis tų baitų, o tada iš karto nupiešia pirmą puslapį. Nušokite į 40 puslapį ir blogai sudėtame faile visas atsisiuntimas gali prasidėti iš naujo. Erzinantis dalykas tas, kad skaitytojas visą laiką norėjo tik pirmojo puslapio. Linearizavimas yra struktūrinis atsakymas į šią problemą. Jis pertvarko PDF taip, kad peržiūros programa galėtų atvaizduoti pradinį puslapį iš mažo failo priešdėlio, o likusią dalį parsisiųsti pagal poreikį, ir būtent todėl Adobe šią savybę rinkoje vadina „Fast Web View“

Nieko iš to nėra kitoks failo formatas. Linearizuotas PDF yra paprastas PDF, kurį standartą atitinkantis skaitytuvas atvers be jokio ypatingo apdorojimo. Visas triukas slypi vien baitų tvarkoje ir dviejose papildomose struktūrose, kurias failas neša. ISO 32000-1 visą šią sąrangą aprašo F priede, ir kartą pamačius išdėstymą, elgsena nustoja atrodyti kaip magija ir ima atrodyti kaip sąmoningas failo tvarkos iškeitimas į pirmojo piešimo delsą

Ką linearizavimas iš tikrųjų pertvarko

Paprastas PDF savo objektus gali išbarstyti beveik bet kokia tvarka. Tai veikia dėl kryžminių nuorodų lentelės failo gale: skaitytuvas nušoka į pabaigą, nuskaito startxref rodyklę, įkelia xref ir nuo ten pagal poslinkį gali rasti kiekvieną objektą. Toks sumanymas puikus vietiniams failams, kur šuolis į pabaigą nieko nekainuoja, ir prastas failui, atkeliaujančiam srautu per tinklą, kur pabaiga yra būtent ta dalis, kuri atvyksta paskutinė. Kad nupieštų pirmą puslapį, įprastam skaitytuvui reikia puslapio objekto, jo turinio srauto, jo nurodomų šriftų ir visų jo piešiamų paveikslėlių, o netvarkingame faile visa tai gali gulėti bet kur, įskaitant paskutinį megabaitą

Linearizavimas tvarką sutaiso. Objektai, reikalingi pirmam puslapiui parodyti, surenkami į vientisą bloką arti priekio, iškart po nedidele antraštės sekcija, todėl jie atkeliauja baitų sraute anksti. Visa kita — likę puslapiai ir jų bendrai naudojami ištekliai — seka nuspėjama seka. Antra, pilna kryžminių nuorodų lentelė vis dar gyvena gale tiems skaitytuvams, kurie optimizavimo nepaiso, tačiau linearizuotas failas priekyje dar padeda pirmojo puslapio kryžmines nuorodas ir parametrus, kurių reikia srautiniam skaitytuvui. Skaitytuvui nebereikia pasiekti uodegos, kad galėtų ką nors nupiešti

PDF: greta sugretinti paprasto PDF ir linearizuoto PDF, įgalinančio fast web view, baitų išdėstymai
Linearizuotas failas linearizavimo žodyną, pirmojo puslapio xref ir užuominų srautą perkelia priekyje visko kito, o paprastas PDF savo vienintelę rodyklę laiko failo uodegoje

Pirmojo puslapio objektų rinkinys ir linearizavimo parametrų žodynas

Pats pirmasis objektas linearizuotame faile po %PDF antraštės yra linearizavimo parametrų žodynas. Būtent jo ieško srautinis skaitytuvas, kad nuspręstų, ar optimizavimas yra ir kaip juo naudotis. Žodynas įrašo viso failo ilgį, baito poslinkį, kuriame prasideda pagrindinė kryžminių nuorodų sekcija, pirmojo puslapio objekto numerį ir po jo einančio užuominų srauto vietą bei ilgį. Su šiais skaičiais skaitytuvas jau iš pirmųjų kilobaitų žino, kiek turi parsisiųsti, kad parodytų pirmą puslapį, ir kur ieškoti rodyklės, leidžiančios nušokti kitur

F priedas griežtai apibrėžia, ką čia reiškia „pirmas puslapis“. Pirmojo puslapio sekcijoje turi būti pats puslapio objektas, jo turinio srautai ir tų srautų nurodomi ištekliai, kad parsisiuntus tą priešdėlį puslapis būtų savarankiškas. Bendrai naudojami ištekliai — kiekviename puslapyje naudojamas šriftas, antraštėje kartojamas logotipas — tvarkomi ypatingai: jie pasirodo pakankamai anksti, kad pasitarnautų pirmam puslapiui, tačiau pažymimi kaip bendri, kad skaitytuvas jų nesisiųstų iš naujo vėliau atvaizduodamas 30 puslapį. Būtent šį skirtumą tarp puslapiui privačių ir bendrų objektų dažniausiai supainioja namudiniai „optimizatoriai“, ir būtent klaida čia pagamina failą, kuris skelbiasi esąs linearizuotas, bet vis tiek stringa

Užuominų srautai: rodyklė, dėl kurios šuoliai per puslapius atpinga

Greitas pirmojo puslapio parodymas yra tik pusė vertės. Kita pusė yra šuolis į bet kurį puslapį neparsisiunčiant visko, kas tarp jų, ir būtent tai suteikia užuominų srautai. Linearizuotas failas neša puslapių poslinkių užuominų lentelę ir bendrų objektų užuominų lentelę, saugomas kaip srautą, nurodytą iš parametrų žodyno. Puslapių poslinkių lentelė kiekvienam puslapiui įrašo, kur faile prasideda jo objektai ir kiek jie tęsiasi. Bendrų objektų lentelė tą patį daro ištekliams, naudojamiems keliuose puslapiuose

Turėdamas šias lenteles, skaitytuvas, norintis 40 puslapio, failo neanalizuoja nuosekliai. Jis pasižiūri užuominų lentelėje, kokį baitų intervalą užima 40 puslapis, paprašo serverio būtent to intervalo ir, atkeliavus tiems baitams, puslapį atvaizduoja, tuo pačiu mechanizmu prisitraukdamas visus bendrus išteklius, kurių dar neturi. Užuominų srautas iš esmės yra virš dokumento paklotas laisvosios kreipties žemėlapis, ir būtent dėl jo gerai linearizuotas 500 puslapių failas per lėtą ryšį jaučiasi žvalus, o to paties dydžio neoptimizuotas — ne

Kodėl serveris turi bendradarbiauti

Linearizavimas remiasi prielaida, kad perdavimo kanalas gali pristatyti bet kokias failo atkarpas, ir šią prielaidą verta patikrinti prieš kaltinant formatą dėl prastų rezultatų. Mechanizmas yra HTTP baitų aptarnavimas: skaitytuvas siunčia intervalų užklausas, o serveris į jas atsako 206 Partial Content atsakymais. Jei serveris neskelbia Accept-Ranges: bytes arba jei prieš jį esantis tarpinis serveris ar CDN intervalų užklausas sutraukia į pilnus perdavimus, skaitytuvas neturi jokio būdo atsisiųsti vien 40 puslapį ir grįžta prie viso failo siuntimo. PDF vidinė struktūra tada yra visiškai teisinga ir visiškai iššvaistyta

Būtent šis gedimas dažniausiai klaidingai diagnozuojamas kaip „linearizavimas neveikia“. Failas yra tvarkingas; netvarkingas yra pristatymo kelias. Prieš perdarydami dokumentą, sąlygine užklausa įsitikinkite, kad serveris tam URL, į kurį kreipiasi skaitytuvas, iš tikrųjų grąžina dalinį turinį. Daugelis statinių serverių tai daro pagal numatytuosius nustatymus, o daugelis blogai sukonfigūruotų aplikacijų serverių ir podėlio sluoksnių — ne

PDF: PDF linearizavimo užuominų lentelės, valdančios HTTP intervalo užklausą, kuri vienam puslapiui grąžina 206 Partial Content
Užuominų lentelės skaitytuvui pasako bet kurio puslapio baitų intervalą, todėl intervalus mokantis serveris 40 puslapį pateikia vienu daliniu atsakymu; be baitų aptarnavimo iš naujo siunčiamas visas failas

Laipsniški atnaujinimai tyliai sugriauna linearizavimą

Štai apribojimas, kuris nustebina žmones, teisingai generuojančius linearizuotus failus ir paskui besistebinčius, kodėl optimizavimas išgaravo. Linearizavimas remiasi vienu kruopščiai sutvarkytu išdėstymu su rodykle priekyje. Laipsniškas atnaujinimas tai pažeidžia pagal patį savo sumanymą. Kai įrankis prideda parašą, užpildo formos lauką ar prikabina anotaciją per laipsnišką išsaugojimą, jis failo neperrašo. Jis prie galo prikabina pakeistus objektus, naują kryžminių nuorodų sekciją ir naują trailer, palikdamas pradinius baitus nepaliestus. Tas prikabinimas ir yra visa laipsniškų atnaujinimų esmė: jis greitas ir išsaugo ankstesnę versiją auditui arba parašo patikrai

Šalutinis poveikis toks, kad dabar failas naujausius kryžminių nuorodų duomenis turi uodegoje, už kruopščiai padėto pirmojo puslapio bloko, o linearizavimo parametrų žodynas priekyje aprašo išdėstymą, kuris failo nebeatitinka. Standartą atitinkantis skaitytuvas neatitikimą aptinka ir dokumentą traktuoja kaip įprastą, nelinearizuotą PDF. Fast Web View dingo, nors pirminė linearizuota struktūra vis dar tebeguli pirmoje failo pusėje. Prikabinus kelis atnaujinimus, kiekvienas jų sukrauna dar vieną versiją gale, ir atotrūkis tarp pasenusios priekinės rodyklės bei tikros būsenos platėja

PDF: laipsniški atnaujinimai prikabina versijas, kurios išderina linearizavimo žodyną, kol vienas galutinis perrašymas atkuria fast web view
Kiekvienas laipsniškas išsaugojimas sukrauna dar vieną versiją už pasenusio priekinio žodyno; užbaigus pilnu perrašymu ir linearizavus paskutinį kartą, Fast Web View išlieka sveikas

Jei jūsų darbo eigai reikia ir pataisų, ir Fast Web View, taisyklė plaukia tiesiai iš struktūros: kol dokumentas dar kinta, redaguokite laipsniškai, o gale vieną kartą linearizuokite iš naujo. Išdėstymą atkuria pilnas perrašymas. HotPDF terminais tai reiškia, kad vykstanti pataisa eina per BeginIncrementalUpdate ir SaveIncrementalUpdate, kurie prikabina pokytį, o užbaigimo žingsnis įkelia visą dokumentą ir serializuoja jį iš naujo su LoadFromFile, po kurio eina SaveLoadedDocument; tai nubraukia susikaupusias senas versijas ir išveda vieną švarų išdėstymą. Toks pats kompromisas išlenda su objektų srautais: UseObjectStreams įjungimas kartu su UseXRefStream suspaudžia kryžmines nuorodas ir tankiai supakuoja objektus, o tai padeda failo dydžiui, tačiau, kaip ir bet kuris struktūrinis pasirinkimas, turi būti pritaikytas per tą galutinį perrašymą, o ne prikabinamas prie prikabintos versijos

// Vykstančios pataisos: prikabinamas pokytis, ankstesnės versijos lieka sveikos.
// Po to failas NĖRA linearizuotas.
Pdf.BeginIncrementalUpdate('report.pdf');
Pdf.AddPage;
Pdf.CurrentPage.TextOut(72, 760, 0, 'Addendum');
Pdf.SaveIncrementalUpdate('report.pdf');

// Užbaigimo žingsnis: pilna pakartotinė serializacija pagamina vieną švarų
// išdėstymą ir nubraukia sukrautas versijas. Išvestį vėl paleiskite per linearizatorių.
Pdf.LoadFromFile('report.pdf');
Pdf.SaveLoadedDocument('report-final.pdf');

HotPDF neatskleidžia vieno iškvietimo „linearizuok“ procedūros, todėl praktinis šablonas yra pagaminti švarų, visiškai perrašytą failą ir paleisti per jį atskirą optimizatorių. Pertvarkymą tiesiogiai atlieka komandinės eilutės įrankiai. qpdf vienu jungikliu perrašo failą į linearizuotą pavidalą:

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

Kaip nustatyti, ar failas linearizuotas

Nepasitikėkite failo vardu ar įrankiu, kuris tvirtina jį pagaminęs; patikrinkite baitus. Tiesiausias patikrinimas yra failo pradžia: atverkite ją ir pažiūrėkite, ar pirmasis objektas po antraštės yra linearizavimo parametrų žodynas su raktu /Linearized. Skaitytojui skirtas trumpesnis kelias yra Acrobat dokumento savybių dialogas, kuris praneša „Fast Web View: Yes“ tik tada, kai struktūra tikrai yra ir yra aktuali

Scenarijams qpdf praneša ir apie struktūros buvimą, ir apie jos vientisumą, o tai svarbu, nes failas gali nešti linearizavimo žodyną, kuris jo išdėstymo nebeatitinka — būtent tokią būseną palieka laipsniškas atnaujinimas:

# Praneša "File is linearized" ir patikrina užuominų lenteles pagal išdėstymą
qpdf --check report-web.pdf

# Išsamiai išveda linearizavimo parametrus ir užuominų duomenis
qpdf --show-linearization report-web.pdf

Būtent tikrinimo žingsnis atidirba savo duoną. Patikra, kuri tik patvirtina, kad žodynas yra, mielai palaimins failą, kurio rodyklė rodo į neteisingus poslinkius; patikra, kuri sugretina užuominų lenteles su tikromis objektų pozicijomis, ir pasako, ar optimizavimas atlaikys tikro skaitytuvo intervalų užklausas

Linearizavimą vis dar verta taikyti bet kuriam dideliam dokumentui, pateikiamam per žiniatinklį, ypač mobiliesiems skaitytojams su nelygiu ryšiu, ir jis kainuoja kelis procentus failo dydžio dėl į priekį iškeltos rodyklės. Du dalykai, kuriuos reikia laikyti galvoje: teisinga turi būti ir struktūra PDF viduje, ir baitų aptarnavimas už jo ribų, o bet kokia vėlesnė pataisa optimizavimą atšaukia, kol failo neperrašote. Pakartotinį linearizavimą laikykite paskutiniu grandinės žingsniu, po to, kai visi kiti pakeitimai jau nusistovėję. Čia aprašyta kryžminių nuorodų, objektų srautų ir laipsniškų atnaujinimų elgsena yra dalis struktūrinio modelio, kurį įgyvendina HotPDF Delphi komponentas, skirtas Delphi ir C++Builder; platesnį failo išdėstymo foną rasite straipsnyje kaip sudarytas PDF, o laipsniškų atnaujinimų ir didelių failų darbo eigą kode — straipsnyje didelių PDF apdorojimas iš Delphi