Različica PDF 1.5 je uvedla dve shranjevalni strukturi, ki ju prejšnji format datoteke ni mogel izraziti: tok objektov (object stream) in tok navzkrižnih sklicev (cross-reference stream). Tok objektov je en sam Flate-stisnjen vsebnik z oznako /Type /ObjStm, ki vsebuje veliko majhnih posrednih objektov, zapakiranih drug za drugim, namesto da bi bili razpršeni po telesu datoteke. Tok navzkrižnih sklicev pa je iskalna tabela datoteke, zapisana kot stisnjeni binarni podatki s polji spremenljive širine, ki nadomešča tabelo ASCII fiksne širine na koncu vsakega PDF-ja do različice 1.4. Ti dve strukturi delujeta skupaj. Ko so objekti enkrat zapakirani v tok, jih stara besedilna tabela ne more več nasloviti, zato mora z njimi priti tudi binarni xref.
Če to primerjamo s klasično postavitvijo, je prihranek enostavno opaziti. V datoteki PDF 1.4 vsak posredni objekt stoji nestisnjen za svojo glavo obj, tabela na koncu pa porabi natanko 20 bajtov ASCII na vnos, pri čemer je stiskanje prepovedano. Dokument z 200.000 objekti prenaša približno 4 MB podatkov o navzkrižnih sklicih, še preden je izrisan en sam znak, na vrhu pa so naloženi vsi nestisnjeni slovarji. PDF 1.5 napada obe številki hkrati: slovarji se stisnejo v vsebnike Flate, 4 MB velika tabela pa se zmanjša na nekaj sto kilobajtov binarne kode. Standard ISO 32000-1 definira ti dve strukturi v poglavjih §7.5.7 in §7.5.8.
Kje se prihranki dejansko poznajo
Tokovi objektov vplivajo le na objekte, ki niso tokovi (non-stream objects), kar pomeni, da stisnejo strukturo in ne slikovnih pik. Vsebina strani je bila stisnjena s Flate že pred različico 1.5, slikovni podatki pa imajo lastne kodeke, zato se velikost brošure z veliko slikami komaj kaj spremeni. Datoteke, katerih velikost se drastično zmanjša, so tiste z bogato strukturo: obrazci AcroForm z več tisoč slovarji polj, globoka drevesa kazal, strukturni elementi označenih PDF-jev. Ti objekti so majhni, številni in skoraj identični drug drugemu, to ponavljanje pa je natanko tisto, kar Flate izkoristi, ko se znajdejo v enem samem medpomnilniku, namesto da bi bili razpršeni po telesu datoteke z vmesnimi glavami.
Hitro lahko podcenimo, kolikšen del stare datoteke predstavljajo režijski stroški. Arhiv obrazcev, ki je bil leta urejan, lahko porabi precej več kot polovico svojih bajtov za glave slovarjev, odmike xref in revizije, ki jih noben bralec ne bo nikoli pogledal. Dve tukaj opisani funkciji odpravljata prva dva izmed teh stroškov. Tretji, nakopičene revizije, pa se umakne šele stiskanju, ko datoteki ni več treba pomniti lastne zgodovine.
V HotPDF obe funkciji vklopite prek dveh lastnosti, pri čemer je njuna medsebojna odvisnost pomembnejša od vrstnega reda, v katerem ju zapišete:
var
Pdf: THotPDF;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.FileName := 'catalog-2026.pdf';
Pdf.UseXRefStream := True; // binary xref, prerequisite for ObjStm
Pdf.UseObjectStreams := True; // pack objects into /Type /ObjStm
Pdf.BeginDoc;
Pdf.CurrentPage.SetFont('Arial', [], 11);
Pdf.CurrentPage.TextOut(50, 760, 0, 'Compressed structure demo');
Pdf.EndDoc; // emits XRefStm + ObjStm containers
finally
Pdf.Free;
end;
end;
Lastnost UseObjectStreams zahteva, da je UseXRefStream nastavljen na True. Do stisnjenega objekta se dostopa prek vnosa xref tipa 2, ki zabeleži številko toka objekta in indeks, klasična 20-bajtna besedilna vrstica pa nima mesta za shranjevanje tega para. Zato samostojna nastavitev UseObjectStreams ne prinaša vidnih sprememb; delujoča konfiguracija zahteva nastavitev obeh zastavic pred klicem BeginDoc. Če ju nastavite po BeginDoc, je HotPDF že izbral starejšo postavitev.
Zakaj sta obe privzeto izklopljeni
HotPDF pušča obe lastnosti privzeto nastavljeni na False, razlog za to pa se pokaže pri integracijah s starejšo programsko oprema. Pregledovalnik, ki razume le PDF 1.4, ne bo sporočil, da ne zna delati s stisnjenimi objekti. Ko naleti na tok xref, ne najde pričakovanih ključnih besed za zaključek datoteke (trailer) in sporoči, da je tabela navzkrižnih sklicev poškodovana, ali pa datoteke preprosto ne odpre. Če vaši izhodi potujejo v zastarel faks prehod, strojni tiskalnik z vgrajenim interpreterjem ali razčlenjevalnik, ki ga je nekdo napisal po specifikaciji 1.4 pred desetletjem, pustite obe zastavici izklopljeni in se sprijaznite z večjo datoteko. Za arhivsko shranjevanje in spletno dostavo, kjer vsi uveljavljeni pregledovalniki že dvajset let podpirajo PDF 1.5, pa je njun vklop praktično brezplačna kompresija.
Obstaja tudi sekundarni učinek, o katerem je vredno obvestiti vašo ekipo za podporo. Ko so slovarji zapakirani v tokovih objektov, primerjava dveh ustvarjenih datotek bajt za bajtom izgubi smisel. Sprememba enega samega polja namreč lahko ponovno stisne (re-Flate) celoten vsebnik in prerazdeli vse dokumente za njim. Takšne datoteke primerjajte (diff) po vsebini objektov in ne z binarno primerjavo.
Inkrementalne posodobitve in bajtni odmiki, ki jih varujejo
Digitalni podpis pokriva ekspliciten /ByteRange: dva razpona fizične datoteke, podana kot absolutni bajtni odmiki, na podlagi katerih je bil izračunan povzetek CMS. Če datoteko prepišete, čeprav je na zaslonu videti povsem enaka, se vsi ti odmiki premaknejo. Povzetek se ne ujema več in podpis se prikaže kot poškodovan. To je natanko tista težava, ki jo standard ISO 32000-1 §7.5.6 rešuje z inkrementalnimi posodobitvami. Novi in spremenjeni objekti se dodajo za obstoječim %%EOF, nato pa se zapiše nov del navzkrižnih sklicev, katerega vnos /Prev kaže nazaj na prejšnjega. Prvotni bajti se nikoli ne spremenijo, zato podpisana revizija ostane preverljiva, Acrobat pa lahko vsako podpisano različico prikaže samostojno v plošči za podpise.
HotPDF to omogoča prek lastne vstopne točke:
Pdf.BeginIncrementalUpdate('contract-signed.pdf');
Pdf.AddPage;
Pdf.CurrentPage.SetFont('Arial', [], 10);
Pdf.CurrentPage.TextOut(50, 760, 0, 'Addendum recorded 2026-06-11');
Pdf.SaveIncrementalUpdate('contract-updated.pdf'); // appends the delta only
```
Dve stvari lahko povzročita težave. Metoda BeginIncrementalUpdate mora prejeti ime izvirne datoteke, saj dodani del xref beleži odmike, ki so smiselni le glede na te natančne izvirne bajte. Če jo usmerite na preimenovano ali ponovno shranjeno kopijo, bodo odmiki opisovali datoteko, ki ne obstaja več. Druga stvar pa je, da je shranjevanje že po zasnovi namenjeno le dodajanju na konec, zato je izhod vedno večji od vhoda. Ta rast ni potrata, ki bi jo morali odpraviti, ampak lastnost, ki ohranja prejšnje podpisane različice nedotaknjene.
Spreminjanje naložene datoteke poteka prek LoadFromFile
Razvijalci, ki so HotPDF spoznali prek njegovega API-ja za ustvarjanje dokumentov, pogosto naletijo na določeno oviro. Metoda BeginDoc odpre popolnoma nov dokument, kar je napačno orodje, kadar želite spremeniti že obstoječega. Urejanje obstoječe datoteke namesto tega poteka prek klicov za naložene dokumente:
PageCount := Pdf.LoadFromFile('base.pdf');
Pdf.InsertPagesFromDocument(OtherDoc, '1-3', 5); // pages 1-3 after page 5
Pdf.MovePage(2, 5);
Pdf.SaveLoadedDocument('modified.pdf');
Če to dvoje zmešate, bo rezultat izhodna datoteka, ki vsebuje vašo novo vsebino in ničesar izvirnega, saj je klic BeginDoc ustvaril nov dokument poleg tistega, za katerega ste verjeli, da ga urejate. Razumejte LoadFromFile s SaveLoadedDocument kot en sklop navodil ter BeginDoc z EndDoc kot drugega. Klic obeh sklopov nad isto datoteko je skoraj vedno napačen.
Kdaj stisniti dodano datoteko
Shranjevanje z dodajanjem na konec prinaša dolgoročne stroške. Nočno opravilo, ki v isti PDF odtisne eno vrstico stanja, v enem letu ustvari 365 revizij, vsaka revizija pa s seboj vleče nov del xref. Ko ta zgodovina izgubi svojo uporabnost in v datoteki ni več podpisov, ki bi morali preživeti, lahko celotno zadevo sploščite s ponovno serializacijo prek poti za naložene dokumente:
Pdf.LoadFromFile('stamped.pdf');
Pdf.SaveLoadedDocument('compacted.pdf');
To ponovno shranjevanje predstavlja popolno prepisovanje. Namerno zavrže prejšnje revizije in uniči vse podpise v datoteki, zato to dejanje postavite za enak varnostni pravilnik kot kateri koli drug destruktiven korak. Eno od produkcijskih pravil, ki drži: datoteko stisnite, ko število revizij preseže določen prag ali ko dodani režijski stroški presežejo določen delež osnovne datoteke, in nikoli ne stiskajte dokumenta, ki vsebuje aktivne podpise.
Preverjanje izhoda pred pošiljanjem
Preverjanje teh dveh funkcij je zelo konkretno. Odprite rezultat v programu Adobe Acrobat in potrdite tri točke: lastnosti dokumenta javljajo različico PDF 1.5 ali novejšo, ko so tokovi objektov vklopljeni; plošča za podpise še vedno potrjuje vsako predhodno podpisano revizijo po inkrementalni posodobitvi; število strani in zaznamki pa so preživeli cikel nalaganja, spreminjanja in shranjevanja brez poškodb. Za arhivski izhod datoteko pošljite tudi skozi veraPDF, saj je stisnjen xref natanko tista struktura, ki jo strogi preverjevalnik pregleda podrobneje kot kateri koli popustljiv pregledovalnik. Če vaše delo vključuje zelo velike vnose, se metode preverjanja v našem vodniku po Direct File API za velike PDF delovne tokove naravno povezujejo z inkrementalnim shranjevanjem, mehanika podpisovanja za zgoraj opisanimi bajtnimi razponi pa je podrobno obravnavana v članku o digitalnih podpisih HotPDF in PAdES.
Obe funkciji sta na voljo kot del komponente HotPDF za Delphi in C++Builder, poleg drugih API-jev za ustvarjanje, obrazce, šifriranje in podpisovanje, ki so opisani na tem blogu. Na strani izdelka je na voljo povezava do celotne reference API, če želite zgoraj prikazane klice povezati z lastnim postopkom obdelave dokumentov.