Tehnični članek

Izgradnja interaktivnega pregledovalnika PDF v Delphiju s TPDFlibViewer

Vgradnja plošče za branje PDF v poslovno aplikacijo Delphi ne zahteva pisanja upodabljalne zanke, pogona za pomikanje ali modela povečave. Knjižnica losLab PDF Library prinaša TPDFlibViewer, kontrolnik VCL, ki z enim klicem odpre dokument ter prinaša izbiro besedila, pomikanje, povečavo z označevalnim pravokotnikom, posnetke zaslona, označevanje območij, označevanje zadetkov iskanja celotnega besedila, interakcijo z anotacijami in tiskanje kot privzeto obnašanje. Ta članek opisuje pet korakov, ki prazno formo spremenijo v delujoč pregledovalnik: postavitev kontrolnika, povezava pripravljenih dejanj, dodajanje označevanja iskanja, obravnava klikov na anotacije in tiskanje

Zakaj uporabiti kontrolnik pregledovalnika namesto pisanja upodabljalne zanke?

Argument slojenja je na prvem mestu, saj pojasnjuje, kaj dejansko dobite. Upodabljanje strani v bitno sliko predstavlja enostavnih 20 odstotkov; preostalih 80 odstotkov pa je vse, kar jo obdaja: predpomnilnik bitnih slik strani s proračunom za odstranjevanje, postavitev pomikanja po straneh različnih velikosti, načini povečave, ki se preračunajo ob spremembi velikosti, testiranje zadetkov, ki preslika piksel miške nazaj v koordinate strani pri kateri koli povečavi in rotaciji, ter razporejanje ponovnega risanja, ki ne utripa. Kontrolnik TPDFlibViewer upravlja vse to in prenaša delo rastriranja na isti pogon, opisan v članku o večpogonskem upodabljanju, tako da nadzorna in upodabljalna plast ostaneta ločeno zamenljivi. Koda vaše forme komunicira s pomičnim kontrolnikom Windows in ne z upodabljalnikom

Kako vgradite pregledovalnik PDF v aplikacijo Delphi?

Metoda TPDFlibViewer.LoadFromFile odpre dokument prek lastne interne instance TPDFlib kontrolnika in ob uspehu vrne 1, zato minimalni pregledovalnik zahteva le klic konstruktorja, dodelitev lastnosti Parent in en zagon. Če že imate instanco TPDFlib drugje v aplikaciji, metoda AttachLibrary namesto tega prikaže njen izbrani dokument, pri čemer lastništvo ostane na vaši strani

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;  // pomikanje z vlečenjem; navaden klik še vedno sledi povezavam
end;

Lastnost ViewerMode izbere delovanje miške med petimi vrednostmi: vmSelect je privzeto orodje za izbiro besedila, vmHand omogoča pomikanje z vlečenjem s kazalcem za prijemanje, medtem ko navaden klik še vedno sledi povezavam, vmZoom je povečava z označevalnim pravokotnikom, ki poveča vlečeno območje čez celoten pogled, vmSnapshot zajame vlečeno območje v odložišče (z dogodkom OnSnapshot in stikalom SnapshotToClipboard), vmHighlight pa izriše trajne prosojne poudarke območja. Pri orodju za poudarjanje velja omeniti koordinate: območja se shranjujejo v uporabniškem prostoru strani in ne v slikovnih pikah odjemalca, zato poudarki AddHighlightRegion preživijo povečavo, rotacijo pogleda in navigacijo po straneh brez kakršnega koli beleženja na vaši strani. V rotiranem pogledu lahko smer vlečenja poteka celo nasproti osem strani, kam po inverznem preslikovanju rotacije prinese negativno širino ali višino — kontrolnik pa pravokotnik normalizira namesto vas

Kako povezati orodno vrstico PDF v Delphiju brez povezovalne kode?

