带有节点和快递的多页文档 pdf-lib.js

分享于2022年07月17日 express node.js 问答
【问题标题】:带有节点和快递的多页文档 pdf-lib.js(multipage document pdf-lib.js with node & express)
【发布时间】:2022-06-24 05:17:53
【问题描述】:

我正在尝试从我的文件系统上的模板创建一个多页文档,但我却在文档中的所有页面上出现了相同页面标题的奇怪行为。有什么想法我在这里做错了吗?

我不太明白的是我们添加页面的方式。为什么我们需要在下面的例子中引用 newDoc ,而我们在做 await newDoc.copyPages(page, [0]) ?而不仅仅是 newDoc.addPage(page) ? 会不会是因为在复制数据流的时候两个页面的字段名相同,所以表单字段 Title 被覆盖了?

注意: 我已经知道 StackOverflow 没有 pdf-lib.js.org 的标签,不要与其他 pdf 库混淆。

const payload = {
  rows: [{
    id: 1,
    title: 'Foo',

  },{
    id: 2,
    title: 'Bar'
  },
  formData: {
    hello: 'World',
    lorum: 'Ipsum'
  }
  ]
}

const makePdf = async (payload) => {
  const newDoc = await PDFDocument.create()
  newDoc.getForm().acroForm.dict.set(PDFName.of('NeedAppearances'), PDFBool.True)
  for (const row of payload.rows) {
    await addPage(row, payload.formData, newDoc)
  }
  return newDoc
}

const addPage = async (dataRow, formData, newDoc) => {
  const rowId = dataRow.id
  let templateName
  switch(true) {
    case (rowId === 1):
      templateName = 'foo'
    break
    case (rowId === 2):
      templateName = 'bar'
    break
  }
  const templatePath = path.join(__dirname, `../templates/pdfs_/${templateName}.pdf`)
  const template = await fs.readFileSync(templatePath)
  const page = await PDFDocument.load(template)
  const form = page.getForm()
  form.acroForm.dict.set(PDFName.of('NeedAppearances'), PDFBool.True)
  switch(templateName) {
    case 'foo':
      foo(form, formData)
    break
    case 'bar':
      bar(form, formData)
  }
  // dataRow.title logs correct strings ie: 'Foo' & 'Bar'
  form.getField('Title').setText(dataRow.title)
  const [firstPage] = await newDoc.copyPages(page, [0])
  return await newDoc.addPage(firstPage)
}

const bar = (form, formData) => {
  form.getField('Lorum').setText(formData.lorum)
}

const foo = (form, payload) => {
  form.getField('Hello').setText(formData.hello)
}

return makePdf(payload)

// Produces 2 page pdf with the same title
// [[ title: Foo, Hello: World ], [title: Foo, Lorum: Ipsum ]]

  • How to Ask - 你想达到什么目的?可以分享一下模板示例吗?
  • 每个标题字段,每个页面上的不同文本
  • 看我的回答,我想这就是你要找的东西
  • @yeya 感谢您的回复。我试图为每一行加载一个新模板,我看到你所有页面只有一个模板。如果我将 loadTemplate 函数移到 for 循环中,它可能会起作用。您还需要将 pdfSample.templateDoc 传递给 addDocPage 函数吗?我已经有一段时间没有使用 JS 类了,但我会尝试一下,让你知道它是否有效。
  • 是的,您可以为每个文件调用 loadTemplate。不,您不需要填充 templateDoc,因为 loadTemplate 在每个 loadTemplate 上都保存到 this 。如果它回答了您的问题,请接受答案。

【解决方案1】:

我没有你的模板文件,所以我不确定你想要实现什么。

copyPage 确实是必需的。

您应该在再次更改之前保存 PDFDocument

我试图重写你的代码,我忽略了 PDFName PDFBool 行,但我相信你能明白。

const {PDFDocument} = require('pdf-lib');
const fs = require('fs');

const payload = {
    rows: [
        { id: 1, title: 'Foo', }, 
        { id: 2, title: 'Bar', },
    ], 
    formData: { hello: 'World', lorum: 'Ipsum', }
}

class PdfSample {

    async loadTemplate(templatePath) {
        const templateFile = fs.readFileSync(templatePath);
        const templateDoc = this.templateDoc = await PDFDocument.load(templateFile);
        this.templateForm = templateDoc.getForm();
    }

    async initDoc() {
        this.resultDoc = await PDFDocument.create();
    }

    fillTextField(fieldName, value) {
        const field = this.templateForm.getField(fieldName);
        field.setText(value);
    }

    get formFieldsNames() {
        return this.templateForm.getFields().map(field => field.getName());
    }

    async addDocPage() {
        const newPageBytes = await this.templateDoc.save();

        const newPage = await PDFDocument.load(newPageBytes);

        const [pageToAdd] = await this.resultDoc.copyPages(newPage, [0]);
        return this.resultDoc.addPage(pageToAdd);
    }

    async saveResult(outputPath) {

        const pdfBytes = await this.resultDoc.save();
        fs.writeFileSync(outputPath, pdfBytes);

    }
}

const pdfSample = new PdfSample();

(async () => {
    await pdfSample.initDoc();
    await pdfSample.loadTemplate('./pdfs_/OoPdfFormExample.pdf');

    console.log(pdfSample.formFieldsNames);

    for (const row of payload.rows) {
        pdfSample.fillTextField('Given Name Text Box', row.title);

        pdfSample.fillTextField('Family Name Text Box', payload.formData.hello);
        pdfSample.fillTextField('House nr Text Box', payload.formData.lorum);

        await pdfSample.addDocPage(pdfSample.templateDoc);
    }

    await pdfSample.saveResult('./pdfs_/result.pdf');
})();

样本表格来自 this site

  • 我没有使用这个基于类的代码,因为我无法让它工作,但答案就在这段代码中。您需要在复制到新页面之前保存页面字节 const newPageBytes = await page.save(); const newPage = await PDFDocument.load(newPageBytes); const [firstPage] = await newDoc.copyPages(newPage, [0]); newDoc.addPage(firstPage)
  • 谢谢。赏金去哪儿了?