如何在 gpdf 中使用 IPAex 哥特体(IPAex Gothic)?
用 gpdf.WithFont 注册 ipaexg.ttf。IPAex 只提供 Regular 一个字重,粗体需要合成或与 Mincho 配对。
这个问题的另一种表达
你想在 gpdf 文档里用 IPAex Gothic —— 日本 情报处理推进机构(IPA)维护的比例西文哥特体。常见场景:e-Tax 的 PDF 附件、面向政府的书面文件,或者自 2010 年前后就统一使用 IPAex 的内部样式。三处容易踩坑:该拿哪个文件、没有 Bold 怎么办、IPA Font License 到底要求你做什么。
速答
用 gpdf.WithFont("IPAexGothic", bytes) 注册 ipaexg.ttf,设为默认字体。粗体强调要么用 template.Bold() 合成,要么与 IPAex Mincho 配对,因为 IPAex 只发行 Regular。随字体一起保留许可证文本。
完整示例
package main
import (
"log"
"os"
"github.com/gpdf-dev/gpdf"
"github.com/gpdf-dev/gpdf/document"
"github.com/gpdf-dev/gpdf/template"
)
func main() {
font, err := os.ReadFile("ipaexg.ttf")
if err != nil {
log.Fatal(err)
}
doc := gpdf.NewDocument(
gpdf.WithPageSize(gpdf.A4),
gpdf.WithMargins(document.UniformEdges(document.Mm(25))),
gpdf.WithFont("IPAexGothic", font),
gpdf.WithDefaultFont("IPAexGothic", 10.5),
)
page := doc.AddPage()
page.AutoRow(func(r *template.RowBuilder) {
r.Col(12, func(c *template.ColBuilder) {
c.Text("請求書", template.FontSize(24), template.Bold())
c.Text("令和8年4月17日発行")
c.Text("金額: ¥100,000 (税込)")
})
})
data, err := doc.Generate()
if err != nil {
log.Fatal(err)
}
if err := os.WriteFile("invoice.pdf", data, 0o644); err != nil {
log.Fatal(err)
}
}
在 moji.or.jp/ipafont 下载 IPAex00401.zip,解压出 ipaexg.ttf 放到 main.go 旁边,然后 go run main.go。
三个文件到底选哪个
打开 zip,你会看到 3 个 TTF 和一份许可证。名字很容易弄混:
| 文件 | 内容 |
|---|---|
ipaexg.ttf | IPAex Gothic —— 无衬线,西文比例宽度。大多数文档选这个。 |
ipaexm.ttf | IPAex Mincho —— 衬线(明朝体),西文比例宽度。长文本或与哥特体配对强调时用。 |
ipag.ttf | IPA Gothic(没有 "ex")—— 无衬线,西文等宽。现在很少用。 |
IPAex 里的 "ex" 是 "extended proportional" 的缩写。原版 IPA 字体把西文字符放在 CJK 全角格子上,中英混排时显得拉伸。IPAex 把西文改为比例宽度,同时保持 CJK 字符在常规格子上。任何夹杂英文词、URL、数字的业务文档——也就是日本的几乎所有业务文档——都该用 IPAex。
如果你接手的旧项目用 ipag.ttf,原因大概是历史遗留(原版 IPA Gothic: 2003 年;IPAex: 2010 年)。把族名保持为 IPAexGothic、文件换成 ipaexg.ttf,代码侧改一行即可。
没有 Bold 怎么办
IPAex 每族只发行一个字重:Regular。这和 Noto Sans JP 的 9 字重相比是最大短板,也是大家看完 IPAex 决定放弃的首要原因。
gpdf 里有两种处理方式。
合成粗体。 template.Bold() 在 Regular 字形上叠加描边。排版上算作弊——真正的粗体字重有专门重画的粗笔画轮廓,不是 Regular 再描一次。但对于 10 pt 以上的发票标题和表格标签,合成粗体对大多数读者来说是看不出差别的:
c.Text("合計金額", template.Bold())
和 IPAex 明朝配对。 日文排版的经典强调手法并不是加粗,而是衬线 / 无衬线切换。同时注册两族:
gothic, _ := os.ReadFile("ipaexg.ttf")
mincho, _ := os.ReadFile("ipaexm.ttf")
doc := gpdf.NewDocument(
gpdf.WithFont("IPAexGothic", gothic),
gpdf.WithFont("IPAexMincho", mincho),
gpdf.WithDefaultFont("IPAexGothic", 10.5),
)
page.AutoRow(func(r *template.RowBuilder) {
r.Col(12, func(c *template.ColBuilder) {
c.Text("請求書", template.FontFamily("IPAexMincho"), template.FontSize(24))
c.Text("ご請求内容は下記の通りです。")
})
})
这种组合在日本的婚礼请柬和正式报告里很常见 —— 标题用明朝、正文用哥特。如果你的文档要交到政府机关,这个组合通常正是对方期待的。
简述 IPA Font License
IPAex 不是 SIL OFL,而是 IPA Font License Agreement v1.0,OSI 批准,总体宽松,但有两点值得注意:
- 字体二进制分发到哪里,许可证文本就要跟到哪里。 如果你
//go:embed了 TTF,就把许可证文本一起打包。项目根目录放一个LICENSES/IPA-FONT-1.0.txt对大多数分发场景已经够用。 - 不要重命名字体。 如果你修改 TTF 本身再分发,衍生字体必须使用不同名字(不得包含 "IPA" 或 "IPAex")。注意:这个限制不适用于渲染时的字形子集化。许可证第 3.4 条明确把"用该字体生成的输出文档"从命名限制中豁免。
含义:gpdf 在 doc.Generate() 时的子集化没问题。嵌入到 PDF 里的子集不需要改名,也不会触发"衍生字体程序"相关条款。你是在创建文档,不是在重新分发字体。
补充一点,gpdf 核心 OSS 仓库本身没有内置 IPAex(golden 测试用的是 Noto 系 SIL OFL 字体),这样下游用户的仓库顶层 LICENSE 就不用为 IPA 许可证的兼容性操心。你的应用里怎么用 IPAex 是你项目的决定,不是我们的。
什么时候选 IPAex 而非 Noto Sans JP
| 维度 | IPAex Gothic | Noto Sans JP |
|---|---|---|
| 字重数 | 1(Regular) | 9(Thin → Black) |
| 许可证 | IPA Font License v1.0 | SIL OFL 1.1 |
| 西文处理 | 比例宽度(IPAex)或等宽(IPA) | 比例宽度 |
| 预装于 | 部分日本系 Linux 发行版、TeX Live ptex-fonts | Android、ChromeOS |
| 典型受众 | 日本政府、法律、学术 | 消费者 Web、国际 |
| 文件大小 | 7.5 MB(Gothic) | 5 MB(仅 Regular) |
当你的输出要穿越日本的制度性边界时 —— e-Tax PDF 附件、法院文书、投向日本期刊的学术论文 —— 选 IPAex,因为这些生态里的评审、审稿人和 OCR 工具都是按 IPA 调校的。其他场景用 Noto Sans JP 即可。两者渲染结果非常接近,选择的依据是"出口在哪个生态"而非美学。
延伸阅读
- 如何在 gpdf 中嵌入日文字体? —— 适用于任何 CJK TTF 的通用配方
- 如何在 gpdf 中使用 Noto Sans JP? —— SIL OFL 且有 9 字重的替代选项
- 为什么 gpdf 生成的 PDF 中日文显示为方块? —— 当字形没显示时的排查
- 字体指南 ——
WithFont完整参考
试试 gpdf
gpdf 是一个 Go PDF 生成库。MIT 协议、零外部依赖、原生支持 CJK。
go get github.com/gpdf-dev/gpdf