Dos documentos abiertos a la vez, el mismo número de página, cada uno en su propio panel desplazable: ese es el núcleo de un visor de comparación. PDFium Component ofrece esto a través de un modelo de objetos sencillo donde TPdf posee el fichero y TPdfView posee la visualización. Un documento, un TPdf, un TPdfView. Si usted quiere tres paneles, usted tiene tres pares. Las partes difíciles no son las llamadas a la API; son la aritmética de diseño cuando se cambia el tamaño de la ventana y la lógica de sincronización de páginas cuando decide qué vista debe seguir a cuál
Diseño del formulario
El formulario VCL alberga tres contenedores TScrollBox uno al lado del otro, cada uno con un TPdfView en su interior y alineado a alClient para que llene la caja. Dos componentes TSplitter se sitúan entre las cajas para que el usuario pueda ajustar el ancho de las columnas en tiempo de ejecución. Una barra de herramientas sobre los paneles lleva los botones de apertura, los controles de zoom y el conmutador de dos vistas / tres vistas
El modo de tres vistas es un booleano que el formulario rastrea internamente. Cuando se invierte, usted recalcula las anchuras y muestra u oculta la tercera columna. El enfoque más simple es borrar todas las propiedades Align, ocultar los divisores y, a continuación, establecer posiciones absolutas:
procedure TFormMain.UpdateLayout;
var
TotalWidth: Integer;
begin
TotalWidth := ClientWidth;
if ThreeViewMode then
begin
ScrollBox3.Visible := True;
ScrollBox1.Left := 0;
ScrollBox1.Width := TotalWidth div 3;
ScrollBox2.Left := ScrollBox1.Width;
ScrollBox2.Width := TotalWidth div 3;
ScrollBox3.Left := ScrollBox2.Left + ScrollBox2.Width;
ScrollBox3.Width := TotalWidth - ScrollBox3.Left;
// Apply the same (ClientHeight - toolbar height) to all three Height values
end
else
begin
ScrollBox3.Visible := False;
ScrollBox1.Left := 0;
ScrollBox1.Width := TotalWidth div 2;
ScrollBox2.Left := ScrollBox1.Width;
ScrollBox2.Width := TotalWidth - ScrollBox2.Left;
end;
end;
Establecer Align := alNone en las tres cajas antes de la aritmética de enteros evita que el motor de restricciones VCL se pelee con sus asignaciones. Restaure la visibilidad de los divisores después de posicionar si desea arrastrar para cambiar el tamaño en el modo de dos vistas
La altura de cada caja de desplazamiento es el área del cliente menos la altura del panel de la barra de herramientas. Debido a que la barra de herramientas está acoplada en la parte superior con alTop, ClientHeight - PanelButtons.Height le da el espacio vertical utilizable. Asigne esto a las tres cajas dentro de la misma llamada UpdateLayout de modo que nunca haya un fotograma en el que una caja sea más alta que las demás y cause un parpadeo en el diseño
Abrir un documento
Cada par de paneles necesita su propio procedimiento de apertura. El patrón es corto: desactive el componente, establezca el nombre del fichero, intente activar, y capture EPdfError si el fichero requiere una contraseña. Tenga en cuenta que TPdfView.Active es lo que controla el renderizado, pero TPdf.Active es lo que realmente abre el fichero; son independientes. Establecer PdfView.Active := True cuando su TPdf enlazado aún no está activo es inofensivo pero no muestra nada
procedure TFormMain.OpenPdfFile(PdfComponent: TPdf;
PdfViewComponent: TPdfView);
var
Password: string;
begin
if not OpenDialog.Execute then
Exit;
PdfComponent.Active := False;
PdfComponent.FileName := OpenDialog.FileName;
PdfComponent.Password := '';
try
PdfComponent.Active := True;
except
on E: EPdfError do
begin
if InputQuery('Password', 'Enter document password:', Password) then
begin
PdfComponent.Password := Password;
PdfComponent.Active := True;
end
else
raise;
end;
end;
if PdfComponent.Active then
begin
PdfViewComponent.PageNumber := 1;
SetActivePdfView(PdfViewComponent);
end;
end;
Siempre compruebe PdfComponent.Active después de la asignación; un fichero dañado o una contraseña incorrecta hace que la carga falle silenciosamente sin lanzar una excepción en la ruta predeterminada. Establecer PdfViewComponent.PageNumber := 1 de forma explícita después de una apertura exitosa evita un número de página obsoleto del documento anterior
El código de manejo de contraseñas anterior lanza un error en cualquier error que no sea el mensaje conocido de contraseña. Eso es intencional: usted quiere que los ficheros corruptos o no soportados afloren de inmediato en lugar de que sean tragados como un panel silencioso en blanco. Un usuario que no ve nada no tiene idea de si el fichero se cargó y simplemente está vacío, o si el componente lo rechazó. Lanzar el error lo mantiene visible
Seguimiento del panel activo
Cuando el usuario hace clic dentro de un panel, ese panel se vuelve activo. El formulario rastrea un campo privado FActivePdfView: TPdfView. La retroalimentación visual es un cambio de color del borde en el TScrollBox que lo contiene: establézcalo en clHighlight para el activo y clWindow para los demás. Conecte esto a cada TPdfView.OnClick y al procedimiento de apertura para que el foco siga al documento que acaba de abrir
Algunas operaciones se aplican a todos los paneles visibles en lugar de solo al activo. Un booleano FAllViewsMode en el formulario impulsa esa rama. Cuando es verdadero, los cambios de zoom y la navegación de páginas se distribuyen a cada panel que tiene un documento activo:
procedure TFormMain.ApplyZoomToAll(NewZoom: Double);
begin
if PdfView1.Active then PdfView1.Zoom := NewZoom;
if PdfView2.Active then PdfView2.Zoom := NewZoom;
if ThreeViewMode and PdfView3.Active then PdfView3.Zoom := NewZoom;
end;
Navegación de páginas sincronizada
La navegación sincronizada es opcional pero útil para flujos de trabajo de revisión de documentos donde ambos ficheros cubren el mismo rango de páginas. La lógica pertenece a un manejador de eventos que se dispara después de que el usuario navega por una vista. Cuando una vista de origen cambia su PageNumber, el manejador propaga ese número a las otras vistas, sujeto a una guarda: la vista de destino debe tener al menos esa cantidad de páginas, de lo contrario se omite
El PageNumber en TPdfView y en TPdf son independientes. TPdf.PageNumber rastrea qué página el componente de documento considera actual; TPdfView.PageNumber rastrea lo que se muestra en pantalla. Para fines de navegación, usted necesita la propiedad de la vista, no la propiedad del documento
Una casilla de verificación etiquetada con algo como "Sync pages" le da el control al usuario. Cuando está desmarcada, cada panel navega independientemente y el manejador sale inmediatamente. Esa independencia es importante para casos de uso donde los dos documentos tienen diferentes recuentos de páginas, o donde el usuario quiere encontrar el pasaje equivalente en una traducción que comienza en una página diferente. Forzar siempre la sincronización haría que la herramienta fuera más difícil de usar que una simple disposición de escritorio de dos ventanas
Una cosa a tener en cuenta: establecer PdfView.PageNumber mediante programación dentro del manejador de sincronización disparará a su vez el evento de cambio en esa vista. Protéjase contra la recursividad infinita con una bandera booleana que usted establece antes de la asignación y borra inmediatamente después. La bandera es por formulario, no por vista, porque las tres vistas comparten el mismo manejador
Zoom por panel
Cada TPdfView lleva su propia propiedad Zoom, un Double en porcentaje donde Zoom := 100 significa tamaño real (100%). Establecerlo anula cualquier FitMode activo. Para un botón de ajustar a la anchura en el panel activo, lea el zoom de ajuste desde PdfView.PageWidthZoom[PdfView.PageNumber] y asígnelo. Para ajustar a la página, use PageZoom[PageNumber]. Ambas son propiedades de matriz indexadas por el número de página en base 1, así que protéjase contra un número de página cero antes de acceder a ellas
Cuando usted exporta la página actual a una imagen, lea la rotación desde la vista pero llame a `RenderPage` en el componente TPdf, no en la vista. La forma de mapa de bits de TPdf.RenderPage toma dimensiones explícitas en píxeles más un valor TRotation y un conjunto TRenderOptions. La variante de la función devuelve un TBitmap propiedad del llamador que usted libera por sí mismo después de guardar:
procedure TFormMain.SaveActiveViewAsImage;
var
Pdf: TPdf;
Bmp: TBitmap;
Jpeg: TJpegImage;
begin
if not Assigned(FActivePdfView) or not FActivePdfView.Active then
Exit;
Pdf := FActivePdfView.Pdf;
Pdf.PageNumber := FActivePdfView.PageNumber;
Bmp := Pdf.RenderPage(
0, 0,
Round(Pdf.PageWidth * 2),
Round(Pdf.PageHeight * 2),
FActivePdfView.Rotation, [], clWhite);
try
if SavePictureDialog.Execute then
begin
Jpeg := TJpegImage.Create;
try
Jpeg.Assign(Bmp);
Jpeg.CompressionQuality := 90;
Jpeg.SaveToFile(SavePictureDialog.FileName);
finally
Jpeg.Free;
end;
end;
finally
Bmp.Free;
end;
end;
El multiplicador de 2x sobre la anchura y la altura da una salida más nítida para los documentos con texto fino. El try/finally alrededor de la liberación del mapa de bits no es opcional; una cancelación en TSaveDialog aún pasa por el bloque finally, y usted quiere que el mapa de bits se libere sin importar lo que haya hecho el usuario
Requisitos de la DLL
PDFium Component envuelve la biblioteca nativa pdfium. Un proceso host de 32 bits necesita pdfium32.dll; un host de 64 bits necesita pdfium64.dll. Las variantes con el motor de JavaScript V8 añaden el sufijo v8 y pesan aproximadamente entre 23 y 27 MB frente a las compilaciones estándar de 5 a 6 MB. Para un visor de comparación que desactiva el rellenado de formularios (Pdf.FormFill := False), la compilación estándar sin V8 es suficiente y mantiene la distribución más pequeña
Coloque la DLL en el mismo directorio que el ejecutable, o en cualquier directorio del PATH del sistema. El componente la carga bajo demanda cuando se activa el primer TPdf, de manera que una DLL faltante aflora en ese momento en lugar de al inicio de la aplicación. Si usted distribuye un instalador, el enfoque más fiable es copiar la DLL en la carpeta de la aplicación durante la instalación en lugar de confiar en un directorio del sistema que un administrador podría limpiar más tarde
Las compilaciones de V8 son principalmente útiles cuando necesita interactuar con las acciones de JavaScript de PDF, por ejemplo para disparar campos de cálculo o manejadores de envío. Un visor de comparación pasivo no tiene ninguna razón para ejecutar JavaScript; establecer Pdf.FormFill := False antes de Active := True omite por completo el entorno de relleno de formularios, lo que también significa que no se inicializa ningún motor de JS incluso si se utiliza la compilación estándar. Ese es el valor predeterminado correcto para un visor de sólo lectura sin importar qué variante de la DLL usted distribuya
Para obtener más detalles sobre el componente PDFium Component y su API completa, visite la página del producto Delphi PDFium Component