コンテンツにスキップ

テキスト: シェーピングの継ぎ目、CJK、ラン処理

テキストモジュールは、シェーピングの継ぎ目を担います。UTF-8 のランを配置済みのグリフに変換する最小限のインターフェイス、利用可能な場合に使う実際の OpenType バックエンド、利用できない場合の決定論的なフォールバック、そしてスクリプト固有のシェーパー用レジストリで構成されます。

Terminal window
composer require nextpdf/core:^3

ShaperInterface は、テキストレイアウトパイプラインと OpenType シェーピングエンジンの間にある継ぎ目です。意図的に最小限に設計されており、shape() メソッドを 1 つだけ持ち、ShaperInput を受け取って ShapingResult を返します。戻り値の型は、コンシューマーから見える唯一の出力です。実装はシェーピングエンジンの内部を漏らしてはならず、型付けされた戻り値がその構造を強制します。ShapingResult は、GlyphRun レコードのリスト、そのまま返されたソーステキスト、スクリプトと方向、および結果を生成したバックエンドを示す shaperImpl タグを保持します。

バックエンドの選択は明示的で、機能を偽りません。ShaperFactory は機能プローブを 1 回実行します。ホスト上で動作する HarfBuzz バインディングがあれば、create() は HarfBuzz バックエンドのシェーパーを返します。それ以外の場合は NullShaper を返します。NullShaper はパススルーのフォールバックです。Unicode コードポイントごとに 1 つの合成グリフを生成し、アドバンスとオフセットを 0 にします。可観測性によってフォールバックを検出できるよう、結果にタグを付けます。アドバンスの解決はフォントメトリクスモジュールに委ねます。これは文書化された劣化パスであり、完全なシェーピングではありません。置換、リガチャ、マーク配置、文脈依存形には実際のバックエンドが必要です。wouldUseRealShaper() は診断用の述語です。本番コードでは、代わりに結果の shaperImpl タグで分岐すべきです。

スクリプト固有のシェーピングは SPI であり、同梱実装ではありません。ScriptShaperRegistry は PSR-11 スタイルのレジストリで、ISO 15924 スクリプトタグに基づいて MongolianShaperInterface または TibetanShaperInterface を解決します。レジストリは大文字小文字を区別しない形でキーを格納し、スクリプトコードの許容性の判定を単一の信頼できる情報源に委ねます。レジストリとスクリプトシェーパーインターフェイスは凍結された契約であり、拡張機能は呼び出し箇所に手を加えることなく Phase-12 プロバイダーを登録できます。エンジンは継ぎ目を提供し、複雑なスクリプト用のプロバイダーはコンシューマーが供給します。

CJK のラン処理は、タイポグラフィのエンコーディングの継ぎ目の上に位置します。埋め込まれた CJK TrueType フェースは、Identity-H CMap と CIDFontType2 の子孫を持つ Type 0 フォントとして出力されます。ISO 32000-2 §9.7.4(RAG ダイジェストはライセンス上限により切り詰められています。_downgraded-claims-o3.md に記録されています)。TrueType プログラムが埋め込まれている場合、Type 2 CIDFont は CIDToGIDMap エントリを通じて文字識別子をグリフインデックスにマッピングします。ISO 32000-2 §9(ダイジェストは B1 契約ページにピン留めされています)。サブセッターは元のグリフ番号を正確に保持するため、/CIDToGIDMap /Identity はサブセットに対して有効なまま維持されます。CjkFontValidator は、候補フォントが選択される前に、そのフォントがスクリプトに必要な Unicode ブロックをカバーしているかどうかを確認する診断です。

種別主なメンバー安定性導入バージョン
ShaperInterfaceinterfaceshape(ShaperInput): ShapingResultstable3.2.0
ShaperFactoryfinal classdefault(), create(), wouldUseRealShaper()stable3.2.0
NullShaperfinal readonly classパススルーのフォールバックシェーパーstable3.2.0
ShapingResultfinal readonly class$glyphRuns, $originalText, $script, $direction, $shaperImplstable3.2.0
ScriptShaperRegistryfinal classregisterMongolian(), getMongolian(), hasMongolian()、およびチベット文字の同等のメソッドstable3.1.0
CjkFontValidatorfinal classvalidateCoverage(), detectScript(), isCjkCodepoint()stable1.0.0

アクセサーの register*get*has* という形状、および ScriptShaperRegistry とスクリプトシェーパーインターフェイスは凍結された契約です。ShapingResult は、設計上、コンシューマーから見える唯一のシェーパー出力です。

examples/text/shaper-factory.php
<?php
declare(strict_types=1);
require_once __DIR__ . '/../../vendor/autoload.php';
use NextPDF\Font\Shaper\ShaperFactory;
use NextPDF\Font\Shaper\ShaperImpl;
$factory = ShaperFactory::default();
$shaper = $factory->create();
// Branch on the result tag, not on the concrete class.
$wouldShape = $factory->wouldUseRealShaper()
? 'HarfBuzz backend available'
: 'NullShaper fallback (degraded — no substitution or positioning)';
echo $wouldShape, "\n";

