Техническая статья

Таблица HotPDF в PDF

· PDF-программы

Этот пример компонента HotPDF преобразует записи базы данных в таблицу в формате PDF с постраничной нумерацией. Это компактный шаблон генерации отчетов: чтение строк из набора данных, отрисовка заголовка таблицы, вывод каждой строки в известной вертикальной позиции и добавление новой страницы, когда текущая страница заполнена.

В примере используется устаревший источник данных TTable, но логика PDF независима от этого уровня хранения. Тот же подход рендеринга можно использовать с FireDAC, ADO, наборами данных в памяти, результатами REST или любыми данными приложения, которые можно перечислять построчно.

Ключевой момент проектирования - разделение отрисовки строк и настройки страницы. PrintRow отрисовывает одну строку, PreparePage создает повторяющийся заголовок и метаданные страницы, а основной цикл управляет постраничной нумерацией. Такое разделение упрощает адаптацию примера для более широких таблиц, итогов, группировки или фирменных заголовков отчетов.

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.

Что нужно адаптировать для использования в производственной среде.

  • Замените пример таблицы DBDEMOS на набор данных или результат запроса приложения.
  • Определите ширину столбцов на основе ожидаемых данных, а не жестко задавайте каждую координату x.
  • Обрабатывайте длинный текст, обертывая или усекая поля перед отрисовкой строки.
  • Повторяйте заголовки таблицы на каждой новой странице, чтобы экспортируемые отчеты оставались читаемыми.
  • Сохраняйте поля страницы и высоту строк в именованных константах, чтобы изменения в макете были предсказуемыми.

Почему прямое рисование таблиц в PDF полезно.

Прямое рисование обеспечивает полный контроль над шрифтами, цветами, границами, переносами на новые страницы и сводными строками, не завися от визуального конструктора отчетов. Это особенно полезно для серверных или пакетных инструментов, где формат вывода должен оставаться стабильным на разных машинах.