PDF generation in GO

Thinley Norbu
Nov 26, 2020 · 3 min read
PDF generation in GOlang

Previously, we talked about generating PDF in React. While it works fine, there are various scenarios where generating PDF in React might not be a viable solution. So in this article, I want to demonstrate a quick way to generate PDF in GO.

I came across a lot of packages in GO for PDF generation after some research viz. gopdf, gofpdf, go-wkhtmltopdf, etc. While packages like gopdf and gofpdf offer us a solution for PDF generation but I found it takes lot of work to generate PDF with complex design.

So for this article, we will only talk about go-wkhtmltopdf because I found it easy to use. Basically, what we do is use html template to generate a PDF.

We can install this package by running the following command.

go get -u github.com/SebastiaanKlippert/go-wkhtmltopdf

Bear in mind that this package is a Golang command line wrapper for wkhtmltopdf. So we got to have wkhtmltopdf installed in our system. We can have it installed easily by running following command.

sudo apt-get install xvfb libfontconfig wkhtmltopdf

Now we are ready to do some PDF generation. Have a look at below code.

package pdfGenerator

import (
"bytes"
"html/template"
"io/ioutil"
"log"
"os"
"strconv"
"time"

"github.com/SebastiaanKlippert/go-wkhtmltopdf"
)

//pdf requestpdf struct
type RequestPdf struct {
body string
}

//new request to pdf function
func NewRequestPdf(body string) *RequestPdf {
return &RequestPdf{
body: body,
}
}

//parsing template function
func (r *RequestPdf) ParseTemplate(templateFileName string, data interface{}) error {

t, err := template.ParseFiles(templateFileName)
if err != nil {
return err
}
buf := new(bytes.Buffer)
if err = t.Execute(buf, data); err != nil {
return err
}
r.body = buf.String()
return nil
}

//generate pdf function
func (r *RequestPdf) GeneratePDF(pdfPath string) (bool, error) {
t := time.Now().Unix()
// write whole the body
err1 := ioutil.WriteFile("storage/"+strconv.FormatInt(int64(t), 10)+".html", []byte(r.body), 0644)
if err1 != nil {
panic(err1)
}

f, err := os.Open("storage/" + strconv.FormatInt(int64(t), 10) + ".html")
if f != nil {
defer f.Close()
}
if err != nil {
log.Fatal(err)
}

pdfg, err := wkhtmltopdf.NewPDFGenerator()
if err != nil {
os.Remove("storage/" + strconv.FormatInt(int64(t), 10) + ".html")
log.Fatal(err)
}

pdfg.AddPage(wkhtmltopdf.NewPageReader(f))

pdfg.PageSize.Set(wkhtmltopdf.PageSizeA4)

pdfg.Dpi.Set(300)

err = pdfg.Create()
if err != nil {
log.Fatal(err)
}

err = pdfg.WriteFile(pdfPath)
if err != nil {
log.Fatal(err)
}
os.Remove("storage/" + strconv.FormatInt(int64(t), 10) + ".html")

return true, nil
}

We have some setup done for functions that uses wkhtmltopdf. We can use this functions as our need in our app flow.

Next thing is our html template. Create a html and add required CSS. We can leave space for our data by following syntax.

{{.Data}}

This way, we can replace this variable by actual data in our document.

package main

import (
"fmt"
"os"

u "github.comm/gofpdftable/pdfGenerator"
)

func main() {

r := u.NewRequestPdf("")

//html template path
templatePath := "templates/sample.html"

//path for download pdf
outputPath := "./storage/pdf.pdf"


//html template data
templateData := struct {
Data string
}{
Data: "data"
}

if err := r.ParseTemplate(templatePath, templateData); err == nil {
ok, _ := r.GeneratePDF(outputPath)
fmt.Println(ok, "pdf generated successfully")
} else {
fmt.Println(err)
}
}

We can supply data to our template by this way. Now, the Data’s string “data” will be put to the place where {{.Data}} is kept in our html template.

Now run main.go, we should have PDF generated from our html template.

We can also make a loop inside our html template to make the content dynamic. We can follow this syntax to do so.

{{ range $key, $value := $value }}<div>{{ $value }}</div>{{ end }}

This way, we can have our PDF generated in a easy and non tiring way. What is your thoughts on this method? Any suggestions and feedback are welcome. Happy coding!

wesionaryTEAM

Visionary Development Team. We ❤︎ Technology!