Article technique

Compression native d'images binaires JBIG2 dans les PDF Delphi

Un contrat numérisé représente quelques centaines de points par pouce d'encre noire sur papier blanc. Stocké sous forme de bitmap avec un bit par pixel, il est déjà petit, mais une centaine de pages de ce type gonflent tout de même un PDF au-delà de ce que vous enverriez par e-mail. Le bon filtre modifie l'équation. JBIG2 offre le taux de compression le plus élevé défini par l'ISO 32000-1 pour les images binaires, et sur une pile de textes numérisés, il réduit régulièrement de moitié ce que produit le CCITT Group 4. C'est le filtre à utiliser lorsque l'entrée est télécopiée, numérisée ou autrement réduite à deux couleurs, et HotPDF peut l'écrire directement dans un PDF

Le format atteint ce ratio grâce à deux idées qu'un codec d'image générique ne possède pas. Il modélise la façon dont les lignes noires se détachent sur un fond blanc, et il remarque qu'une page numérisée est principalement constituée de quelques centaines de formes de glyphes identiques répétées des milliers de fois. Comprendre ces deux points est ce qui vous permet de choisir les options d'encodage de manière délibérée au lieu de deviner

La place de JBIG2 dans la spécification PDF

L'ISO 32000-1 liste JBIG2Decode parmi les filtres de flux au §7.4.7, disponibles à partir de PDF 1.4. Il s'applique à un seul endroit : les XObjects image dont /BitsPerComponent vaut 1 et dont l'espace colorimétrique se résout en un seul canal. C'est là tout l'intérêt. JBIG2 est un codec binaire, il ne concurrence donc jamais DCT ou JPXDecode sur des photographies. Il concurrence CCITTFaxDecode, les filtres de télécopie du Groupe 3 et du Groupe 4, sur le type exact de page bicolore que produit un scanner de documents

Le décodeur consomme l'organisation JBIG2 intégrée que la norme appelle le profil PDF, où chaque flux d'images contient une séquence de segments plutôt qu'un simple flux de bits. Un flux /JBIG2Globals optionnel transporte des segments partagés par plusieurs images du même document, ce qui est le mécanisme permettant au contenu répété d'être stocké une seule fois pour tout un fichier plutôt qu'une fois par page. HotPDF émet le flux par image par défaut et garde le canal des variables globales libre à moins qu'un backend ne le demande

L'architecture de l'encodeur orientée backend

Un encodeur JBIG2 complet est un logiciel volumineux, et ses parties les plus agressives ont historiquement été encombrées de brevets et distribuées sous des licences qui ne conviennent pas à tous les produits. HotPDF résout cette tension en séparant l'interface du moteur. L'unité HPDFJBIG2 définit les appels que le reste de la bibliothèque effectue, et elle est livrée avec un modeste encodeur intégré pour que JBIG2 fonctionne immédiatement. Lorsque vous avez besoin de taux de compression de niveau production, vous enregistrez un moteur plus puissant et la bibliothèque lui délègue la tâche, sans aucune modification de votre code d'appel

Le basculement se fait par un unique appel d'enregistrement. Si aucun backend n'est enregistré, l'encodeur se rabat sur sa propre méthode intégrée. Enregistrez-en un et chaque encodage ultérieur passera par lui

uses
  HPDFJBIG2;

// Demandez ce qui est actif, puis installez facultativement un moteur plus puissant.
if not IsJBIG2EncoderBackendAvailable then
  // Le backend de production n'est pas présent : HotPDF utilise son chemin MMR intégré.
  RegisterJBIG2EncoderBackend(MyVendorJBIG2Encode);

// Plus tard, pour revenir au comportement intégré :
// ClearJBIG2Backends;

Le même point d'ancrage existe pour le décodage via RegisterJBIG2DecoderBackend, avec IsJBIG2DecoderBackendAvailable pour le sonder. C'est la raison pour laquelle une bibliothèque est livrée avec un petit chemin intégré plus une interface pour backend plutôt qu'un seul encodeur monolithique. Le chemin intégré garde le binaire léger et exempt de complications de licence, tandis que l'interface permet à une équipe ayant licencié un encodeur complet de l'intégrer sans toucher du tout à la couche d'écriture du PDF

Ce que les options d'encodage échangent réellement

L'encodage est configuré via TJBIG2EncodeOptions, un enregistrement avec les champs Lossless, UseGlobalSegments, UseSymbolDictionary et LossyLevel. L'enveloppe adaptée aux composants THPDFJBIG2Options publie Lossless, UseSymbolDictionary et LossyLevel afin qu'ils puissent être définis depuis l'Inspecteur d'objets, et elle se convertit en l'enregistrement en interne. Trois intentions pilotent les paramètres

La reconstruction sans perte (Lossless) conserve chaque pixel. Définissez Lossless à True et laissez LossyLevel à zéro, et le bitmap décodé sera identique bit pour bit à l'entrée. C'est le seul choix sûr pour les dessins au trait, les dessins techniques et toute page où un pixel manquant pourrait en changer le sens, comme une signature ou un tampon. Le codage par dictionnaire de symboles active la déduplication sensible au texte et c'est l'option qui sépare JBIG2 des filtres de télécopie. Le niveau de perte, un entier de 0 à 9, permet à un backend capable de sacrifier de la fidélité pour de la taille en traitant des marques presque identiques comme le même symbole. Zéro signifie sans perte. L'encodeur intégré honore uniquement le chemin sans perte et ignore tout niveau de perte non nul ; les niveaux plus élevés ne prennent effet qu'une fois qu'un backend qui les implémente est enregistré

var
  Options: TJBIG2EncodeOptions;
