Para exportar un TDataSet o un reporte a PDF en Delphi, losLab PDF Library ofrece dos caminos. PDFlibTableExport adapta cualquier TDataSet —una consulta FireDAC, un ClientDataSet, una tabla en memoria— en una tabla PDF paginada, y tres puentes bajo Addons entregan un reporte de FastReport, QuickReport o ReportBuilder preparado al mismo generador de PDF. Ambos producen un PDF real sin controlador de impresora y sin ventana visible
Los dos problemas parecen similares y no lo son. Un DBGrid o el resultado de una consulta sin procesar no tienen un diseño propio, por lo que exportarlo significa inventar uno: columnas, encabezados, saltos de página. Un documento de FastReport o ReportBuilder ya contiene un diseño diseñado, por lo que exportarlo significa reproducir fielmente los comandos de dibujo de otro en el espacio PDF. losLab PDF Library mantiene estas preocupaciones en unidades separadas precisamente porque los modos de falla difieren, y el resto de este artículo analiza cada uno y dónde deja de ser confiable
¿Cómo exportar un TDataSet a PDF en Delphi?
PDFlibTableExport convierte un conjunto de datos en una tabla en una sola llamada. El exportador recorre la lista de campos una vez, omite los campos blob binarios automáticamente (ftBlob, ftGraphic, ftBytes y sus similares no tienen texto de celda útil), alinea a la derecha las columnas numéricas y pinta una franja de cebra opcional sobre una banda de encabezado con estilo. Internamente, construye la cuadrícula con CreateTable, llena las celdas con SetTableCellContent y renderiza con DrawTableRows, la misma API pública de tabla que manejaría manualmente. El contenedor de conveniencia PDFlibExportDataSet configura los milímetros y un origen en la esquina superior izquierda por usted, por lo que el llamador solo proporciona una página
uses
Data.DB, FireDAC.Comp.Client, FireDAC.Stan.StorageBin,
PDFlibrary, PDFlibTableExport;
var
MemTable: TFDMemTable;
PDF: TPDFlib;
Options: TPDFlibTableExportOptions;
begin
MemTable := TFDMemTable.Create(nil);
PDF := TPDFlib.Create;
try
MemTable.LoadFromFile('customer.FDS'); // any TDataSet works here
PDF.SetOrigin(1);
PDF.SetMeasurementUnits(1); // millimetres
PDF.SetPageSize('A4');
PDF.AddStandardFont(4);
Options := DefaultTableExportOptions;
Options.Title := 'Customers';
Options.ColumnWidth := 32; // 11 fields fit on A4 at 32 mm
Options.RepeatHeader := True; // redraw the header on each page
PDFlibExportDataSet(PDF, MemTable, 'customers.pdf', Options);
finally
PDF.Free;
MemTable.Free;
end;
end;
La omisión automática de blobs es un comportamiento predeterminado, no una camisa de fuerza. Cuando necesite control por campo (una etiqueta de columna personalizada, un ancho más estrecho o descartar una columna de clave interna que no sea un blob), construya un TPDFlibTableExporter directamente y conecte su evento OnFieldFilter, que se activa una vez por campo y le entrega una especificación mutable. Establezca Include en False para descartar el campo, o establezca ColumnWidth y DisplayLabel para anular los valores predeterminados. Ese gancho también es donde se excluye una columna de memo ancha que no desea que expanda la página
Paginación: pasar el estado de dibujo entre páginas
DrawTableRows lleva el estado de paginación, y entender su contrato lo es todo. Se llama con una primera fila y una última fila; pasar una última fila inferior a 1 significa dibujar hasta el final de la tabla, limitado por la altura proporcionada. La función devuelve la altura que realmente dibujó, y GetTableLastDrawnRow informa la última fila que cupo. Ese par es el estado que se pasa de una página a la siguiente: cuando la última fila dibujada no llega al total, abre una nueva página y reanuda desde la fila siguiente. No se vuelve a medir nada: la continuación se reanuda exactamente donde se quedó la llamada anterior
El bucle también es donde vive el encabezado repetido. En cada página de continuación, el exportador dibuja opcionalmente la fila 1 nuevamente (el encabezado) antes de las filas de datos, y utiliza la altura devuelta por esa primera llamada a DrawTableRows para desplazar el cuerpo debajo de ella. Esta es la razón por la que DrawTableRows devuelve una altura en lugar de una coordenada de la siguiente fila: el valor de retorno es lo que permite apilar un encabezado vuelto a dibujar y el cuerpo continuo sin codificar de forma fija el tamaño de ninguno de los dos
¿Cómo exportar un reporte de FastReport o ReportBuilder a PDF?
Los puentes de reportes adoptan el enfoque opuesto: nunca inventan un diseño, lo reproducen. Cada puente se conecta a su motor en el punto de exportación nativo del motor. PDFlibFRExport subclasifica TfrxCustomExportFilter y recibe los objetos de memo, imagen, forma y línea de FastReport, traduciendo cada uno en una primitiva de PDFlibPas. PDFlibRBDevice subclasifica TppFileDevice y recorre la lista DrawCommand de ReportBuilder. PDFlibQRExport toma una tercera ruta completamente diferente, que cubre la siguiente sección. Los tres se ejecutan de forma headless, que es la única razón para recurrir a ellos en un servidor
La ejecución sin interfaz (headless) no es gratuita, y FastReport es el ejemplo de advertencia. TfrxReport.Export se enruta a través de las páginas de vista previa, que consultan la propiedad ShowDialog del filtro, y esa propiedad hereda un valor predeterminado de True. En una máquina sin escritorio interactivo, el diálogo modal devuelve un resultado de cancelación y la exportación falla silenciosamente. Establezca ShowDialog en False antes de llamar a Export y el reporte se renderizará de forma silenciosa. ReportBuilder tiene interruptores paralelos — AllowPrintToFile y ShowPrintDialog — y QuickReport, que maneja todo a través de Prepare, no necesita ninguna supresión de diálogo en absoluto
var
Exporter: TPDFlibFRExport;
begin
Report.PrepareReport; // build the pages first
Exporter := TPDFlibFRExport.Create(nil);
try
Exporter.FileName := 'invoice.pdf';
Exporter.ShowDialog := False; // headless: skip the modal, no cancel
Report.Export(Exporter);
finally
Exporter.Free;
end;
end;
Tres motores, tres sistemas de coordenadas
Cada puente realiza su propia aritmética de coordenadas, porque cada motor mide el mundo de manera diferente. PDFlibFRExport trata las posiciones de los objetos de FastReport como píxeles a 96 PPI y escala por 96/25.4 para llegar a milímetros. PDFlibRBDevice lee los comandos de dibujo de ReportBuilder en milésimas de milímetro y los divide por 1000, y se registra a sí mismo a través de ppRegisterDevice, por lo que establecer ppReport.DeviceType en 'PDFlibPas' y llamar a Print es suficiente para enrutar la salida a través de él. PDFlibQRExport no hace ninguna matemática de coordenadas: una página de QuickReport preparada ya es un metarchivo EMF, por lo que el puente guarda cada página en un flujo de datos (stream) y la envía a ImportEMFFromStream, permitiendo que la ruta de renderizado de contexto de dispositivo y metarchivos de la biblioteca coloque cada glifo y regla. Los puentes de FastReport y ReportBuilder, por el contrario, emiten primitivas de dibujo vectorial nativas y texto nativo
var
PDF: TPDFlib;
begin
PDF := TPDFlib.Create;
try
PDF.SetOrigin(1);
// Prepares the report and appends every page in one call; the bridge
// turns each page's EMF into PDF through ImportEMFFromStream.
PDFlibQRExportReport(PDF, QuickRep1, 'ledger.pdf');
finally
PDF.Free;
end;
end;
Dónde termina la fidelidad
Conozca los límites antes de confiar un flujo de trabajo a estos puentes. El exportador de conjuntos de datos descarta las columnas binarias de forma silenciosa, por lo que un reporte que deba mostrar una imagen incrustada necesita un enfoque diferente al de una tabla simple. La ruta de QuickReport, debido a que pasa por EMF, incrusta el texto en glifos vectoriales: la página se ve bien pero no contiene texto seleccionable ni buscable ni una estructura PDF etiquetada para accesibilidad. FastReport y ReportBuilder conservan texto real, pero los controladores tipados cubren las vistas comunes (memo, imagen, forma, línea) y recurren a un cuadro delimitador u omiten las exóticas, por lo que un reporte que dependa de texto enriquecido, códigos de barras o rellenos degradados perderá detalles. Nada de esto es un defecto; es el límite honesto de una capa de traducción
Una advertencia operativa supera al resto. Ninguno de los tres motores se incluye dentro de losLab PDF Library, y los puentes se excluyen deliberadamente de la compilación principal: solo se compilan desde un proyecto que ya tiene FastReport VCL 6.x, QuickReport 8 o ReportBuilder 20 en su ruta de búsqueda. Las superficies de metarchivo y DrawCommand a las que se dirigen se han mantenido estables en varias versiones principales del motor, pero usted es responsable de la coincidencia de versiones. Consiga eso correctamente y las dos rutas cubren todo el espectro, desde un volcado DBGrid ad-hoc hasta una factura diseñada. El exportador de tablas y los puentes de reportes se incluyen con losLab PDF Library para Delphi y C++Builder