Enota PDFlibViewerActions popolnoma odstrani povezovalno kodo orodne vrstice. Ponuja dvajset pripravljenih potomcev razreda TAction, ki pokrivajo navigacijo po straneh (TPDFlibViewerFirstPage, TPDFlibViewerPriorPage, TPDFlibViewerNextPage, TPDFlibViewerLastPage), povečavo (TPDFlibViewerZoomIn, TPDFlibViewerZoomOut, TPDFlibViewerFitWidth, TPDFlibViewerFitPage, TPDFlibViewerActualSize), rotacijo pogleda (TPDFlibViewerRotateClockwise, TPDFlibViewerRotateAntiClockwise), tiskanje, pogovorna okna za odpiranje in shranjevanje datotek, kopiranje izbranega besedila in eno stikalno dejanje na orodje miške (TPDFlibViewerSelectMode, TPDFlibViewerHandMode, TPDFlibViewerMarqueeZoomMode, TPDFlibViewerSnapshotMode, TPDFlibViewerHighlightMode). Vsako dejanje kaže na kontrolnik prek svoje objavljene (published) lastnosti Viewer in vzdržuje lastno stanje: vsako dejanje se onemogoči, ko ni odprtega dokumenta, navigacijska dejanja sledijo položaju strani, kopiranje spremlja obstoj izbire, dejanja načina in prilagajanja pa ohranjajo svoje označeno stanje usklajeno z aktivnim orodjem in načinom povečave. V času načrtovanja se vsa registrirajo v kategoriji losLab PDF urejevalnika Action List; med izvajanjem pa so isti razredi dolgi le tri vrstice vsak

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;

Eno past na ravni enote je vredno poznati, če gradite lastno registracijo dejanj na vrhu tega vzorca. Funkcija RegisterActions je deklarirana v enoti System.Actions in ne v Vcl.ActnList, prevajalnikovo opozorilo o napaki, ki ga prejmete, če imate v klavzuli uses le Vcl.ActnList, pa je aktivno zavajajoče: dobesedni zapis razrednega polja [TAction1, TAction2, ...] se obravnava kot množica, kar povzroči verigo napak E2010 Incompatible types: 'Integer' and 'class of ...', ki izgledajo kot neujemanje tipov v vaših razredih dejanj. Rešitev je en sam vnos v uses, vendar diagnostika kaže povsod drugam kot nanj. Za orodne vrstice povečave lastnost TPDFlibViewer.EffectiveZoomPercent poroča o dejanski povečavi, ki je v veljavi — vključno z izračunanim faktorjem v načinih prilagajanja širini ali strani —, tako da lahko korak povečave gradi na resnični trenutni povečavi in ne na zadnji vrednosti, ki ste jo dodelili

Označevanje zadetkov iskanja in zrcaljenje koordinat v ozadju

Metoda HighlightSearchHits preišče celoten dokument za iskalno poizvedbo in izriše prosojno vrstico čez vsak zadetek ter vrne število zadetkov; ClearSearchHighlights, SearchHighlightCount in lastnost SearchHighlightColor dopolnjujejo to področje. Ker se pravokotniki zadetkov shranjujejo v prostoru strani in se ponovno projicirajo ob vsakem risanju, ostanejo vrstice prilepljene na besedilo, ko uporabnik drsi, povečuje in rotira pogled. Če uporabljate spremljevalno ploščo TPDFlibSearchPanel, njena metoda RunSearch samodejno izriše iste vrstice v priključenem pregledovalniku prek lastnosti HighlightInView, ki je privzeto vklopljena

procedure TMainForm.btnSearchClick(Sender: TObject);
var
  Hits: Integer;
begin
  FViewer.SearchHighlightColor := clYellow;
  Hits := FViewer.HighlightSearchHits(edtQuery.Text, False);  // razlikovanje velikih/malih črk je izklopljeno
  StatusBar1.SimpleText := Format('%d matches', [Hits]);
end;

Subtilen del in razlog, da raje izberete vgrajeno pot kot pa lastno sestavljanje iz surovih rezultatov iskanja, je pretvorba koordinatnega sistema. Pogon za besedilo poroča o pravokotnikih zadetkov v koordinatah, ki so odvisne od nastavitve Origin v knjižnici: pri Origin 0 ali 3 poteka os Y od spodaj navzgor, kot v izvornem uporabniškem prostoru PDF, pri Origin 2 ali 3 pa se os X meri od desnega roba. Prekrivna plast pregledovalnika deluje v prostoru strani od zgoraj navzdol in z leve strani, zato je treba vsak pravokotnik zadetka zrcaliti (Y = PageHeight - Bottom in kjer je primerno X = PageWidth - Right), preden se shrani — jemanje sporočenega zgornjega roba kot končnega pod privzeto izhodiščno točko izriše poudarek na navpično zrcaljenem položaju na strani. TPDFlibViewer izvede to pretvorbo glede na aktivno izhodišče (origin), ko zabeleži vsak zadetek, nato pa ob risanju uporabi rotacijsko preslikavo, zaradi česar se vrstice pravilno postavijo pod vsakim kotom. Sama prosojnost uporablja Windows API AlphaBlend s konstantno vrednostjo alfa vira, saj platno VCL nima izvornega sestavljanja alfa. Ozadje iskanja besedila in geometrijski API-ji so podrobno opisani v članku o iskanju besedila in naštevanju elementov strani

