Een gescand contract van vijftig pagina's herhaalt op elke pagina hetzelfde alfabet, maar een JBIG2-encoder die per afbeelding één symboolwoordenboek opbouwt, traint dat alfabet vijftig keer apart opnieuw. HotPDF, de native Delphi- en C++Builder-PDF-component, kan in plaats daarvan één gedeeld symboolwoordenboek opbouwen over het hele document en dit promoveren tot één documentbrede /JBIG2Globals-stroom, zodat de eigen JBIG2-stroom van elke pagina alleen naar symbool-ID's verwijst in plaats van een eigen kopie van het alfabet op te slaan
Dit stuk blijft bewust smal en behandelt alleen hoe HotPDF die pagina-overschrijdende deling intern opbouwt — JBIG2-basisprincipes, de vergelijking met CCITT, en de afwegingen tussen Lossless en LossyLevel staan al in het bijbehorende artikel over native JBIG2-bilevelcompressie in Delphi, wat dit stuk aanneemt dat u heeft gelezen
Waarom herhaalt JBIG2-compressie per pagina nog steeds dezelfde kosten?
Het antwoord is dat niets status tussen aanroepen doorgeeft. Elke keer dat HotPDF's encoder een symboolwoordenboek opbouwt voor één afbeelding, is dat woordenboek beperkt tot die ene AddImage-aanroep: de vormherkenningspas begint bij nul, elke glyph op de pagina wordt als nieuw geclassificeerd, en de resulterende bitmaps worden vers rekenkundig gecodeerd en opgeslagen. Voer diezelfde encoder vijftig pagina's in hetzelfde lettertype, en hij herhaalt graag die hele trainingspas vijftig keer, omdat vanuit zijn oogpunt elke pagina een ongerelateerde afbeelding is die toevallig gelijkaardig oogt. Per-afbeelding UseSymbolDictionary verslaat een vlakke generieke-regiocodering al met een ruime marge op één pagina, maar het loopt ver vóór het plafond vast dat een echte meerpaginascan op tafel laat liggen
Hoe deelt HotPDF één symboolwoordenboek tussen pagina's?
Schakel AccumulateGlobalsAcrossPages in op THPDFJBIG2Options en HotPDF houdt één symboolwoordenboek in het geheugen levend gedurende de hele levensduur van het document in plaats van het na elke afbeelding weg te gooien. De glyphs van elke volgende pagina worden tegen dat lopende woordenboek gecontroleerd voordat er iets opnieuw gecodeerd wordt: een vorm die al bestaat, wordt hergebruikt via zijn symbool-ID, en alleen een vorm die nog nooit is gezien, wordt toegevoegd en in het woordenboek gecodeerd. De vergelijking hergebruikt dezelfde tolerantielogica die LossyLevel op één pagina toepast — een licht ruizige scan van dezelfde letter telt nog steeds als overeenkomst — dus groeit de accumulator niet stilzwijgend uit tot één woordenboekvermelding per pixelniveau-variatie van dezelfde glyph. Extractie gebeurt eerst en voedt die vergelijking: HotPDF doorloopt de bitmap van elke pagina en haalt verbonden vormen eruit via flood fill tegen de zwarte pixels, hetzelfde idee als het handmatig natekenen van inktvlekken, en het zijn die geëxtraheerde vormen, niet ruwe pixelblokken, die tegen het lopende woordenboek worden vergeleken
Hoe het gedeelde woordenboek zich in een /JBIG2Globals-stroom bevindt
Het geaccumuleerde woordenboek wordt geschreven als één symboolwoordenboek-segment binnen de /JBIG2Globals-stroom, vastgehouden op een vast segmentnummer, zodat elke pagina naar hetzelfde doel kan wijzen. Binnen de ingebedde JBIG2-organisatie die ISO 32000-1 §7.4.7 definieert, kan een tekstregio-segment een ander segment als zijn symboolbron benoemen via het verwezen-segmentveld in de segmentheader, en dat is precies het mechanisme waarop HotPDF leunt: de globals-stroom draagt het ene grote symboolwoordenboek, en de eigen JBIG2-stroom van elke pagina krimpt tot een pagina-info-segment plus een tekstregio-segment waarvan de verwijzingslijst terugwijst naar het globals-segment. Wat vroeger een op zichzelf staande bitstream per pagina was, wordt een korte lijst van posities en symbool-ID's, en elke pagina die op deze manier is opgebouwd, verwijst naar hetzelfde indirecte /JBIG2Globals-object in plaats van een kopie ervan. HotPDF's eigen regressiedekking controleert precies dat: codeer een kort document waarbij elke pagina een andere glyph-indeling heeft, laad het opnieuw, en tel hoeveel afzonderlijke /JBIG2Globals-objectverwijzingen in het bestand voorkomen — één document, één objectverwijzing, ongeacht hoeveel pagina's symbolen ervoor hebben bijgedragen
Pagina-overschrijdende accumulatie van symboolwoordenboeken inschakelen
De schakelaar bevindt zich op hetzelfde optiesrecord dat in het bijbehorende artikel wordt behandeld, en het vereist dat vier instellingen met elkaar overeenstemmen voordat accumulatie daadwerkelijk in werking treedt
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;
Die combinatie is geen optionele versiering. De externe-encodernaad die in het bilevelcompressie-artikel wordt beschreven — die u registreert via RegisterJBIG2EncoderBackend voor productiekwaliteit-verhoudingen — is gebouwd rond per-afbeelding-codering, en HotPDF's eigen accumulatiedemo's en regressietests koppelen AccumulateGlobalsAcrossPages altijd aan UseExternalEncoder := False. Behandel dat als een harde vereiste in plaats van een suggestie: pagina-overschrijdende deling is een functie van de native encoder, en een geregistreerde externe backend maakt eenvoudigweg geen deel uit van het pad dat het gedeelde woordenboek opbouwt
Hoeveel kleiner wordt een meerpaginascan daadwerkelijk?
Het eerlijke antwoord begint met wat de naald aanvankelijk niet deed bewegen. Een eerdere release voegde een op inhoud geadresseerde cache toe voor /JBIG2Globals-stromen — een opzoeking gesleuteld op een 64-bit FNV-1a-hash van de streambytes, zodat twee afbeeldingen die toevallig byte-identieke globals-data produceerden, één PDF-object konden delen. Gemeten tegen echte output hielp die cache nauwelijks, omdat HotPDF's bestaande detectie van hele-afbeelding-duplicaten byte-identieke afbeeldingen al invouwde voordat de cache ooit de kans kreeg om te draaien. De les was dat deduplicatie op streamniveau alleen loont zodra twee werkelijk verschillende paginabeelden nog steeds één groeiend woordenboek kunnen delen, wat precies is wat echte pagina-overschrijdende accumulatie oplevert
Voor dat moeilijkere geval plaatst HotPDF's eigen technische schatting de extra besparing op ongeveer 30 tot 60 procent kleiner dan alleen deduplicatie op streamniveau bereikt, voor een typische meerpaginascan opgebouwd uit één terugkerend lettertype — het bereik beweegt mee met hoeveel van de visuele woordenschat van het document daadwerkelijk terugkeert, aangezien een pagina vol unieke diagrammen het woordenboek niets te hergebruiken geeft. Behandel dat als een ontwerpdoel in plaats van een garantie voor een specifieke invoer, en meet uw eigen documenten in plaats van op één getal te vertrouwen. De demo JBIG2Benchmark die met HotPDF wordt meegeleverd, bestaat precies voor dat doel: het codeert dezelfde meerpaginascan op vier verschillende manieren en drukt de resulterende bestandsgrootte voor elke configuratie af, zodat de vergelijking loopt tegen uw eigen scanmix in plaats van een synthetische
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.
Waar pagina-overschrijdende accumulatie zijn grenzen bereikt
Het geaccumuleerde woordenboek is begrensd op 4096 symbolen, hetzelfde plafond dat de native encoder per afbeelding al op één pagina afdwingt. Overschrijd die limiet halverwege een document en HotPDF werpt geen uitzondering op of breekt de run niet af: de accumulator weigert de nieuwe glyph, en de pagina die deze introduceerde valt automatisch terug op onafhankelijke per-afbeelding-codering, zodat het document nog steeds correct uitkomt — u verliest alleen de pagina-overschrijdende besparing voor welke pagina's ook het plafond overschreden. Een tweede vangnet houdt totale grootte in de gaten in plaats van symboolaantal: zodra de gecombineerde symboolbreedte van het geaccumuleerde woordenboek 131071 pixels overschrijdt, stort HotPDF de huidige batch automatisch naar schijf en start een verse globals-groep, in plaats van één structuur in het geheugen onbegrensd te laten groeien. Geen van beide limieten vereist code aan uw kant, aangezien beide automatische fallbacks zijn in plaats van uitzonderingen die u moet opvangen
PDF/A-conformiteit is de ene instelling die het hele mechanisme volledig uitschakelt in plaats van het alleen te begrenzen. HotPDF vervangt JBIG2 stilzwijgend door CCITT Groep 4 zodra PDFACompliance niet leeg is, op elke pagina, onafhankelijk van AccumulateGlobalsAcrossPages of iets anders op JBIG2Options — een bewuste conformiteitskeuze, geen bug, maar het betekent dat een archiveringsprofiel en pagina-overschrijdende symbooldeling elkaar vandaag uitsluiten. Voor welke configuratie u ook kiest, decodeer wat u heeft geschreven voordat u het vertrouwt: laad het bestand terug met LoadFromFile en haal elke pagina door ExtractLoadedImage, die de gedeelde globals voor u oplost op dezelfde manier als elke conforme lezer zou doen, en vergelijk het resultaat met uw bronbitmaps
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;
Pagina-overschrijdende woordenboekdeling raakt alleen de bilevel-afbeeldingskant van een document. Als dezelfde pijplijn ook gegenereerde tekstpagina's naast de scans uitvoert — voorbladen, indexpagina's, een OCR-tekstlaag — dan pakken objectstromen en xref-stromen de andere helft van het bestandsgroottebudget aan door de documentstructuur die die pagina's toevoegen te comprimeren. Pagina-overschrijdende JBIG2-globals maakt deel uit van de HotPDF-component voor Delphi en C++Builder, naast de per-afbeelding JBIG2-opties en de rest van de compressiepijplijn