Fachartikel

HotPDF-Tabelle in PDF

· PDF-Software

Dieses HotPDF-Komponentenbeispiel konvertiert Datenbankeinträge in eine paginierte PDF-Tabelle. Es ist ein kompaktes Berichtserstellungsmuster: Lesen Sie Zeilen aus einem Datensatz, zeichnen Sie die Tabellenüberschrift, drucken Sie jede Zeile an einer bekannten vertikalen Position und fügen Sie eine neue Seite hinzu, wenn die aktuelle Seite voll ist.

Das Beispiel verwendet eine veraltete TTable-Datenquelle, aber die PDF-Logik ist unabhängig von dieser Datenspeicherschicht. Der gleiche Rendering-Ansatz kann mit FireDAC, ADO, In-Memory-Datensätzen, REST-Ergebnissen oder jedem Anwendungsdatensatz verwendet werden, der zeilenweise durchlaufen werden kann.

Der wichtigste Designaspekt ist die Trennung des Zeilen-Renderings von der Seitenaufbereitung. PrintRow zeichnet eine Zeile, PreparePage erstellt die wiederholte Überschrift und die Seitenmetadaten, und die Hauptschleife steuert die Paginierung. Diese Trennung macht das Beispiel einfacher anpassbar für breitere Tabellen, Gesamtsummen, Gruppierungen oder markenspezifische Berichtüberschriften.

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.

Was im Produktionsbetrieb angepasst werden muss

  • Ersetzen Sie die Beispiel-DBDEMOS-Tabelle durch den Anwendungsdatensatz oder das Abfrageergebnis.
  • Messen Sie die Spaltenbreiten anhand der erwarteten Daten anstelle der harten Kodierung jeder x-Koordinate.
  • Behandeln Sie langen Text, indem Sie Felder umbrechen oder abschneiden, bevor die Zeile gezeichnet wird.
  • Wiederholen Sie die Tabellenüberschriften auf jeder neuen Seite, damit exportierte Berichte lesbar bleiben.
  • Behalten Sie Seitenränder und Zeilenhöhe in benannten Konstanten, um Layoutänderungen vorhersehbar zu machen.

Warum das direkte Zeichnen von PDF-Tabellen nützlich ist.

Das direkte Zeichnen bietet volle Kontrolle über Schriftarten, Farben, Rahmen, Seitenumbrüche und Zusammenfassungszeilen, ohne auf einen visuellen Berichtsentwickler angewiesen zu sein. Es ist besonders nützlich für serverseitige oder Batch-Tools, bei denen das Ausgabeformat über verschiedene Maschinen hinweg stabil bleiben muss.