Article technique

Imprimer un PDF chargé dans Delphi avec HotPDF et TPrinter

HotPDF imprime un PDF chargé sur une imprimante physique, sur n'importe quel contexte de périphérique Windows ou dans le presse-papiers via three appels : PrintLoadedDocument pilote un TPrinter VCL, RenderLoadedPageToDC dessine une page directement dans n'importe quel HDC, et RenderLoadedPageToClipboard copie une page sous forme de bitmap ou de métafichier vectoriel. HotPDF est un composant PDF VCL natif pour Delphi et C++Builder, qui a intégré ce chemin d'impression de premier ordre dans la version 2.331.0, puis l'a perfectionné dans la version 2.348.0, vous évitant ainsi d'avoir à coder manuellement une boucle TPrinter autour d'un moteur de rendu bitmap. Ces trois appels dessinent le contenu vectoriel de la page directement là où cela est nécessaire, pour qu'une impression reste aussi nette que ce que l'imprimante peut générer

Pourquoi dessiner une page PDF directement sur l'imprimante plutôt que sous forme de bitmap ?

Une page PDF est un programme d'opérateurs de dessin vectoriel — tracés, texte et images exécutés selon le modèle graphique ISO 32000-1 — et non une grille de pixels. Le moyen évident d'en imprimer une consiste à tramer la page sous forme de bitmap puis à transférer ce bitmap sur le canevas de l'imprimante. Bien que cela fonctionne, cela gâche le principal atout d'une imprimante. Une imprimante laser moderne offre une résolution de 600 ou 1200 PPP (DPI) ; un bitmap tramé à 150 ou 300 PPP puis étiré pour s'adapter à la page apparaît flou, avec des contours de texte dentelés et une interpolation visible. RenderLoadedPageToDC adopte une meilleure approche : il rejoue le flux de contenu de la page sous forme d'appels GDI émis directement dans le contexte de périphérique cible, de sorte que le texte vectoriel et les dessins au trait parviennent à l'imprimante sous forme de vecteurs et ne soient tramés qu'une seule fois, par l'imprimante, à sa propre résolution

Comment imprimer un PDF chargé sur une imprimante physique dans Delphi ?

HotPDF imprime un PDF chargé avec PrintLoadedDocument(Printer, Options), qui prend un TPrinter VCL assigné et un enregistrement THPDFPrintOptions, imprime les pages sélectionnées, et renvoie le nombre de pages transmises. Définissez d'abord Printer.PrinterIndex pour choisir un périphérique, renseignez un enregistrement d'options — la fonction de classe THPDFPrintOptions.Default fournit des valeurs judicieuses — et appelez la méthode. La bibliothèque ouvre la tâche avec Printer.BeginDoc, parcourt la plage de pages en émettant une instruction Printer.NewPage entre chaque page, et la ferme avec Printer.EndDoc, vous évitant ainsi de devoir gérer le cycle de vie de la tâche d'impression manuellement

uses Printers;

var
  Pdf: THotPDF;
  Opts: THPDFPrintOptions;
begin
  Pdf := THotPDF.Create(nil);
  try
    if Pdf.LoadFromFile('invoice.pdf') > 0 then
    begin
      Printer.PrinterIndex := -1;         // -1 selects the default printer
      Opts := THPDFPrintOptions.Default;  // all pages, 300 DPI, FitPage, colour
      Opts.PageRange := '1,3-5';          // one-based; empty prints every page
      Opts.Copies := 2;
      Pdf.PrintLoadedDocument(Printer, Opts);
    end;
  finally
    Pdf.Free;
  end;
end;

La chaîne PageRange est basée sur 1 et se lit de la même façon qu'une boîte de dialogue d'impression : '1,3-5' imprime la page 1 et les pages 3 à 5, et une chaîne vide imprime l'ensemble du document. Copies correspond directement à Printer.Copies. Soyez toutefois réaliste concernant Collate : la VCL gérant l'assemblage de manière incohérente d'une version de Delphi à l'autre, HotPDF définit le nombre de copies et laisse la gestion de l'assemblage au pilote d'imprimante par défaut. Si vous avez besoin d'un ordre d'assemblage garanti, configurez-le sur le pilote via TPrinterSetupDialog plutôt que de vous fier à cet indicateur

