Om een TDataSet of een rapport te exporteren naar PDF in Delphi, biedt losLab PDF Library twee paden. PDFlibTableExport past elke TDataSet — een FireDAC-query, a ClientDataSet, een in-memory tabel — aan tot een gepagineerde PDF-tabel, en drie bridges onder Addons overhandigen een voorbereid FastReport-, QuickReport- of ReportBuilder-rapport aan dezelfde PDF-schrijver. Beide produceren een echte PDF zonder printerdriver en zonder zichtbaar venster
De twee problemen lijken op elkaar, maar zijn dat niet. Een DBGrid of een ruw queryresultaat heeft geen eigen lay-out, dus exporteren betekent er een bedenken: kolommen, headers, pagina-einden. Een FastReport- of ReportBuilder-document heeft al een ontworpen lay-out, dus exporteren betekent het getrouw opnieuw afspelen van de tekenopdrachten van iemand anders in de PDF-ruimte. losLab PDF Library houdt deze zorgen in afzonderlijke units juist omdat de foutmodi verschillen, en de rest van dit artikel behandelt elk daarvan en waar ze niet meer betrouwbaar zijn
Hoe exporteert u een TDataSet naar PDF in Delphi?
PDFlibTableExport zet een dataset in één aanroep om in een tabel. De exporteur doorloopt de veldenlijst eenmalig, slaat binaire blob-velden automatisch over — ftBlob, ftGraphic, ftBytes en dergelijke hebben geen bruikbare celtekst — lijnt numerieke kolommen rechts uit en tekent een optionele zebrastreep over een gestylede headerband. Onder de motorkap bouwt het het raster op met CreateTable, vult cellen met SetTableCellContent en rendert met DrawTableRows, dezelfde openbare Table-API die u handmatig zou aansturen. De handige wrapper PDFlibExportDataSet configureert millimeters en een oorsprong linksboven voor u, zodat de beller alleen een pagina hoeft op te geven
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;
Het automatisch overslaan van blobs is een standaardinstelling, geen dwangbuis. Wanneer u controle per veld nodig hebt — een aangepast kolomlabel, een smallere breedte, of het weglaten van een interne sleutelkolom die geen blob is — maakt u rechtstreeks een TPDFlibTableExporter aan en koppelt u deze aan de gebeurtenis OnFieldFilter, die eenmaal per veld wordt geactiveerd en u een aanpasbare specificatie geeft. Stel Include in op False om het veld weg te laten, of stel ColumnWidth en DisplayLabel in om de standaardwaarden te overschrijven. Die hook is ook de plek waar u een brede memokolom uitsluit waarvan u niet wilt dat deze de pagina overbelast
Paginering: de tekenstatus doorgeven tussen pagina's
DrawTableRows draagt de pagineringsstatus, en het begrijpen van het contract daarvan is het hele eieren eten. U roept het aan met een eerste rij en een laatste rij; het doorgeven van een laatste rij kleiner dan 1 betekent tekenen tot het einde van de tabel, begrensd door de hoogte die u hebt opgegeven. De functie retourneert de hoogte die daadwerkelijk is getekend, en GetTableLastDrawnRow rapporteert de laatste rij die paste. Dat paar is de status die u van de ene pagina naar de volgende doorgeeft: wanneer de laatst getekende rij minder is dan het totaal, opent u een nieuwe pagina en gaat u verder vanaf de rij daarna. Er wordt niets opnieuw gemeten — het vervolg gaat precies verder waar de vorige aanroep ophield
// How the exporter continues a long table across pages
PageHeight := PDF.PageHeight;
Y := PageHeight - Options.Top;
Row := 1;
while Row <= TotalRows do
begin
DrawHeight := Y - Options.BottomMargin;
// Last row = 0 means "draw to the end", bounded by DrawHeight
PDF.DrawTableRows(TableID, Options.Left, Y, DrawHeight, Row, 0);
LastDrawn := PDF.GetTableLastDrawnRow(TableID);
if LastDrawn >= TotalRows then
Break; // the whole table fit on this page
if LastDrawn < Row then
Break; // safety: no forward progress, bail out
PDF.NewPage;
Row := LastDrawn + 1; // continue from the first undrawn row
Y := PageHeight - Options.Top;
end;
De lus is ook de plek waar de herhalende header leeft. Op elke vervolgpagina tekent de exporteur optioneel rij 1 opnieuw — de header — vóór de gegevensrijen, en gebruikt de hoogte die door die eerste DrawTableRows-aanroep is geretourneerd om de body eronder te verschuiven. Dit is de reden waarom DrawTableRows een hoogte retourneert in plaats van een coördinaat voor de volgende rij: de retourwaarde is wat u in staat stelt een opnieuw getekende header en de doorlopende body te stapelen zonder de grootte van een van beide hard te coderen
Hoe exporteert u een FastReport- of ReportBuilder-rapport naar PDF?
De rapportbridges kiezen de tegenovergestelde benadering: ze bedenken nooit een lay-out, ze spelen er een opnieuw af. Elke bridge sluit aan op zijn engine via de native export-aansluiting van de engine. PDFlibFRExport is een subklasse van TfrxCustomExportFilter en ontvangt FastReport's memo-, afbeelding-, vorm- en lijnobjecten, en vertaalt elk daarvan in een PDFlibPas-primitief. PDFlibRBDevice is een subklasse van TppFileDevice en doorloopt de DrawCommand-lijst van ReportBuilder. PDFlibQRExport kiest een heel andere, derde route, die in de volgende sectie wordt behandeld. Alle drie draaien headless, wat de belangrijkste reden is om ze op een server te gebruiken
Headless is niet gratis, en FastReport is het waarschuwende voorbeeld. TfrxReport.Export loopt via de voorbeeldpagina's, die de eigenschap ShowDialog van het filter raadplegen, en die eigenschap erft standaard True. Op een machine zonder interactief bureaublad retourneert de modale dialoog een annuleringsresultaat en mislukt de export stilletjes. Stel ShowDialog in op False voordat u Export aanroept en het rapport wordt geruisloos gerenderd. ReportBuilder heeft vergelijkbare schakelaars — AllowPrintToFile en ShowPrintDialog — en QuickReport, dat alles aanstuurt via Prepare, heeft helemaal geen dialoogonderdrukking nodig
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;
Drie engines, drie coördinatensystemen
Elke bridge voert zijn eigen coördinatenberekening uit, omdat elke engine de wereld anders meet. PDFlibFRExport behandelt posities van FastReport-objecten als pixels bij 96 PPI en schaalt met 96/25.4 om millimeters te bereiken. PDFlibRBDevice leest de tekenopdrachten van ReportBuilder in duizendsten van millimeters en deelt door 1000, en registreert zichzelf via ppRegisterDevice, dus het instellen van ppReport.DeviceType op 'PDFlibPas' en het aanroepen van Print is voldoende om de uitvoer erdoorheen te leiden. PDFlibQRExport doet helemaal geen coördinatenberekeningen: een voorbereide QuickReport-pagina is al een EMF-metafile, dus the bridge slaat elke pagina op in een stream en voedt deze aan ImportEMFFromStream, waardoor het device-context- en metafile-renderingpad van de bibliotheek elke glyph en lijn plaatst. De bridges van FastReport en ReportBuilder daarentegen sturen native vector-tekenprimitieven en native tekst uit
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;
Waar de getrouwheid ophoudt
Ken de grenzen voordat u een werkstroom aan deze bridges toevertrouwt. De dataset-exporteur laat binaire kolommen stilletjes vallen, dus een rapport dat een ingebedde afbeelding moet tonen heeft een andere aanpak nodig dan een gewone tabel. Het QuickReport-pad zet tekst, omdat het via EMF loopt, om in vector-glyphs — de pagina ziet er goed uit maar bevat geen selecteerbare of doorzoekbare tekst en geen getagde PDF-structuur voor toegankelijkheid. FastReport en ReportBuilder behouden echte tekst, maar de getypeerde handlers dekken de algemene weergaven — memo, afbeelding, vorm, lijn — en vallen terug op een bounding box of slaan de exotische over, dus een rapport dat leunt op rich text, barcodes of gradiëntvullingen zal detail verliezen. Niets hiervan is een defect; het is de eerlijke grens van een vertaallaag
Eén operationele waarschuwing is belangrijker dan de rest. Geen van de drie engines wordt geleverd binnen losLab PDF Library, en de bridges zijn bewust uitgesloten van de hoofdbuild — ze compileren alleen vanuit een project dat al FastReport VCL 6.x, QuickReport 8 of ReportBuilder 20 in zijn zoekpad heeft. De metafile- en DrawCommand-interfaces waarop ze zich richten zijn stabiel gebleven over verschillende grote engineversies, maar u bent zelf verantwoordelijk voor de versie-overeenstemming. Zorg dat dit klopt en de twee paden dekken het hele bereik, van een ad-hoc DBGrid-dump tot een ontworpen factuur. De tabelexporteur en de rapportbridges worden beide geleverd met losLab PDF Library voor Delphi en C++Builder