Za izvoz TDataSet ali poročila v PDF v Delphi ponuja losLab PDF Library dve poti. PDFlibTableExport prilagodi kateri koli TDataSet — poizvedbo FireDAC, ClientDataSet, tabelo v pomnilniku — v paginirano tabelo PDF, trije mostovi pod Addons pa predajo pripravljeno poročilo FastReport, QuickReport ali ReportBuilder istemu pisalniku PDF. Oba ustvarita dejanski PDF brez gonilnika tiskalnika in brez vidnega okna
Težavi sta videti podobni, vendar nista. DBGrid ali surov rezultat poizvedbe nima lastne postavitve, zato izvoz pomeni izumljanje nove: stolpci, glave, prelomi strani. Dokument FastReport ali ReportBuilder že vsebuje oblikovano postavitev, zato izvoz pomeni zvesto ponovno izvajanje ukazov za risanje nekoga drugega v prostoru PDF. losLab PDF Library ohranja te zadeve v ločenih enotah ravno zato, ker se načini napak razlikujejo, preostanek tega članka pa opisuje vsakega od njih in kje preneha biti zanesljiv
Kako izvoziti TDataSet v PDF v Delphi?
PDFlibTableExport pretvori nabor podatkov v tabelo z enim samim klicem. Izvoznik enkrat prehodi seznam polj, samodejno preskoči binarna polja blob — ftBlob, ftGraphic, ftBytes in njim sorodna nimajo uporabnega besedila celice —, desno poravna številčne stolpce in nariše neobvezno zebro črto čez oblikovan pas glave. Pod pokrovom zgradi mrežo s CreateTable, napolni celice s SetTableCellContent in izriše z DrawTableRows, isto javno tabelarno API, ki bi jo upravljali ročno. Priročni ovijalec PDFlibExportDataSet namesto vas konfigurira milimetre in izhodišče zgoraj levo, tako da klicatelj zagotovi le stran
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;
Samodejni preskok blobov je privzeta nastavitev, ne prisilni jopič. Ko potrebujete nadzor nad posameznimi polji — oznako stolpca po meri, ožjo širino ali opustitev notranjega ključnega stolpca, ki ni blob —, neposredno zgradite TPDFlibTableExporter in povežite njegov dogodek OnFieldFilter, ki se sproži enkrat na polje in vam predloži spremenljivo specifikacijo. Nastavite Include na False, da opustite polje, ali pa nastavite ColumnWidth in DisplayLabel, da preglasite privzete nastavitve. Ta kavelj je tudi mesto, kjer izključite širok stolpec beležke (memo), za katerega ne želite, da bi razširil stran
Paginacija: prenašanje stanja risanja med stranmi
DrawTableRows prenaša stanje paginacije in razumevanje njegove pogodbe je ključ do vsega. Pokličete ga s prvo in zadnjo vrstico; prenos zadnje vrstice pod 1 pomeni risanje do konca tabele, omejeno z višino, ki ste jo določili. Funkcija vrne višino, ki jo je dejansko izrisala, GetTableLastDrawnRow pa poroča o zadnji vrstici, ki je ustrezala. Ta par je stanje, ki ga prenesete z ene strani na drugo: ko je zadnja izrisana vrstica krajša od celotnega števila, odprete novo stran in nadaljujete z naslednjo vrstico. Nič se ne meri znova — nadaljevanje se začne natanko tam, kjer se je prejšnji klic končal
// Jak exportér pokračuje s dlouhou tabulkou napříč stránkami
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;
Zanka je tudi mesto, kjer se nahaja ponavljajoča se glava. Na vsaki naslednji strani izvoznik neobvezno ponovno nariše vrstico 1 — glavu — pred vrsticami s podatki in uporabi višino, ki jo vrne ta prvi klic DrawTableRows, za odmik telesa pod njo. Zato DrawTableRows vrne višino in ne koordinate naslednje vrstice: vrnjena vrednost je tisto, kar vam omogoča, da zložite ponovno narisano glavo in nadaljevalno telo, ne da bi trdo kodirali velikost katerega koli od njiju
Kako izvoziti poročilo FastReport ali ReportBuilder v PDF?
Mostovi za poročila uporabljajo nasprotni pristop: nikoli ne izumljajo postavitve, temveč jo ponovijo. Vsak most se poveže s svojim pogonom na njegovem nativnem izvoznem vmesniku. PDFlibFRExport deduje iz TfrxCustomExportFilter in prejema FastReportove objekte beležk, slik, oblik in črt ter vsakega prevede v primitiv PDFlibPas. PDFlibRBDevice deduje iz TppFileDevice in prehodi seznam DrawCommand v ReportBuilderju. PDFlibQRExport izbere popolnoma tretjo pot, ki jo obravnava naslednji razdelek. Vsi trije tečejo headless (brez glave), kar je edini razlog za njihovo uporabo na strežniku
Headless način ni brezplačen in FastReport je opozorilni primer. TfrxReport.Export poteka skozi predogledne strani, ki se posvetujejo z lastnostjo filtra ShowDialog, ta lastnost pa deduje privzeto vrednost True. Na računalniku brez interaktivnega namizja modalno pogovorno okno vrne rezultat preklica in izvoz tiho spodleti. Nastavite ShowDialog na False, preden pokličete Export, in poročilo se bo izrisalo tiho. ReportBuilder ima vzporedni stikali — AllowPrintToFile in ShowPrintDialog —, QuickReport, ki vse vodi prek Prepare, pa sploh ne potrebuje zatiranja pogovornih oken
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;
Trije pogoni, trije koordinatni sistemi
Vsak most izvaja svojo aritmetiko koordinat, saj vsak pogon meri svet drugače. PDFlibFRExport obravnava položaje objektov FastReport kot slikovne pike pri 96 PPI in jih skalira z 96/25.4, da doseže milimetre. PDFlibRBDevice bere ukazne risbe ReportBuilderja v tisočinkah mm in deli s 1000 ter se registrira prek ppRegisterDevice, tako da nastavitev ppReport.DeviceType na 'PDFlibPas' in klic Print zadoščata za preusmeritev izhoda skozenj. PDFlibQRExport sploh ne izvaja koordinatne matematike: pripravljena stran QuickReport je že EMF datoteka meta, zato most shrani vsako stran v tok in jo posreduje ImportEMFFromStream, s čimer prepusti poti za upodabljanje konteksta naprave in datotek meta knjižnice, da postavi vsak glif in pravilo. Mostova za FastReport in ReportBuilder pa nasprotno oddajata nativne vektorske risalne primitive in nativno besedilo
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;
Kje se konča zvestoba upodabljanja
Spoznajte meje, preden tem mostovom zaupate delovni tok. Izvoznik nabora podatkov tiho opusti binarna polja, zato poročilo, ki mora prikazovati vdelano sliko, potrebuje drugačen pristop kot navadna tabela. Pot QuickReport zaradi uporabe EMF pretvori besedilo v vektorske glife — stran je videti pravilno, vendar ne vsebuje izbirljivega ali preiskovalnega besedila in nobene označene strukture PDF za dostopnost. FastReport in ReportBuilder ohranjata dejansko besedilo, toda tipizirani rokovalci pokrivajo pogoste poglede — beležke, slike, oblike, črte — in se vrnejo k omejevalnemu okvirju (bounding box) ali preskočijo eksotične, zato bo poročilo, ki se opira na bogato besedilo, črtne kode ali prelivna polnila, izgubilo podrobnosti. Nič od tega ni napaka; je le pošten rob prevajalskega sloja
Eno operativno opozorilo prekaša vsa druga. Noben od treh pogonov se ne dobavlja znotraj losLab PDF Library in mostovi so namerno izključeni iz glavne sestave — prevajajo se le iz projekta, ki že ima FastReport VCL 6.x, QuickReport 8 ali ReportBuilder 20 v svoji iskalni poti. Površine datotek meta in DrawCommand, ki jih ciljajo, so ostale stabilne skozi več večjih različic pogonov, vendar ste sami odgovorni za ujemanje različic. Če to uredite pravilno, obe poti pokrivata celoten razpon, od ad-hoc izpisa DBGrid do oblikovanega računa. Izvoznik tabel in mostovi za poročila so priloženi losLab PDF Library za Delphi in C++Builder