package academy import ( "bytes" "fmt" "time" "github.com/jung-kurt/gofpdf" ) // CertificateData holds all data needed to generate a certificate PDF type CertificateData struct { CertificateID string UserName string CourseName string CompanyName string Score int IssuedAt time.Time ValidUntil time.Time } // GenerateCertificatePDF generates a PDF certificate and returns the bytes func GenerateCertificatePDF(data CertificateData) ([]byte, error) { pdf := gofpdf.New("L", "mm", "A4", "") // Landscape A4 pdf.SetAutoPageBreak(false, 0) pdf.AddPage() pageWidth, pageHeight := pdf.GetPageSize() // Background color - light gray pdf.SetFillColor(250, 250, 252) pdf.Rect(0, 0, pageWidth, pageHeight, "F") // Border - decorative pdf.SetDrawColor(79, 70, 229) // Purple/Indigo pdf.SetLineWidth(3) pdf.Rect(10, 10, pageWidth-20, pageHeight-20, "D") pdf.SetLineWidth(1) pdf.Rect(14, 14, pageWidth-28, pageHeight-28, "D") // Header - Company/BreakPilot Logo area companyName := data.CompanyName if companyName == "" { companyName = "BreakPilot Compliance" } pdf.SetFont("Helvetica", "", 12) pdf.SetTextColor(120, 120, 120) pdf.SetXY(0, 25) pdf.CellFormat(pageWidth, 10, companyName, "", 0, "C", false, 0, "") // Title pdf.SetFont("Helvetica", "B", 32) pdf.SetTextColor(30, 30, 30) pdf.SetXY(0, 42) pdf.CellFormat(pageWidth, 15, "SCHULUNGSZERTIFIKAT", "", 0, "C", false, 0, "") // Decorative line pdf.SetDrawColor(79, 70, 229) pdf.SetLineWidth(1.5) lineY := 62.0 pdf.Line(pageWidth/2-60, lineY, pageWidth/2+60, lineY) // "Hiermit wird bescheinigt, dass" pdf.SetFont("Helvetica", "", 13) pdf.SetTextColor(80, 80, 80) pdf.SetXY(0, 72) pdf.CellFormat(pageWidth, 8, "Hiermit wird bescheinigt, dass", "", 0, "C", false, 0, "") // Name pdf.SetFont("Helvetica", "B", 26) pdf.SetTextColor(30, 30, 30) pdf.SetXY(0, 85) pdf.CellFormat(pageWidth, 12, data.UserName, "", 0, "C", false, 0, "") // "die folgende Compliance-Schulung erfolgreich abgeschlossen hat:" pdf.SetFont("Helvetica", "", 13) pdf.SetTextColor(80, 80, 80) pdf.SetXY(0, 103) pdf.CellFormat(pageWidth, 8, "die folgende Compliance-Schulung erfolgreich abgeschlossen hat:", "", 0, "C", false, 0, "") // Course Name pdf.SetFont("Helvetica", "B", 20) pdf.SetTextColor(79, 70, 229) pdf.SetXY(0, 116) pdf.CellFormat(pageWidth, 10, data.CourseName, "", 0, "C", false, 0, "") // Score if data.Score > 0 { pdf.SetFont("Helvetica", "", 12) pdf.SetTextColor(80, 80, 80) pdf.SetXY(0, 130) pdf.CellFormat(pageWidth, 8, fmt.Sprintf("Testergebnis: %d%%", data.Score), "", 0, "C", false, 0, "") } // Bottom section - Dates and Signature bottomY := 148.0 certIDShort := data.CertificateID if len(certIDShort) > 12 { certIDShort = certIDShort[:12] } // Left: Issued Date pdf.SetFont("Helvetica", "", 10) pdf.SetTextColor(100, 100, 100) pdf.SetXY(40, bottomY) pdf.CellFormat(80, 6, fmt.Sprintf("Abschlussdatum: %s", data.IssuedAt.Format("02.01.2006")), "", 0, "L", false, 0, "") // Center: Valid Until pdf.SetXY(pageWidth/2-40, bottomY) pdf.CellFormat(80, 6, fmt.Sprintf("Gueltig bis: %s", data.ValidUntil.Format("02.01.2006")), "", 0, "C", false, 0, "") // Right: Certificate ID pdf.SetXY(pageWidth-120, bottomY) pdf.CellFormat(80, 6, fmt.Sprintf("Zertifikats-Nr.: %s", certIDShort), "", 0, "R", false, 0, "") // Signature line sigY := 162.0 pdf.SetDrawColor(150, 150, 150) pdf.SetLineWidth(0.5) // Left signature pdf.Line(50, sigY, 130, sigY) pdf.SetFont("Helvetica", "", 9) pdf.SetTextColor(120, 120, 120) pdf.SetXY(50, sigY+2) pdf.CellFormat(80, 5, "Datenschutzbeauftragter", "", 0, "C", false, 0, "") // Right signature pdf.Line(pageWidth-130, sigY, pageWidth-50, sigY) pdf.SetXY(pageWidth-130, sigY+2) pdf.CellFormat(80, 5, "Geschaeftsfuehrung", "", 0, "C", false, 0, "") // Footer pdf.SetFont("Helvetica", "", 8) pdf.SetTextColor(160, 160, 160) pdf.SetXY(0, pageHeight-22) pdf.CellFormat(pageWidth, 5, "Dieses Zertifikat wurde elektronisch erstellt und ist ohne Unterschrift gueltig.", "", 0, "C", false, 0, "") pdf.SetXY(0, pageHeight-17) pdf.CellFormat(pageWidth, 5, fmt.Sprintf("Verifizierung unter: https://compliance.breakpilot.de/verify/%s", data.CertificateID), "", 0, "C", false, 0, "") // Generate PDF bytes var buf bytes.Buffer if err := pdf.Output(&buf); err != nil { return nil, fmt.Errorf("failed to generate PDF: %w", err) } return buf.Bytes(), nil }