gofpdf是Go中生成基础PDF最稳的选择,纯Go实现、无CGO、跨平台友好;需统一用"pt"单位,正确AddFont并SetFont才能显示中文,MultiCell不自动分页需手动处理,Fpdf实例非并发安全,且必须调用AddPage()后才能写入内容。

直接用 gofpdf 是 Go 里生成基础 PDF 最稳的选择——纯 Go 实现、无 CGO、不依赖系统库、交叉编译友好。它不适合渲染 HTML 或做复杂排版,但导出发票、日志摘要、结构化报表这类需求,又快又可靠。
gofpdf.New() 单位和纸张参数设错,字体/边距全乱
单位("pt" / "mm" / "cm")不是可有可无的配置项,它直接影响: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 或 goroutine 里并发写,必然出问题。
- 每个请求/任务都新建实例:
pdf := gofpdf.New(...),用完丢弃 - 字体只需注册一次(比如在
init()或服务启动时),重复AddFont()会 panic:font already exists - 单个实例内存占用约 2–5 MB,批量生成时注意 GC,大文件(>30 页)建议在循环末尾加
runtime.GC() - 别用
embed.FS直接读字体字节再注册——AddFont()只接受文件路径,不支持字节流;想打包字体,得先写入临时文件再传路径
最易被忽略的一点:所有内容必须在 AddPage() 之后写入。New() 只建对象,不建页面;漏掉这步,OutputFileAndClose() 生成的是空白 PDF,且无任何错误提示。



















