Generare PDF dinamici con Go e Gin

La generazione di PDF dinamici in Go può essere semplice quando si utilizzano librerie come gofpdf. Tuttavia, per molte applicazioni è necessario un layout più complesso, comprendente un header, un logo e testo formattato. In questo articolo vedremo come creare un endpoint REST in Go che produce PDF in modo dinamico con Gin, includendo elementi grafici e formattazione avanzata.

Prerequisiti

Assicurati di avere Go installato e aggiungi le dipendenze necessarie:

go get github.com/gin-gonic/gin
go get github.com/jung-kurt/gofpdf

Struttura dell'applicazione

L'applicazione prevede:

  • Un endpoint POST che riceve i dati per il PDF.
  • Una funzione per generare il PDF con layout avanzato.
  • Header personalizzato con logo e data.
  • Testo formattato con paragrafi.

Codice di esempio

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")
}

Test dell'endpoint

Puoi testare il servizio con un comando curl, salvando direttamente il PDF generato:

curl -X POST http://localhost:8080/generate-pdf \
-H "Content-Type: application/json" \
-d '{
  "title": "Report Mensile",
  "content": [
    "Benvenuto al nostro report mensile.",
    "Le vendite sono aumentate del 15% rispetto al mese scorso.",
    "Grazie per l’attenzione."
  ]
}' --output report.pdf

Considerazioni finali

Con gofpdf e Gin è possibile generare PDF direttamente in memoria, senza scriverli su disco, offrendo così una risposta rapida e personalizzata al client. L'esempio mostrato può essere esteso con tabelle, piè di pagina, font personalizzati e contenuti multilingua. Per layout HTML complessi si possono valutare soluzioni alternative come unipdf o chromedp per il rendering HTML-to-PDF.

Torna su