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 informe
Un exportador de tablas PDF necesita más que un bucle 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 parezca intencionada aunque contenga pocas filas. Este ejemplo muestra la estructura mínima: una rutina para preparar la página, otra para dibujar filas y un bucle principal que controla los saltos.
Esa estructura da al código de producción un lugar claro para mejoras. La rutina de cabecera puede añadir logotipos, nombres de informe, 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 bucle puede añadir grupos, totales y marcas de continuación sin reescribir el dibujo.
Comprobaciones de datos
- Decida cómo representar campos nulos antes de dibujar la fila.
- Mida texto que pueda superar el ancho de columna y elija ajuste, recorte o elipsis.
- Mantenga los valores numéricos alineados a la derecha cuando el informe se use para comparar.
- Gestione 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 contrapartida es que las decisiones de maquetación son explícitas. Suele ser adecuado para informes operativos, facturas, exportaciones de auditoría y herramientas por lotes donde la salida debe ser estable y el desarrollador quiere control predecible de cada elemento impreso.
Lista de validación
- Genere un informe con cero filas, una fila, exactamente una página completa y una fila más allá del salto de página.
- Pruebe los valores más anchos previstos en cada columna.
- Abra el PDF generado en más de un visor para detectar diferencias de fuente o recorte.
- Confirme que cabeceras repetidas, márgenes y números de página sigan siendo consistentes tras la localización.