iText填充PDF模板设置标记报错,求最新解决方案
问题描述
我正在使用iText填充PDF模板,同时希望为模板以及填充的元素添加标记。第一步尝试为待插入元素添加标记,代码如下:
PdfReader reader = new PdfReader("myTemplatepath"); ByteArrayOutputStream baos = new ByteArrayOutputStream(); PdfStamper stamper = new PdfStamper(reader, baos); PdfWriter writer = stamper.getWriter(); writer.setTagged(); //Make document tagged
但调用writer.setTagged()时出现错误:
java.lang.IllegalArgumentException: Tagging must be set before opening the document
我在一篇2007年的帖子中了解到问题在于PdfStamper不支持标记,最佳方案是创建新的带标记PDF。现咨询:当前是否有新的实现方案?若没有,针对带有自动填充可编辑元素的复杂模板,最佳处理方式是什么?
解决方案
先明确一下:你用的应该是iText 5(从PdfStamper这个类就能看出来),这个版本里PdfStamper确实从始至终都不支持给现有PDF添加标记——因为标记结构需要在文档初始化阶段就确定,而PdfStamper是基于已生成的PDF做修改,没办法回溯去初始化标记树,所以那个2007年的结论在iText 5环境下至今依然成立。
不过现在iText已经迭代到7.x版本了,这一代的API完全重构,针对标记PDF的支持完善了很多,这也是当前的最优解决方案:
1. 升级到iText 7,在修改模板时添加标记
iText 7用PdfDocument替代了iText 5里PdfReader+PdfStamper的组合,并且支持在初始化文档时启用标记,后续还能给填充的表单字段关联标记元素。
示例代码如下:
// 加载模板文件 PdfReader reader = new PdfReader("myTemplatepath"); // 准备输出流(这里用字节流,你也可以直接写文件) PdfWriter writer = new PdfWriter(new ByteArrayOutputStream()); // 关键:初始化PDF文档时就启用标记,这一步必须在文档打开前做 PdfDocument pdfDoc = new PdfDocument(reader, writer); pdfDoc.setTagged(); // 获取表单并填充内容 PdfAcroForm form = PdfAcroForm.getAcroForm(pdfDoc, true); Map<String, PdfFormField> formFields = form.getFormFields(); // 填充示例字段 formFields.get("username").setValue("Alice Smith"); formFields.get("phone").setValue("123-456-7890"); // 给填充的字段添加标记(以username字段为例,关联到Span标记并设置替代文本) PdfStructElem usernameTag = new PdfStructElem(pdfDoc.getStructTreeRoot(), PdfName.Span); usernameTag.setAlt("用户姓名"); formFields.get("username").setStructParent(usernameTag.getStructParentIndex()); // 完成操作后关闭文档 pdfDoc.close();
如果你的原模板本身已经是带标记的PDF(比如用Adobe Acrobat创建时就开启了标记功能),iText 7还能自动保留原有标记结构,填充的内容可以直接融入其中,不需要手动构建太多标记元素。
2. 若无法升级到iText 7,针对复杂模板的替代方案
如果因为项目限制必须用iText 5,那确实只能走“重建带标记PDF”的路子,步骤大概是:
- 读取原模板的所有页面元素、表单字段定义
- 创建一个全新的带标记PDF文档(初始化时就调用
setTagged()) - 将原模板的页面元素逐一复制到新文档中,同时还原表单字段的结构
- 在新文档中填充表单字段,并为每个字段手动添加对应的标记元素
这种方式的缺点很明显:对于布局复杂、包含大量图片/嵌套元素的模板,工作量极大,还容易丢失原格式。所以能升级的话,优先选iText 7。
内容的提问来源于stack exchange,提问作者Liz Lamperouge

