跳到內容

產生可供下游工具擷取的文字內容

NextPDF Core 是 PDF 產生端(producer),不提供任何公開的 PDF 轉文字讀取器。在 Core 的脈絡下,「擷取文字內容」指的是你在製作文件時,就讓其中的文字可被擷取。這些字元攜帶 /ToUnicode CMap,文件也具備已加標籤的邏輯結構。接著,符合規範的閱讀器或下游擷取工具就能依閱讀順序還原 Unicode 文字。

從任意第三方 PDF 把文字讀取出來,是取用端(consumer)要處理的事。這項工作屬於 Inspect 模組的旁掛元件或外部工具,不是 Core 產生端介面的一部分。

Terminal window
composer require nextpdf/core:^3

內容串流中的文字呈現運算子會把文字放置到頁面上(ISO 32000-2 §9.4.3)。字元碼並不是 Unicode。/ToUnicode CMap 讓閱讀器能把這些字碼對映回 Unicode 以供擷取(ISO 32000-2 §9.10.2)。已加標籤的結構樹會記錄邏輯閱讀順序,因此擷取時會依文件順序,而非繪製順序,還原文字(ISO 32000-2 §14.8)。

enableTaggedPdf() 會建立這個結構樹,並為內嵌子集字型保留 /ToUnicode CMap。這個組合讓輸出能被可靠擷取。

Document::enableTaggedPdf(string $lang = 'en') 會建立結構樹,並設定符合性模式,以便保留 /ToUnicode CMap。Document::setLanguage(string $lang) 會記錄 BCP-47 自然語言。在寫入內容之前,兩者都必須先呼叫。接著使用一般的 setFont() / cell() / multiCell() 介面寫入文字即可。

<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();
$doc->setLanguage('en');
$doc->enableTaggedPdf('en'); // structure tree + ToUnicode retention
$doc->addPage();
$doc->setFont('helvetica', '', 12);
$doc->multiCell(0, 7, 'This text is extractable by a downstream reader.');
file_put_contents(__DIR__ . '/extractable.pdf', $doc->getPdfData());

這是可獨立執行、能在測試載具上執行的完整程式。它對應到 範例 examples/38-extract-text-content.php。它會製作一份已加標籤的文件,其文字攜帶 /ToUnicode CMap 與邏輯閱讀順序,接著下游擷取器便能依序還原 Unicode 文字。

<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
$paragraphs = [
'NextPDF produces documents whose text content is extractable.',
'A tagged structure tree records the logical reading order.',
'The ToUnicode CMap lets a reader recover Unicode from glyph codes.',
];
$doc = Document::createStandalone();
$doc->setTitle('Extractable text content');
$doc->setAuthor('NextPDF Cookbook');
$doc->setLanguage('en'); // BCP 47; validated on enableTaggedPdf()
// Configure tagged mode BEFORE content so the structure tree captures the
// text in reading order and the /ToUnicode CMap is retained.
$doc->enableTaggedPdf('en');
$doc->addPage();
$doc->setFont('helvetica', '', 12);
foreach ($paragraphs as $p) {
$doc->multiCell(0, 7, $p); // captured in reading order
$doc->ln(2);
}
$pdf = $doc->getPdfData();
// $pdf contains a /StructTreeRoot and per-font /ToUnicode CMaps; an external
// extractor (or the Inspect sidecar) recovers the Unicode text in order.
echo "Wrote a tagged PDF with extractable text content\n";
echo 'Paragraphs authored: ' . count($paragraphs) . "\n";
echo "Text is recoverable via the /ToUnicode CMap + tagged reading order.\n";
// The harness sets NEXTPDF_COOKBOOK_OUTPUT and runs this script under the
// semantic profile; emit the document to the side-channel.
$out = getenv('NEXTPDF_COOKBOOK_OUTPUT');
file_put_contents($out !== false && $out !== '' ? $out : __DIR__ . '/extractable.pdf', $pdf);

預期的 STDOUT:

Wrote a tagged PDF with extractable text content
Paragraphs authored: 3
Text is recoverable via the /ToUnicode CMap + tagged reading order.
  • 是產生端,不是讀取器。 Core 沒有公開的 extractText()。從既有的第三方 PDF 中讀出文字是取用端的任務。請改用搭配 Spectrum 旁掛元件的 Inspect 模組,或外部擷取工具。這份範例是讓你自己的輸出可被擷取。
  • 先設定標籤。 在寫入內容之前先呼叫 enableTaggedPdf(),結構樹才會依閱讀順序記錄文字。在加入內容之後才呼叫,並不會為先前的內容加上標籤。
  • 無效的語言標記。 enableTaggedPdf() 會驗證 BCP-47 標記,若遇到無效值,會擲出 InvalidConfigException。請使用已註冊的標記,例如 enzh-Hant-TWja
  • 純(未加標籤)輸出。 若未呼叫 enableTaggedPdf(),純輸出可能會為了縮小體積,而對使用預定義 CMap 的字型省略 /ToUnicode CMap。如此一來,那些字型的擷取結果就不可靠。當可擷取性很重要時,請為文件加上標籤。

加標籤會新增結構樹並保留 /ToUnicode CMap,使輸出體積略為增加。這項成本與內容量成正比,且不會改變單一遍(single-pass)的繪製模型。

已加標籤的文字內容在設計上就是機器可讀。不要把機密放進文件文字中,還期待它們保持隱藏,因為任何拿到該檔案的人,都能擷取可被擷取的文字。這是一份關於產生端正確性的範例,不是機密性控制措施。若需機密性,請參閱加密範例。

陳述規範條款參考 ID
ToUnicode CMap 會將字元碼對映到 Unicode,以供文字擷取。ISO 32000-2§9.10.2
文字呈現運算子會在內容串流中將字串放置到頁面上。ISO 32000-2§9.4.3
已加標籤的結構樹會記錄用於擷取的邏輯閱讀順序。ISO 32000-2§14.8

這份範例會產出可被擷取的文字內容。它並未主張符合 PDF/UA-2;那項判定應由檢查工具完成。請參閱無障礙範例。