Teknisk artikkel

PDF-linearisering og Fast Web View: slik virker det

Legg en skannet rapport på 80 MB bak en lenke, åpne den i en nettleser, og se hva som skjer: viseren blir sittende på et blankt felt til en stor andel av de bytene har kommet frem, og maler så side én på én gang. Hopp til side 40, og på en dårlig bygget fil kan hele nedlastingen starte på nytt. Det frustrerende er at leseren bare ville ha den første siden. Linearisering er det strukturelle svaret på det problemet. Den omorganiserer en PDF slik at en viser kan gjengi åpningssiden fra et lite prefiks av filen og hente resten ved behov, som er grunnen til at Adobe markedsfører funksjonen som «Fast Web View»

Ingenting av dette er et annet filformat. En linearisert PDF er en helt vanlig PDF som en leser som følger spesifikasjonen åpner uten særskilt håndtering. Trikset ligger utelukkende i hvordan bytene er ordnet, og i to ekstra strukturer filen bærer. ISO 32000-1 spesifiserer hele arrangementet i vedlegg F, og når du først har sett oppsettet, slutter oppførselen å se ut som magi og begynner å se ut som en bevisst bytting av filrekkefølge mot ventetid til første opptegning

Hva linearisering faktisk omorganiserer

En vanlig PDF kan spre objektene sine i nesten hvilken som helst rekkefølge. Kryssreferansetabellen på slutten av filen er det som får det til å virke: en leser søker til slutten, leser startxref-pekeren, laster inn xref-en, og kan derfra finne hvert objekt ved hjelp av offsetet. Den utformingen er utmerket for lokale filer, der det å søke til slutten ikke koster noe, og dårlig for en fil som strømmer inn over et nettverk, der slutten er nøyaktig den delen som kommer sist. For å gjengi side én trenger en konvensjonell leser sideobjektet, innholdsstrømmen, fontene den refererer til og eventuelle bilder den tegner, og i en uordnet fil kan de ligge hvor som helst, også i den siste megabyten

Linearisering retter opp rekkefølgen. Objektene som trengs for å vise den første siden, samles i en sammenhengende blokk nær fronten, rett etter en liten hodeseksjon, slik at de kommer tidlig i bytestrømmen. Alt annet, de gjenværende sidene og ressursene de deler, følger i en forutsigbar rekkefølge. En andre, fullstendig kryssreferansetabell bor fortsatt på slutten for lesere som overser optimaliseringen, men en linearisert fil plasserer også en kryssreferanse for første side og parametrene en strømmende leser trenger, helt foran. Leseren må ikke lenger nå halen før den kan tegne noe som helst

PDF: Byteoppsettet i en vanlig PDF og en linearisert PDF side om side, det som muliggjør fast web view
En linearisert fil flytter lineariseringsdictionaryen, xref-en for første side og hint-strømmen foran alt annet, mens en vanlig PDF beholder sin eneste indeks i halen av filen

Objektsettet for første side og lineariseringsparameterdictionaryen

Aller første objekt i en linearisert fil, etter %PDF-hodet, er lineariseringsparameterdictionaryen. Det er den en strømmende leser ser etter for å avgjøre om optimaliseringen er til stede og hvordan den skal brukes. Dictionaryen registrerer lengden på hele filen, byteoffsetet der hovedkryssreferanseseksjonen begynner, objektnummeret til den første siden, og plasseringen og lengden på hint-strømmen som følger. Med de tallene vet en leser, ut fra de første kilobytene alene, hvor mye den må hente for å vise side én og hvor den skal lete etter indeksen som lar den hoppe andre steder

Vedlegg F er strengt på hva «første side» betyr her. Førstesideseksjonen må inneholde selve sideobjektet, innholdsstrømmene og ressursene de strømmene refererer til, slik at siden er selvforsynt når det prefikset er lastet ned. Delte ressurser, en font som brukes på hver side, en logo som gjentas i en topptekst, håndteres særskilt: de dukker opp tidlig nok til å betjene den første siden, men merkes som delte slik at leseren ikke henter dem på nytt når den senere gjengir side 30. Det skillet mellom sideprivate og delte objekter er den delen de fleste hjemmelagde «optimaliserere» gjør feil, og å gjøre det feil er det som produserer en fil som utgir seg for å være linearisert, men fortsatt stopper opp

