Műszaki cikk

JBIG2 kódoló backendek és a Free Pascal linker

A PDFlibPas kéttónusú képeket két különböző backenden keresztül kódolhatja JBIG2-be. Az egyik egy natív Object Pascal MMR kódoló, amely mindig jelen van. A másik egy külső szimbólumszótár-kódoló, amely a beolvasott szövegen lényegesen kisebb kimenetet állít elő, és ez opcionális: egy projektnek a backend egységet kell linkelnie, hogy egyáltalán létezzen. Ez a megkülönböztetés e funkció leggyakoribb meglepetésének forrása, ezért érdemes először kimondani: a DefaultJBIG2EncodeOptions alapértelmezés szerint a külső kódolót kéri, és ha a backend egység nincs linkelve, a kérés csendben a Pascal MMR útra esik vissza

Delphi és C++Builder alatt a külső backend előre lefordított statikus objektumok halmaza. Free Pascal alatt DLL-lé kellett válnia, és az út erre a következtetésre linker-történet, amely hasznos bárkinek, aki megpróbált C++ objektumokat linkelni Free Pascal programba

A regisztráció a szerződés

A backend egység az inicializációs szakaszában regisztrálja magát a RegisterJBIG2EncoderBackend hívásával. A hívók az opciós biten keresztül kérik, a PDF_JBIG2_OPTION_EXTERNAL_ENCODER jelzőn, amelynek értéke 4, vagy a kibővített képbemeneti pontok UseExternalEncoder paraméterén keresztül. A könyvtár esernyőegysége szándékosan nem húzza be a backend egységet, mert egy nagy objektumkészlet cipelése minden projekt saját döntése kell legyen; a C++Builder fában például azok a projektek foglalják be explicit módon, amelyek igénylik

A hívók számára a következmény az, hogy a külső kódoló kérése preferencia, nem garancia, és az egységet elfelejtő build hiba helyett nagyobb fájlokat állít elő. Ha a kimenet mérete elég fontos ahhoz, hogy a jobb kódolót kérje, elég fontos ahhoz is, hogy ellenőrizze, megkapta-e

