Teknisk artikel

HotPDF-tabell till PDF

Detta HotPDF-komponentexempel konverterar databasposter till en sidnumrerad PDF-tabell. Det är ett kompakt rapportgenereringsmönster: läs rader från en datauppsättning, rita tabellhuvudet, skriv ut varje rad på en känd vertikal position och lägg till en ny sida när den aktuella sidan är full.

Exemplet använder en äldre TTable-datakälla, men PDF-logiken är oberoende av det lagringslagret. Samma renderingsmetod kan användas med FireDAC, ADO, datauppsättningar i minnet, REST-resultat eller andra programdata som kan räknas upp rad för rad.

Den viktigaste designpunkten är att separera radrendering från sidinställningar. PrintRow ritar en rad, PreparePage skapar den upprepade rubriken och sidans metadata, och huvudslingan styr sidnumreringen. Denna separation gör det lättare att anpassa urvalet till bredare tabeller, totaler, gruppering eller varumärkesrapportrubriker.

Rapportstruktur

En PDF-tabellexportör behöver mer än en loop över poster. Utdata måste behålla kolumnjustering, upprepa sammanhang på varje sida och få sista sidan att se avsiktlig ut även med få rader. Exemplet visar minsta struktur: en rutin för siduppsättning, en för radritning och en huvudloop som styr sidbrytningar.

Strukturen ger produktionskod tydliga förbättringspunkter. En huvudrutin kan lägga till logotyper, rapportnamn, filtersammanfattningar och sidnummer. En radrutin kan hantera justering, radbrytning, växlande bakgrunder eller talformat. Loopen kan lägga till grupper, totalsummor och fortsättningsmarkeringar utan att rita om allt.

Datagränskontroller

  • Bestäm hur null-fält ska renderas innan raden ritas.
  • Mät text som kan överstiga kolumnbredden och välj radbrytning, klippning eller ellips.
  • Håll numeriska värden högerjusterade när rapporten används för jämförelse.
  • Hantera tomma dataset med en läsbar platshållarsida i stället för en blank PDF.
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.

Vad ska man anpassa i produktionen

  • Ersätt DBDEMOS-exemplet med applikationsdatauppsättningen eller frågeresultatet.
  • Mät kolumnbredder från förväntad data istället för att hårdkoda varje x-koordinat.
  • Hantera lång text genom att linda eller trunkera fält innan du ritar raden.
  • Upprepa tabellrubriker på varje ny sida så att exporterade rapporter förblir läsbara.
  • Behåll sidmarginaler och radhöjd i namngivna konstanter för att göra layoutändringar förutsägbara.

Varför direkt PDF-tabellritning är användbar

Direktritning ger full kontroll över teckensnitt, färger, ramar, sidbrytningar och sammanfattningsrader utan att vara beroende av en visuell rapportdesigner. Det är särskilt användbart för verktyg på serversidan eller i batch-stil där utdataformatet måste förbli stabilt över maskiner.

Avvägningen är att layoutbeslut blir explicita. Det passar ofta bra för drift rapporter, fakturor, audit-exporter och batchverktyg där utdata måste vara stabil och utvecklaren vill ha förutsägbar kontroll över varje tryckt element.

Valideringschecklista

  • Generera en rapport med noll rader, en rad, exakt en full sida och en rad förbi sidgränsen.
  • Testa de bredaste förväntade värdena i varje kolumn.
  • Öppna den genererade PDF-filen i mer än en viewer för att hitta font- eller klippningsskillnader.
  • Bekräfta att upprepade rubriker, marginaler och sidnummer är konsekventa efter lokalisering.