gofpdf是Go生态中稳定轻量的PDF生成库,纯Go实现无CGO依赖;需统一用"pt"单位、正确注册并切换中文字体、手动处理MultiCell分页、并发时每个任务新建实例。

用 gofpdf 生成 PDF 是目前 Go 生态里最稳、最轻量、部署最省心的选择——纯 Go 实现、无 CGO、不依赖系统库、交叉编译后单文件可直接运行。它不适合渲染 HTML 或做自动分页长文,但导出发票、报表、合同、日志摘要这类结构化文档,又快又可靠。
gofpdf.New() 单位和纸张设错,整个布局就全乱
单位不是可有可无的配置项,它直接决定:Cell() 宽高、SetFontSize() 大小、SetMargins() 数值含义。混用单位会导致文字被截、边距错位、页脚压住内容。
- 统一推荐用
"pt":字体大小和坐标单位一致,计算直观(12pt 字 ≈ 12pt 高) -
gofpdf.New("P", "pt", "A4", "")中的"A4"是预设常量;若自定义尺寸,必须传&gofpdf.Rect{W: 595, H: 842}(A4 在 pt 下的宽高) - 第四个参数是字体目录路径,留空即可;别填
"./fonts"——gofpdf不会自动扫描该目录,这个参数实际已被弃用
中文显示方块?AddFont + SetFont 缺一不可
方块不是编码问题,是字体链断裂:注册了但没切换,或路径错了,或字体本身不含中文字符集。
- 下载开源中文字体(如
NotoSansCJKsc-Regular.ttf),放进项目./fonts/目录 - 运行时用
os.Executable()推导绝对路径:exeDir, _ := filepath.Dir(os.Executable())→fontPath := filepath.Join(exeDir, "fonts", "NotoSansCJKsc-Regular.ttf") - 注册:
pdf.AddFont("noto", "", fontPath)(第一个参数是逻辑名,后续SetFont必须完全一致) - 注册后立刻切换:
pdf.SetFont("noto", "", 12);漏掉这句,所有中文仍走默认Helvetica(无中文) - 别用 Windows 自带的
simhei.ttf:部分版本含版权保护头,gofpdf加载失败且静默忽略
MultiCell 写长文本却卡在一页底部不翻页
MultiCell() 会自动换行,但不会自动分页。光标到底部后继续写,内容直接被裁掉,也不报错。
立即学习“go语言免费学习笔记(深入)”;
- 每次调用前检查位置:
y := pdf.GetY(),A4 高 842pt,留 50pt 页脚,则安全上限 ≈ 792pt - 超限时手动翻页:
if y > 792 { pdf.AddPage(); pdf.SetY(50) }(SetY(50)避免贴顶) -
SetAutoPageBreak(true, 50)只对MultiCell()生效,且必须在AddPage()后、首次MultiCell()前设置 - 别信
Cell()的“自动”行为:它不换行也不分页,超宽直接截断,适合固定长度字段(如订单号)
并发生成 PDF 时输出错乱或 panic
gofpdf.Fpdf 实例不是 goroutine 安全的。复用一个全局实例在 HTTP handler 或循环中,高并发下会输出错乱甚至 panic。
- 每个请求 / 任务都应新建
gofpdf.New()实例,用完丢弃 - 实测单个实例约占用 2–5 MB 内存,开销可控
- 避免在循环里反复调用
AddFont()——字体只注册一次即可,重复注册会 panic 报font alrea - 如果需高频生成,注意关闭压缩:
pdf.Compress = false,并优先用WriteTo()流式写入,降低内存峰值
最容易被忽略的是:所有页面级操作(如页眉、链接、字体切换)都绑定在当前页上下文中,AddPage() 后不会继承——表头、页脚、锚点链接都得手动重设。这不是 bug,是设计约束,得按它的节奏来。



