PDFlibPas JBIG2 kódolási kérés folyamata: a külső kódoló preferenciája a backend egység nélkül csendben a natív Pascal MMR útra esik vissza
A külső szimbólumszótár-kódoló kérése preferencia: linkelve a kimenet zsugorodik; nem linkelve a Pascal MMR út csendben fut nagyobb fájlokkal
uses
  PDFlibrary,
{$IFDEF FPC}
  PDFlibJBIG2EncDLL;    // dinamikus backend Free Pascalhoz
{$ELSE}
  PDFlibJBIG2EncC;      // statikus objektumkészlet Delphihez / C++Builderhez
{$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;

Az egység lefordítása két sor volt. A szimbólumok jelentették a munkát

A backend egység Free Pascal alá való lefordítása pontosan két változtatást vett igénybe: az assemblerdialektus beállítását és egy rekordalapú formátumbeállítás-konstruktor cseréjét a globális alapértelmezett változóra. Ez korrekt tükrözi, mennyire hordozható az egyenes vonalú Pascal a két fordító között

A szimbólumoldal volt az igazi munka. Az objektumkészlet 176 C szimbólumra hivatkozik. Ebből 128-nak már volt Pascal-implementációja az egységen belül, és csak exportneveket kellett hozzácsatolni, mert a Delphi a függvénynevet használja szimbólumnévként, míg a Free Pascal explicit publikus névdeklarációt követel. Huszonhét a JPEG 2000 kodekkel osztozott, és pontosan egy helyről kellett exportálni, mert kétszeres definíciójuk megtör minden programot, amely mindkettőt linkeli. A maradék 21 platform- és C futásidejű bejegyzés volt, tizenhat Win32 fájlfüggvény plusz néhány standard függvénytári hívás, és ezek egy új kompatibilitási egységbe kerültek

Egyik sem koncepciójában nehéz, és mindegyik szükséges, mielőtt a linker egyáltalán megpróbálkozna. A linker volt az, ahol megállt

Három linkelési útvonal, három zsákutca

A Free Pascal belső linkere nem tudja olvasni az objektumfájlokat, mert azokat egy olyan fordító állította elő, amely asszociatív COMDAT szekciókat bocsát ki, és a belső linker arról számol be, hogy nem támogatja őket. Ez lapos elutasítás, nem figyelmeztetés

Külső linkerre váltás válasznak látszott. A Free Pascallel érkező binutils linker nyíltan összeomlik, amikor szekció-szemétszedést alkalmaz erre az archívumra, és ez a jelző a Free Pascal 64 bites Windows célhoz átadott rögzített paraméterkészletének része, tehát nem távolítható el a parancssorból; az elnyomására dokumentált kapcsolók ezen az úton figyelmen kívül maradnak. Egy sokkal újabb binutils szolgáltatása egészen másképp bukik meg: egyáltalán nem tudja feldolgozni a Free Pascal linkszkriptjét, szkript nélkül üres kimenetet ad, szkripttel pedig relokációs hibák falát

Egy útközben felfedezett határvonal akkor is tudásra érdemes, ha soha nem ütközik a linkerproblémába. A külső linker az objektumfájl-útvonalakat a végrehajtható fájl kimeneti könyvtárához képest oldja fel, nem a forrásfához, így egy relatív include-object direktíva csak akkor működik, amikor a kimeneti könyvtár éppen egyezik a fordítási munkakönyvtárral. Egy könyvtár ezt nem feltételezheti egy fogyasztó projektről, ami önmagában ok arra, hogy lazán objektumok helyett linkelt függvénytárat részesítsen előnyben

Három megbukott linker-útvonal a C++ JBIG2 kódoló objektumokhoz Free Pascal alatt, valamint a két lapos C belépési pontot kitettő DLL, amely megoldotta őket
A COMDAT szekciók legyőzik a belső linkert, és mindkét külső linker megbukik, így a C++ kódoló egyetlen DLL-ként érkezik, amelyet a backend egység dinamikusan köt be

Miért nem segít egy másik C++ fordító

A kézenfekvő következő ötlet a C++ oldal újbóli lefordítása egy olyan fordítóval, amelynek objektumait a Free Pascal olvasni tudja. Ez sem működik, és az ok fundamentális, nem kapcsolókérdés. Egy sablont tartalmazó minimális C++ fordítási egység, minden kódgenerációs szolgáltatás kikapcsolásával lefordítva, gyenge külső szimbólumokat is kibocsát, mert a sablon- és inline-példányosítás konstrukcióból fakadóan állítja elő őket. A Free Pascal ezt a szimbólumosztályt laposan elutasítja. A fordított irány is megbukik: egy főáramú C++ linker a másik fordító objektumait nem tudja fogyasztani ugyanezen COMDAT szekciókezelés miatt

Tehát a C++ kód nem szállítható objektumként a Free Pascalnak egyetlen elérhető úton sem. DLL-ként szállítható, és ez történt: a kódoló és a képfeldolgozási függősége egyetlen függvénytárba épült, amely két lapos C belépési pontot tesz ki, és a Free Pascal backend egység ezeket dinamikusan köti be, majd pontosan úgy regisztrálja magát, ahogyan a statikus backend teszi. A Delphi és C++Builder útvonalat egyáltalán nem érintették, ami a helyes kimenet; egy eszközlánc hordozhatósági problémája nem zavarhatja meg azt az eszközláncot, amely már működött

A polaritás az egyetlen dolog, amely csíphet

Egy Windows kéttónusú bittérkép és egy JBIG2 kódoló között konvencióeltérés van, amelyet egyetlen típusrendszer sem fog el. Egy bitenként-egypixeles eszközfüggetlen bittérkép pásztázásvonala a beállított bitet fehérként kezeli. A kódoló a beállított bitet feketeként kezeli. Adja át a pásztázásvonalakat változatlanul, és egy tökéletesen érvényes JBIG2 folyamot kap, amely az oldalának fényképészeti negatívja

Egybites DIB és JBIG2 polaritási konvenció: a beállított bit fehér a pásztázásvonalon és fekete a kódolóban, javítva minden bájt inverziójával
Ugyanazok a bájtok, ellenkező jelentés: bájtonkénti inverzió nélkül a kódoló érvényes JBIG2 folyamot állít elő a fényképészeti negatívról
// Egybites DIB: a beállított bit fehéret jelent. JBIG2 kódoló:
// a beállított bit feketét jelent. Minden bájt inverziója útközben
for I := 0 to RowBytes - 1 do
  Row[I] := Row[I] xor $FF;

Az ellenőrzési módszer ugyanannyit számít, mint a javítás. A tömörített folyamok hosszának összehasonlítása semmit nem mond, mert egy negatív kép hasonló méretre tömörödik. Az oldal megnézése csak azt bizonyítja, hogy nem nyilvánvalóan invertált. A megbízható ellenőrzés az, hogy mindkét kódolási út — a natív Pascal és a külső — kimenetét PNG-be rendereli, és bájtonként összehasonlítja: mindkét kódoló veszteségmentes ugyanazon forráskép esetén, tehát bármi más, mint a tökéletes egyezés, az egyikben lévő hiba. Ez az összehasonlítás mostantól állandó regressziós teszt, és az az állításfajta, amelyet érdemes felépíteni, akárhányszor két implementációnak pontosan kell egyeznie

Melyik backendet használja

Általános kéttónusú tartalomhoz, ditherelt rácsos árnyalatokhoz, vonalas grafikákhoz, vegyes grafikákhoz a natív Pascal MMR kódoló elegendő, és nincs telepítési költsége. Beolvasott szöveghez, amelyre a JBIG2-t tervezték, a külső szimbólumszótár-kódoló az, ahol a méretcsökkentés lakik, mert az ismétlődő glífaalakokat szótárba helyezi ki minden egyes előfordulás újrakódolása helyett. Ha beolvasott dokumentumok archívumait állítja elő, ez a különbség elég nagy ahhoz, hogy megváltoztassa a tárolástervezést

A felsőbb szintű kérdés, hogyan állítják elő egyáltalán a kéttónusú képet, ugyanannyira számít a kimenet méretére; a régión alapuló monokróm renderelést a monokróm régiórenderelési cikk tárgyalja, a dokumentumszintű méretstratégiát pedig a PDF fájlméret-optimalizálás és betűtípus-szubszettelés. Ismétlődő oldalakkal rendelkező szkenneléskészleteknél a deduplikáció gyakran legyőzi a jobb tömörítést, amellyel a perceptuális képdeduplikáció foglalkozik. Az eszközlánc és a backend elérhetősége platformonként a losLab PDF Developer Library terméklapon található