全部文章

如何在 gpdf 中使用 IPAex 哥特体(IPAex Gothic)?

用 gpdf.WithFont 注册 ipaexg.ttf。IPAex 只提供 Regular 一个字重,粗体需要合成或与 Mincho 配对。

作者: gpdf team

这个问题的另一种表达

你想在 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.ttfIPAex Gothic —— 无衬线,西文比例宽度。大多数文档选这个。
ipaexm.ttfIPAex Mincho —— 衬线(明朝体),西文比例宽度。长文本或与哥特体配对强调时用。
ipag.ttfIPA 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 批准,总体宽松,但有两点值得注意:

  1. 字体二进制分发到哪里,许可证文本就要跟到哪里。 如果你 //go:embed 了 TTF,就把许可证文本一起打包。项目根目录放一个 LICENSES/IPA-FONT-1.0.txt 对大多数分发场景已经够用。
  2. 不要重命名字体。 如果你修改 TTF 本身再分发,衍生字体必须使用不同名字(不得包含 "IPA" 或 "IPAex")。注意:这个限制不适用于渲染时的字形子集化。许可证第 3.4 条明确把"用该字体生成的输出文档"从命名限制中豁免。

含义:gpdf 在 doc.Generate() 时的子集化没问题。嵌入到 PDF 里的子集不需要改名,也不会触发"衍生字体程序"相关条款。你是在创建文档,不是在重新分发字体。

补充一点,gpdf 核心 OSS 仓库本身没有内置 IPAex(golden 测试用的是 Noto 系 SIL OFL 字体),这样下游用户的仓库顶层 LICENSE 就不用为 IPA 许可证的兼容性操心。你的应用里怎么用 IPAex 是你项目的决定,不是我们的。

什么时候选 IPAex 而非 Noto Sans JP

维度IPAex GothicNoto Sans JP
字重数1(Regular)9(Thin → Black)
许可证IPA Font License v1.0SIL OFL 1.1
西文处理比例宽度(IPAex)或等宽(IPA)比例宽度
预装于部分日本系 Linux 发行版、TeX Live ptex-fontsAndroid、ChromeOS
典型受众日本政府、法律、学术消费者 Web、国际
文件大小7.5 MB(Gothic)5 MB(仅 Regular)

当你的输出要穿越日本的制度性边界时 —— e-Tax PDF 附件、法院文书、投向日本期刊的学术论文 —— 选 IPAex,因为这些生态里的评审、审稿人和 OCR 工具都是按 IPA 调校的。其他场景用 Noto Sans JP 即可。两者渲染结果非常接近,选择的依据是"出口在哪个生态"而非美学。

延伸阅读

试试 gpdf

gpdf 是一个 Go PDF 生成库。MIT 协议、零外部依赖、原生支持 CJK。

go get github.com/gpdf-dev/gpdf

⭐ 在 GitHub 上 Star · 阅读文档