Article technique

Rendu des dégradés PDF dans Delphi : axial et radial

Un PDF dont l'en-tête comporte un arrière-plan en dégradé de style Word ou Excel, rendu sous forme d'image, s'affichait auparavant avec un rectangle blanc vide là où le mélange de couleurs devrait se trouver. Le moteur de rendu de page de HotPDF prend désormais en charge l'opérateur PDF sh (shading) pour les dégradés axiaux de type 2 et radiaux de type 3 : il évalue la fonction d'ombrage le long de l'axe du dégradé et dessine le mélange, alors que les versions précédentes ignoraient silencieusement l'opérateur et laissaient la zone blanche comme le papier. Les deux types d'ombrage qui couvrent presque tous les dégradés des documents bureautiques sont intégrés ; les types maillés (mesh) ne le sont pas, et cet article présente en toute franchise la limite de cette prise en charge

Pourquoi un dégradé PDF s'affiche-t-il sous forme de boîte blanche ?

Un dégradé PDF est une construction vectorielle, et non un bitmap que le moteur de rendu peut copier. Un dictionnaire d'ombrage (ISO 32000-1 §8.7.4.5) décrit la géométrie — un axe ou une paire de cercles dans le tableau Coords — ainsi qu'une fonction de couleur, et l'opérateur sh dessine cet ombrage sur la zone de découpe actuelle, le même modèle graphique que celui exposé par HotPDF lorsque vous dessinez des tracés et des remplissages sur le canevas d'une page. Le moteur de rendu de page de HotPDF parcourt le flux de contenu opérateur par opérateur, et tout opérateur pour lequel il ne dispose pas de chemin de code est ignoré afin que le reste de la page s'affiche tout de même. Avant la version 2.335.0, sh figurait dans cet ensemble ignoré : un en-tête ou un dégradé pleine page ne contribuait en rien à la sortie, de sorte que l'arrière-plan apparaissait blanc. Le fichier n'a jamais été corrompu. Le moteur de rendu n'avait tout simplement pas de gestionnaire pour cet opérateur spécifique, et une boîte blanche correspond exactement à ce à quoi ressemble un gestionnaire manquant

La chaîne d'évaluation des fonctions derrière un ombrage

Un ombrage ne peut pas être dessiné tant que sa fonction n'a pas été évaluée, cette mécanique a donc dû être intégrée en premier. Les ombrages axiaux de type 2 et radiaux de type 3 comportent tous deux une /Function qui associe une position paramétrique le long du dégradé à une couleur (ISO 32000-1 §7.10), et l'évaluateur de fonction de HotPDF résout le type FunctionType et l'aiguille. L'interpolation exponentielle de type 2 (§7.10.3) calcule C0 + t^N * (C1 - C0) avec verrouillage de Domain et Range ; les fonctions échantillonnées de type 0 (§7.10.2) lisent une grille d'échantillons de 8, 16 ou 32 bits et choisissent le plus proche via la table Encode, le cas traité en détail dans notre présentation des recherches de couleurs échantillonnées de type 0 ; les fonctions d'assemblage de type 3 (§7.10.4) sélectionnent une sous-fonction par le tableau Bounds et y effectuent une récursion. Les fonctions de calcul PostScript de type 4 (§7.10.5) ne sont pas évaluées — elles se dégradent proprement vers une évaluation échouée plutôt que vers une couleur erronée, conformément à la politique prudente de HotPDF sur l'ensemble du chemin de rendu. Cet évaluateur a été livré dans la version 2.334.0, une version avant l'opérateur lui-même, car le moteur d'ombrage ne vaut que ce que vaut la fonction sous-jacente

Comment HotPDF transforme-t-il un dégradé continu en pixels ?

HotPDF produit une approximation du mélange continu à l'aide d'une série finie de bandes de couleur unie. Le moteur d'ombrage parcourt l'axe du dégradé, le divise en un maximum de 64 bandes — moins lorsque l'axe est court en pixels de périphérique — évalue la fonction à la position paramétrique de chaque bande et remplit un rectangle perpendiculaire à l'axe avec une brosse unie. Le compromis réside dans le nombre de bandes par rapport à la fluidité : un plus grand nombre de bandes suit le mélange de plus près mais nécessite plus de remplissages, et 64 constitue la limite volontairement fixée. Comme ce nombre varie également avec la longueur de l'axe en pixels de périphérique, la résolution a son importance. Effectuer le rendu d'une même page à une résolution DPI supérieure allonge l'axe, de sorte qu'un dégradé court gagne en bandes et que ses rayures s'adoucissent jusqu'à atteindre la limite maximale

