PDFlibPas dokáže kódovať dvojúrovňové obrázky ako JBIG2 cez dva rozdielne backendy. Jeden je natívny MMR kódovač v Object Pascale, ktorý je prítomný vždy. Druhý je externý kódovač so slovníkom symbolov, ktorý na naskenovanom texte produkuje podstatne menší výstup, a je voliteľný: projekt musí zlinkovať jednotku backendu, aby vôbec existoval. Toto rozlíšenie je zdrojom najčastejšieho prekvapenia pri tejto funkcii, takže stojí za to povedať ho na začiatku: DefaultJBIG2EncodeOptions vyžaduje externý kódovač predvolene a keď jednotka backendu nie je zlinkovaná, požiadavka potichu prepadne na pascaleskú cestu MMR
V Delphi a C++Builder je externý backend sada predpripravených statických objektov. Vo Free Pascale sa z neho musela stať DLL a cesta k tomuto záveru je linkerový príbeh, ktorý je užitočný pre každého, kto sa pokúsil linkovať C++ objekty do programu vo Free Pascale
Registrácia je kontrakt
Jednotka backendu sa registruje zo svojej inicializačnej sekcie volaním RegisterJBIG2EncoderBackend. Volajúci si ju vyžiadajú buď cez bit v options, PDF_JBIG2_OPTION_EXTERNAL_ENCODER s hodnotou 4, alebo cez parameter UseExternalEncoder rozšírených vstupných bodov pre obrázky. Knižničný umbrella jednotku backendu zámerne nepriťahuje, pretože nosiť veľkú sadu objektov by mala byť rozhodnutie každého projektu; v strome C++Builder ju napríklad výslovne zaraďujú projekty, ktoré ju chcú
Dôsledok pre volajúcich je, že požiadavka na externý kódovač je preferencia, nie záruka, a build, ktorý na jednotku zabudne, produkuje väčšie súbory namiesto chyby. Ak veľkosť výstupu stojí za to, aby ste o lepší kódovač požiadali, stojí za to aj skontrolovať, že ste ho dostali
uses
PDFlibrary,
{$IFDEF FPC}
PDFlibJBIG2EncDLL; // dynamický backend pre Free Pascal
{$ELSE}
PDFlibJBIG2EncC; // statická sada objektov pre Delphi / C++Builder
{$ENDIF}
var
Pdf: TPDFlib;
ImageId: Integer;
begin
Pdf := TPDFlib.Create(nil);
try
Pdf.NewDocument;
Pdf.NewPage;
// Interpolate, SymbolExtract, UseExternalEncoder, SkipBlackDots,
// BlackDotSize, LossyLevel
ImageId := Pdf.AddImageJBIG2FromFileEx('scan-page-1.tif',
0, 1, 1, 0, 0, 0);
if ImageId = 0 then
raise Exception.Create('JBIG2 encoding failed');
Pdf.SaveToFile('archive.pdf');
finally
Pdf.Free;
end;
end;
Kompilácia jednotky stála dve zmeny. Prácou boli symboly
Aby sa jednotka backendu sama skompilovala pod Free Pascalom, stálo to presne dve zmeny: nastavenie assembler dialektu a nahradenie konštruktora nastavení formátu založeného na zázname globálnou predvolenou premennou. To je spravodlivý odraz toho, ako prenositeľný je priamočiary Pascal medzi oboma kompilátormi
Symbolová strana bola tá skutočná práca. Sada objektov odkazuje na 176 C symbolov. Z nich 128 už malo pascaleské implementácie vnútri jednotky a potrebovalo len priradené exportné názvy, pretože Delphi používa názov funkcie ako názov symbolu, zatiaľ čo Free Pascal vyžaduje explicitnú deklaráciu public názvu. Dvadsaťsedem zdieľal kodek JPEG 2000 a museli byť exportované presne z jedného miesta, keďže dvojnásobná definícia rozbije každý program, ktorý linkuje oboje. Zostávajúcich 21 boli platformové a C runtime vstupy, šestnásť Win32 funkcií pre súbory plus hrsť volaní štandardnej knižnice, a tie skončili v novej jednotke kompatibility
Nič z toho nie je koncepčne ťažké a všetko je nevyhnutné skôr, než sa linker vôbec pokúsi. Na linkeru to zastavilo
Tri linkovacie cesty, tri slepé uličky
Interný linker Free Pascalu nevie čítať objektové súbory, pretože ich vyrobil kompilátor emitujúci asociatívne sekcie COMDAT a interný linker hlási, že ich nepodporuje. To je rovné odmietnutie, nie varovanie
Prepnutie na externý linker vyzeralo ako odpoveď. Linker binutils dodávaný s Free Pascalom páde úplne počas aplikovania section garbage collection na tento archív a tento príznak je súčasťou pevnej sady parametrov, ktorú Free Pascal odovzdáva pre cieľ 64-bit Windows, takže ho nemožno z príkazového riadku odobrať; zdokumentované prepínače na jeho potlačenie sa na tejto ceste ignorujú. Dodanie oveľa novšieho binutils zlyhá inak: nedokáže spracovať link skript Free Pascalu vôbec, pričom bez skriptu produkuje prázdny výstup a so skriptom stenu relokačných chýb
Hranica odhalená počas toho stojí za poznanie, aj keď na linkerový problém nikdy nenarazíte. Externý linker rozpoznáva cesty k objektovým súborom relatívne k výstupnému adresáru spustiteľného súboru, nie k stromu zdrojov, takže relatívna direktíva include-object funguje len vtedy, keď výstupný adresár náhodou zodpovedá pracovnému adresáru pri kompilácii. Knižnica to nemôže o projekte spotrebiteľa predpokladať, čo je samo o sebe dôvod preferovať zlinkovanú knižnicu pred voľnými objektmi
Prečo iný C++ kompilátor nepomôže
Zjavný ďalší nápad je zostaviť C++ stranu kompilátorom, ktorého objekty Free Pascal vie čítať. Ani to nefunguje a dôvod je fundamentálny, nie otázka prepínačov. Minimálna C++ prekladová jednotka obsahujúca template, skompilovaná so všetkými funkciami generovania kódu vypnutými, stále emituje slabé externé symboly, pretože inštancionalizácia template a inline ich produkuje už konštrukciou. Free Pascal túto triedu symbolov odmietá úplne. Opačný smer zlyháva tiež: hlavný prúd C++ linkerov nedokáže skonzumovať objekty od druhého kompilátora kvôli rovnakému zaobchádzaniu so sekciami COMDAT
C++ kód teda nemôže byť dodaný Free Pascalu ako objekty žiadnou dostupnou cestou. Dodaný byť môže ako DLL, a to sa aj stalo: kódovač a jeho závislosť na spracovaní obrazu sú zabudované do jednej knižnice vystavujúcej dva ploché C vstupné body a jednotka backendu vo Free Pascale ich viaže dynamicky a registruje sa presne tak, ako to robí statický backend. Cesta Delphi a C++Builder nebola dotknutá vôbec, čo je správny výsledok; problém prenositeľnosti na jednom toolchainu by nemal narušiť toolchain, ktorý už fungoval
Polarita je tá jediná vec, ktorá vás skousne
Medzi Windows dvojúrovňovou bitmapou a JBIG2 kódovačom je nezhoda konvencií, ktorú nezachytí žiadny typový systém. Scanline bitmapy s jedným bitom na pixel považuje nastavený bit za biely. Kódovač považuje nastavený bit za čierny. Odovzdajte scanlines nezmenené a dostanete úplne platný JBIG2 stream fotografického negatívu vašej stránky
// Jednobitová DIB: nastavený bit znamená biely. JBIG2 kódovač:
// nastavený bit znamená čierny. Invertujte každý bajt na vstupe
for I := 0 to RowBytes - 1 do
Row[I] := Row[I] xor $FF;
Metóda overenia je dôležitá rovnako ako oprava. Porovnávanie dĺžok komprimovaných streamov vám nič nepovie, pretože negatívny obraz sa skomprimuje na podobnú veľkosť. Pohľad na stránku dokáže len to, že nie je zjavne invertovaná. Spoľahlivá kontrola je vyrenderovať výstup oboch kódovacích ciest, natívny Pascal aj externý, do PNG a porovnať ich bajt po bajte: oba kódovače sú bezstratové na tom istom zdrojovom obraze, takže čokoľvek iné než presná zhoda je chyba v jednom z nich. Toto porovnanie je teraz trvalý regresný test a je to druh asercie, ktorý stojí za vybudovanie vždy, keď majú dve implementácie súhlasiť presne
Ktorý backend použiť
Pre všeobecný dvojúrovňový obsah, ditherované poltóny, line art a zmiešanú grafiku je natívny pascaleský MMR kódovač postačujúci a nemá žiadne náklady na nasadenie. Pre naskenovaný text, čo je prípad, pre ktorý bol JBIG2 navrhnutý, je miestom zmenšenia veľkosti externý kódovač so slovníkom symbolov, pretože opakujúce sa tvary glyfov faktoruje do slovníka namiesto opätovného kódovania každého výskytu. Ak produkujete archívy naskenovaných dokumentov, ten rozdiel je dostatočne veľký na to, aby zmenil plánovanie úložiska
Otázka upstream, ako je dvojúrovňový obraz v prvom rade vyrobený, je pre veľkosť výstupu rovnako dôležitá; renderovanie monochromatických regiónov pokrýva článok o renderovaní monochromatických regiónov a stratégia veľkosti na úrovni dokumentu optimalizáciu veľkosti PDF a subsetovanie fontov. Pre sady skenov s opakujúcimi sa stranami často deduplikácia zvíťazí nad lepšou komprimáciou, čo je téma perceptuálnej deduplikácie obrazu. Dostupnosť toolchainov a backendov podľa platformy je uvedená na produktovej stránke losLab PDF Developer Library