PDFlibPas kan koda bilevel-bilder som JBIG2 via två olika backends. Den ena är en inbyggd Object Pascal-MMR-kodare som alltid finns där. Den andra är en extern symbolordlistekodare som ger betydligt mindre utdata på skannad text, och den är valfri: ett projekt måste länka backendenheten för att den ska finnas alls. Den distinktionen är källan till den vanligaste överraskningen med den här funktionen, så den är värd att ange först: DefaultJBIG2EncodeOptions begär den externa kodaren som standard, och när backendenheten inte är länkad faller begäran tyst tillbaka till Pascal-MMR-spåret
På Delphi och C++Builder är den externa backenden en uppsättning förbyggda statiska objekt. På Free Pascal fick den bli en DLL, och vägen till det beslutet är en länkarberättelse som är användbar för alla som har försökt länka C++-objekt i ett Free Pascal-program
Registreringen är kontraktet
Backendenheten registrerar sig själv från sitt initialiseringsavsnitt genom att anropa RegisterJBIG2EncoderBackend. Anropare begär den antingen via alternativbiten PDF_JBIG2_OPTION_EXTERNAL_ENCODER, som har värdet 4, eller via parametern UseExternalEncoder hos de utökade bildingångspunkterna. Bibliotekets paraplyenhet drar medvetet inte in backendenheten, eftersom att bära på en stor objektuppsättning bör vara varje projekts beslut; i C++Builder-trädet inkluderas den till exempel uttryckligen av de projekt som vill ha den
Konsekvensen för anropare är att begära den externa kodaren är en preferens, inte en garanti, och ett bygge som glömmer enheten ger större filer i stället för ett fel. Om utdatastorleken betyder tillräckligt mycket för att be om den bättre kodaren betyder den tillräckligt mycket för att kontrollera att du fick den
uses
PDFlibrary,
{$IFDEF FPC}
PDFlibJBIG2EncDLL; // dynamisk backend för Free Pascal
{$ELSE}
PDFlibJBIG2EncC; // statisk objektuppsättning för 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;
Att kompilera enheten var två rader. Symbolerna var arbetet
Att få backendenheten själv att kompilera under Free Pascal krävde exakt två ändringar: att ställa in assemblerdialekten och att ersätta en postbaserad konstruktor för formatinställningar med den globala standardvariabeln. Det är en rättvis bild av hur portabel okomplicerad Pascal är mellan de två kompilatorerna
Symbolsidan var det verkliga arbetet. Objektuppsättningen refererar 176 C-symboler. Av dem hade 128 redan Pascal-implementeringar inuti enheten och behövde bara exportnamn knutna till sig, eftersom Delphi använder funktionsnamnet som symbolnamn medan Free Pascal kräver en uttrycklig publik namndeklaration. Tjugosju delades med JPEG 2000-codecen och fick exporteras från exakt ett ställe, eftersom att definiera dem två gånger bryter alla program som länkar båda. De återstående 21 var plattforms- och C-runtime-poster, sexton Win32-filfunktioner plus ett antal standardbiblioteksanrop, och de gick in i en ny kompatibilitetsenhet
Inget av detta är konceptuellt svårt, och allt är nödvändigt innan länkaren ens försöker. Länkaren var där det stannade
Tre länkningsvägar, tre återvändsgränder
Den interna Free Pascal-länkaren kan inte läsa objektfilerna, eftersom de producerades av en kompilator som avger associerande COMDAT-sektioner och den interna länkaren rapporterar att den inte stöder dem. Det är ett blankt avslag, inte en varning
Att byta till en extern länkare såg ut som svaret. Binutils-länkaren som medföljer Free Pascal kraschar blankt när den tillämpar section garbage collection på detta arkiv, och den flaggan är en del av den fasta parameteruppsättning Free Pascal skickar för 64-bit Windows-målet, så den kan inte tas bort från kommandoraden; de dokumenterade omkopplarna för att undertrycka den ignoreras på den vägen. Att i stället tillhandahålla en mycket nyare binutils misslyckas annorlunda: den kan inte bearbeta Free Pascal-länkskriptet alls, vilket ger en tom utdata utan skriptet och en vägg av relocation-fel med det
En gräns som upptäcktes på vägen är värd att känna till även om du aldrig stöter på länkarproblemet. Den externa länkaren löser objektfilsvägar relativt den körbara filens utdatakatalog i stället för källträdet, så ett relativt include-object-direktiv fungerar bara när utdatakatalogen råkar vara densamma som arbetskatalogen vid kompileringen. Ett bibliotek kan inte anta det om en konsumentprojekt, vilket i sig är en anledning att föredra ett länkat bibliotek framför lösa objekt
Varför en annan C++-kompilator inte hjälper
Den uppenbara nästa idén är att bygga om C++-sidan med en kompilator vars objekt Free Pascal kan läsa. Det fungerar inte heller, och orsaken är grundläggande snarare än en fråga om omkopplare. En minimal C++-översättningsenhet som innehåller en mall, kompilerad med alla kodgenereringsfunktioner avstängda, avger fortfarande svaga externa symboler, eftersom mall- och inline-instansiering producerar dem genom konstruktion. Free Pascal avvisar den symbolklassen blankt. Motsatt riktning misslyckas också: en mainstream C++-länkare kan inte konsumera objekt från den andra kompilatorn på grund av samma COMDAT-sektionhantering
Så C++-koden kan inte levereras som objekt till Free Pascal via någon tillgänglig väg. Den kan levereras som en DLL, vilket är vad som hände: kodaren och dess bildbehandlingsberoende byggs in i ett bibliotek som exponerar två platta C-ingångspunkter, och Free Pascal-backendenheten binder dem dynamiskt och registrerar sig exakt som den statiska backenden gör. Delphi- och C++Builder-spåret rördes inte alls, vilket är det rätta resultatet; ett portabilitetsproblem på en verktygskedja ska inte störa verktygskedjan som redan fungerade
Polariteten är det enda som kommer bita dig
Mellan en Windows-bilevel-bitmapp och en JBIG2-kodare finns ett konventionsmissmöte som inget typsystem fångar. En scanline i en enhetsoberoende bitmapp med en bit per pixel behandlar en sättad bit som vit. Kodaren behandlar en sättad bit som svart. Lämna över scanlinjerna oförändrade och du får en helt giltig JBIG2-ström av det fotografiska negativet av din sida
// Enbitars DIB: sättad bit betyder vit. JBIG2-kodare: sättad bit
// betyder svart. Invertera varje byte på vägen in
for I := 0 to RowBytes - 1 do
Row[I] := Row[I] xor $FF;
Verifieringsmetoden betyder lika mycket som fixen. Att jämföra komprimerade strömlängder säger ingenting, eftersom en negativ bild komprimerar till en liknande storlek. Att titta på sidan bevisar bara att den inte är uppenbart inverterad. Den tillförlitliga kontrollen är att rendera utdata från båda kodningsspåren, inbyggd Pascal och extern, till PNG och jämföra dem byte för byte: båda kodare är förlustfria på samma källbild, så allt annat än en exakt matchning är en bugg i en av dem. Den jämförelsen är nu ett permanent regressionstest, och det är den sorts assertion som är värd att bygga närhelst två implementeringar förväntas stämma exakt
Vilken backend man ska använda
För allmänt bilevel-innehåll, dithrade halvtoner, linjekonst, blandad grafik, är den inbyggda Pascal-MMR-kodaren tillräcklig och har ingen distributionskostnad. För skannad text, vilket är fallet JBIG2 designades för, är den externa symbolordlistekodaren där storleksminskningen finns, eftersom den faktoriserar upprepade glyfformer i en ordlista i stället för att koda om varje förekomst. Om du producerar arkiv av skannade dokument är den skillnaden stor nog att ändra lagringsplaneringen
Den överordnade frågan, hur bilevel-bilden produceras från början, betyder lika mycket för utdatastorleken; regionbaserad monokrom rendering tas upp i artikeln om monokrom regionsrendering, och dokumentomfattande storleksstrategi i PDF-filstorleksoptimering och font subsetting. För skanningsuppsättningar med upprepade sidor slår deduplicering ofta bättre komprimering, vilket är ämnet för perceptuell bilddeduplicering. Tillgänglighet per plattform för verktygskedjor och backends listas på produktsidan för losLab PDF Developer Library