// The shading discretizer uses up to 64 color bands along the gradient
// axis, but never more than the axis is long in device pixels. Rendering
// the same page at a higher DPI lengthens that axis, so a short gradient
// gains bands and the banding softens until the 64-band ceiling is hit.
Thumb := Pdf.RenderLoadedPageToBitmap(PageIndex, 96);   // coarser bands
Proof := Pdf.RenderLoadedPageToBitmap(PageIndex, 300);  // up to 64 bands

Rendu d'une page ombrée vers un bitmap

Le point d'entrée public reste inchangé : RenderLoadedPageToBitmap prend un index de page basé sur 0 et une valeur de résolution DPI, renvoie un bitmap 24 bits appartenant à l'appelant, et renvoie nil en cas d'échec plutôt que de lever une exception, de sorte que la prise en charge de l'ombrage soit transparente pour votre code existant. Une page dont l'en-tête en dégradé s'affichait auparavant en blanc renvoie désormais le même bitmap avec le mélange dessiné, sans qu'aucune modification d'API ne soit requise pour l'obtenir. La seule chose à retenir est la propriété : l'appelant libère le bitmap renvoyé. L'ensemble du pipeline de rendu — interprétation du flux de contenu, glyphes intégrés et mise en cache des pages — est décrit dans le guide du rendu des pages PDF vers un bitmap dans Delphi

var
  Pdf: THotPDF;
  Bmp: TBitmap;
begin
  Pdf := THotPDF.Create(nil);
  try
    if Pdf.LoadFromFile('quarterly-report.pdf') > 0 then
    begin
      // Page 1 (zero-based) at 150 DPI. A gradient-filled header that
      // used to come back blank now carries its color blend.
      Bmp := Pdf.RenderLoadedPageToBitmap(0, 150);
      if Assigned(Bmp) then
      try
        Bmp.SaveToFile('page1.bmp');
      finally
        Bmp.Free;
      end;
    end;
  finally
    Pdf.Free;
  end;
end;

Un garde-fou simple contre les régressions découle de ce même appel. Effectuez le rendu de la page, échantillonnez un pixel à l'emplacement de la bande de dégradé et assurez-vous qu'il ne s'agit plus de la couleur blanche par défaut utilisée auparavant — un contrôle d'une seule ligne qui évite tout retour silencieux à l'ancien comportement d'absence d'opération

Bmp := Pdf.RenderLoadedPageToBitmap(0, 150);
if Assigned(Bmp) then
try
  // True when the sh operator painted; False if the region fell back to white.
  HeaderPainted := Bmp.Canvas.Pixels[Bmp.Width div 2, 8] <> clWhite;
finally
  Bmp.Free;
end;

Ce que HotPDF ne dessine pas : ombrages maillés et limites de découpe

Les quatre types d'ombrage maillé (mesh) sont hors de portée, et il convient d'en être conscient lors de la préparation des documents. Les maillages triangulaires de Gouraud de forme libre (type 4), les maillages de Gouraud en réseau (type 5), les maillages de patchs de Coons (type 6) et les maillages de patchs de produit tensoriel (type 7), définis dans l'ISO 32000-1 §8.7.4.5.5 à §8.7.4.5.8, ne font l'objet d'aucun rendu ; une page qui en utilise un s'affiche tout de même, seule cette région restant en l'état. Deux autres aspects méritent d'être énoncés clairement. Les rectangles de bande couvrent la bande de l'ombrage plutôt que d'être découpés exactement selon le tracé de découpe actuel, de sorte qu'un dégradé masqué dans une forme arrondie ou irrégulière s'affiche comme sa bande limite jusqu'à ce que la découpe précise de région soit intégrée dans une version ultérieure. HotPDF suppose également que la sortie de la fonction est en RVB ou en niveaux de gris, ainsi, un ombrage conçu en CMJN ou dans un espace colorimétrique atypique fait l'objet d'une approximation plutôt que d'une gestion des couleurs. Pour les documents que la plupart des équipes Delphi et C++Builder traitent réellement — rapports, factures, relevés et diaporamas exportés en PDF — les ombrages axiaux et radiaux couvrent l'essentiel des dégradés rencontrés, les épreuves graphiques chargées en maillages devant être traitées comme des aperçus plutôt que comme des épreuves finales

Le moteur de rendu de page de HotPDF, la prise en charge de l'opérateur sh décrite ici et l'évaluateur de fonction de type 0/2/3 sous-jacent sont livrés dans le cadre du composant HotPDF pour Delphi et C++Builder — une bibliothèque VCL native sans dépendances envers des DLL externes, couvrant la création, la modification, l'extraction de texte et le rendu de pages PDF dans un seul package