PDFlibPas có thể mã hóa ảnh bilevel thành JBIG2 qua hai backend khác nhau. Một là bộ mã hóa MMR Object Pascal thuần luôn có mặt. Hai là bộ mã hóa symbol-dictionary ngoài tạo ra đầu ra nhỏ hơn đáng kể trên văn bản quét, và nó là tùy chọn: một dự án phải liên kết unit backend thì nó mới tồn tại. Sự phân biệt đó là nguồn của bất ngờ phổ biến nhất với tính năng này, nên đáng nói trước: DefaultJBIG2EncodeOptions mặc định yêu cầu bộ mã hóa ngoài, và khi unit backend chưa được liên kết, yêu cầu đó lặng lẽ quay về con đường MMR Pascal
Trên Delphi và C++Builder, backend ngoài là một bộ object tĩnh dựng sẵn. Trên Free Pascal, nó phải trở thành một DLL, và con đường dẫn tới kết luận đó là một câu chuyện linker hữu ích cho bất kỳ ai đã từng thử liên kết object C++ vào một chương trình Free Pascal
Đăng ký chính là hợp đồng
Unit backend tự đăng ký từ phần khởi tạo của nó bằng cách gọi RegisterJBIG2EncoderBackend. Caller yêu cầu nó hoặc qua bit tùy chọn PDF_JBIG2_OPTION_EXTERNAL_ENCODER có giá trị 4, hoặc qua tham số UseExternalEncoder của các điểm vào ảnh mở rộng. Chiếc ô thư viện cố tình không kéo unit backend vào, vì việc mang theo một bộ object lớn phải là quyết định của từng dự án; chẳng hạn trong cây C++Builder, nó được các dự án muốn dùng include một cách tường minh
Hệ quả với caller là yêu cầu bộ mã hóa ngoài là một sở thích, không phải bảo đảm, và một bản build quên unit sẽ tạo ra tệp lớn hơn thay vì báo lỗi. Nếu kích thước đầu ra quan trọng đến mức phải xin bộ mã hóa tốt hơn, thì nó cũng quan trọng đến mức phải kiểm tra xem bạn có thực sự nhận được nó
uses
PDFlibrary,
{$IFDEF FPC}
PDFlibJBIG2EncDLL; // backend động cho Free Pascal
{$ELSE}
PDFlibJBIG2EncC; // bộ object tĩnh cho 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;
Biên dịch unit chỉ là hai dòng. Symbol mới là phần việc
Để chính unit backend biên dịch được dưới Free Pascal chỉ cần đúng hai thay đổi: đặt khẩu ngữ assembler, và thay một constructor cài đặt định dạng dựa trên record bằng biến mặc định toàn cục. Đó là bức phản ánh công bằng về mức di động của Pascal đơn giản giữa hai trình biên dịch
Phần symbol mới là công việc thật. Bộ object tham chiếu 176 symbol C. Trong số đó, 128 symbol đã có hiện thực Pascal bên trong unit và chỉ cần gắn tên export, vì Delphi dùng tên hàm làm tên symbol trong khi Free Pascal đòi hỏi khai báo public name tường minh. Hai mươi bảy symbol dùng chung với codec JPEG 2000 và phải được export từ đúng một nơi, vì định nghĩa chúng hai lần làm hỏng mọi chương trình liên kết cả hai. 21 symbol còn lại là các mục nền tảng và runtime C, gồm mười sáu hàm tệp Win32 cộng một ít lệnh gọi thư viện chuẩn, và chúng được đưa vào một unit tương thích mới
Chẳng gì trong số đó khó về mặt khái niệm, và tất cả đều cần thiết trước khi linker thậm chí chịu thử. Linker là nơi mọi thứ dừng lại
Ba con đường liên kết, ba ngõ cụt
Linker nội bộ của Free Pascal không đọc được các tệp object, vì chúng được tạo bởi một trình biên dịch phát ra các section COMDAT kết hợp và linker nội bộ báo rằng nó không hỗ trợ chúng. Đó là một sự từ chối dứt khoát, không phải cảnh báo
Chuyển sang một linker ngoài trông như là câu trả lời. Linker binutils kèm theo Free Pascal sập hoàn toàn khi áp dụng thu gom rác section lên archive này, và cờ đó là một phần của bộ tham số cố định Free Pascal truyền cho mục tiêu Windows 64-bit, nên không thể bỏ nó khỏi dòng lệnh; các switch được ghi trong tài liệu để tắt nó bị bỏ qua trên con đường này. Cung cấp một binutils mới hơn nhiều thì thất bại theo kiểu khác: nó không xử lý nổi script liên kết của Free Pascal, cho ra đầu ra rỗng khi không có script và một bức tường lỗi relocation khi có script
Một ranh giới được phát hiện dọc đường đáng biết ngay cả khi bạn chưa từng dính lỗi linker. Linker ngoài phân giải đường dẫn tệp object tương đối với thư mục đầu ra của tệp thực thi chứ không phải cây mã nguồn, nên chỉ thị include-object tương đối chỉ hoạt động khi thư mục đầu ra trùng khớp tình cờ với thư mục làm việc lúc biên dịch. Một thư viện không thể giả định điều đó về dự án của người dùng, và điều đó tự nó đã là lý do để chuộng thư viện liên kết thay vì object rời
Vì sao một trình biên dịch C++ khác cũng vô ích
Ý tưởng tiếp theo hiển nhiên là dựng lại phía C++ bằng một trình biên dịch mà Free Pascal đọc được object của nó. Điều đó cũng không chạy, và lý do mang tính nền tảng chứ không phải chuyện của các switch. Một translation unit C++ tối giản chứa một template, được biên dịch với mọi tính năng sinh mã tắt, vẫn phát ra các weak external symbol, vì việc khởi tạo template và inline sinh ra chúng ngay từ cấu trúc. Free Pascal từ chối thẳng class symbol đó. Chiều ngược lại cũng thất bại: một linker C++ chính thống không tiêu hóa được object của trình biên dịch kia vì cùng cách xử lý section COMDAT
Vậy mã C++ không thể được giao cho Free Pascal dưới dạng object bằng bất kỳ con đường hiện có nào. Nó có thể được giao dưới dạng DLL, và đó chính là điều đã xảy ra: bộ mã hóa và phụ thuộc xử lý ảnh của nó được dựng thành một thư viện exposing hai điểm vào C phẳng, và unit backend Free Pascal gắn động các điểm đó rồi tự đăng ký đúng như backend tĩnh. Con đường Delphi và C++Builder không bị động đến chút nào, và đó là kết quả đúng; vấn đề di động trên một toolchain không nên làm xáo trộn toolchain vốn đã chạy tốt
Cực tính là thứ duy nhất sẽ cắn bạn
Giữa một bitmap bilevel Windows và một bộ mã hóa JBIG2 có một sự lệch quy ước mà không hệ thống kiểu nào bắt được. Scanline bitmap độc lập thiết bị một bit mỗi pixel coi bit bật là màu trắng. Bộ mã hóa coi bit bật là màu đen. Đưa scanline đi qua nguyên trạng và bạn nhận được một luồng JBIG2 hoàn toàn hợp lệ của bản âm bản trang của mình
// DIB một bit: bit bật nghĩa là trắng. Bộ mã hóa JBIG2: bit bật
// nghĩa là đen. Đảo mọi byte trên đường vào
for I := 0 to RowBytes - 1 do
Row[I] := Row[I] xor $FF;
Phương pháp xác minh quan trọng ngang với bản sửa. So độ dài luồng nén chẳng nói lên điều gì, vì một ảnh âm bản nén ra kích thước tương đương. Nhìn trang chỉ chứng minh nó không bị đảo một cách lộ liễu. Kiểm tra đáng tin là kết xuất đầu ra của cả hai con đường mã hóa, Pascal thuần và ngoài, ra PNG rồi so từng byte một: cả hai bộ mã hóa đều lossless trên cùng ảnh nguồn, nên bất cứ thứ gì không khớp tuyệt đối đều là bug của một trong hai. Phép so sánh đó giờ là một regression test cố định, và đó là kiểu khẳng định đáng dựng bất cứ khi nào hai hiện thực được kỳ vọng khớp nhau tuyệt đối
Chọn backend nào
Với nội dung bilevel nói chung, halftone dither, line art, đồ họa trộn, bộ mã hóa MMR Pascal thuần là đủ dùng và không tốn chi phí triển khai. Với văn bản quét — trường hợp JBIG2 được thiết kế cho — bộ mã hóa symbol-dictionary ngoài mới là nơi mức giảm kích thước cư trú, vì nó tách các hình dạng glyph lặp lại vào một dictionary thay vì mã hóa lại từng lần xuất hiện. Nếu bạn đang sản xuất kho lưu trữ tài liệu quét, khác biệt đó đủ lớn để thay đổi kế hoạch lưu trữ
Câu hỏi thượng nguồn — ảnh bilevel được tạo ra như thế nào ngay từ đầu — cũng quan trọng không kém cho kích thước đầu ra; kết xuất đơn sắc theo vùng được đề cập trong bài viết về kết xuất vùng đơn sắc, còn chiến lược kích thước toàn tài liệu trong tối ưu kích thước tệp PDF và font subsetting. Với các bộ scan có trang lặp lại, loại trùng lặp thường thắng nén tốt hơn, chủ đề của loại trùng lặp ảnh theo nhận thức. Mức sẵn có của toolchain và backend theo từng nền tảng được liệt kê trên trang sản phẩm losLab PDF Developer Library