Incrustar un panel de lectura de PDF en una aplicación de negocios en Delphi no requiere escribir un ciclo de renderizado, un motor de desplazamiento ni un modelo de zoom. losLab PDF Library incluye TPDFlibViewer, un control VCL que abre un documento con una llamada y trae como comportamiento integrado la selección de texto, el paneo, el zoom por marquesina, las instantáneas, el resaltado de regiones, el resaltado de búsqueda de texto completo, la interacción con anotaciones y la impresión. Este artículo recorre los cinco pasos que convierten un formulario vacío en un visor funcional: colocar el control, conectar las acciones listas para usar, agregar el resaltado de búsqueda, manejar los clics en anotaciones e imprimir
¿Por qué usar un control visor en lugar de escribir un ciclo de renderizado?
El argumento de las capas va primero, porque explica lo que usted realmente está comprando. Renderizar una página a un mapa de bits es el 20 por ciento fácil; el otro 80 por ciento es todo lo que lo rodea: una caché de mapas de bits de página con un presupuesto de desalojo, un diseño de desplazamiento a través de páginas de distintos tamaños, modos de zoom que se recalculan al redimensionar, pruebas de impacto (hit-testing) que mapean un píxel del mouse de vuelta a coordenadas de página con cualquier zoom y rotación, y una programación de repintado que no parpadea. TPDFlibViewer es dueño de todo eso y delega el trabajo de rasterización al mismo motor descrito en el artículo de renderizado multimotor, de modo que la capa de control y la capa de renderizado se mantienen reemplazables por separado. El código de su formulario habla con un control de Windows desplazable, no con un renderizador
¿Cómo se incrusta un visor de PDF en una aplicación Delphi?
TPDFlibViewer.LoadFromFile abre un documento a través de la instancia interna TPDFlib propia del control y devuelve 1 si tiene éxito, así que el visor mínimo es una llamada al constructor, una asignación de Parent y una carga. Si usted ya tiene una instancia TPDFlib en otra parte de la aplicación, AttachLibrary muestra su documento seleccionado en su lugar, y la propiedad del objeto permanece de su lado
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; // paneo arrastrando; un clic simple sigue abriendo los enlaces
end;
La propiedad ViewerMode selecciona lo que hace el mouse, entre cinco valores: vmSelect es la herramienta predeterminada de selección de texto, vmHand panea arrastrando con un cursor de agarre mientras un clic simple sigue abriendo los enlaces, vmZoom es un zoom por marquesina que amplía la banda arrastrada hasta llenar la ventana de visualización, vmSnapshot captura una región arrastrada al portapapeles (con un evento OnSnapshot y un interruptor SnapshotToClipboard), y vmHighlight pinta resaltados de región translúcidos y persistentes. La herramienta de resaltado merece una nota sobre coordenadas: las regiones se almacenan en el espacio de usuario de la página, no en píxeles de cliente, de modo que las marcas de AddHighlightRegion sobreviven al zoom, a la rotación de la vista y a la navegación entre páginas sin ninguna contabilidad de su parte. En una vista rotada, la dirección de arrastre puede incluso ir en contra de los ejes de la página, lo que produce un ancho o alto negativo tras el mapeo de rotación inversa — el control normaliza el rectángulo por usted
¿Cómo se conecta una barra de herramientas PDF en Delphi sin código de pegamento?
La unidad PDFlibViewerActions elimina por completo el pegamento de la barra de herramientas. Proporciona veinte descendientes de TAction listos para usar que cubren la navegación de páginas (TPDFlibViewerFirstPage, TPDFlibViewerPriorPage, TPDFlibViewerNextPage, TPDFlibViewerLastPage), el zoom (TPDFlibViewerZoomIn, TPDFlibViewerZoomOut, TPDFlibViewerFitWidth, TPDFlibViewerFitPage, TPDFlibViewerActualSize), la rotación de la vista (TPDFlibViewerRotateClockwise, TPDFlibViewerRotateAntiClockwise), la impresión, los diálogos de abrir archivo y guardar como, copiar el texto seleccionado, y una acción de cambio por cada herramienta de mouse (TPDFlibViewerSelectMode, TPDFlibViewerHandMode, TPDFlibViewerMarqueeZoomMode, TPDFlibViewerSnapshotMode, TPDFlibViewerHighlightMode). Cada acción apunta a un control a través de su propiedad publicada Viewer y mantiene su propio estado: toda acción se deshabilita a sí misma mientras no hay ningún documento abierto, las acciones de navegación siguen la posición de página, la de copiar sigue si existe una selección, y las acciones de modo y de ajuste mantienen su estado marcado sincronizado con la herramienta activa y el modo de zoom. En tiempo de diseño todas se registran en la categoría losLab PDF del editor de Action List; en tiempo de ejecución las mismas clases son tres líneas cada una
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;
Vale la pena conocer una trampa de alcance de unidad si usted construye su propio registro de acciones sobre este patrón. RegisterActions está declarada en System.Actions, no en Vcl.ActnList, y el error de compilación que obtiene con solo Vcl.ActnList en la cláusula uses es activamente engañoso: el literal de arreglo de clases [TAction1, TAction2, ...] termina por interpretarse como un conjunto, lo que produce una cadena de errores E2010 Incompatible types: 'Integer' and 'class of ...' que parecen una incompatibilidad de tipos en sus clases de acción. La solución es una sola entrada en uses, pero el diagnóstico apunta a todas partes menos a ella. Para barras de herramientas de zoom, TPDFlibViewer.EffectiveZoomPercent reporta el zoom realmente en efecto — incluido el factor calculado en los modos de ajuste al ancho y ajuste a la página — de modo que un paso de acercamiento puede partir de la ampliación actual real en lugar del último valor que usted asignó
Resaltado de coincidencias de búsqueda y la inversión de coordenadas detrás
HighlightSearchHits busca una consulta en todo el documento y pinta una barra translúcida sobre cada coincidencia, devolviendo el conteo de coincidencias; ClearSearchHighlights, SearchHighlightCount y la propiedad SearchHighlightColor completan la superficie. Como los rectángulos de coincidencia se almacenan en el espacio de la página y se reproyectan en cada repintado, las barras se mantienen pegadas a su texto mientras el usuario se desplaza, hace zoom y rota la vista. Si usted usa el TPDFlibSearchPanel complementario, su RunSearch pinta las mismas barras en el visor adjunto de forma automática a través de la propiedad HighlightInView, que está activada de forma predeterminada
procedure TMainForm.btnSearchClick(Sender: TObject);
var
Hits: Integer;
begin
FViewer.SearchHighlightColor := clYellow;
Hits := FViewer.HighlightSearchHits(edtQuery.Text, False); // sin distinguir mayúsculas
StatusBar1.SimpleText := Format('%d matches', [Hits]);
end;
La parte sutil, y la razón para preferir la ruta integrada en lugar de armar la suya a partir de los resultados de búsqueda crudos, es la conversión del sistema de coordenadas. El motor de texto reporta los rectángulos de coincidencia en coordenadas que dependen del ajuste Origin de la biblioteca: con Origin 0 o 3 el eje Y crece de abajo hacia arriba como en el espacio de usuario nativo de PDF, y con Origin 2 o 3 el eje X se mide desde el borde derecho. La superposición del visor trabaja en un espacio de página de arriba hacia abajo y con base a la izquierda, así que cada rectángulo de coincidencia debe invertirse (Y = PageHeight - Bottom, y cuando corresponda X = PageWidth - Right) antes de almacenarse — tomar el borde superior reportado al pie de la letra bajo el origen predeterminado pinta el resaltado en la posición verticalmente reflejada de la página. TPDFlibViewer realiza esta conversión según el origen activo cuando registra cada coincidencia, y luego aplica el mapeo de rotación directa en el momento de pintar, que es la razón por la que las barras caen correctamente en cualquier ángulo. La translucidez en sí usa la API AlphaBlend de Windows con un alfa de origen constante, ya que el lienzo de la VCL no tiene composición alfa nativa. Las API subyacentes de búsqueda de texto y geometría se tratan en profundidad en el artículo de búsqueda de texto y enumeración de elementos de página
Clics en anotaciones, sugerencias y exportación de adjuntos
TPDFlibViewer trata las anotaciones como objetos interactivos de primera clase. Hacer clic en una anotación que no es un enlace dispara OnAnnotClick con el número de página, el índice de anotación con base 1 y la cadena de subtipo; AnnotAtPagePoint expone la misma prueba de impacto de forma programática y omite las anotaciones Popup acompañantes, de modo que usted siempre obtiene la anotación que el usuario realmente ve. Pasar el mouse sobre una anotación muestra su texto Contents como la sugerencia (hint) del control a través del interruptor AnnotHints, que está activado de forma predeterminada y coopera con la propiedad estándar ShowHint de la VCL. Para anotaciones FileAttachment, SaveAnnotAttachmentToFile escribe el archivo incrustado en disco y devuelve 1 si tiene éxito, lo que completa un flujo de clic para guardar en un puñado de líneas
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;
Impresión y los límites que debe tener en cuenta
La impresión no necesita una integración aparte. PrintDoc muestra el diálogo de impresión estándar de Windows — elección de impresora, rango de páginas, copias, intercalado — y entrega el trabajo al motor de impresión de la biblioteca, mientras que PrintDoc(False) envía todo el documento a la impresora predeterminada sin ninguna interfaz. PrintDocRange es totalmente programático, con un nombre de impresora explícito, página inicial y final, número de copias e indicador de intercalado, que es el punto de entrada adecuado para trabajos de impresión por lotes o disparados desde un servidor a partir de la misma instancia del visor
Dos límites merecen mención honesta. Primero, TPDFlibViewer es un control VCL de Windows; la capa del visor queda excluida de las compilaciones NOVCL, así que los servicios de consola y los destinos sin VCL deben usar directamente la API de renderizado TPDFlib subyacente. Segundo, el uso sin interfaz (headless) se admite deliberadamente pero está acotado: CaptureRegion renderiza cualquier región del cliente — páginas más resaltados — en un mapa de bits propiedad de quien llama y funciona en un control sin padre y sin identificador de ventana, porque la ruta de pintado evita las llamadas VCL que forzarían la creación del identificador. Eso hace práctica la verificación a nivel de píxel en pruebas automatizadas, pero cualquier cosa que genuinamente necesite un ciclo de mensajes, como las herramientas de mouse interactivas, sigue perteneciendo a una sesión de interfaz real. En el lado del rendimiento, el control mantiene una caché de páginas en memoria con un presupuesto configurable mediante SetCacheLimit, y puede persistir las páginas renderizadas entre sesiones; el artículo de la caché de páginas en disco del visor cubre esa capa y su comportamiento de DPI por monitor en detalle
Cinco pasos, entonces: colocar el control y llamar a LoadFromFile, adjuntar las acciones listas para usar de PDFlibViewerActions, llamar a HighlightSearchHits para la búsqueda en página, manejar OnAnnotClick para los flujos de anotaciones y exponer PrintDoc. Cada pieza mostrada aquí viene incluida en la versión estándar de losLab PDF Library para Delphi, donde el control visor, las clases de acción y el motor de renderizado se instalan juntos