For å eksportere en TDataSet eller en rapport til PDF i Delphi, tilbyr losLab PDF Library to metoder. PDFlibTableExport tilpasser enhver TDataSet — en FireDAC-spørring, et ClientDataSet, en tabell i minnet — til en paginert PDF-tabell, og tre broer under Addons overleverer en klargjort FastReport-, QuickReport- eller ReportBuilder-rapport til den samme PDF-skriveren. Begge produserer en ekte PDF uten skriverdriver og uten synlig vindu
De to problemene ser like ut, men er det ikke. En DBGrid eller et rått spørringsresultat har ingen egen layout, så å eksportere det betyr å finne opp en: kolonner, overskrifter, sideskift. Et FastReport- eller ReportBuilder-dokument har allerede en utformet layout, så å eksportere det betyr å trofast gjengi noen andres tegnekommandoer i PDF-rommet. losLab PDF Library holder disse oppgavene i separate enheter nettopp fordi feilmodusene er forskjellige, og resten av denne artikkelen går gjennom hver enkelt og hvor de slutter å være pålitelige
Hvordan eksporterer du en TDataSet til PDF i Delphi?
PDFlibTableExport gjør om et datasett til en tabell i ett enkelt kall. Eksporteren går gjennom feltlisten én gang, hopper over binære blob-felt automatisk — ftBlob, ftGraphic, ftBytes og lignende har ingen nyttig celletekst — høyrejusterer numeriske kolonner og tegner en valgfri sebrastripe over et stilisert overskriftsbånd. Under panseret bygger den rutenettet med CreateTable, fyller celler med SetTableCellContent og rendrer med DrawTableRows, det samme offentlige Table API-et som du ville styrt manuelt. Den praktiske wrapperen PDFlibExportDataSet konfigurerer millimeter og et topp-venstre-origo for deg, slik at oppkalleren bare trenger å oppgi en side
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 automatiske hoppet over blob-felt er en standard, ikke en tvangstrøye. Når du trenger kontroll per felt — en tilpasset kolonneetikett, en smalere bredde, eller å utelate en intern nøkkelkolonne som ikke er en blob — oppretter du en TPDFlibTableExporter direkte og kobler deg til dens OnFieldFilter-hendelse, som utløses én gang per felt og gir deg en endringsbar spesifikasjon. Sett Include to False for å utelate feltet, eller angi ColumnWidth og DisplayLabel for å overstyre standardverdiene. Denne hendelsen er også der du ekskluderer en bred memo-kolonne som du ikke vil at skal blåse opp siden
Paginering: overføring av tegnetilstand mellom sider
DrawTableRows bærer pagineringstilstanden, og å forstå dens kontrakt er hele poenget. Du kaller den med en første rad og en siste rad; å sende en siste rad under 1 betyr å tegne til slutten av tabellen, begrenset av høyden du oppga. Funksjonen returnerer høyden den faktisk tegnet, og GetTableLastDrawnRow rapporterer den siste raden som fikk plass. Dette paret er tilstanden du overfører fra én side til den neste: når den siste tegnede raden er mindre enn totalen, åpner du en ny side og fortsetter fra raden etter. Ingenting måles på nytt — fortsettelsen starter akkurat der det forrige kallet slapp
// 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;
Løkken er også der det gjentakende overskriftshodet lever. På hver fortsettelsesside tegner eksporteren valgfritt rad 1 igjen — overskriften — før dataradene, og den bruker høyden returnert av det første DrawTableRows-kallet til å forskyve hoveddelen under den. Dette er grunnen til at DrawTableRows returnerer en høyde i stedet for en koordinat for neste rad: returverdien er det som lar deg stable en nytegnet overskrift og den fortsettende hoveddelen uten å hardkode størrelsen på noen av dem
Hvordan eksporterer du en FastReport- eller ReportBuilder-rapport til PDF?
Rapportbroene bruker motsatt tilnærming: de finner aldri på en layout, de gjengir en. Hver bro kobles til sin motor ved motorens opprinnelige eksportgrensesnitt. PDFlibFRExport underklasser TfrxCustomExportFilter og mottar FastReports memo-, bilde-, figur- og linjeobjekter, og oversetter hvert av dem til en PDFlibPas-primitiv. PDFlibRBDevice underklasser TppFileDevice og går gjennom ReportBuilders DrawCommand-liste. PDFlibQRExport tar en helt tredje vei, som dekkes i neste avsnitt. Alle tre kjører hodeløst, noe som er hele grunnen til å bruke dem på en server
Hodeløs kjøring er ikke gratis, og FastReport er et advarende eksempel. TfrxReport.Export ruter gjennom forhåndsvisningssidene, som konsulterer filterets ShowDialog-egenskap, og den egenskaper arver en standardverdi på True. På en maskin uten interaktivt skrivebord returnerer den modale dialogboksen et avbrutt-resultat, og eksporten mislykkes i det stille. Sett ShowDialog til False før du kaller Export, og rapporten rendres lydløst. ReportBuilder har de parallelle bryterne — AllowPrintToFile og ShowPrintDialog — og QuickReport, som driver alt gjennom Prepare, trenger ingen dialogundertrykkelse i det hele tatt
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 koordinatsystemer
Hver bro gjør sin egen koordinataritmetikk, fordi hver motor måler verden forskjellig. PDFlibFRExport behandler FastReport-objektposisjoner som piksler ved 96 PPI og skalerer med 96/25,4 for å nå millimeter. PDFlibRBDevice leser ReportBuilders tegnekommandoer i tusendels millimeter og deler på 1000, og den registrerer seg selv gjennom ppRegisterDevice, så å sette ppReport.DeviceType til 'PDFlibPas' og kalle Print er nok til å rute utdataene gjennom den. PDFlibQRExport gjør ingen koordinatmatematikk i det hele tatt: en klargjort QuickReport-side er allerede en EMF-metafil, så broen lagrer hver side til en strøm og sender den til ImportEMFFromStream, og lar bibliotekets enhetskontekst- og metafilrendrings-bane plassere hver glyf og linje. FastReport- og ReportBuilder-broene sender derimot ut native vektortegningsprimitiver og native tekst
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;
Hvor nøyaktigheten slutter
Kjenn grensene før du overlater en arbeidsflyt til disse broene. Datasetteksporteren utelater binære kolonner i det stille, så en rapport som må vise et innebygd bilde trenger en annen tilnærming enn en ren tabell. QuickReport-banen, fordi den går via EMF, baker tekst inn i vektorglyfer — siden ser riktig ut, men inneholder ingen velgbar eller søkbar tekst og ingen tagget PDF-struktur for universell utforming. FastReport og ReportBuilder beholder ekte tekst, men de spesifikke håndtererene dekker de vanlige visningene — memo, bilde, figur, linje — og faller tilbake til en avgrensningsboks eller hopper over de mer eksotiske, så en rapport som baserer seg på rik tekst, strekkoder eller gradientfyll vil miste detaljer. Ingenting av dette er en feil; det er den ærlige grensen for et oversettelseslag
Én driftsmessig advarsel er viktigere enn resten. Ingen av de tre motorene følger med losLab PDF Library, og broene er bevisst ekskludert fra hovedbyggingen — de kompilerer bare fra et prosjekt som allerede har FastReport VCL 6.x, QuickReport 8 eller ReportBuilder 20 i søkebanen sin. Grensesnittene for metafiler og DrawCommand som de retter seg mot har holdt seg stabile over flere store motorversjoner, men du har ansvaret for at versjonene samsvarer. Få det riktig, og de to banene dekker hele spekteret, fra en ad-hoc DBGrid-dump til en designet faktura. Tabelleksporteren og rapportbroene leveres begge med losLab PDF Library for Delphi og C++Builder