CJK およびアラビア語の PDF をコピー&ペーストできるようにする
中国語・日本語・韓国語(CJK)およびアラビア語のテキストを、元の論理的な文字列としてコピー&ペーストできる Portable Document Format (PDF) ファイルを作成します。エンジンは、/ToUnicode CMap によってすべてのグリフを Unicode へマッピングし、その結果を Normalization Form Compatibility Composition (NFKC) で正規化したうえで、シェーピングされたランやレタースペーシングされたランを、/ActualText を保持する /Span でラップします。フォントを登録してからコンテンツを書き込めば、抽出結果は正しく保たれます。抽出の検証は PyMuPDF ではなく pdftotext/Poppler で行ってください。veraPDF は PDF/Universal Accessibility 2 (PDF/UA-2) を検証するものであり、抽出を検証するものではありません。
インストール
「インストール」という見出しのセクションcomposer require nextpdf/coreCJK フォント(Noto Sans CJK など)と、文字マップがアラビア語 Presentation Forms-B ブロックをカバーするアラビア語対応フォント(Noto Naskh Arabic など)を登録します。埋め込みがライセンスで許可されているフォントのみを埋め込んでください。
概念の概要
「概念の概要」という見出しのセクションコンテンツストリーム内のグリフコードは Unicode ではありません。/ToUnicode CMap は、リーダーがテキストを抽出できるよう、各コードを Unicode へマッピングします(ISO 32000-2 §9.10)。エンジンは、Unicode UAX #15 で定義されている Normalization Form Compatibility Composition (NFKC) によって、それらの値を正規化します。CJK 互換漢字とアラビア語の表示形は基底文字へマッピングされるため、検索やコピーでは互換用コードポイントではなく正規化されたテキストが返されます。
/ToUnicode だけでは不十分なケースが 2 つあります。レタースペーシングされたラテン文字と、シェーピングされた右から左へのアラビア語です。エンジンはこれらを、スペースを空けたり並べ替えたりしたグリフとして描画するため、グリフの順序だけでは論理的な文字列を復元できません。エンジンは各ランを、囲まれたコンテンツの正確な置換である /ActualText を保持する /Span マークコンテンツシーケンスでラップします(ISO 32000-2 §14.9)。/ActualText を尊重する抽出ツールは、論理的な文字列を返します。
API サーフェス
「API サーフェス」という見出しのセクション| シンボル | 場所 | 役割 |
|---|---|---|
FontRegistry::register(string $fontFile, string $alias = ''): FontInfo | NextPDF\Typography\FontRegistry | CJK およびアラビア語の書体の登録 |
DocumentFactory::create(): Document | NextPDF\Core\DocumentFactory | レジストリを使用するドキュメントの構築 |
Document::writeHtml(string $html): static | NextPDF\Core\Concerns\HasTextOutput | 多言語コンテンツのレンダリング |
コードサンプル — クイックスタート
「コードサンプル — クイックスタート」という見出しのセクション<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\DocumentFactory;use NextPDF\Graphics\ImageRegistry;use NextPDF\Typography\FontRegistry;
$fonts = new FontRegistry();$fonts->register(__DIR__ . '/NotoSansCJK-Regular.ttf', alias: 'CJK');$fonts->register(__DIR__ . '/NotoNaskhArabic-Regular.ttf', alias: 'Arabic');
$doc = (new DocumentFactory($fonts, new ImageRegistry(maxCacheBytes: 0)))->create();$doc->addPage();$doc->writeHtml( '<p style="font-family: \'CJK\';">PDF 2.0 引擎 — 量子</p>' . '<p style="direction: rtl; font-family: \'Arabic\';">فاتورة</p>');$doc->save(__DIR__ . '/multilingual.pdf');pdftotext multilingual.pdf - | head# Extracts the logical text: "PDF 2.0 引擎 — 量子" and the logical Arabic "فاتورة",# not compatibility code points or reversed presentation forms.コードサンプル — プロダクション
「コードサンプル — プロダクション」という見出しのセクションこの自己完結したサンプルでは、ドキュメントにタグを付け、レタースペーシングされた見出しを追加し、ハーネスのパスへ書き込みます。
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\DocumentFactory;use NextPDF\Graphics\ImageRegistry;use NextPDF\Typography\FontRegistry;
$fonts = new FontRegistry();$fonts->register(__DIR__ . '/NotoSansCJK-Regular.ttf', alias: 'CJK');$fonts->register(__DIR__ . '/NotoNaskhArabic-Regular.ttf', alias: 'Arabic');
$doc = (new DocumentFactory($fonts, new ImageRegistry(maxCacheBytes: 0)))->create();$doc->setTitle('Multilingual extraction');$doc->enableTaggedPdf('en');$doc->addPage();
$html = <<<'HTML'<h1 style="font-family: 'CJK'; letter-spacing: 3px;">CODE WORD</h1><p style="font-family: 'CJK';">中文 · 日本語 · 한국어 · 量子 (compatibility ideograph)</p><p style="direction: rtl; font-family: 'Arabic';">المبلغ الإجمالي 380.00</p>HTML;
$doc->writeHtml($html);
$out = getenv('NEXTPDF_OUT');$doc->save($out !== false ? $out : __DIR__ . '/multilingual-copy-paste-extraction.pdf');
echo "Wrote the multilingual PDF\n";出力ファイルに対して pdftotext を実行します。レタースペーシングされた見出しはスペースが挿入されずに CODE WORD として抽出され、CJK 行は基底文字として抽出され、アラビア語の行は論理的な文字列として抽出されます。
エッジケースと注意点
「エッジケースと注意点」という見出しのセクション- 抽出の検証は PyMuPDF ではなく pdftotext で行ってください。 PyMuPDF の生テキストモードはインラインの
/ActualTextを無視し、視覚的なグリフを返すため、正しさを過小に報告することがあります。Poppler(pdftotext)は/ActualTextを尊重します。veraPDF は PDF/UA-2 を検証するものであり、抽出を検証するものではありません。 - 抽出には
/ToUnicodeが必要です。 ライターが/ToUnicodeCMap を出力するよう、フォントを登録して埋め込んでください。埋め込まれていない非標準フォントでは、Unicode マッピングを保証できません。 /ActualTextは、シェーピングされたランとレタースペーシングされたランをカバーします。 プレーンでシェーピングもスペーシングもされていないテキストは、/ToUnicodeだけで正しく抽出されます。/Spanのラッパーは、スペースが空けられたランや並べ替えられたランを保持します。- タグ付き HTML テーブルは PDF/UA-2 チェックを通過します。 抽出は正しく行われ、タグ付き HTML の
<table>は、いまやveraPDF --flavour ua2を失敗ゼロで通過します。アクセシビリティを参照してください。
パフォーマンス
「パフォーマンス」という見出しのセクション/ToUnicode CMap と /Span ラッパーの構築は、グリフ数に比例して線形にスケールします。このレシピのバジェットは wall_ms: 1500, peak_mb: 96 です。
セキュリティに関する注記
「セキュリティに関する注記」という見出しのセクションユーザー指定の多言語文字列は長さを検証し、出力サイズを抑えてください。/ToUnicode ビルダーはサロゲートの片割れやコードスペース外のコードを拒否するため、不正なマップから破損した抽出リソースが生成されることはありません。エンジンはスクリプトを実行せず、ローカルフォントについてリモートリソースを取得することもありません。
| 記述 | 仕様 | 条項 |
|---|---|---|
抽出のための、文字コードから Unicode への /ToUnicode CMap によるマッピング | ISO 32000-2 | §9.10 |
囲まれたコンテンツの正確な置換である /ActualText | ISO 32000-2 | §14.9 |
| 互換分解の後に正準合成を行う NFKC | Unicode UAX #15 | §1.2 |
商業的コンテキスト
「商業的コンテキスト」という見出しのセクション該当なし。
- テキストコンテンツを抽出する — タグ付き抽出の基礎。
- 右から左に書くアラビア語 HTML をレンダリングする — アラビア語のシェーピングと右から左へのテキスト。
- タイポグラフィ —
/ToUnicodeと NFKC 正規化。 - アクセシビリティ —
/ActualTextとタグ付きテーブルのサポート。