HotPDF importa un archivo SVG a un PDF cargado o creado como un Form XObject reutilizable mediante ImportSVGFormXObject, que devuelve el nombre de recurso del formulario creado. Colocarlo es una llamada aparte, DrawSVGFormXObject, de modo que una sola importación puede estamparse en muchas páginas a distintos tamaños sin duplicar ni un solo byte del arte
Esa separación importa más de lo que parece. Un logotipo rasterizado a 300 DPI y colocado en 400 páginas son 400 copias de un mapa de bits. El mismo logotipo como Form XObject es un único objeto referenciado 400 veces, se mantiene nítido a cualquier nivel de zoom, y se imprime a la resolución del dispositivo en lugar de a los DPI que parecieran suficientes en el momento de generarlo
¿Por qué un Form XObject en lugar de dibujar el SVG cada vez?
Un Form XObject es el mecanismo propio de PDF para contenido reutilizable: un flujo de contenido autocontenido con su propia caja delimitadora y diccionario de recursos, invocado por nombre. Importar un SVG a uno de ellos te da tres propiedades que el dibujo directo no ofrece. El arte existe una sola vez en el archivo. Cada colocación es una pequeña matriz y una invocación. Y las formas siguen siendo vectores, de modo que un visor al 800 % de zoom dibuja curvas, no píxeles
El detalle delicado son los recursos. Las fuentes y los estados gráficos de un formulario deben residir en el propio diccionario /Resources del formulario, no en los recursos de la página que resultara estar activa en el momento de la importación. HotPDF escribe las fuentes Base 14 y las entradas ExtGState que un formulario necesita dentro del propio formulario, porque un formulario que toma prestado un nombre de su primera página anfitriona se renderiza correctamente allí, pero produce un nombre de recurso sin resolver en cualquier otra página en la que se coloque después
Dos inversiones, una para la geometría y otra para los glifos
El sistema de coordenadas de SVG crece hacia abajo desde la esquina superior izquierda; un Form XObject de PDF usa el espacio de usuario de PDF, que crece hacia arriba desde la esquina inferior izquierda. La transformación del viewBox raíz realiza una inversión del eje Y para que la geometría quede correctamente ubicada. El texto necesita entonces una segunda inversión, local, porque un glifo dibujado dentro de un marco ya invertido quedaría boca abajo
Esta es la misma corrección que aparece en sentido inverso cuando una página PDF se exporta como SVG, descrita en exportar páginas PDF a SVG. Hacerlo mal es fácil de detectar en un arte con texto y casi invisible en uno sin texto, razón por la cual un logotipo con eslogan es una prueba de humo mejor que un logotipo solo
uses
HPDFDoc;
var
Pdf: THotPDF;
SvgBytes: TBytes;
FormName: AnsiString;
Stats: THPDFSVGImportStatistics;
I: Integer;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.FileName := 'catalogue.pdf';
Pdf.BeginDoc;
SvgBytes := LoadFileBytes('brand-logo.svg');
FormName := Pdf.ImportSVGFormXObject(SvgBytes, Stats);
if FormName = '' then
raise Exception.Create('SVG rejected - see the import statistics');
// Una importación, muchas colocaciones: los bytes del arte existen una sola vez
for I := 1 to 40 do
begin
if I > 1 then
Pdf.AddPage;
Pdf.CurrentPage.DrawSVGFormXObject(FormName, 40, 780, 120, 40);
end;
Pdf.EndDoc;
finally
Pdf.Free;
end;
end;
¿Cómo sobreviven los hipervínculos al viaje?
No pueden sobrevivir dentro del formulario, porque un Form XObject es contenido y los destinos de enlace en PDF son anotaciones, que pertenecen a una página. Por eso, un elemento <a> del SVG de origen no puede copiarse sin más al flujo de contenido del formulario
HotPDF resuelve esto conservando los rectángulos de enlace como metadatos en el espacio del formulario durante el análisis, y materializándolos como anotaciones de enlace a nivel de página en cada colocación. DrawSVGFormXObject transforma esos rectángulos a través del rectángulo de destino que pasas, de modo que un formulario colocado dos veces a distintos tamaños obtiene dos conjuntos de áreas activas correctamente dimensionados. Los rectángulos de enlace se transforman únicamente a través del tamaño raíz y nunca entran en el flujo de contenido, lo que mantiene el arte dibujado idéntico sin importar cuántos enlaces lleve. Pasa AddLinks = False cuando quieras el arte sin la interactividad, por ejemplo en una página solo para impresión
Qué rechaza el importador, y por qué eso es precisamente la clave
SVG es un dialecto XML con scripting, referencias externas y expansión de entidades, lo que convierte a un archivo SVG arbitrario en una entrada hostil, igual que ocurre con un archivo HTML arbitrario. El perfil de importación falla de forma cerrada (fail-closed) ante todo lo que se extiende fuera del documento: referencias a recursos externos, scripts, atributos de manejadores de eventos, DTD y cualquier url() que no sea una referencia de fragmento local
Fail-closed significa que la importación falla en lugar de descartar en silencio la construcción problemática. Esa distinción importa en un pipeline por lotes, porque un descarte silencioso produce un documento al que le falta algo que nadie sabe que falta, mientras que un rechazo produce un archivo que puedes poner en cuarentena e inspeccionar. Si aceptas cargas de SVG procedentes de usuarios, este es el comportamiento que quieres, y combina bien con el aislamiento a nivel de proceso descrito en aislamiento de procesos de códecs para imágenes no confiables
Vale la pena conocer un límite interno si además renderizas fuentes en color. El subconjunto estricto de SVG usado para los glifos en color OpenType SVG pasa por un punto de entrada aparte y conserva su comportamiento de fallo original, más restrictivo. La importación de arte general es una ruta distinta, de modo que añadir soporte de arcos, transparencia y CSS al arte importado no amplió lo que se permite contener a un glifo de fuente
Degradados, máscaras y la regla de no rasterizar
Los degradados se importan como bandas de color vectoriales acotadas, restringidas por la trayectoria de recorte de la geometría original, y no como una imagen rasterizada. Las máscaras usan el mismo enfoque: el contorno de la máscara se convierte en una trayectoria de recorte vectorial. El resultado es que una ilustración cargada de degradados sigue siendo independiente de la resolución y se mantiene pequeña, mientras que un importador que rasterizara produciría un objeto de imagen grande cuya calidad queda fijada en el momento de la importación
El coste está en la fidelidad de los casos extremos. Una aproximación por bandas de un degradado suave no es matemáticamente idéntica al degradado SVG, y un arte con decenas de máscaras suaves superpuestas se parecerá más al original si en cambio lo renderizas como una imagen de alta resolución. Para conocer los modelos de sombreado que PDF soporta de forma nativa, y cuándo recurrir a ellos directamente, consulta renderizar sombreados axiales y radiales
// Leer las estadísticas de importación te dice qué llegó realmente
Writeln(Format('nodes=%d css rules=%d gradients=%d masks=%d',
[Stats.NodeCount, Stats.CSSRuleCount, Stats.GradientCount,
Stats.MaskCount]));
Writeln(Format('text runs=%d links=%d form bytes=%d',
[Stats.TextRunCount, Stats.LinkCount, Stats.OutputBytes]));
// Colocar el mismo formulario a dos tamaños produce dos conjuntos de enlaces correctos
Pdf.CurrentPage.DrawSVGFormXObject(FormName, 40, 700, 240, 80);
Pdf.CurrentPage.DrawSVGFormXObject(FormName, 320, 700, 120, 40);
// Colocación solo para impresión: arte sin las áreas activas
Pdf.CurrentPage.DrawSVGFormXObject(FormName, 40, 60, 120, 40, False);
Dónde encaja la importación de SVG junto a las demás rutas vectoriales
Existen tres rutas de importación vectorial y no son intercambiables. La importación de SVG es adecuada para el arte producido por herramientas de diseño y para todo lo que llega desde un pipeline web. La importación de metarchivos, tratada en importación vectorial de EMF y WMF, es adecuada para el contenido generado por aplicaciones de Windows y por componentes de gráficos que dibujan sobre un contexto de dispositivo. El dibujo directo mediante la API de página es adecuado para el arte que calcula tu propio código, donde no existe ningún formato intermedio
El argumento de la reutilización favorece la importación de SVG específicamente para elementos repetidos. Membretes, logotipos, marcas de agua, sellos de certificación y filetes decorativos se dibujan todos de forma idéntica en muchas páginas, y cada uno de ellos es un objeto de formulario más una invocación por página. Mide la diferencia de tamaño de archivo en un documento real antes de decidir que no importa: en un catálogo de 500 páginas con un logotipo vectorial por página, la diferencia entre un formulario y 500 copias en línea suele ser de varios megabytes
La importación de SVG, la importación de metarchivos y el dibujo vectorial directo comparten el mismo escritor de flujo de contenido para Delphi y C++Builder; la lista completa de funciones está en la página del componente HotPDF Delphi PDF