# How to generate PDF in Go

In this article I am going to show you one way of generating PDF documents using Go. Generating a PDF document is a common need in many applications.

We are building an invoice management application and one of the requirements is the to generate a PDF.

This blog post is part of the [**Building a Web App with Golang**](https://blog.gkomninos.com/series/webapp-using-golang) .

## From HTML to PDF

In order to create a PDF document we will first create an HTML document and use the [weasyprint](https://doc.courtbouillon.org/weasyprint/stable/) tool to generate the PDF from the HTML .

There are some golang libraries out there but I found that weasyprint works very nice and haven't run into any issue yet.

Let's first install weasyprint in our machine and see how it works.

Please see the detailed instructions in weasyprint's [documentation](https://doc.courtbouillon.org/weasyprint/stable/first_steps.html) .

Here is how you can install by utilizing python PIP:

```bash
pip install weasyprint
```

In the documentation there are instructions for many operating systems. Please check there if you run into issues with the above.

Now let's try a demo to verify that it works.

### Writing our pdf generator

First let's create a new git branch

```bash
git checkout -b invoice-generation
```

Then create a folder named `templates` .

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1718860309533/4aaa155c-ef70-4fd4-b87a-7d32c89db745.png align="center")

Ins

Create an HTML file named invoice.pdf.html

```xml
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Invoice 1001/24</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            margin: 20px;
            display: flex;
            justify-content: center;
        }
        .container {
            width: 80%;
            max-width: 800px;
        }
        .header {
            display: flex;
            justify-content: space-between;
            margin-bottom: 20px;
        }
        .seller {
            text-align: right;
        }
        .invoice-info {
            text-align: right;
            margin-bottom: 20px;
        }
        .buyer {
            margin-top: 20px;
        }
        .line-items {
            margin-bottom: 20px;
        }
        .line-items table {
            width: 100%;
            border-collapse: collapse;
        }
        .line-items th, .line-items td {
            border: 1px solid #000;
            padding: 8px;
            text-align: left;
        }
        .totals {
            margin-bottom: 20px;
        }
        .totals table {
            width: 100%;
            border-collapse: collapse;
        }
        .totals td {
            padding: 8px;
            text-align: right;
        }
        .totals .total {
            font-weight: bold;
        }
        .footer {
            border-top: 1px solid #000;
            padding-top: 20px;
            text-align: left;
        }
    </style>
</head>
<body>

    <div class="container">
        <div class="header">
            <div class="buyer">
                <h3>Bill To:</h3>
                <p>Buyer LTD</p>
                <p>Buyer Address</p>
                <p>VAT: Vat-number</p>
            </div>
            <div class="seller">
                <h2>Seller</h2>
                <p>Seller Address</p>
                <p>Seller Address2</p>
                <p>T.I.C. No: Seller Tax Number</p>
                <p>V.A.T. No: Seller Vat Number</p>
            </div>
        </div>

        <div class="invoice-info">
            <h2>Tax Invoice No.: 1001/24</h2>
            <p>Invoice Date: 02/01/2024</p>
        </div>

        <div class="line-items">
            <table>
                <tr>
                    <th>Description</th>
                    <th>Amount (€)</th>
                </tr>
                <tr>
                    <td>Service Description</td>
                    <td>1,000.00</td>
                </tr>
            </table>
        </div>

        <div class="totals">
            <table>
                <tr>
                    <td>Fees</td>
                    <td>€1,000.00</td>
                </tr>
                <tr>
                    <td>VAT</td>
                    <td>€0</td>
                </tr>
                <tr class="total">
                    <td>Total</td>
                    <td>€1,000.00</td>
                </tr>
            </table>
        </div>

        <div class="footer">
            <h3>Invoice Payable within 14 days to:</h3>
            <p>Bank Name</p>
            <p>IBAN: IBAN</p>
            <p>BIC: Bic Number</p>
            <p>Account Owner Name</p>
        </div>
    </div>

</body>
</html>
```

**DISCLAIMER: I used AI (ChatGPT) to assist creating the HTML**

Let's see how it looks:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1718861159803/b1a17e02-84b7-4ffe-b40e-29a5482fb9c2.png align="center")

The HTML and CSS needs some polishing, but let's continue.

Now we try to create a pdf from the command line to verify that weasyprint works .

```bash
weasyprint templates/invoice.pdf.html invoice.pdf
```

this will create a pdf document. It looks like that:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1718861306216/f2e369a3-acf5-42b3-ad67-e36912d98946.png align="center")

