PDF Library for Delphi acepta las fuentes que un proyecto web ya distribuye. Importa contenedores WOFF2, enumera los ejes de diseño de una fuente variable como JSON, e instancia un conjunto elegido de coordenadas de eje en una fuente estática que las rutas ordinarias de embebido, subsetting y extracción manejan sin saber nada sobre variaciones. Se cubren tanto los contornos TrueType como los contornos CFF2
La situación que esto resuelve es común e irritante. La tipografía de una marca se define en un sistema de diseño, el sistema de diseño distribuye archivos WOFF2 variables, y el generador de PDF no puede leer ni el contenedor ni los datos de variación. La solución alternativa habitual — pedirle a la fundidora instancias TTF estáticas de cada peso realmente usado — toma semanas y produce archivos que nadie mantiene sincronizados
¿Por qué WOFF2 no es simplemente una fuente comprimida?
Tratar WOFF2 como un simple envoltorio Brotli y descomprimirlo le da bytes que no son un sfnt válido. El formato también permite tablas transformadas y reordenadas: glyf, loca y hmtx se pueden almacenar en una forma reorganizada que hay que revertir antes de que la fuente se pueda analizar. Un descompresor de propósito general no puede hacer eso
En Windows, la plataforma ofrece análisis y desempaquetado de contenedores desde Windows 10 versión 1703, así que la biblioteca usa la implementación del sistema en lugar de distribuir otro decodificador. Esa decisión mantiene el despliegue libre de una DLL adicional, y significa que la reversión de la transformación la maneja el mismo código que usa la propia pila de texto del sistema operativo. En otras plataformas, WOFF1 todavía recorre una ruta en Pascal puro, y WOFF2 se reporta explícitamente como no compatible, que es una respuesta honesta en lugar de una fuente corrompida en silencio
La ruta de importación valida antes de decodificar y después: el encabezado de 48 bytes, la longitud del contenedor, los campos reservados, el tamaño de sfnt declarado y un límite máximo de 512 MiB se verifican todos primero, y el tamaño del flujo decodificado se compara contra la declaración después. Una fuente web es contenido descargado, y los dos modos de falla que vale la pena bloquear son una discrepancia de tamaño y una bomba de descompresión
uses
PDFlibrary;
var
Lib: TPDFlib;
FontID, InstanceID: Integer;
AxesJSON: WideString;
begin
Lib := TPDFlib.Create;
try
Lib.NewDocument;
Lib.SetPageSize('A4');
Lib.NewPage;
// WOFF2, WOFF1, TTF, OTF y TTC entran todos por la misma llamada
FontID := Lib.AddOpenTypeFontFromFile('Inter.woff2', 0);
if FontID <= 0 then
raise Exception.Create('font could not be imported');
// ¿Qué puede hacer realmente esta fuente?
if Lib.GetFontVariationAxes(FontID, AxesJSON) = 1 then
Log(AxesJSON); // etiquetas, mínimo, valor por defecto, máximo por eje
// Fijar una ubicación de diseño en una fuente estática
InstanceID := Lib.CreateOpenTypeFontVariation(FontID,
'wght=620,wdth=87.5');
Lib.DrawOpenTypeText(56, 760, InstanceID, 24, 'Quarterly results', 0);
Lib.SaveToFile('report.pdf');
finally
Lib.Free;
end;
end;
Qué tiene que reconstruir la instanciación, y qué elimina
Una fuente variable almacena un contorno por defecto por glifo, más deltas indexados por posición dentro del espacio de diseño. Producir una instancia estática significa interpolar esos deltas en las coordenadas solicitadas, mover los puntos, y luego recalcular todo lo que se deriva de ellos
Para los contornos TrueType eso significa analizar fvar, avar, gvar, HVAR y MVAR, aplicar deltas de tupla con numeración de puntos compartida y privada, e interpolar los puntos que no llevan un delta explícito. Los puntos fantasma horizontales junto con HVAR determinan el ancho de avance y el side bearing izquierdo. Los glifos compuestos necesitan que se resuelvan primero sus dependencias de componentes y sus mapeos de puntos, y luego se aplican la traslación, la escala, la matriz y los desplazamientos de anclaje — con límites de profundidad y detección de ciclos, porque de lo contrario un grafo de componentes malicioso recursaría sin fin
Los metadatos también se mueven, y esta es la parte que la mayoría de las implementaciones se salta. MVAR actualiza las métricas globales en hhea, OS/2 y post, de modo que el interlineado, la posición de subrayado y el ángulo itálico coincidan con la instancia y no con el valor por defecto. Los ejes wght, wdth, ital y slnt además actualizan la clase de peso, la clase de ancho, las banderas de selección, macStyle y el ángulo itálico, de modo que una herramienta que inspeccione la fuente estática reporte el estilo que usted pidió. Una fuente cuya geometría es negrita y cuyos metadatos dicen regular quedará mal etiquetada en todo lo que venga después
La salida luego aplana los contornos compuestos y elimina las tablas que ya no aplican: las tablas de variación, los hints, las tablas de dispositivo y cualquier firma. Los límites de glifo, las métricas de resumen de hhea, los topes de hints de maxp, los checksums por tabla y el checksum de la fuente completa se recalculan todos
CFF2 toma una ruta distinta hacia el mismo destino
CFF2 no se puede embeber directamente como un programa de fuente PDF: sus charstrings no llevan anchos, y depende de las tablas OpenType que lo rodean para el orden de glifos y las métricas. Así que instanciar una fuente CFF2 produce en su lugar una fuente OpenType CFF con clave CID, que las rutas existentes de dibujo, embebido y subsetting ya manejan
Cargar una fuente CFF2 produce de inmediato una instancia estática de coordenadas por defecto para el documento actual, mientras que el sfnt CFF2 original se conserva en el objeto de fuente para que la enumeración de ejes y la instanciación posterior sigan viendo todo el espacio de diseño. Cada glifo se de-subrutiniza individualmente durante la instanciación, lo cual elimina por completo la aritmética de variación y permite que el subsetter existente funcione sin cambios. La contrapartida es que una instancia estática puede ser ligeramente más grande que una que conservó subrutinas compartidas — un precio justo por no necesitar una segunda implementación de subsetting
Verificar una instancia antes de confiar en ella
La instanciación es el tipo de función que puede estar sutilmente mal de formas que ninguna excepción reporta. Tres verificaciones capturan casi todo
Exporte la instancia y ábrala en una herramienta de fuentes. SaveFontToFile escribe el programa de fuente conservado, de modo que una instancia en memoria se puede volcar sin producir antes un PDF. El archivo exportado debería analizarse sin problemas, no contener tablas de variación, y reportar los metadatos de estilo que usted solicitó
Compare los avances en una ubicación distinta a la predeterminada. Si su pipeline puede producir la misma instancia de otra manera, compare los anchos de avance y los límites de curva glifo por glifo; la implementación de referencia aquí coincidió con una instanciación independiente de una fuente de dos ejes y 313 glifos, con una diferencia máxima de cero en cada glifo
Y extraiga de vuelta el texto del PDF generado. Una instancia que se embebe pero no produce texto extraíble ha perdido su mapeo ToUnicode, algo que importa para la búsqueda y la accesibilidad mucho después de que la verificación visual pasara. La ruta de extracción más amplia se cubre en extracción de texto, imágenes y fuentes
// Volcar una instancia generada para inspección o reutilización
Lib.SaveFontToFile('Inter-620-87.ttf');
// Si se piden coordenadas fuera del rango declarado, se recortan (clamp),
// así que lea el JSON de ejes en lugar de asumir los límites de una fuente
// {"axes":[{"tag":"wght","min":100,"default":400,"max":900}, ...]}
Notas prácticas para un pipeline de fuentes de marca
Guarde en caché las instancias por conjunto de coordenadas. Cada conjunto distinto de coordenadas es un programa de fuente distinto, así que un documento que mezcla el peso 400 y el peso 620 embebe dos subconjuntos en lugar de una sola familia compartida. Generar la misma instancia repetidamente es trabajo desperdiciado; generar docenas de pesos casi idénticos es tamaño de archivo desperdiciado
Mida con la instancia con la que va a dibujar. Una instancia condensada tiene avances más angostos que la instancia por defecto, así que cualquier diseño que midió antes de instanciar midió los anchos equivocados. Esta es, por sí sola, la causa más común de texto que se desborda de su cuadro solo en producción
Mantenga una ruta de reserva para los glifos que le falten a su fuente de marca. Una fuente de marca variable típicamente cubre el latino y poco más, y un informe que contenga un nombre de cliente en cirílico mostrará glifos faltantes si no hay reserva; el mecanismo de reserva automática se describe en reserva automática de fuentes para glifos faltantes. Y cuando el objetivo es un archivo más pequeño y no un peso específico, el subsetting hace más trabajo que la instanciación, como se cubre en optimización del tamaño de archivo PDF y subsetting de fuentes
La importación de fuentes web, la instanciación variable, el subsetting y la reserva son parte del mismo pipeline de fuentes para Delphi, C++Builder y Free Pascal; la lista completa de funciones está en la página de PDF Library for Delphi