Tehnični članak

Skupna slovarja simbolov JBIG2 med stranmi v Delphiju

Petdesetstranska skenirana pogodba ponavlja isto abecedo na vsaki strani, vendar kodirnik JBIG2, ki za vsako sliko izdela svoj slovar simbolov, to abecedo ponovno uri petdesetkrat. HotPDF, izvorna komponenta PDF za Delphi in C++Builder, lahko namesto tega zbira en skupni slovar simbolov za celoten dokument in ga promovira v en sam tok /JBIG2Globals na ravni dokumenta, tako da lastni tok JBIG2 vsake strani samo kaže na ID-je simbolov, namesto da bi shranjeval svojo kopijo abecede

Ta članek je namenoma omejen in obravnava samo način, kako HotPDF interno izdela to skupno rabo med stranmi — osnove JBIG2, primerjava CCITT ter kompromisi med Lossless in LossyLevel so že opisani v spremljevalnem članku o izvornem binarnem stiskanju JBIG2 v Delphiju, za katerega predpostavljamo, da ste ga prebrali

Zakaj stiskanje JBIG2 po posameznih straneh še vedno ponavlja isti strošek?

Odgovor je, da se med klici ne prenaša nobeno stanje. Vsakič, ko HotPDF-jev kodirnik izdela slovar simbolov za eno sliko, je ta slovar omejen na ta posamezni klic AddImage: prehod za ujemanje oblik se začne z ničle, vsak glif na strani je razvrščen kot nov, nastale bitne slike pa so aritmetično kodirane in shranjene na novo. Isti kodirnik oskrbite s petdesetimi stranmi v isti pisavi in z veseljem bo petdesetkrat ponovil celoten učni prehod, saj je z njegovega vidika vsaka stran nepovezana slika, ki je po naključju podobna. UseSymbolDictionary po posamezni strani na eni strani že z veliko prednostjo prekaša plosko kodiranje generičnih območij, vendar doseže zgornjo mejo precej pred tem, kar bi lahko ponudil pravi večstranski sken

Kako HotPDF deli en slovar simbolov med stranmi?

Na THPDFJBIG2Options omogočite AccumulateGlobalsAcrossPages in HotPDF ohrani en slovar simbolov v pomnilniku za celotno življenjsko dobo dokumenta, namesto da bi ga po vsaki sliki zavrgel. Glifi vsake naslednje strani se pred ponovnim kodiranjem preverijo glede na ta tekoči slovar: oblika, ki že obstaja, se ponovno uporabi prek svojega ID-ja simbola, oblika, ki je še nihče ni videl, pa se doda in kodira v slovar. Primerjava ponovno uporablja isto logiko tolerance, ki jo LossyLevel uporablja na eni strani — nekoliko šumeč sken iste črke še vedno velja za ujemanje — zato se akumulator neopazno ne napihne v po en vnos slovarja za vsako različico istega glifa na ravni posameznih slikovnih pik. Izločanje poteka najprej in napaja to primerjavo: HotPDF prehodi bitno sliko vsake strani ter z zapolnjevanjem območij izloči povezane oblike iz črnih slikovnih pik, kar je enaka zamisel kot ročno sledenje črnilnim madežem, primerjajo pa se prav te izločene oblike, ne surovi bloki slikovnih pik, s tekočim slovarjem

Kako je skupni slovar nameščen v toku /JBIG2Globals

Zbrani slovar se zapiše kot en segment slovarja simbolov znotraj toka /JBIG2Globals, kjer je shranjen pod stalno številko segmenta, da lahko vsaka stran kaže na isti cilj. Znotraj vdelane organizacije JBIG2, ki jo določa ISO 32000-1 §7.4.7, lahko segment besedilnega območja poimenuje drug segment kot vir simbolov prek polja referred-to-segment v glavi segmenta, in prav na ta mehanizem se opira HotPDF: tok globals vsebuje en velik slovar simbolov, lasten tok JBIG2 vsake strani pa se skrči na segment informacij o strani in segment besedilnega območja, katerega seznam referred-to kaže nazaj na segment globals. Kar je bilo prej samostojen bitni tok za vsako stran, postane kratek seznam položajev in ID-jev simbolov, vsaka tako izdelana stran pa se sklicuje na isti posredni objekt /JBIG2Globals namesto na njegovo kopijo. HotPDF-jeva regresijska pokritost preverja prav to: kodira kratek dokument, v katerem ima vsaka stran drugačno razporeditev glifov, ga ponovno naloži in prešteje, koliko različnih sklicev na objekte /JBIG2Globals se pojavi v datoteki — en dokument, en sklic na objekt, ne glede na to, koliko strani je prispevalo simbole

Vklop zbiranja slovarja simbolov med stranmi

Stikalo je na istem zapisu možnosti, ki je opisan v spremljevalnem članku, in za dejanski vklop zbiranja se morajo ujemati štiri nastavitve

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;

