Polices personnalisées : le contrat d’extension FontRegistry
FontRegistryInterface est un contrat dont la durée de vie suit celle du processus, destiné à enregistrer et rechercher des polices. Enregistre une police à partir d’un chemin de fichier, d’un répertoire ou de données binaires brutes, puis verrouille le registre afin que les workers de production ne puissent plus le modifier.
Installation
Section intitulée « Installation »composer require nextpdf/core:^3Vue d’ensemble conceptuelle
Section intitulée « Vue d’ensemble conceptuelle »Le registre des polices est un singleton qui survit aux instances individuelles de Document. Il ne contient que des données PHP pures, sans ressource ni objet d’extension. Il peut donc être partagé sans risque entre plusieurs requêtes dans un worker à exécution longue.
Le contrat prend en charge trois modes d’enregistrement :
- Depuis un fichier.
register()analyse un fichier.ttf,.otf,.ttcou.pfbet renvoie les métadonnées analysées. Pour une TrueType Collection, indique l’index de la sous-police. - Depuis un répertoire.
addFontDirectory()ajoute un chemin de recherche que le moteur parcourt lorsqu’il résout une famille par son nom. - Depuis des données binaires.
registerFromBinary()analyse des octets TrueType ou OpenType bruts. C’est le chemin utilisé par le pont@font-facepour les polices récupérées depuis des URIdata:ou des sources distantes.
Pour amortir la latence de la première requête, appelle warmup() au démarrage du worker afin de pré-analyser un lot de polices. Appelle ensuite lock(). Après lock(), chaque méthode de mutation lève une LogicException. Ces méthodes sont register(), addFontDirectory(), warmup(), registerBase14() et registerFromBinary(). Les méthodes de recherche restent disponibles : get(), has(), all() et getSearchDirectories(). Ce verrou constitue le mécanisme de sécurité en production. Il garantit qu’aucune requête ne peut modifier l’ensemble de polices partagé.
Dans la plupart des cas, tu n’as pas à implémenter FontRegistryInterface. Le moteur fournit l’implémentation et tu l’appelles. Tu ne l’implémentes réellement que lorsque tu as besoin d’une stratégie de résolution de polices personnalisée, par exemple adossée à un magasin adressé par contenu. Dans les deux cas, le contrat reste la frontière.
Surface de l’API
Section intitulée « Surface de l’API »NextPDF\Contracts\FontRegistryInterface (stable, depuis la 1.7.0) :
| Méthode | Renvoie | Rôle |
|---|---|---|
register(string $fontFile, string $alias, int $fontIndex) | FontInfo | Analyse et enregistre un fichier de police. Lève une exception si le registre est verrouillé ou si le fichier ne peut pas être analysé. |
registerFromBinary(string $fontData, string $alias) | FontInfo | Enregistre une police à partir d’octets TrueType ou OpenType bruts. |
registerBase14(string $key, FontInfo $font) | void | Enregistre une police standard Base 14 préconstruite. |
addFontDirectory(string $directory) | void | Ajoute un répertoire de recherche de polices. |
warmup(array $fontFiles) | void | Pré-analyse un lot de polices au démarrage du worker. |
lock() | void | Fige le registre pour empêcher toute mutation ultérieure. |
isLocked() | bool | Indique si le registre est verrouillé. |
get(string $family, string $style) | FontInfo | null | Recherche une police par famille et style. |
has(string $key) | bool | Vérifie si une clé d’enregistrement existe. |
all() | array<string, FontInfo> | Renvoie toutes les polices enregistrées. |
getSearchDirectories() | list<string> | Renvoie les répertoires de recherche dans l’ordre. |
memoryUsage() | MemoryReport | Indique la consommation mémoire actuelle du registre. |
Exemple de code — Démarrage rapide
Section intitulée « Exemple de code — Démarrage rapide »<?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');}Exemple de code — Production
Section intitulée « Exemple de code — Production »Cette routine de démarrage d’un worker préchauffe un ensemble de polices, verrouille le registre et observe chaque chargement pour le suivi des licences. Tous les types qu’elle utilise sont publics.
<?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); }}Cas limites et pièges
Section intitulée « Cas limites et pièges »- Registre verrouillé. Toute mutation après
lock()lève uneLogicException. Vérifie toujoursisLocked()avant un préchauffage conditionnel dans un worker recyclé. - L’enregistrement binaire n’est pas mis en cache par clé.
registerFromBinary()écrit dans un fichier temporaire et l’analyse. Traite leFontInforenvoyé comme le handle. - Index TTC. Pour une TrueType Collection, le troisième argument de
register()sélectionne la sous-police. La valeur par défaut0sélectionne la première face. - Résolution de famille.
get()renvoienullpour un couple famille/style inconnu. Ne suppose jamais un résultat non nul.
Performances
Section intitulée « Performances »warmup() déplace le coût d’analyse de la première requête vers le démarrage. Les méthodes du registre travaillent sur des données PHP pures. Les recherches sont des lectures de map en temps constant. Appelle memoryUsage() pour dimensionner l’ensemble de polices résidant dans un worker par rapport à ton budget mémoire.
Notes de sécurité
Section intitulée « Notes de sécurité »Une police enregistrée devient du contenu PDF intégrable. Valide la provenance de la police avant l’enregistrement. N’enregistre pas de données binaires contrôlées par un attaquant sans contrôle de taille et de format. Le hook FontLoadedEvent est le point pris en charge pour faire respecter la conformité de licence des polices et enregistrer les faces intégrées par un document.
Conformité
Section intitulée « Conformité »Aucune revendication normative de signature ou d’archivage ne s’applique. L’intégration et le sous-ensemblage des polices se conforment au modèle de polices PDF 2.0 ; cette conformité relève du sous-ensembleur interne, pas de ce contrat.
Contexte commercial
Section intitulée « Contexte commercial »NextPDF Enterprise ajoute l’attestation de licence de police et une politique de sous-ensemblage auditée par-dessus la même FontRegistryInterface. Ton code d’enregistrement reste inchangé d’une édition à l’autre, parce que le contrat est la frontière.
Voir aussi
Section intitulée « Voir aussi »- Présentation de la création d’extensions
- Déclencheurs d’action et écouteurs d’événements
- Mise en page personnalisée et interception de texte
- Règles de stabilité du SPI
Contrats et modules liés
Section intitulée « Contrats et modules liés »- Référence du module Font — l’implémentation du registre, l’analyse et les mécanismes internes du sous-ensemblage.
- Référence des contrats de typographie — où
FontRegistryInterfaceest référencé. - Déclencheurs d’action et écouteurs d’événements —
FontLoadedEventet le dispatcher. - Mise en page personnalisée et interception de texte — le contrat de stratégie équivalent au moment du rendu.
- Règles de stabilité du SPI — la promesse d’interface portée par
FontRegistryInterface.
Le glossaire définit registre de polices, registre d’images et écouteur d’événements ; consulte le glossaire publié pour chaque définition canonique.