自定义字体:FontRegistry 扩展契约
FontRegistryInterface 是一份进程生命周期级别的契约,用于注册和查找字体。你可以从文件路径、目录或原始二进制数据注册字体,随后锁定此 registry(注册表),防止生产环境的 worker 再修改它。
composer require nextpdf/core:^3概念总览
标题为“概念总览”的章节字体 registry 是一个 singleton(单例),生命周期比单个 Document 实例更长。它只保存纯 PHP 数据,不持有任何资源 handle 或扩展对象。因此,它可以在长时间运行的 worker 中安全地跨多个请求共用。
此契约支持三种注册途径:
- 从文件注册。
register()会解析.ttf、.otf、.ttc或.pfb文件,并返回解析后的元数据。对于 TrueType Collection,请传入子字体 Index(索引)。 - 从目录注册。
addFontDirectory()会新增一条搜索路径,引擎按名称 resolve(解析)字体家族时会扫描这条路径。 - 从二进制数据注册。
registerFromBinary()会解析原始 TrueType 或 OpenType 字节。这正是@font-face桥接器处理从data:URI 或远程来源获取的字体时所用的途径。
若要分摊首次请求的延迟,请在 worker 启动时调用 warmup(),预先解析一批字体。随后调用 lock()。在 lock() 之后,每个修改方法都会抛出 LogicException。这些方法包括 register()、addFontDirectory()、warmup()、registerBase14() 与 registerFromBinary()。查找方法仍可使用:get()、has()、all() 与 getSearchDirectories()。这个锁定机制是生产环境的安全防护,确保没有任何请求能修改共用的字体集合。
大多数情况下,你不需要自行实现 FontRegistryInterface。引擎已提供实现,直接调用即可。只有在需要自定义字体解析策略时,才需要自行实现它,例如以内容寻址存储作为后端的策略。在这两种情况下,契约都是边界。
API 接口
标题为“API 接口”的章节NextPDF\Contracts\FontRegistryInterface(稳定,自 1.7.0 起):
| 方法 | 返回 | 用途 |
|---|---|---|
register(string $fontFile, string $alias, int $fontIndex) | FontInfo | 解析并注册一个字体文件。若 registry 已锁定或文件无法解析,则会抛出异常。 |
registerFromBinary(string $fontData, string $alias) | FontInfo | 从原始 TrueType 或 OpenType 字节注册一个字体。 |
registerBase14(string $key, FontInfo $font) | void | 注册一个预先构建的 Base 14 标准字体。 |
addFontDirectory(string $directory) | void | 新增一个字体搜索目录。 |
warmup(array $fontFiles) | void | 在 worker 启动时预先解析一批字体。 |
lock() | void | 冻结 registry,使其无法再被修改。 |
isLocked() | bool | 报告 registry 是否已锁定。 |
get(string $family, string $style) | FontInfo | null | 按字体家族与样式查找字体。 |
has(string $key) | bool | 检查某个注册键是否存在。 |
all() | array<string, FontInfo> | 返回每一个已注册的字体。 |
getSearchDirectories() | list<string> | 按顺序返回搜索目录。 |
memoryUsage() | MemoryReport | 报告 registry 当前的内存使用量。 |
代码示例——快速上手
标题为“代码示例——快速上手”的章节<?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');}代码示例——生产环境
标题为“代码示例——生产环境”的章节这段 worker 启动例程会预热一组字体、锁定 registry,并观测每次加载,以便进行授权跟踪。它用到的每个类型都是公开的。
<?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); }}边界情况和陷阱
标题为“边界情况和陷阱”的章节- 已锁定的 registry。 在
lock()之后的任何修改都会抛出LogicException。在复用的 worker 中,执行有条件的预热前一律先检查isLocked()。 - 二进制注册不会按注册键缓存。
registerFromBinary()会写入一个临时文件再进行解析。请把返回的FontInfo视为 handle。 - TTC 索引。 对于 TrueType Collection,
register()的第三个参数用来选择子字体。默认值0会选择第一个子字体。 - 字体家族解析。 对于未知的字体家族与样式组合,
get()会返回null。切勿假设结果一定非 null。
warmup() 会把解析成本从首次请求移到启动阶段。registry 的方法都在纯 PHP 数据上运行。查找是常数时间的 map 读取。调用 memoryUsage(),根据你的内存预算评估 worker 常驻字体集合的大小。
安全性注意事项
标题为“安全性注意事项”的章节已注册的字体会成为可嵌入的 PDF 内容。注册前必须验证字体的 provenance(来源信息)。在未做大小与格式检查前,切勿注册受攻击者控制的二进制数据。FontLoadedEvent hook 是强制执行字体授权合规,并记录文件嵌入了哪些字体的官方支持切入点。
符合性
标题为“符合性”的章节没有任何签名或归档的规范性声明适用于此处。字体嵌入与子集化符合 PDF 2.0 字体模型;该符合性由内部子集化器负责,而非由此契约负责。
商业场景
标题为“商业场景”的章节NextPDF Enterprise 在同一份 FontRegistryInterface 之上,叠加了字体授权证明与经审计的子集化政策。因为契约就是边界,所以你的注册代码在各版本之间都无需修改。
另请参阅
标题为“另请参阅”的章节相关契约与模块
标题为“相关契约与模块”的章节- 字体模块参考——registry 的实现、解析与子集化内部细节。
- 排版契约参考——
FontRegistryInterface编入目录的位置。 - 动作触发器与事件监听器——
FontLoadedEvent与其分派器。 - 自定义布局与文字拦截——同层级的绘制阶段策略契约。
- SPI 稳定性规则——
FontRegistryInterface背后的接口承诺。
词汇表定义了 font registry、image registry 与 event listener;各项的标准定义请参阅已发布的词汇表。