Hint-strømmer: indeksen som gjør sidehopp billige

Å vise side én raskt er bare halve verdien. Den andre halvparten er å hoppe til en vilkårlig side uten å laste ned alt imellom, og det er det hint-strømmene gir. En linearisert fil bærer en hint-tabell for sideoffseter og en for delte objekter, lagret som en strøm referert fra parameterdictionaryen. Sideoffsettabellen registrerer, for hver side, hvor objektene dens begynner i filen og hvor langt de strekker seg. Tabellen for delte objekter gjør det samme for ressurser brukt på tvers av flere sider

Gitt de tabellene parser ikke en leser som vil ha side 40, filen sekvensielt. Den slår opp i hint-tabellen for å finne byteområdet side 40 opptar, ber tjeneren om nøyaktig det området, og gjengir siden når de bytene ankommer, og henter eventuelle delte ressurser den ikke allerede har, gjennom den samme mekanismen. Hint-strømmen er i praksis et kart for tilfeldig tilgang lagt over dokumentet, og den er grunnen til at en velinearisert fil på 500 sider føles responsiv over en treg linje mens en uoptimalisert fil av samme størrelse ikke gjør det

Hvorfor tjeneren må samarbeide

Linearisering forutsetter at transporten kan levere vilkårlige skiver av filen, og den forutsetningen er verdt å kontrollere før du gir formatet skylden for dårlige resultater. Mekanismen er HTTP-byteservering: leseren sender områdeforespørsler, og tjeneren besvarer dem med 206 Partial Content-svar. Hvis tjeneren ikke annonserer Accept-Ranges: bytes, eller hvis en mellomtjener eller et CDN foran den slår områdeforespørsler sammen til fulle overføringer, har leseren ingen måte å hente side 40 isolert på, og faller tilbake til å laste ned hele filen. Strukturen inne i PDF-en er da helt korrekt og fullstendig bortkastet

Dette er feilen som oftest blir feildiagnostisert som at «linearisering virker ikke». Filen er i orden; leveringsveien er det ikke. Før du bygger et dokument på nytt, bekreft med en betinget forespørsel at verten faktisk returnerer delvis innhold for URL-en leseren treffer. Mange statiske verter gjør dette som standard, og mange feilkonfigurerte applikasjonstjenere og hurtiglagerlag gjør det ikke

PDF: PDF-lineariseringens hint-tabeller som driver en HTTP-områdeforespørsel som returnerer 206 Partial Content for én side
Hint-tabeller gir leseren byteområdet til enhver side, så en områdekyndig vert leverer side 40 med ett delvis svar; uten byteservering lastes hele filen ned igjen

Inkrementelle oppdateringer ødelegger stille lineariseringen

Her er begrensningen som overrasker folk som genererer lineariserte filer korrekt og så lurer på hvorfor optimaliseringen fordamper. Linearisering avhenger av ett enkelt, nøye ordnet oppsett med indeksen foran. En inkrementell oppdatering bryter med det etter sin natur. Når et verktøy legger til en signatur, fyller ut et skjemafelt eller føyer til en merknad gjennom en inkrementell lagring, skriver det ikke om filen. Det føyer de endrede objektene, en ny kryssreferanseseksjon og en ny trailer til slutten, og lar de opprinnelige bytene være urørt. Den tilføyingen er hele poenget med inkrementelle oppdateringer: den er rask, og den bevarer den tidligere revisjonen for revisjonsspor eller signaturvalidering

Bivirkningen er at filen nå har sine nyeste kryssreferansedata i halen, etter den nøye plasserte førstesideblokken, og at lineariseringsparameterdictionaryen foran beskriver et oppsett som ikke lenger stemmer med filen. En leser som følger spesifikasjonen oppdager uoverensstemmelsen og behandler dokumentet som en vanlig, ikke-linearisert PDF. Fast Web View er borte, selv om den opprinnelige lineariserte strukturen fortsatt ligger der i første halvdel av filen. Føyer du på flere oppdateringer, stabler hver av dem enda en revisjon på slutten, og gapet mellom den foreldede frontindeksen og den virkelige tilstanden vokser

PDF: Inkrementelle oppdateringer føyer til revisjoner som desynkroniserer en linearisert dictionary til én avsluttende omskriving gjenoppretter fast web view
Hver inkrementelle lagring stabler enda en revisjon forbi den foreldede frontdictionaryen; å avslutte med en full omskriving og relinearisere til slutt holder Fast Web View intakt

