Node.js使用pdf-lib合并带填充输入字段的PDF后表单字段空白如何解决
问题根因
pdf-lib 执行多PDF合并操作时,默认仅迁移页面的静态可视内容,不会自动同步源PDF的AcroForm表单结构、已填充的字段值以及字段对应的外观渲染流,因此填充过的表单字段合并后会显示为空白。
修复方案
你可以选择两种方案解决该问题,按需选择即可:
- 方案1:保留合并后PDF的表单可编辑能力,手动同步所有源PDF的表单字段到目标文档
- 方案2:不需要保留编辑能力时,直接将所有表单字段扁平化转为静态内容,是最简单稳妥的处理方式
可直接复用的实现代码
const { PDFDocument } = require('pdf-lib'); const fs = require('fs/promises'); async function mergePdfsWithForm(pdfPaths, outputPath, needEditableForm = false) { // 初始化目标PDF文档 const mergedPdf = await PDFDocument.create(); const mergedForm = mergedPdf.getForm(); for (let fileIndex = 0; fileIndex < pdfPaths.length; fileIndex++) { const pdfBytes = await fs.readFile(pdfPaths[fileIndex]); const srcPdf = await PDFDocument.load(pdfBytes); const srcForm = srcPdf.getForm(); const srcFields = srcForm.getFields(); // 处理表单字段重名问题,给每个源文件的字段加唯一前缀 const fieldPrefix = `file_${fileIndex}_`; const fieldMap = new Map(); // 同步所有源表单字段到目标表单 for (const srcField of srcFields) { const newFieldName = fieldPrefix + srcField.getName(); let newField; // 按字段类型同步 if (srcField.constructor.name === 'PDFTextField') { newField = mergedForm.createTextField(newFieldName); newField.setText(srcField.getText()); } else if (srcField.constructor.name === 'PDFCheckBox') { newField = mergedForm.createCheckBox(newFieldName); srcField.isChecked() && newField.check(); } else if (srcField.constructor.name === 'PDFRadioGroup') { newField = mergedForm.createRadioGroup(newFieldName); newField.select(srcField.getSelected()); } // 其他字段类型可按需补充,例如下拉框、签名框等 if (newField) { // 同步字段的外观属性(字体、大小、颜色等) newField.setFontSize(srcField.getFontSize()); newField.setColor(srcField.getColor()); fieldMap.set(srcField.getName(), newField); } } // 拷贝源PDF的所有页面到目标文档 const pages = await mergedPdf.copyPages(srcPdf, srcPdf.getPageIndices()); for (let pageIdx = 0; pageIdx < pages.length; pageIdx++) { const newPage = pages[pageIdx]; mergedPdf.addPage(newPage); // 把当前页的字段关联到新页面 const srcPageFields = srcForm.getFieldsForPage(srcPdf.getPage(pageIdx)); for (const srcField of srcPageFields) { const newField = fieldMap.get(srcField.getName()); if (newField) { // 同步字段的位置和尺寸到新页面 const widgets = srcField.acroField.getWidgets(); for (const widget of widgets) { newField.addToPage(newPage, { x: widget.getRectangle().x, y: widget.getRectangle().y, width: widget.getRectangle().width, height: widget.getRectangle().height }); } } } } } // 如果不需要保留表单编辑能力,直接扁平化所有字段 if (!needEditableForm) { mergedForm.flatten(); } // 导出合并后的PDF const mergedPdfBytes = await mergedPdf.save(); await fs.writeFile(outputPath, mergedPdfBytes); } // 调用示例 // mergePdfsWithForm(['./1.pdf', './2.pdf'], './merged.pdf', false)
注意事项
- 如果涉及复杂字段类型(比如签名框、多级下拉框),可自行补充对应字段类型的同步逻辑
- 字段前缀的命名规则可按需调整,只要保证所有字段名不重复即可
- 不需要保留编辑能力时优先用扁平化方案,兼容性更好,不会出现不同PDF阅读器打开显示异常的问题
内容的提问来源于stack exchange,提问作者Tirlochan Arora
相关产品推荐
相关产品推荐

