L'impression avec Delphi

On a souvent posé des questions sur l'impression de document avec Delphi:

"Comment imprimer facilement avec Delphi ?"

"Comment imprimer une grille de données ?"

"Comment redéfinir l'imprimante par défaut ?"

"Comment connaître la zone non imprimable ?"

etc ....

Voici donc une petite série d'articles pour aider les nouveaux utilisateurs de ce langage ! (et pourquoi pas les autres). Pour éviter une indigestion au lecteur, j'ai voulu séparer ce tutoriel en 4 leçons distinctes. Je parlerai tout d'abord de la base avec l'unité Printers, ensuite de quelques API utilises à l'impression, puis de ce que l'on peut faire en réunissant tout ça. Enfin, je termirai avec une technique pour créer un aperçu avant impression, qui pose souvent des problèmes aux développeurs débutants.

Episode 1: Les objets TCanvas et Printer (pour les très débutants).

Episode 2: Utilisation des API sur l'imprimante

Episode 3: Que faire avec tout ça ?

Episode 4: L'aperçu avant impression!

 

Préambule

Avant de rentrer dans le vif du sujet, il convient de préciser que je vais présenter ici ce que j'appelle un mode d'impression "lignes à lignes" qui gère la création et la gestion de chaque élément de notre impression. Il existe bien sur des outils et composants "tout prèt" pour réaliser des impressions faciles. On peut ainsi trouver des outils de reporting (Générateurs d'états) ainsi que des composants qui prennent en charge une grande partie du travail de composition des pages. Un que j'affectionne particulièrement s'appelle TMWPrintObject et a été développé par James "Woody" Woodard. N'hésitez pas à lui réclamer (en anglais) la dernière version avec l'exemple qui l'accompagne, c'est en freeware et très bien fait. Pensez juste à lui dire que je vous ai parlé de lui, il verra que j'ai tenu parole :-) .

 TCanvas et Printer (épisode 1)

