Bralnik PDF v Delphiju se v osnovi zanaša na dve komponenti in njuno medsebojno povezavo. Komponenta TPdf upravlja dokument: odpre datoteko, jo dešifrira in odgovarja na vprašanja o številu strani in metapodatkih. Komponenta TPdfView pa je vizualna kontrola, ki izriše strani na zaslonu ter upravlja drsenje, zoom in stran, ki jo uporabnik trenutno gleda. PDFium VCL vključuje isti izrisovalnik, kot je vgrajen v brskalnik Chrome, zato se glifi, glajenje robov (anti-aliasing) in barve na platnu ujemajo s tem, kar vaši uporabniki že vidijo v svojem brskalniku. Delo ni v samem izrisovanju, temveč v povezovanju objekta dokumenta s pogledom, nalaganju brez sesutja pri poškodovanih ali z geslom zaščitenih datotekah ter zagotavljanju kontrol, zaradi katerih je bralnik videti dokončan: obračanje strani, spreminjanje zooma in prilagajanje strani oknu.
Ta članek vas vodi skozi postopek gradnje po korakih. Vse tukaj opisano izrisuje po eno stran naenkrat, kar je tisto, kar večina delovnih procesov z dokumenti zahteva. Če potrebujete strani zložene v en neprekinjeno drseč stolpec, je to drugačna odločitev o postavitvi, ki jo obravnava ločen članek.
Povezovanje TPdf s TPdfView
Postavite komponenti TPdf in TPdfView na obrazec, nato pa pogledu povejte, kateri dokument naj prikaže. Ta ena sama dodelitev predstavlja celotno povezavo med nevizualnim dokumentom in kontrolo, ki ga izrisuje.
procedure TFormMain.FormCreate(Sender: TObject);
begin
// Pdf and PdfView were dropped at design time.
PdfView.Pdf := Pdf; // the view paints whatever this document holds
PdfView.FitMode := pfmFitWidth; // start the user at a sensible zoom
end;
Preden lahko zaženete karkoli od tega, mora biti na računalniku nameščena izvorna knjižnica PDFium. PDFium VCL kliče datoteko pdfium32.dll or pdfium64.dll, odvisno od vaše ciljne platforme, dokument pa se preprosto ne bo odprl, če knjižnice DLL ni mogoče najti. Priložite ustrezno knjižnico DLL poleg svoje izvršljive datoteke ali jo postavite na mesto, kjer jo bo sistemski nalagalnik našel. Različice s podporo za V8 obstajajo le za PDF-je, ki vsebujejo JavaScript, ki ga želite izvesti (kar običajen bralnik ne počne), zato raje uporabite standardno knjižnico DLL, razen če imate konkreten razlog za nasprotno.
Nalaganje dokumenta brez zaupanja vnosu
Prvi instinkt je običajno, da nalaganje ovijete v blok try/except in sproženo izjemo obravnavate kot napako. Ta instinkt je v tem primeru napačen, njegova uporaba pa ustvari bralnik, ki deluje dobro le, dokler mu nekdo ne posreduje poškodovane datoteke. Nastavitev Active := True ob napaki pri nalaganju ne sproži izjeme. PDFium VCL ujame notranjo napako in pusti lastnost Active na vrednosti False, zato je edini zanesljiv način za preverjanje, ali se je dokument odprl, branje te lastnosti po njeni nastavitvi.
procedure TFormMain.OpenDocument(const FileName: string);
begin
Pdf.FileName := FileName;
Pdf.Active := True; // never raises; failure leaves Active = False
if not Pdf.Active then
begin
ShowMessage('Could not open ' + FileName);
Exit;
end;
PdfView.PageNumber := 1; // the view tracks its own current page
UpdatePageLabel;
end;
Pozorni morate biti na dve podrobnosti. Prva je, da lastnost PageNumber obstaja na obeh objektih in da sta neodvisni. Pdf.PageNumber je dokumentova predstava o trenutni strani; PdfView.PageNumber pa je stran, ki jo kontrola dejansko prikazuje, in to je tista, ki jo nastavite za pomikanje uporabnika skozi datoteko. Nastavitev ene ne premakne druge, zato bralnik vedno upravlja lastnost pogueda. Druga podrobnost je indeksiranje z začetkom pri 1: strani potekajo od 1 do Pdf.PageCount in ne od 0, kar lahko preseneti tiste, ki so navajeni na polja z začetnim indeksom nič.
Upravljanje šifrirane datoteke
Šifrirani dokumenti se vključijo v isti postopek nalaganja. Če je geslo za odpiranje nastavljeno pred aktivacijo, se dokument ob odpiranju dešifrira; če je napačno ali manjka, ostane lastnost Active na False, enako kot pri poškodovani datoteki. Rešitev je torej, da uporabnika pozovete k vnosu gesla in znova poskusite z aktivacijo.
procedure TFormMain.OpenWithPassword(const FileName: string);
var
Password: string;
begin
Pdf.FileName := FileName;
Pdf.Active := True;
if not Pdf.Active then
begin
if InputQuery('Password required', 'Password:', Password) then
begin
Pdf.Password := Password; // must be set before Active := True
Pdf.Active := True;
end;
if not Pdf.Active then
begin
ShowMessage('Unable to open the document.');
Exit;
end;
end;
PdfView.PageNumber := 1;
end;
Ker je napaka tiha tako pri napačnem geslu kot pri poškodovani datoteki, ju ne morete razlikovati samo na podlagi lastnosti Active. V praksi je to za bralnik sprejemljivo: uporabnik bodisi vnese pravilno geslo bodisi ugotovi, da se datoteka ne bo odprla, sporočilo pa je v obeh primerih enako.
Pomikanje po dokumentu
Ko je dokument odprt, je navigacija le matematično spreminjanje vrednosti PdfView.PageNumber v mejah Pdf.PageCount. Edino dejansko delo je omejevanje vrednosti (clamping), tako da gumbi nikoli ne potisnejo strani izven obsega, gumbi za prvo in zadnjo stran pa ostanejo onemogočeni na začetku in koncu datoteke.
procedure TFormMain.GoToPage(NewPage: Integer);
begin
if not Pdf.Active then
Exit;
if NewPage < 1 then
NewPage := 1
else if NewPage > Pdf.PageCount then
NewPage := Pdf.PageCount;
PdfView.PageNumber := NewPage;
UpdatePageLabel;
end;
// the four navigation buttons reduce to one call each
procedure TFormMain.FirstClick(Sender: TObject); begin GoToPage(1); end;
procedure TFormMain.PrevClick(Sender: TObject); begin GoToPage(PdfView.PageNumber - 1); end;
procedure TFormMain.NextClick(Sender: TObject); begin GoToPage(PdfView.PageNumber + 1); end;
procedure TFormMain.LastClick(Sender: TObject); begin GoToPage(Pdf.PageCount); end;
Polje za vnos »prikaži stran N« uporablja enak klic GoToPage s pretvorjenim celim številom, omejitev pa pokriva primer, ko uporabnik vpiše 9999 v dokument z desetimi stranmi. Naj bo UpdatePageLabel edino mesto, ki izpiše »Stran 3 od 12«, da se izpis nikoli ne razkladi s tem, kar prikazuje pogled.
Zoom: eksplicitni odstotki in načini prilagajanja
Zoom pri TPdfView je na voljo v dveh oblikah, ki medsebojno delujeta. Razumevanje tega delovanja določa razliko med zoom kontrolo, ki deluje pravilno, in tisto, ki se bori proti uporabniku. Neposredna pot je lastnost Zoom, ki predstavlja odstotek, pri čemer 100 pomeni dejansko velikost. Druga pot je FitMode, ki pogledu naroči, naj sam izračuna zoom in ga ponovno preračunava ob vsakem spreminjanju velikosti okna.
// fixed magnifications
PdfView.Zoom := 100; // actual size
PdfView.Zoom := 50; // half
PdfView.Zoom := 200; // double
// let the view size the page to the window, and keep it sized on resize
PdfView.FitMode := pfmFitWidth; // page width fills the control
PdfView.FitMode := pfmFitPage; // whole page visible
PdfView.FitMode := pfmActualSize; // 1:1 with the document's points
Tukaj je del, ki pogosto povzroča težave. Neposredna dodelitev vrednosti Zoom ponastavi FitMode na pfmNone. To je pravilno vedenje in ne napaka: v trenutku, ko uporabnik izbere natanko 150 %, pogled ne more več upoštevati nastavitve »prilagodi širini«, saj sta ti dve zahtevi v konfliktu. Posledica za vaš uporabniški vmesnik je, da sta gumb za povečavo in gumb za prilagajanje strani oknu medsebojno izključujoči se stanji, orodna vrstica pa mora prikazati aktivni način. Ko uporabnik klikne prilagajanje strani, nastavite FitMode; ko klikne številčni zoom, nastavite Zoom in pustite, da se način prilagajanja samodejno počisti.
Če bi raje sami izračunali vrednost prilagajanja, na primer za nastavitev drsnika za zoom s trenutnim odstotkom prilagajanja, vam pomočniki za posamezne strani ponudijo te številke brez spreminjanja načina. Lastnosti PageWidthZoom[N], PageZoom[N] in ActualSizeZoom[N] vrnejo odstotek, ki bi stran N prilagodil širini, jo prikazal v celoti ali jo izrisal v dejanski velikosti.
// seed a zoom readout from the fit-to-width value of the current page
var
FitPercent: Double;
begin
FitPercent := PdfView.PageWidthZoom[PdfView.PageNumber];
ZoomEdit.Text := Format('%.0f%%', [FitPercent]);
end;
Kaj dokončan bralnik dejansko potrebuje
Prvotni naslov morda obeta več dela, kot je potrebno. Zgornji bralnik obsega le nekaj deset vrstic in že opravlja vse, kar zahteva delovni proces z dokumenti: odpre datoteko, preživi napake, prikaže stran, se pomika med stranmi in spreminja povečavo ročno ali s prilagajanjem. PDFium težje dele opravi tiho. Vgrajene pisave se pravilno razrešijo, opombe in obrazci se izrišejo tam, kamor jih dokument postavi, stran, ki jo vidite, pa se ujema s tisto, kar vidi uporabnik brskalnika Chrome, saj ju izrisuje isti motor.
Iz tega izhodišča so vsi dodatki inkrementalni in ne strukturni. Izbira besedila in iskanje bereta iz iste plasti besedila, ki jo PDFium že gradi; metapodatki, kot sta Pdf.Title in Pdf.Author, so oddaljeni le eno branje lastnosti; vrtenje in siva lestvica (grayscale) sta možnosti izrisa, ki ju posredujete ob risanju strani na bitno sliko. Nič od tega ne spreminja ogrodja, ki ga imate tukaj, to pa so objekt dokumenta, pogled in povezovalni tok nalaganja in navigacije. Ko pravilno vzpostavite to ogrodje, je vse ostalo le še dekoracija.
Komponenti TPdf in TPdfView, uporabljeni v članku, sta del paketa PDFium VCL za Delphi in C++Builder, ki na svoji produktni strani ponuja celotno referenco bralnika.