Technical Article

Prúdy objektov a prírastkové aktualizácie v Delphi s HotPDF

Verzia PDF 1.5 priniesla dve úložné štruktúry, ktoré staršie formáty súborov nedokázali vyjadriť: prúd objektov (object stream) a prúd krížových odkazov (cross-reference stream). Prúd objektov je jeden Flate-komprimovaný kontajner s označením /Type /ObjStm, ktorý obsahuje množstvo malých nepriamych objektov uložených za sebou, namiesto toho, aby boli rozptýlené v tele súboru. Prúd krížových odkazov je vyhľadávacia tabuľka súboru prepísaná do komprimovanej binárnej podoby s premenlivou šírkou polí, ktorá nahrádza tabuľku ASCII s pevnou šírkou, ktorá uzatvárala každé PDF do verzie 1.4. Tieto dve štruktúry fungujú spoločne. Keď sa objekty zabalia do prúdu, stará textová tabuľka ich už nedokáže adresovať, takže binárny xref je nevyhnutnosťou.

Ak to porovnáte s klasickým usporiadaním, výhody sú zrejmé. V súbore PDF 1.4 každý nepriamy objekt leží nekomprimovaný za svojou vlastnou hlavičkou obj a tabuľka na konci spotrebúva presne 20 bajtov ASCII na každú položku, pričom kompresia je zakázaná. Dokument s 200 000 objektmi tak nesie približne 4 MB krížových odkazov ešte predtým, než sa vykreslí čo i len jeden znak, a navyše sú nad tým navrstvené nekomprimované telá slovníkov. PDF 1.5 rieši oba tieto problémy naraz: slovníky sa zabalia do Flate kontajnerov a 4 MB tabuľka sa zmenší na niekoľko stoviek kilobajtov binárnych dát. Norma ISO 32000-1 definuje tieto dve štruktúry v odsekoch §7.5.7 a §7.5.8.

Kde sa úspora reálne prejaví

Prúdy objektov ovplyvňujú iba ne-prúdové objekty (non-stream objects), takže komprimujú štruktúru, nie obrazové body. Obsah stránky bol komprimovaný pomocou Flate už pred verziou 1.5 a obrázky používajú vlastné kodeky, preto sa veľkosť brožúr s množstvom obrázkov takmer nezmení. Súbory, ktorých veľkosť výrazne klesne, sú tie s bohatou štruktúrou: AcroFormy s tisíckami slovníkov polí, hlboké stromy záložiek či štrukturálne prvky tagged-PDF. Tieto objekty sú malé, početné a takmer identické, a práve túto duplicitu dokáže Flate využiť, keď sú umiestnené v jedinom vyrovnávacom pamäťovom priestore, namiesto toho, aby boli rozptýlené po tele dokumentu s hlavičkami medzi nimi.

Je ľahké podceniť, akú veľkú časť starého súboru tvorí réžia. Archív formulárov, do ktorého sa roky zapisovali zmeny, môže mať viac ako polovicu bajtov obsadenú hlavičkami slovníkov, výplňou krížových odkazov a revíziami, ktoré už nikto nikdy neuvidí. Dve spomínané funkcie pomáhajú vyriešiť prvé dva problémy. Tretí – nahromadené revízie – sa dá vyriešiť iba komprimáciou (compaction) súboru, kedy sa odstráni jeho stará história.

V HotPDF aktivujete obe funkcie prostredníctvom dvojice vlastností, pričom ich vzájomná závislosť je dôležitejšia ako poradie, v akom ich zapíš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;

Vlastnosť UseObjectStreams vyžaduje nastavenie UseXRefStream na hodnotu True. Ku komprimovanému objektu sa pristupuje cez položku xref typu 2, ktorá zaznamenáva číslo prúdu objektov a index, a klasický 20-bajtový textový riadok nemá miesto na uloženie tejto dvojice. Samotné UseObjectStreams preto nevykoná žiadnu viditeľnú zmenu. Konfigurácia funguje iba vtedy, ak nastavíte oba príznaky pred volaním BeginDoc. Ak ich nastavíte až po BeginDoc, HotPDF už použije staršie usporiadanie.

Prečo sú obe funkcie predvolene vypnuté

HotPDF ponecháva obe vlastnosti predvolene nastavené na False. Dôvodom je integrácia so staršími systémami. Prehliadač, ktorý rozumie iba PDF 1.4, neoznámi, že nevie spracovať komprimované objekty. Keď narazí na prúd krížových odkazov, nenájde očakávané kľúčové slová päty (trailer) a nahlási poškodenú tabuku krížových odkazov alebo súbor odmietne otvoriť. Ak vaše výstupy smerujú do starej faxovej brány, hardvérovej tlačiarne s integrovaným interpretom alebo parsera napísaného pre špecifikáciu 1.4 pred desiatimi rokmi, nechajte oba príznaky vypnuté a akceptujte väčšiu veľkosť súboru. Pre archívne účely a webovú distribúciu, kde každý bežný prehliadač podporuje PDF 1.5 už dvadsať rokov, prináša ich zapnutie kompresiu takmer zadarmo.

Existuje aj sekundárny efekt, o ktorom by mal vedieť váš podporný tím. Keď sú slovníky zabalené v prúdoch objektov, porovnávanie dvoch vygenerovaných súborov bajt po bajte stráca zmysel. Zmena jedného poľa totiž môže prekomprimovať celý kontajner a presunúť všetky objekty za ním. Porovnávajte takéto súbory podľa obsahu objektov, nie pomocou binárneho porovnania.

