技術文章

Delphi PDF 中的原生 JBIG2 雙色調影像壓縮

掃描的合約是每英寸數百個點的黑色墨水印在白紙上。以每像素一位元的點陣圖儲存時已經很小,但一百頁這樣的文件仍會讓 PDF 膨脹到無法用電子郵件傳送的大小。正確的濾鏡能改變這個算術。JBIG2 是 ISO 32000-1 為雙色調影像定義的最高壓縮比濾鏡,在堆疊的掃描文字上,它的壓縮率通常是 CCITT Group 4 的兩倍。當輸入是傳真、掃描或以其他方式簡化為兩種顏色時,這就是應該使用的濾鏡,而 HotPDF 可以直接將其寫入 PDF

這種格式透過兩個通用影像編解碼器所沒有的概念來實現高壓縮比。它對黑色筆跡在白色背景上的分布方式建立模型,並且注意到掃描頁面基本上是相同的幾百個字形重複數千次。理解這兩點,才能讓您有意識地選擇編碼選項,而非靠猜測

JBIG2 在 PDF 規格中的位置

ISO 32000-1 在 §7.4.7 中將 JBIG2Decode 列為串流濾鏡之一,從 PDF 1.4 起可用。它只適用於一個地方:/BitsPerComponent 為 1 且色彩空間解析為單一通道的影像 XObject。這正是重點所在。JBIG2 是雙色調編解碼器,因此它從不與 DCT 或 JPXDecode 在照片上競爭,而是與 CCITTFaxDecode(Group 3 和 Group 4 傳真濾鏡)在文件掃描器所產生的那類雙色調頁面上競爭

解碼器消耗標準稱為 PDF 設定檔的嵌入式 JBIG2 組織,其中每個影像串流都持有一系列片段,而非裸位元串流。一個可選的 /JBIG2Globals 串流承載在同一文件中多個影像之間共用的片段,這是讓重複內容在整個文件中只儲存一次、而非每頁儲存一次的機制。HotPDF 預設發出每個影像串流,並保持全域通道空閒,除非後端要求使用它

後端優先的編碼器架構

完整的 JBIG2 編碼器是一大塊軟體,其中最激進的部分歷史上曾受到專利約束,並以不適合所有產品的授權條款發布。HotPDF 透過將介面與引擎分離來解決這個矛盾。HPDFJBIG2 單元定義了函式庫其餘部分所呼叫的介面,並附帶一個適度的內建編碼器,讓 JBIG2 開箱即用。當您需要生產等級的壓縮比時,可以登錄一個更強大的引擎,函式庫便委託給它執行,而您的呼叫程式碼無需任何更改

切換只需一次登錄呼叫。在未登錄後端的情況下,編碼器退回到其內建路徑;登錄後,後續的每次編碼都透過它執行

uses
  HPDFJBIG2;

// Query what is active, then optionally install a stronger engine.
if not IsJBIG2EncoderBackendAvailable then
  // Production backend not present: HotPDF uses its built-in MMR path.
  RegisterJBIG2EncoderBackend(MyVendorJBIG2Encode);

// Later, to return to the built-in behaviour:
// ClearJBIG2Backends;

解碼也有相同的掛鉤,透過 RegisterJBIG2DecoderBackend 提供,並以 IsJBIG2DecoderBackendAvailable 探測。這就是為什麼函式庫附帶一個小型內建路徑加上一個後端接縫,而非一個單一整合的編碼器。內建路徑保持二進位檔精簡且無授權困擾,而接縫則讓已授權完整編碼器的團隊無需碰觸 PDF 寫入層即可插入它

編碼選項實際上的取捨

編碼透過 TJBIG2EncodeOptions 進行設定,這是一個帶有 LosslessUseGlobalSegmentsUseSymbolDictionaryLossyLevel 欄位的記錄。元件友善的包裝器 THPDFJBIG2Options 公開了 LosslessUseSymbolDictionaryLossyLevel,使它們可以從物件檢查器中設定,並在內部轉換為記錄。三種意圖驅動這些設定

無損重建保留每個像素。將 Lossless 設為 True 並將 LossyLevel 保留為零,解碼後的點陣圖與輸入完全相同,每一位元都一致。這是線條藝術、技術圖紙以及任何落掉像素可能改變含義的頁面(如簽名或印章)的唯一安全選擇。符號字典編碼開啟了文字感知的去重複功能,這是將 JBIG2 與傳真濾鏡區分開來的選項。有損程度是一個從 0 到 9 的整數,讓有能力的後端透過將近似相同的標記視為同一符號來以保真度換取大小。零表示無損。內建編碼器只遵守無損路徑而忽略任何非零的有損程度,因此較高的程度只在登錄了實作它們的後端後才生效

var
  Options: TJBIG2EncodeOptions;
