Technical Article

JBIG2-Zweifarbkomprimierung in Delphi-PDFs mit HotPDF

Ein gescannter Vertrag besteht aus einigen Hundert Punkten pro Zoll schwarzer Tinte auf weißem Papier. Als Ein-Bit-pro-Pixel-Bitmap gespeichert ist er bereits klein, doch hundert solcher Seiten blähen ein PDF immer noch auf eine Größe auf, die sich kaum per E-Mail versenden lässt. Der richtige Filter verändert die Rechnung. JBIG2 ist die Komprimierung mit dem höchsten Verhältnis, die ISO 32000-1 für Zweifarbbilder definiert, und bei einem Stapel gescannter Texte reduziert er die Dateigröße regelmäßig auf die Hälfte von CCITT Group 4. Dieser Filter ist die richtige Wahl, wenn die Eingabe gefaxt, gescannt oder auf zwei Farben reduziert wurde, und HotPDF kann ihn direkt in ein PDF schreiben

Das Format erzielt dieses Verhältnis durch zwei Konzepte, die ein generischer Bild-Codec nicht kennt. Es modelliert, wie schwarze Läufe vor einem weißen Hintergrund angeordnet sind, und erkennt, dass eine gescannte Seite hauptsächlich aus denselben wenigen Hundert Glyphenformen besteht, die tausende Male wiederholt werden. Dieses Verständnis ermöglicht es, die Encoding-Optionen bewusst zu wählen statt zu raten

JBIG2 in der PDF-Spezifikation

ISO 32000-1 listet JBIG2Decode unter den Stream-Filtern in §7.4.7, verfügbar ab PDF 1.4. Er gilt nur für einen Bereich: Image-XObjects, deren /BitsPerComponent 1 ist und deren Farbraum auf einen einzigen Kanal auflöst. Das ist der eigentliche Zweck. JBIG2 ist ein Zweifarb-Codec und konkurriert daher nie mit DCT oder JPXDecode bei Fotos. Er konkurriert mit CCITTFaxDecode, den Group-3- und Group-4-Fax-Filtern, auf genau der Art von Zweifarbseiten, die ein Dokumentenscanner erzeugt

Der Decoder verarbeitet die eingebettete JBIG2-Organisation, die der Standard als PDF-Profil bezeichnet, wobei jeder Bild-Stream eine Folge von Segmenten statt eines bloßen Bitstreams enthält. Ein optionaler /JBIG2Globals-Stream trägt Segmente, die über mehrere Bilder im selben Dokument geteilt werden. Dieser Mechanismus ermöglicht es, wiederholte Inhalte einmal für die gesamte Datei statt einmal pro Seite zu speichern. HotPDF gibt den seitenspezifischen Stream standardmäßig aus und hält den Globals-Kanal frei, sofern kein Backend ihn anfordert

Die Backend-orientierte Encoder-Architektur

Ein vollständiger JBIG2-Encoder ist ein umfangreiches Stück Software, und seine leistungsfähigsten Teile waren historisch durch Patente belastet und unter Lizenzen ausgeliefert, die nicht jedem Produkt entsprechen. HotPDF löst diese Spannung, indem es die Schnittstelle vom Engine trennt. Das Unit HPDFJBIG2 definiert die Aufrufe, die der Rest der Bibliothek macht, und liefert einen einfachen integrierten Encoder mit, sodass JBIG2 sofort einsatzbereit ist. Wenn Sie Verhältnisse in Produktionsqualität benötigen, registrieren Sie eine leistungsfähigere Engine, und die Bibliothek delegiert an sie, ohne dass sich Ihr aufrufender Code ändert

Die Umschaltung erfolgt durch einen einzigen Registrierungsaufruf. Ist kein Backend registriert, fällt der Encoder auf seinen integrierten Pfad zurück. Registrieren Sie eines, laufen alle nachfolgenden Encodierungen darüber

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;

Dieselbe Erweiterungsmöglichkeit gibt es für das Dekodieren über RegisterJBIG2DecoderBackend, mit IsJBIG2DecoderBackendAvailable zur Abfrage. Deshalb liefert eine Bibliothek einen kleinen integrierten Pfad plus eine Backend-Naht statt eines monolithischen Encoders. Der integrierte Pfad hält die Binärdatei schlank und frei von Lizenzproblemen, während die Naht einem Team, das einen vollständigen Encoder lizenziert hat, ermöglicht, ihn einzubinden, ohne die PDF-Schreibschicht zu berühren

Was die Encoding-Optionen bewirken

Das Encoding wird über TJBIG2EncodeOptions konfiguriert, ein Record mit den Feldern Lossless, UseGlobalSegments, UseSymbolDictionary und LossyLevel. Der komponentenfreundliche Wrapper THPDFJBIG2Options veröffentlicht Lossless, UseSymbolDictionary und LossyLevel, sodass sie aus dem Object Inspector gesetzt werden können, und konvertiert intern in den Record. Drei Absichten steuern die Einstellungen

