PDFlibPas može kodovati bilevel slike kao JBIG2 kroz dva različita backend-a. Jedan je nativni Object Pascal MMR enkoder koji je uvek prisutan. Drugi je spoljni enkoder simbol-rečnika koji proizvodi znatno manji izlaz na skeniranom tekstu, i on je opcion: projekat mora povezati backend jedinicu da bi uopšte postojao. Ta razlika je izvor najčešćeg iznenađenja sa ovom funkcijom, pa vredi reći prvo: DefaultJBIG2EncodeOptions traži spoljni enkoder podrazumevano, i kada backend jedinica nije povezana zahtev tiho pada nazad na Pascal MMR put
Na Delphi-ju i C++Builder-u spoljni backend je skup prethodno izgrađenih statičkih objekata. Na Free Pascal-u morao je postati DLL, i put do tog zaključka priča je o linkeru korisna svakome ko je pokušao da poveže C++ objekte u Free Pascal program
Registracija je ugovor
Backend jedinica registruje se iz svog inicijalizacionog odeljka pozivom RegisterJBIG2EncoderBackend. Pozivaoci je traže ili kroz options bit, PDF_JBIG2_OPTION_EXTERNAL_ENCODER, koji ima vrednost 4, ili kroz parametar UseExternalEncoder proširenih ulaznih tačaka za slike. Krovna jedinica biblioteke namerno ne ubacuje backend jedinicu, jer nošenje velikog skupa objekata treba da bude odluka svakog projekta; u C++Builder stablu, na primer, ona je uključena eksplicitno od strane projekata koji je žele
Posledica za pozivaoce je da je traženje spoljnog enkodera preferenca, ne garancija, i build koji zaboravi jedinicu proizvodi veće datoteke umesto greške. Ako veličina izlaza vredi dovoljno da tražite bolji enkoder, vredi dovoljno da proverite da li ste ga dobili
uses
PDFlibrary,
{$IFDEF FPC}
PDFlibJBIG2EncDLL; // dinamički backend za Free Pascal
{$ELSE}
PDFlibJBIG2EncC; // statički skup objekata za 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;
Kompajliranje jedinice bilo je dve linije. Simboli su bili posao
Da se backend jedinica sama kompajlira pod Free Pascal-om trebalo je tačno dve izmene: postavljanje asemblerskog dijalekta, i zamena konstruktora podešavanja formata zasnovanog na zapisu globalnom podrazumevanom promenljivom. To je pravedan odraz koliko je direktan Pascal prenosiv između dva kompajlera
Simbolska strana bio je pravi posao. Skup objekata referencira 176 C simbola. Od njih, 128 već je imalo Pascal implementacije unutar jedinice i trebali su im samo priloženi izvozni nazivi, jer Delphi koristi ime funkcije kao ime simbola dok Free Pascal traži eksplicitnu deklaraciju javnog naziva. Dvadeset sedam bilo je deljeno sa JPEG 2000 kodekom i moralo se izvesti iz tačno jednog mesta, jer dvostruka definicija lomi svaki program koji povezuje oba. Preostalih 21 bili su platformski i C runtime unosi, šesnaest Win32 funkcija datoteka plus nekoliko poziva standardne biblioteke, i otišli su u novu kompatibilnu jedinicu
Ništa od toga konceptualno nije teško, i sve od toga neophodno je pre nego što linker uopšte pokuša. Linker je mesto gde je stalo
Tri rute povezivanja, tri ćorsokaka
Interni Free Pascal linker ne može čitati objektne datoteke, jer su proizvedene kompajlerom koji emituje asocijativne COMDAT sekcije, a interni linker izveštava da ih ne podržava. To je ravno odbijanje, ne upozorenje
Prelazak na spoljni linker delovao je kao odgovor. binutils linker isporučen sa Free Pascal-om pada odmah primenjujući sakupljanje smeća sekcija na ovaj arhiv, i ta zastavica deo je fiksnog skupa parametara koje Free Pascal prosleđuje za 64-bitni Windows cilj, pa se ne može ukloniti iz komandne linije; dokumentovane prekidače za njeno potiskivanje ovaj put ignoriše. Davanje mnogo novijeg binutils-a umesto toga otkazuje drugačije: ne može uopšte obraditi Free Pascal skriptu za povezivanje, proizvodeći prazan izlaz bez skripte i zid grešaka relokacije sa njom
Granica otkrivena usput vredi znati čak i ako nikad ne nađete linker problem. Spoljni linker razrešava putanje objektnih datoteka u odnosu na izlazni direktorijum izvršne datoteke, a ne u odnosu na stablo izvora, pa relativna include-object direktiva radi samo kada se izlazni direktorijum slučajno poklapa sa radnim direktorijumom u vreme kompajliranja. Biblioteka ne može to pretpostaviti o projektu potrošača, što je samo po sebi razlog da se preferira povezana biblioteka nad rasutim objektima
Zašto drugačiji C++ kompajler ne pomaže
Očigledna sledeća ideja je ponovna izgradnja C++ strane kompajlerom čije objekte Free Pascal može čitati. Ni to ne radi, i razlog je fundamentalan pre nego pitanje prekidača. Minimalna C++ prevodna jedinica koja sadrži šablon, kompajlirana sa svakom funkcijom generisanja koda isključenom, i dalje emituje slabe eksterne simbole, jer instanciranje šablona i inline-a proizvodi ih po konstrukciji. Free Pascal odbacuje tu klasu simbola odmah. Obrnuti smer takođe otkazuje: mainstream C++ linker ne može konzumirati objekte drugog kompajlera zbog istog rukovanja COMDAT sekcijama
C++ kod dakle ne može biti isporučen kao objekti Free Pascal-u ni jednom dostupnom rutom. Može biti isporučen kao DLL, što se i desilo: enkoder i njegova zavisnost obrade slike izgrađeni su u jednu biblioteku koja izlaže dva ravna C ulaza, i Free Pascal backend jedinica ih vezuje dinamički i registruje se tačno kao što radi statički backend. Delphi i C++Builder put uopšte nije diran, što je ispravan ishod; problem prenosivosti na jednom toolchain-u ne treba da poremeti toolchain koji je već radio
Polaritet je ona stvar koja će vas ujesti
Između Windows bilevel bitmape i JBIG2 enkodera postoji neslaganje konvencija koje nijedan sistem tipova neće uhvatiti. Scanline od jednog bita po pikselu device-nezavisne bitmape tretira postavljeni bit kao belo. Enkoder tretira postavljeni bit kao crno. Predajte scanline-ove nepromenjene i dobićete sasvim valjan JBIG2 tok fotografskog negativa vaše stranice
// Jednobitni DIB: postavljeni bit znači belo. JBIG2 enkoder:
// postavljeni bit znači crno. Invertujte svaki bajt pri unosu
for I := 0 to RowBytes - 1 do
Row[I] := Row[I] xor $FF;
Metoda verifikacije vredi koliko i popravka. Poređenje dužina komprimovanih tokova vam ništa ne govori, jer negativ slike komprimuje se u sličnu veličinu. Gledanje stranice dokazuje samo da nije očigledno invertovana. Pouzdana provera je renderovati izlaz oba puta kodovanja, nativni Pascal i spoljni, u PNG i uporediti ih bajt po bajt: oba enkodera bezgubitni su na istoj izvornoj slici, pa je sve osim tačnog poklapanja greška u jednom od njih. To poređenje sada je trajan regresioni test, i to je vrsta tvrđenja vredna izgradnje kad god dve implementacije treba da se poklope tačno
Koji backend koristiti
Za opšti bilevel sadržaj, ditherovane polutonske, linijsku grafiku, mešovitu grafiku, nativni Pascal MMR enkoder je adekvatan i nema trošak raspoređivanja. Za skenirani tekst, koji je slučaj za koji je JBIG2 projektovan, spoljni enkoder simbol-rečnika je mesto gde živi smanjenje veličine, jer razlaže ponovljene oblike glifova u rečnik umesto da ponovo koduje svaku pojavu. Ako proizvodite arhive skeniranih dokumenata, ta razlika dovoljno je velika da izmeni planiranje skladišta
Uzvodno pitanje, kako se bilevel slika uopšte proizvodi, jednako je važno za veličinu izlaza; monohromno renderovanje po regionima pokriveno je u članku o monohromnom renderovanju regiona, a strategija veličine celog dokumenta u optimizaciji veličine PDF datoteke i podskupovima fontova. Za skenirane skupove sa ponovljenim stranicama, deduplikacija često pobeđuje bolju kompresiju, čemu je tema perceptualna deduplikacija slika. Dostupnost toolchain-a i backend-a po platformi navedena je na stranici proizvoda losLab PDF Developer Library