Preglednik PDF-a u Delphiju svodi se na dvije komponente i njihovo međusobno povezivanje. TPdf upravlja dokumentom: otvara datoteku, dešifrira je i odgovara na upite o broju stranica i metapodacima. TPdfView je vizualna kontrola koja iscrtava stranice na zaslonu te upravlja pomicanjem (scrolling), zumiranjem i stranicom koju korisnik trenutno gleda. PDFium VCL koristi isti mehanizam za iscrtavanje (rendering engine) koji se isporučuje s preglednikom Chrome, tako da glifovi, zaglađivanje (anti-aliasing) i boje na platnu odgovaraju onome što vaši korisnici već vide u svom pregledniku. Glavni zadatak nije u samom renderiranju. On se sastoji od povezivanja objekta dokumenta s prikazom, učitavanja bez rušenja aplikacije na oštećenoj datoteci ili datoteci zaštićenoj lozinkom, te pružanja korisniku onih nekoliko osnovnih kontrola koje preglednik čine potpunim: okretanje stranica, promjena zumiranja i prilagodba stranice prozoru.
Ovaj vodič prolazi kroz taj postupak redoslijedom kojim ga stvarno gradite. Sve što je ovdje opisano prikazuje jednu po jednu stranicu, što je i najčešći zahtjev u obradi dokumenata. Ako trebate stranice naslagane u jednoj neprekidnoj koloni koja se pomiče, to je drugačija odluka o izgledu koja je pokrivena u zasebnom članku i nije dio ovog puta.
Povezivanje TPdf i TPdfView
Postavite TPdf and TPdfView na formu, a zatim recite prikazu koji dokument treba prikazati. Ta jednostavna dodjela predstavlja cjelokupnu vezu između nevizualnog dokumenta i kontrole koja ga iscrtava.
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;
Prije nego što se išta od ovoga pokrene, nativna PDFium knjižnica mora biti na računalu. PDFium VCL poziva pdfium32.dll ili pdfium64.dll ovisno o vašoj ciljnoj platformi, a dokument se jednostavno neće otvoriti ako se DLL ne može pronaći. Isporučite odgovarajući DLL uz svoju izvršnu datoteku ili ga smjestite tamo gdje će ga sustav sam pronaći. Verzije s omogućenim V8 mehanizmom postoje samo za PDF-ove koji sadrže JavaScript koji želite izvršiti, što običan preglednik ne zahtijeva, pa koristite standardni DLL osim ako nemate konkretan razlog protiv toga.
Učitavanje dokumenta bez povjerenja u ulazne podatke
Instinkt nas navodi da učitavanje omotamo u try/except blok i tretiramo bačenu iznimku kao neuspjeh. Taj je instinkt ovdje pogrešan, a njegovo slijeđenje stvara preglednik koji izgleda dobro sve dok vam netko ne prosljedi oštećenu datoteku. Postavljanje Active := True ne podiže iznimku u slučaju neuspjeha učitavanja. PDFium VCL hvata internu pogrešku i ostavlja svojstvo Active na vrijednosti False, pa je jedini točan način da saznate je li se dokument otvorio čitanje tog svojstva nakon što ga postavite.
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;
Dvije stvari zaslužuju pažnju. Prva je da PageNumber postoji na oba objekta i da su oni neovisni. Pdf.PageNumber je dokumentov koncept trenutne stranice; PdfView.PageNumber je stranica koju kontrola zapravo prikazuje i to je ona koju postavljate kako biste korisnika pomicali kroz datoteku. Postavljanje jednog ne pomiče drugo, tako da preglednik uvijek upravlja svojstvom prikaza. Druga stvar je indeksiranje koje počinje od 1: stranice idu od 1 do Pdf.PageCount, a ne od 0, što može zbuniti svakoga tko je navikao na polja s početnim indeksom nula.
Rukovanje šifriranom datotekom
Šifrirani dokumenti uklapaju se u isti tijek učitavanja. Ako se lozinka za otvaranje postavi prije aktivacije, dokument se dešifrira prilikom otvaranja; ako je pogrešna ili nedostaje, Active ostaje na False, baš kao i kod oštećene datoteke. Stoga se rješenje sastoji u traženju lozinke od korisnika i ponovnom pokušaju aktivacije.
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;
Budući da je neuspjeh tih i za pogrešnu lozinku i za oštećenu datoteku, ne možete ih razlikovati samo na temelju svojstva Active. U praksi je to prihvatljivo za preglednik: korisnik ili unese ispravnu lozinku ili sazna da se datoteka ne može otvoriti, a poruka je ista u oba slučaja.
Kretanje kroz stranice dokumenta
Nakon što je dokument otvoren, navigacija je aritmetika nad svojstvom PdfView.PageNumber ograničena s Pdf.PageCount. Jedini stvarni posao je ograničavanje vrijednosti (clamping), tako da gumbi nikada ne pomaknu stranicu izvan raspona, a gumbi za prvu i zadnju stranicu ostaju onemogućeni na krajevima 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 unos teksta "idi na stranicu N" poziva istu metodu GoToPage s analiziranim cijelim brojem, a ograničavanje pokriva slučaj kada korisnik utipka 9999 u datoteku od deset stranica. Neka UpdatePageLabel bude jedino mjesto koje ispisuje poruku poput "Stranica 3 od 12", tako da prikazani broj nikada ne odudara od onoga što se stvarno prikazuje.
Zumiranje: točni postoci i načini prilagodbe
Zumiranje na TPdfView dolazi u dva oblika koji međusobno djeluju, a razumijevanje te interakcije čini razliku između kontrole zumiranja koja se ponaša kako treba i one koja se bori protiv korisnika. Izravni put je svojstvo Zoom, postotak u kojem 100 označava stvarnu veličinu. Drugi put je FitMode, koji govori prikazu da sam izračuna zumiranje i da ga ponovno izračunava prilikom svake promjene veličine prozora.
// 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
Evo dijela na kojem se programeri često spotaknu. Izravno dodjeljivanje vrijednosti svojstvu Zoom resetira FitMode na pfmNone. To je ispravno ponašanje, a ne bug: onog trenutka kada korisnik odabere točno 150%, prikaz više ne može poštovati opciju "prilagodi širini" jer su ta dva zahtjeva u konfliktu. Posljedica za vaše korisničko sučelje je da su gumb za zumiranje i gumb za prilagodbu stranice međusobno isključiva stanja, a alatna traka trebala bi jasno prikazati aktivni način rada. Kada korisnik klikne na prilagodbu stranice, postavite FitMode; kada klikne na numeričko zumiranje, postavite Zoom i pustite da se način prilagodbe sam poništi.
Ako radije sami želite izračunati vrijednost prilagodbe, primjerice kako biste klizač za zumiranje inicijalizirali s trenutnim postotkom prilagodbe, pomoćne funkcije po stranici pružaju vam te brojeve bez promjene načina rada. PageWidthZoom[N], PageZoom[N] i ActualSizeZoom[N] vraćaju postotak koji bi stranicu N prilagodio širini, prilagodio cijelu ili je prikazao u stvarnoj veličini.
// 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;
Što dovršeni preglednik zapravo treba
Izvorni naslov preuveličava količinu posla. Gore opisani preglednik ima svega nekoliko desetaka redaka, a već obavlja posao koji je potreban za rad s dokumentima: otvara datoteku, preživljava oštećenu, prikazuje stranicu, kreće se među stranicama i mijenja povećanje ručno ili prilagodbom. PDFium tiho odrađuje teže dijelove. Ugrađeni fontovi se razrješavaju, bilješke (annotations) i polja obrazaca iscrtavaju se tamo gdje ih dokument postavi, a stranica koju vidite odgovara onoj koju bi vidio korisnik preglednika Chrome, jer ih iscrtava isti mehanizam.
Od ove osnove, dodaci su inkrementalni, a ne strukturalni. Odabir teksta i pretraga čitaju iz istog tekstualnog sloja koji PDFium već izgrađuje; metapodaci kao što su Pdf.Title and Pdf.Author udaljeni su samo jedno čitanje svojstva; rotacija i crno-bijeli prikaz su opcije iscrtavanja koje prosljeđujete kada stranicu crtate u bitmapu. Ništa od toga ne mijenja kralježnicu sustava koju ovdje imate, a to su objekt dokumenta, prikaz i tijek učitavanja i navigacije koji ih povezuje. Uspostavite tu kralježnicu kako treba, a sve ostalo je samo ukras.
Komponente TPdf i TPdfView korištene u ovom članku dio su paketa PDFium VCL za Delphi i C++Builder, koji sadrži cjelovitu dokumentaciju preglednika na svojoj stranici proizvoda.