HotPDF representa una página PDF cargada en un objeto TBitmap de Delphi a través de una sola llamada: RenderLoadedPageToBitmap(PageIndex, DPI). La función interpreta el flujo de contenido de la página y devuelve un mapa de bits RGB de 24 bits propiedad del llamador a la resolución que elija, que es exactamente lo que necesita una tira de miniaturas, una vista previa de impresión o una canalización de exportación de PDF a imagen. Este artículo recorre la API y luego la parte que distingue a un representador utilizable de un juguete: dibujar texto a partir de los propios programas de fuentes incrustados en lugar de fuentes del sistema parecidas
¿Por qué representar una página PDF es más difícil que dibujar una imagen?
Una página PDF no es una imagen. Es un programa: un flujo de operadores que construyen rutas, seleccionan fuentes, establecen colores y colocan glifos, ejecutados con respecto al modelo gráfico definido en la norma ISO 32000-1 §8. Nada en el archivo dice cómo se ve ningún píxel. Para producir un mapa de bits debe ejecutar ese programa (mantener una matriz de transformación actual, una pila de estados gráficos para q/Q, una ruta de recorte, espacios de color de relleno y trazo) y rasterizar el resultado. Por eso "simplemente mostrar la página 3 como una imagen" es un intérprete de flujo de contenido, no una conversión de formato de archivo
El representador de HotPDF, introducido en la versión v2.253.0, está construido como seis unidades desacopladas que reflejan ese modelo: un núcleo de matriz afín para el álgebra de transformación de PDF [a b c d e f], una pila de estados gráficos, un solucionador de espacio de color (DeviceRGB, DeviceGray, DeviceCMYK, Indexed), un constructor de rutas que conecta los operadores de rutas de PDF con GDI, una capa de métricas de fuentes que lee matrices /Widths para avances correctos, y el intérprete que distribuye los operadores y dirige los otros cinco. Los XObjects de imagen pasan por la misma pila de decodificación que utiliza la biblioteca para la extracción, por lo que cada filtro de imagen que HotPDF puede decodificar para la extracción (incluidas las imágenes JPEG 2000 comprimidas con JPXDecode) también aparece en la salida representada
Representar una página cargada a un TBitmap
RenderLoadedPageToBitmap toma un índice de página basado en cero y un valor de PPP (DPI), donde 72 PPP asignan una unidad de espacio de usuario de PDF a un píxel. Devuelve nil en caso de fallo (índice fuera de rango, recursos faltantes) en lugar de generar una excepción, por lo que un visor puede omitir una página dañada y continuar. El llamador es propietario del mapa de bits devuelto y debe liberarlo
var
Pdf: THotPDF;
Bmp: TBitmap;
begin
Pdf := THotPDF.Create(nil);
try
if Pdf.LoadFromFile('report.pdf') > 0 then
begin
Bmp := Pdf.RenderLoadedPageToBitmap(0, 144); // page 1 at 144 DPI
if Bmp <> nil then
try
Image1.Picture.Assign(Bmp);
finally
Bmp.Free; // caller owns the bitmap
end;
end;
finally
Pdf.Free;
end;
end;
El argumento DPI realiza el trabajo de escalado para cada escenario común. Una tira de miniaturas se representa a 36 o 48 PPP y obtiene mapas de bits pequeños y rápidos; una vista previa en pantalla a 96 o 144 PPP coincide con la densidad típica de la pantalla; una ruta de exportación a 300 PPP produce imágenes con calidad de impresión. La rotación de la página a partir de la entrada /Rotate y la inversión del origen de /MediaBox (PDF coloca el origen abajo a la izquierda, GDI arriba a la izquierda) se gestionan dentro de la matriz de página a dispositivo, por lo que una página Carta de EE. UU. (US Letter) a 72 PPP se devuelve exactamente como 612×792 píxeles en la orientación correcta
¿Por qué las miniaturas de PDF representadas muestran glifos incorrectos?
Los glifos incorrectos o aproximados en la salida PDF representada casi siempre significan que el representador está sustituyendo una fuente del sistema en lugar de utilizar la fuente incrustada en el archivo. El primer representador de HotPDF hacía exactamente eso: eliminaba el prefijo de subconjunto de /BaseFont (convirtiendo ABCDEF+Arial en Arial), solicitaba a GDI una fuente del sistema con ese nombre y dibujaba el texto con ella. Para un documento que utiliza Arial o Times New Roman con codificación estándar, el resultado parece cercano. Pero es una aproximación y falla en formas bien definidas
Las fuentes incrustadas por subconjunto son el peor caso. Una fuente de subconjunto puede llevar solo los cuarenta glifos que un documento realmente utiliza, con códigos de caracteres asignados en un orden privado para ese archivo (el código 1 podría ser "T", el código 2 "h", etc.). Una fuente del sistema no sabe nada sobre esa asignación privada, por lo que el texto desaparece o sale como caracteres incorrectos por completo. Las codificaciones personalizadas, las fuentes de símbolos, las fuentes de códigos de barras y cualquier tipografía no instalada en la máquina de representación fallan de la misma manera. Un representador que se detiene en la sustitución de fuentes del sistema produce miniaturas que son reconocibles como la página, hasta que la página utiliza las fuentes que hicieron necesaria la incrustación en primer lugar
Representación de glifos incrustados: dibujar a partir del propio programa de fuentes
HotPDF cerró esa brecha a lo largo de cinco lanzamientos (de v2.268.0 a v2.272.0) analizando los programas de fuentes incrustados y reproduciendo sus contornos de glifos como rutas vectoriales de GDI rellenas. El texto en una página representada ahora proviene de los mismos datos de contorno que utiliza un visor conforme, lo que significa que las fuentes de subconjuntos, las codificaciones personalizadas y las tipografías no instaladas se representan con sus formas exactas. La cobertura se construyó según el tipo de fuente:
Para fuentes Type0/CIDFontType2 con un programa TrueType incrustado (FontFile2), el representador analiza directamente las tablas glyf y loca: los contornos cuadráticos se convierten en las curvas Bézier cúbicas que GDI entiende, se reconstruyen los puntos implícitos en la curva entre puntos consecutivos fuera de la curva y se reproducen recursivamente los glifos compuestos. Se admiten tanto los diseños Identity como los de flujo explícito CIDToGIDMap, y los avances de CID respetan las entradas de ancho /W y /DW, por lo que el texto Identity-H de dos bytes avanza correctamente
Los programas CFF (FontFile3, ya sea CIDFontType0C, Type1C o un contenedor OpenType) obtienen un intérprete completo de cadenas de caracteres (charstring) de Tipo 2: líneas, curvas, la familia flex, máscaras de sugerencia (hints) y llamadas a subrutinas locales/globales con el sesgo de subrutina correcto. Los programas CFF indexados por CID mapean códigos de caracteres a través del juego de caracteres de la fuente, lo que es importante para las fuentes de subconjunto cuyo orden de glifos difiere del orden de CID, y se respeta la selección de diccionario de fuentes (font-DICT) por glifo a través de FDArray/FDSelect. Las fuentes TrueType simples (no CID) resuelven códigos de un byte a través de la propia tabla cmap de la fuente incrustada con una cadena de subtablas robusta (los formatos Unicode 4 y 12 primero, luego las subtablas de símbolos con el espejo de uso privado F000, luego los formatos Macintosh heredados) mientras que las fuentes Type1 simples se resuelven a través de la codificación integrada del programa CFF
Dos refinamientos completan el panorama. Primero, los diccionarios /Encoding de fuentes simples se resuelven según la prioridad que prescribe la norma ISO 32000-1 §9.6.6: las matrices /Differences anulan la codificación base, que a su vez anula el mapa propio del programa de fuentes (la ruta de la que dependen las cadenas de herramientas derivadas de TeX y PostScript, con nombres de glifos que se resuelven a través de la Adobe Glyph List, el juego de caracteres CFF o el cmap TrueType). Segundo, las fuentes Type3, cuyos glifos son en sí mismos pequeños flujos de contenido, se reproducen a través del representador con la matriz de fuentes, el tamaño de fuente y la matriz de texto compuestos; los /Widths del espacio del glifo se interpretan a través de la /FontMatrix como exige la norma ISO 32000-1 §9.6.5, y los procedimientos de glifos que declaran un cuadro delimitador d1 se recortan a él, de modo que un glifo de código de barras mal formado no pueda pintar fuera de su celda. Cuando un código no se puede mapear (un programa dañado, un carácter no mapeado), el representador recurre al dibujo con fuente del sistema para ese glifo en lugar de descartar la ejecución de texto
¿Cómo hacer rápidas las representaciones repetidas?
La respuesta que incluye HotPDF es una caché de páginas utilizadas recientemente: RenderLoadedPageToBitmapCached mantiene hasta RenderCacheCapacity páginas representadas (por defecto 8) indexadas por índice de página y DPI, y un acierto de caché devuelve una copia nueva propiedad del llamador sin tocar el flujo de contenido, normalmente miles de veces más rápido que volver a interpretar la página. Ese patrón se adapta exactamente a los visores: un usuario que cambia entre dos páginas, o un evento de cambio de tamaño que vuelve a solicitar la misma página con el mismo DPI, acierta en la caché en todo momento
// Thumbnail strip: first pass renders, scrolling back hits the cache
for I := 0 to ThumbCount - 1 do
begin
Bmp := Pdf.RenderLoadedPageToBitmapCached(I, 48);
if Bmp <> nil then
try
ThumbList.AddThumbnail(I, Bmp);
finally
Bmp.Free;
end;
end;
// After editing a loaded page in place:
Pdf.InvalidateRenderedPageCache; // next render reflects the change
Sea realista con la factura de memoria antes de aumentar la capacidad. Una página Carta de EE. UU. (US Letter) a 300 PPP es de 2550×3300 píxeles, aproximadamente 25 MB como un mapa de bits de 24 bits, por lo que ocho páginas almacenadas en caché a resolución de exportación ocupan aproximadamente 200 MB. A PPP de miniatura, las mismas ocho entradas cuestan muy por debajo de un megabyte. Dimensione RenderCacheCapacity para el DPI al que realmente almacena en caché y llame a InvalidateRenderedPageCache después de cualquier edición en el lugar; la caché está indexada solo por página y DPI, y no puede ver si cambió el contenido subyacente. La carga de un nuevo documento la borra automáticamente
Una segunda caché funciona debajo de la caché de páginas: los XObjects de imagen decodificados se mantienen en un almacén con presupuesto de bytes limitado por ImageCacheMaxBytes (por defecto 32 MB) con desalojo de los menos utilizados recientemente. Una imagen de logotipo o membrete repetida en cada página se decodifica una vez por carga de documento en lugar de una vez por operador Do, lo que reduce aproximadamente a la mitad el tiempo de representación para páginas con imágenes compartidas y acelera la exportación TIFF multipágina en la misma medida. InvalidateRenderedPageCache borra también esta caché
Qué se sigue representando de forma aproximada
El representador se dirige al subconjunto común de documentos PDF y vale la pena saber dónde están los límites. Los espacios de color basados en CalRGB, Lab e ICC se aproximan en lugar de gestionarse por color; se manejan los espacios de color del dispositivo, las paletas indexadas y las búsquedas de color de funciones Tipo 0 muestreadas, pero un archivo de producción de impresión que dependa de intenciones de representación de ICC no será colorimétricamente exacto. Los patrones de sombreado (sh) y los modos de mezcla más allá del alfa simple también están fuera de alcance, y la recursividad del Form XObject está limitada en profundidad como protección de ciclo. Para facturas, informes, contratos y formularios (páginas hechas de texto, rutas e imágenes), la salida es fiel; para una prueba de diseño llena de degradados y grupos de transparencia, trate el mapa de bits como una vista previa, no como una prueba
La lectura práctica: si su canalización genera documentos con HotPDF o consume archivos PDF empresariales típicos, RenderLoadedPageToBitmap realiza el viaje de ida y vuelta con las formas de glifos incrustadas exactas, avances de CID correctos y geometría de página correcta. Las aproximaciones viven en los rincones del modelo gráfico que los documentos comerciales rara vez visitan
RenderLoadedPageToBitmap, su variante almacenada en caché y la canalización de representación de glifos incrustados que se describe aquí se distribuyen como parte de HotPDF Component para Delphi y C++Builder: una biblioteca VCL nativa sin dependencias de DLL externas, que cubre la creación, edición, extracción de texto y representación de páginas de PDF en un solo paquete