跳转到内容

创建可选内容图层(OCG)

将内容放入具名的可选内容组(OCG),通常称为图层。阅读器可以在其图层面板中切换各个图层,其中一个图层默认隐藏。本示例遵循 examples/26-layers.php

OCG 是带有 Type /OCG 的 ISO 32000-2 可选内容组字典。NextPDF 会将分层标记包裹在 BDC/EMC 之间,并使用 OC 标记内容标签。

Terminal window
composer require nextpdf/core:^3

不需要任何可选扩展。图层 API 自 1.0.0 起保持稳定,并可在 8.1–8.4 的向后兼容矩阵上运行。

startLayer($name, $visible) 会打开一个 OCG。在配对的 endLayer() 之前绘制的所有内容,都属于该组。$name 是阅读器在图层面板中展示给用户的标签。ISO 32000-2 规定 OCG 的 Name 必须是面向用户的字符串。传入 $visible: false 会将该组以 OFF 状态注册到默认配置中,因此阅读器会隐藏它,直到用户手动打开。

可见性取决于阅读器是否配合。成员资格字典的默认可见性策略是 AnyOn,意思是只要任一被引用的组为 ON,该图层就会显示。被隐藏的图层只是按照阅读器惯例隐藏;它并未被移除或保护,既不是涂黑遮蔽,也不是安全控制机制。要移除内容,就不要绘制它。

API 接口由 PHPDoc 自动生成。本示例会用到以下两个方法:

  • startLayer(string $name, bool $visible = true): static — 打开一个具名 OCG;$visible: false 会让它默认隐藏。
  • endLayer(): static — 关闭最近打开的图层(与 startLayer() 成对)。
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();
$doc->addPage();
$doc->startLayer('Content', visible: true);
$doc->setFont('helvetica', '', 12);
$doc->cell(0, 8, 'Always-visible body content.', newLine: true);
$doc->endLayer();
$doc->startLayer('Debug Grid', visible: false); // hidden until toggled
$doc->setDrawColor(200, 200, 200);
for ($x = 0.0; $x <= 210.0; $x += 10.0) {
$doc->line($x, 0, $x, 297);
}
$doc->endLayer();
$doc->save(getenv('NEXTPDF_COOKBOOK_OUTPUT') ?: __DIR__ . '/layers.pdf');

这是可以直接放入测试夹具运行的完整示例。它会遵循 NEXTPDF_COOKBOOK_OUTPUT,且不会固定自身产生的任何熵值。

<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Contracts\Alignment;
use NextPDF\Core\Document;
$doc = Document::createStandalone();
$doc->setTitle('Layer Examples (OCG)');
$doc->addPage();
// Layer 1 — background, visible by default.
$doc->startLayer('Background', visible: true);
$doc->setFillColor(230, 240, 250);
$doc->rect(10, 10, 190, 277, 'F');
$doc->endLayer();
// Layer 2 — watermark, visible by default; can be toggled off.
$doc->startLayer('Watermark', visible: true);
$doc->setFont('helvetica', 'B', 54);
$doc->setTextColor(200, 200, 200);
$doc->startTransform();
$doc->rotate(45, 105, 148);
$doc->setXY(30, 135);
$doc->cell(150, 20, 'DRAFT', align: Alignment::Center);
$doc->stopTransform();
$doc->endLayer();
// Layer 3 — main content, visible by default.
$doc->startLayer('Content', visible: true);
$doc->setTextColor(0);
$doc->setFont('helvetica', 'B', 20);
$doc->setXY(10, 15);
$doc->cell(0, 14, 'Layer Examples (OCG)', newLine: true);
$doc->ln(4);
$doc->setFont('helvetica', '', 11);
$doc->multiCell(0, 7, 'This document contains four optional content groups. '
. "Toggle them in your reader's Layers panel.");
$doc->endLayer();
// Layer 4 — debug grid, hidden by default.
$doc->startLayer('Debug Grid', visible: false);
$doc->setDrawColor(180, 180, 180);
$doc->setLineWidth(0.15);
for ($x = 0.0; $x <= 210.0; $x += 10.0) {
$doc->line($x, 0, $x, 297);
}
for ($y = 0.0; $y <= 297.0; $y += 10.0) {
$doc->line(0, $y, 210, $y);
}
$doc->endLayer();
$out = getenv('NEXTPDF_COOKBOOK_OUTPUT') ?: __DIR__ . '/layers.pdf';
$doc->save($out);
echo "Created layers.pdf\n";
  • 每个 startLayer() 都要搭配一个 endLayer() 未关闭的图层会留下悬空的 BDC,且没有对应的 EMC,使文档结构变得不正确。请确保每次打开调用都有对应的关闭调用。
  • 被隐藏的图层并未被移除。 visible: false 只是按照阅读器惯例把内容隐藏起来。相关标记和文本仍然存在于文件中,而且可以恢复。这并不是涂黑遮蔽。对于敏感数据,请不要绘制它。
  • 图层面板的支持程度各不相同。 要切换图层,需要使用支持公开可选内容控制的阅读器。打印管线与功能精简的查看器,可能始终显示或始终隐藏默认关闭的图层。
  • 嵌套。 允许嵌套图层,但每个内层组的可见性仍然各自独立。除非你接入成员资格策略,否则不要假设外层关闭的图层会隐藏内层打开的组。

每个图层会新增一个 OCG 字典,外加一组围绕其标记的 BDC/EMC 配对。这点额外开销可以忽略不计。成本随图层内的内容增加,而不是随图层数量增加,因此这仍然远低于 2000 ms / 64 MB 的预算。

可选内容的可见性**取决于阅读器是否配合,并非访问控制。**隐藏某个图层并不会加密、涂黑遮蔽或移除它的内容。任何人都可以重新启用该图层,或将相关字节提取出来。绝对不要用隐藏图层掩盖机密文字;应完全省略该内容。本示例不解析任何输入,也不访问网络。

陈述规范条款参考 ID
OCG 字典带有 Type /OCGISO 32000-2§8.11.2
OCG 的 Name 是必填、面向用户的标签。ISO 32000-2§8.11.2
可选内容会包裹在 BDC/EMC 之间,并使用 OC 标签。ISO 32000-2§8.11.3.2
OCMD 策略有 AllOn/AnyOn/AnyOff/AllOff(默认为 AnyOn)。ISO 32000-2§8.11.4.3

可重现性配置文件 — 结构性。 尾部的 /ID 与日期原子值在每次保存时都会不同。测试夹具会剥离这些原子值,再比较经 qpdf 规范化后的结构。本示例说明 NextPDF 如何生成该结构。它不声明一般性的 ISO 32000-2 符合性。

不适用。可选内容组是 Core 的能力,没有 Premium 的使用门槛。