ShaperFactory::default() は本番用の機能プローブを組み込みます。create() は、選択したバックエンドをファクトリーのライフタイムにわたってメモ化します。機能に関する正確な判断は、wouldUseRealShaper() と各結果の shaperImpl タグから得られます。

examples/text/script-shaper-registry.php
<?php
declare(strict_types=1);
require_once __DIR__ . '/../../vendor/autoload.php';
use NextPDF\Text\Shaping\MongolianShaperInterface;
use NextPDF\Text\Shaping\ScriptShaperRegistry;
final readonly class ComplexScriptBootstrap
{
public function __construct(private ScriptShaperRegistry $registry) {}
/**
* Register a consumer-supplied Mongolian shaper provider at boot so
* the layout pipeline can resolve it by ISO 15924 script tag.
*/
public function register(MongolianShaperInterface $mongolian): void
{
$this->registry->registerMongolian($mongolian);
}
public function hasMongolian(): bool
{
return $this->registry->hasMongolian();
}
}

レジストリは、複雑なスクリプト用プロバイダーの統合ポイントです。エンジンは継ぎ目と凍結されたアクセサーの形状を提供します。モンゴル文字とチベット文字の実装はコンシューマーが供給します。

  • NullShaper の結果では、アドバンスとオフセットが 0 です。これらの位置情報をテキストレイアウトにそのまま渡してはいけません。アドバンスはフォントメトリクスモジュールから解決し、フォールバックは shaperImpl タグで検出してください。
  • 入力が空の場合は、空のランではなく空の glyphRuns リストが生成されます。コンシューマーの反復処理コードでは、長さ 0 のランを特別扱いする必要はありません。
  • ScriptShaperRegistryPsr\Container\ContainerInterface を直接は実装しません。これにより、型付きアクセサーは静的解析の下で絞り込まれた戻り値の型を保ちます。getMongolian()getTibetan() を使用し、汎用の get() は使用しないでください。
  • スクリプトタグは正規の ISO 15924 alpha-4 値と照合され、大文字小文字を区別しない形で格納されます。Mong または Tibt を渡してください。ルックアップでは大文字小文字は関係ありません。
  • CJK 拡張 B の文字は Unicode 面 2 に存在し、サブセット内で cmap Format 12 サブテーブルを必要とします。エンコーディングパスがこれを処理します。CJK について、基本多言語面だけで足りると想定しないでください。

機能プローブは ShaperFactory インスタンスごとに 1 回実行され、バックエンドはメモ化されるため、create() の繰り返し呼び出しにコストはかかりません。NullShaper は入力ランのコードポイント数に対して線形であり、I/O はありません。ScriptShaperRegistry の解決は定数時間のキー付きルックアップです。CjkFontValidator は、すべてのコードポイントを検査するのではなく一定間隔でサンプリングするため、20,000 グリフの CJK フォントに対してもカバレッジチェックを低コストに保ちます。壁時計時間 1500 ms、ピーク 64 MB という performance_budget は、一般的な実行をカバーします。実際のシェーピングで支配的なコストは OpenType バックエンド自体であり、フォールバックが有効な場合はプロセスのスコープ外です。

シェーパーの継ぎ目は UTF-8 文字列を受け取ります。NullShaper は、不正な形式の UTF-8 に対して例外を発生させるのではなく、ベストエフォートで分割して許容します。フォールバックの文書化された契約は、すでに「実際のシェーピングを行わない」ものだからです。呼び出し側は低品質の出力に備えています。バイトオフセットのクラスター契約はバイト指向の長さを使用します。これはマルチバイト入力に対して正しく、コードポイント単位でずれるクラスターマッピングの欠陥を回避します。実際のバックエンドは、存在する場合、サードパーティのネイティブライブラリです。その入力は信頼できないものとして扱い、ランの長さを上流で制限してください。スクリプトシェーパーレジストリはコンシューマーが供給するプロバイダーを格納します。それらの実装の信頼境界はエンジンではなくコンシューマー側にあります。

主張規格条項根拠
埋め込まれた CJK TrueType フェースを、Identity-H CMap と CIDFontType2 の子孫を持つ Type 0 フォントとして出力ISO 32000-2§9.7.4RAG ダイジェストはライセンス上限により切り詰め。プレフィックスは 7a5258772f508e3b、参照先は _downgraded-claims-o3.md
埋め込まれた Type 2 CIDFont による、CIDToGIDMap を通じた文字識別子からグリフインデックスへのマッピングISO 32000-2§9

両方の条項は言い換えられています。2 つ目はダイジェストにピン留めされており(B1 契約ページから再利用)、1 つ目は ADR-013 と cmap エンコーダー開発者向け概要によって裏付けられています。NextPDF は規範的なテキストを複製しません。シェーパーバックエンドは PDF 適合性とは独立しています。ここでの適合性の主張は、エンコーディングの継ぎ目が生成する CJK フォント辞書の出力に関するものであり、ADR-013 と cmap エンコーダー開発者向け概要でさらに文書化されています。

高度なテキスト前処理パイプラインと抽出サービスは、Core のシェーパーの継ぎ目とラン処理の値型を基盤に構築されます。Core のテキストモジュールは、継ぎ目、フォールバック、スクリプトシェーパーレジストリをライセンスなしで提供します。変換リンクの省略は意図的なものです。