begin
  Options := DefaultJBIG2EncodeOptions;   // Lossless True, symbol dictionary on
  Options.Lossless := True;
  Options.LossyLevel := 0;                // 0 keeps every pixel
  Options.UseSymbolDictionary := True;    // dedupe repeated glyphs
  // Pass Options to a backend, or let THPDFJBIG2Options carry them.
end;

符號字典與掃描文字的優勢

一頁掃描的文字其實不是文字的影像。它是相同的字母 e 列印了幾百次、相同的 t、相同的逗號,每個實例都是同一基礎形狀略帶雜訊的複本。符號字典捕捉了這個結構。編碼器將頁面上不同的標記收集成字典,每種形狀只儲存一次,然後將頁面記錄為引用字典項目的位置清單。同一字形出現一千次的代價是一個儲存的點陣圖加上一千個低成本的位置記錄

這正是 JBIG2 超越 CCITT Group 4 之處。Group 4 在沒有字形概念的情況下,根據上一行對每條掃描線進行編碼,因此每次出現字母時都要付出完整的代價。JBIG2 只付一次。當同一字典被提升到文件層級的全域串流時,節省效果在多頁掃描中複合,因為頁面與頁面共用的形狀在整個文件中只儲存一次。在密集文字上,差異並不微小 - - 這正是 JBIG2 存在的原因

通用區域與 MMR 用於其他情況

並非每張雙色調影像都是文字。地圖、示意圖、工程圖和混合頁面有線條藝術,沒有任何字典能對其進行概括。對於這些情況,JBIG2 對通用區域進行編碼,即一個直接壓縮像素而不進行任何符號訓練的矩形。標準允許通用區域使用 MMR,即 Group 4 傳真已使用的修改 READ 編碼,它根據上一行對每行像素建立模型

這是 HotPDF 在其內建編碼器中提供的路徑。當沒有登錄後端且請求無損時,函式庫將點陣圖壓縮為單個 MMR 通用區域,並將其包裝在 PDF 設定檔所需的 JBIG2 片段結構中。它不需要字典、不需要訓練過程,也不需要第二個影像作為參考,因此它是線條藝術和混合雙色調內容的可靠預設選項。在純文字上,它無法與完整的符號字典編碼器媲美,但它始終正確、始終無損、始終存在。其編碼器介面只需一次呼叫

var
  Encoder: THPDFJBIG2Encoder;
  ImageData: TJBIG2ByteArray;
  Scanlines: TJBIG2ScanlineArray;  // one byte array per row, MSB-first
  W, H: Integer;
begin
  // Scanlines, W and H describe a 1-bit page; each row is (W + 7) div 8 bytes.
  Encoder := THPDFJBIG2Encoder.Create;
  try
    if Encoder.EncodeToByteArray(Scanlines, W, H, ImageData) then
      // ImageData now holds a JBIG2 stream ready for a /JBIG2Decode XObject.
      ;
  finally
    Encoder.Free;
  end;
end;

建立文件時啟用它

在日常使用中,您不必直接操作編碼器類別。HotPDF 將 JBIG2 作為文件上的影像壓縮選項公開。列舉 THPDFImageCompressionType 在 Flate、JPEG 和 CCITT 選項旁邊包含了 icJBIG2,而文件帶有一個 JBIG2Options 屬性,類型為 THPDFJBIG2Options,持有選取該壓縮方式時所使用的設定。在添加您想以此方式壓縮的雙色調影像之前,先配置好這兩者

var
  Pdf: THotPDF;
begin
  Pdf := THotPDF.Create(nil);
  try
    Pdf.ImageCompressionType := icJBIG2;     // route 1-bit images through JBIG2
    Pdf.JBIG2Options.Lossless := True;        // keep every pixel
    Pdf.JBIG2Options.UseSymbolDictionary := True;
    Pdf.JBIG2Options.LossyLevel := 0;
    // Add pages and place your scanned 1-bit images here.
  finally
    Pdf.Free;
  end;
end;

值得注意的一個便利功能是 DBGridHotPDFExport 附加元件,它可以將 TDBGrid 直接渲染為 PDF。其輸出主要是雙色調的線條和文字,因此設定為 JBIG2 的文件可以讓這些匯出保持緊湊,無需您進行任何額外處理。本部落格上的兩個相關主題對周圍工作流程有更深入的介紹。關於在建立報表時如何配置影像和字型,請參閱Delphi 中帶字型和影像的報表輸出。當壓縮的文件必須滿足封存設定檔時,Delphi 中的 PDF/A、PDF/X 和 PDF/UA 驗證說明了哪些濾鏡在特定符合層級下是允許的。JBIG2 作為 HotPDF Component 的一部分提供,適用於 Delphi 和 C++Builder,並附有本部落格其他地方介紹的載入、編輯和加密 API