Egy PDF-olvasó panel beágyazása egy Delphi üzleti alkalmazásba nem igényli leképezési ciklus (rendering loop), görgetőmotor vagy nagyítási modell megírását. A losLab PDF Library tartalmazza a TPDFlibViewer-t, egy VCL-vezérlőt, amely egyetlen hívással megnyit egy dokumentumot, és beépített viselkedésként biztosítja a szövegkijelölést, a kézi eltolást (panning), a keretes nagyítást, a pillanatképek készítését, a tartománykiemelést, a teljes szöveges keresési találatok kiemelését, a megjegyzésekkel (annotation) való interakciót és a nyomtatást. Ez a cikk végigvezeti Önt azon az öt lépésen, amellyel egy üres űrlapból működő nézegetőt készíthet: a vezérlő elhelyezése, a kész műveletek összekapcsolása, a keresési kiemelések hozzáadása, a megjegyzésekre való kattintások kezelése és a nyomtatás
Miért használjunk nézegető vezérlőt a leképezési ciklus megírása helyett?
Az első érv a rétegződés, mivel ez magyarázza meg, hogy valójában mit is vásárol. Egy oldal leképezése bitképbe csupán a könnyű 20 százalék; a többi 80 százalék minden egyéb, ami ezt körülveszi: egy oldal-bitkép gyorsítótár ürítési kerettel, görgetési elrendezés különböző méretű oldalak között, átméretezéskor újraszámolt nagyítási módok, találatvizsgálat (hit-testing), amely egy egérpixelt bármilyen nagyítás és forgatás mellett leképez az oldalkoordinátákra, valamint egy villódzásmentes újrarajzolási ütemezés. A TPDFlibViewer mindezeket birtokolja, és a raszteres munkát a többmotoros leképezésről szóló cikkben leírt motorra bízza, így a vezérlőréteg és a leképezőréteg külön-külön cserélhető marad. Az Ön űrlapkódja egy görgethető Windows-vezérlővel kommunikál, nem pedig egy leképezővel
Hogyan ágyazhat be PDF-nézegetőt egy Delphi alkalmazásba?
A TPDFlibViewer.LoadFromFile a vezérlő saját belső TPDFlib példányán keresztül nyit meg egy dokumentumot, és siker esetén 1-et ad vissza, így a minimális nézegető egy konstruktorhívásból, egy Parent hozzárendelésből és egy betöltésből áll. Ha már rendelkezik TPDFlib példánnyal az alkalmazás más részén, az AttachLibrary megjeleníti annak kiválasztott dokumentumát, miközben a tulajdonjog az Ön oldalán marad
uses PDFlibViewer;
procedure TMainForm.FormCreate(Sender: TObject);
begin
FViewer := TPDFlibViewer.Create(Self);
FViewer.Parent := Self;
FViewer.Align := alClient;
if FViewer.LoadFromFile('quarterly-report.pdf', '') = 1 then
FViewer.ViewerMode := vmHand; // eltolás húzással; a sima kattintás továbbra is követi a linkeket
end;
A ViewerMode tulajdonság határozza meg, hogy mit tesz az egér, öt lehetséges érték közül: a vmSelect az alapértelmezett szövegkijelölő eszköz, a vmHand húzással (fogó kurzorral) végzi az eltolást, miközben a sima kattintás továbbra is követi a linkeket, a vmZoom egy keretes nagyítás, amely a kijelölt területet a nézetablak kitöltéséhez nagyítja fel, a vmSnapshot a kijelölt tartományt a vágólapra másolja (egy OnSnapshot eseménnyel és egy SnapshotToClipboard kapcsolóval), a vmHighlight pedig tartós, áttetsző tartománykiemeléseket rajzol. A kiemelő eszköz koordinátáival kapcsolatban érdemes megjegyezni: a tartományok az oldal felhasználói terében (page user space) tárolódnak, nem pedig klienspixelekben, így az AddHighlightRegion jelölések túlélik a nagyítást, a nézet forgatását és az oldalak közötti navigációt anélkül, hogy Önnek bármilyen nyilvántartást kellene vezetnie. Forgatott nézetben a húzás iránya akár az oldaltengelyekkel szemben भी futhat, ami az inverz forgatási leképezés után negatív szélességet vagy magasságot eredményez — a vezérlő normalizálja a téglalapot az Ön számára
Hogyan kapcsolhat össze PDF eszköztárat Delphiben összekötő kód nélkül?
A PDFlibViewerActions egység teljesen szükségtelenné teszi az eszköztár összekötő kódját. Húsz kész TAction leszármazottat biztosít, amelyek lefedik az oldalléptetést (TPDFlibViewerFirstPage, TPDFlibViewerPriorPage, TPDFlibViewerNextPage, TPDFlibViewerLastPage), a nagyítást (TPDFlibViewerZoomIn, TPDFlibViewerZoomOut, TPDFlibViewerFitWidth, TPDFlibViewerFitPage, TPDFlibViewerActualSize), a nézet forgatását (TPDFlibViewerRotateClockwise, TPDFlibViewerRotateAntiClockwise), a nyomtatást, a fájlmegnyitási és mentési párbeszédpaneleket, a kijelölt szöveg másolását, valamint egéreszközönként egy kapcsoló műveletet (TPDFlibViewerSelectMode, TPDFlibViewerHandMode, TPDFlibViewerMarqueeZoomMode, TPDFlibViewerSnapshotMode, TPDFlibViewerHighlightMode). Mindegyik művelet a közzétett Viewer tulajdonságán keresztül egy vezérlőre mutat, és fenntartja a saját állapotát: minden művelet letiltja magát, ha nincs megnyitva dokumentum, a navigációs műveletek követik az oldalpozíciót, a másolás figyeli, hogy létezik-e kijelölés, a mód- és illeszkedési műveletek pedig szinkronban tartják bejelölt állapotukat az aktív eszközzel és nagyítási móddal. Tervezési időben mindegyik a losLab PDF kategóriában regisztrálódik az Action List szerkesztőben; futásidőben ugyanezek az osztályok mindössze három sorból állnak
Érdemes ismerni egy egységhatókörű (unit-scope) csapdát, ha saját műveletregisztrációt épít erre a mintára. A RegisterActions a System.Actions egységben van deklarálva, nem pedig a Vcl.ActnList-ben, és a fordító hibaüzenete, amelyet akkor kap, ha a uses záradékban csak a Vcl.ActnList szerepel, kifejezetten félrevezető: a [TAction1, TAction2, ...] osztálytömb-literál halmazként (set) lesz értelmezve, ami E2010 Incompatible types: 'Integer' and 'class of ...' hibák láncolatát eredményezi, mintha típuseltérés lenne a műveletosztályokban. A javítás egyetlen uses bejegyzés, de a diagnosztika mindenhova mutat, csak oda nem. A nagyító eszköztárak esetében a TPDFlibViewer.EffectiveZoomPercent a ténylegesen érvényben lévő nagyítást jelenti — beleértve a szélesség- és oldalillesztési módok számított tényezőjét is —, így a nagyítási lépés a valós aktuális nagyításra épülhet, nem pedig a legutóbb hozzárendelt értékre
Keresési találatok kiemelése és a mögötte lévő koordináta-tükrözés
A HighlightSearchHits a teljes dokumentumban keres egy kifejezést, és áttetsző sávot rajzol minden egyes találatra, visszatérve a találatok számával; a ClearSearchHighlights, a SearchHighlightCount és a SearchHighlightColor tulajdonság egészíti ki a funkciót. Mivel a találati téglalapok az oldaltérben tárolódnak, és minden rajzoláskor újravetítődnek, a sávok a szöveghez tapadva maradnak, miközben a felhasználó görgeti, nagyítja vagy forgatja a nézetet. Ha a kísérő TPDFlibSearchPanel-t használja, annak RunSearch metódusa automatikusan megrajzolja ugyanazokat a sávokat a csatlakoztatott nézegetőben a HighlightInView tulajdonságon keresztül, amely alapértelmezés szerint be van kapcsolva
procedure TMainForm.btnSearchClick(Sender: TObject);
var
Hits: Integer;
begin
FViewer.SearchHighlightColor := clYellow;
Hits := FViewer.HighlightSearchHits(edtQuery.Text, False); // kis- és nagybetűkre nem érzékeny
end;
A dolog finomsága, és amiért érdemesebb a beépített utat választani, mint a saját megvalósítást a nyers keresési eredményekből, a koordináta-rendszer konverziója. A szöveges motor a találati téglalapokat a könyvtár Origin (origó) beállításától függő koordinátákban jelenti: 0 vagy 3 Origin esetén az Y tengely alulról felfelé halad, mint a natív PDF felhasználói térben, míg 2 vagy 3 Origin esetén az X tengelyt a jobb széltől mérik. A nézegető átfedése felülről lefelé haladó, balról kezdődő oldaltérben működik, így minden találati téglalapot tükrözni kell (Y = PageHeight - Bottom, és ahol alkalmazható X = PageWidth - Right), mielőtt eltárolnák — ha az alapértelmezett origó mellett a jelentett felső élet készpénznek vesszük, a kiemelés függőlegesen tükrözött helyzetben jelenik meg az oldalon. A TPDFlibViewer elvégzi ezt a konverziót az aktív origó szerint minden egyes találat rögzítésekor, majd a rajzoláskor alkalmazza az előre irányuló forgatási leképezést, ezért esnek a sávok pontosan a helyükre minden szögben. Maga az áttetszőség a Windows AlphaBlend API-ját használja állandó forrás-alfa értékkel, mivel a VCL rajzlap (canvas) nem rendelkezik natív alfa-kompozícióval. A mögöttes szöveges keresési és geometriai API-kat a szöveges keresésről és az oldalelemek felsorolásáról szóló cikk tárgyalja részletesen
Megjegyzés-kattintások, tippek és mellékletek exportálása
A TPDFlibViewer első osztályú interaktív objektumokként kezeli a megjegyzéseket (annotations). Egy nem link típusú megjegyzésre való kattintás kiváltja az OnAnnotClick eseményt az oldalszámmal, a megjegyzés 1-alapú indexével és az altípus karakterláncával; az AnnotAtPagePoint programozottan teszi elérhetővé ugyanezt a találatvizsgálatot, és kihagyja a Popup típusú kísérő megjegyzéseket, így mindig azt a megjegyzést kapja meg, amelyet a felhasználó ténylegesen lát. Ha a kurzort egy megjegyzés fölé viszi, annak Contents szövege jelenik meg a vezérlő tippjeként (hint) az AnnotHints kapcsolón keresztül, amely alapértelmezés szerint be van kapcsolva, és együttműködik a szabványos VCL ShowHint tulajdonsággal. A FileAttachment megjegyzések esetén a SaveAnnotAttachmentToFile lemezre írja a beágyazott fájlt, és siker esetén 1-et ad vissza, ami néhány sorban megvalósítja a kattintással történő mentés folyamatát
procedure TMainForm.ViewerAnnotClick(Sender: TObject; APage, AnnotIndex: Integer;
const Subtype: WideString);
begin
if Subtype = 'FileAttachment' then
if FViewer.SaveAnnotAttachmentToFile(APage, AnnotIndex,
'C:\Temp\attached-invoice.xml') = 1 then
StatusBar1.SimpleText := 'Melléklet elmentve';
end;
Nyomtatás és a tervezendő korlátok
A nyomtatás nem igényel külön integrációt. A PrintDoc megjeleníti a szabványos Windows nyomtatási párbeszédpanelt — nyomtatóválasztás, oldaltartomány, példányszám, szétválogatás —, és átadja a feladatot a könyvtár nyomtatómotorjának, míg a PrintDoc(False) a teljes dokumentumot a felhasználói felület nélkül küldi el az alapértelmezett nyomtatónak. A PrintDocRange teljesen programozottan fut egy explicit nyomtatónévvel, kezdő- és végoldallal, példányszámmal és szétválogatási jelzővel, ami megfelelő belépési pont az ugyanabból a nézegető példányból indított kötegelt vagy szerver által kiváltott nyomtatási feladatokhoz
Két korlátot őszintén meg kell említeni. Először is, a TPDFlibViewer egy Windows VCL-vezérlő; a nézegető réteg ki van zárva a NOVCL-buildből, így a konzolos szolgáltatásoknak és a nem VCL-célpontoknak közvetlenül a mögöttes TPDFlib leképezési API-t kell használniuk. Másodszor, a fejléc nélküli (headless) használat szándékosan támogatott, de korlátozott: a CaptureRegion bármely kliensrégiót — oldalakat és kiemeléseket — leképez egy hívó tulajdonában lévő bitképbe, és ablakkezelő (window handle) nélküli, szülő nélküüli vezérlőn is működik, mivel a rajzolási útvonal elkerüli azokat a VCL-hívásokat, amelyek kényszerítenék a kezelő létrehozását. Ez praktikussá teszi a képpontszintű ellenőrzést az automatizált tesztekben, de minden, aminek valóban szüksége van üzenetciklusra (message loop), mint például az interaktív egéreszközök, továbbra is valódi felhasználói felületi munkamenethez tartozik. Teljesítmény oldalon a vezérlő egy konfigurálható keretű memóriabeli oldalgyorsítótárat tart fenn a SetCacheLimit segítségével, és képes megőrizni a leképezett oldalakat a munkamenetek között; a nézegető lemezes oldalgyorsítótáráról szóló cikk részletesen foglalkozik ezzel a réteggel és annak monitoronkénti DPI viselkedésével
Tehát öt lépés: helyezze el a vezérlőt és hívja meg a LoadFromFile-t, csatolja a kész műveleteket a PDFlibViewerActions egységből, hívja meg a HighlightSearchHits-et az oldalon belüli kereséshez, kezelze az OnAnnotClick-et a megjegyzés-munkafolyamatokhoz, és tegye elérhetővé a PrintDoc-ot. Minden itt bemutatott elem a szabványos losLab PDF Library for Delphi csomag részét képezi, amelyben a nézegető vezérlő, a műveleti osztályok és a leképezőmotor együtt települ