Technical Article

Izgradite interaktivni preglednik PDF-a u Delphiju pomoću TPDFlibViewer-a

Ugradnja ploče za čitanje PDF-a u Delphi poslovnu aplikaciju ne zahtijeva pisanje petlje za iscrtavanje, mehanizma za pomicanje (scroll engine) ili modela zumiranja. losLab PDF Library isporučuje TPDFlibViewer, VCL kontrolu koja otvara dokument jednim pozivom i donosi odabir teksta, pomicanje (panning), zumiranje okvirom (marquee zoom), snimke (snapshots), isticanje regija, isticanje rezultata pretraživanja cijelog teksta, interakciju s anotacijama i ispis kao ugrađeno ponašanje. Ovaj članak prolazi kroz pet koraka koji praznu formu pretvaraju u funkcionalni preglednik: postavljanje kontrole, povezivanje gotovih akcija, dodavanje isticanja pretraživanja, rukovanje klikovima na anotacije i ispis

Zašto koristiti kontrolu preglednika umjesto pisanja petlje za iscrtavanje?

Argument slojevitosti dolazi na prvo mjesto jer objašnjava što zapravo kupujete. Iscrtavanje stranice u bitmapu je lakših 20 posto; ostalih 80 posto je sve što je omotano oko toga: predmemorija bitmape stranice s proračunom za izbacivanje, izgled pomicanja kroz stranice različitih veličina, načini zumiranja koji se ponovno izračunavaju pri promjeni veličine, testiranje pogotka (hit-testing) koje mapira piksel miša natrag u koordinate stranice pri bilo kojem zumiranju i rotaciji, te raspoređivanje ponovnog iscrtavanja koje ne treperi. TPDFlibViewer posjeduje sve to i delegira rasterski rad istom mehanizmu opisanom u članku o višestrukim mehanizmima iscrtavanja, tako da sloj kontrole i sloj iscrtavanja ostaju zasebno zamjenjivi. Vaš kôd forme komunicira s Windows kontrolom koja se može pomicati, a ne s iscrtavačem

Kako ugraditi PDF preglednik u Delphi aplikaciju?

TPDFlibViewer.LoadFromFile otvara dokument kroz vlastitu internu instancu TPDFlib kontrole i vraća 1 u slučaju uspjeha, tako da je minimalni preglednik poziv konstruktora, dodjela Parent-a i jedno učitavanje. Ako već držite instancu TPDFlib negdje drugdje u aplikaciji, AttachLibrary umjesto toga prikazuje njezin odabrani dokument, pri čemu vlasništvo ostaje na vašoj 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;  // pan by dragging; a plain click still follows links
end;

Svojstvo ViewerMode odabire što miš radi, između pet vrijednosti: vmSelect je zadani alat za odabir teksta, vmHand pomiče drag-and-drop metodom s kursorom za hvatanje dok običan klik i dalje prati poveznice, vmZoom je zumiranje okvirom koje povećava povučeni pojas kako bi ispunio prikaz, vmSnapshot snima povučenu regiju u međuspremnik (s događajem OnSnapshot i prekidačem SnapshotToClipboard), a vmHighlight iscrtava trajna prozirna isticanja regija. Alat za isticanje vrijedi spomenuti zbog koordinata: regije se pohranjuju u korisničkom prostoru stranice, a ne u pikselima klijenta, tako da oznake AddHighlightRegion preživljavaju zumiranje, rotaciju prikaza i navigaciju stranicom bez ikakvog vođenja evidencije s vaše strane. U rotiranom prikazu smjer povlačenja može čak ići nasuprot osi stranice, što rezultira negativnom širinom ili visinom nakon inverznog mapiranja rotacije — kontrola normalizira pravokutnik umjesto vas

Kako povezati PDF alatnu traku u Delphiju bez veznog kôda?

