En femti-siders skannet kontrakt gjentar det samme alfabetet på hver side, men en JBIG2-koder som bygger én symbolordbok per bilde, trener opp det alfabetet på nytt femti separate ganger. HotPDF, den native Delphi- og C++Builder-PDF-komponenten, kan i stedet akkumulere én delt symbolordbok gjennom hele dokumentet og forfremme den til én enkelt dokumentnivå-/JBIG2Globals-strøm, slik at hver sides egen JBIG2-strøm bare refererer symbol-ID-er i stedet for å lagre sin egen kopi av alfabetet
Denne artikkelen holder seg bevisst smal og dekker bare hvordan HotPDF bygger den delingen på tvers av sider internt — JBIG2-grunnlaget, CCITT-sammenligningen, og Lossless-versus-LossyLevel-avveiningene hører allerede hjemme i følgeartikkelen om nativ JBIG2-binærnivå-komprimering i Delphi, som denne artikkelen forutsetter at du har lest
Hvorfor gjentar per-side JBIG2-komprimering fortsatt den samme kostnaden?
Svaret er at ingenting bærer tilstand mellom kall. Hver gang HotPDFs koder bygger en symbolordbok for ett bilde, er den ordboken avgrenset til det ene AddImage-kallet: form-matchings-passeringen starter fra null, hver glyf på siden klassifiseres som ny, og de resulterende bitkartene aritmetisk-kodes og lagres på nytt. Mat den samme koderen med femti sider satt i samme skrifttype, og den gjentar villig hele den treningspasseringen femti ganger, fordi fra dens synspunkt er hver side et urelatert bilde som tilfeldigvis ser likt ut. Per-side UseSymbolDictionary slår allerede en flat generisk-region-koding med god margin på én enkelt side, men den flater ut godt før taket et ekte flersides-skann etterlater på bordet
Hvordan deler HotPDF én enkelt symbolordbok på tvers av sider?
Aktiver AccumulateGlobalsAcrossPages på THPDFJBIG2Options, og HotPDF holder én symbolordbok i live i minnet gjennom hele dokumentets levetid i stedet for å forkaste den etter hvert bilde. Hver etterfølgende sides glyfer sjekkes mot den løpende ordboken før noe blir re-kodet: en form som allerede finnes, gjenbrukes ved sin symbol-ID, og bare en form ingen har sett før, blir lagt til og kodet inn i ordboken. Sammenligningen gjenbruker den samme toleranselogikken LossyLevel anvender på én enkelt side — en litt støyete skanning av den samme bokstaven teller fortsatt som en match — slik at akkumulatoren ikke stille sveller til én ordbokoppføring per piksel-nivå-variasjon av den samme glyfen. Uttrekking skjer først og mater den sammenligningen: HotPDF går gjennom hver sides bitkart og trekker ut sammenhengende former gjennom flood fill mot de svarte pikslene, den samme idéen som å spore blekkflekker for hånd, og det er de uttrukkede formene, ikke rå pikselblokker, som sammenlignes mot den løpende ordboken
Hvordan den delte ordboken sitter inne i en /JBIG2Globals-strøm
Den akkumulerte ordboken skrives som ett symbolordbok-segment inne i /JBIG2Globals-strømmen, holdt ved et fast segmentnummer slik at hver side kan peke på det samme målet. Inne i den innebygde JBIG2-organiseringen ISO 32000-1 §7.4.7 definerer, kan et tekstregion-segment navngi et annet segment som sin symbolkilde gjennom referert-til-segment-feltet i segmentheaderen, og det er den nøyaktige mekanismen HotPDF lener seg på: globals-strømmen bærer den ene store symbolordboken, og hver sides egen JBIG2-strøm krymper ned til et side-info-segment pluss et tekstregion-segment hvis referert-til-liste peker tilbake på globals-segmentet. Det som pleide å være en selvstendig bitstrøm per side, blir en kort liste med posisjoner og symbol-ID-er, og hver side bygget på denne måten refererer til det identiske indirekte /JBIG2Globals-objektet i stedet for en kopi av det. HotPDFs egen regresjonsdekning sjekker nøyaktig det: kod et kort dokument der hver side har et forskjellig glyf-layout, last det inn på nytt, og tell hvor mange distinkte /JBIG2Globals-objektreferanser som dukker opp i filen — ett dokument, én objektreferanse, uansett hvor mange sider som bidro med symboler til den
Å skru på akkumulering av symbolordbok på tvers av sider
Bryteren sitter på den samme opsjons-recorden dekket i følgeartikkelen, og den krever at fire innstillinger er enige med hverandre før akkumulering faktisk trer i kraft
var
Pdf: THotPDF;
Bmp: TBitmap;
PageIdx, ImgIdx: Integer;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.JBIG2Options.Lossless := True;
Pdf.JBIG2Options.UseSymbolDictionary := True;
Pdf.JBIG2Options.UseGlobalSegments := True;
Pdf.JBIG2Options.AccumulateGlobalsAcrossPages := True; // opt-in, default False
Pdf.JBIG2Options.UseExternalEncoder := False; // accumulation needs the native path
Pdf.JBIG2Options.UseNativeArithmeticFallback := True;
Pdf.BeginDoc;
for PageIdx := 0 to ScannedPages.Count - 1 do
begin
if PageIdx > 0 then
Pdf.AddPage;
Bmp := ScannedPages[PageIdx]; // 1-bit TBitmap for this page
ImgIdx := Pdf.AddImage(Bmp, icJBIG2);
Pdf.CurrentPage.ShowImage(ImgIdx, 0, 0, Bmp.Width, Bmp.Height, 0);
end;
Pdf.EndDoc; // the shared /JBIG2Globals stream is finalized here
finally
Pdf.Free;
end;
end;
Den koblingen er ikke valgfri pynt. Den eksterne koder-sømmen beskrevet i binærnivå-komprimeringsartikkelen — den man registrerer gjennom RegisterJBIG2EncoderBackend for produksjonsklasse-komprimeringsforhold — er bygget rundt per-bilde-koding, og HotPDFs egne akkumulerings-demoer og regresjonstester parer alltid AccumulateGlobalsAcrossPages med UseExternalEncoder := False. Behandle det som et hardt krav snarere enn et forslag: deling på tvers av sider er en nativ-koder-funksjon, og en registrert ekstern backend er rett og slett ikke en del av veien som bygger den delte ordboken
Hvor mye mindre blir et flersides-skann faktisk?
Det ærlige svaret starter med hva som ikke flyttet nålen først. En tidligere utgivelse la til en innholdsadressert buffer for /JBIG2Globals-strømmer — et oppslag nøkkelsatt på en 64-bit FNV-1a-hash av strømbytene, slik at to bilder som tilfeldigvis produserte byte-identiske globals-data, kunne dele ett PDF-objekt. Målt mot ekte utdata hjalp den bufferen knapt, fordi HotPDFs eksisterende hele-bilde-duplikatdeteksjon allerede kollapset byte-identiske bilder før bufferen noensinne fikk sjansen til å kjøre. Lærdommen var at strøm-nivå-deduplisering bare lønner seg når to genuint forskjellige sidebilder likevel kan dele én voksende ordbok, noe ekte akkumulering på tvers av sider leverer
For det vanskeligere tilfellet setter HotPDFs egen ingeniør-anslag den ekstra besparelsen til omtrent 30 til 60 prosent mindre enn strøm-nivå-deduplisering alene oppnår, for et typisk flersides-skann bygget fra én gjentakende skrifttype — området beveger seg med hvor mye av dokumentets visuelle ordforråd som faktisk gjentar seg, ettersom en side full av unike diagrammer ikke gir ordboken noe å gjenbruke. Behandle det som et designmål snarere enn en garanti for noe spesifikt inndata, og mål dine egne dokumenter i stedet for å stole på ett enkelt tall. JBIG2Benchmark-demoen som følger med HotPDF, finnes nettopp for det formålet: den koder det samme flersides-skannet på fire forskjellige måter og skriver ut den resulterende filstørrelsen for hver konfigurasjon, slik at sammenligningen kjører mot din egen skann-miks i stedet for en syntetisk en
procedure RunScenario(const Title: string; AccumulateGlobals: Boolean);
var
Pdf: THotPDF;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.JBIG2Options.Lossless := True;
Pdf.JBIG2Options.UseSymbolDictionary := True;
Pdf.JBIG2Options.UseGlobalSegments := True;
Pdf.JBIG2Options.AccumulateGlobalsAcrossPages := AccumulateGlobals;
Pdf.JBIG2Options.UseExternalEncoder := not AccumulateGlobals;
// ... encode the same three-page scan here, then compare file sizes.
finally
Pdf.Free;
end;
end;
begin
RunScenario('Per-image lossless baseline', False);
RunScenario('Cross-page accumulated globals', True);
end.
Hvor akkumulering på tvers av sider når sine grenser
Den akkumulerte ordboken er begrenset til 4096 symboler, det samme taket den per-bilde-native koderen allerede håndhever på én enkelt side. Krysser man den grensen midt i dokumentet, kaster ikke HotPDF et unntak eller avbryter kjøringen: akkumulatoren avviser den nye glyfen, og siden som introduserte den, faller automatisk tilbake til uavhengig per-bilde-koding, slik at dokumentet fortsatt kommer ut korrekt — man slutter bare å få besparelsen på tvers av sider for hvilke som helst sider som presset forbi taket. En annen sikringsmekanisme følger med på total størrelse i stedet for symbolantall: så snart den akkumulerte ordbokens kombinerte symbolbredde krysser 131071 piksler, søler HotPDF automatisk den gjeldende batchen til disk og starter en ny globals-gruppe, i stedet for å la én struktur i minnet vokse uten grense. Ingen av grensene trenger noen kode på din side, ettersom begge er automatiske reserveløsninger snarere enn unntak man må fange
PDF/A-konformitet er den ene innstillingen som skrur av hele mekanismen i stedet for bare å begrense den. HotPDF erstatter stille CCITT Group 4 for JBIG2 i det øyeblikket PDFACompliance er ikke-tom, på hver eneste side, uavhengig av AccumulateGlobalsAcrossPages eller noe annet på JBIG2Options — et bevisst konformitetsvalg, ikke en bug, men det betyr at en arkivprofil og deling av symboler på tvers av sider er gjensidig utelukkende i dag. Uansett hvilken konfigurasjon man lander på, avkod det man skrev før man stoler på det: last filen inn igjen med LoadFromFile og trekk hver side gjennom ExtractLoadedImage, som løser den delte globals-en for deg på samme måte som enhver konform leser ville gjort, og sammenlign resultatet mot kildebitkartene
var
Loaded: THotPDF;
PageBmp: TBitmap;
PageIdx: Integer;
begin
Loaded := THotPDF.Create(nil);
try
Loaded.LoadFromFile('scanned-contract.pdf');
for PageIdx := 0 to Loaded.PagesCount - 1 do
begin
PageBmp := Loaded.ExtractLoadedImage(PageIdx); // resolves the shared globals for you
try
// Compare PageBmp against the source bitmap for this page.
finally
PageBmp.Free;
end;
end;
finally
Loaded.Free;
end;
end;
Deling av ordbok på tvers av sider rører bare den binærnivå-bilde-siden av et dokument. Hvis den samme pipelinen også genererer tekstsider ved siden av skannene — omslagsark, indekssider, et OCR-tekstlag — angriper objektstrømmer og xref-strømmer den andre halvparten av filstørrelsesbudsjettet ved å komprimere dokumentstrukturen de sidene legger til. JBIG2-globals på tvers av sider følger med som en del av HotPDF-komponenten for Delphi og C++Builder, sammen med per-bilde JBIG2-opsjonene og resten av komprimeringspipelinen