begin
  Options := DefaultJBIG2EncodeOptions;   // Lossless True, dictionnaire de symboles activé
  Options.Lossless := True;
  Options.LossyLevel := 0;                // 0 conserve chaque pixel
  Options.UseSymbolDictionary := True;    // déduplique les glyphes répétés
  // Passez les options à un backend, ou laissez THPDFJBIG2Options s'en charger.
end;

Les dictionnaires de symboles et pourquoi les numérisations de textes l'emportent

Une page de texte numérisée n'est pas vraiment une image de mots. C'est la même lettre e imprimée plusieurs centaines de fois, le même t, la même virgule, chaque occurrence étant une copie légèrement bruitée d'une forme sous-jacente. Un dictionnaire de symboles capture cette structure. L'encodeur rassemble les marques distinctes de la page dans un dictionnaire, stocke chaque forme une fois, puis enregistre la page sous forme d'une liste de positions qui référencent les entrées du dictionnaire. Mille occurrences du même glyphe coûtent un bitmap stocké plus mille placements peu coûteux

C'est précisément là que JBIG2 prend l'avantage sur le CCITT Group 4. Le Group 4 code chaque ligne de balayage par rapport à la ligne au-dessus sans aucune notion de glyphe, il paie donc le coût total de chaque lettre chaque fois qu'elle apparaît. JBIG2 paie une fois. Lorsque le même dictionnaire est promu au flux global au niveau du document, l'économie s'accumule sur une numérisation de plusieurs pages, car les formes partagées page après page sont stockées une seule fois pour l'ensemble du fichier. Sur un texte dense, la différence n'est pas marginale. C'est la raison pour laquelle JBIG2 existe

Région générique et MMR pour tout le reste

Toutes les images binaires ne sont pas du texte. Les cartes, les schémas, les dessins d'ingénierie et les pages mixtes contiennent des dessins au trait qu'aucun dictionnaire ne peut résumer. Pour ceux-ci, JBIG2 code une région générique, un rectangle de pixels compressé directement sans aucun entraînement de symboles. La norme autorise une région générique à utiliser MMR, le codage READ modifié modifié que la télécopie du Group 4 utilise déjà, qui modélise chaque ligne de pixels par rapport à la ligne située au-dessus d'elle

C'est le chemin que HotPDF fournit dans son encodeur intégré. Lorsqu'aucun backend n'est enregistré et que la demande est sans perte, la bibliothèque compresse le bitmap en une seule région générique MMR et l'enveloppe dans la structure de segments JBIG2 requise par le profil PDF. Il n'a besoin d'aucun dictionnaire, d'aucune passe d'entraînement et d'aucune deuxième image à référencer, c'est donc la valeur par défaut fiable pour les dessins au trait et les contenus binaires mixtes. Il n'égalera pas un encodeur complet par dictionnaire de symboles sur du texte pur, mais il est toujours correct, toujours sans perte et toujours présent. La surface de l'encodeur pour cela se résume à un seul appel

var
  Encoder: THPDFJBIG2Encoder;
  ImageData: TJBIG2ByteArray;
  Scanlines: TJBIG2ScanlineArray;  // un tableau d'octets par ligne, MSB en premier
  W, H: Integer;
begin
  // Scanlines, W et H décrivent une page 1 bit ; chaque ligne fait (W + 7) div 8 octets.
  Encoder := THPDFJBIG2Encoder.Create;
  try
    if Encoder.EncodeToByteArray(Scanlines, W, H, ImageData) then
      // ImageData contient maintenant un flux JBIG2 prêt pour un XObject /JBIG2Decode.
      ;
  finally
    Encoder.Free;
  end;
end;

L'activer lors de la création d'un document

Pour un usage quotidien, vous ne touchez pas directement à la classe d'encodage. HotPDF expose JBIG2 comme un choix de compression d'image sur le document. L'énumération THPDFImageCompressionType inclut icJBIG2 aux côtés des options Flate, JPEG et CCITT, et le document possède une propriété JBIG2Options de type THPDFJBIG2Options qui contient les paramètres utilisés lorsque cette compression est sélectionnée. Configurez les deux avant d'ajouter les images binaires que vous souhaitez compresser de cette manière

var
  Pdf: THotPDF;
begin
  Pdf := THotPDF.Create(nil);
  try
    Pdf.ImageCompressionType := icJBIG2;     // achemine les images 1-bit via JBIG2
    Pdf.JBIG2Options.Lossless := True;        // conserve chaque pixel
    Pdf.JBIG2Options.UseSymbolDictionary := True;
    Pdf.JBIG2Options.LossyLevel := 0;
    // Ajoutez des pages et placez vos images numérisées 1 bit ici.
  finally
    Pdf.Free;
  end;
end;

Une commodité digne d'être mentionnée est le module complémentaire DBGridHotPDFExport, qui rend un TDBGrid directement en PDF. Sa sortie est constituée en grande partie de règles et de textes binaires, de sorte qu'un document configuré pour JBIG2 conserve ces exportations compactes sans aucune manipulation supplémentaire de votre part. Deux sujets connexes sur ce blog approfondissent le flux de travail environnant. Pour savoir comment les images et les polices sont déposées lorsque vous créez des rapports, consultez la sortie de rapports avec des polices et des images dans Delphi. Lorsqu'un document compressé doit satisfaire un profil d'archivage, les règles de la validation PDF/A, PDF/X et PDF/UA dans Delphi vous indiquent quels filtres un niveau de conformité donné accepte. JBIG2 est livré dans le cadre du Composant HotPDF pour Delphi et C++Builder, à côté des API de chargement, d'édition et de chiffrement abordées ailleurs ici