Integrarea unui panou de citire PDF într-o aplicație de business Delphi nu necesită scrierea unei bucle de randare, a unui motor de derulare sau a unui model de zoom. losLab PDF Library include TPDFlibViewer, un control VCL care deschide un document cu un singur apel și oferă selectare de text, panoramare, zoom de tip marquee, instantanee, evidențierea regiunilor, evidențierea rezultatelor căutării textuale, interacțiunea cu adnotările și tipărirea ca funcționalități integrate. Acest articol parcurge cei cinci pași care transformă un formular gol într-un vizualizator funcțional: adăugarea controlului, conectarea acțiunilor predefinite, adăugarea evidențierii căutării, gestionarea clicurilor pe adnotări și tipărirea
De ce să folosiți un control de vizualizare în loc să scrieți o buclă de randare?
Argumentul stratificării este pe primul loc, deoarece explică ce achiziționați de fapt. Randarea unei pagini într-o imagine bitmap reprezintă partea ușoară de 20 la sută; celelalte 80 de procente reprezintă tot ceea ce o înconjoară: un cache de pagini bitmap cu un buget de evacuare, aspectul de derulare pe pagini de diferite dimensiuni, moduri de zoom care se recalculează la redimensionare, testarea coliziunilor (hit-testing) care asociază un pixel de mouse înapoi la coordonatele paginii la orice zoom și rotație, și programarea de reîmprospătare (repaint) fără pâlpâire. TPDFlibViewer deține toate acestea și deleagă activitatea de rasterizare aceluiași motor descris în articolul despre randarea multi-motor, astfel încât stratul de control și stratul de randare să poată fi înlocuite separat. Codul formularului dvs. comunică cu un control Windows derulabil, nu cu un motor de randare
Cum se integrează un vizualizator PDF într-o aplicație Delphi?
TPDFlibViewer.LoadFromFile deschide un document prin intermediul propriei instanțe interne TPDFlib a controlului și returnează 1 în caz de succes, astfel încât vizualizatorul minim este un apel de constructor, o atribuire Parent și o încărcare. Dacă dețineți deja o instanță TPDFlib în altă parte a aplicației, AttachLibrary afișează în schimb documentul selectat al acesteia, dreptul de proprietate rămânând de partea dvs.
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;
Proprietatea ViewerMode selectează ce face mouse-ul, având cinci valori posibile: vmSelect este instrumentul implicit de selectare a textului, vmHand derulează prin glisare cu un cursor de prindere (grab cursor) în timp ce un simplu clic continuă să urmeze linkurile, vmZoom este un zoom de tip marquee care mărește zona glisată pentru a umple spațiul de vizualizare, vmSnapshot capturează o regiune glisată în clipboard (cu un eveniment OnSnapshot și un comutator SnapshotToClipboard), iar vmHighlight desenează marcaje translucide persistente pentru regiuni. Instrumentul de evidențiere merită o mențiune legată de coordonate: regiunile sunt stocate în spațiul utilizatorului al paginii (page user space), nu în pixeli client, astfel încât marcajele AddHighlightRegion supraviețuiesc zoom-ului, rotației vizualizării și navigării între pagini fără a necesita evidență suplimentară din partea dvs. Într-o vizualizare rotită, direcția de glisare poate fi chiar opusă axelor paginii, ceea ce generează o lățime sau o înălțime negativă după maparea inversă a rotației — controlul normalizează dreptunghiul pentru dvs.
Cum se conectează o bară de instrumente PDF în Delphi fără cod de legătură?
Unitatea PDFlibViewerActions elimină complet codul de legătură pentru bara de instrumente. Aceasta oferă douăzeci de descendenți TAction gata pregătiți care acoperă navigarea în pagină (TPDFlibViewerFirstPage, TPDFlibViewerPriorPage, TPDFlibViewerNextPage, TPDFlibViewerLastPage), zoom-ul (TPDFlibViewerZoomIn, TPDFlibViewerZoomOut, TPDFlibViewerFitWidth, TPDFlibViewerFitPage, TPDFlibViewerActualSize), rotația vizualizării (TPDFlibViewerRotateClockwise, TPDFlibViewerRotateAntiClockwise), imprimarea, dialogurile de deschidere și salvare fișier, copierea textului selectat și o acțiune de comutare pentru fiecare instrument de mouse (TPDFlibViewerSelectMode, TPDFlibViewerHandMode, TPDFlibViewerMarqueeZoomMode, TPDFlibViewerSnapshotMode, TPDFlibViewerHighlightMode). Fiecare acțiune indică un control prin proprietatea sa publicată Viewer și își menține propria stare: fiecare acțiune se dezactivează singură atunci când nu este deschis niciun document, acțiunile de navigare urmăresc poziția paginii, copierea urmărește dacă există o selecție, iar acțiunile de mod și potrivire își mențin starea bifată în sincronizare cu instrumentul activ și modul de zoom. La momentul proiectării (design time), toate se înregistrează în categoria losLab PDF a editorului Action List; la execuție (runtime), aceleași clase necesită doar trei linii de cod fiecare
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;
O capcană legată de domeniul unității merită cunoscută dacă vă construiți propria înregistrare a acțiunilor peste acest model. RegisterActions este declarat în System.Actions, nu în Vcl.ActnList, iar eroarea de compilare pe care o primiți având doar Vcl.ActnList în clauza uses este extrem de înșelătoare: literalul de tip tablou de clase [TAction1, TAction2, ...] revine la a fi analizat ca un set, producând un lanț de erori E2010 Incompatible types: 'Integer' and 'class of ...' care arată ca o nepotrivire de tip în clasele dvs. de acțiuni. Soluția este o singură adăugare în clauza uses, dar diagnosticul indică oriunde în altă parte. Pentru barele de instrumente de zoom, TPDFlibViewer.EffectiveZoomPercent raportează zoom-ul efectiv aplicat — inclusiv factorul calculat în modurile de potrivire pe lățime (fit-width) și potrivire pe pagină (fit-page) — astfel încât un pas de zoom înainte să se bazeze pe mărirea reală curentă, mai degrabă decât pe ultima valoare atribuită
Evidențierea rezultatelor căutării și inversarea coordonatelor din spatele acesteia
HighlightSearchHits caută o interogare în întregul document și desenează o bară translucidă peste fiecare potrivire, returnând numărul de rezultate; ClearSearchHighlights, SearchHighlightCount și proprietatea SearchHighlightColor completează această funcționalitate. Deoarece dreptunghiurile rezultatelor sunt stocate în spațiul paginii și sunt reproiectate la fiecare desenare (paint), barele rămân lipite de text în timp ce utilizatorul derulează, face zoom și rotește vizualizarea. Dacă utilizați componenta parteneră TPDFlibSearchPanel, metoda sa RunSearch desenează automat aceleași bare în vizualizatorul atașat prin intermediul proprietății HighlightInView, care este activată implicit
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;
Partea subtilă, și motivul pentru care este preferabilă calea integrată în locul implementării proprii pe baza rezultatelor brute ale căutării, este conversia sistemului de coordonate. Motorul de text raportează dreptunghiurile potrivirilor în coordonate care depind de setarea Origin a bibliotecii: cu Origin 0 sau 3 axa Y rulează de jos în sus ca în spațiul nativ al utilizatorului PDF, iar cu Origin 2 sau 3 axa X este măsurată de la marginea dreaptă. Suprapunerea vizualizatorului funcționează în spațiul paginii de sus în jos și aliniat la stânga, astfel încât fiecare dreptunghi al rezultatului trebuie inversat (Y = PageHeight - Bottom și, unde este cazul, X = PageWidth - Right) înainte de a fi stocat — considerarea marginii superioare raportate ca atare sub originea implicită desenează evidențierea în poziția oglindită vertical pe pagină. TPDFlibViewer efectuează această conversie conform originii active atunci când înregistrează fiecare rezultat, apoi aplică maparea rotației directe în momentul desenării, motiv pentru care barele se așază corect la orice unghi. Translucența în sine utilizează API-ul Windows AlphaBlend cu o valoare alfa sursă constantă, deoarece canvas-ul VCL nu are compoziție alfa nativă. Căutarea de text subiacentă și API-urile de geometrie sunt acoperite în detaliu în articolul despre căutarea de text și enumerarea elementelor de pagină
Clicuri pe adnotări, indicii și exportul fișierelor atașate
TPDFlibViewer tratează adnotările ca obiecte interactive de prim rang. Clicul pe o adnotare care nu este de tip link declanșează OnAnnotClick cu numărul paginii, indexul adnotării (baza 1) și șirul de caractere al subtipului; AnnotAtPagePoint expune programatic același test de coliziune și omite adnotările însoțitoare de tip Popup, astfel încât să obțineți întotdeauna adnotarea pe care o vede efectiv utilizatorul. Trecerea mouse-ului peste o adnotare afișează textul Contents al acesteia ca indiciu (hint) al controlului prin comutatorul AnnotHints, care este activat implicit și conlucrează cu proprietatea standard VCL ShowHint. Pentru adnotările de tip FileAttachment, SaveAnnotAttachmentToFile scrie fișierul încorporat pe disc și returnează 1 în caz de succes, ceea ce finalizează un flux de lucru clic-pentru-salvare în doar câteva linii de cod
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;
Imprimarea și limitele pentru care ar trebui să vă planificați
Tipărirea nu necesită o integrare separată. PrintDoc afișează dialogul standard de tipărire din Windows — selecția imprimantei, intervalul de pagini, numărul de copii, colaționarea — și transmite sarcina motorului de tipărire al bibliotecii, în timp ce PrintDoc(False) trimite întregul document la imprimanta implicită fără nicio interfață grafică. PrintDocRange funcționează complet programatic cu un nume explicit de imprimantă, pagina de început și de sfârșit, numărul de copii și indicatorul de colaționare, ceea ce reprezintă punctul corect de intrare pentru lucrările de tipărire în loturi sau declanșate de server, rulate de pe aceeași instanță de vizualizator
Două limitări merită o mențiune onestă. În primul rând, TPDFlibViewer este un control Windows VCL; stratul de vizualizare este exclus din build-urile NOVCL, astfel încât serviciile de consolă și țintele non-VCL ar trebui să utilizeze direct API-ul de randare subiacent TPDFlib. În al doilea rând, utilizarea în mod headless este acceptată în mod intenționat, dar limitată: CaptureRegion randează orice regiune client — pagini plus evidențieri — într-o imagine bitmap deținută de apelant și funcționează pe un control fără părinte, fără un handle de fereastră, deoarece calea de desenare evită apelurile VCL care ar forța crearea unui handle. Acest lucru face practică verificarea la nivel de pixel în testele automate, dar orice lucru care are cu adevărat nevoie de o buclă de mesaje (message loop), cum ar fi instrumentele interactive de mouse, aparține în continuare unei sesiuni UI reale. Pe partea de performanță, controlul menține un cache de pagini în memorie cu un buget configurabil prin SetCacheLimit și poate persista paginile randate între sesiuni; articolul despre cache-ul de pagini pe disc al vizualizatorului acoperă acel strat și comportamentul său DPI per-monitor în detaliu
Așadar, cinci pași: plasați controlul și apelați LoadFromFile, atașați acțiunile predefinite din PDFlibViewerActions, apelați HighlightSearchHits pentru căutarea în pagină, gestionați OnAnnotClick pentru fluxurile de lucru cu adnotări și expuneți PrintDoc. Fiecare piesă prezentată aici este inclusă în versiunea standard a losLab PDF Library pentru Delphi, unde controlul de vizualizare, clasele de acțiuni și motorul de randare se instalează împreună