Kliki na anotacije, namigi in izvoz prilog

TPDFlibViewer obravnava anotacije kot prvoosebne interaktivne objekte. Klik na anotacijo, ki ni povezava, sproži dogodek OnAnnotClick s številko strani, 1-baziranim indeksom anotacije in nizom podtipa; metoda AnnotAtPagePoint programsko izpostavlja isti test zadetkov in preskoči spremljevalne anotacije Popup, tako da vedno dobite tisto anotacijo, ki jo uporabnik dejansko vidi. Lebdenje nad anotacijo prikaže njeno vsebino Contents kot namig kontrolnika prek stikala AnnotHints, ki je privzeto vklopljeno in sodeluje z običajno lastnostjo VCL ShowHint. Za anotacije FileAttachment metoda SaveAnnotAttachmentToFile zapiše vgrajeno datoteko na disk in ob uspehu vrne 1, kar zaključi potek dela "klikni-za-shranjevanje" v le nekaj vrsticah

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 := 'Priloga je shranjena';
end;

Tiskanje in omejitve, ki jih morate načrtovati

Tiskanje ne zahteva ločene integracije. Metoda PrintDoc prikaže standardno pogovorno okno Windows za tiskanje — izbira tiskalnika, obseg strani, kopije, zbiranje — in predala delo tiskalnemu pogonu knjižnice, medtem ko klic PrintDoc(False) pošlje celoten dokument na privzeti tiskalnik brez kakršnega koli uporabniškega vmesnika. Metoda PrintDocRange deluje popolnoma programsko z eksplicitnim imenom tiskalnika, začetno in končno stranjo, številom kopij in zastavico za zbiranje, wat je prava vstopna točka za serijska ali strežniško sprožena opravila tiskanja, ki jih vodi ista instanca pregledovalnika

Dve omejitvi si zaslužita odkrit naštevanje. Prvič, TPDFlibViewer je kontrolnik Windows VCL; plast pregledovalnika je izključena iz gradenj NOVCL, zato bi morale konzolne storitve in cilji, ki niso VCL, uporabljati neposredni upodabljalni API TPDFlib. Drugič, brezglava (headless) uporaba je namerno podprta, a omejena: metoda CaptureRegion upodobi katero koli regijo odjemalca — strani in poudarke — v bitno sliko v lasti klicatelja in deluje na kontrolniku brez starša ter brez ročaja okna (window handle), saj se risalna pot izogne klicem VCL, ki bi prisilili ustvarjanje ročaja. To omogoča praktično preverjanje na ravni slikovnih pik v avtomatiziranih testih, vendar vse, kar resnično potrebuje zanko sporočil, kot so interaktivna orodja za miško, še vedno sodi v pravo sejo uporabniškega vmesnika. Na strani zmogljivosti kontrolnik vzdržuje predpomnilnik strani v pomnilniku z nastavljivim proračunom prek SetCacheLimit in lahko ohrani upodobljene strani med sejami; članek o predpomnilniku strani pregledovalnika na disku podrobno opisuje to plast in njeno obnašanje pri DPI za posamezne monitorje

Pet korakov torej: postavite kontrolnik in pokličite LoadFromFile, pripnite pripravljena dejanja iz PDFlibViewerActions, pokličete HighlightSearchHits za iskanje znotraj strani, obravnavate OnAnnotClick za poteke dela z anotacijami in izpostavite PrintDoc. Vsak prikazani del se prinaša v standardni knjižnici losLab PDF Library za Delphi, kjer se kontrolnik pregledovalnika, razredi dejanj in upodabljalni pogon namestijo skupaj