Hello, I'm Shrijith. I'm building git-lrc, an AI code reviewer that runs on every commit. It is free, unlimited, and source-available on Github. Star Us to help devs discover the project. Do give it a try and share your feedback for improving the product.
PDF generation isn't a one-size-fits-all problem.Depending on whether you're building simple invoices, complex reports, beautiful exports, or dynamic UIs turned into PDFs, there are severalsolid approaches you can use in Go.
This article covers the main practical methods with complete examples, pros/cons, and guidance on when to pick what.
Why Generate PDFs in Go?
- Full backend control — no SaaS dependency.
- Low latency — avoid extra API hops.
- Customization — adjust layouts, formats, and security options.
- Automation — dynamically build content from your services.
| Advantage | Reason |
|---|---|
| Privacy | No third-party data leaks |
| Cost savings | No per-document fees |
| Speed | Generate on demand |
Native Go Libraries for PDF Generation
1. Using gofpdf for Basic PDFs
gofpdf is the classic choice: pure Go, dependency-free.
Example:
package main
import (
"github.com/jung-kurt/gofpdf"
)
func main() {
pdf := gofpdf.New("P", "mm", "A4", "")
pdf.AddPage()
pdf.SetFont("Arial", "B", 16)
pdf.Cell(40, 10, "Hello, gofpdf!")
pdf.OutputFileAndClose("basic_gofpdf.pdf")
}
Output: A simple one-line "Hello" PDF.
| Pros | Cons |
|---|---|
| Pure Go | Manual layout effort |
| No external tools needed | Limited complex layout support |
2. Using unidoc/unipdf for High-End PDFs
unipdf is a commercial-grade library.
It's powerful, feature-rich, and paid for production — but free for personal and trial use.
Example:
package main
import (
"github.com/unidoc/unipdf/v3/model"
"log"
"os"
)
func main() {
pdf := model.NewPdfWriter()
f, err := os.Create("unipdf_output.pdf")
if err != nil {
log.Fatal(err)
}
defer f.Close()
err = pdf.Write(f)
if err != nil {
log.Fatal(err)
}
}
Pros:
- Vector graphics, annotations, encryption.
- Table/grid helpers.
- Commercial support.
Cons:
- License required for production.
Link: UniPDF Documentation
3. Using pdfcpu for PDF Manipulation
pdfcpu is more focused on editing, processing, and validating PDFs, but it can generate simple PDFs too.
Example:
package main
import (
"github.com/pdfcpu/pdfcpu/pkg/api"
"log"
)
func main() {
err := api.CreateDemoPDF("pdfcpu_demo.pdf", "", nil)
if err != nil {
log.Fatal(err)
}
}
Pros:
- Splitting, merging, watermarking PDFs.
- CLI and library mode.
Cons:
- Not designed for full creative layout generation.
Link: pdfcpu Documentation
Template-Based Generation: Go → LaTeX → PDF
For professional typesetting, LaTeX is still king.
Generate .tex files dynamically in Go, compile using pdflatex.
Example:
package main
import (
"os"
"os/exec"
)
func main() {
content := `\documentclass{article}
\begin{document}
Hello, \textbf{LaTeX} World!
\end{document}`
os.WriteFile("output.tex", []byte(content), 0644)
cmd := exec.Command("pdflatex", "output.tex")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
panic(err)
}
}
| Pros | Cons |
|---|---|
| Stunning typography | Requires LaTeX installed |
| Advanced features (TOC, BibTeX) | Learning curve |
Link: Quick Start with LaTeX
Markdown + Pandoc: Flexible and Clean
If you like Markdown, you can generate it dynamically and pipe it through Pandoc to create PDFs.
Example:
package main
import (
"os"
"os/exec"
)
func main() {
markdown := `# Auto Report
This report was generated by Go and Pandoc.`
os.WriteFile("report.md", []byte(markdown), 0644)
cmd := exec.Command("pandoc", "report.md", "-o", "report.pdf")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
panic(err)
}
}
| Pros | Cons |
|---|---|
| Easy content authoring | Needs Pandoc installed |
| Good support for tables, links, images | Slightly heavier pipeline |
Link: Pandoc Markdown Syntax
HTML to PDF: Chrome Headless, wkhtmltopdf
If you already have HTML layouts (e.g., templates, dashboards), rendering HTML into PDF makes perfect sense.
Using Headless Chrome (via chromedp)
chromedp lets you programmatically control Chrome.
Example:
package main
import (
"context"
"io/ioutil"
"log"
"time"
"github.com/chromedp/chromedp"
)
func main() {
ctx, cancel := chromedp.NewContext(context.Background())
defer cancel()
var pdfBuf []byte
err := chromedp.Run(ctx,
chromedp.Navigate("https://example.com"),
chromedp.Sleep(2*time.Second), // wait for page load
chromedp.ActionFunc(func(ctx context.Context) error {
var err error
pdfBuf, _, err = page.PrintToPDF().Do(ctx)
return err
}),
)
if err != nil {
log.Fatal(err)
}
ioutil.WriteFile("chrome_output.pdf", pdfBuf, 0644)
}
| Pros | Cons |
|---|---|
| Full CSS, JS, dynamic pages | Heavy runtime (Chrome) |
Link: chromedp PrintToPDF
Using wkhtmltopdf
wkhtmltopdf is a simpler, mature tool: turn any HTML file into a PDF without needing full Chrome.
Example:
package main
import (
"os/exec"
"log"
)
func main() {
cmd := exec.Command("wkhtmltopdf", "https://example.com", "output.pdf")
cmd.Stdout = nil
cmd.Stderr = nil
err := cmd.Run()
if err != nil {
log.Fatal(err)
}
}
| Pros | Cons |
|---|---|
| Lightweight | Not always perfect with complex JS |
Link: wkhtmltopdf Manual
Quick Comparison of All Methods
| Method | Best For | Needs External Tools? | Difficulty |
|---|---|---|---|
gofpdf |
Simple static PDFs | No | Easy |
unipdf |
Complex dynamic documents | No (license needed) | Medium |
pdfcpu |
PDF processing and editing | No | Easy |
| Go → LaTeX → PDF | High-quality, complex typesetting | Yes (LaTeX) | Medium-Hard |
| Go → Markdown → Pandoc → PDF | Lightweight reports, docs | Yes (Pandoc) | Medium |
| Headless Chrome | Full web page snapshots | Yes (Chrome) | Medium |
| wkhtmltopdf | Simple HTML to PDF exports | Yes | Easy-Medium |
Choosing the Right Method
| Situation | Recommended Option |
|---|---|
| Static invoices, simple docs | gofpdf |
| Long reports, academic documents | LaTeX + pdflatex |
| Blog-like or documentation exports | Markdown + Pandoc |
| HTML dashboard snapshots | chromedp or wkhtmltopdf |
| Need to manipulate existing PDFs | pdfcpu |
| Premium enterprise-grade needs | unipdf |
Final Thoughts
The right choice depends heavily on your:
- Required layout complexity
- Tolerance for installing external tools
- Performance and runtime footprint
When in doubt, start simple (like gofpdf) and scale complexity only when necessary.
*AI agents write code fast. They also silently remove logic, change behavior, and introduce bugs -- without telling you. You often find out in production.
git-lrc fixes this. It hooks into git commit and reviews every diff before it lands. 60-second setup. Completely free.*
Any feedback or contributors are welcome! It's online, source-available, and ready for anyone to use.
⭐ Star it on GitHub:
HexmosTech
/
git-lrc
Free, Unlimited AI Code Reviews That Run on Commit
AI agents write code fast. They also silently remove logic, change behavior, and introduce bugs -- without telling you. You often find out in production.
git-lrc fixes this. It hooks into git commit and reviews every diff before it lands. 60-second setup. Completely free.
See It In Action
See git-lrc catch serious security issues such as leaked credentials, expensive cloud operations, and sensitive material in log statements
git-lrc-intro-60s.mp4
Why
- 🤖 AI agents silently break things. Code removed. Logic changed. Edge cases gone. You won't notice until production.
- 🔍 Catch it before it ships. AI-powered inline comments show you exactly what changed and what looks wrong.
- 🔁 Build a habit, ship better code. Regular review → fewer bugs → more robust code → better results in your team.
- 🔗 Why git? Git is universal. Every editor, every IDE, every AI…
Top comments (0)