Lägg en skannad rapport på 80 MB bakom en länk, öppna den i en webbläsare och se vad som händer: visaren sitter på en tom ruta tills en stor del av dessa byte har anlänt, och målar sedan upp sida ett på en gång. Hoppa till sida 40 och på en dåligt byggd fil kan hela nedladdningen starta om. Det frustrerande är att läsaren bara ville ha den första sidan hela tiden. Linjärisering är det strukturella svaret på det problemet. Den omarrangerar en PDF så att en visare kan rendera öppningssidan från ett litet prefix av filen och hämta resten på begäran, vilket är varför Adobe marknadsför funktionen som "Fast Web View"
Inget av detta är ett annorlunda filformat. En linjäriserad PDF är en vanlig PDF som en överensstämmande läsare kommer att öppna utan någon särskild hantering. Tricket ligger helt i hur byten är ordnade och i två extra strukturer som filen bär med sig. ISO 32000-1 specificerar hela arrangemanget i bilaga F, och när du väl har sett layouten slutar beteendet att se ut som magi och börjar se ut som ett avsiktligt utbyte av filordning mot fördröjning vid första uppritningen
Praktiskt sammanhang
En normal PDF kan sprida sina objekt i nästan vilken ordning som helst. Korsreferenstabellen i slutet av filen är det som får detta att fungera: en läsare söker sig till slutet, läser pekaren startxref, laddar xref, och därifrån kan den lokalisera varje objekt efter dess förskjutning. Den designen är utmärkt för lokala filer, där sökning till slutet inte kostar något, och dålig för en fil som strömmar in över ett nätverk, där slutet är exakt den del som anländer sist. För att rendera sida ett behöver en konventionell läsare sidobjektet, dess innehållsström, teckensnitten det refererar till och eventuella bilder det ritar, och i en oordnad fil kan dessa sitta var som helst, inklusive i den sista megabyten
Linjärisering fixar ordningen. Objekten som behövs för att visa den första sidan samlas i ett angränsande block nära början, precis efter en liten rubriksektion, så att de anländer tidigt i byteströmmen. Allt annat, de återstående sidorna och de resurser de delar, följer i en förutsägbar sekvens. En andra, komplett korsreferenstabell lever fortfarande i slutet för läsare som ignorerar optimeringen, men en linjäriserad fil placerar också en korsreferens för första sidan och de parametrar en strömmande läsare behöver i början. Läsaren behöver inte längre nå svansen innan den kan rita någonting
Implementeringssteg
Det allra första objektet i en linjäriserad fil, efter %PDF-rubriken, är ordboken för linjäriseringsparametrar. Det är vad en strömmande läsare letar efter för att avgöra om optimeringen finns och hur den ska användas. Ordboken registrerar längden på hela filen, den byteförskjutning där den huvudsakliga korsreferenssektionen börjar, objektnumret på den första sidan, samt platsen och längden på hintströmmen som följer. Med de siffrorna vet en läsare, enbart från de inledande kilobytena, hur mycket den måste hämta för att visa sida ett och var den ska leta efter indexet som låter den hoppa någon annanstans
Bilaga F är strikt om vad "första sidan" betyder här. Förstasidesektionen måste innehålla själva sidobjektet, dess innehållsströmmar och resurserna dessa strömmar refererar till, så att sidan är självtillräcklig när prefixet väl har laddats ned. Delade resurser, ett teckensnitt som används på varje sida, en logotyp som upprepas i en sidfot, hanteras särskilt: de dyker upp tidigt nog för att betjäna den första sidan men flaggas som delade så att läsaren inte hämtar dem på nytt när den senare renderar sida 30. Denna distinktion mellan sidoprivata och delade objekt är den del de flesta hemmasnickrade "optimerare" gör fel med, och att göra det fel är vad som producerar en fil som påstår sig vara linjäriserad men ändå stannar upp
Kontrollpunkter
Att visa sida ett snabbt är bara halva värdet. Den andra halvan är att hoppa till en godtycklig sida utan att ladda ner allt däremellan, och det är vad hintströmmarna tillhandahåller. En linjäriserad fil bär med sig en hinttabell för sidförskjutning och en hinttabell för delade objekt, lagrade som en ström som refereras från parameterordboken. Tabellen för sidförskjutning registrerar, för varje sida, var dess objekt börjar i filen och hur långa de är. Tabellen för delade objekt gör samma sak för resurser som används på tvärs av flera sidor
Givet de tabellerna så tolkar en läsare som vill ha sida 40 inte filen sekventiellt. Den konsulterar hinttabellen för att lära sig det byteintervall sida 40 upptar, ber servern om exakt det intervallet, och renderar sidan när dessa byten anländer, och drar in alla delade resurser som den inte redan har genom samma mekanism. Hintströmmen är i praktiken en karta för slumpmässig åtkomst lagd över dokumentet, och den är anledningen till att en väl linjäriserad 500-sidors fil känns responsiv över en långsam länk medan en ooptimerad i samma storlek inte gör det
Praktiskt sammanhang
Linjärisering förutsätter att transporten kan leverera godtyckliga skivor av filen, och det antagandet är värt att kontrollera innan du skyller på formatet för dåliga resultat. Mekanismen är byte-servering över HTTP: läsaren utfärdar intervallförfrågningar (range requests) och servern besvarar dem med svar på 206 Partial Content. Om servern inte annonserar Accept-Ranges: bytes, eller om en proxy eller ett CDN framför den komprimerar intervallförfrågningar till fullständiga överföringar, har läsaren inget sätt att hämta sida 40 isolerat och faller tillbaka på att ladda ner hela filen. Strukturen inuti PDF:en är då helt korrekt och fullkomligt bortkastad
Detta är misslyckandet som oftast feldiagnosticeras som "linjärisering fungerar inte". Filen är bra; det är leveransvägen som inte är det. Innan du bygger om ett dokument, bekräfta med en villkorlig begäran att värden faktiskt returnerar partiellt innehåll för webbadressen läsaren besöker. Många statiska värdar gör detta som standard, och många felkonfigurerade applikationsservrar och cachningslager gör det inte
Implementeringssteg
Här är begränsningen som överraskar människor som genererar linjäriserade filer korrekt och sedan undrar varför optimeringen fördunstar. Linjärisering är beroende av en enda, noggrant ordnad layout med sitt index i början. En inkrementell uppdatering bryter mot det genom design. När ett verktyg lägger till en signatur, fyller i ett formulärfält eller lägger till en kommentar genom ett inkrementellt sparande, skriver det inte om filen. Den lägger till de ändrade objekten, en ny korsreferenssektion och en ny avslutning i slutet, vilket lämnar de ursprungliga bytena orörda. Det tillägget är hela poängen med inkrementella uppdateringar: det är snabbt och det bevarar den tidigare revisionen för granskning eller signaturvalidering
Bieffekten är att filen nu har sina nyaste korsreferensdata i svansen, efter det noggrant placerade förstasidesblocket, och linjäriseringsparameterordboken i början beskriver en layout som inte längre stämmer överens med filen. En överensstämmande läsare upptäcker skillnaden och behandlar dokumentet som en normal, icke-linjäriserad PDF. Fast Web View är borta, även om den ursprungliga linjäriserade strukturen fortfarande sitter där i den första halvan av filen. Om du bifogar flera uppdateringar så staplas ytterligare en revision ovanpå den andra i slutet och gapet mellan det inaktuella frontindexet och det verkliga tillståndet vidgas
Om ditt arbetsflöde behöver både redigeringar och Fast Web View, följer regeln direkt från strukturen: redigera inkrementellt medan dokumentet är i förändring, och linjärisera sedan om en gång på slutet. En fullständig omskrivning är vad som återställer layouten. I termer av HotPDF betyder det att en pågående redigering går via BeginIncrementalUpdate och SaveIncrementalUpdate, vilka bifogar ett delta, medan det avslutande steget laddar hela dokumentet och serialiserar det på nytt med LoadFromFile följt av SaveLoadedDocument, vilket kastar de samlade gamla revisionerna och sänder ut en ren layout. Samma utbyte dyker upp med objektströmmar: att aktivera UseObjectStreams tillsammans med UseXRefStream komprimerar korsreferensen och packar objekten tätt, vilket hjälper filstorleken men, i likhet med alla strukturella val, måste det appliceras under den slutliga omskrivningen i stället för att skruvas fast på en bifogad revision
// 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 exponerar inte en enda "linjärisera"-rutin, så det praktiska mönstret är att producera en ren, fullständigt omskriven fil och köra en dedikerad optimerare över den. Kommandoradsverktyg hanterar omarrangemanget direkt. qpdf skriver om en fil till linjäriserad form med en enda flagga:
qpdf --linearize report-final.pdf report-web.pdf
Kontrollpunkter
Lita inte på filnamnet eller verktyget som gör anspråk på att ha producerat det; verifiera bytena. Den mest direkta kontrollen är början av filen: öppna den och leta efter ordboken för linjäriseringsparametrar som det första objektet efter rubriken, som bär på nyckeln /Linearized. En läsarvänd genväg är Acrobats dialogruta för dokumentegenskaper, som rapporterar "Fast Web View: Yes" bara när strukturen verkligen är närvarande och aktuell
För skriptade kontroller rapporterar qpdf både närvaron och integriteten för strukturen, vilket spelar roll eftersom en fil kan bära på en linjäriseringsordbok som inte längre återspeglar dess layout, exakt det tillstånd en inkrementell uppdatering lämnar efter sig:
# 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
Valideringssteget är det som förtjänar sitt värde. En genomgång som bara bekräftar att ordboken existerar kommer med glädje att välsigna en fil vars index pekar på fel förskjutningar; en kontroll som stämmer av hinttabellerna mot de faktiska objektpositionerna är vad som talar om för dig att optimeringen kommer att hålla måttet under en verklig läsares intervallförfrågningar
Linjärisering är fortfarande värd att tillämpa på alla stora dokument som serveras över nätet, särskilt för mobila läsare på ojämna uppkopplingar, och det kostar några procent av filstorleken för det frontladdade indexet. De två sakerna att hålla raka är att strukturen inuti PDF:en och byteserveringen utanför den båda måste vara rätt, och att eventuella redigeringar i efterhand ångrar optimeringen tills du skriver om filen. Behandla om-linjärisering som det sista steget i rörledningen, efter att alla andra ändringar är avgjorda. Korsreferensen, objektströmmen och det inkrementella uppdateringsbeteendet som beskrivs här är en del av den strukturella modell som HotPDF Component för Delphi och C++Builder implementerar. För en bredare bakgrund om fillayouten, se hur en PDF är strukturerad, och för inkrementella uppdateringar och ett stort filarbetsflöde i kod, se att bearbeta stora PDF-filer från Delphi