För att exportera en TDataSet eller en rapport till PDF i Delphi erbjuder losLab PDF Library två vägar. PDFlibTableExport anpassar valfri TDataSet — en FireDAC-fråga, en ClientDataSet, en tabell i minnet — till en sidindelad PDF-tabell, och tre bryggor under Addons skickar en förberedd FastReport-, QuickReport- eller ReportBuilder-rapport till samma PDF-skrivare. Båda skapar en riktig PDF utan skrivardrivrutin och utan synligt fönster
De två problemen ser liknande ut men är det inte. En DBGrid eller ett rått sökresultat har ingen egen layout, så att exportera det innebär att uppfinna en: kolumner, rubriker, sidbrytningar. Ett FastReport- eller ReportBuilder-dokument har redan en designad layout, så att exportera det innebär att troget spela upp någon annans ritkommandon i PDF-rymden. losLab PDF Library håller dessa delar i separata enheter just för att felscenarierna skiljer sig åt, och resten av denna artikel går igenom var och en och var de upphör att vara tillförlitliga
Hur exporterar man en TDataSet till PDF i Delphi?
PDFlibTableExport förvandlar en datamängd till en tabell i ett enda anrop. Exporteraren går igenom fältlistan en gång, hoppar automatiskt över binära blob-fält — ftBlob, ftGraphic, ftBytes och liknande har ingen användbar celltext — högerjusterar numeriska kolumner och ritar en valfri zebrarand över ett stiliserat rubrikband. Under huven bygger den rutnätet med CreateTable, fyller celler med SetTableCellContent och renderar med DrawTableRows, samma offentliga Table-API som du skulle styra manuellt. Den bekväma wrappern PDFlibExportDataSet konfigurerar millimeter och en övre vänsterstartpunkt åt dig, så att anroparen bara behöver tillhandahålla en sida
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;
Det automatiska blob-hoppet är en standardinställning, inte en tvångströja. När du behöver kontroll per fält — en anpassad kolumnetikett, en smalare bredd eller att ta bort en intern nyckelkolumn som inte är en blob — kan du skapa en TPDFlibTableExporter direkt och koppla dess OnFieldFilter-händelse, som utlöses en gång per fält och ger dig en redigerbar specifikation. Sätt Include till False för att ta bort fältet, eller ställ in ColumnWidth och DisplayLabel för att åsdosätta standardinställningarna. Denna händelse är också där du exkluderar en bred memo-kolumn som du inte vill ska blåsa upp sidan
Sidindelning: skicka rittillståndet mellan sidor
DrawTableRows bär på sidindelningstillståndet, och att förstå dess kontrakt är hela grejen. Du anropar den med en första rad och en sista rad; att skicka en sista rad under 1 innebär att rita till slutet av tabellen, begränsad av den höjd du angav. Funktionen returnerar höjden den faktiskt ritade, och GetTableLastDrawnRow rapporterar den sista raden som fick plats. Detta par är det tillstånd du skickar från en sida till nästa: när den senast ritade raden inte når upp till det totala antalet, öppnar du en ny sida och fortsätter från raden efter. Ingenting mäts om — fortsättningen plockar upp exakt där det föregående anropet slutade
// 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;
Loopen är också där det upprepade rubrikhuvudet lever. På varje fortsättningssida ritar exporteraren valfritt rad 1 igen — rubriken — före dataraderna, och den använder höjden som returneras av det första DrawTableRows-anropet för att förskjuta kroppen under den. Detta är anledningen till att DrawTableRows returnerar en höjd istället för en koordinat för nästa rad: returvärdet är det som låter dig stapla en omritad rubrik och den fortsatta kroppen utan att hårdkoda storleken på någon av dem
Hur exporterar man en FastReport- eller ReportBuilder-rapport till PDF?
Rapportbryggorna väljer motsatt tillvägagångssätt: de uppfinner aldrig en layout, de spelar upp en. Varje brygga ansluter till sin motor av motorns inbyggda exportgränssnitt. PDFlibFRExport underklassar TfrxCustomExportFilter och tar emot FastReports memo-, bild-, form- och linjeobjekt och översätter varje objekt till en PDFlibPas-primitiv. PDFlibRBDevice underklassar TppFileDevice och går igenom ReportBuilders DrawCommand-lista. PDFlibQRExport tar en helt tredje väg, vilken beskrivs i nästa avsnitt. Alla tre körs i headless-läge, vilket är hela anledningen till att använda dem på en server
Headless är inte gratis, och FastReport är ett varnande exempel. TfrxReport.Export leds via förhandsgranskningssidorna, som konsulterar filtrets ShowDialog-egenskap, och den egenskapen ärver standardvärdet True. På en maskin utan ett interaktivt skrivbord returnerar den modala dialogrutan ett avbrytande resultat och exporten misslyckas tyst. Sätt ShowDialog till False innan du anropar Export så renderas rapporten tyst. ReportBuilder har motsvarande växlar — AllowPrintToFile och ShowPrintDialog — och QuickReport, som driver allt genom Prepare, behöver inte undertrycka några dialogrutor alls
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;
Tre motorer, tre koordinatsystem
Varje brygga gör sin egen koordinataritmetik eftersom varje motor mäter världen på olika sätt. PDFlibFRExport behandlar FastReport-objektpositioner som pixlar vid 96 PPI och skalar med 96/25.4 för att nå millimeter. PDFlibRBDevice läser ReportBuilders ritkommandon i tusendels millimeter och dividerar med 1000, och den registrerar sig själv via ppRegisterDevice, så att ställa in ppReport.DeviceType till 'PDFlibPas' och anropa Print är tillräckligt för att dirigera utdata genom den. PDFlibQRExport gör ingen koordinatmatematik alls: en förberedd QuickReport-sida är redan en EMF-metafil, så bryggan sparar varje sida till en ström och skickar den till ImportEMFFromStream, vilket låter bibliotekets enhetskontext- och metafilrenderingsbana placera varje tecken och linje. FastReport- och ReportBuilder-bryggorna å andra sidan skapar inbyggda vektorritningsprimitiver och inbyggd text
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;
Var troheten slutar
Känn till gränserna innan du anförtror ett arbetsflöde till dessa bryggor. Datamängdsexporteraren hoppar över binära kolumner tyst, så en rapport som måste visa en inbäddad bild behöver ett annat tillvägagångssätt än en vanlig tabell. QuickReport-banan, eftersom den går via EMF, bakar in text i vektorfigurer — sidan ser rätt ut men innehåller ingen valbar eller sökbar text och ingen taggad PDF-struktur för tillgänglighet. FastReport och ReportBuilder behåller riktig text, men de typade hanterarna täcker de vanliga vyerna — memo, bild, form, linje — och faller tillbaka på en begränsningsruta eller hoppar över de exotiska, så en rapport som förlitar sig på rik text, streckkoder eller gradientfyllningar kommer att förlora detaljer. Inget av detta är en defekt; det är den ärliga gränsen för ett översättningslager
Ett driftsmässigt förbehåll går före resten. Ingen av de tre motorerna levereras inuti losLab PDF Library, och bryggorna är avsiktligt uteslutna från huvudbygget — de kompileras endast från ett projekt som redan har FastReport VCL 6.x, QuickReport 8 eller ReportBuilder 20 i sin sökväg. Metafil- och DrawCommand-ytorna de riktar in sig på har förblivit stabila under several stora motorversioner, men du ansvarar för att versionerna matchar. Få det rätt och de två banorna täcker hela bredden, från en ad-hoc DBGrid-dump till en designad faktura. Tabellsexporteraren och rapportbryggorna levereras båda med losLab PDF Library för Delphi och C++Builder