Το PDFlibPas μπορεί να κωδικοποιεί bilevel εικόνες ως JBIG2 μέσω δύο διαφορετικών backends. Το ένα είναι ένας εγγενής encoder MMR σε Object Pascal που είναι πάντα παρών. Το άλλο είναι ένας εξωτερικός encoder symbol-dictionary που παράγει ουσιαστικά μικρότερη έξοδο σε σαρωμένο κείμενο, και είναι προαιρετικός: ένα project πρέπει να συνδέει τη μονάδα backend για να υπάρχει καθόλου. Η διάκριση αυτή είναι η πηγή της πιο συνηθισμένης έκπληξης με αυτή τη λειτουργία, οπότε αξίζει να δηλωθεί πρώτα: το DefaultJBIG2EncodeOptions ζητά τον εξωτερικό encoder από προεπιλογή, και όταν η μονάδα backend δεν είναι συνδεδεμένη το αίτημα πέφτει σιωπηλά στη διαδρομή MMR της Pascal
Σε Delphi και C++Builder το εξωτερικό backend είναι ένα σύνολο προκατασκευασμένων static objects. Σε Free Pascal έπρεπε να γίνει DLL, και ο δρόμος προς αυτό το συμπέρασμα είναι μια ιστορία linker χρήσιμη σε όποιον έχει προσπαθήσει να συνδέσει C++ objects σε πρόγραμμα Free Pascal
Η εγγραφή είναι το συμβόλαιο
Η μονάδα backend εγγράφεται η ίδια από την ενότητα αρχικοποίησής της καλώντας την RegisterJBIG2EncoderBackend. Οι καλούντες τη ζητούν είτε μέσω του bit επιλογών, PDF_JBIG2_OPTION_EXTERNAL_ENCODER, που έχει τιμή 4, είτε μέσω της παραμέτρου UseExternalEncoder των εκτεταμένων σημείων εισόδου εικόνας. Η ομπρέλα της βιβλιοθήκης δεν τραβάει σκόπιμα τη μονάδα backend, γιατί η μεταφορά ενός μεγάλου συνόλου objects πρέπει να είναι απόφαση κάθε project· στο δέντρο C++Builder, για παράδειγμα, συμπεριλαμβάνεται ρητά από τα projects που τη θέλουν
Η συνέπεια για τους καλούντες είναι ότι το αίτημα για τον εξωτερικό encoder είναι προτίμηση, όχι εγγύηση, και ένα build που ξεχνά τη μονάδα παράγει μεγαλύτερα αρχεία αντί για σφάλμα. Αν το μέγεθος εξόδου έχει αρκετή σημασία ώστε να ζητείται ο καλύτερος encoder, έχει αρκετή σημασία ώστε να ελέγχετε ότι τον λάβατε
uses
PDFlibrary,
{$IFDEF FPC}
PDFlibJBIG2EncDLL; // δυναμικό backend για Free Pascal
{$ELSE}
PDFlibJBIG2EncC; // στατικό σύνολο objects για 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;
Η μεταγλώττιση της μονάδας ήταν δύο γραμμές. Τα σύμβολα ήταν η δουλειά
Το να μεταγλωττιστεί η ίδια η μονάδα backend υπό Free Pascal πήρε ακριβώς δύο αλλαγές: τον ορισμό της διαλέκτου assembler, και την αντικατάσταση ενός constructor ρυθμίσεων μορφοποίησης βασισμένου σε record με τη γενική μεταβλητή προεπιλογής. Αυτό είναι δίκαιη ανταπόκριση του πόσο μεταφέρσιμος είναι ο απλός Pascal ανάμεσα στους δύο μεταγλωττιστές
Η πλευρά των συμβόλων ήταν η πραγματική δουλειά. Το σύνολο objects αναφέρεται σε 176 C σύμβολα. Από αυτά, τα 128 είχαν ήδη υλοποιήσεις Pascal μέσα στη μονάδα και χρειαζόντουσαν μόνο ονόματα εξαγωγής, γιατί το Delphi χρησιμοποιεί το όνομα της συνάρτησης ως όνομα συμβόλου ενώ το Free Pascal απαιτεί ρητή δήλωση public name. Είκοσι επτά μοιράζονταν με τον codec JPEG 2000 και έπρεπε να εξαχθούν από ακριβώς ένα σημείο, αφού ο διπλός ορισμός τους χαλάει οποιοδήποτε πρόγραμμα συνδέει και τα δύο. Τα υπόλοιπα 21 ήταν καταχωρήσεις πλατφόρμας και C runtime, δεκαέξι συναρτήσεις αρχείων Win32 συν μια χούφτα κλήσεις τυπικής βιβλιοθήκης, και πήγαν σε μια νέα μονάδα συμβατότητας
Κανένα από αυτά δεν είναι εννοιολογικά δύσκολο, και όλα είναι απαραίτητα πριν ο linker προσπαθήσει καν. Στον linker σταμάτησε
Τρεις διαδρομές σύνδεσης, τρία αδιέξοδα
Ο εσωτερικός linker του Free Pascal δεν μπορεί να διαβάσει τα object αρχεία, γιατί παράχθηκαν από μεταγλωττιστή που εκπέμπει associative COMDAT ενότητες και ο εσωτερικός linker αναφέρει ότι δεν τις υποστηρίζει. Είναι μια επίπεδη άρνηση, όχι προειδοποίηση
Η μετάβαση σε εξωτερικό linker έμοιαζε με την απάντηση. Ο linker binutils που συνοδεύει το Free Pascal καταρρέει ευθέως ενώ εφαρμόζει section garbage collection σε αυτό το archive, και η σημαία αυτή είναι μέρος του σταθερού συνόλου παραμέτρων που περνά το Free Pascal για τον στόχο 64-bit Windows, οπότε δεν μπορεί να αφαιρεθεί από τη γραμμή εντολών· οι τεκμηριωμένοι διακόπτες για την καταστολή της αγνοούνται σε αυτή τη διαδρομή. Η προμήθεια πολύ νεότερου binutils αποτυγχάνει διαφορετικά: δεν μπορεί να επεξεργαστεί καθόλου το link script του Free Pascal, παράγοντας κενή έξοδο χωρίς το script και έναν τοίχο από σφάλματα relocation με αυτό
Ένα όριο που ανακαλύφθηκε στην πορεία αξίζει να το ξέρετε ακόμα κι αν δεν συναντήσετε ποτέ το πρόβλημα του linker. Ο εξωτερικός linker επιλύει τις διαδρομές object αρχείων σε σχέση με τον κατάλογο εξόδου του εκτελέσιμου και όχι με το δέντρο πηγών, οπότε μια σχετική οδηγία include-object λειτουργεί μόνο όταν ο κατάλογος εξόδου τυχαίνει να ισούται με τον κατάλογο εργασίας μεταγλώττισης. Μια βιβλιοθήκη δεν μπορεί να το υποθέσει αυτό για το project ενός καταναλωτή, που από μόνο του είναι λόγος προτίμησης μιας συνδεδεμένης βιβλιοθήκης έναντι χαλαρών objects
Γιατί ένας άλλος μεταγλωττιστής C++ δεν βοηθά
Η προφανής επόμενη ιδέα είναι να ξαναχτιστεί η πλευρά C++ με μεταγλωττιστή των οποίων τα objects μπορεί να διαβάσει το Free Pascal. Δεν λειτουργεί ούτε αυτό, και ο λόγος είναι θεμελιώδης και όχι θέμα διακοπτών. Μια ελάχιστη μονάδα μετάφρασης C++ που περιέχει template, μεταγλωττισμένη με κάθε δυνατότητα παραγωγής κώδικα ανενεργή, εκπέμπει ούτως ή άλλως weak external σύμβολα, γιατί η instantiation template και inline τα παράγει εκ κατασκευής. Το Free Pascal απορρίπτει κατηγορηματικά αυτή την κατηγορία συμβόλων. Η αντίστροφη κατεύθυνση αποτυγχάνει επίσης: ένας mainstream linker C++ δεν μπορεί να καταναλώσει objects από τον άλλο μεταγλωττιστή λόγω του ίδιου χειρισμού COMDAT ενοτήτων
Έτσι ο κώδικας C++ δεν μπορεί να παραδοθεί ως objects στο Free Pascal με καμία διαθέσιμη διαδρομή. Μπορεί να παραδοθεί ως DLL, που είναι αυτό που έγινε: ο encoder και η εξάρτησή του επεξεργασίας εικόνας χτίστηκαν σε μία βιβλιοθήκη που εκθέτει δύο επίπεδες εισόδους C, και η μονάδα backend του Free Pascal τις δεσμεύει δυναμικά και εγγράφεται ακριβώς όπως κάνει το static backend. Η διαδρομή Delphi και C++Builder δεν αγίχτηκε καθόλου, που είναι το σωστό αποτέλεσμα· ένα πρόβλημα φορητότητας σε ένα toolchain δεν πρέπει να διαταράξει το toolchain που δούλευε ήδη
Η πολικότητα είναι το ένα πράγμα που θα σας δαγκώσει
Ανάμεσα σε ένα bilevel bitmap των Windows και έναν encoder JBIG2 υπάρχει ασυμφωνία συμβάσεων που κανένα σύστημα τύπων δεν θα πιάσει. Μια scanline device-independent bitmap ενός bit ανά pixel θεωρεί αναμένο bit ως λευκό. Ο encoder θεωρεί αναμένο bit ως μαύρο. Παραδώστε τις scanlines αναλλοίωτες και θα πάρετε ένα απόλυτα έγκυρο stream JBIG2 της φωτογραφικής αρνητικής εικόνας της σελίδας σας
// DIB ενός bit: αναμένο bit σημαίνει λευκό. Encoder JBIG2: αναμένο
// bit σημαίνει μαύρο. Αντιστρέψτε κάθε byte στην είσοδο
for I := 0 to RowBytes - 1 do
Row[I] := Row[I] xor $FF;
Η μέθοδος επαλήθευσης έχει ίση σημασία με τη διόρθωση. Η σύγκριση μηκών συμπιεσμένων streams δεν σας λέει τίποτα, γιατί μια αρνητική εικόνα συμπιέζεται σε παρόμοιο μέγεθος. Το να κοιτάξετε τη σελίδα αποδεικνύει μόνο ότι δεν είναι προφανώς αντεστραμμένη. Ο αξιόπιστος έλεγχος είναι να αποδοθεί η έξοδος και των δύο διαδρομών κωδικοποίησης, εγγενής Pascal και εξωτερική, σε PNG και να συγκριθούν byte προς byte: και οι δύο encoders είναι lossless στην ίδια εικόνα πηγής, οπότε οτιδήποτε άλλο από ακριβή ταύτιση είναι σφάλμα σε έναν από τους δύο. Η σύγκριση αυτή είναι πλέον μόνιμο regression test, και είναι ο τύπος ισχυρισμού που αξίζει να χτίζεται κάθε φορά που δύο υλοποιήσεις πρέπει να συμφωνούν ακριβώς
Ποιο backend να χρησιμοποιήσετε
Για γενικό bilevel περιεχόμενο, dithered halftones, line art, μικτά γραφικά, ο εγγενής encoder MMR της Pascal είναι επαρκής και δεν έχει κόστος ανάπτυξης. Για σαρωμένο κείμενο, που είναι η περίπτωση για την οποία σχεδιάστηκε το JBIG2, ο εξωτερικός encoder symbol-dictionary είναι εκεί όπου ζει η μείωση μεγέθους, γιατί παραγοντοποιεί επαναλαμβανόμενα σχήματα glyph σε λεξικό αντί να ξανακωδικοποιεί κάθε εμφάνιση. Αν παράγετε αρχεία σαρωμένων εγγράφων, η διαφορά αυτή είναι αρκετά μεγάλη ώστε να αλλάξει τον προγραμματισμό αποθήκευσης
Το ανάντη ερώτημα, πώς παράγεται εξαρχής η bilevel εικόνα, έχει ίση σημασία για το μέγεθος εξόδου· η μονόχρωμη απόδοση βάσει περιοχών καλύπτεται στο άρθρο για την απόδοση μονόχρωμων περιοχών, και η στρατηγική μεγέθους σε επίπεδο εγγράφου στη βελτιστοποίηση μεγέθους PDF και subsetting γραμματοσειρών. Για σύνολα σαρώσεων με επαναλαμβανόμενες σελίδες, η αποδιπλοποίηση συχνά κερδίζει την καλύτερη συμπίεση, που είναι το θέμα του αντιληπτικού deduplication εικόνων. Η διαθεσιμότητα toolchain και backend ανά πλατφόρμα καταγράφεται στη σελίδα προϊόντος losLab PDF Developer Library