Ir al contenido

Fuentes personalizadas: el contrato de extensión FontRegistry

FontRegistryInterface es un contrato vigente durante todo el ciclo de vida del proceso para registrar y buscar fuentes. Permite registrar fuentes desde una ruta de archivo, un directorio o datos binarios sin procesar y, a continuación, bloquear el registro para que los workers de producción no puedan mutarlo.

Ventana de terminal
composer require nextpdf/core:^3

El registro de fuentes es un singleton que persiste más allá de las instancias individuales de Document. Solo contiene datos PHP puros, sin manejadores de recursos ni objetos de extensión. Eso permite compartirlo con seguridad entre solicitudes en un worker de larga duración.

El contrato admite tres vías de registro:

  • Desde un archivo. register() analiza un archivo .ttf, .otf, .ttc o .pfb y devuelve los metadatos analizados. En una colección TrueType, se pasa el índice de la subfuente.
  • Desde un directorio. addFontDirectory() añade una ruta de búsqueda que el motor explora al resolver una familia por su nombre.
  • Desde datos binarios. registerFromBinary() analiza bytes TrueType u OpenType sin procesar. Esta es la vía que utiliza el puente @font-face para las fuentes obtenidas desde URI data: o fuentes remotas.

Para repartir la latencia de la primera solicitud, llamar a warmup() durante el arranque del worker permite preanalizar un lote de fuentes. Después, llamar a lock(). Tras lock(), cada método de mutación lanza LogicException. Esos métodos son register(), addFontDirectory(), warmup(), registerBase14() y registerFromBinary(). Los métodos de búsqueda siguen disponibles: get(), has(), all() y getSearchDirectories(). Este bloqueo es el mecanismo de seguridad de producción. Garantiza que ninguna solicitud pueda alterar el conjunto de fuentes compartido.

En la mayoría de los casos no se implementa FontRegistryInterface. El motor proporciona la implementación y el código de extensión la invoca. se implementa cuando se necesita una estrategia de resolución de fuentes personalizada, por ejemplo, una respaldada por un almacén direccionado por contenido. En ambos casos, el contrato es la frontera.

NextPDF\Contracts\FontRegistryInterface (estable, desde 1.7.0):

MétodoDevuelvePropósito
register(string $fontFile, string $alias, int $fontIndex)FontInfoAnaliza y registra un archivo de fuente. Lanza una excepción si el registro está bloqueado o el archivo no se puede analizar.
registerFromBinary(string $fontData, string $alias)FontInfoRegistra una fuente a partir de bytes TrueType u OpenType sin procesar.
registerBase14(string $key, FontInfo $font)voidRegistra una fuente estándar Base 14 predefinida.
addFontDirectory(string $directory)voidAñade un directorio de búsqueda de fuentes.
warmup(array $fontFiles)voidAnaliza por adelantado un lote de fuentes en el arranque del worker.
lock()voidCongela el registro frente a cualquier mutación posterior.
isLocked()boolIndica si el registro está bloqueado.
get(string $family, string $style)FontInfo | nullBusca una fuente por familia y estilo.
has(string $key)boolComprueba si existe una clave de registro.
all()array<string, FontInfo>Devuelve todas las fuentes registradas.
getSearchDirectories()list<string>Devuelve los directorios de búsqueda en orden.
memoryUsage()MemoryReportInforma sobre el uso de memoria actual del registro.
<?php
declare(strict_types=1);
use NextPDF\Contracts\FontRegistryInterface;
/** @var FontRegistryInterface $fonts */
$info = $fonts->register('/srv/fonts/Inter-Regular.ttf', 'Inter');
if (!$fonts->has('inter')) {
throw new RuntimeException('Inter failed to register');
}

Esta rutina de arranque del worker precalienta un conjunto de fuentes, bloquea el registro y observa cada carga para el seguimiento de licencias. Cada tipo que utiliza es público.

<?php
declare(strict_types=1);
use NextPDF\Contracts\FontRegistryInterface;
use NextPDF\Event\Content\FontLoadedEvent;
use NextPDF\Event\EventDispatcher;
use NextPDF\Event\ListenerProvider;
use Psr\Log\LoggerInterface;
final class FontWarmup
{
/** @param list<string> $fontFiles */
public function __construct(
private readonly FontRegistryInterface $fonts,
private readonly LoggerInterface $logger,
private readonly array $fontFiles,
) {}
public function boot(): EventDispatcher
{
$listeners = new ListenerProvider();
$listeners->addListener(
FontLoadedEvent::class,
function (FontLoadedEvent $event): void {
$this->logger->info('font.loaded', [
'family' => $event->family,
'style' => $event->style,
'type' => $event->fontType->name,
]);
},
);
if (!$this->fonts->isLocked()) {
$this->fonts->warmup($this->fontFiles);
$this->fonts->lock();
}
return new EventDispatcher($listeners);
}
}
  • Registro bloqueado. Cualquier mutación posterior a lock() lanza LogicException. Comprobar siempre isLocked() antes de un precalentamiento condicional en un worker reciclado.
  • El registro binario no se almacena en caché por clave. registerFromBinary() escribe en un archivo temporal y lo analiza. Tratar el FontInfo devuelto como el manejador.
  • Índice de TTC. Para una colección TrueType, el tercer argumento de register() selecciona la subfuente. El valor predeterminado 0 selecciona la primera cara.
  • Resolución de familia. get() devuelve null para un par de familia y estilo desconocido. No asumir nunca un resultado no nulo.

warmup() traslada el costo del análisis de la primera solicitud al arranque. Los métodos del registro trabajan sobre datos PHP puros. Las búsquedas son lecturas de un mapa en tiempo constante. Llamar a memoryUsage() permite dimensionar el conjunto de fuentes residente de un worker respecto del presupuesto de memoria.

Una fuente registrada se convierte en contenido PDF incrustable. Validar la procedencia de la fuente antes del registro. No registrar datos binarios controlados por un atacante sin comprobaciones de tamaño y formato. El hook FontLoadedEvent es el punto admitido para hacer cumplir las licencias de fuentes y registrar qué caras incrusta un documento.

No se aplican afirmaciones normativas de firma ni de archivado. La incrustación y el subconjunto de fuentes se ajustan al modelo de fuentes de PDF 2.0; esa conformidad corresponde al subconjuntador interno, no a este contrato.

NextPDF Enterprise añade la atestación de licencias de fuentes y una política de subconjunto auditada sobre la misma FontRegistryInterface. El código de registro no cambia entre ediciones porque el contrato es la frontera.

El glosario define registro de fuentes, registro de imágenes y escucha de eventos; consultar el glosario publicado para cada definición canónica.