跳转到内容

生成可供下游工具提取的文本内容

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;是否符合应由检查工具判定。请参阅无障碍范例。