Artículo técnico

Tabla HotPDF a PDF

Este ejemplo del componente HotPDF convierte registros de la base de datos en una tabla PDF paginada. Es un patrón compacto de generación de informes: lee filas de un conjunto de datos, dibuja el encabezado de la tabla, imprime cada fila en una posición vertical conocida y agrega una nueva página cuando la página actual está llena.

El ejemplo utiliza una fuente de datos TTable heredada, pero la lógica de PDF es independiente de esa capa de almacenamiento. El mismo enfoque de renderizado se puede utilizar con FireDAC, ADO, conjuntos de datos en memoria, resultados REST o cualquier dato de aplicación que se pueda enumerar fila por fila.

El punto de diseño clave es separar el renderizado de filas de la configuración de la página. PrintRow dibuja una fila, PreparePage crea el encabezado repetido y los metadatos de la página, y el bucle principal controla la paginación. Esa separación hace que el ejemplo sea más fácil de adaptar a tablas más anchas, totales, agrupaciones o encabezados de informes con marca.

Estructura del reporte

Un exportador de tablas PDF necesita más que un ciclo sobre registros. La salida debe mantener la alineación de columnas, repetir el contexto en cada página y hacer que la última página se vea intencional aunque tenga pocas filas. Este ejemplo muestra la estructura mínima: una rutina para preparar la página, otra para dibujar filas y un ciclo principal que controla los saltos.

Esa estructura da al código de producción un lugar claro para mejoras. La rutina de encabezado puede agregar logotipos, nombres del reporte, resúmenes de filtros y números de página. La rutina de fila puede manejar alineación, ajuste, fondos alternos o formato numérico. El ciclo puede agregar grupos, totales y marcas de continuación sin reescribir el dibujo.

Comprobaciones de datos

  • Decide cómo representar campos nulos antes de dibujar la fila.
  • Mide texto que pueda superar el ancho de columna y elige ajuste, recorte o elipsis.
  • Mantén los valores numéricos alineados a la derecha cuando el reporte se use para comparar.
  • Maneja datasets vacíos con una página legible en lugar de un PDF en blanco.
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.

Qué adaptar en producción.

  • Reemplace la tabla de muestra DBDEMOS con el conjunto de datos o el resultado de la consulta de la aplicación.
  • Mida los anchos de las columnas a partir de los datos esperados en lugar de codificar cada coordenada x.
  • Maneje el texto largo envolviéndolo o truncando los campos antes de dibujar la fila.
  • Repita los encabezados de la tabla en cada nueva página para que los informes exportados sigan siendo legibles.
  • Mantenga los márgenes de página y la altura de las filas en constantes con nombre para que los cambios de diseño sean predecibles.

¿Por qué es útil el dibujo directo de tablas PDF?

El dibujo directo proporciona un control total sobre las fuentes, los colores, los bordes, los saltos de página y las filas de resumen, sin depender de un diseñador de informes visual. Es especialmente útil para herramientas del lado del servidor o de tipo por lotes, donde el formato de salida debe permanecer estable en diferentes máquinas.

La compensación es que las decisiones de layout quedan explícitas. Suele ser una buena opción para reportes operativos, facturas, exportaciones de auditoría y herramientas batch donde la salida debe ser estable y el desarrollador quiere control predecible sobre cada elemento impreso.

Lista de validación

  • Genera un reporte con cero filas, una fila, exactamente una página completa y una fila después del salto de página.
  • Prueba los valores más anchos esperados en cada columna.
  • Abre el PDF generado en más de un visor para detectar diferencias de fuente o recorte.
  • Confirma que encabezados repetidos, márgenes y números de página sigan consistentes después de la localización.