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

使用gofpdi库合并PDF遇异常:字节流导入后页面内容重复

gofpdi字节流合并PDF重复内容问题排查

我尝试用gofpdi库通过字节流合并两个PDF,结果生成的文件两页都是第二个PDF的内容;但换成从文件导入第二个PDF时,结果就正常。

问题代码(字节流导入版)

func test(pdfm bytes.Buffer, m pdf.Maroto, pdf2 bytes.Buffer) {
    pdf := gofpdf.New("P", "mm", "A4", "")

    w, h := m.GetPageSize()

    importer := gofpdi.NewImporter()
    rs := io.ReadSeeker(bytes.NewReader(pdfm.Bytes()))
    tpl1 := importer.ImportPageFromStream(pdf, &rs, 1, "/MediaBox")
    pdf.AddPage()
    importer.UseImportedTemplate(pdf, tpl1, 0, 0, w, h)     
    
    importer2 := gofpdi.NewImporter()
    rs2 := io.ReadSeeker(bytes.NewReader(pdf2.Bytes())) // 修正原代码笔误:bytes()改为Bytes()

    tpl2 := importer2.ImportPageFromStream(pdf, &rs2, 1, "/MediaBox")
    pdf.AddPage()
    importer.UseImportedTemplate(pdf, tpl2, 0, 0, w, h) // 核心错误:使用了第一个importer实例

    err := pdf.OutputFileAndClose("pdfs/example.pdf")
    if err != nil {
        fmt.Errorf("error in generating the output pdf file: %w", err)
    }
}

正常代码(文件导入版)

func test(pdfm bytes.Buffer, m pdf.Maroto, pdf2 bytes.Buffer) {
    pdf := gofpdf.New("P", "mm", "A4", "")

    w, h := m.GetPageSize()

    importer := gofpdi.NewImporter()
    rs := io.ReadSeeker(bytes.NewReader(pdfm.Bytes()))
    tpl1 := importer.ImportPageFromStream(pdf, &rs, 1, "/MediaBox")
    pdf.AddPage()
    importer.UseImportedTemplate(pdf, tpl1, 0, 0, w, h) 
    
    pdf.AddPage()
    importer2 := gofpdi.NewImporter()

    tpl2 := importer2.ImportPage(pdf, "test.pdf", 1, "/MediaBox")
    pdf.AddPage()
    importer.UseImportedTemplate(pdf, tpl2, 0, 0, w, h)

    err := pdf.OutputFileAndClose("pdfs/example.pdf")
    if err != nil {
        fmt.Errorf("error in generating the output pdf file: %w", err)
    }
}

问题分析与修正

  • 核心错误:模板使用实例不匹配
    gofpdi的Importer实例内部会维护导入PDF的资源映射表,每个PDF的模板必须和对应的Importer实例绑定使用。字节流版本中,第二个PDF的模板tpl2是通过importer2导入的,但调用UseImportedTemplate时却用了第一个实例importer,导致资源引用混乱,最终重复渲染了第二个PDF的内容。

  • 次要问题:字节流处理冗余
    bytes.NewReader返回的*bytes.Reader已经实现了io.ReadSeeker接口,不需要额外赋值给io.ReadSeeker类型变量再取地址,可简化写法。

修正后的字节流合并代码

func test(pdfm bytes.Buffer, m pdf.Maroto, pdf2 bytes.Buffer) {
    pdf := gofpdf.New("P", "mm", "A4", "")
    w, h := m.GetPageSize()

    // 处理第一个PDF
    importer1 := gofpdi.NewImporter()
    rs1 := bytes.NewReader(pdfm.Bytes())
    tpl1 := importer1.ImportPageFromStream(pdf, &rs1, 1, "/MediaBox")
    pdf.AddPage()
    importer1.UseImportedTemplate(pdf, tpl1, 0, 0, w, h)

    // 处理第二个PDF
    importer2 := gofpdi.NewImporter()
    rs2 := bytes.NewReader(pdf2.Bytes())
    tpl2 := importer2.ImportPageFromStream(pdf, &rs2, 1, "/MediaBox")
    pdf.AddPage()
    importer2.UseImportedTemplate(pdf, tpl2, 0, 0, w, h) // 使用对应的importer2实例

    if err := pdf.OutputFileAndClose("pdfs/example.pdf"); err != nil {
        fmt.Printf("生成PDF失败:%v\n", err)
    }
}

另外注意原代码中pdf2.bytes()是拼写错误,应改为pdf2.Bytes(),这也是隐性问题之一。

内容的提问来源于stack exchange,提问作者Pratik Saha Joy

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 12:15:42