Artikel Teknis

Backend Encoder JBIG2 dan Linker Free Pascal

PDFlibPas dapat mengodekan gambar bilevel sebagai JBIG2 melalui dua backend yang berbeda. Yang satu adalah encoder MMR Object Pascal native yang selalu tersedia. Yang lain adalah encoder symbol-dictionary eksternal yang menghasilkan output jauh lebih kecil pada teks hasil pindai, dan bersifat opsional: sebuah proyek harus me-link unit backend agar backend itu ada. Distinksi itulah sumber kejutan paling umum pada fitur ini, jadi layak dinyatakan lebih dulu: DefaultJBIG2EncodeOptions meminta encoder eksternal secara default, dan ketika unit backend tidak di-link permintaan itu diam-diam jatuh kembali ke jalur MMR Pascal

Di Delphi dan C++Builder backend eksternal berupa sekumpulan static objects yang sudah jadi. Di Free Pascal ia harus menjadi DLL, dan jalan menuju kesimpulan itu adalah kisah linker yang berguna bagi siapa pun yang pernah mencoba me-link objek C++ ke dalam program Free Pascal

Registrasi adalah kontraknya

Unit backend mendaftarkan dirinya dari bagian initialisation-nya dengan memanggil RegisterJBIG2EncoderBackend. Caller memintanya melalui bit options, PDF_JBIG2_OPTION_EXTERNAL_ENCODER, yang bernilai 4, atau melalui parameter UseExternalEncoder pada entry point gambar extended. Umbrella pustaka dengan sengaja tidak menarik unit backend ke dalamnya, karena membawa sekumpulan objek besar seharusnya menjadi keputusan masing-masing proyek; di pohon C++Builder, misalnya, unit itu disertakan secara eksplisit oleh proyek-proyek yang menginginkannya

Konsekuensinya bagi caller: meminta encoder eksternal adalah preferensi, bukan jaminan, dan build yang lupa menyertakan unit itu menghasilkan berkas lebih besar alih-alih error. Jika ukuran output cukup penting untuk meminta encoder yang lebih baik, ia cukup penting untuk diverifikasi bahwa Anda benar-benar mendapatkannya

Alur permintaan encode JBIG2 PDFlibPas di mana preferensi encoder eksternal diam-diam jatuh kembali ke jalur MMR Pascal native tanpa unit backend
Meminta encoder symbol-dictionary eksternal adalah preferensi: di-link, output menyusut; tidak di-link, jalur MMR Pascal berjalan diam-diam dengan berkas lebih besar
uses
  PDFlibrary,
{$IFDEF FPC}
  PDFlibJBIG2EncDLL;    // backend dinamis untuk Free Pascal
{$ELSE}
  PDFlibJBIG2EncC;      // kumpulan static object untuk 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;

Mengompilasi unit hanya dua baris. Simbol adalah pekerjaan sesungguhnya

Membuat unit backend itu sendiri terkompilasi di Free Pascal hanya butuh tepat dua perubahan: mengatur dialect assembler, dan mengganti konstruktor format-settings berbasis record dengan variabel default global. Itu cerminan yang adil tentang betapa portabelnya Pascal yang lugas di antara kedua kompiler

Sisi simbol adalah pekerjaan sesungguhnya. Kumpulan objek itu mereferensikan 176 simbol C. Dari jumlah itu, 128 sudah punya implementasi Pascal di dalam unit dan hanya perlu nama export dilekatkan, karena Delphi memakai nama fungsi sebagai nama simbol sementara Free Pascal mensyaratkan deklarasi public name yang eksplisit. Dua puluh tujuh berbagi dengan codec JPEG 2000 dan harus diekspor dari tepat satu tempat, karena mendefinisikannya dua kali merusak program apa pun yang me-link keduanya. Sisa 21 adalah entri platform dan C runtime, enam belas fungsi berkas Win32 ditambah segelintir panggilan pustaka standar, dan semuanya masuk ke unit kompatibilitas baru

Tidak ada yang sulit secara konseptual, dan semuanya diperlukan sebelum linker bahkan mau mencoba. Di linker pula semuanya berhenti

Tiga jalur linking, tiga jalan buntu

Linker internal Free Pascal tidak bisa membaca object files, karena objek-objek itu dibuat oleh kompiler yang memancarkan section COMDAT asosiatif dan linker internal melaporkan bahwa ia tidak mendukungnya. Itu penolakan bulat-bulat, bukan peringatan

Beralih ke linker eksternal tampak seperti jawabannya. Linker binutils yang dibundel Free Pascal crash total ketika menerapkan section garbage collection pada arsip ini, dan flag itu bagian dari set parameter tetap yang diteruskan Free Pascal untuk target Windows 64-bit, sehingga tidak bisa dihapus dari command line; switch yang didokumentasikan untuk menekannya diabaikan pada jalur ini. Mengganti dengan binutils yang jauh lebih baru gagal dengan cara berbeda: ia tidak bisa memproses link script Free Pascal sama sekali, menghasilkan output kosong tanpa script dan tembok relocation errors dengan script

