Teknisk artikkel

Bygg en interaktiv PDF-viser i Delphi med TPDFlibViewer

Å bygge inn et panel for PDF-visning i en Delphi-forretningsapplikasjon krever ikke at du skriver en tegnesløyfe, en rulle-motor eller en zoom-modell. losLab PDF Library leveres med TPDFlibViewer, en VCL-kontroll som åpner et dokument med ett enkelt kall og tilbyr tekstmarkering, panorering, markeringszoom, skjermbilder, utheving av områder, fulltekstsøk-utheving, annotasjonsinteraksjon og utskrift som innebygd funksjonalitet. Denne artikkelen går gjennom de fem trinnene som gjør et tomt skjema om til en fungerende viser: plasser kontrollen, koble til de ferdige handlingene, legg til søkeutheving, håndter annotasjonsklikk og skriv ut

Hvorfor bruke en visningskontroll i stedet for å skrive en tegnesløyfe?

Lagdelingsargumentet kommer først, fordi det forklarer hva du faktisk kjøper. Å tegne en side til et bitmap er den enkle 20-prosenten; de andre 80 prosentene er alt som er pakket rundt det: en hurtigbuffer for side-bitmaps med et utkastelsesbudsjett, rulleoppsett på tvers av sider av forskjellige størrelser, zoom-moduser som beregnes på nytt ved størrelsesendring, trefftesting som kartlegger en musepiksel tilbake til sidekoordinater ved enhver zoom og rotasjon, og tidsplanlegging for tegning som ikke flimrer. TPDFlibViewer eier alt dette og delegerer rasterarbeidet til den samme motoren som er beskrevet i artikkelen om tegning med flere motorer, slik at kontrollaget og tegnelaget forblir separat utskiftbare. Skjemakoden din snakker med en rullbar Windows-kontroll, ikke med en tegner

Hvordan bygger du inn en PDF-viser i en Delphi-applikasjon?

TPDFlibViewer.LoadFromFile åpner et dokument gjennom kontrollens egen interne TPDFlib-instans og returnerer 1 ved suksess, så den minimale viseren er et konstruktørkall, en Parent-tilordning og én innlasting. Hvis du allerede har en TPDFlib-instans et annet sted i applikasjonen, viser AttachLibrary det valgte dokumentet i stedet, med eierskapet forblir på din side

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;

Egenskapen ViewerMode velger hva musen gjør, blant fem verdier: vmSelect er standardverktøyet for tekstmarkering, vmHand panorerer ved å dra med en gripe-markør mens et vanlig klikk fremdeles følger lenker, vmZoom er en markeringszoom som forstørrer det dratte området for å fylle visningsområdet, vmSnapshot fanger et dratt område til utklippstavlen (med en OnSnapshot-hendelse og en SnapshotToClipboard-bryter), og vmHighlight tegner vedvarende halvtransparente områdeuthevinger. Uthevingsverktøyet er verdt en merknad om koordinater: områder lagres i sidens brukerrom, ikke i klientpiksler, så markeringer lagt til med AddHighlightRegion overlever zooming, visningsrotasjon og sidenavigasjon uten noen bokføring på din side. I en rotert visning kan dratilretningen til og med gå mot sideaksene, noe som gir en negativ bredde eller høyde etter den omvendte rotasjonskartleggingen — kontrollen normaliserer rektangelet for deg

Hvordan kobler du opp en PDF-verktøylinje i Delphi uten limkode?

Enheten PDFlibViewerActions fjerner verktøylinjens limkode fullstendig. Den tilbyr tjue ferdige TAction-etterkommere som dekker sidenavigasjon (TPDFlibViewerFirstPage, TPDFlibViewerPriorPage, TPDFlibViewerNextPage, TPDFlibViewerLastPage), zoom (TPDFlibViewerZoomIn, TPDFlibViewerZoomOut, TPDFlibViewerFitWidth, TPDFlibViewerFitPage, TPDFlibViewerActualSize), visningsrotasjon (TPDFlibViewerRotateClockwise, TPDFlibViewerRotateAntiClockwise), utskrift, dialogbokser for åpning og lagring av filer, kopiering av valgt tekst, og én bryterhandling per museverktøy (TPDFlibViewerSelectMode, TPDFlibViewerHandMode, TPDFlibViewerMarqueeZoomMode, TPDFlibViewerSnapshotMode, TPDFlibViewerHighlightMode). Hver handling peker på en kontroll via den publiserte egenskapen Viewer og opprettholder sin egen tilstand: hver handling deaktiveres når intet dokument er åpent, navigasjonshandlingene sporer sideposisjonen, kopiering sporer om en markering eksisterer, og modus- og tilpasningshandlingene holder sin markerte tilstand synkronisert med det aktive verktøyet og zoom-modusen. Under design registreres de alle i losLab PDF-kategorien i Action List-editoren; under kjøring er de samme klassene på bare tre linjer hver

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;

En felle på enhetsnivå er verdt å kjenne til hvis du bygger din egen handlingsregistrering på toppen av dette mønsteret. RegisterActions er deklarert i System.Actions, ikke i Vcl.ActnList, og kompilatorfeilen du får med bare Vcl.ActnList i uses-klausulen er aktivt villedende: klassetabellen [TAction1, TAction2, ...] faller tilbake til å bli tolket som et sett, noe som produserer en rekke med E2010 Incompatible types: 'Integer' and 'class of ...'-feil som ser ut som en typeuoverensstemmelse i handlingsklassene dine. Løsningen er en enkelt oppføring i uses, men feilsøkingen peker overalt unntatt på den. For verktøylinjer for zoom rapporterer TPDFlibViewer.EffectiveZoomPercent den faktiske zoomen som er i bruk — inkludert den beregnede faktoren i moduser for tilpasning til bredde og tilpasning til side — slik at et zoom-inn-trinn kan bygge på den reelle gjeldende forstørrelsen i stedet for den siste verdien du tildelte

