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 un flujo de trabajo de exportación de PDF a imagen. Este artículo recorre la API y luego la parte que distingue a un representador utilizable de uno de juguete: dibujar texto a partir de los propios programas de fuentes incrustados en lugar de fuentes del sistema similares
¿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, definen colores y colocan glifos, ejecutados contra el modelo de gráficos definido en la norma ISO 32000-1 §8. Nada en el archivo indica 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 estado de gráficos para q/Q, una ruta de recorte, espacios de color de relleno y trazo, y rasterizar el resultado. Es por eso que "solo mostrar la página 3 como una imagen" requiere 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 estado de gráficos, un solucionador de espacio de color (DeviceRGB, DeviceGray, DeviceCMYK, Indexed), un creador 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 envía los operadores y controla los otros cinco. Los objetos XObject de imagen pasan por la misma pila de decodificación que la biblioteca utiliza para la extracción, por lo que cada filtro de imagen que HotPDF puede decodificar para la extracción — incluidos las imágenes JPEG 2000 comprimidas con JPXDecode — también aparece en la salida representada
Representar una página cargada en un TBitmap
RenderLoadedPageToBitmap toma un índice de página basado en cero y un valor de PPP, donde 72 PPP mapean una unidad de espacio de usuario de PDF a un píxel. Devuelve nil en caso de falla (índice fuera de rango, falta de recursos) 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 de PPP 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 página de la entrada /Rotate y el giro del origen de /MediaBox (PDF coloca el origen abajo a la izquierda, GDI arriba a la izquierda) se manejan dentro de la matriz de página a dispositivo, por lo que una página tamaño Carta de EE. UU. a 72 PPP se devuelve exactamente como 612×792 píxeles con 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 del 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 se ve similar. Pero es una aproximación y falla de maneras bien definidas
Las fuentes incrustadas de subconjunto son el peor caso. Una fuente de subconjunto puede contener solo los cuarenta glifos que un documento realmente utiliza, con códigos de caracteres asignados en un orden privado de ese archivo — el código 1 podría ser "T", el código 2 "h", y así sucesivamente. Una fuente del sistema no sabe nada sobre esa asignación privada, por lo que el texto desaparece o se muestra como caracteres incorrectos por completo. Las codificaciones personalizadas, las fuentes de símbolos, las fuentes de códigos de barras y cualquier tipo de letra no instalado 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 desde el propio programa de fuente
HotPDF cerró esa brecha a lo largo de cinco lanzamientos (versiones 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 subconjunto, las codificaciones personalizadas y los tipos de letra no instalados se representan con sus formas exactas. La cobertura se construyó por variante de fuente:
Para las fuentes Type0/CIDFontType2 con un programa TrueType incrustado (FontFile2), el representador analiza las tablas glyf y loca directamente: los contornos cuadráticos se convierten a las curvas Bézier cúbicas que GDI comprende, se reconstruyen los puntos implícitos sobre la curva entre puntos consecutivos fuera de la curva y los glifos compuestos se reproducen de forma recursiva. 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 de dos bytes Identity-H avanza correctamente
Los programas CFF (FontFile3, ya sea CIDFontType0C, Type1C o un contenedor OpenType) obtienen un intérprete completo de cadenas de caracteres Type 2 (charstrings): líneas, curvas, la familia flex, máscaras de sugerencia (hint masks) y llamadas a subrutinas locales/globales con el sesgo de subrutina correcto. Los programas CFF basados en CID mapean códigos de caracteres a través del conjunto de caracteres (charset) de la fuente, lo cual es importante para fuentes de subconjunto cuyo orden de glifos difiere del orden de CID, y se respeta la selección de font-DICT por glifo a través de FDArray/FDSelect. Las fuentes TrueType simples (que no son 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 — formatos Unicode 4 y 12 primero, luego subtablas de símbolos con el espejo de uso privado F000, luego formatos heredados de Macintosh — mientras que las fuentes Type1 simples se resuelven a través de la codificación integrada del programa CFF
Dos refinamientos completan la imagen. Primero, los diccionarios de codificación /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 propio mapa del programa de fuente — 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 Lista de Glifos de Adobe, el conjunto de caracteres CFF o el cmap de 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 fuente, el tamaño de la fuente y la matriz de texto compuestos; los /Widths del espacio del glifo se interpretan a través de la /FontMatrix como requiere la norma ISO 32000-1 §9.6.5, y los procedimientos de glifo que declaran un cuadro delimitador d1 se recortan a él, de modo que un glifo de código de barras con formato incorrecto no pueda dibujarse fuera de su celda. Cuando un código no se puede mapear — un programa dañado, un carácter no asignado — el representador recurre al dibujo con fuente del sistema para ese glifo en lugar de descartar el bloque de texto
¿Cómo lograr que las representaciones repetidas sean rápidas?
La respuesta que ofrece 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 PPP, y un acierto de caché devuelve una copia nueva propiedad del llamador sin tocar el flujo de contenido — típicamente 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 los mismos PPP, aprovecha la caché en cada ocasión
// 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 consciente del costo de memoria antes de aumentar la capacidad. Una página tamaño Carta de EE. UU. a 300 PPP tiene 2550×3300 píxeles, lo que representa aproximadamente 25 MB como mapa de bits de 24 bits, por lo que os páginas almacenadas en caché a resolución de exportación ocupan aproximadamente 200 MB. A los PPP de miniaturas, las mismas ocho entradas cuestan mucho menos de un megabyte. Defina el tamaño de RenderCacheCapacity para los PPP a los que realmente almacena en caché y llame a InvalidateRenderedPageCache después de cualquier edición en el lugar — la caché se indexa únicamente por página y PPP, y no puede detectar que el contenido subyacente cambió. Cargar un nuevo documento la limpia automáticamente
Una segunda caché funciona debajo de la caché de páginas: los objetos XObject de imagen decodificados se mantienen en un almacén con presupuesto de bytes limitado por ImageCacheMaxBytes (por defecto 32 MB) con desalojo de los elementos menos utilizados recientemente (LRU). 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 de TIFF de varias páginas en la misma medida. InvalidateRenderedPageCache también limpia esta caché
Qué se sigue representando de manera 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 CalRGB, Lab y basados en 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 función Tipo 0 muestreadas, pero un archivo de producción de impresión que dependa de propósitos de representación de ICC no será colorimétricamente exacto. Los patrones de sombreado (sh) y los modos de mezcla más allá del canal alfa simple también están fuera de alcance, y la recursividad del objeto Form XObject está limitada en profundidad como protección de bucles. Para facturas, informes, contratos y formularios — páginas compuestas 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 flujo de trabajo genera documentos con HotPDF o consumes archivos PDF comerciales típicos, RenderLoadedPageToBitmap realiza el ciclo completo con las formas de glifos incrustadas exactas, los avances de CID correctos y la geometría de página correcta. Las aproximaciones viven en las esquinas del modelo de gráficos que los documentos comerciales rara vez visitan
RenderLoadedPageToBitmap, su variante almacenada en caché y el flujo de trabajo de representación de glifos incrustados descrito aquí se distribuyen como parte del componente 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