Jedinica PDFlibViewerActions u potpunosti uklanja vezni kôd alatne trake. Pruža dvadeset gotovih potomaka klase TAction koji pokrivaju navigaciju stranicama (TPDFlibViewerFirstPage, TPDFlibViewerPriorPage, TPDFlibViewerNextPage, TPDFlibViewerLastPage), zumiranje (TPDFlibViewerZoomIn, TPDFlibViewerZoomOut, TPDFlibViewerFitWidth, TPDFlibViewerFitPage, TPDFlibViewerActualSize), rotaciju prikaza (TPDFlibViewerRotateClockwise, TPDFlibViewerRotateAntiClockwise), ispis, dijaloge za otvaranje i spremanje datoteka, kopiranje označenog teksta i jednu radnju prebacivanja po alatu miša (TPDFlibViewerSelectMode, TPDFlibViewerHandMode, TPDFlibViewerMarqueeZoomMode, TPDFlibViewerSnapshotMode, TPDFlibViewerHighlightMode). Svaka akcija pokazuje na kontrolu putem svog objavljenog svojstva Viewer i održava vlastito stanje: svaka se akcija onemogućuje kada nema otvorenog dokumenta, navigacijske akcije prate poziciju stranice, kopiranje prati postoji li odabir, a akcije načina i prilagođavanja sinkroniziraju svoje označeno stanje s aktivnim alatom i načinom zumiranja. U vrijeme dizajna, sve se registriraju u kategoriji losLab PDF uređivača Action List; u vremenu izvršavanja, iste klase zauzimaju po tri retka

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;

Jednu zamku s opsegom jedinice vrijedi znati ako gradite vlastitu registraciju akcija na temelju ovog obrasca. RegisterActions je deklariran u System.Actions, a ne u Vcl.ActnList, a pogreška prevoditelja koju dobijete sa samo Vcl.ActnList u klauzuli uses aktivno dovodi u zabludu: klasa-niz literal [TAction1, TAction2, ...] se interpretira kao skup, proizvodeći lanac pogrešaka E2010 Incompatible types: 'Integer' and 'class of ...' koje izgledaju kao nepodudaranje tipova u vašim klasama akcija. Rješenje je jedan unos u klauzuli uses, ali dijagnostika pokazuje svugdje osim na njega. Za alatne trake za zumiranje, TPDFlibViewer.EffectiveZoomPercent izvještava o zumiranju koje je stvarno na snazi — uključujući izračunati faktor u načinima prilagodbe širini i prilagodbe stranici — tako da se korak zumiranja može graditi na stvarnom trenutnom povećanju, a ne na posljednjoj vrijednosti koju ste dodijelili

Isticanje rezultata pretraživanja i okretanje koordinata iza toga

HighlightSearchHits pretražuje cijeli dokument za upit i iscrtava prozirnu traku preko svakog podudaranja, vraćajući broj pogodaka; ClearSearchHighlights, SearchHighlightCount i svojstvo SearchHighlightColor nadopunjuju sučelje. Budući da se pravokutnici pogodaka pohranjuju u prostoru stranice i ponovno projiciraju pri svakom iscrtavanju, trake ostaju zalijepljene za svoj tekst dok korisnik pomiče, zumira i rotira prikaz. Ako koristite prateći TPDFlibSearchPanel, njegov RunSearch automatski iscrtava iste trake u priloženom pregledniku putem svojstva HighlightInView, koje je prema zadanim postavkama uključeno

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;

Suptilan dio, i razlog zašto preferirati ugrađeni put umjesto izrade vlastitog rješenja iz sirovih rezultata pretraživanja, jest pretvorba koordinatnog sustava. Tekstualni mehanizam javlja pravokutnike podudaranja u koordinatama koje ovise o postavci knjižnice Origin: s Origin 0 ili 3 Y os ide odozdo prema gore kao u nativnom korisničkom prostoru PDF-a, a s Origin 2 ili 3 X os se mjeri od desnog ruba. Preklapanje preglednika radi u prostoru stranice odozgo prema dolje, s lijeve strane, tako da se svaki pravokutnik pogotka mora okrenuti (Y = PageHeight - Bottom, i gdje je primjenjivo X = PageWidth - Right) prije nego što se pohrani — uzimanje prijavljenog gornjeg ruba zdravo za gotovo pod zadanim ishodištem iscrtava isticanje na vertikalno zrcaljenoj poziciji na stranici. TPDFlibViewer izvodi ovu pretvorbu prema aktivnom ishodištu kada bilježi svaki pogodak, a zatim primjenjuje mapiranje rotacije unaprijed u vrijeme iscrtavanja, zbog čega trake točno slijeću pod svakim kutom. Sama prozirnost koristi Windows AlphaBlend API s konstantnim izvornim alfa kanalom, budući da VCL platno nema nativno alfa komponiranje. Temeljno pretraživanje teksta i geometrijski API-ji detaljno su pokriveni u članku o pretraživanju teksta i nabrajanju elemenata stranice

