Norint integruoti PDF skaitymo skydelį į Delphi verslo programą, nereikia rašyti atvaizdavimo ciklo, slinkimo variklio ar mastelio keitimo modelio. „losLab PDF Library“ pateikia „TPDFlibViewer“ – VCL valdiklį, kuris atidaro dokumentą vienu iškvietimu ir suteikia teksto parinkimą, panoramavimą, mastelio keitimą rėmeliu, momentines nuotraukas, sričių paryškinimą, pilno teksto paieškos atitikmenų paryškinimą, sąveiką su anotacijomis ir spausdinimą kaip numatytąją elgseną. Šiame straipsnyje aprašomi penki žingsniai, kurie paverčia tuščią formą veikiančia peržiūros programa: valdiklio įterpimas, paruoštų veiksmų susiejimas, paieškos paryškinimo pridėjimas, anotacijų spustelėjimų apdorojimas ir spausdinimas
Kodėl verta naudoti peržiūros valdiklį, užuot rašius atvaizdavimo ciklą?
Pirmiausia verta paminėti sluoksniavimą, nes jis paaiškina, ką iš tikrųjų gaunate. Puslapio atvaizdavimas bitų žemėlapyje sudaro lengvuosius 20 procentų; kiti 80 procentų yra viskas, kas jį supa: puslapių bitų žemėlapių talpykla su išvalymo biudžetu, slinkimo išdėstymas skirtingų dydžių puslapiuose, mastelio keitimo režimai, kurie perskaičiuojami keičiant lango dydį, pataikymo tikrinimas (hit-testing), kuris susieja pelės tašką atgal su puslapio koordinatėmis bet kokiu masteliu ir pasukimu, bei perpiešimo planavimas be mirgėjimo. „TPDFlibViewer“ valdo visa tai ir deleguoja rasterizavimo darbą tam pačiam varikliui, aprašytam straipsnyje apie atvaizdavimą naudojant kelis variklius, todėl valdymo sluoksnis ir atvaizdavimo sluoksnis išlieka atskirai keičiami. Jūsų formos kodas bendrauja su slenkamu „Windows“ valdikliu, o ne su atvaizdavimo varikliu
Kaip integruoti PDF peržiūros programą Delphi programoje?
„TPDFlibViewer.LoadFromFile“ atidaro dokumentą per paties valdiklio vidinį „TPDFlib“ egzempliorių ir sėkmės atveju grąžina 1, todėl minimaliai peržiūros programai reikia tik konstruktoriaus iškvietimo, „Parent“ priskyrimo ir vieno įkėlimo. Jei jau turite „TPDFlib“ egzempliorių kitoje programos vietoje, „AttachLibrary“ vietoj to parodys jo pasirinktą dokumentą, o nuosavybės teisė liks jūsų pusėje
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; // pan by dragging; a plain click still follows links
end;
Savybė „ViewerMode“ parenka, ką daro pelė, iš penkių reikšmių: „vmSelect“ yra numatytasis teksto parinkimo įrankis, „vmHand“ panoramuoja tempiant su sugriebimo žymekliu, o paprastas spustelėjimas vis tiek seka nuorodas, „vmZoom“ yra mastelio keitimas rėmeliu, kuris padidina nuvilktą sritį, kad ji užpildytų peržiūros sritį, „vmSnapshot“ užfiksuoja nuvilktą sritį į iškarpinę (su „OnSnapshot“ įvykiu ir „SnapshotToClipboard“ jungikliu), o „vmHighlight“ piešia išliekančius permatomus sričių paryškinimus. Paryškinimo įrankio koordinates verta paminėti atskirai: sritys saugomos puslapio naudotojo erdvėje, o ne kliento taškeliuose, zodžiu, „AddHighlightRegion“ žymos išlieka keičiant mastelį, sukant vaizdą ir naršant puslapius be jokios papildomos apskaitos jūsų pusėje. Pasuktame vaizde tempimo kryptis gali netgi nesutapti su puslapio ašimis, kas po atvirkštinio pasukimo susiejimo duoda neigiamą plotį arba aukštį — valdiklis normalizuoja stačiakampį už jus
Kaip prijungti PDF įrankių juostą Delphi nenaudojant rišamojo kodo?
„PDFlibViewerActions“ modulis visiškai pašalina įrankių juostos rišamąjį kodą. Jame pateikiama dvidešimt paruoštų „TAction“ įpėdinių, apimančių puslapių naršymą („TPDFlibViewerFirstPage“, „TPDFlibViewerPriorPage“, „TPDFlibViewerNextPage“, „TPDFlibViewerLastPage“), mastelio keitimą („TPDFlibViewerZoomIn“, „TPDFlibViewerZoomOut“, „TPDFlibViewerFitWidth“, „TPDFlibViewerFitPage“, „TPDFlibViewerActualSize“), vaizdo sukimą („TPDFlibViewerRotateClockwise“, „TPDFlibViewerRotateAntiClockwise“), spausdinimą, failų atidarymo ir išsaugojimo dialogus, pasirinkto teksto kopijavimą ir po vieną perjungimo veiksmą kiekvienam pelės įrankiui („TPDFlibViewerSelectMode“, „TPDFlibViewerHandMode“, „TPDFlibViewerMarqueeZoomMode“, „TPDFlibViewerSnapshotMode“, „TPDFlibViewerHighlightMode“). Kiekvienas veiksmas nukreipia į valdiklį per jo viešąją „Viewer“ savybę ir palaiko savo būseną: kiekvienas veiksmas išsijungia, kai nėra atidarytas joks dokumentas, naršymo veiksmai seka puslapio padėtį, kopijavimas seka, ar yra pasirinkimas, o režimo ir pritaikymo veiksmai sinchronizuoja savo pažymėtą būseną su aktyviu įrankiu ir mastelio režimu. Projektavimo metu jie visi užregistruojami „losLab PDF“ kategorijoje „Action List“ redaktoriuje; vykdymo metu tos pačios klasės užima tik po tris eilutes
uses System.Actions, Vcl.ActnList, PDFlibViewer, PDFlibViewerActions;
procedure TMainForm.BuildToolbar;
var
NextPage: TPDFlibViewerNextPage;
FitWidth: TPDFlibViewerFitWidth;
HandMode: TPDFlibViewerHandMode;
begin
NextPage := TPDFlibViewerNextPage.Create(Self);
NextPage.ActionList := ActionList1;
NextPage.Viewer := FViewer;
btnNext.Action := NextPage;
FitWidth := TPDFlibViewerFitWidth.Create(Self);
FitWidth.ActionList := ActionList1;
FitWidth.Viewer := FViewer;
btnFitWidth.Action := FitWidth;
HandMode := TPDFlibViewerHandMode.Create(Self);
HandMode.ActionList := ActionList1;
HandMode.Viewer := FViewer;
btnHand.Action := HandMode;
end;
Kuriant savo veiksmų registraciją remiantis šiuo šablonu, verta žinoti apie vieną modulio apimties spąstus. „RegisterActions“ yra deklaruojamas „System.Actions“, o ne „Vcl.ActnList“, o kompiliatoriaus klaida, kurią gaunate „uses“ skiltyje turėdami tik „Vcl.ActnList“, yra klaidinanti: klasių masyvo literatui „[TAction1, TAction2, ...]“ pritaikoma aibės sintaksė, o tai sukelia klaidas „E2010 Incompatible types: 'Integer' and 'class of ...'“, kurios atrodo kaip tipų nesutapimas jūsų veiksmų klasėse. Sprendimas yra viena papildoma įtrauktis „uses“ skiltyje, tačiau diagnostika rodo bet kur, tik ne į ją. Mastelio keitimo įrankių juostose „TPDFlibViewer.EffectiveZoomPercent“ praneša apie realiai galiojantį mastelį — įskaitant apskaičiuotą koeficientą pločio ir puslapio pritaikymo režimuose — todėl mastelio didinimo žingsnis gali būti pagrįstas realiu dabartiniu padidinimu, o ne paskutine jūsų priskirta reikšme
Paieškos atitikmenų paryškinimas ir už jo slypintis koordinačių apvertimas
„HighlightSearchHits“ ieško užklausos visame dokumente ir virš kiekvieno atitikmens nupiešia permatomą juostą bei grąžina atitikmenų skaičių; „ClearSearchHighlights“, „SearchHighlightCount“ ir savybė „SearchHighlightColor“ užbaigia šią sąsają. Kadangi atitikmenų stačiakampiai saugomi puslapio erdvėje ir perskaičiuojami kiekvieno piešimo metu, juostos lieka priklijuotos prie teksto, kai naudotojas slenka, keičia mastelį ir suka vaizdą. Jei naudojate papildomą skydelį „TPDFlibSearchPanel“, jo „RunSearch“ automatiškai nupiešia tas pačias juostas prijungtoje peržiūros programoje per savybę „HighlightInView“, kuri yra įjungta pagal numatytuosius nustatymus
procedure TMainForm.btnSearchClick(Sender: TObject);
var
Hits: Integer;
begin
FViewer.SearchHighlightColor := clYellow;
Hits := FViewer.HighlightSearchHits(edtQuery.Text, False); // case-insensitive
StatusBar1.SimpleText := Format('%d matches', [Hits]);
end;
Svarbi ir subtili dalis, dėl kurios verta rinktis integruotą sprendimą, o ne kurti savo iš neapdorotų paieškos rezultatų, yra koordinačių sistemos konvertavimas. Teksto variklis apie atitikmenų stačiakampius praneša koordinatėmis, kurios priklauso nuo bibliotekos nustatymo „Origin“: naudojant „Origin“ 0 arba 3, Y ašis eina iš apačios į viršų, kaip ir natūralioje PDF naudotojo erdvėje, o su „Origin“ 2 arba 3, X ašis matuojama nuo dešiniojo krašto. Peržiūros programos perdanga veikia iš viršaus į apačią nukreiptoje, kairėje pusėje pagrįstoje puslapio erdvėje, zodžiu, kiekvienas atitikmens stačiakampis prieš išsaugant turi būti apverstas („Y = PageHeight - Bottom“ ir, kai taikoma, „X = PageWidth - Right“) — priimant nurodytą viršutinį kraštą tiesiogiai pagal numatytąją koordinačių pradžią, paryškinimas bus nupieštas vertikaliai veidrodinėje padėtyje puslapyje. „TPDFlibViewer“ atlieka šį konvertavimą pagal aktyviąją koordinačių pradžią, kai įrašo kiekvieną atitikmenį, o po to piešimo metu pritaiko tiesioginį pasukimo susiejimą, todėl juostos teisingai atsiduria bet kuriuo kampu. Pats permatomumas naudoja „Windows“ „AlphaBlend“ API su pastovia pradine alfa reikšme, nes VCL drobė neturi natūralios alfa kompozicijos. Pagrindinės teksto paieškos ir geometrijos API išsamiai aprašytos straipsnyje apie teksto paiešką ir puslapio elementų išvardijimą
Anotacijų spustelėjimai, patarimai ir priedų eksportavimas
„TPDFlibViewer“ vertina anotacijas kaip lygiaverčius interaktyvius objektus. Spustelėjus ne nuorodos anotaciją, suaktyvinamas įvykis „OnAnnotClick“ su puslapio numeriu, anotacijos indeksu (pradedant nuo 1) ir potipio eilute; „AnnotAtPagePoint“ programiškai atskleidžia tą patį pataikymo tikrinimą ir praleidžia susijusias iškylančiąsias (Popup) anotacijas, kad visada gautumėte anotaciją, kurią naudotojas realiai mato. Užvedus pelės žymeklį ant anotacijos, jos turinio tekstas rodomas kaip valdiklio patarimas per jungiklį „AnnotHints“, kuris pagal numatytuosius nustatymus yra įjungtas ir bendradarbiauja su standartine VCL savybe „ShowHint“. Naudojant „FileAttachment“ anotacijas, „SaveAnnotAttachmentToFile“ įrašo įterptąjį failą į diską ir sėkmės atveju grąžina 1, o tai leidžia realizuoti priedo išsaugojimą spustelėjimu vos keliomis kodo eilutėmis
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 := 'Attachment saved';
end;
Spausdinimas ir ribos, kurias turėtumėte numatyti
Spausdinimui nereikia atskiros integracijos. „PrintDoc“ parodo standartinį „Windows“ spausdinimo dialogą — spausdintuvo pasirinkimą, puslapių diapazoną, kopijas, rūšiavimą — ir perduoda užduotį bibliotekos spausdinimo varikliui, o „PrintDoc(False)“ išsiunčia visą dokumentą į numatytąjį spausdintuvą be jokios naudotojo sąsajos. „PrintDocRange“ veikia visiškai programiškai su nurodytu spausdintuvo pavadinimu, pradžios ir pabaigos puslapiais, kopijų skaičiumi ir rūšiavimo vėliava, kas yra tinkamas pradžios taškas grupinėms arba serverio inicijuotoms spausdinimo užduotims, valdomoms iš to paties peržiūros programos egzemplioriaus
Verta nuoširdžiai paminėti dvi ribas. Pirma, „TPDFlibViewer“ yra „Windows“ VCL valdiklis; peržiūros programos sluoksnis neįtrauktas į NOVCL rinkinius, todėl konsolės paslaugos ir ne VCL taikiniai turėtų tiesiogiai naudoti pagrindinę „TPDFlib“ atvaizdavimo API. Antra, naudojimas be naudotojo sąsajos (headless) yra sąmoningai palaikomas, bet ribotas: „CaptureRegion“ atvaizdoja bet kurią kliento sritį — puslapius ir paryškinimus — į kviečiančiajam priklausantį bitų žemėlapį ir veikia su valdikliu be tėvinio elemento bei be lango rankenėlės (window handle), nes piešimo kelias vengia VCL iškvietimų, kurie verstų sukurti rankenėlę. Tai daro pikselių lygio patikrinimą automatizuotuose testuose praktišką, tačiau viskas, kam tikrai reikia pranešimų ciklo (message loop), pavyzdžiui, interaktyvūs pelės įrankiai, vis tiek priklauso realiai naudotojo sąsajos sesijai. Kalbant apie našumą, valdiklis palaiko puslapių atminties talpyklą su konfigūruojamu limitu per „SetCacheLimit“ ir gali išlaikyti atvaizduotus puslapius tarp sesijų; straipsnis apie peržiūros programos disko puslapių talpyklą išsamiai aprašo šį sluoksnį ir jo elgseną esant skirtingiems monitoriaus DPI rodikliams
Taigi, penki žingsniai: įkelkite valdiklį ir iškvieskite „LoadFromFile“, prijunkite paruoštus veiksmus iš „PDFlibViewerActions“, iškvieskite „HighlightSearchHits“ paieškai puslapyje, apdorokite „OnAnnotClick“ anotacijų srautams ir pateikite „PrintDoc“. Kiekviena čia parodyta dalis tiekiama standartinėje „losLab PDF Library“ Delphi aplinkai, kur peržiūros valdiklis, veiksmų klasės ir atvaizdavimo variklis įsidiegia kartu