Za izvoz TDataSet-a ili izvješća u PDF u Delphiju, losLab PDF Library nudi dva puta. PDFlibTableExport prilagođava bilo koji TDataSet — FireDAC upit, ClientDataSet, tablicu u memoriji — u PDF tablicu s prijelomom stranica, a tri mosta u Addons predaju pripremljeno FastReport, QuickReport ili ReportBuilder izvješće istom PDF piscu. Oba stvaraju pravi PDF bez upravljačkog programa pisača i bez vidljivog prozora
Ova dva problema izgledaju slično, no to nisu. DBGrid ili čisti rezultat upita nemaju vlastiti raspored, pa izvoziti ih znači osmisliti ga: stupce, zaglavlja, prijelome stranica. FastReport ili ReportBuilder dokument već sadrži dizajnirani raspored, pa izvoziti ga znači vjerno reproducirati tuđe naredbe za crtanje u PDF prostoru. losLab PDF Library drži ove zadatke u zasebnim jedinicama upravo zato što se načini zakazivanja razlikuju, a ostatak ovog članka objašnjava svaki od njih i gdje oni prestaju biti pouzdani
Kako izvesti TDataSet u PDF u Delphiju?
PDFlibTableExport pretvara skup podataka u tablicu u jednom pozivu. Izvoznik jednom prolazi kroz popis polja, automatski preskače binarna blob polja — ftBlob, ftGraphic, ftBytes i slična nemaju koristan tekst u ćelijama — desno poravnava numeričke stupce i crta opcionalnu zebra prugu preko stilizirane trake zaglavlja. Ispod haube gradi mrežu s CreateTable, popunjava ćelije s SetTableCellContent i iscrtava s DrawTableRows, što je isti javni Table API koji biste ručno pokretali. Praktični omotač PDFlibExportDataSet konfigurira milimetre i gornji lijevi koordinatni početak za vas, tako da pozivatelj treba samo osigurati stranicu
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;
Automatsko preskakanje bloba je zadana postavka, a ne pravilo bez iznimke. Kada trebate kontrolu po pojedinom polju — prilagođenu oznaku stupca, manju širinu ili izbacivanje stupca s internim ključem koji nije blob — izravno izradite TPDFlibTableExporter i povežite njegov događaj OnFieldFilter, koji se pokreće jednom po polju i predaje vam promjenjivu specifikaciju. Postavite Include na False za uklanjanje polja, ili postavite ColumnWidth and DisplayLabel za nadjačavanje zadanih vrijednosti. Taj događaj je također mjesto gdje isključujete široki memo stupac koji ne želite da preoptereti stranicu
Paginacija: prijenos stanja crtanja između stranica
DrawTableRows nosi stanje paginacije, a razumijevanje njegova ponašanja je ključ cijele igre. Pozivate ga s prvim i zadnjim retkom; slanje zadnjeg retka ispod 1 znači crtanje do kraja tablice, ograničeno visinom koju ste zadali. Funkcija vraća visinu koju je stvarno iscrtala, a GetTableLastDrawnRow javlja zadnji redak koji je stao. Taj par je stanje koje prenosite s jedne stranice na drugu: kada je zadnji iscrtani redak manji od ukupnog broja, otvarate novu stranicu i nastavljate od retka nakon njega. Ništa se ponovno ne mjeri — nastavak započinje točno tamo gdje je prethodni poziv stao
// 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;
Petlja je također mjesto gdje se nalazi ponavljajuće zaglavlje. Na svakoj sljedećoj stranici izvoznik opcionalno ponovno crta redak 1 — zaglavlje — prije redaka s podacima, te koristi visinu vraćenu tim prvim pozivom DrawTableRows kako bi pomaknuo tijelo ispod njega. Zbog toga DrawTableRows vraća visinu, a ne koordinate sljedećeg retka: povratna vrijednost je ono što vam omogućuje da posložite ponovno nacrtano zaglavlje i nastavak tijela bez tvrdog kodiranja veličine bilo kojeg od njih
Kako izvesti FastReport ili ReportBuilder izvješće u PDF?
Mostovi za izvješća imaju suprotan pristup: nikada ne osmišljavaju raspored, već ga reproduciraju. Svaki se most povezuje sa svojim pokretačem na izvornom izvoznom sučelju pokretača. PDFlibFRExport podklasira TfrxCustomExportFilter i prima FastReportove objekte memo, slika, oblika i linija, prevodeći svaki u PDFlibPas primitiv. PDFlibRBDevice podklasira TppFileDevice i prolazi kroz popis DrawCommand za ReportBuilder. PDFlibQRExport koristi posve treći put, o kojem govori sljedeći odjeljak. Sva tri rade bez grafičkog sučelja (headless), što je i glavni razlog za njihovo korištenje na poslužitelju
Rad bez sučelja (headless) nije jednostavan, a FastReport je opominjući primjer. TfrxReport.Export ide kroz stranice pregleda, koje provjeravaju svojstvo filtra ShowDialog, a to svojstvo nasljeđuje zadanu vrijednost True. Na računalu bez interaktivne radne površine, modalni dijaloški okvir vraća rezultat otkazivanja i izvoz tiho ne uspijeva. Postavite ShowDialog na False prije nego što pozovete Export i izvješće će se iscrtati bez ikakvih obavijesti. ReportBuilder ima paralelne prekidače — AllowPrintToFile i ShowPrintDialog — a QuickReport, koji sve pokreće kroz Prepare, uopće ne treba potiskivanje dijaloških okvira
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;
Tri pokretača, tri koordinatna sustava
Svaki most radi vlastitu koordinatnu aritmetiku jer svaki pokretač mjeri prostor drugačije. PDFlibFRExport tretira pozicije objekata FastReport-a kao piksele pri 96 PPI i skalira s 96/25.4 kako bi dobio milimetre. PDFlibRBDevice čita naredbe za crtanje ReportBuilder-a u tisućinkama mm i dijeli ih s 1000, te se registrira kroz ppRegisterDevice, tako da je postavljanje ppReport.DeviceType na 'PDFlibPas' i pozivanje Print dovoljno za usmjeravanje izlaza kroz njega. PDFlibQRExport uopće ne radi koordinatnu matematiku: pripremljena stranica QuickReport-a već je EMF datoteka metapodataka, pa most sprema svaku stranicu u tok i šalje je u ImportEMFFromStream, dopuštajući da kontekst uređaja i putanja iscrtavanja metapodataka knjižnice postave svaki glif i pravilo. Nasuprot tome, mostovi FastReport i ReportBuilder ispisuju izvorne primitivce vektorskog crtanja i izvorni 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;
Gdje završava točnost prikaza
Upoznajte granice prije nego što povjerite radni tijek ovim mostovima. Izvoznik skupa podataka tiho odbacuje binarne stupce, pa izvješće koje mora prikazati ugrađenu sliku zahtijeva drugačiji pristup od obične tablice. Putanja QuickReport-a, budući da ide kroz EMF, pretvara tekst u vektorske glifove — stranica izgleda ispravno, ali ne sadrži tekst koji se može označiti ili pretraživati, niti označenu PDF strukturu za pristupačnost. FastReport i ReportBuilder zadržavaju pravi tekst, ali njihovi rukovatelji pokrivaju uobičajene prikaze — memo, sliku, oblik, liniju — i vraćaju se na granični okvir ili preskaču egzotične elemente, pa će izvješće koje se oslanja na obogaćeni tekst, crtične kodove ili ispune gradijentom izgubiti detalje. Ništa od ovoga nije pogreška; to je stvarna granica prevoditeljskog sloja
Jedno operativno upozorenje nadilazi ostala. Nijedan od ova tri pokretača ne dolazi unutar losLab PDF Library, a mostovi su namjerno isključeni iz glavne gradnje — oni se prevode samo iz projekta koji već ima FastReport VCL 6.x, QuickReport 8 ili ReportBuilder 20 na svojoj putanji pretraživanja. Površine datoteke metapodataka i DrawCommand koje ciljaju ostale su stabilne kroz nekoliko glavnih verzija pokretača, ali vi ste odgovorni za usklađivanje verzija. Kada to ispravno postavite, ova dva puta pokrivaju cijeli spektar, od ad-hoc izvoza DBGrid-a do dizajniranog računa. Izvoznik tablica i mostovi za izvješća dolaze s losLab PDF Library za Delphi i C++Builder