go-wkhtmltopdf icon indicating copy to clipboard operation
go-wkhtmltopdf copied to clipboard

Creating PDF files in a loop

Open zaky opened this issue 1 year ago • 4 comments

func main() { for _, v := range []string{"import1.pdf", "import2.pdf", "import3.pdf"} { CreatePDFFile("header.html", "footer.html", "content.html", v) } }

func CreatePDFFile(header, footer, content, output string) error { // Initialize library. if err := pdf.Init(); err != nil { log.Print("fail init", err)

	return err
}
defer pdf.Destroy()
//Insert the content inside the template
// Create object from URL.
object, err := pdf.NewObject(content)
if err != nil {
	log.Print("fail new object", err)
	return err
}
defer object.Destroy()
object.Header.CustomLocation = header
object.Footer.CustomLocation = footer
converter, err = pdf.NewConverter()
if err != nil {
	log.Print("fail new converter", err)
	return err
}

defer converter.Destroy()

converter.PaperSize = pdf.Letter

// Add created objects to the converter.
converter.Add(object)

// Convert objects and save the output PDF document.
outFile, err := os.Create(output)
if err != nil {
	log.Print("fail create", err)
	return err
}
defer outFile.Close()

// Run converter.
if err := converter.Run(outFile); err != nil {
	log.Print("fail run", err)
	return err
}
return nil

}

Runing this code will create 1 valid file, the first one and two invalid files.

zaky avatar Jul 04 '23 17:07 zaky

Hi @zaky

The library needs to be initialized only once, when the application starts. This should work fine:

package main

import (
	"log"
	"os"

	pdf "github.com/adrg/go-wkhtmltopdf"
)

func main() {
	// Initialize library.
	if err := pdf.Init(); err != nil {
		log.Fatal(err)
	}
	defer pdf.Destroy()

	for _, v := range []string{"import1.pdf", "import2.pdf", "import3.pdf"} {
		if err := CreatePDFFile("header.html", "footer.html", "content.html", v); err != nil {
			log.Fatal(err)
		}
	}
}

func CreatePDFFile(header, footer, content, output string) error {
	// Create object from URLs.
	object, err := pdf.NewObject(content)
	if err != nil {
		return err
	}
	defer object.Destroy()

	object.Header.CustomLocation = header
	object.Footer.CustomLocation = footer

	// Create converter.
	converter, err := pdf.NewConverter()
	if err != nil {
		return err
	}
	defer converter.Destroy()

	converter.PaperSize = pdf.Letter

	// Add created objects to the converter.
	converter.Add(object)

	// Convert objects and save the output PDF document.
	outFile, err := os.Create(output)
	if err != nil {
		return err
	}
	defer outFile.Close()

	// Run converter.
	if err := converter.Run(outFile); err != nil {
		return err
	}

	return nil
}

adrg avatar Jul 04 '23 18:07 adrg

Actually the code is part of a web server. What I fount is that if I put the pdf.init/pdf.destroy in main the first request is working fine but not the following requests. From the second request the server jut stock.

zaky avatar Jul 04 '23 20:07 zaky

For usage inside goroutines see the Basic web page to PDF conversion server or the Configurable web page to PDF conversion server examples. Issue https://github.com/adrg/go-wkhtmltopdf/issues/3 is also relevant.

The library must be initialized on the main thread and the conversion must be performed on the main thread as well. This is a known limitation of wkhtmltox, not of this package.

adrg avatar Jul 05 '23 11:07 adrg

I suppose, as mentioned in previous comment, this code block

func init() {
	// Set main function to run on the main thread.
	runtime.LockOSThread()
}

in general cases prevents situation with library stuck

zbykovskyi avatar Jan 07 '24 09:01 zbykovskyi