PDF 表单填写
认出 PDF 里的可填写表单域,直接在浏览器里填好再导出,可选保持可填写或展平成静态内容,防止被人改回去。
文件只在浏览器里读取和写入,不会上传。带打开口令的 PDF 请先取消密码再传上来。
使用说明
PDF 里的「可填写表单」有两种完全不同的实现,先搞清楚手上这份是哪种,能省掉很多来回。标准做法叫 AcroForm:字段定义写在文件的 AcroForm 字典里,每个域声明自己的类型、取值范围和外观流,这类文件任何工具都能读写,本工具处理的就是它。另一种是 Adobe 早年的 XFA 方案,整张表单的外观和逻辑装在一段 XML 里,PDF 那层只是外壳,第三方库读不到里面的字段,本页遇到会专门提示,能读到的只有外壳里的 AcroForm 域。
还有一种情况是域被「拍平」过。拍平会把每个域当前的外观画进页面内容流,然后删掉字段定义,之后的阅读器只当它是普通文字。拍平是单向的:没有工具能把画进页面的内容还原成可填写的域。所以本页读不到任何域时,除了「本来就不是表单」,第二常见的解释就是这份文件已经被拍平或打印重排过了。
中文为什么填不了,这里说得直白一点。写入之后 pdf-lib 会用内置的 Helvetica 重画该域的外观流,这个标准字体只有西文字形,遇到汉字它会悄悄换成问号,而且不报错。实测存进文件的外观流就是两个 3F 字节,也就是「??」,你在阅读器里看到的正是这个。真正解决要先嵌一份中文字体再重画外观,而当前依赖组合下字体子集化是坏的,完整嵌入又会让一份中文字体把结果撑到十几 MB。两害相权,这里选择在写入前拦下非 ASCII 值并说明原因,而不是给你一份看着填了、实际是问号的文件。要填中文,请用支持中文的 PDF 编辑器,或者先换掉这个文件的表单。
「保持可填写」和「展平」的区别只有一个:域还在不在。保持可填写适合还要继续填、或者要把数据从表单里提取出来的场合,域是可编辑的,也不影响检索。展平适合交给别人当正式件,或者要避免有人改回内容:外观画进页面后,阅读器没有可以编辑的对象,文字仍然可以选中和搜索,但表单结构本身没了。本页输出的是新文件,原件不会被覆盖。
表单常常只是流程里的一环。要先把几份扫描件合成一份再填,用PDF 合并;只挑需要签的几页出来,用PDF 拆分;填好后想把内容固定在页面上同时保留可搜索文字,就在导出时选展平,也可以配合PDF 加页码一起做成归档件。要确认这份文件的表单结构和文档信息,用PDF 元数据修改看更清楚。
常见问题
为什么这个 PDF 读出来一个表单域都没有?
三种常见原因。一是本来就没有表单,只是普通排版的文件;二是表单被拍平过,域已经画进页面内容流,文件里不再有可填写的字段定义,任何工具都恢复不了;三是 XFA 表单,那是一种把整张表单装在 XML 里的老式写法,字段定义不在 PDF 的 AcroForm 里,pdf-lib 这类库读不到,本页会专门提示这种情况。
AcroForm 和 XFA 有什么区别?
AcroForm 是 PDF 规范里的标准表单,字段定义写在文件的 AcroForm 字典里,每个域有自己的类型、取值范围和外观流,第三方库都能读写。XFA 是 Adobe 私有的 XML 表单方案,整张表单的外观和逻辑放在一段 XML 里,PDF 那一层只是外壳。XFA 只有 Acrobat 系阅读器完整支持,浏览器和第三方库基本读不到,遇到纯 XFA 文件本页会说明无法填写。
填中文为什么被拦下来?
写入后 pdf-lib 会用内置的 Helvetica 重画该域的外观流,这个字体只有西文字形,中文会被替换成问号,而且它不抛错,属于静默出错。实测存进去的外观流就是两个 3F 字节,也就是「??」,值本身虽然在文件里是正确编码,但阅读器按外观流显示,你看到的还是问号。所以这里宁可先拦下来,让你改成拼音或数字,也不给你一份看起来填了其实是问号的文件。
那为什么不像加水印那样让我上传字体?
因为表单这条路上嵌字目前是坏的。pdf-lib 保存时要调字体子集化,而当前依赖的字库版本之间不兼容,实测任意一份系统 ttf 都会在保存时报错;改成完整嵌入虽然能过,但一份中文字体会让结果文件从几十 KB 涨到十几 MB,不适合当作默认行为。加水印能嵌字是因为那条路径只画一行文字,和表单重画外观流的调用方式不同。
「保持可填写」和「展平」该选哪个?
给别人继续填或存档留底,选保持可填写,域还是活的,能被检索和继续编辑。要交给别人当正式件,选展平:展平会把每个域当前的外观画进页面内容流,然后把字段定义删掉,之后的阅读器只能当普通文字显示,改不了、也复制不出结构化数据。注意展平不可逆,输出是新文件,原件不受影响。
展平后还能搜索到填进去的文字吗?
能。展平是把外观流的内容画进页面,文字在页面上就是普通文本,选中、搜索、打印都正常。但域本身没了,所以「表单数据」这层结构消失了,用表单提取工具再去读会得到零个域。如果对方需要把数据导入系统,就别展平,保持可填写。
文件里原有值的域,我没改会被重画吗?
不会。工具只写你改过的域,没改的域不会被标脏,保存时自然也不会重画外观流,原来长什么样就还是什么样。只有你动过的那几个域,外观会用内置西文字体重画,所以字体样式可能和原文档里其它域不完全一致,这是 pdf-lib 的既有行为,不是本工具额外做的处理。
签名域和按钮为什么填不了?
签名域里放的是密码学签名数据,不是一段文本,改动会让签名失效,所以任何工具都不该去动它;按钮域(包括提交按钮和清除按钮)只是触发器,本身没有可填的值。这两类域本页只列出来并标注为只读,导出时原样保留。