Trenger arbeidsflyten din både redigeringer og Fast Web View, følger regelen direkte av strukturen: rediger inkrementelt mens dokumentet er i støpeskjeen, og linearisér så på nytt én gang til slutt. En full omskriving er det som gjenoppretter oppsettet. I HotPDF-termer betyr det at en pågående redigering går gjennom BeginIncrementalUpdate og SaveIncrementalUpdate, som føyer til en delta, mens det avsluttende trinnet laster inn hele dokumentet og serialiserer det på nytt med LoadFromFile etterfulgt av SaveLoadedDocument, som forkaster de oppsamlede gamle revisjonene og skriver ut ett rent oppsett. Den samme avveiningen dukker opp med objektstrømmer: å slå på UseObjectStreams sammen med UseXRefStream komprimerer kryssreferansen og pakker objektene tett, noe som hjelper på filstørrelsen, men som ethvert strukturelt valg må anvendes under den avsluttende omskrivingen i stedet for å boltes på en tilføyd revisjon

// Redigeringer underveis: føy til en delta, hold tidligere revisjoner intakte.
// Dette etterlater filen IKKE linearisert.
Pdf.BeginIncrementalUpdate('report.pdf');
Pdf.AddPage;
Pdf.CurrentPage.TextOut(72, 760, 0, 'Addendum');
Pdf.SaveIncrementalUpdate('report.pdf');

// Avsluttende trinn: full reserialisering gir ett rent oppsett,
// og forkaster de stablede revisjonene. Kjør linearisereren på utdataene.
Pdf.LoadFromFile('report.pdf');
Pdf.SaveLoadedDocument('report-final.pdf');

HotPDF eksponerer ingen ettkalls-«linearize»-rutine, så det praktiske mønsteret er å produsere en ren, fullstendig omskrevet fil og kjøre en dedikert optimaliserer over den. Kommandolinjeverktøy håndterer omorganiseringen direkte. qpdf skriver om en fil til linearisert form med ett enkelt flagg:

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

Hvordan du finner ut om en fil er linearisert

Ikke stol på filnavnet eller på verktøyet som hevder å ha produsert den; verifiser bytene. Den mest direkte kontrollen er filhodet: åpne det og se etter lineariseringsparameterdictionaryen som første objekt etter hodet, med nøkkelen /Linearized. En snarvei på lesersiden er Acrobats dialog for dokumentegenskaper, som melder «Fast Web View: Yes» bare når strukturen faktisk er til stede og aktuell

For skriptede kontroller melder qpdf både om strukturen finnes og om den henger sammen, noe som betyr noe fordi en fil kan bære en lineariseringsdictionary som ikke lenger gjenspeiler oppsettet, nøyaktig den tilstanden en inkrementell oppdatering etterlater:

# Rapporterer "File is linearized" og validerer hint-tabellene mot layouten
qpdf --check report-web.pdf

# Dumper lineariseringsparametrene og hintdataene i detalj
qpdf --show-linearization report-web.pdf

Valideringstrinnet er det som gjør nytte for seg. En kontroll som bare bekrefter at dictionaryen finnes, vil velvillig godkjenne en fil hvis indeks peker på feil offseter; en kontroll som avstemmer hint-tabellene mot de faktiske objektposisjonene, er det som forteller deg at optimaliseringen holder under en ekte lesers områdeforespørsler

Linearisering er fortsatt verdt å bruke på ethvert stort dokument som serveres over nettet, særlig til mobile lesere på ujevne forbindelser, og den koster noen få prosent av filstørrelsen for den frontlastede indeksen. De to tingene å holde styr på er at både strukturen inne i PDF-en og byteserveringen utenfor den må være riktige, og at enhver redigering i etterkant opphever optimaliseringen inntil du skriver filen om. Behandle relinearisering som siste trinn i rørledningen, etter at hver annen endring er avklart. Oppførselen for kryssreferanser, objektstrømmer og inkrementelle oppdateringer som er beskrevet her, er en del av den strukturelle modellen HotPDF Delphi Component for Delphi og C++Builder implementerer; for den bredere bakgrunnen om filoppsett, se hvordan en PDF er strukturert, og for arbeidsflyten med inkrementelle oppdateringer og store filer i kode, se behandling av store PDF-er fra Delphi