HotPDF exporta una página de cualquier documento PDF cargado a marcado SVG independiente con una sola llamada, BuildLoadedPageSVG, que devuelve el documento SVG completo como una cadena. El marcado exportado conserva la geometría de la página, el texto como elementos text reales de SVG, las imágenes rasterizadas incrustadas y el estado de trazo que los operadores PDF habían establecido en cada operación de dibujo
Esa última parte es donde la mayoría de los conversores caseros fallan en silencio. Convertir una página PDF en SVG parece un problema de coordenadas, pero en realidad es un problema de estado. PDF es una máquina de pila cuyo estado gráfico cambia a medida que se interpreta el flujo de contenido; SVG es un árbol declarativo cuyos elementos llevan cada uno sus propios atributos de presentación. Todo lo que el intérprete no capture en el momento en que se emite un elemento simplemente desaparece del resultado, y la falla es silenciosa: se obtiene un SVG válido que representa una página sutilmente incorrecta
¿Por qué una página PDF no se convierte simplemente en SVG?
Tres discrepancias hacen que la conversión no sea trivial, y las tres producen un resultado que parece correcto hasta que se compara lado a lado con el original. La primera es el eje y. El espacio de usuario de PDF crece hacia arriba desde la esquina inferior izquierda de la página; SVG crece hacia abajo desde la esquina superior izquierda. Un solo volteo a nivel de página corrige las coordenadas de dibujo, pero rompe cada glifo, porque voltear todo el lienzo también refleja las formas de las letras
La segunda discrepancia es la herencia. En PDF, q y Q apilan y desapilan un estado gráfico que incluye el ancho de línea, el remate de línea, la unión de línea, el límite de inglete, el arreglo de guiones, la fase de guiones y el alfa. En SVG, un elemento que no especifica un atributo lo hereda de un grupo ancestro, lo cual es una regla de alcance completamente distinta. Un exportador que solo rastrea la matriz de transformación actual y olvida el estado de trazo permite que el estado restaurado después de una Q se filtre hacia los elementos siguientes
La tercera es que PDF expresa varias cosas por convención en lugar de por valor. Los remates y uniones de línea son números enteros, un ancho de línea igual a cero significa un trazo ultrafino en el espacio del dispositivo en lugar de una línea invisible, y las variantes con asterisco de los operadores de pintura cambian la regla de relleno en vez del color. Cada una de esas cosas necesita una traducción, no una copia
Una sola llamada para el caso común
Para la tarea habitual de exportar páginas para un visor web, una herramienta de comparación de diferencias o una entrega de diseño, la superficie de la API es una sola función. BuildLoadedPageSVG toma un índice de página basado en cero sobre el documento actualmente cargado y devuelve el documento SVG como un AnsiString:
var
Pdf: THotPDF;
I: Integer;
Svg: AnsiString;
Output: TFileStream;
begin
Pdf := THotPDF.Create(nil);
try
if Pdf.LoadFromFile('statements.pdf', '') <= 0 then
Exit; // LoadFromFile devuelve el número de páginas
for I := 0 to Pdf.LoadedPageCount - 1 do
begin
Svg := Pdf.BuildLoadedPageSVG(I);
if Length(Svg) = 0 then
Continue;
Output := TFileStream.Create(Format('page-%d.svg', [I + 1]), fmCreate);
try
Output.WriteBuffer(Svg[1], Length(Svg));
finally
Output.Free;
end;
end;
finally
Pdf.Free;
end;
end;
La misma exportación está disponible en la herramienta de línea de comandos de HotPDF como el comando export-svg, que resulta útil en canalizaciones de compilación y scripts de regresión donde se necesita una representación de una página comparable como texto sin escribir código Pascal. Como SVG es texto, es un complemento natural para la ruta de rasterización descrita en renderizar una página PDF a un mapa de bits: el mapa de bits muestra el aspecto de la página, el SVG muestra de qué está hecha
¿Cómo se asigna el texto PDF a los elementos de texto SVG?
HotPDF compone la cadena de la matriz de texto como prefijo por CTM por matriz de texto por volteo de glifo, donde el volteo de glifo es una multiplicación por la derecha por matrix(1,0,0,-1,0,0). Ese factor del lado derecho existe únicamente para cancelar el volteo vertical a nivel de página en las formas de los glifos, ya que el texto SVG dibujado en un marco local volteado, de lo contrario, aparecería al revés. Colocar la corrección en la matriz en lugar de en código con casos especiales significa que el texto rotado, reflejado e inclinado sale correcto sin ramificaciones adicionales
El posicionamiento horizontal usa la sintaxis de múltiples valores del atributo x del elemento text de SVG, una coordenada por carácter, acumulada a partir de cada avance de glifo más el espaciado de caracteres Tc y el espaciado de palabras Tw vigentes en ese momento. El escalado horizontal Tz se incorpora en las columnas a y c de la matriz de texto en lugar de emitirse por separado, de modo que un visor que ignore atributos de texto poco comunes igual coloca cada glifo donde el PDF lo puso. El texto producido mediante conformación compleja, tratado en conformación de texto de escritura compleja, sigue el mismo camino, porque el conformador ya resolvió los clústeres en glifos posicionados para cuando se interpreta el flujo de contenido
Rotación e imágenes: dos volteos fáciles de invertir por error
Una página con una entrada /Rotate distinta de cero necesita una pretransformación compuesta por un volteo contra la altura del lienzo rotado y una rotación expresada en un espacio de visualización con el eje y hacia arriba. Las tres matrices de rotación son (0,-1,1,0,0,W) para 90 grados, (-1,0,0,-1,W,H) para 180 y (0,1,-1,0,H,0) para 270, donde W y H son las dimensiones de la página antes de la rotación. Derivarlas a mano invita a errores de signo en exactamente tres lugares, así que el exportador las compone mediante la misma rutina de multiplicación de matrices que maneja el resto de las transformaciones
Las imágenes incrustadas necesitan su propio volteo, porque el espacio de imagen de PDF coloca la primera fila de muestras en el borde superior del cuadrado unitario, mientras que el elemento image de SVG tiene un marco local con el eje y hacia abajo. La transformación emitida es, por lo tanto, la CTM multiplicada por la derecha por matrix(1,0,0,-1,0,1). Hacer esto mal produce fotografías reflejadas verticalmente en una página que por lo demás es perfecta, el tipo de defecto que un revisor humano detecta al instante y que una prueba automatizada muchas veces no
¿Qué preserva realmente el dispositivo de estado gráfico?
HotPDF despacha los operadores de estado de trazo w, J, j, M y d a través de una interfaz de dispositivo opcional independiente, de modo que la fidelidad del trazo se agregó sin cambiar la vtable del dispositivo de contenido existente y sin romper la compatibilidad binaria con el código compilado contra versiones anteriores. En concreto, el SVG exportado recibe palabras clave traducidas en lugar de enteros PDF sin procesar:
// Las enumeraciones enteras de PDF se convierten en atributos de palabra clave SVG
// remate de línea 0, 1, 2 -> butt, round, square
// unión de línea 0, 1, 2 -> miter, round, bevel
//
// Un ancho de línea cero significa un trazo ultrafino en el espacio del
// dispositivo en PDF, por lo que el exportador emite vector-effect="non-scaling-stroke"
// para mantener el trazo visible y con un ancho cercano a un píxel de dispositivo tras la CTM
//
// f* B* b* seleccionan la regla par-impar y emiten fill-rule="evenodd",
// mientras que f B b mantienen el valor predeterminado de SVG, nonzero winding
Restaurar el estado en una Q cubre en conjunto la opacidad, el ancho de línea, el remate, la unión, el límite de inglete, el arreglo de guiones y la fase de guiones. Los Form XObjects anidados capturan y restauran ese mismo conjunto completo en sus límites, de modo que un borde discontinuo definido dentro de un sello no puede filtrar su patrón hacia el contenido de página que sigue. Si ya se rastrea el comportamiento de recorte y de la CTM por otras razones, este es el mismo modelo de estado que aparece en importación de vectores EMF y WMF, funcionando en la dirección opuesta
Límites que conviene conocer antes de publicarlo
El exportador es transparente respecto a su alcance, y conocer los límites de antemano sale más barato que descubrirlos en producción. El color llega al dispositivo SVG a través de los operadores rg, RG, g y G. Los rellenos establecidos mediante un espacio de color más scn, que es como se pintan los colores Separation, DeviceN e ICCBased, no llegan al dispositivo como una tripleta RGB resuelta, así que las páginas que usan colores planos de esta manera exportan su geometría pero no esos colores. Para fuentes orientadas a impresión, es mejor rasterizar o aplanar antes los colores planos; el modelo de pintura en sí se trata en renderizado de colores planos Separation y DeviceN
Dos notas menores ahorran tiempo de depuración. Los literales de color hexadecimales se emiten en mayúsculas, de modo que una prueba que verifique #ff0000 falla contra un #FF0000 perfectamente correcto. Y el dispositivo SVG tiene conteo de referencias a través de su interfaz, lo que significa que liberarlo es cuestión de dejar que la interfaz salga de alcance en lugar de llamar a Free sobre el objeto, una distinción que importa si se extiende el dispositivo para emitir marcado propio junto con el contenido de la página
La exportación a SVG combina de forma natural con la comparación estructural cuando se necesita saber si un documento generado realmente cambió entre dos compilaciones. El conjunto más amplio de herramientas en torno a los documentos cargados, desde el renderizado hasta la edición y la exportación, está documentado en la página del componente PDF para Delphi HotPDF