Comment la résolution DPI et FitPage déterminent-elles l'échelle d'impression ?

FitPage et DPI interagissent d'une manière qu'il convient de comprendre avant de déployer votre application. Lorsque FitPage est à True — la valeur par défaut — HotPDF lit la zone MediaBox de chaque page, la met à l'échelle selon les dimensions Printer.PageWidth et Printer.PageHeight en préservant le rapport d'aspect, et effectue le rendu à l'échelle permettant à la page de remplir le papier. Dans ce mode, Options.DPI est de fait supplanté par l'échelle d'adaptation, la sortie devant correspondre aux dimensions physiques de la page de l'imprimante. Lorsque FitPage est à False, HotPDF effectue le rendu de chaque page à l'échelle 1:1 à la résolution exacte de Options.DPI, en mappant 72 unités PDF pour des pixels de périphérique DPI, ce qu'il convient de faire lorsque la page source et le papier possèdent déjà les mêmes dimensions et que vous exigez une résolution exacte. Optez pour FitPage lorsque la taille de la page source et celle du bac d'alimentation diffèrent ; choisissez le rapport 1:1 lorsqu'elles correspondent et que vous souhaitez une mise à l'échelle déterministe

Deux options ajoutées dans la version 2.348.0 corrigent les impressions qui paraissent presque correctes. Chaque imprimante physique possède une marge matérielle — une bordure non imprimable que le matériel ne peut atteindre — et par défaut, l'origine de la page se situe à l'intérieur, de sorte qu'une page pleine page perde une bande sur deux de ses côtés. Définissez IgnoreHardMargin sur True et HotPDF interrogera les valeurs PHYSICALOFFSETX et PHYSICALOFFSETY de l'imprimante, puis décalera l'origine du périphérique pour annuler ce décalage afin que l'origine de la page corresponde au coin réel du papier. La seconde option, AutoRotate, est activée par défaut : elle lit la zone MediaBox de chaque page et, lorsqu'une page est plus large que haute, bascule l'imprimante en mode paysage de sorte qu'une page large s'imprime sans être tronquée au lieu d'être recadrée au format portrait. AutoRotate s'efface dès lors que vous définissez Orientation explicitement, un choix d'orientation délibéré l'emportant toujours

Comment effectuer le rendu d'une page PDF dans n'importe quel contexte de périphérique ?

RenderLoadedPageToDC(PageIndex, DC, DPI, W, H) constitue la primitive sous-jacente à la boucle d'impression, et s'avère utile en soi. PrintLoadedDocument l'appelle une fois par page sur le canevas de l'imprimante, mais la cible peut être n'importe quel HDC Windows : un DC mémoire hors écran, le canevas d'un composant au sein d'une liseuse personnalisée ou une surface d'aperçu avant impression. PageIndex est basé sur 0, W et H représentent les unités de périphérique de la surface cible, et le moteur de rendu parcourt les /Contents de la page et émet des appels GDI dans votre DC. Un comportement délibéré mérite d'être souligné : il ne dessine aucun remplissage d'arrière-plan. L'appelant est propriétaire de la surface, de sorte que si vous souhaitez du papier blanc derrière la page, vous devez remplir le DC vous-même avant le rendu, ce qu'il convient de faire pour superposer une page sur un canevas existant si vous le souhaitez

// Draw page 1 into an off-screen 24-bit surface at 150 DPI
var
  Surface: TBitmap;
begin
  Surface := TBitmap.Create;
  try
    Surface.PixelFormat := pf24bit;
    Surface.SetSize(1275, 1650);           // 8.5 x 11 in at 150 DPI
    Surface.Canvas.Brush.Color := clWhite;
    Surface.Canvas.FillRect(Surface.Canvas.ClipRect);  // caller paints the background
    Pdf.RenderLoadedPageToDC(0, Surface.Canvas.Handle, 150, Surface.Width, Surface.Height);
    // Surface now holds the composited page, ready to blit or save
  finally
    Surface.Free;
  end;
end;

