IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

L'impression avec Delphi


précédentsommairesuivant

III. Impression avec Delphi (épisode 3)

Nous allons poursuivre notre étude sur l'impression avec Delphi en utilisant les divers éléments vus dans les précédents épisodes pour imprimer une grille de données. Pour cela, nous allons réaliser ensemble ce projet exemple. Nous utiliserons les données de la table employee.db de la base DBDemos fournie comme exemple avec Delphi. Nous allons imprimer les données de la table en ajoutant à notre état de sortie un en-tête et un bas de page.

1) Créez un projet et sauvegardez-le à l'endroit qui vous plaira et ajouter l'unité Printers à la clause uses.

2) Sur la « Tform », disposez à votre guise un TDatasource (Datasource1), un TTable (Datas), un TDBGrid (DBGrid1) et un TButton (Imprimer). Dans l'inspecteur d'objet, définissez les propriétés :

  • Datasource1.Dataset := Datas
  • DBGrid1.DataSource := Datasource1
  • Datas.DatabaseName := DBDemos
  • Datas.Active := true

Les données doivent maintenant être affichées dans la grille.

Bien! En ce qui concerne le coté visuel, c'est terminé. Vous pouvez fermer la feuille pour vous concentrer sur le code lui-même. Pour cette fois, j'ai préféré écrire le code et vous le copirez en entier.

 
Sélectionnez
implementation

{$R *.DFM}

var

Nous avons d'abord besoin de définir quelques variables globales

 
Sélectionnez
Entete, Details, PiedPage: TRect; {pour connaître les limites des zones respectives}
 YCurr: integer; {La position en cours pour l'écriture d'une ligne}
 C: TCanvas; {Notre canvas pour imprimer}
 Li: integer; {nombre de lignes imprimées }
 Dx, Dy: integer; {Décalage par rapport aux marges}
 NPage: word = 0;  {Le numéro de page à incrémenter à chaque nouvelle page}
 TotalPages: word = 0;  {Le nombre de pages à imprimer. Pour afficher Page x / TotalPages }

{ TForm1 }

{Ajoutons 2 fonctions de conversion des millimètres en pixels (voir « Travaillez en mm sur les imprimantes ») }

 
Sélectionnez
function Millimetres2PixelsX(Millims: integer): integer;
begin
 result := MulDiv(GetDeviceCaps(Printer.Handle, LOGPIXELSX), 10 * Millims, 254);
end;

function Millimetres2PixelsY(Millims: integer): integer;
begin
 result := MulDiv(GetDeviceCaps(Printer.Handle, LOGPIXELSY), 10 * Millims, 254);
end;

Une procédure pour modifier plus facilement la fonte de notre canvas d'imprimante

 
Sélectionnez
procedure DefFonte(F: TFont; Nom: string; Taille: integer; Styl: TFontStyles);
begin
 with F do begin
   Name := Nom;
   Size := Taille;
   Style := Styl;
  end;
end;

Dans l'en-tète, nous allons imprimer en haut à gauche: La date Centré (horizontal et vertical), le nom de la table en haut à droite, le numéro de la page

 
Sélectionnez
procedure TForm1.ImprimeEnTete;
var
 S: string;
