„PDFium“ yra žinomas kaip PDF peržiūros variklis, naudojamas, pavyzdžiui, „Chrome“ naršyklėje. Tačiau pirmiausia verta patikslinti, kad „PDFium VCL“ taip pat leidžia kurti visiškai naujus dokumentus. Dokumentų kūrimo dalis apgaubia PDFium puslapio objektų API: sukuriate tuščią dokumentą, pridedate nurodytų matmenų puslapius ir pasirinktomis koordinatėmis įkeliate tekstą, vektorinius objektus bei vaizdus. Jums nereikia mokytis jokios puslapio aprašymo kalbos ar naudoti spausdinimo tvarkyklių. Jūs tiesiog iškviečiate metodus, biblioteka surenka PDF objektus, o funkcija SaveAs įrašo rezultatą į diską.
Tačiau ši biblioteka neturi puslapio maketavimo variklio (layout engine). Tai svarbu pabrėžti iš karto, nes tai nulemia visus tolesnius pavyzdžius. „PDFium VCL“ talpina turinį tiksliai ten, kur nurodote, naudodama absoliučiąsias koordinates, ir niekur kitur. Ji pati nekelia teksto į kitą eilutę, neskirsto jo per kelis puslapius ir neformuoja lentelių iš eilučių bei stulpelių. Visi šie skaičiavimai yra jūsų užduotis. Jei tikitės sistemos, kuri automatiškai tvarko tekstą kaip teksto rengyklė, turėkite omenyje: tai yra žemo lygio turinio išdėstymo API, labiau panaši į piešimą drobėje nei į knygos maketavimą. Generuojant sąskaitas-faktūras, sertifikatus, etiketes ar ataskaitas, kur iš anksto žinoma kiekvieno elemento vieta, šis tikslumas yra būtent tai, ko reikia.
Minimalus kodas failui sukurti
Tik trys iškvietimai skiria tuščią TPdf komponentą nuo išsaugoto PDF failo: sukurti dokumentą, pridėti puslapį ir įrašyti failą. Visas kitas turinys įterpiamas tarp šių žingsnių.
uses
Vcl.Graphics, // for clBlack and TColor
PDFium; // TPdf lives here
procedure CreateBlankPdf(const FileName: string);
var
Pdf: TPdf;
begin
Pdf := TPdf.Create(nil);
try
Pdf.CreateDocument; // empty in-memory document
Pdf.AddPage(0, 595, 842); // A4 portrait, in points
Pdf.AddText('First page', 'Arial', 18, 50, 780);
Pdf.SaveAs(FileName); // serialize to disk
finally
Pdf.Active := False;
Pdf.Free;
end;
end;
Viena detalė gali suklaidinti mačiusius senesnius kodo pavyzdžius: po CreateDocument nereikia nustatyti savybės Pdf.Active := True. Savybė Active praneša, ar dokumento rodyklė (handle) egzistuoja, o CreateDocument ją jau sukūrė, todėl ši savybė tampa True iškart po iškvietimo. Pakartotinis jos nustatymas geriausiu atveju nieko nedaro, o blogiausiu – klaidina kodą skaitančius programuotojus. Savybė Active yra svarbi užbaigiant darbą: nustačius reikšmę False, susijęs dokumentas atlaisvinamas prieš iškviečiant Free. Laikykite CreateDocument ir failo įkėlimą nesuderinamais veiksmais. Biblioteka neleis sukurti naujo dokumento tame TPdf komponente, kuriame jau yra atidarytas kitas dokumentas, todėl norint jį naudoti pakartotinai, pirmiausia reikia uždaryti esamą dokumentą.
Koordinatės prasideda apatiniame kairiajame kampe
Antrasis parametrų pora metode AddText (ir visuose kituose išdėstymo metoduose) nurodo tašką PDF naudotojo erdvėje. Atskaitos taškas yra apatiniame kairiajame puslapio kampe, X ašis eina į dešinę, o Y ašis – į viršų. Vienas matavimo vienetas yra lygus vienam taškui (1/72 colio), todėl A4 puslapis yra 595 × 842 vienetų dydžio, o US Letter – 612 × 792 vienetų. Į viršų kylanti Y ašis yra dažniausia klaidų priežastis, kai tekstas „atsiduria už puslapio ribų“, nes ekrano ir taškinių vaizdų koordinatėse pradžia yra viršuje, o Y didėja žemyn. 842 taškų aukščio puslapyje antraštė puslapio viršuje bus ties Y = 780, o ne ties Y = 60. Jei tekstas atsirado netikėtoje vietoje, greičiausiai pamiršote atimti savo Y reikšmę iš bendro puslapio aukščio.
Pirmasis metodo AddPage parametras nurodo įterpimo poziciją (indeksavimas prasideda nuo 1), o 0 patogumo dėlei reiškia „dokumento pradžią“. Perduokite 0 arba 1 pirmajam puslapiui įterpti pačioje pradžioje; norėdami pridėti puslapį pabaigoje, perduokite reikšmę, atitinkančią esamą puslapių skaičių. Ką tik pridėtas puslapis automatiškai tampa dabartiniu puslapiu, į kurį bus nukreipti visi vėlesni piešimo iškvietimai. Jei pridedate kelis puslapius ir vėliau norite grįžti prie ankstesnio, nustatykite savybę PageNumber, kad perkeltumėte žymeklį. Jei puslapius pildote eilės tvarka juos kurdami, šio parametro keisti nereikia.
Teksto rašymas ir tyliosios šriftų taisyklės
Metodas AddText priima visus teksto fragmentui reikalingus parametrus: eilutę, šrifto pavadinimą, dydį taškais, X ir Y koordinates, taip pat pasirinktinę spalvą, nepermatomumo (alpha) baitą ir pasukimo kampą laipsniais.
procedure WriteHeader(Pdf: TPdf; const Title, Author: string);
begin
// Title in black, default opacity, no rotation
Pdf.AddText(Title, 'Arial', 20, 50, 780);
// A lighter byline 24 points below it
Pdf.AddText('By ' + Author, 'Arial', 11, 50, 756, clGray);
// A faint diagonal draft stamp across the page
Pdf.AddText('DRAFT', 'Arial', 64, 180, 380, clGray, $30, 45.0);
end;
Nepermatomumo baitas kinta nuo $00 (visiškai permatomas) iki $FF (nepermatomas). Pavyzdžiui, nustačius reikšmę $30, gaunamas maždaug 19 procentų nepermatomumas, todėl juodraščio žyma atrodo kaip vandens ženklas, per kurį galima įskaityti tekstą. Kampas pasuka tekstą prieš laikrodžio rodyklę aplink jo atskaitos tašką, todėl 45 laipsnių kampas sukuria klasikinį įstrižą užrašą nuo kampo iki kampo. Vandens ženklui kurti nereikia jokios specialios funkcijos – tai tiesiog didelis, pusiau permatomas ir pasuktas AddText teksto blokas, o jo piešimas prieš arba po pagrindinio teksto nulemia, ar jis bus fone, ar ant turinio viršaus.
Šriftams reikėtų skirti ypatingą dėmesį, nes jų neradus klaidos pranešimas nėra rodomas. Perdavus šrifto pavadinimą, „PDFium VCL“ kreipiasi į operacinę sistemą, kad gautų to šrifto TrueType duomenis ir įterptų juos į dokumentą. Dėl šios priežasties jūsų kompiuteryje sukurtas failas atrodys taip pat ir kitame kompiuteryje, kuriame šis šriftas neįdiegtas. Tačiau jei šrifto pavadinime padarysite klaidą arba tokio šrifto nebus kūrimo sistemoje, jokia išimtis (exception) nebus sukelta. Biblioteka tiesiog sukurs teksto objektą su nurodytu pavadinimu, bet neįterps pačio šrifto duomenų – peržiūros programa bandys jį pakeisti kuo nors panašiu. Testuojant jūsų sistemoje tekstas atrodys gerai, tačiau atidarius dokumentą kitoje sistemoje su kitais šriftais, teksto išdėstymas arba simboliai gali pasikeisti. Naudokite tik tuos šriftus, kurie tikrai yra generavimo kompiuteryje, valdykite šriftų sąrašą kaip projekto priklausomybę ir būtinai patikrinkite sukurtą dokumentą kitoje sistemoje prieš pradėdami juo pasitikėti.
Vektorinės formos: kelio kūrimas ir pritaikymas
Linijos, stačiakampiai ir užpildytos sritys yra kuriamos per kelius (paths). Kelias atidaromas su CreatePath, kur nurodomas pradžios taškas bei visi stiliai vienu metu: užpildymo režimas, užpildo bei rėmelio spalvos su jų nepermatomumo reikšmėmis, linijos plotis ir sujungimai. Tada kelias praplečiamas naudojant LineTo, BezierTo ir ClosePath, o galiausiai iškviečiamas AddPath, kuris pritaiko sukurtą kelią puslapiui. Šį pritaikymo žingsnį lengva pamiršti, o jį praleidus puslapyje nieko nebus nupiešta.
procedure DrawDivider(Pdf: TPdf; X, Y, Width: Single);
begin
// A thin horizontal rule. The rectangle overload sets a box directly:
// X, Y, Width, Height, then fill mode and colors.
Pdf.CreatePath(X, Y, Width, 0.5, fmNone, clBlack, $FF,
True, clBlack, $FF, 1.0);
Pdf.AddPath;
end;
procedure DrawTriangle(Pdf: TPdf);
begin
// Point overload: start at the first vertex, line to the rest, close.
Pdf.CreatePath(200, 300, fmWinding, clBlue, $80, True, clNavy, $FF, 2.0);
Pdf.LineTo(300, 300);
Pdf.LineTo(250, 400);
Pdf.ClosePath;
Pdf.AddPath; // nothing is drawn until this runs
end;
Dvi funkcijos perkrovos (overloads) apima dažniausius atvejus. Keturių koordinačių forma priima X, Y, plotį bei aukštį ir sukuria lygiagretų stačiakampį vienu iškvietimu. Tai patogu naudojant rėmeliams, langelių kraštinėms ar užpildytiems fono skydeliams piešti. Dviejų koordinačių forma nustato tik pradžios tašką, o likusį kontūrą brėžiate patys naudodami LineTo ir BezierTo. Užpildymo režimas nurodo, kaip dažomos persidengiančios sriitys: fmWinding tinka daugumai vientisų formų, fmAlternate skirtas iškarpoms ir susikertantiems kontūrams, o fmNone palieka tik nubrėžtą rėmelį be jokio užpildo (kaip parodyta daliklių pavyzdyje).
Lentelių kūrimas rankiniu būdu naudojant kelius ir tekstą
Kadangi lentelės objekto bibliotekoje nėra, lentelė formuojama per ciklą. Nustatote stulpelių X postūmius ir eilutės aukštį, įrašote kiekvieną langelį naudodami AddText ir nubrėžiate linijas stačiakampiais keliais. Visi matematiniai skaičiavimai atliekami jūsų kode, tačiau tai yra paprasta schema, kurią vėliau galima pritaikyti bet kokiam tinkleliui.
procedure DrawTable(Pdf: TPdf; Left, Top: Double);
const
ColX: array[0..2] of Double = (0, 110, 210); // column offsets
RowH = 20;
var
Y: Double;
Row: Integer;
begin
// Header row
Pdf.AddText('Item', 'Arial', 10, Left + ColX[0], Top);
Pdf.AddText('Qty', 'Arial', 10, Left + ColX[1], Top);
Pdf.AddText('Price', 'Arial', 10, Left + ColX[2], Top);
// Rule under the header
Pdf.CreatePath(Left, Top - 5, 260, 0.5, fmNone, clBlack, $FF);
Pdf.AddPath;
// Data rows, stepping Y downward each iteration
Y := Top;
for Row := 1 to 3 do
begin
Y := Y - RowH;
Pdf.AddText('Item ' + IntToStr(Row), 'Arial', 9, Left + ColX[0], Y);
Pdf.AddText(IntToStr(Row * 2), 'Arial', 9, Left + ColX[1], Y);
Pdf.AddText('$' + IntToStr(Row * 10) + '.00', 'Arial', 9, Left + ColX[2], Y);
end;
end;
Atkreipkite dėmesį, kad kiekviename žingsnyje Y mažėja per eilutės aukštį, nes teigiama kryptis yra į viršų. Čia taip pat pastebimas teksto pločio matavimo trūkumas: niekas neapsaugo nuo to, kad ilgas pavadinimas užeis ant kito stulpelio, nes biblioteka nežino, kokio pločio yra jūsų išvesta eilutė. Jei generuojate fiksuoto formato dokumentus, kur kontroliuojate duomenis, tiesiog numatykite platesnius stulpelius. Jei turinys yra visiškai dinaminis, turite riboti įvedamų simbolių skaičių arba patys matuoti simbolių plotį prieš juos išvesdami (tokiais atvejais verta apsvarstyti specializuotas maketavimo bibliotekas).
Vaizdai ir keli puslapiai
Rastrinis turinys įkeliamas naudojant vaizdo funkcijas. Metodas AddPicture priima įkeltą TPicture ir patalpina jį nurodytame taške su pasirinktiniu mastelio keitimu; AddImage priima failo kelią arba TBitmap tiesiogiai, o AddJpegImage įrašo JPEG baitus tiesiai iš srauto be papildomo konvertavimo į taškinį vaizdą. Kaip ir kitais atvejais, patalpinimo koordinatės nurodo apatinį kairįjį vaizdo kampą naudotojo erdvėje, o plotis bei aukštis nurodomi puslapio taškais (points), o ne šaltinio pikseliais.
procedure CreateMultiPageReport(const FileName: string; PageCount: Integer);
var
Pdf: TPdf;
P: Integer;
begin
Pdf := TPdf.Create(nil);
try
Pdf.CreateDocument;
for P := 1 to PageCount do
begin
Pdf.AddPage(P, 595, 842); // append; the new page becomes current
Pdf.AddText('Page ' + IntToStr(P) + ' of ' + IntToStr(PageCount),
'Arial', 10, 50, 30); // footer near the bottom edge
// ... draw this page's body here ...
end;
Pdf.SaveAs(FileName);
finally
Pdf.Active := False;
Pdf.Free;
end;
end;
Kelių puslapių dokumento kūrimas tėra vieno puslapio kūrimo ciklas. Kiekvienas AddPage iškvietimas prideda puslapį ir padaro jį dabartiniu, todėl visas vėliau brėžiamas turinys bei puslapio porštė atsiduria naujame puslapyje. Šiame cikle nereikia keisti PageNumber savybės, nes pridėjus puslapį žymeklis jau persikėlė į jį. PageNumber reikalingas tik tada, kai norite grįžti į anksčiau sukurtą puslapį ne iš eilės. Funkciją SaveAs iškviečiate vieną kartą pačioje pabaigoje. Jei reikalingas archyvavimui skirtas PDF formatas, dokumento objektas siūlo metodą SaveAsPdfA bei kitus variantus, todėl išvesties standarto keitimas tėra kitos išsaugojimo funkcijos pasirinkimas.
Tinkamiausios naudojimo sritys
Galima teigti, kad „PDFium VCL“ dokumentų kūrimo API yra patikimas ir plonas sluoksnis virš paties PDFium puslapio objektų modelio: tikras dokumentų kūrimas, tikras šriftų įterpimas, vektorinis bei rastrinis turinys, išsaugomas į standartus atitinkantį failą. Tačiau tai nėra ir nepretenduoja būti automatiškai maketuojantis dokumentų variklis. Pagrindinė takoskyra yra teksto išdėstymas. Jei kuriate dokumentus pagal šablonus (sąskaitas, sertifikatus, etiketes ar prietaisų skydelius), kur viskas išdėstyta pagal fiksuotą tinklelį, absoliučiųjų koordinačių modelis yra greitas, o kodas išlieka lengvai skaitomas. Jei jūsų turinį sudaro ilgi tekstai, kurie turi patys persikelti į kitas eilutes ar puslapius, jums tektų patiems kurti maketavimo variklį ant šių funkcijų pagrindo, o tai būtų netinkamas sprendimas. Svarbiausia yra teisingai įvertinti savo dokumentų struktūrą.
Šiame straipsnyje aprašyti dokumentų kūrimo metodai yra dalis bibliotekos PDFium VCL Component, skirtos Delphi programuotojams, kuri apjungia kūrimo galimybes su atvaizdavimo bei teksto išgavimo funkcijomis.