从可填写表单到冻结记录:AcroForm 填写与扁平化
Spec: ISO 32000-2, §12.7ISO 32000-2 §12.7
一份 PDF 表单有两段生命。它先是可填写的:一组人在里头键入、勾选,或从中选取的有类型字段。然后,当约定达成时,它变成一份冻结记录:那些值被印进页面本身,因此每一个查看器、在每一台设备上,都看见恰恰所约定的内容。NextPDF 构建第一者,并产生第二者,并带一个刻意的保证——它不会在跨越的途中悄悄地把那些值丢掉。
为何这很重要
标题为“为何这很重要”的章节“我填了什么”与“你看见什么”之间的落差,正是表单出错的地方。
一个可填写字段,技术上是一个绘制在页面之上的小型交互式控件。不同的查看器可以把它渲染得各不相同。有些会兑现一个已保存的值,有些会用一个你没有的字体重新生成外观,有些则让读者再次编辑它。对一份你想要协作者持续编辑的草稿而言,那正是要点。对那份所约定内容的已签副本而言,它是一项负担:那份记录不应取决于是哪个应用程序打开它,且它在事后不应可被编辑。
扁平化弥合了这道落差。它取每个字段的当前值,并把它印进页面,作为普通的、不可变的图形——与一个标题或一个 logo 同一类的内容。在那之后,便没有字段可编辑,也没有外观可重新生成。文档在每一处都显示一样东西、同一样东西。
简短版本
标题为“简短版本”的章节- 一份 AcroForm 是文档的交互式表单:一棵声明在目录中的有类型字段树(Spec: ISO 32000-2, §12.7ISO 32000-2 §12.7)。
- 每个字段透过一个 widget 注解而变得可见——那个你点击或键入的页面上矩形(Spec: ISO 32000-2, §12.5ISO 32000-2 §12.5)。
- NextPDF 为每一种受支持的非签名表单控件——文本、复选框、单选按钮、列表框与组合框(choice),以及按钮——配备有类型构建器,外加一个把它们写为正规 PDF 对象的字段管理器。
- 扁平化把每个字段的值渲染进页面内容流,并移除此时多余的交互式表单,留下一份冻结记录。
- 如果你请求扁平化一份没有页面的文档,NextPDF 不会悄悄地销毁你的字段值。它会保留该表单、警告你,并让你添加一页之后正确地扁平化。
NextPDF 如何处理
标题为“NextPDF 如何处理”的章节这个心智模型是两层。字段是数据:一个名称、一个类型、一个值,以及一组标志。widget 是图像:一个特定页面上、让查看器与该字段交互的矩形(Spec: ISO 32000-2, §12.5ISO 32000-2 §12.5)。一个字段甚至能透过数个 widget 浮现出来——单选按钮组正是这么运作的,数个页面上的选项接到一个底层的值。
NextPDF 给每一种字段类型各自的有类型构建器,因此你永远不必亲手组装一份原始字典。类型替你携带了 PDF 正确的细节。一个复选框、一个单选按钮,以及一个按钮,在规格中全都共享同一种底层表单类型,并以它们的标志来彼此区分;引擎会从你所选的类型设定那些标志,而非要你记住哪一个比特表示“单选”。一个列表框与一个组合框都是 choice 字段;同样地,构建器会挑出正确的编码。你把字段类型用文字陈述一次,而字节随之而来。
扁平化是后半部。扁平化器按拥有它们的页面把 widget 注解分组,用每个 widget 的矩形在那一页上定位,然后把每个值渲染为一小段内容流运算子——设定一个颜色、设定一个文本位置、绘出字形——追加到那一页既有的内容上(Spec: ISO 32000-2, §8.4ISO 32000-2 §8.4)。该值不再是一个活的字段,而成为印上的墨水。因为该表单不再携带任何交互式字段,引擎接着便移除 AcroForm 条目:已没有任何东西还需要可交互了。
- Declare typed fieldsAdd text, checkbox, radio, choice, and button fields through their typed builders; the engine sets the spec-correct PDF type and flags.
- Place the widgetsEach field is drawn as a widget annotation — a rectangle on a chosen page that a viewer can type into, tick, or pick from.
- Collect inputShip it fillable: a reader supplies values, or your code sets them, leaving a filled but still-editable document.
- Flatten the valuesRender each field's value into the page content stream as graphics; the painted value is now immutable.
- Drop the interactive formWith every value baked in, remove the AcroForm so nothing remains editable — a frozen record of what was agreed.
实际示例
标题为“实际示例”的章节一份小而有代表性的表单:构建几个有类型字段,然后把它扁平化成一份冻结记录。
<?php
declare(strict_types=1);
use NextPDF\Core\Document;
$document = Document::createStandalone();$document->addPage();
// Typed builders, called straight on the document. You pick the field// type by choosing its builder method — textField, checkBox, comboBox —// and you pass the value to freeze at creation time. The engine writes// the spec-correct PDF type and flags for you.$document->textField('full_name', x: 40, y: 700, w: 220, h: 18, default: 'Ada Lovelace');$document->checkBox('agree_terms', x: 40, y: 660, size: 14, checked: true);$document->comboBox( 'plan', x: 40, y: 620, w: 160, h: 18, items: ['Starter', 'Team', 'Enterprise'], selected: 'Team',);
// Flatten: the values become immutable page graphics and the// interactive AcroForm is dropped. The result is a frozen record.$document->flattenForms();
$bytes = $document->getPdfData();在 flattenForms() 之前,这是一份可填写表单。在它之后,那些相同的值被印进页面,且已没有字段可供更改。你藉由选择字段的构建器方法——textField、checkBox、comboBox——来挑选字段类型,因此一个错误的类型无法被编码为一个松散的字符串:一个拼写错误是一个对不存在之方法的调用,会在任何字段被写出之前被捕捉,而非一个悄悄出错的字段。那正是这个引擎其余部分所采取的同一套拒绝猜测的立场;参见一个拒绝猜测的 API。
常见的误解
标题为“常见的误解”的章节陷阱在于相信填写一个字段就冻结了它。它没有。一个已填写字段仍携带一个活的值,一个有能力的查看器能编辑它,以及一个某些查看器会重新生成的外观。“我设了值”与“文档现在是一份固定记录”是两个不同的状态。唯有扁平化才从一者跨越到另一者,因为唯有扁平化才把值变成不再像字段那样行事的页面图形。
镜像反面的错误,是扁平化一份你仍需要据以收集输入的草稿。一旦扁平化,那些字段就没了——那正是整个要点——因此请扁平化那份你打算让它成为最终版的副本,而非你仍在传阅的那一份。
限制与边界
标题为“限制与边界”的章节NextPDF 的表单支持是完整的核心:为常见交互式字段控件配备的有类型构建器、一个表单扁平化器,以及一个把字段写为正规 PDF 对象的字段管理器。本页描述的就是那个核心面向。
| Edition | Availability |
|---|---|
| Core | Typed builders for text, checkbox, radio, choice (list box and combo box), and push-button fields; widget placement; a field manager; and a form flattener that bakes values into page graphics. Available in every edition. |
| Pro | Not in this edition |
| Enterprise | Not in this edition |
扁平化在设计上是单向的。它移除交互式表单,好让记录固定下来;它不是一个“暂时锁定”的切换,而且没有一个能从被印上的图形重新推导出可编辑字段的“反扁平化”。如果你需要一份人们能继续编辑的副本,就保留未扁平化的表单,并把一份复本扁平化。
扁平化也不是签名。它让文档在普通查看器的意义上变得不可编辑,但它并不以密码学方式证明是谁产生了它、或它自此未曾变更。当记录必须可被证明就是当初所约定的那一份时,请先扁平化、再签名;参见签名在 PDF 中如何安置。
最后,一份带标签的、无障碍的表单,与一份已扁平化的表单是不同的关注点。如果那份可填写的版本必须能与辅助技术配合使用,那么字段在仍可交互时就需要无障碍名称与结构;参见什么让 PDF 无障碍。
相关文档
标题为“相关文档”的章节- 什么让 PDF 无障碍 — 带标签的表单字段与无障碍名称,针对可填写阶段。
- 一个拒绝猜测的 API — 为何字段类型是一个有类型的枚举,而非一个引擎必须去解读的字符串。
- PDF 文件剖析 — 一份表单据以构建的目录、页面与注解究竟住在何处。
- 签名在 PDF 中如何安置 — 如何让一份冻结记录可被证明就是当初所约定的那一份。
词汇表
标题为“词汇表”的章节- AcroForm — 一份 PDF 的交互式表单:声明在文档目录中、让文件可填写的那棵有类型字段树(Spec: ISO 32000-2, §12.7ISO 32000-2 §12.7)。
- 字段 — 一个表单控件的数据侧:一个名称、一个类型、一个值,以及标志。独立于它在页面上看起来如何。
- widget 注解 — 页面上那个查看器藉以与字段交互的可见、可点击矩形(Spec: ISO 32000-2, §12.5ISO 32000-2 §12.5)。一个字段可以有数个。
- choice 字段 — 一个提供一组选项的字段:列表框把它们展开显示,组合框则显示一个下拉。两者都是同一种 PDF 字段类型。
- 扁平化 — 把每个字段的当前值渲染进页面、作为不可变图形,并移除交互式表单,产生一份冻结记录。
- 冻结记录 — 一份已扁平化的文档:它在每一个查看器中都显示一样固定的东西,且已没有字段可供编辑。