PDF 1.5 je uveo dve strukture za skladištenje koje prethodni format fajla nije mogao da izrazi: tok objekata (object stream) i tok unakrsnih referenci (cross-reference stream). Tok objekata je kontejner komprimovan Flate metodom, označen sa /Type /ObjStm, koji drži mnogo malih indirektnih objekata spakovanih jedan za drugim, umesto da budu razbacani kroz telo fajla. Tok unakrsnih referenci je tabela za pretraživanje fajla prepisana kao komprimovani binarni kod sa poljima promenljive širine, umesto ASCII tabele fiksne širine koja je zatvarala svaki PDF do verzije 1.4. Oni idu zajedno. Kada se objekti spakuju u tok, stara tekstualna tabela više ne može da im pristupi, tako da binarni xref mora doći sa njim.
Uporedite to sa klasičnim rasporedom i lako je uočiti trošak koji se time eliminiše. U PDF 1.4 fajlu svaki indirektni objekat stoji nekomprimovan iza sopstvenog obj zaglavlja, a tabela na kraju troši tačno 20 ASCII bajtova po unosu, pri čemu je kompresija zabranjena. Dokument sa 200.000 objekata nosi oko 4 MB podataka o unakrsnim referencama pre nego što se iscrta ijedan karakter, sa svim nekomprimovanim telima rečnika postavljenim na to. PDF 1.5 napada oba broja odjednom: rečnici se pakuju u Flate kontejnere, a tabela od 4 MB se smanjuje na nekoliko stotina kilobajta binarnih podataka. Standard ISO 32000-1 definiše ove dve strukture u §7.5.7 i §7.5.8.
Gde se ušteda zapravo ostvaruje
Tokovi objekata utiču samo na objekte koji sami po sebi nisu tokovi (non-stream objects), tako da komprimuju strukturu, a ne piksele. Sadržaj stranice je već bio komprimovan Flate metodom pre verzije 1.5, a slikovni podaci koriste sopstvene kodeke, zbog čega se veličina brošura sa mnogo slika jedva menja. Fajlovi čija se veličina drastično smanjuje su oni sa bogatom strukturom: AcroForms sa hiljadama rečnika polja, duboka stabla obeleživača (outlines), strukturni elementi tagovanog PDF-a. Ti objekti su mali, brojni i skoro identični jedni drugima, a to ponavljanje je upravo ono što Flate koristi kada se nađu u jednom baferu, umesto da budu raspoređeni po telu dokumenta sa umetnutim zaglavljima između njih.
Lako je potceniti koliki deo starog fajla predstavlja suvišne podatke (overhead). Arhiva obrazaca koja je godinama trpela izmene može potrošiti više od polovine svojih bajtova na zaglavlja rečnika, xref popunjavanje i revizije koje nijedan čitalac nikada neće pogledati. Ove dve funkcije rešavaju prva dva problema. Treći – nagomilane revizije – rešava se isključivo kompaktovanjem, kada fajl više ne mora da pamti sopstvenu istoriju.
U HotPDF-u obe funkcije aktivirate preko para svojstava (properties), a njihova međusobna zavisnost je važnija od redosleda kojim ih pišete u kodu:
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;
Svojstvo UseObjectStreams zahteva da UseXRefStream bude postavljeno na True. Komprimovanom objektu se pristupa preko unosa tipa 2 u xref tabeli, koji beleži broj toka objekta i indeks, a klasični tekstualni red od 20 bajtova nema mesta za čuvanje tog para podataka. Zato samo UseObjectStreams samo po sebi ne donosi vidljive rezultate; ispravna konfiguracija zahteva da oba flega budu postavljena pre poziva BeginDoc. Ako ih postavite nakon BeginDoc, HotPDF se već opredelio za stariji raspored.
Zašto su oba po defaultu isključena
HotPDF ostavlja oba svojstva podešena na False iz praktičnih razloga, što se najbolje vidi u integraciji sa starijim spoljnim kodom. Program za pregled koji razume samo PDF 1.4 ne prijavljuje eksplicitno da ne može da obradi komprimovane objekte. On nailazi na xref tok, ne pronalazi ključne reči u trejleru (trailer keywords) koje očekuje i prijavljuje oštećenu tabelu unakrsnih referenci ili jednostavno odbija da otvori fajl. Ako vaš izlaz ide u zastareli faks server, hardverski štampač koji pokreće ugrađeni interpreter ili parser koji je neko napisao pre deset godina za standard 1.4, držite oba flega isključena za taj kanal i prihvatite veći fajl. Za arhiviranje i distribuciju putem veba, gde svaki moderni čitač već dvadeset godina podržava PDF 1.5, njihovo uključivanje pruža kompresiju koju dobijate praktično besplatno.
Postoji i prateći efekat o kome bi trebalo da obavestite svoj tim za podršku. Kada se rečnici spakuju u tokovi objekata, poređenje dva generisana fajla bajt po bajt gubi smisao, jer izmena samo jednog polja može ponovo komprimovati ceo kontejner i pomeriti sve nakon njega. Poredite takve fajlove prema sadržaju objekata, a ne binarnim poređenjem.
Inkrementalna ažuriranja i ofseti bajtova koje ona štite
Digitalni potpis pokriva eksplicitan /ByteRange: dva opsega fizičkog fajla, zadata kao apsolutni ofseti bajtova, preko kojih je izračunat CMS sažetak (digest). Prepišite fajl, čak i u verziju koja na ekranu izgleda identično, i svi ti ofseti će se pomeriti. Sažetak prestaje da se podudara i potpis se prikazuje kao nevažeći. To je problem koji standard ISO 32000-1 §7.5.6 rešava inkrementalnim ažuriranjima. Novi i izmenjeni objekti se dodaju nakon postojećeg %%EOF markera, a zatim se piše novi odeljak unakrsnih referenci čiji unos /Prev ukazuje na prethodnu tabelu. Originalni bajtovi se nikada ne menjaju, pa potpisana revizija ostaje proverljiva, a Acrobat može prikazati svaku potpisanu reviziju zasebno u panelu za potpise.
HotPDF ovo omogućava kroz sopstvenu pristupnu tač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
Dve stvari mogu zbuniti programere. Funkcija BeginIncrementalUpdate mora primiti originalno ime fajla, jer dodati xref odeljak beleži ofsete koji imaju smisla samo u odnosu na te tačne originalne bajtove; ako je usmerite na preimenovanu ili ponovo sačuvanu kopiju, ofseti će opisivati fajl koji više ne postoji. Takođe, čuvanje je po dizajnu predviđen isključivo za dodavanje na kraj, pa je izlazni fajl uvek veći od ulaznog. Taj rast nije višak podataka koji treba eliminisati – to je upravo ono što omogućava da prethodne potpisane revizije ostanu netaknute.
Izmena učitanog fajla ide preko LoadFromFile
Programeri koji su se prvi put upoznali sa HotPDF-om preko njegovog API-ja za generisanje često naiđu na problem. Poziv BeginDoc otvara potpuno novi dokument, što je pogrešan alat kada želite da promenite već postojeći. Izmena postojećeg fajla se umesto toga vrši preko poziva za učitane 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');
Pomšajte ova dva pristupa i rezultat će biti izlazni fajl koji sadrži samo vaš novi sadržaj, bez ičega iz originala, jer je BeginDoc kreirao potpuno novi dokument pored onog za koji ste verovali da ga menjate. Posmatrajte LoadFromFile i SaveLoadedDocument kao jedan skup alata, a BeginDoc i EndDoc kao drugi. Rutina koja koristi oba pristupa na istom fajlu je skoro uvek pogrešna.
Kada treba kompaktovati dodati fajl
Čuvanje dodavanjem na kraj donosi dugoročne troškove. Posao koji svake noći dodaje jednu liniju statusa u isti PDF generisaće 365 revizija tokom godine, a svaka revizija povlači novu xref sekciju za sobom. Kada ta istorija više nije potrebna, a nijedan potpis u fajlu ne mora da se sačuva, možete sve to spljoštiti (flatten) ponovnom serijalizacijom kroz putanju učitanog dokumenta:
Pdf.LoadFromFile('stamped.pdf');
Pdf.SaveLoadedDocument('compacted.pdf');
Ovo ponovno čuvanje je kompletno prepisivanje. Ono namerno odbacuje prethodne revizije i kvari svaki potpis koji se nalazi u fajlu, pa ga postavite iza istih bezbednosnih kontrola koje primenjujete na bilo koji drugi destruktivan korak. Jedno produkcijsko pravilo koje se pokazalo dobrim: kompaktujte kada broj revizija pređe određeni prag ili kada dodati višak podataka premaši određeni udeo u osnovnom fajlu, i nikada nemojte kompaktovati dokument čiji panel za potpise sadrži bilo šta.
Provera izlaza pre isporuke
Provera ovog para funkcija je prilično jednostavna. Otvorite rezultat u Adobe Acrobat-u i potvrdite tri stvari: svojstva dokumenta prijavljuju PDF 1.5 ili noviji kada su tokovi objekata uključeni; panel za potpise i dalje validira svaku prethodno potpisanu reviziju nakon inkrementalnog ažuriranja; broj stranica i obeleživači su prošli kroz ciklus učitavanja, izmene i čuvanja bez oštećenja. Za arhivski izlaz, provucite fajl i kroz veraPDF, jer je komprimovani xref upravo ona vrsta strukture koju strogi validator proverava mnogo detaljnije nego bilo koji popustljivi program za pregled. Ako vaš rad uključuje veoma velike ulaze, metode inspekcije opisane u našem vodiču kroz Direct File API za tokove obrade velikih PDF-ova prirodno se kombinuju sa inkrementalnim čuvanjem, dok su mehanizmi potpisivanja iza gore pomenutih opsega bajtova detaljno obrađeni u članku o HotPDF digitalnim potpisima i PAdES-u.
Obe funkcije se isporučuju kao deo HotPDF komponente za Delphi i C++Builder, pored API-ja za generisanje, obrasce, enkripciju i potpisivanje koji su pokriveni na drugim mestima na ovom blogu. Stranica proizvoda sadrži link do kompletne API reference ako želite da uskladite gore navedene pozive sa sopstvenim tokom obrade dokumenata.