跳转到内容

Content:文本与结构化内容模型

Content 模块负责构建文本显示运算符、文本状态运算符、文本阴影、文档级 JavaScript 以及标记内容属性字典。它位于布局与内容流之间。

Terminal window
composer require nextpdf/core:^3

Content 包含用于将解析后文本转换为 PDF 运算符的原语。TextRenderer 是核心组件。它会为字符串构建文本显示运算符,以及位于其前面的文本状态运算符。Tj 运算符使用当前字体及其他文本相关图形参数绘制字符串中的字形——ISO 32000-2 §9。TextRenderer 会根据当前生效的 TypographyMode,在单个显示运算符和带定位的 TJ 数组之间作出选择。当前 TypographyMode 使用 TJ 数组时,它会应用字偶距调整。

文本状态会被完整建模。setTextRenderingMode() 接受一个 TextRenderingMode 枚举值。它的八种取值与 ISO 32000-2 文本渲染模式一一对应:填充、描边、先填充后描边、不可见,以及四种裁剪变体(表 104)。渲染器还会控制描边宽度、字符间距与字词间距、水平拉伸、文本升降、从右到左方向,以及一个可选的 Hyphenator。调用 buildTextStateOperators() 会将累积的状态作为单个运算符块发出。

TextShadow 是一个值对象——包含颜色、以用户单位表示的 X 和 Y 偏移,以及不透明度。渲染器使用它发出带偏移的第二遍绘制。默认偏移为细微的 0.5/−0.5,不透明度为 0.5,与 CSS 风格的柔和阴影相匹配。

JavaScriptManager 负责文档级脚本。includeJs() 注册文档脚本。addJsObject() 注册命名脚本对象。writeJavaScript() / writeOpenAction() 会将它们序列化到目录和 OpenAction 中。该管理器在发出之前会对每个脚本主体进行防护,并执行 PDF 字符串编码。

PropertiesRegistry 是标记内容属性存储区。register() 会为属性字典返回一个稳定的标记 Index(索引)。registerOcg() / registerOcgs() 按对象编号绑定可选内容组。writeProperties() 会将该注册表序列化到页面资源字典中。当 ContentStream 模块以属性列表打开标记序列时,引用的就是这些数据。

这里包含两个图像解码器,因为它们对应 PDF 原生的直通格式。JBig2LoaderJpxLoader 会解析 JBIG2 和 JPEG 2000 的段结构,并返回 ImageData,但从不对像素进行栅格化。编码后的字节会原封不动地传递给查看器。当 JBIG2 源携带独立的全局段时,JBig2Loader 会通过图像 XObject 上的 /JBIG2Globals 流引用将其嵌入;而 in-stream/in-line 形式仍会像以前一样往返传递。这只是结构性接线——全局段字节会在未栅格化的情况下直通传递,而不是被解码。

关键方法角色
TextRendererbuildTextShowOperator(), buildTextStateOperators(), setTextRenderingMode(), setTextStrokeWidth(), setTextShadow(), setFontSpacing(), setWordSpacing(), setFontStretching(), setTextRise(), setRTL(), setHyphenation()文本显示 + 文本状态运算符构建器
TextRenderingMode(枚举)Fill, Stroke, FillStroke, Invisible, FillClip, StrokeClip, FillStrokeClip, ClipISO 32000-2 文本渲染模式
TextShadow__construct(Color, offsetX, offsetY, opacity)偏移绘制遍的值对象
JavaScriptManagerincludeJs(), addJsObject(), hasJavaScript(), writeJavaScript(), writeOpenAction()文档级 JavaScript 目录接线
PropertiesRegistryregister(), getTagIndex(), registerOcg(), registerOcgs(), getAll(), writeProperties()标记内容 + OCG 属性存储区
JBig2Loaderload(), loadFromString(), parseSegments()JBIG2 直通解码器
JpxLoaderload(), loadFromString(), parseBoxes()JPEG 2000 直通解码器

运行 composer docs:generate-api-php -- --module=Content 获取完整的 PHPDoc 表格。

源文件:examples/28-text-rendering.php

