コンテンツにスキップ

カスタムフォント: FontRegistry 拡張コントラクト

FontRegistryInterface は、フォントの登録と参照を行うための、プロセスの存続期間全体で有効なコントラクトです。ファイルパス、ディレクトリ、または生のバイナリデータからフォントを登録し、その後レジストリをロックして、本番ワーカーによる変更を防ぎます。

Terminal window
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 を実装する必要はありません。実装はエンジンが提供するため、通常はそれを呼び出すだけです。実装するのは、たとえばコンテンツアドレス指定ストアを基盤とするような、カスタムのフォント解決戦略が必要な場合です。いずれの場合も、コントラクトが境界となります。

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 の上に、フォントライセンスの証明と監査済みのサブセット化ポリシーを重ねます。コントラクトが境界となるため、登録コードはエディション間で変更されません。

用語集では font registryimage registryevent listener を定義しています。それぞれの正式な定義については、公開されている用語集を参照してください。