La première chose à dire sur le sujet est que l'impression avec Delphi est des plus simple. Tout passe par une instance unique de la classe TPrinter qui s'obtient par la fonction Printer de l'unité Printers. L'unicité de Printer est nécessaire à cause de son utilisation intensive des API Windows (de façon transparente pour l'utilisateur). Ce modèle est une possibilité d'implémentation de la Design Pattern connue sous le nom de singleton. Voici un extrait de l'unité Printers !

function Printer: TPrinter;

begin

 if FPrinter = nil then FPrinter := TPrinter.Create;

Result := FPrinter;

end;

 

finalization

 FPrinter.free;  {libération automatique dans la partie finalization}

end;

Puisque Printer est en fait une fonction de l'unité Printers, vous y avez accès directement dans votre code. On ne crèe pas un objet Printer comme on crée un objet courant par sa méthode create. Le simple ajoût de l'unité Printers dans la clause uses de votre code vous donne accès à Printer. Alors, comment l'utiliser ? On demande à Printer de commencer une impression, on envoie les commandes désirées et on termine l'impression. C'est aussi simple que cela! Sous forme de code, cela donne:

Printer.BeginDoc;

Printer.TextOut(50, 50, 'Ce que je veux imprimer');

// .....

Printer.EndDoc;

Vous connaissez plus simple ? Moi non ! Bien sur, on ne peut se contenter de cela.Voyons maintenant les propriétés les plus importantes de Printer.

Comme vous le savez, Windows permet l'installation de plusieurs imprimantes. Pour connaitre la liste des imprimantes installées sur l'ordinateur, les utilisateurs de certains langages doivent utiliser des API du style EnumFont et des procédures de callback. Avec Delphi, rien de tout ça! Les imprimantes installées sont répertoriées dans la propriété Printers (TStrings). Ainsi, pour afficher cette liste dans un composant du type TListBox, on placera le composant TListBox sur la feuille et on écrira:

 ListBox1.Items := Printer.Printers;

Vous me direz: "C'est bien d'avoir la liste, mais comment savoir quelle imprimante est utilisée actuellement par défaut et comment choisir dans mon code celle qui m'intéresse pour une impression bien définie!"

A cela je répondrai: PrinterIndex! La valeur de PrinterIndex indique l'imprimante par défaut pour windows. Donc, pour connaitre le nom de l'imprimante, il suffira de d'écrire:

ImprimanteParDefaut := Printer.Printers[Printer.PrinterIndex];

Pour utiliser une imprimante de la liste dans votre code, il suffit de modifier la valeur de PrinterIndex en lui affectant l'index correspondant dans la liste. On écrira par exemple:

Printer.PrinterIndex := 1;

Note: Pour revenir à l'imprimante par défaut de Windows, on affectera à à PrinterIndex la valeur -1 !

Bien ! Cette base étant établie, nous allons voir maintenant les propriétés PageHeight et PageWidth. Vous l'aurez compris, elles nous fournissent la hauteur et la largeur de la feuille à imprimer, définies par le choix du format de papier utilisé (A3, A4, etc ..).

PageHeight et PageWidth renvoie des valeurs en pixels. Il faut savoir que l'objet Printer ne connait QUE les pixels. Inutile de lui parler en millimètres ou en pouce ! Comment sont définies les valeurs de PageHeight et PageWidth ? Tout simplement par utilisation de l'API GetDeviceCaps (que nous utiliserons plus tard).

PageHeight := GetDeviceCaps(Printer.handle, HORZRES); {Vous n'avez pas à utiliser cette API. Printer le fait pour vous}

Mais ça, c'est Printer qui s'en charge. Vous vous contentez de lui demander le résultat. Chose intéressante, c'est que les valeurs tiennent compte des zones non imprimables sur la feuille. Selon l'imprimante utilisée, une certaine zone en bordure de feuille n'est pas accessible à la tète d'impression. PageWidth et PageHeight fournissant la largeur et la hauteur de la zone imprimable, il peut-être intéressant de connaitre la taille de cette zone. Prenons par exemple le cas d'une impression de texte qui devra toujours débuter à 5 centimètres du bord gauche de la feuille, et ce, quelle que soit l'imprimante utilisée. Il faudra alors tenir compte de cette partie plus ou moins grande. Là, nous utiliserons aussi GetDeviceCaps. Pour le détail du calcul, je vous renvoie à 'Coordonnées imprimante en millimètres' où je décris le calcul à effectuer.

La dernière propriété que nous allons voir est la plus importante à mes yeux, puisque c'est elle qui va faire 95% du travail d'impression. Je veux parler du canvas. Le Canvas représente la surface d'impression de la page. Comme pour un composant TImage, ou le Canvas d'une TForm, l'objet Printer implémente un Canvas. Voici ce que dit l'aide Delphi sur l'objet TCanvas.

TCanvas propose des propriétés, événements et méthodes qui simplifient la création d'image pour :

Spécifier le type de pinceau, de crayon et de fonte à utiliser

        Dessiner et remplir diverses formes et lignes

        Ecrire du texte

        Restituer des images graphiques

        Définir la réponse aux modifications de l'image en cours.

C'est donc sur ce canvas que nous enverrons nos commandes. En fait, nous allons utiliser ce canvas exactement comme le Canvas d'un composant TImage (ce qui nous sera très utile dans l'épisode 4 quand nous parlerons de l'aperçu avant impression).

Nous allons terminer ce premier épisode pour débutant en écrivant quelques lignes de codes. Pour cela, nous allons ajouter à notre palette les méthodes TextWidth et TextHeight du canvas. Elles renvoient respectivement la largeur et la hauteur du texte passé en paramètre.

Commencez par créer un nouveau projet et ajouter l'unité Printers à la clause uses de votre code.

Placer ensuite sur votre feuille un TListBox que nous appelerons Imprimantes ainsi que 2 boutons de commande Lister et Episode1.

Dans l'événement OnCLick du bouton Lister, écrivez le code suivant pour afficher la liste des imprimantes installées.

 // Lister les imprimantes installées

 Imprimantes.Items := Printer.Printers;

 // Sélectionner l'imprimante définie dans Windows

 Imprimantes.ItemIndex := Printer.PrinterIndex;

Dans l'événement OnClick d'Imprimantes, donnons nous le moyen de choisir l'imprimante de sortie :

 Printer.PrinterIndex := Imprimantes.ItemIndex;

Enfin, dans l'événement OnClick du bouton Episode1, nous allons créer le travail d'impression:

var

 S: String;

begin

 With Printer do

  begin

   {Démarrage de l'impression}

   BeginDoc;

 

   {Tracé de la zone imprimable}

   Canvas.Rectangle(0, 0, PageWidth, PageHeight);

 

   {Dans chaque coin de la feuille, nous allons écrire le texte 'Delphi c'est super'}

   S := 'Delphi c''est super';

   //Augmentons la taille de la fonte pour une meilleure visibilité

   Canvas.font.Size := 12;

   // Ecrivons en haut à gauche

   Canvas.TextOut(0, 0, S);

   // en haut à droite. La position X sera donc égale à 0 + Largeur_de_la_page - Largeur_du_texte_à_imprimer

   Canvas.TextOut(PageWidth - Canvas.TextWidth(S), 0, S);

   // en bas à gauche

   Canvas.TextOut(0, PageHeight - Canvas.TextHeight(S), S);

  // en bas à droite

   Canvas.TextOut(PageWidth - Canvas.TextWidth(S), PageHeight - Canvas.TextHeight(S), S);

   // Enfin, au centre de la feuille

   Canvas.TextOut(round((PageWidth - Canvas.TextWidth(S)) / 2),

                               round((PageHeight - Canvas.TextHeight(S)) / 2),

                               S);  {Ici, on doit utiliser Round car les coordonnées X,Y doivent être des integer}

 

   {Envoi des commandes à l'imprimante}

   EndDoc;

  end;

 

 Vous n'avez plus qu'à tester! Vous pouvez télécharger le code de cet exemple ICI.

 

La prochaine fois, nous parlerons plus en détail des API GetDeviceCaps, DrawText, CreateFont et TabbedTextOut.

Episode 2

Alphomega

Accueil