指南
字体
在 gpdf 中使用字体和 CJK 文本
注册字体
创建文档时注册 TrueType 字体:
fontData, _ := os.ReadFile("fonts/NotoSansJP-Regular.ttf")
doc := template.New(
template.WithPageSize(document.A4),
template.WithFont("NotoSansJP", fontData),
template.WithDefaultFont("NotoSansJP", 12),
)
多字体族
注册多个字体用于不同场景:
regular, _ := os.ReadFile("fonts/NotoSansJP-Regular.ttf")
bold, _ := os.ReadFile("fonts/NotoSansJP-Bold.ttf")
mono, _ := os.ReadFile("fonts/RobotoMono-Regular.ttf")
doc := template.New(
template.WithFont("NotoSansJP", regular),
template.WithFont("NotoSansJP-Bold", bold),
template.WithFont("RobotoMono", mono),
template.WithDefaultFont("NotoSansJP", 12),
)
使用 FontFamily() 在文本元素中切换字体:
c.Text("Regular text") // uses default NotoSansJP
c.Text("Bold heading", template.FontFamily("NotoSansJP-Bold"), template.FontSize(18))
c.Text("Code snippet", template.FontFamily("RobotoMono"), template.FontSize(10))
CJK 支持
gpdf 对日文、中文和韩文文本提供一流支持。CJK 功能包括自动换行和禁则处理(禁止某些字符出现在行首或行尾)。
fontData, _ := os.ReadFile("fonts/NotoSansJP-Regular.ttf")
doc := template.New(
template.WithPageSize(document.A4),
template.WithMargins(document.UniformEdges(document.Mm(20))),
template.WithFont("NotoSansJP", fontData),
template.WithDefaultFont("NotoSansJP", 12),
)
page := doc.AddPage()
page.AutoRow(func(r *template.RowBuilder) {
r.Col(12, func(c *template.ColBuilder) {
c.Text("日本語テキストのサンプル", template.FontSize(18))
c.Text("gpdfは純Go・ゼロ依存のPDF生成ライブラリです。")
c.Text("CJKテキストの改行やレイアウトを正しく処理します。")
})
})
多语言 CJK 文档
对于包含多种 CJK 语言的文档,分别注册每种字体并使用 FontFamily() 切换:
jpFont, _ := os.ReadFile("fonts/NotoSansJP-Regular.ttf")
scFont, _ := os.ReadFile("fonts/NotoSansSC-Regular.ttf")
krFont, _ := os.ReadFile("fonts/NotoSansKR-Regular.ttf")
doc := template.New(
template.WithPageSize(document.A4),
template.WithFont("NotoSansJP", jpFont),
template.WithFont("NotoSansSC", scFont),
template.WithFont("NotoSansKR", krFont),
template.WithDefaultFont("NotoSansSC", 12),
)
page := doc.AddPage()
page.AutoRow(func(r *template.RowBuilder) {
r.Col(4, func(c *template.ColBuilder) {
c.Text("こんにちは", template.FontFamily("NotoSansJP"))
})
r.Col(4, func(c *template.ColBuilder) {
c.Text("你好", template.FontFamily("NotoSansSC"))
})
r.Col(4, func(c *template.ColBuilder) {
c.Text("안녕하세요", template.FontFamily("NotoSansKR"))
})
})
二维码与 CJK 内容
二维码支持包含日文在内的 Unicode 内容:
c.QRCode("こんにちは世界", template.QRSize(document.Mm(30)))
字体子集化
gpdf 自动对 TrueType 字体进行子集化 — 仅将文档中实际使用的字形嵌入。这显著减小了文件大小,特别是对于可能包含数万个字形的 CJK 字体。
支持的字体格式
| 格式 | 支持情况 |
|---|---|
| TrueType (.ttf) | 完全支持 |
| OpenType (.otf) | 部分支持(TrueType 轮廓) |
| WOFF / WOFF2 | 不支持 |
| Type 1 | 不支持 |
文本整形限制
gpdf 将每个字符一对一映射到字形,不包含文本整形引擎。需要上下文字形替换或双向文本重排的书写系统无法正确排版。
| 书写系统 | 状态 |
|---|---|
| 拉丁文、西里尔文、希腊文 | 完全支持 |
| CJK(日文、中文、韩文) | 完全支持 |
| 阿拉伯文、希伯来文、波斯文、乌尔都文 | 字形能正确嵌入,但不支持连写和 RTL 重排 |
| 天城文、泰文等复杂书写系统 | 不支持上下文整形 |
对于阿拉伯文等连写书写系统,字母会以孤立形态从左向右绘制,而不是连写并从右向左排列。字体 GSUB / GPOS 表中定义的连字和字距调整同样不会应用。
这不影响文本提取。字符会随 ToUnicode CMap 一起嵌入,因此从 PDF 复制或运行 pdftotext 都能得到原始字符串。如果只需要文本可搜索、可选中 —— 例如将 OCR 结果叠加到扫描页面上 —— 该限制不会影响结果。
推荐字体
| 字体 | 适用场景 |
|---|---|
| Noto Sans JP | 日文文本 |
| Noto Sans SC | 简体中文 |
| Noto Sans KR | 韩文文本 |
| Noto Sans | 拉丁/西文文本 |
| Inter | 现代 UI 风格文档 |
| Roboto Mono | 代码和等宽文本 |
JSON Schema 中的字体使用
使用 JSON schema 时,通过 Go 选项注册字体:
fontData, _ := os.ReadFile("fonts/NotoSansJP-Regular.ttf")
schema := []byte(`{
"page": {"size": "A4"},
"body": [
{"row": {"cols": [
{"span": 12, "text": "日本語テキスト", "style": {"font": "NotoSansJP"}}
]}}
]
}`)
doc, err := template.FromJSON(schema, nil,
template.WithFont("NotoSansJP", fontData),
)