Klikovi na anotacije, savjeti i izvoz privitaka

TPDFlibViewer tretira anotacije kao interaktivne objekte prve klase. Klik na anotaciju koja nije poveznica pokreće OnAnnotClick s brojem stranice, 1-baziranim indeksom anotacije i nizom podtipa; AnnotAtPagePoint programski izlaže isti hit-test i preskače prateće Popup anotacije tako da uvijek dobijete anotaciju koju korisnik stvarno vidi. Prelazak mišem preko anotacije prikazuje njezin tekst Contents kao savjet (hint) kontrole putem prekidača AnnotHints, koji je prema zadanim postavkama uključen i surađuje sa standardnim VCL svojstvom ShowHint. Za FileAttachment anotacije, SaveAnnotAttachmentToFile zapisuje ugrađenu datoteku na disk i vraća 1 u slučaju uspjeha, što dovršava tijek rada klikni-za-spremiti u samo nekoliko redaka

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;

Ispis i granice koje biste trebali planirati

Ispis ne zahtijeva zasebnu integraciju. PrintDoc prikazuje standardni Windows dijalog za ispis — izbor pisača, raspon stranica, kopije, slaganje (collation) — i predaje posao mehanizmu za ispis knjižnice, dok PrintDoc(False) šalje cijeli dokument zadanom pisaču bez ikakvog korisničkog sučelja. PrintDocRange radi potpuno programski s eksplicitnim imenom pisača, početnom i završnom stranicom, brojem kopija i zastavicom slaganja, što je prava ulazna točka za skupne poslove ispisa ili poslove pokrenute s poslužitelja koje pokreće ista instanca preglednika

Dvije granice zaslužuju iskreno spominjanje. Prvo, TPDFlibViewer je Windows VCL kontrola; sloj preglednika isključen je iz NOVCL međuina, tako da bi konzolne usluge i ne-VCL ciljevi trebali izravno koristiti temeljni TPDFlib API za iscrtavanje. Drugo, rad bez sučelja (headless) namjerno je podržan, ali je ograničen: CaptureRegion iscrtava bilo koju regiju klijenta — stranice plus istaknute dijelove — u bitmapu u vlasništvu pozivatelja i radi na kontroli bez roditelja (unparented control) bez ručke prozora (window handle), jer put iscrtavanja izbjegava VCL pozive koji bi prisilili stvaranje ručke. To čini provjeru na razini piksela u automatiziranim testovima praktičnom, ali sve što doista treba petlju poruka (message loop), kao što su interaktivni alati za miš, i dalje pripada stvarnoj sesiji korisničkog sučelja. Što se tiče performansi, kontrola održava memorijsku predmemoriju stranica s konfigurabilnim proračunom putem SetCacheLimit i može trajno pohraniti iscrtane stranice kroz sesije; članak o predmemoriji stranica preglednika na disku detaljno pokriva taj sloj i njegovo ponašanje pri DPI skaliranju po monitoru

Pet koraka, dakle: postavite kontrolu i pozovite LoadFromFile, priložite gotove akcije iz PDFlibViewerActions, pozovite HighlightSearchHits za pretraživanje unutar stranice, rukujte s OnAnnotClick za tijekove rada s anotacijama i izložite PrintDoc. Svaki ovdje prikazani dio isporučuje se u standardnoj losLab PDF knjižnici za Delphi, gdje se kontrola preglednika, klase akcija i mehanizam za iscrtavanje instaliraju zajedno