Het inbedden van een PDF-weergavepaneel in een Delphi-bedrijfsapplicatie vereist geen schrijven van een renderingslus, een schuifengine of een zoommodel. losLab PDF Library levert TPDFlibViewer, een VCL-control die een document opent met één aanroep en functies zoals tekstselectie, pannen, kaderzoom, momentopnamen, regiomarkering, accentuering van zoekresultaten in de volledige tekst, interactie met annotaties en afdrukken als ingebouwd gedrag biedt. Dit artikel doorloopt de vijf stappen die een leeg formulier veranderen in een werkende viewer: sleep de control, koppel de kant-en-klare acties, voeg zoekaccentuering toe, verwerk annotatieklikken en druk af
Waarom een viewer-control gebruiken in plaats van zelf een renderingslus te schrijven?
Het argument van de gelaagdheid komt eerst, omdat het verklaart wat u daadwerkelijk koopt. Een pagina renderen naar een bitmap is de makkelijke 20 procent; de overige 80 procent is alles wat eromheen zit: een pagina-bitmapcache met een verwijderingsbudget, een schuiflay-out over pagina's van verschillende formaten, zoommodi die opnieuw berekenen bij het aanpassen van de grootte, hit-testing die een muispixel terugvertaalt naar paginacoördinaten bij elke zoom en rotatie, en hertekenplanning die niet flikkert. TPDFlibViewer beheert dit allemaal en delegeert het rasterwerk naar dezelfde engine die beschreven staat in het artikel over multi-engine rendering, zodat de control-laag en de renderingslaag afzonderlijk vervangbaar blijven. Uw formuliercode communiceert met een schuifbare Windows-control, niet met een renderer
Hoe sluit u een PDF-viewer in binnen een Delphi-applicatie?
TPDFlibViewer.LoadFromFile opent een document via de eigen interne TPDFlib-instantie van de control en retourneert 1 bij succes. De minimale viewer bestaat dus uit een constructor-aanroep, een toewijzing van de Parent en één laadactie. Als u al elders in de applicatie een TPDFlib-instantie beheert, geeft AttachLibrary in plaats daarvan het geselecteerde document weer, waarbij het eigendom aan uw kant blijft
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;
De eigenschap ViewerMode selecteert wat de muis doet, uit vijf waarden: vmSelect is het standaardgereedschap voor tekstselectie, vmHand pant door te slepen met een handcursor terwijl een gewone klik nog steeds koppelingen volgt, vmZoom is een kaderzoom die de gesleepte band vergroot om de viewport te vullen, vmSnapshot legt een gesleept gebied vast naar het klembord (met een OnSnapshot-gebeurtenis en een SnapshotToClipboard-schakelaar), en vmHighlight tekent permanente doorschijnende regiomarkeringen. Het markeergereedschap verdient een opmerking over coördinaten: regio's worden opgeslagen in de gebruikersruimte van de pagina, niet in clientpixels, dus markeringen van AddHighlightRegion overleven zoomen, weergavenrotatie en paginanavigatie zonder enige boekhouding aan uw kant. In een geroteerde weergave kan de sleeprichting zelfs tegen de pagina-assen in lopen, wat na de omgekeerde rotatiekoppeling een negatieve breedte of hoogte oplevert — de control normaliseert de rechthoek voor u
Hoe koppelt u een PDF-werkbalk in Delphi zonder lijmcode?
De unit PDFlibViewerActions verwijdert de lijmcode van de werkbalk volledig. Het biedt twintig kant-en-klare afstammelingen van TAction die paginanavigatie (TPDFlibViewerFirstPage, TPDFlibViewerPriorPage, TPDFlibViewerNextPage, TPDFlibViewerLastPage), zoomen (TPDFlibViewerZoomIn, TPDFlibViewerZoomOut, TPDFlibViewerFitWidth, TPDFlibViewerFitPage, TPDFlibViewerActualSize), weergavenrotatie (TPDFlibViewerRotateClockwise, TPDFlibViewerRotateAntiClockwise), afdrukken, dialogen voor het openen en opslaan van bestanden, het kopiëren van geselecteerde tekst en één schakelactie per muisgereedschap (TPDFlibViewerSelectMode, TPDFlibViewerHandMode, TPDFlibViewerMarqueeZoomMode, TPDFlibViewerSnapshotMode, TPDFlibViewerHighlightMode) dekken. Elke actie verwijst naar een control via de gepubliceerde eigenschap Viewer en behoudt zijn eigen status: elke actie schakelt zichzelf uit zolang er geen document geopend is, de navigatieacties volgen de paginapositie, kopiëren volgt of er een selectie bestaat, en de modus- en passend-acties houden hun ingeschakelde status synchroon met het actieve gereedschap en de zoommodus. Tijdens het ontwerpen registreren ze zich allemaal in de categorie 'losLab PDF' van de Action List-editor; tijdens runtime zijn dezelfde klassen elk drie regels code
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;
Er is één valkuil binnen het unit-bereik die u moet kennen als u uw eigen actieregistratie bovenop dit patroon bouwt. RegisterActions is gedeclareerd in System.Actions, not in Vcl.ActnList, en de compilerfout die u krijgt als u alleen Vcl.ActnList in de uses-clausule hebt, is actief misleidend: de klasse-array-literal [TAction1, TAction2, ...] valt terug naar parsering als een set, wat een keten van E2010 Incompatible types: 'Integer' and 'class of ...'-fouten oplevert die lijken op een type-incompatibiliteit in uw actieklassen. De oplossing is een enkele uses-verwijzing, maar de diagnose wijst overal naartoe behalve daarnaartoe. Voor zoom-werkbalken rapporteert TPDFlibViewer.EffectiveZoomPercent de zoom die daadwerkelijk van kracht is — inclusief de berekende factor in de modi breedte-passend en pagina-passend — zodat een zoomstap kan voortbouwen op de werkelijke huidige vergroting in plaats van op de laatste waarde die u hebt toegewezen
Accentuering van zoekresultaten en de coördinatenomkering erachter
HighlightSearchHits doorzoekt het hele document naar een query en tekent een doorschijnende balk over elke overeenkomst, waarbij het aantal resultaten wordt geretourneerd; ClearSearchHighlights, SearchHighlightCount en de eigenschap SearchHighlightColor maken het geheel compleet. Omdat de resultatenrechthoeken worden opgeslagen in de paginaruimte en bij elke tekenbeurt opnieuw worden geprojecteerd, blijven de balken aan hun tekst geplakt terwijl de gebruiker schuift, zoomt en de weergave roteert. Als u het bijbehorende TPDFlibSearchPanel gebruikt, tekent de methode RunSearch hiervan automatisch dezelfde balken in de gekoppelde viewer via de eigenschap HighlightInView, die standaard is ingeschakeld
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;
Het subtiele deel, en de reden om de voorkeur te geven aan het ingebouwde pad boven het zelf bouwen op basis van ruwe zoekresultaten, is de conversie van het coördinatensysteem. De tekstengine rapporteert resultatenrechthoeken in coördinaten die afhankelijk zijn van de Origin-instelling van de bibliotheek: met Origin 0 of 3 loopt de Y-as van onder naar boven, zoals in de oorspronkelijke PDF-gebruikersruimte, en met Origin 2 of 3 wordt de X-as gemeten vanaf de rechterrand. De overlay van de viewer werkt in een van boven naar beneden gerichte, linksgebaseerde paginaruimte, dus elke resultatenrechthoek moet worden omgekeerd (Y = PageHeight - Bottom, en waar van toepassing X = PageWidth - Right) voordat deze wordt opgeslagen — het simpelweg overnemen van de gerapporteerde bovenrand onder de standaard oorsprong tekent de accentuering op de verticaal gespiegelde positie op de pagina. TPDFlibViewer voert deze conversie uit op basis van de actieve oorsprong wanneer het elk resultaat registreert, en past vervolgens de voorwaartse rotatiekoppeling toe tijdens het tekenen, wat de reden is waarom de balken onder elke hoek correct landen. De doorschijnendheid zelf maakt gebruik van de Windows AlphaBlend-API met een constante bron-alpha, aangezien het VCL-canvas geen systeemeigen alpha-compositing heeft. De onderliggende tekstzoek- en geometrie-API's worden uitgebreid behandeld in het artikel over tekstzoeken en paginalelement-opsomming
Annotatieklikken, hints en export van bijlagen
TPDFlibViewer behandelt annotaties als eersteklas interactieve objecten. Klikken op een niet-link annotatie activeert OnAnnotClick met het paginanummer, de op 1 gebaseerde annotatie-index en de subtype-tekenreeks; AnnotAtPagePoint voert programmatisch dezelfde hit-test uit en slaat bijbehorende popup-annotaties over, zodat u altijd de annotatie krijgt die de gebruiker daadwerkelijk ziet. Zweven over een annotatie toont de tekst van de Contents ervan als de hint van de control via de schakelaar AnnotHints, die standaard is ingeschakeld en samenwerkt met de standaard VCL-eigenschap ShowHint. Voor FileAttachment-annotaties schrijft SaveAnnotAttachmentToFile het ingebedde bestand naar de schijf en retourneert 1 bij succes, wat een klik-en-bewaar-workflow in slechts een paar regels voltooit
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;
Afdrukken en de grenzen waar u rekening mee moet houden
Afdrukken vereist geen afzonderlijke integratie. PrintDoc toont het standaard Windows-afdrukvenster — printerkeuze, paginabereik, kopieën, sortering — en draagt de taak over aan de afdrukengine van de bibliotheek, terwijl PrintDoc(False) het hele document naar de standaardprinter stuurt zonder enige gebruikersinterface. PrintDocRange werkt volledig programmatisch met een expliciete printernaam, begin- en eindpagina, aantal kopieën en sorteringsvlag, wat het juiste ingangspunt is voor batchgewijze of door een server geactiveerde afdruktaken die vanuit dezelfde viewer-instantie worden aangestuurd
Twee grenzen verdienen een eerlijke vermelding. Ten eerste is TPDFlibViewer een Windows VCL-control; de viewer-laag is uitgesloten van NOVCL-builds, dus consoleservices en niet-VCL-doelen moeten de onderliggende TPDFlib-renderings-API rechtstreeks gebruiken. Ten tweede wordt headless gebruik bewust ondersteund maar is dit begrensd: CaptureRegion rendert elk clientgebied — pagina's plus accentueringen — naar een door de aanroeper beheerde bitmap en werkt op een control zonder parent en zonder venster-handle, omdat het tekenpad VCL-aanroepen vermijdt die het maken van een handle zouden forceren. Dat maakt verificatie op pixelniveau in geautomatiseerde tests praktisch, maar alles wat echt een berichtencyclus nodig heeft, zoals de interactieve muisgereedschappen, hoort nog steeds thuis in een echte UI-sessie. Aan de prestatiekant houdt de control een paginacache in het geheugen bij met een configureerbaar budget via SetCacheLimit, en kan hij gerenderde pagina's over sessies heen bewaren; het artikel over de schijfpaginacache van de viewer behandelt die laag en het DPI-gedrag per monitor in detail
Vijf stappen dus: plaats de control en roep LoadFromFile aan, koppel de kant-en-klare acties uit PDFlibViewerActions, roep HighlightSearchHits aan voor in-page zoeken, verwerk OnAnnotClick voor annotatieworkflows en stel PrintDoc beschikbaar. Elk onderdeel dat hier wordt getoond, wordt geleverd in de standaard losLab PDF Library voor Delphi, waar de viewer-control, de actieklassen en de renderingsengine samen worden geïnstalleerd