讓 CJK 與阿拉伯文 PDF 正確支援複製貼上
建立一份 Portable Document Format(PDF)檔案,讓其中的中文、日文、韓文(CJK)與阿拉伯文文字能以原始邏輯字元複製貼上。引擎會透過 /ToUnicode CMap 把每個字形對映到 Unicode,以 Normalization Form Compatibility Composition(NFKC)正規化結果,並把塑形後或加了字距的文字段落包裹在帶有 /ActualText 的 /Span 中。只要註冊字型並寫入內容,擷取結果就能維持正確。請以 pdftotext/Poppler 驗證擷取,而非 PyMuPDF;veraPDF 驗證的是 PDF/Universal Accessibility 2(PDF/UA-2),不是擷取結果。
composer require nextpdf/core註冊一個 CJK 字型(例如 Noto Sans CJK),以及一個支援阿拉伯文、且字元對應表涵蓋 Arabic 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:加了字距的拉丁文,以及塑形後由右至左的阿拉伯文。引擎會把它們繪製成加了間距或重新排序的字形,因此僅靠字形順序無法還原邏輯字串。引擎會把每個文字段落包裹在帶有 /ActualText 的 /Span 標記內容序列中,而 /ActualText 是所包圍內容的確切替換(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 那一行會擷取成對應的基底字元;阿拉伯文那一行則會擷取為邏輯字串。
邊界情況與陷阱
標題為「邊界情況與陷阱」的區段- 請以 pdftotext 驗證擷取,而非 PyMuPDF。 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 建構器會拒絕代理對半碼(surrogate halves)與超出碼空間的字碼,因此格式錯誤的對映無法建立出損毀的擷取資源。引擎不會執行任何指令稿,也不會為本地字型取得任何遠端資源。
符合性
標題為「符合性」的區段| 陳述 | 規範 | 條款 |
|---|---|---|
/ToUnicode CMap 會將字元碼對映到 Unicode,以供擷取。 | ISO 32000-2 | §9.10 |
/ActualText 是所包圍內容的確切替換。 | ISO 32000-2 | §14.9 |
| NFKC 是先進行相容分解,再進行正規組合。 | Unicode UAX #15 | §1.2 |
商業情境
標題為「商業情境」的區段不適用。
另請參閱
標題為「另請參閱」的區段- 擷取文字內容 — 已加標籤擷取的基本概念。
- 繪製由右至左的阿拉伯文 HTML — 阿拉伯文塑形與由右至左文字。
- Typography —
/ToUnicode與 NFKC 正規化。 - 無障礙 —
/ActualText與已加標籤表格的支援。