カスタムフォント: FontRegistry 拡張コントラクト
FontRegistryInterface は、フォントの登録と参照を行うための、プロセスの存続期間全体で有効なコントラクトです。ファイルパス、ディレクトリ、または生のバイナリデータからフォントを登録し、その後レジストリをロックして、本番ワーカーによる変更を防ぎます。
インストール
「インストール」という見出しのセクションcomposer require nextpdf/core:^3概念の概要
「概念の概要」という見出しのセクションフォントレジストリは、個々の Document インスタンスよりも長く存続するシングルトンです。純粋な PHP データだけを保持し、リソースハンドルや拡張オブジェクトは保持しません。そのため、長時間稼働するワーカーでリクエスト間に共有しても安全です。
このコントラクトは 3 つの登録パスをサポートします。
- ファイルから。
register()は.ttf、.otf、.ttc、または.pfbファイルを解析し、解析後のメタデータを返します。TrueType Collection の場合は、サブフォントのインデックスを渡します。 - ディレクトリから。
addFontDirectory()は、名前からファミリーを解決する際にエンジンがスキャンする検索パスを追加します。 - バイナリデータから。
registerFromBinary()は、生の TrueType または OpenType のバイト列を解析します。これは、@font-faceブリッジがdata:URI やリモートソースから取得したフォントに使用するパスです。
初回リクエストのレイテンシを分散させるには、ワーカー起動時に 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 | フォントファイルを解析して登録します。レジストリがロックされている場合や、ファイルを解析できない場合はスローします。 |
registerFromBinary(string $fontData, string $alias) | FontInfo | 生の TrueType または OpenType のバイト列からフォントを登録します。 |
registerBase14(string $key, FontInfo $font) | void | 事前に構築された Base 14 標準フォントを登録します。 |
addFontDirectory(string $directory) | void | フォント検索ディレクトリを追加します。 |
warmup(array $fontFiles) | void | ワーカー起動時にフォントのバッチを事前に解析します。 |
lock() | void | レジストリを凍結し、それ以上の変更を防ぎます。 |
isLocked() | bool | レジストリがロックされているかどうかを報告します。 |
get(string $family, string $style) | FontInfo | null | ファミリーとスタイルでフォントを参照します。 |
has(string $key) | bool | 登録キーが存在するかどうかを確認します。 |
all() | array<string, FontInfo> | 登録済みのすべてのフォントを返します。 |
getSearchDirectories() | list<string> | 検索ディレクトリを順番に返します。 |
memoryUsage() | MemoryReport | 現在のレジストリのメモリ使用量を報告します。 |
コードサンプル — クイックスタート
「コードサンプル — クイックスタート」という見出しのセクション<?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');}コードサンプル — 本番
「コードサンプル — 本番」という見出しのセクションこのワーカー起動ルーチンは、フォントセットをウォームアップし、レジストリをロックして、ライセンス追跡のために各読み込みを監視します。ここで使用している型はすべて public です。
<?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); }}エッジケースと注意点
「エッジケースと注意点」という見出しのセクション- ロックされたレジストリ。
lock()の後の変更はすべてLogicExceptionをスローします。再利用されたワーカーで条件付きウォームアップを行う前には、常にisLocked()を確認してください。 - バイナリ登録はキーによるキャッシュが行われません。
registerFromBinary()は一時ファイルに書き込んでそれを解析します。返されたFontInfoをハンドルとして扱ってください。 - TTC インデックス。 TrueType Collection の場合、
register()の 3 番目の引数でサブフォントを選択します。デフォルトの0は最初のフェイスを選択します。 - ファミリーの解決。
get()は、不明なファミリーとスタイルの組み合わせに対してnullを返します。非 null の結果が返るとは決して想定しないでください。
パフォーマンス
「パフォーマンス」という見出しのセクションwarmup() は、解析コストを初回リクエストから起動時へ移します。レジストリのメソッドは純粋な PHP データに対して動作します。参照は定数時間のマップ読み取りです。ワーカーに常駐させるフォントセットをメモリ予算と照らし合わせて見積もるには、memoryUsage() を呼び出します。
セキュリティに関する注意
「セキュリティに関する注意」という見出しのセクション登録されたフォントは、埋め込み可能な PDF コンテンツになります。登録する前に、フォントの出所を検証してください。サイズと形式のチェックを行わずに、攻撃者が制御できるバイナリデータを登録しないでください。FontLoadedEvent フックは、フォントライセンスのコンプライアンスを強制し、ドキュメントが埋め込むフェイスを記録するための、サポート済みの拡張ポイントです。
署名またはアーカイブに関する規範的な主張は適用されません。フォントの埋め込みとサブセット化は PDF 2.0 のフォントモデルに準拠しますが、その適合性はこのコントラクトではなく、内部のサブセッターが担います。
商用に関する補足
「商用に関する補足」という見出しのセクションNextPDF Enterprise は、同じ FontRegistryInterface の上に、フォントライセンスの証明と監査済みのサブセット化ポリシーを重ねます。コントラクトが境界となるため、登録コードはエディション間で変更されません。
関連するコントラクトとモジュール
「関連するコントラクトとモジュール」という見出しのセクション- フォントモジュールリファレンス — レジストリの実装、解析とサブセット化の内部。
- タイポグラフィコントラクトリファレンス —
FontRegistryInterfaceがカタログ化されている場所。 - アクショントリガーとイベントリスナー —
FontLoadedEventとディスパッチャー。 - カスタムレイアウトとテキストの傍受 — レンダリング時に使う兄弟関係の戦略コントラクト。
- SPI 安定性ルール —
FontRegistryInterfaceを支えるインターフェイスの約束事。
用語集では font registry、image registry、event listener を定義しています。それぞれの正式な定義については、公開されている用語集を参照してください。