This repository has been archived on 2026-02-15. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
breakpilot-pwa/admin-v2/ai-compliance-sdk/internal/academy/certificate_pdf.go
Benjamin Admin 70f2b0ae64 refactor: Consolidate standalone services into admin-v2, add new SDK modules
Remove standalone services (ai-compliance-sdk root, developer-portal,
dsms-gateway, dsms-node, night-scheduler) and legacy compliance/dsgvo pages.
Add new SDK pipeline modules (academy, document-crawler, dsb-portal,
incidents, whistleblower, reporting, sso, multi-tenant, industry-templates).
Add drafting engine, legal corpus files (AT/CH/DE), pitch-deck,
blog and Förderantrag pages.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-15 09:05:18 +01:00

153 lines
4.4 KiB
Go

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
// 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", data.CertificateID[:min(12, len(data.CertificateID))]), "", 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
}
func min(a, b int) int {
if a < b {
return a
}
return b
}