<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Content\TextRenderer;
use NextPDF\Content\TextRenderingMode;
$renderer = new TextRenderer();
$renderer
->setTextRenderingMode(TextRenderingMode::FillStroke)
->setTextStrokeWidth(0.3)
->setWordSpacing(0.5);
$stateOps = $renderer->buildTextStateOperators();

下面的示例会添加柔和阴影和连字符处理器,然后使用调用方提供的转义函数(来自 ADR-015 规定的 PdfStringEscaper 接缝)构建显示运算符。

<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Content\TextRenderer;
use NextPDF\Content\TextShadow;
use NextPDF\Graphics\Color;
use NextPDF\Support\PdfStringEscaper;
$renderer = new TextRenderer();
$renderer
->setTextShadow(new TextShadow(Color::rgb(0, 0, 0), 0.4, -0.4, 0.45))
->setRTL(false);
$showOp = $renderer->buildTextShowOperator(
text: 'Quarterly report',
fontKey: 'F1',
metrics: $fontMetrics,
escapeSegment: static fn (string $s): string => PdfStringEscaper::escapeLiteral($s),
);
$pageContent = $renderer->buildTextStateOperators() . $showOp;
  • buildTextShowOperator() 对空输入返回空字符串。不要发出空的 Tj——如果你的布局可能产生空白文本段,请在上游加以防护。
  • 转义回调是必需的,并负责字符串安全。请传入规范的 PdfStringEscaper::escapeLiteral()(ADR-015)。不完整的转义器会产生语法损坏的字面字符串。
  • TextShadow::offsetY 在左上角原点坐标系中为负值表示向下。正的 Y 值会把阴影向上推,这很少是预期效果。
  • JavaScriptManager 会对脚本输入进行防护。无效的脚本主体会在注册时被拒绝,而不是在写入时被静默丢弃。
  • JBig2LoaderJpxLoader 从不进行栅格化。它们会校验并直通传递编码后的字节。损坏的段会导致解析错误,而不是空白图像。
  • PropertiesRegistry::register() 对每个字典都是幂等的——相同的属性字典会复用同一个标记索引。

运算符构建相对于字符串长度为 O(n);当排版模式使用 TJ 数组时,还会增加一次 O(n) 的字偶距处理。这里没有布局或字形整形开销——那部分留在 Typography 和 Layout 模块中。JavaScript 和属性序列化是 O(条目数)。直通图像加载器以 O(字节数) 进行解析,解码开销为零。这是它们在扫描文档工作负载中的主要优势。参考工作负载的 performance_budget 为 1500 ms 实际耗时和 64 MB 峰值。

JavaScriptManager 接受可能源自不受信任模板的脚本主体。它会对每个主体进行防护并执行 PDF 字符串编码,但文档 JavaScript 仍然是一个活动内容攻击面。对于不受信任的输出,请禁用它,或使用 /modules/core/security/ 中描述的清理路径将其剥除。JBig2LoaderJpxLoader 会解析不受信任的段结构:请限定输入大小和解析时间,并在源由用户提供时,在受限的工作进程中运行提取。文本转义边界就是调用方提供的回调。请始终传入规范的转义器,使控制字节无法从字面字符串中逃逸出来。

该模块发出的文本显示和文本状态运算符与 ISO 32000-2 §9 文本模型保持一致。这包括 Tj 运算符语义,以及 TextRenderingMode 枚举所映射的表 104 渲染模式。这些属于实现事实:运算符的形态由 src/Content/TextRenderer.phpTextRenderingMode 枚举产生,并由 tests/Unit/Content/TextRenderer*JavaScriptManagerIsoTestPropertiesRegistryTest 加以验证。它们并不构成对端到端 PDF 2.0 一致性的断言。字符串转义契约遵循 ADR-015 和 ISO 32000-2 §7.3.4.2。JBIG2 和 JPEG 2000 直通路径会保持编码流不变。独立的 JBIG2 全局段会作为图像 XObject 上的 /JBIG2Globals 流引用被嵌入——经验证,这是结构性接线,而不是解码保真度声明。文档级一致性由 /modules/core/conformance/ 中的预言机与黄金套件加以验证。