You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.04 18:45:03