Pre export TDataSet alebo reportu do PDF v Delphi ponúka losLab PDF Library dve cesty. PDFlibTableExport prispôsobí akýkoľvek TDataSet — dopyt FireDAC, ClientDataSet, tabuľku v pamäti — do stránkovanej tabuľky PDF a tri premostenia v rámci Addons odovzdajú pripravený report FastReport, QuickReport alebo ReportBuilder tomu istému zapisovaču PDF. Obe možnosti vytvárajú skutočné PDF bez ovládača tlačiarne a bez viditeľného okna
Tieto dva problémy vyzerajú podobne, ale nie sú. DBGrid alebo nespracovaný výsledok dopytu nemá vlastné rozloženie, takže jeho export znamená vytvorenie nového rozloženia: stĺpce, hlavičky, zlomy stránok. Dokument FastReport alebo ReportBuilder už obsahuje navrhnuté rozloženie, takže jeho export znamená verné prehratie vykresľovacích príkazov niekoho iného do priestoru PDF. Knižnica losLab PDF Library uchováva tieto záležitosti v samostatných jednotkách práve preto, že chybové stavy sa líšia, a zvyšok tohto článku prechádza každú z nich a miesta, kde prestávajú byť spoľahlivé
Ako exportovať TDataSet do PDF v Delphi?
PDFlibTableExport prevedie dataset na tabuľku jedným volaním. Exportér raz prejde zoznam polí, automaticky preskočí binárne blob polia — ftBlob, ftGraphic, ftBytes a im podobné nemajú žiadny užitočný text bunky —, zarovná číselné stĺpce doprava a vykreslí voliteľné zebrie pruhy cez štylizovaný pás hlavičky. V pozadí zostaví mriežku pomocou CreateTable, vyplní bunky pomocou SetTableCellContent a vykreslí pomocou DrawTableRows, čo je rovnaké verejné rozhranie Table API, ktoré by ste ovládali ručne. Pohodlný wrapper PDFlibExportDataSet pre vás nakonfiguruje milimetre a počiatok vľavo hore, takže volajúci dodá iba stránku
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;
Automatické preskakovanie blobov je predvolené nastavenie, nie obmedzenie. Ak potrebujete kontrolu pre každé pole — vlastný štítok stĺpca, užšiu šírku alebo vynechanie stĺpca s interným kľúčom, ktorý nie je blob —, vytvorte priamo TPDFlibTableExporter a pripojte jeho udalosť OnFieldFilter, ktorá sa spustí raz pre každé pole a odovzdá vám upraviteľnú špecifikáciu. Nastavením Include na False pole vynecháte, alebo nastavením ColumnWidth a DisplayLabel prepíšete predvolené hodnoty. Tento hook je tiež miestom, kde vylúčite široký stĺpec memo, ktorý nechcete nechať nafúknuť stránku
Stránkovanie: odovzdávanie stavu kreslenia medzi stránkami
DrawTableRows nesie stav stránkovania a pochopenie jeho pravidiel je kľúčom k úspechu. Voláte ho s prvým a posledným riadkom; zadanie posledného riadku menšieho ako 1 znamená vykreslenie až do konca tabuľky, ohraničené výškou, ktorú ste zadali. Funkcia vracia výšku, ktorú skutočne vykreslila, a GetTableLastDrawnRow hlási posledný riadok, ktorý sa zmestil. Táto dvojica predstavuje stav, ktorý odovzdávate z jednej stránky na druhú: keď je posledný vykreslený riadok kratší ako celok, otvoríte novú stránku a pokračujete od riadku po ňom. Nič sa znova nemeria — pokračovanie nadväzuje presne tam, kde predchádzajúce volanie skončilo
// 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;
Slučka je tiež miestom, kde sa nachádza opakujúca sa hlavička. Na každej pokračujúcej stránke exportér voliteľne znova vykreslí riadok 1 — hlavičku — pred riadkami údajov a použije výšku vrátenú týmto prvým volaním DrawTableRows na posunutie tela pod ňou. To je dôvod, prečo DrawTableRows vracia výšku namiesto súradnice nasledujúceho riadku: návratová hodnota je to, čo vám umožňuje poskladať znova vykreslenú hlavičku a pokračujúce telo bez pevného kódovania veľkosti oboch
Ako exportovať report FastReport alebo ReportBuilder do PDF?
Premostenia reportov využívajú opačný prístup: nikdy nevytvárajú rozloženie, ale ho prehrávajú. Každé premostenie sa pripája k svojmu enginu na jeho natívnom exportnom rozhraní. PDFlibFRExport dedí z TfrxCustomExportFilter a prijíma objekty memo, obrázky, tvary a čiary z FastReportu, pričom každý z nich prekladá na primitív PDFlibPas. PDFlibRBDevice dedí z TppFileDevice a prechádza zoznam DrawCommand z ReportBuilderu. PDFlibQRExport využíva úplne tretiu cestu, ktorej sa venuje nasledujúca časť. Všetky tri fungujú v headless režime, čo je hlavný dôvod, prečo po nich siahnuť na serveri
Headless režim nie je zadarmo a FastReport je toho výstražným príkladom. TfrxReport.Export smeruje cez stránky náhľadu, ktoré overujú vlastnosť filtra ShowDialog, a táto vlastnosť dedí predvolenú hodnotu True. Na stroji bez interaktívnej pracovnej plochy vráti modálne dialógové okno výsledok zrušenia a export v tichosti zlyhá. Nastavením ShowDialog na False pred volaním Export sa report vykreslí na pozadí. ReportBuilder má paralelné prepínače — AllowPrintToFile and ShowPrintDialog — a QuickReport, ktorý riadi všetko cez Prepare, nepotrebuje žiadne potlačenie dialógového okna
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 enginy, tri súradnicové systémy
Každé premostenie vykonáva vlastnú aritmetiku súradníc, pretože každý engine meria svet inak. PDFlibFRExport považuje pozície objektov FastReport za pixely pri 96 PPI a škáluje ich pomerom 96/25.4, čím získa milimetre. PDFlibRBDevice číta príkazy na kreslenie ReportBuilder v tisícinách mm a delí ich číslom 1000 a registruje sa prostredníctvom ppRegisterDevice, takže nastavenie ppReport.DeviceType na 'PDFlibPas' a volanie Print stačí na nasmerovanie výstupu cez neho. PDFlibQRExport nevykonáva žiadnu súradnicovú matematiku: pripravená stránka QuickReport je už EMF metasúborom, takže premostenie uloží každú stránku do streamu a odovzdá ju ImportEMFFromStream, čím nechá vykresľovaciu cestu kontextu zariadenia a metasúboru knižnice umiestniť každý glyf a pravidlo. Premostenia pre FastReport a ReportBuilder naproti tomu emitujú natívne vektorové kresliace primitíva a natívny 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;
Kde vernosť končí
Spoznajte hranice skôr, než zveríte pracovný postup týmto premosteniam. Exportér datasetov potichu zahadzuje binárne stĺpce, takže report, ktorý musí zobrazovať vložený obrázok, si vyžaduje iný prístup než priama tabuľka. Cesta QuickReport, pretože prechádza cez EMF, zapeká text do vektorových glyfov — stránka vyzerá správne, ale neobsahuje žiadny voliteľný ani vyhľadávateľný text a žiadnu štruktúru tagovaného PDF pre prístupnosť. FastReport a ReportBuilder uchovávajú skutočný text, ale typové handlery pokrývajú bežné pohľady — memo, obrázok, tvar, čiaru — a vracajú sa k ohraničujúcemu rámčeku alebo preskakujú tie exotické, takže report spoliehajúci sa na formátovaný text, čiarové kódy alebo gradientové výplne stratí detaily. Nič z toho nie je chyba; je to poctivá hranica prekladovej vrstvy
Jedno prevádzkové upozornenie prevyšuje ostatné. Ani jeden z týchto troch enginov sa nedodáva v rámci losLab PDF Library a premostenia sú zámerne vylúčené z hlavného buildu — kompilujú sa iba z projektu, ktorý už má vo svojej vyhľadávacej ceste FastReport VCL 6.x, QuickReport 8 alebo ReportBuilder 20. Rozhrania metasúborov a DrawCommand, na ktoré sa zameriavajú, zostali stabilné v rámci niekoľkých hlavných verzií enginov, ale za zhodu verzií zodpovedáte vy. Keď to nastavíte správne, obe cesty pokryjú celé spektrum, od ad-hoc exportu DBGrid až po navrhnutú faktúru. Exportér tabuliek aj premostenia reportov sa dodávajú s losLab PDF Library pre Delphi a C++Builder