### Creating a Go package that wraps the weasyprint command

Create a folder `pkg/pdfgen`

```bash
mkdir -p pkg/pdfgen
```

and add a file `pdfgen.go`

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1718861459795/ec42d453-8d67-4ebe-bdc0-962b1d398a8a.png align="center")

Now in our pdfgen.go:

```go
package pdfgen

import (
	"context"
	"io"
	"os"
	"os/exec"
)

func Generate(ctx context.Context, w io.Writer, html []byte) error {
	tempHTMLFile, err := os.CreateTemp("", "*.html")
	if err != nil {
		return err
	}

	defer os.Remove(tempHTMLFile.Name())

	if _, err = tempHTMLFile.Write(html); err != nil {
		return err
	}

	if err = tempHTMLFile.Close(); err != nil {
		return err
	}

	tempPDFFile, err := os.CreateTemp("", "*.pdf")
	if err != nil {
		return err
	}

	tempPDFFilePath := tempPDFFile.Name()
	defer os.Remove(tempPDFFilePath)

	if err = tempPDFFile.Close(); err != nil {
		return err
	}

	cmd := exec.CommandContext(ctx, "weasyprint", tempHTMLFile.Name(), tempPDFFilePath)

	err = cmd.Run()

	if err != nil {
		return err
	}

	pdfFile, err := os.Open(tempPDFFilePath)
	if err != nil {
		return err
	}

	defer pdfFile.Close()

	if _, err = io.Copy(w, pdfFile); err != nil {
		return err
	}

	return nil
}
```

Let's explain what we did:

the function `Generate` accepts as the context and

and io.Writer and a slice of bytes.

* The slice of bytes contains the HTML we want to convert to PDF
    

The io.Writer is the "place" that we are going to write the contents of the PDF

> We chose to use an `io.Writer` since this will make our package for versatile. We can pass in a file an http.ResponseWriter or everything that implements that interface

The above function works as follows:

1. creates a temporary file and creates a temporary file with our html contents
    
2. creates another temporary file (just to get the name) and removes it
    
3. invokes the weasyprint command
    
4. reads the file that weasyprint created (the PDF) and copies the contents to the io.Writer we passed as argument
    

> Step 2 might be replaced by creating a random string. But then we have to compute the temporary directory which is not the same for all systems or configurations. Since Go handles these for us we just (ab)use that .

Now let's write a unit test.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1718862162980/2867e7c9-7087-47ab-91ab-e7a3e302b6a5.png align="center")

let's run our tests:

```bash
go test -v ./...
```

---

Let's commit our work so far

```bash
git add pkg/ templates/
git commit -m "library that generates pdf from html"
```

> We haven't used our new package in our freelance invoice management project yet. We only wrote a library and we will utilize it in the next article

Find the code in the article's branch in [github](https://github.com/gosom/freelance-invoice-hub/tree/invoice-generation)

## Conclusion

In this article we created a small go package that will help to generate PDFs in Golang. Basically, we just wrote a wrapper over a command line tool that is available in most platforms.

I found that creating PDF this way works very well in Go. There are some Go libraries like [pdfcpu](https://github.com/pdfcpu/pdfcpu) but for the purpose of creating a PDF out of an HTML I found that weasyprint works very well.

In the next article we will integrate our newly created pdfgen package in our application in order to generate PDFs from our invoices.

❤️ Follow me on [X](https://x.com/gkomdev) or [LinkedIn](http://www.linkedin.com/in/georgios-komninos-172508147)

<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">Do you know a better way to generate PDF in Go?</div>
</div>
