Article technique

Tableau HotPDF en PDF

· Logiciels PDF

Cet exemple de composant HotPDF convertit les enregistrements de la base de données en un tableau PDF paginé. Il s'agit d'un modèle compact de génération de rapports : lire les lignes d'un ensemble de données, dessiner l'en-tête du tableau, imprimer chaque ligne à une position verticale connue et ajouter une nouvelle page lorsque la page actuelle est pleine.

L'exemple utilise une source de données TTable héritée, mais la logique PDF est indépendante de cette couche de stockage. La même approche de rendu peut être utilisée avec FireDAC, ADO, des ensembles de données en mémoire, des résultats REST ou toute application de données qui peut être énumérée ligne par ligne.

Le point de conception clé est de séparer le rendu des lignes de la configuration de la page. PrintRow dessine une ligne, PreparePage crée l'en-tête répété et les métadonnées de la page, et la boucle principale contrôle la pagination. Cette séparation rend l'exemple plus facile à adapter à des tableaux plus larges, des totaux, des regroupements ou des en-têtes de rapports personnalisés.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
program TableDemo;
{$APPTYPE CONSOLE}
{ Reduce EXE size by disabling as much of RTTI as possible }
{$IFDEF VER210}
{$WEAKLINKRTTI ON}
{$RTTI EXPLICIT METHODS([]) PROPERTIES([]) FIELDS([])}
{$ENDIF}
 
uses
  {$IFDEF VER230}System.Classes, System.SysUtils, Vcl.Graphics, DB, DBTables,
  {$ELSE} Classes, SysUtils, Graphics, DB, DBTables,
  {$ENDIF} HPDFDoc;
 
var
  HotPDF: THotPDF;
  PageNum, VertPos: Integer;
  CustomerTable: TTable;
  Back: boolean;
 
procedure PrintRow(Position: Integer; No, Company, Addr, City: AnsiString; ShowBackground: boolean);
begin
  if ShowBackground then
  begin
    HotPDF.CurrentPage.SetRGBColor($FFF3DD);
    HotPDF.CurrentPage.Rectangle(50, Position, 520, 20);
    HotPDF.CurrentPage.Fill;
    HotPDF.CurrentPage.SetRGBColor(clBlack);
  end;
  HotPDF.CurrentPage.TextOut(70, Position, 0, No);
  HotPDF.CurrentPage.TextOut(110, Position, 0, Company);
  HotPDF.CurrentPage.TextOut(300, Position, 0, Addr);
  HotPDF.CurrentPage.TextOut(480, Position, 0, City);
end;
 
procedure PreparePage;
begin
  HotPDF.CurrentPage.SetFont('Arial', [fsItalic], 10);
  HotPDF.CurrentPage.TextOut(50, VertPos, 0, 'customer.db' + '  Page' + AnsiString(IntToStr(PageNum)));
  HotPDF.CurrentPage.TextOut(480, VertPos, 0, AnsiString(DateTimeToStr(Now)));
  HotPDF.CurrentPage.MoveTo(50, VertPos + 15);
  HotPDF.CurrentPage.LineTo(570, VertPos + 15);
  HotPDF.CurrentPage.MoveTo(50, VertPos + 45);
  HotPDF.CurrentPage.LineTo(570, VertPos + 45);
  HotPDF.CurrentPage.Stroke;
  HotPDF.CurrentPage.SetFont('Times New Roman', [fsItalic, fsBold], 12);
  HotPDF.CurrentPage.SetRGBFillColor(clNavy);
  PrintRow(VertPos + 25, 'No', 'Company', 'Addr', 'City', false);
  HotPDF.CurrentPage.SetRGBFillColor(clBlack);
  Inc(VertPos, 20);
end;
 
begin
  CustomerTable := TTable.Create(nil);
  try
    CustomerTable.DatabaseName := 'DBDEMOS';
    CustomerTable.TableName := GetCurrentDir + '\customer.db';
    CustomerTable.Active := true;
    CustomerTable.First;
    HotPDF := THotPDF.Create(nil);
    try
      HotPDF.AutoLaunch := true;
      HotPDF.FileName := 'TableDemo.pdf';
      HotPDF.PageLayout := plOneColumn;
      HotPDF.BeginDoc;
      HotPDF.CurrentPage.SetFont('Arial', [fsBold], 24);
      HotPDF.CurrentPage.TextOut(200, 20, 0, 'Customer report'); // Print PDF header
      PageNum := 1;
      VertPos := 60;
      PreparePage; // Table header
      Back := true;
      while (not(CustomerTable.Eof)) do
      begin
        Back := not(Back);
        if (VertPos > 700) then // If end of page then
        begin
          HotPDF.AddPage; // Add page, draw and print
          Inc(PageNum);   // table header and set
          VertPos := 60;  // new print position
          PreparePage;
        end;
        PrintRow(VertPos + 25, // print row from the current position
          AnsiString(CustomerTable.FieldValues['CustNo']), AnsiString(CustomerTable.FieldValues['Company']),
          AnsiString(CustomerTable.FieldValues['Addr1']), AnsiString(CustomerTable.FieldValues['City']), Back);
        Inc(VertPos, 20);
        CustomerTable.Next;
      end;
      HotPDF.EndDoc;
    finally
      HotPDF.Free;
    end;
  finally
    CustomerTable.Free;
  end;
 
end.

Ce qu'il faut adapter en production.

  • Remplacez le tableau DBDEMOS de l'exemple par l'ensemble de données ou le résultat de la requête de l'application.
  • Déterminez les largeurs des colonnes à partir des données attendues au lieu de coder en dur chaque coordonnée x.
  • Gérez les longs textes en les retournant à la ligne ou en tronquant les champs avant de dessiner la ligne.
  • Répétez les en-têtes de tableau sur chaque nouvelle page pour que les rapports exportés restent lisibles.
  • Conservez les marges de page et la hauteur des lignes dans des constantes nommées pour rendre les modifications de mise en page plus prévisibles.

Pourquoi le dessin direct des tableaux PDF est utile.

Le dessin direct offre un contrôle total sur les polices, les couleurs, les bordures, les sauts de page et les lignes de résumé, sans dépendre d'un concepteur de rapports visuel. Il est particulièrement utile pour les outils côté serveur ou de type batch où le format de sortie doit rester stable sur différentes machines.