Prírastkové aktualizácie a bajtové posuny, ktoré chránia

Digitálny podpis pokrýva konkrétny rozsah /ByteRange – dva úseky fyzického súboru vyjadrené ako absolútne bajtové posuny, nad ktorými sa vypočítal odtlačok (digest) CMS. Ak súbor prepíšete (aj keď vyzerá na obrazovke rovnako), všetky tieto posuny sa zmenia. Odtlačok prestane súhlasiť a podpis bude vyhodnotený ako neplatný. Presne tento problém rieši norma ISO 32000-1 §7.5.6 pomocou prírastkových aktualizácií (incremental updates). Nové a zmenené objekty sa zapíšu za existujúcu značku %%EOF a následne sa zapíše nová sekcia krížových odkazov, ktorej položka /Prev odkazuje na tú predchádzajúcu. Pôvodné bajty zostávajú nedotknuté, takže podpísaná revízia je overiteľná a Acrobat ju môže zobraziť samostatne v paneli podpisov.

HotPDF sprístupňuje túto funkciu prostredníctvom vlastnej metódy:

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

Používateľov zvyčajne potrápia dve veci. Metóda BeginIncrementalUpdate musí dostať názov pôvodného súboru, pretože pripojená sekcia xref zaznamenáva posuny platné výhradne pre pôvodné bajty. Ak odkážete na premenovanú alebo znova uloženú kópiu, posuny budú popisovať neexistujúci súbor. Ukladanie je navyše navrhnuté ako append-only (iba pridávanie), takže výstup je vždy väčší ako vstup. Tento nárast veľkosti nie je zbytočný odpad – je to vlastnosť, ktorá zabezpečuje uchovanie predchádzajúcich podpísaných revízií.

Úprava načítaného súboru prebieha cez LoadFromFile

Vývojári, ktorí začínali s HotPDF cez jeho generačné rozhranie API, často narážajú na rovnaký problém. Volanie BeginDoc vytvorí úplne nový dokument, čo nie je vhodné, ak chcete zmeniť existujúci súbor. Úprava existujúceho súboru musí prebiehať cez volania pre načítaný 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');

Ak tieto dva prístupy skombinujete, výsledkom bude súbor obsahujúci iba nový obsah a nič z pôvodného dokumentu. Volanie BeginDoc totiž vytvorí nový dokument vedľa toho, ktorý ste chceli upravovať. Považujte dvojicu LoadFromFile s SaveLoadedDocument za jeden svet a BeginDoc s EndDoc za druhý. Kombinácia oboch prístupov pri práci s rovnakým súborom je takmer vždy chybou.

Kedy komprimovať prírastkový súbor

Ukladanie typu append-only prináša postupný nárast réžie. Nočná úloha, ktorá pridáva jeden stavový riadok do rovnakého PDF, vygeneruje za rok 365 revízií a každá z nich si so sebou nesie novú sekciu xref. Keď táto história prestane byť potrebná a nie je nutné zachovať podpisy, môžete dokument zlúčiť (flatten) opätovnou serializáciou cez cestu pre načítaný dokument:

Pdf.LoadFromFile('stamped.pdf');
Pdf.SaveLoadedDocument('compacted.pdf');

Toto opätovné uloženie predstavuje úplný prepis dokumentu. Zámerne zahodí predchádzajúce revízie a zneplatní všetky podpisy v súbore. Preto by malo byť chránené rovnakým schválením ako iné deštruktívne kroky. Osvedčeným pravidlom pre produkciu je: komprimovať iba vtedy, keď počet revízií prekročí stanovený limit alebo keď veľkosť prírastkov presiahne určitý pomer voči základnému súboru, a nikdy nekomprimovať dokument, ktorý obsahuje digitálne podpisy.

Overenie výstupu pred odoslaním

Overenie oboch týchto funkcií je veľmi jednoznačné. Otvorte výsledok v programe Adobe Acrobat a skontrolujte tri veci: vlastnosti dokumentu musia po zapnutí prúdov objektov uvádzať verziu PDF 1.5 alebo novšiu; panel podpisov musí aj po prírastkovej aktualizácii overiť každú predtým podpísanú revíziu; a počet stránok a záložky prešli procesom načítania, úpravy a uloženia bez poškodenia. Pri archívnych výstupoch otestujte súbor aj nástrojom veraPDF, pretože komprimovaný xref je štruktúra, ktorú prísny validátor skúma oveľa dôkladnejšie než bežný prehliadač. Ak pracujete s veľmi veľkými súbormi, odporúčame skombinovať tieto metódy s postupmi popísanými v našom sprievodcovi rozhraním Direct File API pre veľké PDF, pričom princípy fungovania digitálnych podpisov a rozsahov bajtov nájdete v článku o digitálnych podpisoch a PAdES.

Obe funkcie sú súčasťou komponentu HotPDF Component pre Delphi a C++Builder, spoločne s API pre generovanie, formuláre, šifrovanie a podpisovanie. Stránka produktu obsahuje odkaz na kompletnú referenčnú príručku API, ak chcete integrovať vyššie uvedené volania do vlastných projektov.