TDataSetin tai raportin viemiseksi PDF-muotoon Delphissä losLab PDF Library tarjoaa kaksi polkua. PDFlibTableExport sovittaa minkä tahansa TDataSet-komponentin — FireDAC-kyselyn, ClientDataSetin tai muistitaulukon — sivutettavaksi PDF-taulukoksi, ja kolme siltaa kohdassa Addons välittävät valmiin FastReport-, QuickReport- tai ReportBuilder-raportin samalle PDF-kirjoittimelle. Molemmat tuottavat aidon PDF-tiedoston ilman tulostinajuria ja ilman näkyvää ikkunaa
Nämä kaksi ongelmaa vaikuttavat samanlaisilta, mutta eivät ole. DBGridillä tai raa'alla kyselytuloksella ei ole omaa asettelua, joten sen vieminen tarkoittaa sellaisen keksimistä: sarakkeet, otsikot, sivunvaihdot. FastReport- tai ReportBuilder-asiakirja sisältää jo valmiiksi suunnitellun asettelun, joten sen vieminen tarkoittaa jonkun toisen piirtokomentojen uskollista toistamista PDF-avaruuteen. losLab PDF Library pitää nämä asiat erillisissä yksiköissä juuri siksi, että niiden vikaantumistavat eroavat toisistaan, ja tämän artikkelin loppuosa käy läpi kunkin niistä ja kohdat, joissa ne lakkaavat olemasta luotettavia
Miten TDataSet viedään PDF-muotoon Delphissä?
PDFlibTableExport muuntaa tietojoukon taulukoksi yhdellä kutsulla. Viejä käy kenttäluettelon läpi kerran, ohittaa binaariset blob-kentät automaattisesti — tyypeillä ftBlob, ftGraphic, ftBytes ja vastaavilla ei ole hyödyllistä solutekstiä —, tasaa numeeriset sarakkeet oikealle ja maalaa valinnaisen seeprakuvioinnin tyylitellyn otsikkorivin päälle. Konepellin alla se rakentaa ruudukon CreateTable-funktiolla, täyttää solut SetTableCellContent-funktiolla ja renderoi DrawTableRows-funktiolla, joka on sama julkinen taulukko-API, jota ohjaisit käsin. Helppokäyttöinen kääre PDFlibExportDataSet määrittää millimetrit ja vasemman yläkulman alkupisteen puolestasi, joten kutsuja toimittaa vain sivun
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;
Automaattinen blob-kenttien ohitus on oletusarvo, ei pakkopaita. Kun tarvitset kenttäkohtaista hallintaa — mukautetun sarakkeen otsikon, kapeamman leveyden tai sellaisen sisäisen avainsarakkeen jättämisen pois, joka ei ole blob-kenttä —, luo TPDFlibTableExporter suoraan ja kytke sen OnFieldFilter-tapahtuma, joka käynnistyy kerran kenttää kohden ja antaa sinulle muokattavan määrityksen. Aseta Include-arvoksi False jättääksesi kentän pois tai aseta ColumnWidth ja DisplayLabel korvaamaan oletusasetukset. Tämä tapahtumakäsittelijä on myös paikka, jossa jätät pois leveän muistiokentän (memo), jota et halua paisuttavan sivua
Sivutus: piirtotilan välittäminen sivujen välillä
DrawTableRows kantaa sivutustilaa, ja sen toimintaperiaatteen ymmärtäminen on koko homman ydin. Kutsut sitä antamalla ensimmäisen ja viimeisen rivin; jos annat viimeisen rivin arvoksi alle 1, se tarkoittaa piirtämistä taulukon loppuun asti antamasi korkeuden rajoissa. Funktio palauttaa todellisuudessa piirretyn korkeuden, ja GetTableLastDrawnRow ilmoittaa viimeisen rivin, joka mahtui. Tämä pari on se tila, jonka siirrät sivulta toiselle: kun viimeksi piirretty rivi on vähemmän kuin kokonaismäärä, avaat uuden sivun ja jatkat sitä seuraavalta riviltä. Mitään ei mitata uudelleen — jatko jatkuu täsmälleen siitä, mihin edellinen kutsu jäi
// Miten viejä jatkaa pitkää taulukkoa sivujen yli
PageHeight := PDF.PageHeight;
Y := PageHeight - Options.Top;
Row := 1;
while Row <= TotalRows do
begin
DrawHeight := Y - Options.BottomMargin;
// Viimeinen rivi = 0 tarkoittaa "piirrä loppuun asti", DrawHeightin rajoissa
PDF.DrawTableRows(TableID, Options.Left, Y, DrawHeight, Row, 0);
LastDrawn := PDF.GetTableLastDrawnRow(TableID);
if LastDrawn >= TotalRows then
Break; // koko taulukko mahtui tälle sivulle
if LastDrawn < Row then
Break; // turva: ei edistymistä, keskeytä
PDF.NewPage;
Row := LastDrawn + 1; // jatka ensimmäisestä piirtämättömästä rivistä
Y := PageHeight - Options.Top;
end;
Silmukka on myös paikka, jossa toistuva otsikko sijaitsee. Jokaisella jatkosivulla viejä voi valinnaisesti piirtää rivin 1 — otsikon — uudelleen ennen tietorivejä, ja se käyttää ensimmäisen DrawTableRows-kutsun palauttamaa korkeutta siirtääkseen rungon sen alapuolelle. Tämän vuoksi DrawTableRows palauttaa korkeuden eikä seuraavan rivin koordinaattia: palautusarvo antaa sinun pinota uudelleenpiirretyn otsikon ja jatkuvan rungon ilman, että kummankaan kokoa tarvitsee kovakoodata
Miten FastReport- tai ReportBuilder-raportti viedään PDF-muotoon?
Raporttisillat toimivat päinvastoin: ne eivät koskaan keksi asettelua, vaan toistavat sen. Jokainen silta kytkeytyy moottoriinsa sen natiiviin vientiliitäntään. PDFlibFRExport periytyy luokasta TfrxCustomExportFilter ja vastaanottaa FastReportin teksti- (memo), kuva-, muoto- ja viivaobjektit muuntaen jokaisen PDFlibPas-primitiiviksi. PDFlibRBDevice periytyy luokasta TppFileDevice ja käy läpi ReportBuilderin DrawCommand-luettelon. PDFlibQRExport valitsee täysin kolmannen reitin, jota käsitellään seuraavassa osiossa. Kaikki kolme toimivat ilman käyttöliittymää (headless), mikä on ainoa syy käyttää niitä palvelimella
Headless-ajolla on hintansa, ja FastReport on varoittava esimerkki. TfrxReport.Export reitittää sivut esikatselusivujen kautta, jotka tarkistavat suodattimen ShowDialog-ominaisuuden, jonka oletusarvoksi periytyy True. Koneella, jossa ei ole interaktiivista työpöytää, modaalinen valintaikkuna palauttaa peruutustuloksen ja vienti epäonnistuu hiljaisesti. Aseta ShowDialog-ominaisuuden arvoksi False ennen Export-kutsun tekemistä, niin raportti renderoituu hiljaisesti. ReportBuilderissa on vastaavat kytkimet — AllowPrintToFile ja ShowPrintDialog —, ja QuickReport, joka ajaa kaiken Prepare-kutsun kautta, ei tarvitse valintaikkunoiden vaimennusta lainkaan
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;
Kolme moottoria, kolme koordinaattijärjestelmää
Jokainen silta tekee oman koordinaattilaskentansa, koska jokainen moottori mittaa maailmaa eri tavalla. PDFlibFRExport käsittelee FastReport-objektien sijainteja pikseleinä suhteessa 96 PPI:hin ja skaalaa ne kertoimella 96/25.4 päästäkseen millimetreihin. PDFlibRBDevice lukee ReportBuilderin piirtokomennot millimetrin tuhannesosina ja jakaa ne tuhannella, ja se rekisteröi itsensä ppRegisterDevice-kutsulla, joten ppReport.DeviceType-asetuksen asettaminen arvoon 'PDFlibPas' ja Print-kutsun tekeminen riittää reitittämään tulostuksen sen kautta. PDFlibQRExport ei tee lainkaan koordinaattilaskentaa: valmis QuickReport-sivu on jo EMF-metatiedosto, joten silta tallentaa jokaisen sivun virtaan ja syöttää sen ImportEMFFromStream-funktiolle antaen kirjaston laitekonteksti- ja metatiedostojen renderointipolun sijoittaa jokaisen kirjoitusmerkin ja viivan. FastReport- ja ReportBuilder-sillat puolestaan tuottavat natiiveja vektoripiirto-primitiivejä ja natiivia tekstiä
var
PDF: TPDFlib;
begin
PDF := TPDFlib.Create;
try
PDF.SetOrigin(1);
// Valmistelee raportin ja liittää jokaisen sivun yhdellä kutsulla; silta
// muuntaa kunkin sivun EMF:n PDF:ksi ImportEMFFromStream-funktion kautta.
PDFlibQRExportReport(PDF, QuickRep1, 'ledger.pdf');
finally
PDF.Free;
end;
end;
Mihin tarkkuus päättyy
Tunne rajat ennen kuin sitoudut käyttämään näitä siltoja työnkulussasi. Tietojoukon viejä ohittaa binaarisarakkeet hiljaisesti, joten raportti, jonka on näytettävä upotettu kuva, vaatii toisenlaisen lähestymistavan kuin suora taulukko. QuickReport-polku tekee EMF-muunnoksen kautta tekstistä vektorikuvioita — sivu näyttää oikealta, mutta se ei sisällä valittavaa tai haettavaa tekstiä eikä sivutettua PDF-rakennetta saavutettavuutta varten. FastReport ja ReportBuilder säilyttävät todellisen tekstin, mutta tyypitetyt käsittelijät kattavat yleiset näkymät — teksti (memo), kuva, muoto, viiva — ja turvautuvat rajauslaatikkoon tai ohittavat erikoiset objektit, joten runsaasti tekstiä, viivakoodeja tai liukuvärityksiä sisältävä raportti menettää yksityiskohtia. Tämä ei ole vika, vaan käännöskerroksen rehellinen raja
Yksi toiminnallinen varoitus on ylitse muiden. Mikään näistä kolmesta moottorista ei tule losLab PDF Libraryn mukana, ja sillat on tarkoituksella jätetty pois päärakennuksesta — ne kääntyvät vain sellaisesta projektista, jonka etsintäpolulla on ja FastReport VCL 6.x, QuickReport 8 tai ReportBuilder 20. Metatiedosto- ja DrawCommand-rajapinnat, joihin ne kohdistuvat, ovat pysyneet vakaina useiden moottoriversioiden välillä, mutta olet itse vastuussa versioiden yhteensopivuudesta. Kun saat tämän oikein, nämä kaksi polkua kattavat koko kirjon ad-hoc DBGrid-vedoksesta suunniteltuun laskuun. Sekä taulukonviejä että raporttisillat toimitetaan losLab PDF Library -kirjaston mukana Delphille ja C++Builderille