A PDF 1.5 két olyan tárolási szerkezetet vezetett be, amelyet a korábbi fájlformátum sehogy nem tudott kifejezni: az objektumfolyamot és a kereszthivatkozási folyamot. Az objektumfolyam egyetlen Flate-tömörítésű, /Type /ObjStm jelölésű tároló, amely sok apró indirekt objektumot tart egymáshoz préselve, ahelyett hogy szétszórná őket a fájltörzsben. A kereszthivatkozási folyam a fájl keresőtáblája tömörített, változó szélességű mezőkből álló bináris alakban, annak a rögzített szélességű ASCII-táblának a helyén, amely az 1.4-es verzióig minden PDF-et lezárt. A kettő együtt utazik. Amint az objektumok folyamba kerülnek, a régi szöveges tábla már nem tudja megcímezni őket, tehát a bináris xref-nek jönnie kell vele
Állítsa ezt szembe a klasszikus elrendezéssel, és rögtön látszik, mekkora költséget szüntet meg. Egy PDF 1.4-es fájlban minden indirekt objektum tömörítetlenül ül a saját obj fejléce mögött, a végén lévő tábla pedig pontosan 20 bájt ASCII-t költ bejegyzésenként, tömörítés nélkül. Egy 200 000 objektumos dokumentum nagyjából 4 MB kereszthivatkozási adatot cipel, mielőtt egyetlen betűjel kirajzolódna, és erre jönnek rá a tömörítetlen szótártörzsek. A PDF 1.5 mindkét számot egyszerre támadja meg: a szótárak Flate-tárolókba hajtódnak össze, a 4 MB-os tábla pedig néhány száz kilobájtnyi binárisra zsugorodik. Az ISO 32000-1 a két szerkezetet a §7.5.7 és a §7.5.8 szakaszban definiálja
Hol jelentkezik valójában a megtakarítás
Az objektumfolyamok csak a nem folyam típusú objektumokat érintik, tehát szerkezetet tömörítenek, nem képpontokat. Az oldaltartalom már 1.5 előtt is Flate-tömörített volt, a képadat pedig a saját kodekjeit hozza, ezért egy képekkel teli prospektus alig mozdul. Azok a fájlok esnek össze, amelyek szerkezetben gazdagok: több ezer mezőszótárt tartalmazó AcroFormok, mély vázlatfák, címkézett PDF struktúraelemek. Ezek az objektumok aprók, sokan vannak, és majdnem egyformák, és pontosan ez az ismétlődés az, amit a Flate kihasznál, amint egyetlen pufferben ülnek, nem pedig a törzsben szétszórva, közéjük ékelt fejlécekkel
Könnyű alábecsülni, hogy egy régi fájl mekkora része többletteher. Egy űrlaparchívum, amely évek szerkesztéseit szívta magába, bőven a bájtjai felénél többet költhet szótárfejlécekre, xref-kitöltésre és olyan revíziókra, amelyekre egyetlen olvasó sem fog ránézni. Az itt tárgyalt két képesség az első kettőt nyeri vissza. A harmadik, a felhalmozott revíziók, csak tömörítésnek enged, akkor, amikor a fájlnak már nem kell emlékeznie a saját történetére
A HotPDF-ben mindkettőt egy tulajdonságpárral kapcsolja be, és az, hogy hogyan függnek egymástól, fontosabb, mint az, milyen sorrendben írja le őket:
var
Pdf: THotPDF;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.FileName := 'catalog-2026.pdf';
Pdf.UseXRefStream := True; // bináris xref, az ObjStm előfeltétele
Pdf.UseObjectStreams := True; // objektumok becsomagolása /Type /ObjStm alá
Pdf.BeginDoc;
Pdf.CurrentPage.SetFont('Arial', [], 11);
Pdf.CurrentPage.TextOut(50, 760, 0, 'Compressed structure demo');
Pdf.EndDoc; // XRefStm + ObjStm tárolókat bocsát ki
finally
Pdf.Free;
end;
end;
A UseObjectStreams tulajdonsághoz a UseXRefStream értékét True értékre kell állítani. A tömörített objektum 2-es típusú xref-bejegyzésen keresztül érhető el, amely egy objektumfolyam-számot és egy indexet rögzít, és a klasszikus 20 bájtos szöveges sorban nincs hely ennek a párnak. A UseObjectStreams tehát önmagában semmi láthatót nem tesz; a működő konfiguráció mindkét jelző, a BeginDoc előtt beállítva. Ha a BeginDoc után állítja be őket, a HotPDF már elkötelezte magát a régebbi elrendezés mellett
Miért kikapcsolt mindkettő alapból
A HotPDF mindkét tulajdonságot False értéken hagyja a dobozból kivéve, és ennek oka a régi, alsóbb rendszerekkel való integrációkban mutatkozik meg. Az az olvasó, amely csak a PDF 1.4 formátumot érti, nem jelenti be, hogy nem tud mit kezdeni a tömörített objektumokkal. Belefut egy xref-folyamba, nem találja egyik várt trailer-kulcsszót sem, és sérült kereszthivatkozási táblát jelent, vagy egyszerűen megtagadja a fájl megnyitását. Ha a kimenete egy öregedő faxátjáróba, egy beágyazott értelmezőt futtató hardveres nyomtatóba vagy egy évtizede az 1.4-es specifikáció alapján megírt elemzőbe folyik, arra a csatornára hagyja mindkét jelzőt kikapcsolva, és viselje el a nagyobb fájlt. Archiválási tároláshoz és webes kiszállításhoz, ahol minden főbb megjelenítő húsz éve olvassa a PDF 1.5 formátumot, a bekapcsolásuk szinte ingyen kapott tömörítés
Van egy másodlagos hatás is, amelyről érdemes szólni a támogatási csapatnak. Amint a szótárak objektumfolyamokba kerülnek, két generált fájl bájtról bájtra való összehasonlítása értelmét veszti, mert egyetlen mező módosítása újratömörítheti az egész tárolót, és mindent elmozdíthat mögötte. Az ilyen fájlokat objektumtartalom szerint hasonlítsa össze, ne bináris összevetéssel
Inkrementális frissítések és a bájteltolások, amelyeket megvédenek
A digitális aláírás egy kifejezett /ByteRange tartományt fed le: a fizikai fájl két szakaszát, abszolút bájteltolásként megadva, amelyekre a CMS-kivonat készült. Írja újra a fájlt – akár úgy, hogy a képernyőn azonosnak látszik –, és ezek az eltolások mind elmozdulnak. A kivonat nem fog egyezni, és az aláírás töröttnek olvasódik. Pontosan ezt a problémát oldja meg az ISO 32000-1 §7.5.6 az inkrementális frissítésekkel. Az új és a megváltozott objektumok a meglévő %%EOF után fűződnek hozzá, majd új kereszthivatkozási szakasz íródik, amelynek /Prev bejegyzése az előzőre mutat vissza. Az eredeti bájtokat semmi nem bolygatja, így az aláírt revízió ellenőrizhető marad, és az Acrobat az aláíráspanelen külön-külön tudja bemutatni az egyes aláírt revíziókat
A HotPDF ezt saját belépési ponton keresztül teszi elérhetővé:
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'); // csak a különbséget fűzi hozzá
Két dolgon szoktak megbotlani. A BeginIncrementalUpdate hívásnak az eredeti fájlnevet kell megkapnia, mert a hozzáfűzött xref-szakasz olyan eltolásokat rögzít, amelyek csak pontosan azokra az eredeti bájtokra értelmesek; irányítsa átnevezett vagy újramentett másolatra, és az eltolások egy már nem létező fájlt írnak le. A mentés pedig felépítésénél fogva csak hozzáfűz, tehát a kimenet mindig nagyobb a bemenetnél. Ez a növekedés nem lefaragandó pazarlás. Ugyanaz a tulajdonság, amely érintetlenül hagyja a korábbi aláírt revíziókat
Betöltött fájl módosítása a LoadFromFile hívásán át
Azok a fejlesztők, akik a HotPDF-fel először a generálási API-n keresztül találkoztak, hajlamosak nekimenni egy bizonyos falnak. A BeginDoc vadonatúj dokumentumot nyit, és ez rossz eszköz akkor, amikor egy már létezőt akar módosítani. A meglévő fájl szerkesztése helyette a betöltött dokumentum hívásain fut:
PageCount := Pdf.LoadFromFile('base.pdf');
Pdf.InsertPagesFromDocument(OtherDoc, '1-3', 5); // az 1-3. oldal az 5. oldal után
Pdf.MovePage(2, 5);
Pdf.SaveLoadedDocument('modified.pdf');
Ha a kettőt keveri, a tünet olyan kimeneti fájl, amely az új tartalmát hordozza, az eredetiből viszont semmit, mert a BeginDoc vidáman felépített egy friss dokumentumot amellett, amelyről azt hitte, hogy szerkeszti. Olvassa a LoadFromFile és a SaveLoadedDocument párost egy szókincsként, a BeginDoc és az EndDoc párost pedig egy másikként. Az a rutin, amely ugyanarra a fájlra mindkettőhöz hozzányúl, majdnem mindig hibás
Mikor érdemes tömöríteni egy hozzáfűzött fájlt
A csak hozzáfűző mentésnek lassú ára van. Egy éjszakai feladat, amely egyetlen állapotsort bélyegez ugyanarra a PDF-re, egy év alatt 365 revíziót termel, és minden revízió új xref-szakaszt vontat maga után. Amikor ez a történet túlélte a hasznosságát, és a fájlban egyetlen aláírásnak sem kell fennmaradnia, az egészet ellapíthatja azzal, hogy a betöltött dokumentum útvonalán újrasorosítja:
Pdf.LoadFromFile('stamped.pdf');
Pdf.SaveLoadedDocument('compacted.pdf');
Ez az újramentés teljes újraírás. Szándékosan eldobja a korábbi revíziókat, és eltör minden aláírást, amely még a fájlban van, ezért tegye ugyanaz mögé a szabályzati kapu mögé, amelyet bármely más romboló lépésre alkalmaz. Egy éles környezetben megálló szabály: tömörítsen, ha a revíziók száma átlép egy küszöböt, vagy ha a hozzáfűzött többlet az alapfájl valamekkora hányada fölé nő, és soha ne tömörítsen olyan dokumentumot, amelynek az aláíráspanelén bármi van
A kimenet ellenőrzése kiszállítás előtt
Ennek a képességpárnak az ellenőrzése üdítően kézzelfogható. Nyissa meg az eredményt az Adobe Acrobatban, és győződjön meg három dologról: a dokumentum tulajdonságai PDF 1.5-öt vagy újabbat jeleznek, ha az objektumfolyamok be vannak kapcsolva; az aláíráspanel az inkrementális frissítés után is érvényesnek találja minden korábban aláírt revíziót; és az oldalszám meg a könyvjelzők sértetlenül átjöttek egy betöltési, módosítási és mentési körön. Archiválási kimenetnél tolja át a fájlt a veraPDF eszközön is, mert a tömörített xref pontosan az a szerkezet, amelyet egy szigorú validátor alaposabban vizsgál, mint valaha is egy elnéző megjelenítő. Ha a munkája nagyon nagy bemenetekkel is jár, a Direct File API nagy PDF-munkameneteinek bemutatójában szereplő vizsgálati módszerek természetesen párosulnak az inkrementális mentéssel, a fenti bájttartományok mögötti aláírási gépezetet pedig a HotPDF digitális aláírásokról és a PAdES-ről szóló cikke tárgyalja mélységében
Mindkét képesség a Delphihez és C++Builderhez készült HotPDF Delphi Component részeként érkezik, a blog más helyein tárgyalt generálási, űrlap-, titkosítási és aláírási API-k mellett. A termékoldal a teljes API-referenciára hivatkozik, ha a fenti hívásokat a saját dokumentum-feldolgozósorához szeretné igazítani