Ta kombinacija ni neobvezen okras. Vmesnik zunanjega kodirnika, opisan v članku o binarnem stiskanju — tisti, ki ga za produkcijska razmerja registrirate prek RegisterJBIG2EncoderBackend — temelji na kodiranju po posameznih slikah, HotPDF-jevi primeri zbiranja in regresijski testi pa vedno združijo AccumulateGlobalsAcrossPages z UseExternalEncoder := False. To obravnavajte kot strogo zahtevo, ne kot predlog: skupna raba med stranmi je zmožnost izvornega kodirnika, registrirano zunanje zaledje pa preprosto ni del poti, ki izdela skupni slovar

Kako manjši dejansko postane večstranski sken?

Iskren odgovor se začne pri tem, kar sprva ni prineslo opazne razlike. Prejšnja izdaja je dodala predpomnilnik tokov /JBIG2Globals, temelječ na vsebini — iskanje je uporabljalo 64-bitni razpršilnik FNV-1a bajtov toka, zato sta lahko dve sliki, ki sta po naključju ustvarili bajtno enake podatke globals, delili en objekt PDF. Glede na dejanski izhod je ta predpomnilnik komaj pomagal, saj je HotPDF-jevo obstoječe zaznavanje podvojenih celotnih slik že združevalo bajtno enake slike, še preden je predpomnilnik sploh prišel na vrsto. Naučena lekcija je, da deduplikacija na ravni toka prinese korist šele, ko lahko dve resnično različni sliki strani še vedno delita en rastoči slovar, kar zagotovi pravo zbiranje med stranmi

Za ta zahtevnejši primer HotPDF-jeva lastna inženirska ocena določa dodatni prihranek približno od 30 do 60 odstotkov glede na velikost, ki jo sama deduplikacija na ravni toka doseže, pri tipičnem večstranskem skenu z eno ponavljajočo se pisavo — razpon je odvisen od tega, kolikšen del vizualnega besedišča dokumenta se dejansko ponavlja, saj stran, polna edinstvenih diagramov, slovarju ne ponuja ničesar za ponovno uporabo. To obravnavajte kot cilj zasnove in ne kot jamstvo za določen vhod ter raje izmerite lastne dokumente, kot da bi zaupali eni sami številki. Primer JBIG2Benchmark, ki je priložen HotPDF, obstaja prav za ta namen: isti večstranski sken kodira na štiri različne načine in izpiše nastalo velikost datoteke za vsako konfiguracijo, tako da primerjava temelji na vaši lastni mešanici skenov in ne na sintetični

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.

Kje zbiranje med stranmi doseže svoje omejitve

Zbrani slovar je omejen na 4096 simbolov, kar je ista meja, ki jo izvorni kodirnik na eni strani že uveljavlja za posamezno sliko. Če to mejo presežete sredi dokumenta, HotPDF ne sproži izjeme in ne prekine izvajanja: akumulator zavrne novi glif, stran, ki ga je uvedla, pa se samodejno vrne na neodvisno kodiranje po posamezni sliki, zato dokument ostane pravilen — le prihranek zaradi skupne rabe med stranmi se ne uporablja več za strani, ki so presegle mejo. Drugo varovalo spremlja skupno velikost namesto števila simbolov: ko skupna širina simbolov zbranega slovarja preseže 131071 slikovnih pik, HotPDF trenutni paket prelije na disk in samodejno začne novo skupino globals, namesto da bi eni strukturi v pomnilniku dovolil neomejeno rast. Nobena od teh omejitev ne zahteva kode na vaši strani, saj sta obe samodejna povratna mehanizma in ne izjemi, ki bi ju morali prestrezati

Skladnost s PDF/A je edina nastavitev, ki celoten mehanizem izklopi, namesto da bi ga samo omejila. HotPDF v trenutku, ko PDFACompliance ni prazen, na vsaki strani tiho zamenja JBIG2 s CCITT Group 4, neodvisno od AccumulateGlobalsAcrossPages ali česar koli drugega v JBIG2Options — to je namerna izbira skladnosti in ne napaka, vendar pomeni, da sta arhivski profil in skupna raba simbolov med stranmi danes medsebojno izključujoča. Ne glede na izbrano konfiguracijo pred zaupanjem dekodirajte zapisano: datoteko naložite nazaj z LoadFromFile in vsako stran posredujte skozi ExtractLoadedImage, ki skupne globals razreši tako, kot bi jih vsak skladen bralnik, nato pa rezultat primerjajte z izvornimi bitnimi slikami

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;

Skupna raba slovarja med stranmi vpliva samo na stran binarnih slik dokumenta. Če isti cevovod poleg skenov ustvarja tudi strani z ustvarjenim besedilom — naslovne liste, kazala ali besedilno plast OCR — objektni in xref tokovi napadejo drugo polovico proračuna velikosti datoteke tako, da stisnejo strukturo dokumenta, ki jo te strani dodajo. Skupni globals JBIG2 je del HotPDF Component za Delphi in C++Builder, skupaj z možnostmi JBIG2 po posameznih slikah in preostalim cevovodom stiskanja