Si vous avez simplement besoin d'un TBitmap finalisé — une vignette, un export d'image, un aperçu à mettre en cache — optez plutôt pour RenderLoadedPageToBitmap, présenté dans l'article connexe sur le rendu de pages PDF vers un bitmap. La différence réside dans la propriété de la surface : l'appel bitmap alloue et renvoie un tampon de pixels, tandis que RenderLoadedPageToDC dessine dans un contexte de périphérique que vous possédez déjà, ce qu'un canevas d'imprimante ou un gestionnaire de dessin vous transmet. Les deux partagent le même interprète de flux de contenu et le même moteur de glyphes intégrés, assurant ainsi la reproduction des contours intégrés exacts dans les deux cas

Comment coller une page PDF dans Word ou dans un éditeur d'images ?

RenderLoadedPageToClipboard(PageIndex, DPI, AsMetafile) place une page dans le presse-papiers Windows, prête à être collée dans Word, PowerPoint, un e-mail ou un éditeur d'images. L'indicateur AsMetafile sélectionne le format, et ce choix s'avère plus important qu'il n'y paraît. Transmettez False et HotPDF y placera un bitmap de périphérique 24 bits (CF_BITMAP) rendu à la résolution de votre DPI — une grille fixe de pixels qui se colle partout mais se pixelise lorsque le destinataire l'agrandit. Transmettez True et il y placera un métafichier amélioré (CF_ENHMETAFILE) dans lequel le texte et les éléments vectoriels de la page transitent sous forme de commandes de dessin GDI, garantissant la netteté du résultat collé quel que soit le zoom et son adaptation aux dimensions imposées par le document cible

// Copy page 2 as a scalable vector metafile for pasting into Word
Pdf.RenderLoadedPageToClipboard(1, 300, True);

// Copy the same page as a fixed 300-DPI bitmap instead
Pdf.RenderLoadedPageToClipboard(1, 300, False);

La règle générale est simple. Si un utilisateur doit coller la page dans un document et risque de la redimensionner — une figure dans un rapport, une clause insérée dans un projet de contrat — optez pour le métafichier, car le texte vectoriel s'imprime et s'agrandit proprement. Si la destination ne prend en charge que les données trames (raster), ou si vous souhaitez spécifiquement un cliché figé à une résolution connue, utilisez le bitmap. Extraire les mots d'une page plutôt qu'une image de ceux-ci constitue une tâche totalement différente, gérée par l'API d'extraction de texte, qui renvoie les caractères et leurs positions au lieu d'une image rendue

Où s'arrête le chemin d'impression

Tout ce qui est présenté sur cette page est réservé à Windows, et il convient de le préciser. PrintLoadedDocument, RenderLoadedPageToDC et RenderLoadedPageToClipboard reposent sur TPrinter, le modèle de contexte de périphérique GDI, le presse-papiers Windows et les métafichiers améliorés — autant de structures Win32 et Win64. Sur d'autres plateformes, le reste de HotPDF continue de créer, de modifier et de lire les PDF, mais ce traitement d'impression et de presse-papiers basé sur GDI n'est pas disponible. Le moteur de rendu cible également le sous-ensemble de documents courants composé de texte, de tracés et d'images, de sorte que les épreuves graphiques chargées en dégradés et les couleurs gérées par profil ICC s'impriment sous forme d'aperçu fidèle plutôt que d'épreuve colorimétrique, la même limite que celle que présente le moteur de rendu bitmap

Pour l'impression par lots sur de nombreux fichiers, chargez et imprimez chaque document l'un après l'autre et surveillez la consommation de mémoire : PrintLoadedDocument effectue le rendu d'une page à la fois, mais le graphe du document chargé lui-même réside en mémoire pendant toute la durée du traitement. Lorsque vous gérez une file d'attente d'impression sur des connexes de fichiers volumineux, l'API de diffusion Direct File pour les flux de grands PDF s'associe idéalement avec le chemin d'impression — divisez, fusionnez ou sélectionnez d'abord sur le disque les pages requises, puis ne chargez que le résultat allégé pour l'impression. C'est ainsi que PrintLoadedDocument, RenderLoadedPageToDC et RenderLoadedPageToClipboard 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, le rendu de PDF, et désormais l'impression directe vers TPrinter, tout contexte de périphérique ou le presse-papiers