导出PDF时中文乱码是因为未正确加载中文字体,gofpdf等库默认仅支持西文字体,必须调用AddFont传入真实.ttf路径并配合SetFont使用,缺一不可;Gin需手动设置Content-Type和Content-Disposition头,且gofpdf非并发安全,须每请求新建实例并主动处理分页。

导出PDF时中文乱码,是因为没加载中文字体
Gin本身不处理PDF生成,实际依赖第三方库(如gofpdf或unidoc),而这些库默认只带西文字体。直接调用AddFont却没指定字体路径,或用了系统字体但没打包进二进制,就会出现方块或空白。
- 用
gofpdf时,必须显式调用AddFont并传入真实存在的.ttf文件路径(不能只写字体名) - 推荐把
simhei.ttf或NotoSansCJKsc-Regular.otf放在项目assets/fonts/目录下,并用embed.FS打包进二进制(Go 1.16+) - 若用
unidoc,需调用pdf.NewUTF8FontFromBytes加载字体字节,不能靠系统自动发现
Gin返回PDF响应时Content-Type和Header设置不对
浏览器无法正确识别PDF,或下载后打不开,大概率是HTTP头没设对。Gin的c.Data或c.File不会自动补全关键Header。
- 必须手动设置
c.Header("Content-Type", "application/pdf") - 如果希望强制下载(而非内嵌预览),加
c.Header("Content-Disposition", "attachment; filename=report.pdf") - 用
c.Data返回字节时,别漏掉200状态码;用c.File则需确保路径可读且无权限问题
并发导出PDF时内存暴涨甚至OOM
gofpdf不是并发安全的,每个PDF生成实例都持有独立的缓冲和字体缓存。在Gin handler里复用同一个pdf.Fpdf对象,会导致数据错乱;而每次new又容易堆积临时对象。
在 Go 中使用 google/wire 实现编译时依赖注入——wire.NewSet、wire.Build、wire.Bind(接口→实现)、wire.Struct、wire.Value、wire.Interface
- 每个请求都应创建全新
pdf.New实例,不要全局单例 - 避免在PDF生成过程中做数据库长查询或HTTP远程调用——这些应提前完成并传入PDF逻辑
- 若导出内容复杂,考虑用
runtime.GC()在defer里触发一次回收(仅当实测有帮助时)
PDF导出结果分页错乱或表格截断
这是gofpdf最常被低估的问题:它没有自动换页逻辑,Cell或MultiCell超出页面高度后不会自动跳页,而是继续画到不可见区域,最终PDF看似“少内容”。
立即学习“go语言免费学习笔记(深入)”;
- 必须主动调用
pdf.CheckPageBreak(height)判断是否需要pdf.AddPage() - 表格行高要预估(比如每行
5mm),不能依赖MultiCell返回的高度去动态计算(它返回的是绘制后的实际高度,但此时已晚) - 更稳妥的做法是先用
GetStringWidth估算文本宽度,结合列宽做折行判断,再决定是否换页
字体、Header、并发、分页——这四个点卡住90%的Gin PDF导出需求。其中字体加载和分页控制最容易在线上环境突然失效,调试时得盯着生成的PDF字节流开头是否含%PDF-1.4,以及用pdfcpu validate命令快速验完整性。


















