package pdf import ( "os" "path/filepath" "github.com/jung-kurt/gofpdf/v2" "go.uber.org/zap" ) // FontManager 字体管理器 type FontManager struct { logger *zap.Logger chineseFontName string chineseFontLoaded bool watermarkFontName string watermarkFontLoaded bool } // NewFontManager 创建字体管理器 func NewFontManager(logger *zap.Logger) *FontManager { return &FontManager{ logger: logger, chineseFontName: "ChineseFont", watermarkFontName: "WatermarkFont", } } // LoadChineseFont 加载中文字体到PDF func (fm *FontManager) LoadChineseFont(pdf *gofpdf.Fpdf) bool { if fm.chineseFontLoaded { return true } fontPaths := fm.getChineseFontPaths() if len(fontPaths) == 0 { // 字体文件不存在,使用系统默认字体,不记录警告 return false } // 尝试加载字体 for _, fontPath := range fontPaths { if fm.tryAddFont(pdf, fontPath, fm.chineseFontName) { fm.chineseFontLoaded = true return true } } // 无法加载字体,使用系统默认字体,不记录警告 return false } // LoadWatermarkFont 加载水印字体到PDF func (fm *FontManager) LoadWatermarkFont(pdf *gofpdf.Fpdf) bool { if fm.watermarkFontLoaded { return true } fontPaths := fm.getWatermarkFontPaths() if len(fontPaths) == 0 { return false } // 尝试加载字体 for _, fontPath := range fontPaths { if fm.tryAddFont(pdf, fontPath, fm.watermarkFontName) { fm.watermarkFontLoaded = true return true } } return false } // tryAddFont 尝试添加字体(统一处理中文字体和水印字体) func (fm *FontManager) tryAddFont(pdf *gofpdf.Fpdf, fontPath, fontName string) bool { defer func() { if r := recover(); r != nil { // 静默处理,不记录日志 } }() // 检查文件是否存在 if _, err := os.Stat(fontPath); err != nil { return false } // 将相对路径转换为绝对路径(gofpdf在Output时需要绝对路径) absFontPath, err := filepath.Abs(fontPath) if err != nil { absFontPath = fontPath } // 再次检查绝对路径文件是否存在 if _, err := os.Stat(absFontPath); err != nil { return false } // gofpdf v2使用AddUTF8Font添加支持UTF-8的字体(使用绝对路径) pdf.AddUTF8Font(fontName, "", absFontPath) // 常规样式 pdf.AddUTF8Font(fontName, "B", absFontPath) // 粗体样式 // 验证字体是否可用 pdf.SetFont(fontName, "", 12) testWidth := pdf.GetStringWidth("测试") if testWidth == 0 { return false } return true } // getChineseFontPaths 获取中文字体路径列表(仅TTF格式) func (fm *FontManager) getChineseFontPaths() []string { // 按优先级排序的字体文件列表 fontNames := []string{ "simhei.ttf", // 黑体(默认) "simkai.ttf", // 楷体(备选) "simfang.ttf", // 仿宋(备选) } return fm.buildFontPaths(fontNames) } // getWatermarkFontPaths 获取水印字体路径列表(仅TTF格式) func (fm *FontManager) getWatermarkFontPaths() []string { // 水印字体文件名(尝试大小写变体) fontNames := []string{ "YunFengFeiYunTi-2.ttf", // 优先尝试大写版本 "yunfengfeiyunti-2.ttf", // 小写版本(兼容) } return fm.buildFontPaths(fontNames) } // buildFontPaths 构建字体文件路径列表(仅从resources/pdf/fonts加载) func (fm *FontManager) buildFontPaths(fontNames []string) []string { var fontPaths []string // 获取resources/pdf目录(使用统一的资源路径查找函数) resourcesPDFDir := GetResourcesPDFDir() fontsDir := filepath.Join(resourcesPDFDir, "fonts") // 只从resources/pdf/fonts目录加载 for _, fontName := range fontNames { fontPath := filepath.Join(fontsDir, fontName) fontPaths = append(fontPaths, fontPath) } // 过滤出实际存在的字体文件 var existingFonts []string for _, fontPath := range fontPaths { fileInfo, err := os.Stat(fontPath) if err == nil && fileInfo.Mode().IsRegular() { existingFonts = append(existingFonts, fontPath) } } return existingFonts } // SetFont 设置中文字体 func (fm *FontManager) SetFont(pdf *gofpdf.Fpdf, style string, size float64) { if fm.chineseFontLoaded { pdf.SetFont(fm.chineseFontName, style, size) } else { // 如果没有中文字体,使用Arial作为后备 pdf.SetFont("Arial", style, size) } } // SetWatermarkFont 设置水印字体 func (fm *FontManager) SetWatermarkFont(pdf *gofpdf.Fpdf, style string, size float64) { if fm.watermarkFontLoaded { pdf.SetFont(fm.watermarkFontName, style, size) } else { // 如果水印字体不可用,使用主字体作为后备 fm.SetFont(pdf, style, size) } } // IsChineseFontAvailable 检查中文字体是否可用 func (fm *FontManager) IsChineseFontAvailable() bool { return fm.chineseFontLoaded }