PDF 1.5 uveo je dvije strukture za pohranu koje raniji format datoteke nije mogao izraziti: tok objekata (object stream) i tok unakrsnih referenci (cross-reference stream). Tok objekata je jedan Flate-komprimirani spremnik s oznakom /Type /ObjStm koji sadrži mnogo malih neizravnih objekata pakiranih jedan iza drugoga, umjesto da budu razbacani kroz tijelo datoteke. Tok unakrsnih referenci je tablica pretraživanja datoteke prepisana kao komprimirani binarni zapis s poljima varijabilne širine, umjesto ASCII tablice fiksne širine koja je zatvarala svaki PDF do verzije 1.4. Oni dolaze zajedno. Jednom kada se objekti preklope u tok, stara tekstualna tablica više im ne može pristupiti, pa binarni xref mora doći s njim.
Usporedite to s klasičnim rasporedom i lako je uočiti trošak koji se time uklanja. U PDF 1.4 datoteci svaki neizravni objekt nalazi se nekomprimiran iza vlastitog zaglavlja obj, a tablica na kraju troši točno 20 bajtova ASCII znakova po unosu, pri čemu je kompresija zabranjena. Dokument s 200.000 objekata nosi otprilike 4 MB podataka unakrsnih referenci prije nego što se iscrta ijedan znak, sa svim nekomprimiranim tijelima rječnika naslaganim na vrhu. PDF 1.5 napada oba broja odjednom: rječnici se preklapaju u Flate spremnike, a tablica od 4 MB smanjuje se na nekoliko stotina kilobajta binarnog zapisa. ISO 32000-1 definira ove dvije strukture u §7.5.7 i §7.5.8.
Gdje se ušteda zapravo ostvaruje
Tokovi objekata utječu samo na objekte koji nisu tokovi, pa komprimiraju strukturu, a ne piksele. Sadržaj stranice bio je Flate-komprimiran i prije verzije 1.5, a slikovni podaci nose vlastite kodeke, zbog čega se veličina brošure s mnogo slika jedva mijenja. Datoteke koje se drastično smanjuju su one s teškom strukturom: AcroForms s tisućama rječnika polja, duboka stabla obrisa (bookmarks), elementi strukture označenog PDF-a. Ti su objekti maleni, brojni i gotovo identični jedni drugima, a to ponavljanje je upravo ono što Flate koristi kada se nalaze u jednom međuspremniku (bufferu) umjesto da su raspoređeni po tijelu datoteke sa zaglavljima između njih.
Lako je podcijeniti koliki dio stare datoteke otpada na dodatne podatke (overhead). Arhiva obrazaca koja je pretrpjela godine uređivanja može potrošiti znatno više od polovice svojih bajtova na zaglavlja rječnika, popunjavanje xref-a i revizije koje nijedan čitatelj nikada neće pogledati. Dvije značajke o kojima je ovdje riječ rješavaju prve dvije stavke. Treća, nakupljene revizije, rješava se samo sažimanjem, kada datoteka više ne mora pamtiti vlastitu povijest.
U HotPDF-u obje značajke uključujete putem para svojstava, a način na koji ovise jedno o drugome važniji je od redoslijeda kojim ih postavljate:
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;
UseObjectStreams zahtijeva da UseXRefStream bude postavljen na True. Komprimiranom objektu pristupa se putem xref unosa tipa 2, koji bilježi broj toka objekta i indeks, a klasični tekstualni redak od 20 bajtova nema mjesto za pohranu tog para. Stoga sam UseObjectStreams ne čini ništa vidljivo; oba flag-a postavljena prije BeginDoc čine konfiguraciju koja radi. Ako ih postavite nakon BeginDoc, HotPDF se već obvezao na stariji raspored.
Zašto su oba prema zadanim postavkama isključena
HotPDF ostavlja oba svojstva postavljena na False, a razlog se očituje u integracijama sa starim kodom. Preglednik koji razumije samo PDF 1.4 ne javlja eksplicitno da ne može rukovati komprimiranim objektima. Kad naiđe na xref tok, ne pronalazi ključne riječi trailer koje očekuje i prijavljuje oštećenu tablicu unakrsnih referenci ili jednostavno odbija otvoriti datoteku. Ako vaš izlaz ide u zastarjeli faks prolaz (gateway), hardverski pisač s ugrađenim interpreterom ili parser koji je netko napisao prema specifikaciji 1.4 prije desetak godina, držite oba flaga isključena za taj kanal i pomirite se s većom datotekom. Za arhiviranje i web isporuku, gdje svaki uobičajeni preglednik već dvadeset godina čita PDF 1.5, njihovo uključivanje je kompresija koju dobivate gotovo besplatno.
Postoji i sekundarni efekt o kojem vrijedi obavijestiti vaš tim za podršku. Nakon što se rječnici spakiraju u tokove objekata, uspoređivanje dviju generiranih datoteka bajt po bajt gubi smisao jer promjena jednog polja može ponovno komprimirati (re-Flate) cijeli spremnik i pomiješati sve nakon njega. Uspoređujte takve datoteke prema sadržaju objekata, a ne binarnom usporedbom.
Inkrementalna ažuriranja i bajtovni pomaci koje štite
Digitalni potpis pokriva eksplicitan /ByteRange: dva raspona fizičke datoteke, zadana kao apsolutni pomaci bajtova (byte offsets), nad kojima je izračunat CMS sažetak (digest). Prepišite datoteku, čak i u nešto što izgleda identično na zaslonu, i svi ti pomaci će se pomaknuti. Sažetak se više ne podudara i potpis se prijavljuje kao neispravan. To je točan problem koji ISO 32000-1 §7.5.6 rješava inkrementalnim ažuriranjima. Novi i izmijenjeni objekti dodaju se nakon postojećeg %%EOF, a zatim se zapisuje novi odjeljak unakrsnih referenci čiji unos /Prev pokazuje na prethodni. Izvorni bajtovi se nikada ne mijenjaju, pa potpisana revizija ostaje provjerljiva, a Acrobat može prikazati svaku potpisanu reviziju zasebno na ploči s potpisima.
HotPDF to izlaže kroz vlastitu ulaznu točku:
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
Dvije stvari stvaraju probleme. BeginIncrementalUpdate mora primiti izvorni naziv datoteke jer dodani xref odjeljak bilježi pomake koji imaju smisla samo u odnosu na te točne izvorne bajtove; ako ga usmjerite na preimenovanu ili ponovno spremljenu kopiju, pomaci će opisivati datoteku koja više ne postoji. Također, spremanje je po strukturi predviđeno samo za dodavanje (append-only), pa je izlaz uvijek veći od ulaza. Taj rast nije otpad koji treba optimizirati. To je upravo ono svojstvo koje ostavlja ranije potpisane revizije netaknutima.
Uređivanje učitane datoteke ide preko LoadFromFile
Programeri koji su se prvi put susreli s HotPDF-om putem njegovog API-ja za generiranje obično nailaze na određenu prepreku. BeginDoc otvara potpuno novi dokument, što je pogrešan alat kada želite promijeniti onaj koji već postoji. Uređivanje postojećeg dokumenta umjesto toga koristi pozive za učitani dokument:
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');
Pomiješajte to dvoje i simptom će biti izlazna datoteka koja sadrži vaš novi sadržaj, ali ništa od izvornog jer je BeginDoc veselo izgradio novi dokument pokraj onoga za koji ste vjerovali da ga uređujete. Doživljavajte LoadFromFile sa SaveLoadedDocument kao jedan vokabular, a BeginDoc s EndDoc kao drugi. Rutina koja koristi oba pristupa za istu datoteku gotovo je uvijek pogrešna.
Kada sažeti dodanu datoteku
Inkrementalno spremanje nosi dugoročni trošak. Noćni zadatak koji utiskuje jedan redak statusa na isti PDF stvara 365 revizija tijekom godine, a svaka revizija vuče novi xref odjeljak za sobom. Kada ta povijest nadživi svoju korisnost i nijedan potpis u datoteci ne mora preživjeti, možete poravnati cijelu stvar ponovnim serijaliziranjem kroz putanju učitanog dokumenta:
Pdf.LoadFromFile('stamped.pdf');
Pdf.SaveLoadedDocument('compacted.pdf');
Ovo ponovno spremanje je potpuno prepisivanje. Ono namjerno odbacuje prethodne revizije i uništava svaki potpis koji se još nalazi u datoteci, pa ga stavite iza istih sigurnosnih pravila koja primjenjujete na bilo koji drugi destruktivan korak. Jedno produkcijsko pravilo koje vrijedi: sažmite kada broj revizija prijeđe određeni prag ili kada nakupljeni overhead naraste iznad određenog udjela u osnovnoj datoteci, i nikada nemojte sažimati dokument u čijoj se ploči s potpisima nalazi bilo što.
Provjera izlaza prije slanja
Provjera ovog para značajki osvježavajuće je konkretna. Otvorite rezultat u Adobe Acrobatu i potvrdite tri stvari: svojstva dokumenta prijavljuju PDF 1.5 ili noviji nakon što se uključe tokovi objekata; ploča s potpisima i dalje potvrđuje svaku prethodno potpisanu reviziju nakon inkrementalnog ažuriranja; te su broj stranica i oznake (bookmarks) prošli neoštećeni kroz ciklus učitavanja, izmjene i spremanja. Za arhivski izlaz pošaljite datoteku i kroz veraPDF jer je komprimirani xref upravo ona vrsta strukture koju strogi validator ispituje mnogo detaljnije nego što će to učiniti popustljivi preglednik. Ako vaš rad uključuje i vrlo velike unose, metode inspekcije u našem pregledu Direct File API-ja za rad s velikim PDF-ovima prirodno se nadopunjuju s inkrementalnim spremanjem, a mehanika potpisivanja iza gore opisanih raspona bajtova detaljno je obrađena u članku o HotPDF digitalnim potpisima i PAdES-u.
Obje značajke isporučuju se kao dio komponente HotPDF Component za Delphi i C++Builder, uz API-je za generiranje, obrasce, šifriranje i potpisivanje o kojima se govori na drugim mjestima na ovom blogu. Stranica proizvoda sadrži poveznicu na cjelovitu referencu API-ja ako želite uskladiti gore navedene pozive s vlastitim cjevovodom dokumenata.