最稳妥的PDF生成方案是gofpdf+ttf字体嵌入或pdfcpu配合HTML模板转PDF;前者需AddTTFFont且路径正确,后者复用前端样式但需headless Chrome;电子印章合法需数字签名与哈希校验,非简单贴图。

PDF生成用哪个库最稳妥
直接用 go-pdf 或 unidoc 生成带格式合同容易翻车——前者不支持中文渲染,后者商用要买授权。实际项目里最省心的是 gofpdf + ttf 字体嵌入,或者更推荐 pdfcpu 配合 HTML 模板转 PDF(用 chromedp 渲染后导出)。前者适合结构简单、字段少的合同;后者能复用前端样式,但需额外起 headless Chrome 实例。
常见错误:用 gofpdf 直接写中文却没调用 AddTTFFont,结果输出全是方框;用 pdfcpu 时路径写错字体文件位置,报 font not found 却没提示具体路径。
- 确认字体文件是 TrueType(.ttf),且路径为绝对路径或相对于可执行文件的相对路径
-
gofpdf中必须在New后立即调用AddTTFFont,不能等写完内容再加 - 如果合同含表格/复杂对齐,优先选
chromedp+ HTML 模板,避免手动计算坐标
电子印章怎么盖才合法有效
所谓“电子印章”在技术上就是一张 PNG/SVG 图片叠加到 PDF 特定位置,但法律有效性取决于两点:是否绑定签署人身份(如用数字证书签名),以及是否防篡改(加盖后整份 PDF 要做哈希校验并附签章信息)。单纯用 pdfcpu 的 stamp 命令贴图只是视觉效果,不算合规电子签。
实操建议分两层:先用 pdfcpu 把印章图(透明背景 PNG)盖到 PDF 页面右下角;再用 github.com/cloudflare/cfssl 对 PDF 内容做 SHA256 哈希,连同时间戳、签署人公钥一起用私钥签名,把签名结果写进 PDF 的 AcroForm 字段或单独存为 .sig 文件。
立即学习“go语言免费学习笔记(深入)”;
- 印章图片分辨率至少 300dpi,尺寸建议 200×200px,否则打印模糊
- 盖章位置别写死坐标,应根据合同页数动态计算——最后一页固定区域比第一页更合理
- 不要用 base64 编码印章图直接 embed 到 PDF,会增大体积;用
pdfcpu attach外挂资源更轻量
Gin 接口如何安全返回带章 PDF
用户点击“下载合同”时,Gin 不该直接 c.File 返回临时文件,因为并发请求可能覆盖同一文件名。正确做法是生成唯一 ID(如 uuid.NewString()),用它命名临时 PDF,内存中生成后通过 c.Data 直接流式返回,不落地磁盘。
关键细节:HTTP Header 必须设 Content-Type: application/pdf 和 Content-Disposition: attachment; filename="contract_abc123.pdf",否则浏览器可能打开而非下载;同时响应体要完整,不能中途 panic 导致 PDF 截断(尤其用 chromedp 时超时未处理好就会断流)。
- 生成失败时返回明确错误码(如 500)和简短提示,别暴露内部路径或库名
- 大合同(>5MB)考虑加
Content-Transfer-Encoding: binary防 MIME 解析异常 - 若需审计,记录每次生成的 hash、时间、用户 ID,但别记明文身份证号等敏感字段
为什么本地能盖章线上就失效
多数问题出在时区和字体路径。Gin 服务部署在 Docker 容器里时,默认时区是 UTC,而印章时间戳按本地时间生成,导致验章系统比对失败;另外容器内找不到宿主机的 /usr/share/fonts,gofpdf 加载字体失败却静默忽略,最终 PDF 没文字只有空白框。
解决方法不是改代码逻辑,而是统一环境:Dockerfile 里显式设置 ENV TZ=Asia/Shanghai 并 COPY 字体文件到镜像固定路径(如 /app/fonts/simhei.ttf),代码里用该路径加载;同时所有时间戳都用 time.Now().In(time.FixedZone("CST", 8*60*60)) 显式指定时区。
- 别依赖
os.Getenv("TZ"),容器里不一定有这个变量 - 字体文件权限设为 644,否则 Go 进程读不到
- 上线前用
pdfcpu validate检查生成的 PDF 是否结构完整,比肉眼打开更可靠



















