Prohlížeč PDF v Delphi spočívá ve dvou komponentách a propojení mezi nimi. TPdf vlastní dokument: otevře soubor, dešifruje jej a odpovídá na dotazy týkající se počtu stránek a metadat. TPdfView je vizuální ovládací prvek, který vykresluje stránky na obrazovku a řeší posouvání, přiblížení a stránku, na kterou se uživatel právě dívá. Komponenta PDFium obaluje stejný renderovací engine, který je dodáván uvnitř Chrome, takže glyfy, vyhlazování (anti-aliasing) a barvy, které získáte na plátně, se shodují s tím, co uživatelé již vidí ve svém prohlížeči. Práce není ve vykreslování. Je v propojení objektu dokumentu s pohledem (view), v načtení poškozeného nebo heslem chráněného souboru bez pádu, a v poskytnutí hrstky ovládacích prvků uživateli, díky kterým působí prohlížeč dokončeně: otáčení stránek, změna přiblížení, přizpůsobení stránky oknu
Tento článek vás provede touto sestavou v pořadí, v jakém ji skutečně budujete. Vše zde vykresluje jednu stránku po druhé, což je to, co většina pracovních postupů s dokumenty požaduje. Pokud potřebujete stránky naskládané do jednoho souvisle rolujícího sloupce, jedná se o jiné rozhodnutí o rozložení a není to zdejší cesta
Propojení TPdf a TPdfView
Pusťte TPdf a TPdfView do formuláře a poté řekněte pohledu, jaký dokument má zobrazit. Toto jediné přiřazení je celým propojením mezi nevizuálním dokumentem a ovládacím prvkem, který jej vykresluje
procedure TFormMain.FormCreate(Sender: TObject);
begin
// Pdf a PdfView byly spuštěny v době návrhu (design time).
PdfView.Pdf := Pdf; // pohled vykresluje cokoliv, co tento dokument drží
PdfView.FitMode := pfmFitWidth; // spustí uživatele s rozumným přiblížením
end;
Předtím, než se cokoli z toho spustí, musí být nativní knihovna PDFium na daném stroji. Komponenta PDFium volá do pdfium32.dll nebo pdfium64.dll v závislosti na vaší cílové platformě, a dokument se jednoduše odmítne otevřít, pokud knihovna DLL nelze nalézt. Dodejte odpovídající knihovnu DLL vedle vašeho spustitelného souboru, nebo ji umístěte tam, kde ji systémový zavaděč najde. Sestavení s podporou V8 existují pouze pro PDF, které nesou JavaScript, jenž chcete spustit, což běžný prohlížeč nedělá, takže sáhněte po standardní DLL, ledaže byste měli konkrétní důvod to nedělat
Načtení dokumentu bez důvěry ve vstup
Instinktem je zabalit načítání do bloku try/except a zacházet s vyvolanou výjimkou jako se selháním. Tento instinkt je tady mylný a pokud se ho držíte, vytvoří to prohlížeč, který vypadá v pořádku, dokud mu někdo nepředá poškozený soubor. Nastavení Active := True nevyvolá při selhání načítání chybu. Komponenta PDFium zachytí vnitřní chybu a nechá hodnotu Active na False, takže jediný upřímný způsob, jak zjistit, zda se dokument otevřel, je načíst vlastnost zpět poté, co jste ji nastavili
procedure TFormMain.OpenDocument(const FileName: string);
begin
Pdf.FileName := FileName;
Pdf.Active := True; // nikdy nevyvolá výjimku; selhání ponechá Active = False
if not Pdf.Active then
begin
ShowMessage('Nepodařilo se otevřít ' + FileName);
Exit;
end;
PdfView.PageNumber := 1; // pohled si sleduje svou vlastní aktuální stránku
UpdatePageLabel;
end;
Dvě věci si zaslouží pozornost. První je, že PageNumber existuje na obou objektech a oba jsou nezávislé. Pdf.PageNumber je pojetí aktuální stránky v dokumentu; PdfView.PageNumber je stránka, kterou ovládací prvek skutečně zobrazuje, a to je ta, kterou nastavíte pro pohyb uživatele v souboru. Nastavení jedné nepřesune tu druhou, takže prohlížeč vždy řídí vlastnost pohledu. Druhou věcí je indexování založené na 1 (1-based): stránky běží od 1 do Pdf.PageCount, ne od 0, což zaskočí každého, kdo je zvyklý na pole indexovaná od nuly
Zpracování šifrovaného souboru
Šifrované dokumenty spadají do stejné cesty načítání. Pokud je heslo pro otevření nastaveno před aktivací, dokument se dešifruje při otevírání; pokud je špatné nebo chybí, Active zůstává False přesně tak, jako je tomu u poškozeného souboru. Obnovou je tedy vyzvat k zadání hesla a zkusit aktivaci znovu
procedure TFormMain.OpenWithPassword(const FileName: string);
var
Password: string;
begin
Pdf.FileName := FileName;
Pdf.Active := True;
if not Pdf.Active then
begin
if InputQuery('Vyžadováno heslo', 'Heslo:', Password) then
begin
Pdf.Password := Password; // musí být nastaveno před Active := True
Pdf.Active := True;
end;
if not Pdf.Active then
begin
ShowMessage('Nelze otevřít dokument.');
Exit;
end;
end;
PdfView.PageNumber := 1;
end;
Protože selhání je tiché jak pro špatné heslo, tak pro poškozený soubor, nemůžete odlišit tato dvě jen pomocí Active. V praxi to je pro prohlížeč přijatelné: uživatel buď poskytne správné heslo, nebo se dozví, že soubor se neotevře, a zpráva zní tak jako tak stejně
Procházení dokumentem
S otevřeným dokumentem je navigace aritmetikou nad PdfView.PageNumber omezenou Pdf.PageCount. Jedinou skutečnou prací je omezení mezí (clamping), aby tlačítka nikdy neposunula stránku mimo rozsah a první a poslední tlačítko zůstaly na okrajích souboru zakázány
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;
// čtyři navigační tlačítka se redukují na jedno volání pro každé
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;
Textové pole „Přejít na stránku N“ je to samé volání GoToPage zásobované analyzovaným celým číslem, a omezení (clamp) pokrývá případ, kdy uživatel napíše 9999 do desetistránkového souboru. Ponechte si UpdatePageLabel jako jediné místo, které vypíše „Stránka 3 z 12“, aby se zobrazená hodnota nikdy nerozešla s tím, co pohled ukazuje
Přiblížení: explicitní procenta a režimy přizpůsobení
Přiblížení v TPdfView přichází ve dvou variantách, které se ovlivňují, a pochopení této interakce je rozdílem mezi ovládáním přiblížení, které se chová slušně, a takovým, které bojuje s uživatelem. Přímou cestou je vlastnost Zoom, procento, kde 100 znamená skutečnou velikost. Další cestou je FitMode, který řekne pohledu, aby pro vás vypočítal přiblížení a průběžně ho přepočítával při změně velikosti okna
// fixní zvětšení
PdfView.Zoom := 100; // skutečná velikost
PdfView.Zoom := 50; // polovina
PdfView.Zoom := 200; // dvojnásobek
// nechat pohled přizpůsobit velikost stránky oknu a zachovat ji při změně velikosti
PdfView.FitMode := pfmFitWidth; // šířka stránky vyplní prvek
PdfView.FitMode := pfmFitPage; // viditelná celá stránka
PdfView.FitMode := pfmActualSize; // 1:1 k bodům dokumentu
Zde je část, která lidi zaskočí. Přímé přiřazení do Zoom resetuje FitMode na pfmNone. To je správné chování, ne chyba: v okamžiku, kdy uživatel vybere přesně 150 %, pohled už nemůže zároveň ctít „přizpůsobit na šířku“, protože tyto dva požadavky jsou v konfliktu. Důsledkem pro vaše uživatelské rozhraní je, že tlačítko pro přiblížení a tlačítko pro přizpůsobení stránce se navzájem vylučují, a nástrojová lišta (toolbar) by měla zviditelnit aktivní režim. Když uživatel klikne na přizpůsobit stránce, nastavte FitMode; když klikne na číselné přiblížení, nastavte Zoom a nechte jej vyčistit režim přizpůsobení podle svého uvážení
Pokud byste si raději hodnotu přizpůsobení počítali sami, třeba abyste naplnili posuvník přiblížení aktuálním procentem přizpůsobení, pomocníci vázaní ke stránce (per-page helpers) vám poskytnou hodnoty bez změny režimu. PageWidthZoom[N], PageZoom[N] a ActualSizeZoom[N] vrací procento, které by přizpůsobilo stranu N na šířku, přizpůsobilo ji celou, nebo ji vykreslilo ve skutečné velikosti
// nasazení zobrazení hodnoty přiblížení z procenta přizpůsobení na šířku aktuální stránky
var
FitPercent: Double;
begin
FitPercent := PdfView.PageWidthZoom[PdfView.PageNumber];
ZoomEdit.Text := Format('%.0f%%', [FitPercent]);
end;
Co dokončený prohlížeč skutečně potřebuje
Prohlížeč výše má několik desítek řádků a už dělá práci, kterou dokumentový workflow potřebuje: otevřít soubor, přežít špatný soubor, zobrazit stránku, přesouvat se mezi stránkami a měnit zvětšení ručně nebo přizpůsobením. PDFium dělá ty těžké části tiše. Vložená písma se vyřeší, anotace a pole formulářů se namalují tam, kam je dokument umístí, a stránka, kterou vidíte, odpovídá té, kterou by viděl uživatel Chrome, protože obojí kreslí tentýž engine
Od tohoto základu jsou doplňky spíše inkrementální než strukturální. Výběr textu a vyhledávání čtou ze stejné textové vrstvy, kterou PDFium již buduje; metadata jako Pdf.Title a Pdf.Author jsou vzdálená na přečtení jedné vlastnosti; rotace a stupně šedi jsou možnosti vykreslování, které předáváte, když kreslíte stránku do bitmapy. Nic z toho nemění zdejší kostru, kterou je objekt dokumentu, pohled a tok načti-potom-naviguj, který je spojuje. Zvládněte tuto kostru správně a zbytek je dekorace
Komponenty TPdf a TPdfView použité v celém textu jsou součástí PDFium Component pro Delphi a C++Builder, která nese na své produktové stránce kompletní reference k prohlížeči