Generating dynamic PDFs in Go can be straightforward when using libraries like gofpdf
. However, many applications require a more complex layout, including a header, a logo, and formatted text. In this article, we’ll see how to create a REST endpoint in Go that produces dynamic PDFs with Gin, including graphic elements and advanced formatting.
Prerequisites
Make sure you have Go installed and add the necessary dependencies:
go get github.com/gin-gonic/gin
go get github.com/jung-kurt/gofpdf
Application structure
The application will include:
- A
POST
endpoint that receives data for the PDF. - A function to generate the PDF with an advanced layout.
- A custom header with a logo and date.
- Formatted text with paragraphs.
Example code
package main
import (
"bytes"
"net/http"
"time"
"github.com/gin-gonic/gin"
"github.com/jung-kurt/gofpdf"
)
type PDFRequest struct {
Title string `json:"title"`
Content []string `json:"content"`
}
func generatePDF(title string, content []string) ([]byte, error) {
pdf := gofpdf.New("P", "mm", "A4", "")
pdf.SetMargins(20, 20, 20)
pdf.AddPage()
addHeader(pdf, title)
addBody(pdf, content)
var buf bytes.Buffer
if err := pdf.Output(&buf); err != nil {
return nil, err
}
return buf.Bytes(), nil
}
func addHeader(pdf *gofpdf.Fpdf, title string) {
pdf.ImageOptions("logo.png", 10, 10, 30, 0, false, gofpdf.ImageOptions{}, 0, "")
pdf.SetY(15)
pdf.SetX(45)
pdf.SetFont("Arial", "B", 20)
pdf.Cell(100, 10, title)
pdf.SetFont("Arial", "", 12)
pdf.SetXY(160, 15)
pdf.CellFormat(40, 10, time.Now().Format("02/01/2006"), "", 0, "R", false, 0, "")
pdf.Ln(20)
pdf.Line(10, pdf.GetY(), 200, pdf.GetY())
pdf.Ln(10)
}
func addBody(pdf *gofpdf.Fpdf, paragraphs []string) {
pdf.SetFont("Arial", "", 12)
for _, p := range paragraphs {
pdf.MultiCell(0, 8, p, "", "", false)
pdf.Ln(3)
}
}
func pdfHandler(c *gin.Context) {
var req PDFRequest
if err := c.ShouldBindJSON(\&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
pdfBytes, err := generatePDF(req.Title, req.Content)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to generate PDF"})
return
}
c.Header("Content-Type", "application/pdf")
c.Header("Content-Disposition", `inline; filename="custom-report.pdf"`)
c.Data(http.StatusOK, "application/pdf", pdfBytes)
}
func main() {
router := gin.Default()
router.POST("/generate-pdf", pdfHandler)
router.Run(":8080")
}
Testing the endpoint
You can test the service with a curl command, saving the generated PDF directly:
curl -X POST http://localhost:8080/generate-pdf \
-H "Content-Type: application/json" \
-d '{
"title": "Monthly Report",
"content": [
"Welcome to our monthly report.",
"Sales increased by 15% compared to last month.",
"Thank you for your attention."
]
}' --output report.pdf
Final considerations
With gofpdf
and Gin, it’s possible to generate PDFs entirely in memory, without writing them to disk, providing a fast and personalized response to the client. The example shown can be extended with tables, footers, custom fonts, and multilingual content. For complex HTML layouts, you can consider alternatives like unipdf
or chromedp
for HTML-to-PDF rendering.