Artigo técnico

Tabela HotPDF para PDF

Este exemplo do componente HotPDF converte registros de banco de dados em uma tabela paginada em PDF. É um padrão compacto de geração de relatórios: lê linhas de um conjunto de dados, desenha o cabeçalho da tabela, imprime cada linha em uma posição vertical conhecida e adiciona uma nova página quando a página atual estiver cheia.

O exemplo usa uma fonte de dados TTable legada, mas a lógica do PDF é independente dessa camada de armazenamento. A mesma abordagem de renderização pode ser usada com FireDAC, ADO, conjuntos de dados na memória, resultados REST ou qualquer dado da aplicação que possa ser enumerado linha por linha.

O ponto de design chave é separar a renderização de linhas da configuração da página. PrintRow desenha uma linha, PreparePage cria o cabeçalho repetido e os metadados da página, e o loop principal controla a paginação. Essa separação torna o exemplo mais fácil de adaptar para tabelas mais largas, totais, agrupamentos ou cabeçalhos de relatório personalizados.

Estrutura do relatório

Um exportador de tabelas PDF precisa de mais do que um loop sobre registros. A saída deve manter o alinhamento das colunas, repetir o contexto em cada página e fazer a última página parecer intencional mesmo com poucas linhas. Este exemplo mostra a estrutura mínima: uma rotina para preparar a página, outra para desenhar a linha e um loop principal que controla quebras de página.

Essa estrutura dá ao código de produção pontos claros para melhorias. A rotina de cabeçalho pode adicionar logotipos, nomes do relatório, resumos de filtros e números de página. A rotina de linha pode lidar com alinhamento, quebra, fundos alternados ou formatação numérica. O loop pode adicionar grupos, totais e marcas de continuação sem reescrever o desenho.

Verificações de limite de dados

  • Decida como renderizar campos nulos antes de desenhar a linha.
  • Meça textos que podem exceder a largura da coluna e escolha quebra, corte ou reticências.
  • Mantenha valores numéricos alinhados à direita quando o relatório for usado para comparação.
  • Trate datasets vazios com uma página legível em vez de um PDF em branco.
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.

O que adaptar em produção

  • Substitua a tabela DBDEMOS de exemplo pelo conjunto de dados ou resultado da consulta da aplicação.
  • Determine as larguras das colunas com base nos dados esperados, em vez de codificar cada coordenada x.
  • Lide com textos longos, envolvendo ou truncando os campos antes de desenhar a linha.
  • Repita os cabeçalhos da tabela em cada nova página para que os relatórios exportados permaneçam legíveis.
  • Mantenha as margens da página e a altura das linhas em constantes nomeadas para tornar as alterações de layout mais previsíveis.

Por que o desenho direto de tabelas em PDF é útil.

O desenho direto oferece controle total sobre fontes, cores, bordas, quebras de página e linhas de resumo, sem depender de um designer de relatórios visuais. É especialmente útil para ferramentas do lado do servidor ou em lote, onde o formato de saída deve permanecer estável em diferentes máquinas.

A compensação é que as decisões de layout ficam explícitas. Isso costuma ser adequado para relatórios operacionais, faturas, exportações de auditoria e ferramentas batch em que a saída precisa ser estável e o desenvolvedor quer controle previsível de cada elemento impresso.

Checklist de validação

  • Gere um relatório com zero linhas, uma linha, exatamente uma página cheia e uma linha além da quebra.
  • Teste os valores mais largos esperados em cada coluna.
  • Abra o PDF gerado em mais de um visualizador para detectar diferenças de fonte ou corte.
  • Confirme que cabeçalhos repetidos, margens e números de página continuam consistentes após a localização.