begin
 {On dessine l'entourage (si on le désire)}
 C.Rectangle(Entete);

 DefFonte(C.Font, 'Arial', 10, []);

{ Ecriture de la date en haut à gauche en n'oubliant pas les }
{ décalages par rapport aux marges }
{ Vous remarquerez que l'on aurait aussi pu utiliser DrawText avec DT_LEFT or DT_VTOP}
 C.TextOut(Entete.Left + Dx, Entete.top + Dy, DateToStr(now));
{Notre page en cours}
 S := format('Page %d / %d', [NPage, TotalPages]);
 C.TextOut(Entete.Right - C.TextWidth(S) - Dx, Entete.top + Dy, S);

{Enfin, centré au milieu de notre rectangle d'en-tète le nom de la table}
 DefFonte(C.Font, 'Times New Roman', 24, []);
 DrawText(C.handle,  PChar(Datas.TableName),  length(Datas.tablename),
            Entete,  DT_VCENTER or DT_SINGLELINE or DT_CENTER);
 {On dessine si on le désire un entourage autour de la zone Détails}
 C.Rectangle(Details);
end;

Dans le pied de page, nous allons centrer (horizontal et vertical) le nombre d'enregistrements déjà imprimés par rapport au nombre total d'enregistrements

 
Sélectionnez
procedure TForm1.ImprimePiedPage;
var
 S: string;
begin
 C.Rectangle(PiedPage);
 DefFonte(C.Font, 'Arial', 10, []);
 S := format('%d lignes imprimées sur un total de %d', [Li, Datas.RecordCount]);
 DrawText(C.handle,  PChar(S),  length(S),  PiedPage,  DT_VCENTER or DT_SINGLELINE or DT_CENTER);
end;

J'ai découpé le code en procédures distinctes pour mieux structurer le déroulement du programme

J'utilise ici une variable RealNewPage qui va nous indiquer si on doit demander une nouvelle page à Printer. Pourquoi ? Tout simplement parce que cette procédure NouvellePage va être utilisée en tout premier avant même de commencer l'impression des lignes. Donc, au premier passage, la nouvelle page existe déjà (créée par Printer.BeginDoc). Si on ne prenait pas cette précaution, nous aurions une page blanche à chaque impression lancée.

 
Sélectionnez
procedure TForm1.NouvellePage(RealNewPage: boolean);
begin
 if RealNewPage then Printer.NewPage;
 Inc(NPage);
 ImprimeEnTete;
 YCurr := Details.Top + Dy;
end;
 
Sélectionnez
procedure TForm1.ImprimerClick(Sender: TObject);
var
 HM: integer;
 T: array of integer;
 S: string;
 Bm: TBookmark;
 Bl: boolean;
 LignesParPage: word;
begin

 Li := 0;  {initialisation du nombre de lignes imprimée}
 Dx := Millimetres2PixelsX(2); {Calcul en pixels d'un décalage haut et bas de 2 mm }
                               {en cas d'impression texte près des marges}
 Dy := Millimetres2PixelsY(2);

 with Printer do begin
  Orientation := poLandScape;
  BeginDoc;

  {Hauteur de l'en-tète = 10% de la hauteur totale}
   Entete := Rect(0, 0, PageWidth, muldiv(PageHeight, 10, 100));
  {Hauteur du pied de page = 5% de la hauteur totale}
   PiedPage := Rect(0, PageHeight - muldiv(PageHeight, 5, 100), PageWidth, PageHeight);
  end;

 with Details do
  begin
   Left := 0;
   Right := Printer.PageWidth;
   {Haut de la zone détails à 10 mm sous le bas de l'en-tète}
   Top := EnTete.bottom + Millimetres2PixelsY(10);
   {Bas de la zone détails à 5 mm au-dessus du haut du pied de page}
   Bottom := PiedPage.Top - Millimetres2PixelsY(5);
  end;

Voici le schéma de notre « découpage »

Image non disponible
 
Sélectionnez
C := Printer.Canvas;  {Pour un code plus concis}

 //Calcul du nombre de ligne par page, et ainsi du nombre de page
 DefFonte(C.Font, 'Arial', 11, []);
 Hm := C.TextHeight('M');     {On calcule la hauteur d une ligne en pixels}
{On peut ainsi calculer le nombre de lignes que peut contenir la zone Details}
 LignesParPage := (Details.Bottom - Details.Top - Dy) div HM;
{Le nombre de pages sera calculé en divisant le nombre total de lignes par LignesParPage}
 TotalPages := Datas.RecordCount div LignesParPage;
{ATTENTION: on doit également vérifier si les pages seront complètes.}
{ Avec mod on peut voir s'il nous restera des lignes à imprimer en fin de document}
 Bl := (Datas.RecordCount mod LignesParPage) <> 0;
{Si on obtient un reste, il nout faut ajouter une page}
 if BL then inc(TotalPages);
{Prenons un exemple! Si la zone Details peut contenir 35 lignes et si nous avons }
{42 lignes à imprimer, nous aurons les valeurs suivantes: }
{TotalPages := 42 div 35; Donc  TotalPages  =  1 (1 page de 35 lignes)
{Bl := (42 mod 35) <> 0 ;  Donc  true puisque 42 mod 35 = 7.
{Ainsi, nous avons défini que nous aurons 1 page complète de 35 lignes et une page }
{incomplète de 7 lignes}
{Utilisons le nombre de colonnes de la DBGrid pour définir nos colonnes sur la sortie imprimée}
{Bien sur, cela n'est valable que si vous avez un nombre raisonnable de colonnes.}
{La table utilisée dans l'exemple s'y prète bien.
{Dans un autre cas, vous serez obligé de définir quelles colonnes vous voulez imprimer}
 SetLength(T, DBGrid1.columns.count);
 DefiniColonnes(T);

 NPage := 0;
{Le paramètre de NouvellePage est false, car la page est déjà créée par BeginDoc}
 NouvellePage(false);

 with Datas do begin
   Bm := GetBookmark;
   DisableControls;
   first;
   while not eof do
    begin
     {Utilisation d'une procédure pour construire la chaîne à utiliser avec TabbedTextOut}
     DefLigne(S);
     {à chaque ligne on incrémente le nombre de lignes imprimées (pour le bas de page)}
     Inc(Li);
     TabbedTextOut(C.handle, Dx, yCurr, PChar(S), length(S), high(T), T[0], 0);
     {après impression de la ligne en cours, on passe à la ligne suivante}
     next;
     {Ici, nous allons gérer la nécessité de changer de page. yCurr est notre position }
     {d'impression courante sur notre page.}
     { Puisque nous connaissons la hauteur d'une ligne, nous allons incrémenter yCurr }
     { de cette hauteur pour définir la valeur suivante en y}
     yCurr := yCurr + Hm;
     {Ici, nous devons vérifier si yCurr est toujours à l'intérieur de la zone Details,}
     { et surtout si la place est suffisante pour imprimer une ligne}
     if YCurr > (Details.bottom - Hm) then
      begin
       ImprimePiedPage;
       NouvellePage(true);
      end;

    end;
   {Enfin, si le nombre de lignes ne correspondait pas à des pages complètes,}
   { il faudrait imprimer le bas de page. On est obligé d'imprimer le pied de }
   { page seulement quand une page de détails a été créée pour connaitre le nombre }
   { de lignes déjà imprimées. Dans un autre cas, ImprimerPiedPage pourrait être }
   { ajouté à la procédure NouvellePage}
   if Bl then ImprimePiedPage;

   GotoBookmark(Bm);
   FreeBookmark(Bm);
   EnableControls;
  end;
 Printer.EndDoc;
end;

Pour définir mes colonnes ainsi que leur largeur respective, j'utilise la largeur des colonnes de la DBGrid. Connaissant la largeur de la DBGrid elle-même, je peux calculer le pourcentage de la largeur grille utilisée par chaque colonne Je reporte ce pourcentage sur la largeur de la zone Details Je n'ai pas cherché ici à savoir si mes colonnes remplissent toutes la grilles. Si vous désirez étaler au maximum vos colonnes, utilisez la somme des largeurs des colonnes utilisées plutôt que la largeur de la grille

 
Sélectionnez
procedure TForm1.DefiniColonnes(var Tb: array of integer);
var
 I, J, L: integer;
 T: array of single;
begin
 L := Details.Right - Details.Left;
 SetLength(T, high(Tb));

 with DBGrid1 do  for I := low(T) to high(T) do T[I] := L * (Columns[I].Width / Width);

 Tb[0] := Details.Left;
 for I := low(Tb) + 1 to high(Tb) do Tb[I] := Tb[I - 1] + round(T[I - 1]);

end;

procedure TForm1.DefLigne(var S: string);
var
 I: integer;
begin
 S := '';
 with Datas do for I := 0 to Fields.Count - 1 do S := S + Fields[I].AsString + #9;
 SetLength(S, Length(S) - 1);  {Pour supprimer le dernier caractère de tabulation}
end;

end.

Voilà! Votre impression de grille est terminée. J'ai utilisé TabbedTextOut pour l'exemple. Mais, pour notre grille de données, il aurait été plus présentable d'imprimer chaque valeur séparément. En effet, une présentation correcte de nos données aurait nécessité un formatage à 2 décimales des valeurs numériques ainsi qu'un alignement à droite. Dans ce cas, pour imprimer une copie plus poussée de notre grille, vous pourriez utiliser les propriétés de chaque objet TColumn de DBGrid1.Columns. Mais avec ce que vous avez appris jusqu'à maintenant, ce sera pour vous un jeu d'enfant.

Dans le prochain et dernier épisode (bientôt), nous réaliserons pour notre grille un module d'aperçu avant impression.


précédentsommairesuivant

Ce document est issu de https://www.developpez.com et reste la propriété exclusive de son auteur. La copie, modification et/ou distribution par quelque moyen que ce soit est soumise à l'obtention préalable de l'autorisation de l'auteur.