Két dokumentum egyszerre megnyitva, azonos oldalszám, mindegyik a saját görgethető (scrollable) paneljében: ez egy összehasonlító megjelenítő (comparison viewer) magja. A PDFium Component ezt egy egyértelmű objektummodellen keresztül biztosítja, ahol a TPdf birtokolja a fájlt, a TPdfView pedig a megjelenítést (display). Egy dokumentum, egy TPdf, egy TPdfView. Ha három panelt szeretne, akkor három párja van. A nehéz részek nem az API hívások; hanem az elrendezés (layout) aritmetikája az ablak átméretezésekor, és az oldal-szinkronizálási logika, amikor eldönti, melyik nézetnek melyiket kell követnie
Űrlap elrendezése (Form Layout)
A VCL űrlap három TScrollBox tárolót tartalmaz egymás mellett, mindegyikben egy-egy TPdfView található, amely az alClient-re van igazítva, így kitölti a dobozt. Két TSplitter komponens ül a dobozok között, hogy a felhasználó futásidőben (runtime) beállíthassa az oszlopszélességeket. A panelek feletti eszköztár (toolbar) hordozza a megnyitás gombokat, a nagyítás (zoom) vezérlőket, és a kétnézetes / háromnézetes (two-view / three-view) váltót (toggle)
A háromnézetes mód egy boolean, amelyet az űrlap belsőleg (internally) követ nyomon. Amikor átvált, ön újraszámolja a szélességeket, és megjeleníti vagy elrejti a harmadik oszlopot. A legegyszerűbb megközelítés az összes Align tulajdonság törlése, az elválasztók (splitters) elrejtése, majd abszolút pozíciók beállítása:
procedure TFormMain.UpdateLayout;
var
TotalWidth: Integer;
begin
TotalWidth := ClientWidth;
if ThreeViewMode then
begin
ScrollBox3.Visible := True;
ScrollBox1.Left := 0;
ScrollBox1.Width := TotalWidth div 3;
ScrollBox2.Left := ScrollBox1.Width;
ScrollBox2.Width := TotalWidth div 3;
ScrollBox3.Left := ScrollBox2.Left + ScrollBox2.Width;
ScrollBox3.Width := TotalWidth - ScrollBox3.Left;
// Apply the same (ClientHeight - toolbar height) to all three Height values
end
else
begin
ScrollBox3.Visible := False;
ScrollBox1.Left := 0;
ScrollBox1.Width := TotalWidth div 2;
ScrollBox2.Left := ScrollBox1.Width;
ScrollBox2.Width := TotalWidth - ScrollBox2.Left;
end;
end;
Az Align := alNone beállítása mindhárom dobozon az egész számú (integer) aritmetika előtt elkerüli, hogy a VCL kényszermotor (constraint engine) harcoljon az ön hozzárendeléseivel. Állítsa vissza az elválasztó (splitter) láthatóságát a pozícionálás után, ha a húzással történő átméretezést (drag-to-resize) szeretné használni kétnézetes módban
Minden gördítődoboz (scroll box) magassága a kliens terület (client area) mínusz az eszköztár paneljének magassága. Mivel az eszköztár a tetejére van dokkolva (docked) az alTop-pal, a ClientHeight - PanelButtons.Height adja meg a használható függőleges teret. Rendelje ezt hozzá mindhárom dobozhoz ugyanazon az UpdateLayout híváson belül, így soha nem lesz olyan képkocka (frame), ahol az egyik doboz magasabb lenne a többinél, ami elrendezés-villódzást (layout flicker) okozna
Egy dokumentum megnyitása
Minden panelpárnak szüksége van a saját megnyitási eljárására (open procedure). A minta rövid: inaktiválja a komponenst, állítsa be a fájlnevet, próbálja meg aktiválni, kapja el az EPdfError-t, ha a fájl jelszót igényel. Vegye figyelembe, hogy a TPdfView.Active az, ami a renderelést vezérli, de a TPdf.Active az, ami ténylegesen megnyitja a fájlt; ezek függetlenek. A PdfView.Active := True beállítása, amikor a hozzá kapcsolt TPdf még nem aktív, ártalmatlan, de nem jelenít meg semmit
procedure TFormMain.OpenPdfFile(PdfComponent: TPdf;
PdfViewComponent: TPdfView);
var
Password: string;
begin
if not OpenDialog.Execute then
Exit;
PdfComponent.Active := False;
PdfComponent.FileName := OpenDialog.FileName;
PdfComponent.Password := '';
try
PdfComponent.Active := True;
except
on E: EPdfError do
begin
if InputQuery('Password', 'Enter document password:', Password) then
begin
PdfComponent.Password := Password;
PdfComponent.Active := True;
end
else
raise;
end;
end;
if PdfComponent.Active then
begin
PdfViewComponent.PageNumber := 1;
SetActivePdfView(PdfViewComponent);
end;
end;
Mindig ellenőrizze a PdfComponent.Active-ot a hozzárendelés után; egy sérült fájl vagy rossz jelszó miatt a betöltés (load) csendben meghiúsul anélkül, hogy kivételt vetne fel (raising an exception) az alapértelmezett útvonalon. A PdfViewComponent.PageNumber := 1 explicit beállítása egy sikeres megnyitás után elkerüli az előző dokumentumból visszamaradt, elavult (stale) oldalszámot
A fenti jelszókezelő (password handling) kód minden egyéb hibára kivételt dob (raises), kivéve az ismert jelszóüzenetet. Ez szándékos: azt akarja, hogy a sérült vagy nem támogatott fájlok azonnal felszínre kerüljenek, ahelyett, hogy egy csendes üres panelként elnyelődnének (swallowed). A felhasználónak, aki nem lát semmit, fogalma sincs arról, hogy a fájl betöltődött-e és egyszerűen üres, vagy a komponens utasította el (rejected). A kivételdobás (raising) láthatóan tartja a hibát
Aktív panel követése (Active Panel Tracking)
Amikor a felhasználó egy panelen belül kattint, az a panel aktívvá válik. Az űrlap egy privát FActivePdfView: TPdfView mezőt követ nyomon. A vizuális visszajelzés a szegélyszín (border color) megváltozása a tartalmazó TScrollBox-on: állítsa be clHighlight-ra az aktív számára, és clWindow-ra a többinél. Kösse be (wire) ezt minden TPdfView.OnClick-hez és a megnyitási eljáráshoz, így a fókusz az éppen megnyitott dokumentumot követi
Egyes műveletek (operations) az összes látható panelre vonatkoznak, nem csak az aktívra. Az űrlapon lévő logikai (boolean) FAllViewsMode vezérli ezt az ágat (branch). Ha igaz, a nagyítás (zoom) változásai és az oldalnavigáció szétágazik (fan out) minden olyan panelre, amely aktív dokumentummal rendelkezik:
procedure TFormMain.ApplyZoomToAll(NewZoom: Double);
begin
if PdfView1.Active then PdfView1.Zoom := NewZoom;
if PdfView2.Active then PdfView2.Zoom := NewZoom;
if ThreeViewMode and PdfView3.Active then PdfView3.Zoom := NewZoom;
end;
Szinkronizált oldalnavigáció (Synchronized Page Navigation)
A szinkronizált navigáció opcionális, de hasznos olyan dokumentum-revíziós munkafolyamatokhoz (workflows), ahol mindkét fájl ugyanazt az oldaltartományt fedi le. A logika egy olyan eseménykezelőbe (event handler) tartozik, amely azután tüzel (fires), hogy a felhasználó az egyik nézetben navigált. Amikor egy forrásnézet megváltoztatja a PageNumber-ét, a kezelő propagálja ezt a számot a többi nézetre, egyetlen őr (guard) feltétellel: a cél nézetnek (target view) legalább annyi oldallal kell rendelkeznie, különben hagyja ki (skip)
A PageNumber a TPdfView-n és a TPdf-en függetlenek egymástól. A TPdf.PageNumber nyomon követi, hogy a dokumentum komponens melyik oldalt tekinti aktuálisnak; a TPdfView.PageNumber nyomon követi, hogy mi jelenik meg a képernyőn. Navigációs célokra a nézet tulajdonságot (view property) szeretné használni, nem pedig a dokumentum tulajdonságot (document property)
Egy olyan feliratú jelölőnégyzet (checkbox), mint a "Sync pages" (Oldalak szinkronizálása) átadja a vezérlést a felhasználónak. Ha nincs bejelölve, minden panel függetlenül navigál, és a kezelő (handler) azonnal kilép. Ez a függetlenség fontos olyan használati esetekben (use cases), ahol a két dokumentum oldalszáma eltér, vagy ahol a felhasználó a megfelelő szakaszt (passage) szeretné megtalálni egy fordításban, amely egy másik oldalon kezdődik. A szinkronizálás mindig történő kikényszerítése nehezebben használhatóvá tenné az eszközt, mint egy egyszerű kétablakos (two-window) asztali elrendezés
Még egy dolog, amire figyelni kell: a PdfView.PageNumber programozott beállítása a szinkronizálási kezelőn (sync handler) belül maga is kiváltja (trigger) a változási eseményt (change event) azon a nézeten. Védekezzen (guard) a végtelen rekurzió ellen egy logikai (boolean) jelzővel (flag), amelyet a hozzárendelés (assignment) előtt beállít, és közvetlenül utána töröl. A jelző űrlaponkénti (per-form), nem pedig nézetenkénti (per-view), mert mind a három nézet osztozik ugyanazon a kezelőn
Nagyítás (Zoom) panelenként
Minden TPdfView hordozza a saját Zoom tulajdonságát, ami egy Double százalékban kifejezve, ahol a Zoom := 100 a tényleges méretet (100%) jelenti. Ennek beállítása felülbírál bármilyen aktív FitMode-ot (illesztési módot). Egy szélességhez illesztő (fit-to-width) gomb esetén az aktív panelen olvassa ki az illesztési nagyítást a PdfView.PageWidthZoom[PdfView.PageNumber]-ből, és rendelje hozzá. Az oldalhoz illesztéshez (fit-to-page) használja a PageZoom[PageNumber]-t. Mindkettő 1-alapú oldalszámmal indexelt tömbtulajdonság (array properties), így védekezzen (guard) a nulla oldalszám ellen a hozzáférésük előtt
Amikor az aktuális oldalt egy képre exportálja, olvassa be az elforgatást (rotation) a nézetből, de hívja meg a RenderPage-et a TPdf komponensen, ne a nézeten. A TPdf.RenderPage bittérképes (bitmap) formája explicit pixelméreteket, egy TRotation értéket és egy TRenderOptions halmazt (set) vár el. A függvényváltozat (function variant) egy hívó által birtokolt (caller-owned) TBitmap-et ad vissza, amelyet a mentés után önnek magának kell felszabadítania (free):
procedure TFormMain.SaveActiveViewAsImage;
var
Pdf: TPdf;
Bmp: TBitmap;
Jpeg: TJpegImage;
begin
if not Assigned(FActivePdfView) or not FActivePdfView.Active then
Exit;
Pdf := FActivePdfView.Pdf;
Pdf.PageNumber := FActivePdfView.PageNumber;
Bmp := Pdf.RenderPage(
0, 0,
Round(Pdf.PageWidth * 2),
Round(Pdf.PageHeight * 2),
FActivePdfView.Rotation, [], clWhite);
try
if SavePictureDialog.Execute then
begin
Jpeg := TJpegImage.Create;
try
Jpeg.Assign(Bmp);
Jpeg.CompressionQuality := 90;
Jpeg.SaveToFile(SavePictureDialog.FileName);
finally
Jpeg.Free;
end;
end;
finally
Bmp.Free;
end;
end;
A 2x-es szorzó (multiplier) a szélességen és a magasságon élesebb kimenetet ad a finom szöveget (fine text) tartalmazó dokumentumok esetében. A bittérkép felszabadítását körülvevő try/finally nem opcionális; egy TSaveDialog megszakítása (cancel) továbbra is eléri a finally blokkot, és ön azt szeretné, ha a bittérkép felszabadulna (released), függetlenül attól, hogy mit csinált a felhasználó
DLL követelmények
A PDFium Component becsomagolja (wraps) a natív pdfium könyvtárat. Egy 32 bites gazdafolyamatnak (host process) a pdfium32.dll-re van szüksége; egy 64 bites gazdának a pdfium64.dll-re. A V8 JavaScript motorral (engine) rendelkező változatok a v8 utótagot (suffix) kapják, és nagyjából 23-27 MB-ot nyomnak, szemben az 5-6 MB-os standard buildekkel (builds). Egy összehasonlító megjelenítőhöz (comparison viewer), amely letiltja az űrlapkitöltést (form filling, Pdf.FormFill := False), a standard, nem V8-as build elegendő, és kisebb méretű elosztást (distribution) tesz lehetővé
Helyezze el a DLL-t ugyanabban a könyvtárban, mint a végrehajtható fájl (executable), vagy a rendszer PATH bármely könyvtárába. A komponens igény szerint (on demand) tölti be, amikor az első TPdf aktiválódik, így a hiányzó DLL ezen a ponton kerül felszínre (surfaces), nem pedig az alkalmazás indításakor. Ha telepítőt (installer) szállít (ship), a legmegbízhatóbb megközelítés a DLL bemásolása az alkalmazás mappájába a telepítés során, ahelyett, hogy egy olyan rendszerkönyvtárra hagyatkozna, amelyet egy rendszergazda (administrator) később esetleg kitakarít
A V8-as buildek elsősorban akkor hasznosak, ha PDF JavaScript műveletekkel (actions) kell interakcióba lépnie, például számítási mezők (calculation fields) vagy beküldési kezelők (submit handlers) kiváltásához (trigger). Egy passzív összehasonlító megjelenítőnek (passive comparison viewer) semmi oka sincs a JavaScript futtatására; a Pdf.FormFill := False beállítása az Active := True előtt teljesen átugorja az űrlapkitöltő (form-fill) környezetet, ami azt is jelenti, hogy semmilyen JS motor nem inicializálódik (initialized), még akkor sem, ha a standard buildet használják. Ez a helyes alapértelmezés (default) egy csak olvasható (read-only) megjelenítő számára, függetlenül attól, hogy melyik DLL változatot (variant) szállítja
A PDFium Component komponensről és a teljes API-járól további részletekért látogasson el a Delphi PDFium Component termékoldalára
A frissített példafolyamat lefedi az űrlap elrendezését, a dokumentum megnyitását, az aktív panel követését, a szinkronizált oldalnavigációt, a panelenkénti nagyítást és a DLL-eket