Utheving av søketreff og koordinatsnuingen bak

HighlightSearchHits søker gjennom hele dokumentet etter en spørring og tegner en halvtransparent stolpe over hvert treff, og returnerer antall treff; ClearSearchHighlights, SearchHighlightCount og egenskapen SearchHighlightColor fullfører grensesnittet. Fordi treffrektanglene lagres i siderommet og projiseres på nytt ved hver tegning, forblir stolpene limt til teksten sin mens brukeren ruller, zoomer og roterer visningen. Hvis du bruker den tilhørende TPDFlibSearchPanel, vil dens RunSearch automatisk tegne de samme stolpene i den tilkoblede viseren via egenskapen HighlightInView, som er aktivert som standard

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;

Den subtile delen, og grunnen til å foretrekke den innebygde banen fremfor å lage din egen fra rå søkeresultater, er konverteringen av koordinatsystemet. Tekstmotoren rapporterer treffrektangler i koordinater som avhenger av bibliotekets Origin-innstilling: med Origin 0 eller 3 går Y-aksen nedenfra og opp som i det opprinnelige PDF-brukerrommet, og med Origin 2 or 3 måles X-aksen fra høyre kant. Viserens overlegg fungerer i et siderom som går ovenfra og ned og fra venstre, så hvert treffrektangel må snus (Y = PageHeight - Bottom, og der det er aktuelt X = PageWidth - Right) før det lagres — å ta den rapporterte toppkanten for god fisk under standard opprinnelse tegner uthevingen på den vertikalt speilede posisjonen på siden. TPDFlibViewer utfører denne konverteringen i henhold til den aktive opprinnelsen når den registrerer hvert treff, og bruker deretter den fremadrettede rotasjonskartleggingen ved tegning, og det er derfor stolpene lander riktig i alle vinkler. Selve gjennomsiktigheten bruker Windows sitt AlphaBlend-API med en konstant kilde-alpha, siden VCL-canvaset ikke har innebygd alpha-sammensetning. De underliggende API-ene for tekstsøk og geometri er dekket i dybden i artikkelen om tekstsøk og oppregning av sideelementer

Annotasjonsklikk, hint og eksport av vedlegg

TPDFlibViewer behandler annotasjoner som førsteklasses interaktive objekter. Ved å klikke på en annotasjon som ikke er en lenke, utløses OnAnnotClick med sidenummeret, den 1-baserte annotasjonsindeksen og undertypestrengen; AnnotAtPagePoint eksponerer den samme trefftesten programmatisk og hopper over tilhørende Popup-annotasjoner, slik at du alltid får annotasjonen brukeren faktisk ser. Å hovre over en annotasjon viser dens innholdstekst som kontrollens hint via bryteren AnnotHints, som er aktivert som standard og samarbeider med den standard VCL-egenskapen ShowHint. For FileAttachment-annotasjoner skriver SaveAnnotAttachmentToFile den innebygde filen til disk og returnerer 1 ved suksess, noe som fullfører en klikk-for-å-lagre-arbeidsflyt på noen få linjer

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;

Utskrift og grensene du bør planlegge for

Utskrift krever ingen egen integrasjon. PrintDoc viser den standard Windows-utskriftsdialogen — skrivervalg, sideområde, kopier, sortering — og overleverer jobben til bibliotekets utskriftsmotor, mens PrintDoc(False) sender hele dokumentet til standardskriveren uten noe brukergrensesnitt i det hele tatt. PrintDocRange fungerer fullt programmatisk med et eksplisitt skrivernavn, start- og sluttside, antall kopier og sorteringsflagg, noe som er det rette inngangspunktet for batch- eller serverutløste utskriftsjobber som kjøres fra den samme viserinstansen

To grenser fortjener en ærlig omtale. For det første er TPDFlibViewer en Windows VCL-kontroll; visningslaget er ekskludert fra NOVCL-bygg, så konsolltjenester og ikke-VCL-mål bør bruke det underliggende TPDFlib-tegnings-API-et direkte. For det andre er hodeløs bruk bevisst støttet, men begrenset: CaptureRegion tegner enhver klientregion — sider pluss uthevinger — til et bitmap eid av kaller og fungerer på en foreldreløs kontroll uten et vindushåndtak, fordi tegningsbanen unngår VCL-kall som ville tvunget frem opprettelse av håndtak. Det gjør verifisering på pikselnivå i automatiserte tester praktisk, men alt som virkelig trenger en meldingssløyfe, som de interaktive museverktøyene, hører fremdeles hjemme i en ekte UI-sesjon. På ytelsessiden beholder kontrollen en hurtigbuffer for sider i minnet med et konfigurerbart budsjett via SetCacheLimit, og kan bevare tegnede sider på tvers av sesjoner; artikkelen om side-hurtigbuffer på disk for viseren dekker det laget og dets oppførsel for DPI per skjerm i detalj

Fem trinn altså: plasser kontrollen og kall LoadFromFile, koble til de ferdige handlingene fra PDFlibViewerActions, kall HighlightSearchHits for søk på siden, håndter OnAnnotClick for annotasjonsarbeidsflyter, og eksponer PrintDoc. Hver del som vises her leveres i standard losLab PDF Library for Delphi, der visningskontrollen, handlingsklassene og tegningsmotoren installeres sammen