jung-kurt / gofpdf

A PDF document generator with high level support for text, drawing and images
http://godoc.org/github.com/jung-kurt/gofpdf
MIT License
4.29k stars 772 forks source link

Combining multiple PDFs not working as expected #324

Closed tsawler closed 4 years ago

tsawler commented 4 years ago

I have a slice of strings (called files in the code below), each pointing to a unique PDF that exist on the filesystem. All of the files use the same page size.

The code:

finalPDF := gofpdf.New("P", "mm", "Letter", "./fonts")

for _, x := range files {
    rs := getTemplatePDF(x)
    imp := gofpdi.NewImporter()
    tpl := imp.ImportPageFromStream(finalPDF, &rs, 1, "/MediaBox")
    nrPages := len(imp.GetPageSizes())

    for i := 1; i <= nrPages; i++ {
        finalPDF.AddPage()
        if i > 1 {
            tpl = imp.ImportPageFromStream(finalPDF, &rs, i, "/MediaBox")
        }
        imp.UseImportedTemplate(finalPDF, tpl, 0, 0, 215.9, 0)
    }
}
err = finalPDF.OutputFileAndClose("./tmp/all_status_reports.pdf")

The final PDF, though, consists of nothing more than the contents of the last file in the slice, repeated once for each entry in the slice.

What am I doing wrong?

Many thanks for any help that you can provide.

tsawler commented 4 years ago

Sorry -- I should have included the "getTemplatePDF()" function used in the first snippet:

func getTemplatePDF(x string) io.ReadSeeker {
    infoLog.Println("Opening", x)
    file, err := os.Open(x)
    if err != nil {
        fmt.Println(err)
        return nil
    }

    defer file.Close()

    fileInfo, _ := file.Stat()
    var size = fileInfo.Size()
    infoLog.Println("Size:", size)
    buffer := make([]byte, size)
    file.Read(buffer)

    fileBytes := bytes.NewReader(buffer)
    return fileBytes

}
jung-kurt commented 4 years ago

See the section The range clause in this article. Depending on how files is typed, you may need to have a construction like

for j, _ := range files {
  rs := getTemplatePDF(files[j])
  // ...
}

instead of

for _, x := range files {
  rs := getTemplatePDF(x)
  // ...
}
tsawler commented 4 years ago

Thanks. Files just just a slice of strings. I tried this anyway, but got the same results. I have manually checked the PDFs to be combined, and they are unique. I also write the file name to the console in getTemplatePDF(), and the correct file is being opened.

I'm stumped.

jung-kurt commented 4 years ago

I don't think you want to take the address of rs (an io.ReadSeeker interface) in the line

tpl := imp.ImportPageFromStream(finalPDF, &rs, 1, "/MediaBox")

but I am not sure if that is the source of the problem.

tsawler commented 4 years ago

Thanks. I need a break from glowing rectangles, but will get back to this tonight. I'll let you know how I make out. I strongly suspect that my problem is the result of insufficient sleep and an excess of caffeine...

tsawler commented 4 years ago

So I finally figured it out.

This suggestion:

I don't think you want to take the address of rs (an io.ReadSeeker interface) in the line

tpl := imp.ImportPageFromStream(finalPDF, &rs, 1, "/MediaBox")

is much appreciated, but ImportPageFromStream expects the address of an io.ReadSeeker. However, this is the line that had the problem. Even though tpl is being set for each iteration of the loop through the slice of strings, only the last tpl is actually used. I had to dig through the code for awhile to figure that out.

Anyway, I just took a different approach to building the file. It was faster, and simpler than buiding and using some data structure to keep track of hundreds of templates.

Thanks for your assistance, though.