使用iText v8.0扁平化合并多份表单PDF成功但文件报错
iText7 v8.0 合并带表单PDF的问题与解决方案
问题概述
我用iText7 v8.0写了个简易PDF合并程序,基础代码如下:
Dim pdfDoc As New PdfDocument(New PdfWriter(outputPath)) Dim merger As New PdfMerger(pdfDoc) For Each file In pdfFiles Dim newDoc As New PdfDocument(New PdfReader(file)) merger.Merge(newDoc, 1, newDoc.GetNumberOfPages()) newDoc.Close() Next pdfDoc.Close() merger.Close()
程序平时运行正常,但合并多份通过“另存为”保存的可填写PDF表单时出问题:因为每份PDF的表单字段名称相同,合并后只有第一份文档的填写值被保留,其余都为空。我的需求很明确:不需要动态重命名字段,只要把每份PDF扁平化后再合并就行(源文件会保留)。
我试过两种扁平化方案:
- 直接获取表单扁平化后合并,代码如下,但运行报错:
Dim form As PdfAcroForm = PdfAcroForm.GetAcroForm(newDoc, True) form.FlattenFields() merger.Merge(form.GetPdfDocument, 1, newDoc.GetNumberOfPages())
- 改用临时文件存储扁平化后的文档,能实现合并,但用Adobe打开生成的PDF时会提示“页面存在错误,Adobe可能无法正确显示页面”,实际显示倒是没问题:
Dim merger As New PdfMerger(New PdfDocument(New PdfWriter(outputPath))) For Each pdfFile In pdfFiles Dim myTempFile As String = Path.GetTempFileName Dim myDoc As New PdfDocument(New PdfReader(pdfFile), New PdfWriter(myTempFile)) Dim myForm As PdfAcroForm = PdfAcroForm.GetAcroForm(myDoc, False) myForm.FlattenFields() myDoc.Close() Dim flattenedDoc As New PdfDocument(New PdfReader(myTempFile)) merger.Merge(flattenedDoc, 1, flattenedDoc.GetNumberOfPages) flattenedDoc.Close() File.Delete(myTempFile) Next merger.Close()
现在想知道:能不能在内存里完成PDF加载、扁平化、合并并保存?有没有更优的无报错实现方案?
内存中完成扁平化+合并的解决方案
可以用MemoryStream在内存中处理扁平化后的PDF内容,不需要依赖临时文件,同时能解决Adobe的错误提示问题。核心思路是:对每份源PDF,先在内存流中完成扁平化操作,再从内存流读取处理后的内容进行合并。
完整代码示例:
Using outputDoc As New PdfDocument(New PdfWriter(outputPath)) Using merger As New PdfMerger(outputDoc) For Each pdfFile In pdfFiles ' 用内存流存储扁平化后的PDF Using ms As New MemoryStream() ' 打开源PDF并绑定到内存流的Writer Using tempDoc As New PdfDocument(New PdfReader(pdfFile), New PdfWriter(ms)) Dim form As PdfAcroForm = PdfAcroForm.GetAcroForm(tempDoc, False) If form IsNot Nothing Then form.FlattenFields() End If ' 关闭tempDoc,确保内容写入内存流 End Using ' 重置内存流位置到起始处 ms.Position = 0 ' 从内存流读取扁平化后的PDF并合并 Using flattenedDoc As New PdfDocument(New PdfReader(ms)) merger.Merge(flattenedDoc, 1, flattenedDoc.GetNumberOfPages()) End Using End Using Next End Using End Using
关键说明
- 使用
Using语句自动管理资源,避免手动关闭文档时可能出现的资源泄漏问题; - 扁平化操作完成后,必须重置
MemoryStream的位置到起始处,否则后续读取会从流的末尾开始,导致读取空内容; - 增加了
form IsNot Nothing的判断,避免源PDF没有表单时出现空引用错误; - 全程在内存中处理,既不需要临时文件,也能避免Adobe的页面错误提示——之前临时文件法的错误大概率是因为临时文件的创建/删除时机导致PDF内容写入不完整,内存流处理能彻底规避这个问题。
内容的提问来源于stack exchange,提问作者ADY
相关产品推荐
相关产品推荐

