跳转到内容

将表单字段扁平化为静态页面内容

本示例会将一份交互式 AcroForm 扁平化。它会把每个字段的当前值作为普通图形绘制到页面内容流中,随后移除 AcroForm 字典。结果是一份非交互式表单:字段以静态形式呈现,在任何环境中看起来都一致,即使阅读器不支持表单也是如此(ISO 32000-2 §12.7)。本示例先从 examples/30-form-fields.php 构建表单,再调用 flattenForms()

Terminal window
composer require nextpdf/core:^3

扁平化会把每个部件「打印」到其所在页面上。文本字段会变成 BT … Tj … ET 文本。复选框与单选按钮会变成绘制出的路径。选择字段会绘制出已选中的项目。命令按钮会绘制成一个静态的、类似按钮的方框。这符合规范中静态定义外观的模型,也就是字段内容事先已知时采用的做法(ISO 32000-2 §12.7)。由于这些值已经写入内容,就不需要已弃用的 NeedAppearances 机制。

此配置文件为 structural。文档带有尾部 /ID,后处理流程会在比较两次运行结果之前先将其规范化。

NextPDF\Core\Concerns\HasFormFields::flattenForms(): static 会把文档中创建的每个字段扁平化为静态页面内容,并移除 AcroForm。若没有任何字段,它不会执行任何操作。在内部,它会将这项工作委托给 NextPDF\Form\FormFlattener

使用字段构造函数构建表单。相关步骤请参阅 构建并预填 PDF 表单一节。随后调用 flattenForms(),并确保在 save() 之前完成。

<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();
$doc->setTitle('Flattened Form');
$doc->addPage();
$doc->textField(name: 'full_name', x: 20, y: 30, w: 90, h: 8, default: 'Ada Lovelace');
$doc->checkBox(name: 'agree', x: 20, y: 45, size: 5, checked: true);
// Bake the field values into the page; the AcroForm is removed.
$doc->flattenForms();
$doc->save(__DIR__ . '/flattened.pdf');
echo "Wrote flattened.pdf (no interactive fields)\n";

下方的完整示例会从 examples/30-form-fields.php 构建这份多区段表单。它会先预填表单,接着将其扁平化,并写入 NEXTPDF_COOKBOOK_OUTPUT 供测试工具使用。

<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();
$doc->setTitle('Customer Registration — Flattened');
$doc->addPage();
$doc->setFont('helvetica', 'B', 20);
$doc->cell(0, 14, 'Customer Registration (read-only copy)', newLine: true);
$doc->ln(4);
$leftMargin = 15.0;
$fieldX = 70.0;
$fieldW = 120.0;
$fieldH = 8.0;
$rowSpacing = 12.0;
$prefill = [
'full_name' => 'Ada Lovelace',
'email' => '[email protected]',
'phone' => '+44 20 7946 0000',
'company' => 'Analytical Engines Ltd',
];
$y = 40.0;
$doc->setFont('helvetica', '', 10);
foreach ($prefill as $name => $value) {
$doc->setXY($leftMargin, $y);
$doc->cell(50, $fieldH, ucwords(str_replace('_', ' ', $name)) . ':');
$doc->textField(name: $name, x: $fieldX, y: $y, w: $fieldW, h: $fieldH, default: $value);
$y += $rowSpacing;
}
$y += 6;
$doc->setXY($leftMargin, $y);
$doc->cell(0, 7, 'Newsletter');
$doc->checkBox(name: 'newsletter', x: $leftMargin + 70, y: $y, size: 5, checked: true);
// Flatten: widgets become static page content; the AcroForm is dropped.
$doc->flattenForms();
$out = getenv('NEXTPDF_COOKBOOK_OUTPUT');
$doc->save($out !== false ? $out : __DIR__ . '/registration-flattened.pdf');
echo "Wrote flattened registration form\n";

预期输出:

Wrote flattened registration form

输出会显示相同的值,但不再包含交互式字段。不支持表单的阅读器也会以相同方式绘制它。

  • 扁平化不可逆。 一旦你调用 save(),交互式字段就会消失。如果之后可能还需要编辑这些值,请保留未扁平化的原始文件。
  • 调用顺序。 请在创建字段之后运行 flattenForms(),也就是在 save() 之前。在没有字段时调用它不会执行任何操作,这是安全的。
  • 签名字段不会被扁平化。 /Sig 字段的视觉表面来自其 CMS SignedData 生成的外观,而不是可重新渲染的值。重新栅格化它会产生一个静态的「残影」图形,不再对应任何可验证的签名。因此,扁平化流程会刻意跳过签名字段。请务必在签名之前扁平化表单,绝不要在签名之后。
  • 复选框的真值判断。 当复选框的值为 Yes/On/1/true 时,它会绘制勾选标记。空值或 Off 值则只会绘制方框。
  • 扁平化文本所用的字体。 扁平化后的文本会使用当前字体。若未设置字体,它会回退使用 Helvetica。若字段值是 CJK 或使用自定义字体,请在 flattenForms() 之前设置你想使用的字体。

扁平化的开销会随字段数量呈线性增长。对每个字段,它会追加一个有界的内容块,接着移除 AcroForm 对象。对于一般表单,它会稳定控制在 1500 ms/64 MB 的预算之内。

扁平化会让字段值在常规阅读器中无法编辑。这是呈现方式的改变,并非访问控制。这些值仍会显示在页面内容中,任何文本工具都能提取它们。请勿把扁平化当成遮蔽(redaction),也不要当成敏感值的保护手段。若需要机密性,请使用 用权限加密一节。也请一并阅读该节中关于阅读器需配合(reader-cooperative)的提醒,因为权限位同样无法强制执行读取限制。绝不要扁平化一份已签名的文档。请先扁平化,再签名。

陈述规范条款参考 ID
扁平化表单是字段的非交互式(静态)呈现。ISO 32000-2§12.7
当字段内容事先已知时,其外观为静态定义。ISO 32000-2§12.7
由于外观已被写入内容中,已弃用的 NeedAppearances 标志就变得不必要。ISO 32000-2§12.7

NextPDF 会生成所引条款描述的静态结构。它并未主张全面符合 ISO 32000-2。