PDF preglednik u Delphiju svodi se na dvije komponente i ožičenje (wiring) između njih. TPdf posjeduje dokument: otvara datoteku, dešifrira je te odgovara na pitanja o broju stranica i metapodacima. TPdfView je vizualna kontrola koja crta stranice na zaslonu i rješava pomicanje (scrolling), zumiranje i stranicu koju korisnik trenutno gleda. PDFium Component omotava (wraps) isti pogon za renderiranje koji se isporučuje unutar Chromea, tako da glifovi, izglađivanje rubova (anti-aliasing) i boja koju dobijete na platnu odgovaraju onome što vaši korisnici već vide u svom pregledniku. Posao nije u renderiranju. On se sastoji od povezivanja objekta dokumenta s pogledom, učitavanja bez pada na oštećenoj datoteci ili datoteci zaštićenoj lozinkom, te davanja korisniku pregršt kontrola zbog kojih se preglednik osjeća dovršenim: okretanje stranice, mijenjanje zumiranja, prilagođavanje (fit) stranice prozoru
Oovo prolazi kroz taj sklop (assembly) onim redoslijedom kojim ga zapravo gradite. Sve se ovdje renderira po jednu stranicu odjednom, što je ono što većina radnih tokova (workflows) s dokumentima želi. Ako trebate stranice naslagane u jednom stupcu za kontinuirano pomicanje, to je drugačija odluka o izgledu i to nije put ovdje
Povezivanje TPdf-a s TPdfView-om
Ispustite TPdf i TPdfView na obrazac (form), a zatim recite pogledu koji dokument da prikaže. Ta jedna dodjela cijela je poveznica između nevizualnog dokumenta i kontrole koja ga oslikava
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 (native) biblioteka PDFium-a mora biti na stroju. PDFium Component poziva u pdfium32.dll ili pdfium64.dll ovisno o vašoj ciljanoj platformi, a dokument se jednostavno odbija otvoriti ako se DLL ne može pronaći. Isporučite odgovarajući DLL pored svoje izvršne datoteke ili ga smjestite tamo gdje će ga učitavač (loader) sustava pronaći. Verzije (builds) s omogućenim V8-om postoje samo za PDF-ove koji nose JavaScript koji želite izvršiti, što običan preglednik ne radi, stoga posegnite za standardnim DLL-om osim ako nemate konkretan razlog da to ne učinite
Učitavanje dokumenta bez povjerenja u unos
Instinkt je zamotati učitavanje u try/except i tretirati izbačenu iznimku (thrown exception) kao neuspjeh. Taj instinkt je ovdje pogrešan, a pogreška proizvodi preglednik koji izgleda u redu sve dok mu netko ne preda pokvarenu (broken) datoteku. Postavljanje Active := True ne uzrokuje iznimku pri pogrešci učitavanja. PDFium Component hvata (catches) internu grešku i ostavlja Active postavljenim na False, tako da je jedini pošten (honest) način da saznate je li se dokument otvorio taj da pročitate svojstvo nakon što ste ga postavili
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 neovisna. Pdf.PageNumber je pojam dokumenta o trenutnoj stranici; PdfView.PageNumber je stranica koju kontrola zapravo prikazuje i ona je ta koju postavljate da biste korisnika premještali (move) kroz datoteku. Postavljanje jednog ne pomiče (move) drugo, tako da preglednik (viewer) uvijek pokreće (drives) svojstvo pogleda (view). Drugo je indeksiranje temeljeno na 1 (1-based indexing): stranice idu od 1 do Pdf.PageCount, a ne od 0, što hvata (catches) sve one koji su navikli na polja koja počinju s nulom
Rukovanje šifriranom datotekom
Šifrirani (encrypted) dokumenti preklapaju se (fold) u istu putanju (path) učitavanja. Ako je lozinka za otvaranje postavljena prije aktivacije, dokument se dešifrira (decrypts) dok se otvara; ako je pogrešna ili nedostaje, Active ostaje False točno kao i za oštećenu datoteku. Stoga je oporavak traženje lozinke (prompt for a password) i ponovni pokušaj 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 (failure) tih i za lošu lozinku i za oštećenu datoteku, ne možete ih razlikovati samo prema Active. U praksi je to prihvatljivo za preglednik: korisnik ili da ispravnu lozinku ili sazna da se datoteka neće otvoriti, a poruka se čita isto na oba načina
Listanje kroz dokument
Kada je dokument otvoren, navigacija je aritmetika na PdfView.PageNumber ograničena sa Pdf.PageCount. Jedini pravi posao je stezanje (clamping), tako da gumbi (buttons) nikada ne gurnu (push) stranicu izvan raspona, a prvi i zadnji gumb 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;
Tekstualni okvir "idi na stranicu N (go to page N)" isti je GoToPage poziv koji se dobiva iz parsiranog cijelog broja, a stezaljka (clamp) pokriva slučaj u kojem korisnik upisuje 9999 u datoteku od deset stranica. Zadržite UpdatePageLabel kao jedino mjesto koje piše "Stranica 3 od 12" tako da ispis (readout) nikada ne izađe iz sinkronizacije (drifts out of sync) s onim što pogled prikazuje
Zumiranje: eksplicitni postoci i načini prilagođavanja
Zumiranje na TPdfView dolazi u dva okusa (flavors) koja komuniciraju, a razumijevanje interakcije razlika je između kontrole zumiranja koja se ponaša kako treba i one koja se bori (fights) s korisnikom. Izravan (direct) put je svojstvo Zoom, postotak u kojem 100 znači stvarnu (actual) veličinu. Drugi je put FitMode, koji pogledu (view) kaže da umjesto vas izračuna (compute) zumiranje i nastavi ga iznova izračunavati (recomputing) kako se prozor mijenja (resizes)
// 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
Ovdje je dio koji sapliće ljude (trips people up). Izravno dodjeljivanje (assigning) Zoom-a resetira FitMode na pfmNone. To je ispravno ponašanje, a ne greška (bug): u trenutku kada korisnik odabere točnih 150%, pogled više ne može uvažavati "prilagođavanje širini (fit to width)" jer su ta dva zahtjeva u sukobu (conflict). Posljedica za vaše korisničko sučelje je ta da su gumb (button) za povećanje (zoom-in) i gumb za prilagođavanje stranici (fit-to-page) međusobno isključiva stanja, a alatna traka (toolbar) trebala bi aktivni način učiniti vidljivim. Kada korisnik klikne prilagodi stranici, postavite FitMode; kada kliknu numeričko (numeric) zumiranje, postavite Zoom i pustite ga da sam očisti način prilagođavanja
Ako biste radije sami izračunali vrijednost prilagođavanja (fit value), možda da biste posijali (seed) klizač zumiranja s trenutnim postotkom prilagođavanja (fit percentage), pomoćnici po stranici (per-page helpers) daju vam brojke bez mijenjanja načina. PageWidthZoom[N], PageZoom[N] i ActualSizeZoom[N] vraćaju postotak koji bi stranicu N prilagodio širini, prilagodio cijelu ili bi je renderirao u njezinoj stvarnoj (actual) 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 je dovršenom pregledniku zapravo potrebno
Gore navedeni preglednik (viewer) je nekoliko desetaka linija, a on već obavlja (does) posao (job) koji tijek rada (workflow) dokumenta treba: otvara datoteku, preživljava (survive) onu lošu, prikazuje stranicu, kreće se između stranica i mijenja povećanje (magnification) ručno ili prilagođavanjem (fit). PDFium radi teške (hard) dijelove tiho. Ugrađeni (embedded) fontovi se razrješavaju (resolve), bilješke (annotations) i polja obrasca (form fields) crtaju (paint) tamo gdje ih dokument postavlja, a stranica koju vidite odgovara (matches) onoj koju bi Chromeov korisnik vidio, jer ih crta (drawing) isti motor (engine)
Iz ove su baze dodaci (additions) inkrementalni (incremental), a ne strukturni. Odabir teksta i pretraživanje (search) čitaju s istog sloja (layer) teksta koji PDFium već gradi; metapodaci kao što su Pdf.Title i Pdf.Author udaljeni su (away) samo jedno čitanje (read) svojstva; rotacija i sivi tonovi (grayscale) su render opcije (options) koje prolazite (pass) kada crtate (draw) stranicu na bitmapu. Ništa od toga ne mijenja kralježnicu (spine) koju ovdje imate, a to su objekt dokumenta, pogled (view) i tok učitaj-zatim-navigiraj (load-then-navigate flow) koji ih povezuje (connecting). Ispravite (get right) tu kralježnicu i ostatak (rest) je ukras (decoration)
Komponente TPdf i TPdfView koje se ovdje koriste dio su PDFium Component-a za Delphi i C++Builder, koji nosi punu referencu preglednika na stranici svog proizvoda