HotPDF puede conservar subconjuntos de fuentes TrueType y OpenType en disco y reutilizarlos entre documentos y entre ejecuciones del proceso, así un lote que rindeere diez mil estados de cuenta con las mismas tres fuentes subdivide esas fuentes una sola vez en vez de diez mil. La caché se configura con dos propiedades, se inspecciona con un registro, y es segura dejarla activa: una falla de la caché recae al subsetting normal en memoria y nunca detiene la producción de un documento
El subsetting es costoso por una razón. Construir un subconjunto significa recorrer el cierre de glifos, reescribir loca y glyf, reconstruir cmap y hmtx, y emitir un mapeo CID que el PDF pueda direccionar. Para un documento ese costo se pierde en el ruido. Para un servidor de reportes que produce documentos en un bucle, suele ser el mayor bloque individual de tiempo de CPU en la ejecución
Qué hace posible un acierto de caché
Cuatro cosas deben coincidir: el contenido de la fuente, el conjunto de glifos usados, el modo de subsetting, y el esquema de la caché. Falla cualquiera y HotPDF subdivide desde cero, porque un subconjunto solo es reutilizable cuando habría sido idéntico byte a byte de todos modos
El conjunto de glifos es la condición que sorprende a la gente. Dos facturas que difieren en un solo nombre de cliente usan conjuntos de glifos distintos, y por lo tanto producen subconjuntos distintos y entradas de caché distintas. La caché rinde cuando los documentos comparten un repertorio de glifos — estados de cuenta desde una plantilla fija, formularios cuyos datos variables son numéricos, catálogos dibujados a partir de una base de productos — y no rinde nada cuando cada documento dibuja una porción distinta de una fuente CJK grande. Mide antes de asumir en qué caso estás
var
Pdf: THotPDF;
Info: THPDFFontSubsetCacheInfo;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.EnableFontSubsetting := True;
Pdf.FontSubsetCacheFolder := 'C:\ProgramData\Reports\fontcache';
Pdf.FontSubsetCacheMaxBytes := 64 * 1024 * 1024; // 64 MiB, default is 256
// ... generate the batch ...
Info := Pdf.GetFontSubsetCacheInfo;
LogFmt('subset cache: %d hits, %d misses, %d bytes in %d files',
[Info.HitCount, Info.MissCount, Info.CurrentBytes, Info.FileCount]);
finally
Pdf.Free;
end;
end;
¿Cómo sabes que la caché está haciendo algo?
GetFontSubsetCacheInfo devuelve nueve contadores, y la razón entre los dos primeros responde la pregunta directamente. HitCount y MissCount dan la tasa de aciertos. WriteCount y EvictionCount muestran si las entradas sobreviven lo suficiente para reutilizarse o están siendo empujadas afuera por un presupuesto demasiado pequeño. CurrentBytes y FileCount reportan lo que hay en disco ahora mismo
Los tres restantes son los que vale la pena monitorear. CorruptCount cuenta entradas que fallaron la validación y fueron eliminadas — unas cuantas tras un cierre sucio son normales, un flujo constante significa que el almacenamiento no es confiable. RejectedCount cuenta entradas rechazadas antes de usarse. WriteFailureCount cuenta entradas que no pudieron escribirse en absoluto, lo cual suele significar un problema de permisos en la carpeta en vez de algo sobre fuentes. Ninguno de estos tres detiene la generación de documentos, que es justamente por qué tienes que mirarlos: una caché que silenciosamente nunca escribe se ve igual desde afuera que una caché que funciona, salvo por la factura de CPU
Expulsión, presupuestos y el momento en que reduces uno
FontSubsetCacheMaxBytes por defecto son 268435456 bytes, es decir 256 MiB, y se puede bajar en tiempo de ejecución. Bajarlo dispara expulsión inmediata por el usado menos recientemente (LRU) en vez de esperar a la próxima escritura, así que un servicio que reacciona a la presión de disco puede liberar espacio en el momento en que decide hacerlo, no en un punto posterior que no controla
Poner FontSubsetCacheFolder en una cadena vacía deshabilita el nivel en disco sin borrar nada ya almacenado, y sin cambiar un solo byte de la salida de fuentes. Esa es la propiedad a la que recurrir cuando quieres aislar la caché durante el diagnóstico: apágala, ejecuta el mismo lote, y compara los PDF producidos. Deberían ser idénticos, porque la caché almacena un resultado, no una política
Qué hace la caché cuando una entrada está dañada
La elimina y subdivide normalmente. Las entradas malformadas o truncadas se rechazan antes de que el subconjunto pueda alcanzar un flujo PDF, que es la parte del diseño que más importa: una entrada de caché corrupta que llegara a un documento produciría un PDF con un programa de fuente roto, y ese fallo aparecería lejos de su causa — en un visor, en la máquina de un cliente, semanas después
Las escrituras son atómicas, así que un lector nunca observa una entrada a medio escribir, y un cuelgue a mitad de escritura deja la caché consistente en vez de envenenada. Las entradas compactas de subconjunto conservan los datos de remapeo CID que exigen los diccionarios de fuente PDF/A, así que un subconjunto cacheado sigue siendo un subconjunto conforme — la salida de archivo no tiene que evitar la caché para mantenerse válida
// Reset the disk tier after a font upgrade or a schema change
Pdf.ClearFontSubsetCache;
// Or move it somewhere writable and let the budget apply immediately
Pdf.SetFontSubsetCacheFolder('D:\cache\fonts');
Dónde poner la carpeta en un despliegue real
Tres propiedades deciden esto: la carpeta debe ser escribible por la cuenta con la que corre el servicio, debe estar en almacenamiento local en vez de en un recurso compartido de red, y no debe estar dentro de un directorio que un paso de despliegue borre. Una caché en un recurso compartido convierte cada fallo en un viaje de ida y vuelta y cada acierto en dos; una caché bajo una carpeta de aplicación que el instalador recrea es una caché que arranca fría después de cada actualización
Para servicios con múltiples instancias, da a cada instancia su propia carpeta salvo que hayas confirmado que el almacenamiento maneja el reemplazo atómico concurrente como esperas. El costo de una entrada duplicada es una pasada de subsetting extra; el costo de depurar una carrera sobre caché compartida es una tarde entera
Cuándo recurrir a otra cosa
La caché reduce trabajo repetido. No reduce el trabajo del primer documento, y no ayuda a una carga cuyos conjuntos de glifos nunca se repiten. Si tu salida está dominada por una fuente CJK enorme usada sobre texto impredecible, la palanca más efectiva es el cierre de subsetting mismo — qué glifos se incorporan, y por qué — cubierto en las notas sobre el cierre de subsetting de fuentes y el modelado de glifos. Si tu lote es lento por razones que resultan no ser fuentes en absoluto, el recorrido por la salida de reportes con fuentes e imágenes muestra adónde suele ir el otro tiempo, y el caso de estudio sobre el bug de orden de subsetting de fuentes en EndDoc es un recordatorio de que la corrección del subsetting y la velocidad del subsetting son problemas separados
HotPDF es un componente VCL PDF nativo para Delphi y C++Builder, y la caché de subconjuntos es parte de la biblioteca en vez de un servicio adicional, así que un servidor de reportes la obtiene con solo fijar una ruta de carpeta — consulta la página del componente HotPDF para la lista completa de características de fuentes y rendimiento