Developers , Uncategorized

Generating PDFs with Go

How to generate pdfs with Golang

I recently had to work on a Go project where PDFs had to be generated, with custom data, based on a template.

After considering a few options, I decided to go with wkhtmltopdf’s wrapper, since this a solid and widely used open source library and I would only be using a Go layer on top of it.

Also, this would allow the template to be maintained in HTML, which would hopefully end up in less headaches than creating PDF’s with a lower level generator kind of thing.

Cut to the chase

Not directly related… But I defined a interface for this service so it could be mocked, and so on.

package domain

// PDFService represents the interface of a pdf generation service
type PDFService interface {
	GeneratePDF(data *SomeModel) ([]byte, error)

Here’s the service itself. It quite self explanatory but still, I’ve added comments on meaningful steps.

It essentially consists of first generating an HTML file based on a template and then pass that HTML to wkhtmltopdf.

package pdf

import (
	domain "the-project"

type PDFService struct {}

func NewPDFService() *PDFService {
	return &PDFService{}

func (p PDFService) GeneratePDF(data *domain.SomeModel) ([]byte, error) {
	var templ *template.Template
	var err error

	// use Go's default HTML template generation tools to generate your HTML
	if templ, err = template.ParseFiles("pdf-template.html"); err != nil {
		return nil, err

	// apply the parsed HTML template data and keep the result in a Buffer
	var body bytes.Buffer
	if err = templ.Execute(&body, data); err != nil {
		return nil, err

	// initalize a wkhtmltopdf generator
	pdfg, err := wkhtmltopdf.NewPDFGenerator()
	if err != nil {
		return nil, err

	// read the HTML page as a PDF page
	page := wkhtmltopdf.NewPageReader(bytes.NewReader(body.Bytes()))

	// enable this if the HTML file contains local references such as images, CSS, etc.

	// add the page to your generator

	// manipulate page attributes as needed

	// magic
	err = pdfg.Create()
	if err != nil {
		return nil, err

	return pdfg.Bytes(), nil


Say you want to serve the PDF in an API response:

func (h *YourHandler) GetPDF(w http.ResponseWriter, r *http.Request) {
	// .....

	pdfBytes, err := h.pdfService.GeneratePDF(&data)
	if err != nil {
		httputil.RespondInternalError(w, r, err)

	w.Header().Set("Content-Disposition", "attachment; filename=kittens.pdf")
	w.Header().Set("Content-Type", "application/pdf")

Author: Diogo Simões, Tech Lead at Cloudoki.

Related Articles

AI Developers SaaS Security

BNP Paribas Fortis Hackathons

Unlocking the Future: Exploring APIs, IoT, and AI through Hackathons at BNP Paribas Fortis...

Read more
Developers MetaMask

Implement Login with MetaMask — Javascript

No email, no username, no password. Login with your wallet only....

Read more
Culture Developers

The Secret Sauce of Cloudoki: Stellar Staffing Strategies

Discover the key to success in the software development industry with Cloudoki's staffing ...

Read more
AI Developers SaaS

Cut the BullSaaS on AI: Insights from Cloudoki’s Founder

Explore the transformative power of AI integration in the SaaS industry through an engagin...

Read more