Pular para o conteúdo

Fontes personalizadas: contrato de extensão FontRegistry

FontRegistryInterface define o contrato com ciclo de vida do processo para registrar e localizar fontes. Registre fontes a partir de um caminho de arquivo, de um diretório ou de dados binários brutos e, em seguida, bloqueie o registro para que os workers de produção não possam alterá-lo.

Terminal window
composer require nextpdf/core:^3

O registro de fontes é um singleton que persiste além de cada instância de Document. Ele armazena apenas dados PHP puros, sem handles de recursos nem objetos de extensão, então você pode compartilhá-lo entre requisições em um worker de longa duração.

Use uma das três formas de registro:

  • A partir de um arquivo. O register() analisa um arquivo .ttf, .otf, .ttc ou .pfb e retorna os metadados. Para uma TrueType Collection, passe o índice da subfonte.
  • A partir de um diretório. O addFontDirectory() adiciona um caminho de busca que o engine percorre ao resolver uma família pelo nome.
  • A partir de dados binários. O registerFromBinary() analisa bytes TrueType ou OpenType brutos. Use essa opção para a ponte @font-face quando as fontes vêm de um Uniform Resource Identifier (URI) data: ou de uma origem remota.

Para reduzir a latência da primeira requisição, chame warmup() no boot do worker para pré-analisar um lote de fontes. Em seguida, chame lock(). Após lock(), qualquer método de mutação lança LogicException: register(), addFontDirectory(), warmup(), registerBase14() e registerFromBinary(). Os métodos de consulta continuam disponíveis: get(), has(), all() e getSearchDirectories(). Esse bloqueio protege os workers de produção ao garantir que nenhuma requisição possa alterar o conjunto de fontes compartilhado.

Na maioria dos casos, você não implementa FontRegistryInterface. O engine fornece a implementação, e você a chama. Implemente-a apenas quando precisar de uma estratégia personalizada de resolução de fontes, como uma baseada em armazenamento endereçável por conteúdo. Em ambos os casos, o contrato continua sendo a fronteira.

NextPDF\Contracts\FontRegistryInterface (estável, desde 1.7.0):

MétodoRetornaFinalidade
register(string $fontFile, string $alias, int $fontIndex)FontInfoAnalisa e registra um arquivo de fonte. Lança exceção quando o registro está bloqueado ou quando o arquivo não pode ser analisado.
registerFromBinary(string $fontData, string $alias)FontInfoRegistra uma fonte a partir de bytes TrueType ou OpenType brutos.
registerBase14(string $key, FontInfo $font)voidRegistra uma fonte padrão Base 14 pré-construída.
addFontDirectory(string $directory)voidAdiciona um diretório de busca de fontes.
warmup(array $fontFiles)voidPré-analisa um lote de fontes no boot do worker.
lock()voidCongela o registro para impedir mutações posteriores.
isLocked()boolIndica se o registro está bloqueado.
get(string $family, string $style)FontInfo | nullBusca uma fonte por família e estilo.
has(string $key)boolVerifica se uma chave de registro existe.
all()array<string, FontInfo>Retorna todas as fontes registradas.
getSearchDirectories()list<string>Retorna os diretórios de busca em ordem.
memoryUsage()MemoryReportInforma o uso atual de memória do 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');
}

No boot do worker, aqueça o conjunto de fontes, bloqueie o registro e observe cada carregamento para rastreabilidade de licenças. Este exemplo usa apenas tipos públicos.

<?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. Após lock(), qualquer mutação lança LogicException. Verifique isLocked() antes de um warmup condicional em um worker reciclado.
  • O registro binário não fica em cache por chave. O registerFromBinary() grava os dados em um arquivo temporário e os analisa. Use o FontInfo retornado como handle.
  • Índice de TrueType Collection (TTC). Para uma TrueType Collection, o terceiro argumento de register() seleciona a subfonte. O valor padrão 0 seleciona a primeira face.
  • Resolução de família. O get() retorna null para um par de família e estilo desconhecido. Nunca presuma um resultado não nulo.

warmup() transfere o custo de análise da primeira requisição para o boot do worker. Os métodos do registro usam dados PHP puros, e as consultas são leituras de mapa em tempo constante. Chame memoryUsage() para dimensionar o conjunto de fontes residente no worker em relação ao seu orçamento de memória.

Uma fonte registrada pode ser incorporada em conteúdo Portable Document Format (PDF). Valide a procedência da fonte antes do registro. Não registre dados binários controlados por um atacante sem verificações de tamanho e formato. Use o hook FontLoadedEvent para impor a conformidade com o licenciamento de fontes e registrar quais faces um documento incorpora.

Nenhuma afirmação normativa de assinatura ou de arquivamento se aplica. A incorporação e o subsetting de fontes estão em conformidade com o modelo de fontes do PDF 2.0. O subsetter interno detém essa conformidade; este contrato não.

NextPDF Enterprise adiciona atestação de licenças de fontes e uma política de subsetting auditada sobre o mesmo FontRegistryInterface. Seu código de registro funciona da mesma forma em todas as edições porque o contrato é a fronteira.

O glossário define registro de fontes, registro de imagens e listener de eventos; consulte o glossário publicado para as definições canônicas.