Un visor de PDF en Delphi se reduce a dos componentes y el cableado entre ellos. TPdf posee el documento: abre el fichero, lo desencripta y responde preguntas sobre el recuento de páginas y los metadatos. TPdfView es el control visual que pinta las páginas en pantalla y maneja el desplazamiento, el zoom y la página que el usuario está mirando actualmente. PDFium Component envuelve el mismo motor de renderizado que se distribuye dentro de Chrome, de modo que los glifos, el anti-aliasing y el color que obtiene en el lienzo coinciden con lo que sus usuarios ya ven en su navegador. El trabajo no está en el renderizado. Está en conectar el objeto de documento a la vista, cargar sin bloqueos un fichero dañado o protegido con contraseña, y dar al usuario el puñado de controles que hacen que un visor se sienta acabado: pasar la página, cambiar el zoom, ajustar la página a la ventana
Esto le guía a través de ese ensamblaje en el orden en que realmente lo construye. Todo aquí renderiza una sola página a la vez, que es lo que quieren la mayoría de los flujos de trabajo de documentos. Si necesita páginas apiladas en una columna de desplazamiento continuo, esa es una decisión de diseño diferente y no el camino aquí
Conectar TPdf a TPdfView
Suelte un TPdf y un TPdfView en el formulario, luego indique a la vista qué documento mostrar. Esa única asignación es el enlace completo entre el documento no visual y el control que lo pinta
procedure TFormMain.FormCreate(Sender: TObject);
begin
// Pdf and PdfView were dropped at design time.
PdfView.Pdf := Pdf; // the view paints whatever this document holds
PdfView.FitMode := pfmFitWidth; // start the user at a sensible zoom
end;
Antes de que cualquiera de estas cosas se ejecute, la biblioteca nativa PDFium tiene que estar en la máquina. PDFium Component llama a pdfium32.dll o pdfium64.dll dependiendo de su plataforma de destino, y el documento simplemente se niega a abrirse si no se puede encontrar la DLL. Distribuya la DLL correspondiente junto a su ejecutable, o colóquela donde el cargador del sistema la encuentre. Las versiones habilitadas para V8 existen solo para los PDFs que llevan JavaScript que usted quiere ejecutar, cosa que un visor normal no hace, así que alcance la DLL estándar a menos que tenga una razón concreta para no hacerlo
Cargar un documento sin confiar en la entrada
El instinto es envolver la carga en un try/except y tratar una excepción arrojada como un fallo. Ese instinto es incorrecto aquí, y equivocarse produce un visor que se ve bien hasta que alguien le entrega un fichero roto. Configurar Active := True no genera un raise sobre un fallo de carga. PDFium Component atrapa el error interno y deja Active asentado en False, así que la única manera honesta de saber si el documento se abrió es leer la propiedad de nuevo después de que usted la fije
procedure TFormMain.OpenDocument(const FileName: string);
begin
Pdf.FileName := FileName;
Pdf.Active := True; // never raises; failure leaves Active = False
if not Pdf.Active then
begin
ShowMessage('Could not open ' + FileName);
Exit;
end;
PdfView.PageNumber := 1; // the view tracks its own current page
UpdatePageLabel;
end;
Dos cosas merecen atención. La primera es que PageNumber existe en ambos objetos y los dos son independientes. Pdf.PageNumber es la noción del documento de una página actual; PdfView.PageNumber es la página que el control realmente muestra, y es la que usted ajusta para mover al usuario por el fichero. Establecer una no mueve a la otra, de manera que un visor siempre conduce la propiedad de la vista. La segunda es la indexación en base 1: las páginas van desde 1 hasta Pdf.PageCount, no desde 0, lo cual pilla desprevenido a todo aquel que esté acostumbrado a las matrices en base cero
Manejar un fichero encriptado
Los documentos encriptados se integran en la misma ruta de carga. Si la contraseña de apertura se establece antes de la activación, el documento se desencripta a medida que se abre; si es incorrecta o falta, Active se queda en False exactamente como lo hace para un fichero corrupto. Así que la recuperación es pedir una contraseña e intentar la activación otra vez
procedure TFormMain.OpenWithPassword(const FileName: string);
var
Password: string;
begin
Pdf.FileName := FileName;
Pdf.Active := True;
if not Pdf.Active then
begin
if InputQuery('Password required', 'Password:', Password) then
begin
Pdf.Password := Password; // must be set before Active := True
Pdf.Active := True;
end;
if not Pdf.Active then
begin
ShowMessage('Unable to open the document.');
Exit;
end;
end;
PdfView.PageNumber := 1;
end;
Debido a que el fallo es silencioso tanto para una contraseña incorrecta como para un fichero dañado, no se pueden distinguir los dos basándose únicamente en Active. En la práctica eso es aceptable para un visor: el usuario o bien proporciona la contraseña correcta o bien se entera de que el fichero no se abrirá, y el mensaje se lee igual de ambas formas
Navegar por el documento
Con el documento abierto, la navegación es aritmética sobre PdfView.PageNumber delimitada por Pdf.PageCount. El único trabajo real es el pinzamiento, de modo que los botones nunca empujen la página fuera del rango y los botones del principio y del final permanezcan deshabilitados en los extremos del fichero
procedure TFormMain.GoToPage(NewPage: Integer);
begin
if not Pdf.Active then
Exit;
if NewPage < 1 then
NewPage := 1
else if NewPage > Pdf.PageCount then
NewPage := Pdf.PageCount;
PdfView.PageNumber := NewPage;
UpdatePageLabel;
end;
// the four navigation buttons reduce to one call each
procedure TFormMain.FirstClick(Sender: TObject); begin GoToPage(1); end;
procedure TFormMain.PrevClick(Sender: TObject); begin GoToPage(PdfView.PageNumber - 1); end;
procedure TFormMain.NextClick(Sender: TObject); begin GoToPage(PdfView.PageNumber + 1); end;
procedure TFormMain.LastClick(Sender: TObject); begin GoToPage(Pdf.PageCount); end;
Un cuadro de texto de "ir a la página N" es la misma llamada a GoToPage alimentada por un entero analizado, y la sujeción cubre el caso donde el usuario teclea 9999 en un fichero de diez páginas. Mantenga UpdatePageLabel como el único lugar que escribe "Página 3 de 12" de modo que la lectura nunca se desvíe en sincronía de lo que la vista muestra
Zoom: porcentajes explícitos y modos de ajuste
El zoom en TPdfView llega en dos sabores que interactúan, y comprender la interacción es la diferencia entre un control de zoom que se comporta y uno que se pelea con el usuario. La ruta directa es la propiedad Zoom, un porcentaje donde 100 significa el tamaño real. La otra ruta es FitMode, la cual le indica a la vista que calcule el zoom por usted y que lo mantenga recalculado mientras se cambia el tamaño de la ventana
// fixed magnifications
PdfView.Zoom := 100; // actual size
PdfView.Zoom := 50; // half
PdfView.Zoom := 200; // double
// let the view size the page to the window, and keep it sized on resize
PdfView.FitMode := pfmFitWidth; // page width fills the control
PdfView.FitMode := pfmFitPage; // whole page visible
PdfView.FitMode := pfmActualSize; // 1:1 with the document's points
Aquí está la parte con la que la gente tropieza. Asignar Zoom directamente restablece FitMode a pfmNone. Ese es el comportamiento correcto, no un error: en el momento en que el usuario escoge un 150% exacto, la vista ya no puede a la vez honrar el "ajustar a la anchura", porque ambas peticiones entran en conflicto. La consecuencia para su UI es que un botón de ampliar y un botón de ajustar a la página son estados mutuamente excluyentes, y la barra de herramientas debería hacer visible el modo activo. Cuando el usuario hace clic en ajustar a la página, fije FitMode; cuando haga clic en un zoom numérico, fije Zoom y deje que elimine el modo de ajuste por su cuenta
Si usted prefiere calcular el valor de ajuste por sí mismo, quizás para alimentar un control deslizante de zoom con el porcentaje de ajuste actual, los ayudantes por página le dan los números sin cambiar el modo. PageWidthZoom[N], PageZoom[N] y ActualSizeZoom[N] devuelven el porcentaje que ajustaría la página N a la anchura, la ajustaría completa o la renderizaría en su tamaño real
// seed a zoom readout from the fit-to-width value of the current page
var
FitPercent: Double;
begin
FitPercent := PdfView.PageWidthZoom[PdfView.PageNumber];
ZoomEdit.Text := Format('%.0f%%', [FitPercent]);
end;
Lo que realmente necesita un visor terminado
El visor anterior consta de unas pocas decenas de líneas, y ya hace el trabajo que necesita un flujo de trabajo de documentos: abre un fichero, sobrevive a uno malo, muestra una página, se mueve entre páginas y cambia la ampliación a mano o por ajuste. PDFium hace las partes difíciles silenciosamente. Las fuentes incrustadas se resuelven, las anotaciones y los campos de formulario se pintan allí donde el documento los coloca, y la página que usted ve concuerda con la que vería un usuario de Chrome, porque es el mismo motor el que dibuja ambas
A partir de esta base las incorporaciones son incrementales antes que estructurales. La selección de texto y la búsqueda leen desde la misma capa de texto que PDFium ya construye; metadatos como Pdf.Title y Pdf.Author están a una lectura de propiedad de distancia; la rotación y la escala de grises son opciones de renderizado que usted pasa cuando dibuja una página a un mapa de bits. Ninguno de estos altera la espina dorsal que tiene aquí, que es el objeto documento, la vista, y el flujo de cargar-luego-navegar que los conecta. Entienda bien esa espina dorsal y el resto es decoración
Los componentes TPdf y TPdfView utilizados en todo esto son parte de PDFium Component para Delphi y C++Builder, el cual contiene la referencia completa del visor en su página de producto