Verlustfreie Rekonstruktion behält jeden Pixel. Setzen Sie Lossless auf True und lassen Sie LossyLevel bei null, ist die dekodierte Bitmap Bit für Bit identisch mit der Eingabe. Dies ist die einzig sichere Wahl für Strichzeichnungen, technische Zeichnungen und jede Seite, bei der ein fehlender Pixel die Bedeutung ändern könnte, etwa eine Unterschrift oder ein Stempel. Die Symbolwörterbuch-Kodierung aktiviert textbewusste Deduplizierung und ist die Option, die JBIG2 von den Fax-Filtern unterscheidet. Der verlustbehaftete Pegel, eine ganze Zahl von 0 bis 9, lässt ein fähiges Backend Treue gegen Größe tauschen, indem nahezu identische Zeichen als dasselbe Symbol behandelt werden. Null bedeutet verlustfrei. Der integrierte Encoder unterstützt nur den verlustfreien Pfad und ignoriert jeden verlustbehafteten Pegel ungleich null, sodass die höheren Stufen erst wirksam werden, wenn ein Backend, das sie implementiert, registriert wurde

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;

Symbolwörterbücher und warum Textscans profitieren

Eine Seite gescannten Textes ist nicht wirklich ein Bild aus Wörtern. Es ist derselbe Buchstabe e, hunderte Male gedruckt, dasselbe t, dasselbe Komma, wobei jede Instanz eine leicht verrauschte Kopie einer zugrunde liegenden Form ist. Ein Symbolwörterbuch erfasst diese Struktur. Der Encoder sammelt die einzelnen Zeichen auf der Seite in einem Wörterbuch, speichert jede Form einmal und zeichnet die Seite dann als Liste von Positionen auf, die auf Wörterbucheinträge verweisen. Tausend Vorkommen derselben Glyphe kosten eine gespeicherte Bitmap plus tausend günstige Platzierungen

Genau hier übertrifft JBIG2 CCITT Group 4. Group 4 kodiert jede Abtastzeile gegen die darüberliegende Zeile ohne jedes Konzept einer Glyphe und zahlt so für jeden Buchstaben beim jedem Auftreten den vollen Preis. JBIG2 zahlt einmal. Wenn dasselbe Wörterbuch in den dokumentübergreifenden Globals-Stream hochgestuft wird, verstärkt sich die Einsparung über einen mehrseitigen Scan, weil die von Seite zu Seite gemeinsamen Formen einmal für die gesamte Datei gespeichert werden. Bei dichtem Text ist der Unterschied nicht marginal. Das ist der Grund, warum JBIG2 existiert

Generische Region und MMR für alles andere

Nicht jedes Zweifarbbild ist Text. Karten, Schaltpläne, technische Zeichnungen und gemischte Seiten enthalten Strichzeichnungen, die kein Wörterbuch zusammenfassen kann. Dafür kodiert JBIG2 eine generische Region, ein Rechteck aus Pixeln, das direkt ohne jedes Symboltraining komprimiert wird. Der Standard erlaubt einer generischen Region, MMR zu verwenden, die modifizierte READ-Kodierung, die Group-4-Fax bereits einsetzt und jede Pixelreihe gegen die darüber modelliert

Dies ist der Pfad, den HotPDF in seinem integrierten Encoder ausliefert. Wenn kein Backend registriert ist und die Anforderung verlustfrei ist, komprimiert die Bibliothek die Bitmap als einzelne MMR-generische Region und hüllt sie in die JBIG2-Segmentstruktur, die das PDF-Profil erfordert. Sie benötigt kein Wörterbuch, keinen Trainingspass und kein zweites Referenzbild, ist also der zuverlässige Standard für Strichzeichnungen und gemischte Zweifarbinhalte. Bei reinem Text erreicht er nicht die Qualität eines vollständigen Symbolwörterbuch-Encoders, ist aber immer korrekt, immer verlustfrei und immer vorhanden. Die Encoder-Schnittstelle dafür besteht aus einem einzigen Aufruf

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;

Aktivierung beim Erstellen eines Dokuments

Für die alltägliche Nutzung berühren Sie die Encoder-Klasse nicht direkt. HotPDF stellt JBIG2 als Bildkomprimierungsoption am Dokument bereit. Die Enumeration THPDFImageCompressionType enthält icJBIG2 neben den Optionen Flate, JPEG und CCITT, und das Dokument trägt eine JBIG2Options-Eigenschaft vom Typ THPDFJBIG2Options, die die Einstellungen enthält, die bei dieser Komprimierungswahl verwendet werden. Konfigurieren Sie beides, bevor Sie die Zweifarbbilder hinzufügen, die auf diese Weise komprimiert werden sollen

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;

Erwähnenswert ist auch das Add-on DBGridHotPDFExport, das ein TDBGrid direkt in ein PDF rendert. Seine Ausgabe besteht überwiegend aus Zweifarblinien und Text, sodass ein für JBIG2 konfiguriertes Dokument diese Exporte kompakt hält, ohne zusätzlichen Aufwand Ihrerseits. Zwei verwandte Themen in diesem Blog vertiefen den umgebenden Workflow. Wie Bilder und Schriften beim Erstellen von Berichten platziert werden, erfahren Sie unter Berichtsausgabe mit Schriften und Bildern in Delphi. Wenn ein komprimiertes Dokument ein Archivierungsprofil erfüllen muss, geben die Regeln in PDF/A-, PDF/X- und PDF/UA-Validierung in Delphi an, welche Filter eine bestimmte Konformitätsstufe akzeptiert. JBIG2 ist Teil der HotPDF-Komponente für Delphi und C++Builder, neben den Lade-, Bearbeitungs- und Verschlüsselungs-APIs, die an anderer Stelle hier behandelt werden