Satu batasan yang ditemukan di sepanjang jalan layak diketahui bahkan jika Anda tidak pernah mengalami masalah linker. Linker eksternal menyelesaikan path object file relatif terhadap direktori output executable, bukan pohon sumber, sehingga direktif include-object relatif hanya bekerja ketika direktori output kebetulan sama dengan direktori kerja saat kompilasi. Sebuah pustaka tidak bisa mengasumsikan hal itu tentang proyek konsumennya, dan itu saja sudah menjadi alasan memilih pustaka yang di-link daripada objek lepas

Tiga jalur linker yang gagal untuk objek encoder C++ JBIG2 di Free Pascal dan DLL yang memaparkan dua entry point C flat yang menyelesaikannya
Section COMDAT mengalahkan linker internal dan kedua linker eksternal gagal, sehingga encoder C++ dikirim sebagai satu DLL yang diikat dinamis oleh unit backend

Mengapa kompiler C++ yang lain tidak menolong

Gagasan berikutnya yang paling jelas adalah membangun ulang sisi C++ dengan kompiler yang objeknya bisa dibaca Free Pascal. Itu juga tidak berhasil, dan alasannya fundamental, bukan soal switch. Satu translation unit C++ minimal yang berisi template, dikompilasi dengan semua fitur code-generation dimatikan, tetap memancarkan weak external symbols, karena instansiasi template dan inline memunculkannya secara konstruksi. Free Pascal menolak kelas simbol itu bulat-bulat. Arah sebaliknya juga gagal: linker C++ arus utama tidak bisa mengonsumsi objek dari kompiler satunya karena penanganan section COMDAT yang sama

Jadi kode C++ tidak dapat dikirim sebagai objek ke Free Pascal lewat jalur mana pun yang tersedia. Ia bisa dikirim sebagai DLL, dan itulah yang terjadi: encoder beserta dependensi image-processing-nya dibangun menjadi satu pustaka yang memaparkan dua entry point C flat, dan unit backend Free Pascal mengikatnya secara dinamis lalu mendaftarkan diri persis seperti backend statis. Jalur Delphi dan C++Builder tidak disentuh sama sekali, dan itulah hasil yang benar; masalah portabilitas pada satu toolchain tidak seharusnya mengganggu toolchain yang sudah berfungsi

Polaritas adalah satu hal yang akan menggigit Anda

Antara bitmap bilevel Windows dan encoder JBIG2 ada ketidakcocokan konvensi yang tidak akan tertangkap oleh sistem tipe mana pun. Scanline device-independent bitmap satu bit per piksel memperlakukan bit set sebagai putih. Encoder memperlakukan bit set sebagai hitam. Serahkan scanline apa adanya dan Anda mendapatkan stream JBIG2 yang sepenuhnya valid dari negatif fotografis halaman Anda

Konvensi polaritas DIB satu bit dan JBIG2 di mana bit set berarti putih di scanline dan hitam di encoder, diperbaiki dengan menginversi setiap byte
Byte yang sama, makna yang berlawanan: tanpa inversi tiap byte encoder menghasilkan stream JBIG2 valid dari negatif fotografis
// DIB satu bit: bit set berarti putih. Encoder JBIG2: bit set
// berarti hitam. Inversi setiap byte saat masuk
for I := 0 to RowBytes - 1 do
  Row[I] := Row[I] xor $FF;

Metode verifikasi sama pentingnya dengan perbaikannya. Membandingkan panjang stream terkompresi tidak memberi tahu apa pun, karena gambar negatif terkompresi ke ukuran yang mirip. Melihat halamannya hanya membuktikan bahwa ia tidak jelas-jelas terinversi. Pemeriksaan yang andal adalah merender output kedua jalur pengodean, native Pascal dan eksternal, ke PNG dan membandingkannya byte demi byte: kedua encoder lossless pada gambar sumber yang sama, sehingga apa pun selain kecocokan sempurna adalah bug di salah satunya. Perbandingan itu kini menjadi regression test permanen, dan itulah jenis assertion yang layak dibangun kapan pun dua implementasi diharapkan sepakat secara persis

Backend mana yang dipakai

Untuk konten bilevel umum, halftone dithered, line art, grafik campuran, encoder MMR Pascal native sudah memadai dan tidak berbiaya deployment. Untuk teks hasil pindai, yang merupakan kasus yang JBIG2 dirancang untuknya, encoder symbol-dictionary eksternal adalah tempat penghematan ukuran berada, karena ia memfaktorkan bentuk glyph yang berulang ke dalam dictionary alih-alih mengodekan ulang setiap kemunculannya. Jika Anda memproduksi arsip dokumen hasil pindaian, perbedaan itu cukup besar untuk mengubah perencanaan penyimpanan

Pertanyaan hulunya, bagaimana gambar bilevel diproduksi sejak awal, sama pentingnya bagi ukuran output; rendering monokrom berbasis region dibahas dalam artikel rendering region monokrom, dan strategi ukuran seluruh dokumen dalam optimasi ukuran berkas PDF dan subsetting font. Untuk set pindaian dengan halaman yang berulang, deduplikasi sering mengalahkan kompresi yang lebih baik, yang menjadi subjek deduplikasi gambar perseptual. Ketersediaan toolchain dan backend per platform tercantum di halaman produk losLab PDF Developer Library