Guide

Fonts

Working with fonts and CJK text in gpdf

Registering Fonts

Register TrueType fonts when creating a document:

fontData, _ := os.ReadFile("fonts/NotoSansJP-Regular.ttf")

doc := template.New(
    template.WithPageSize(document.A4),
    template.WithFont("NotoSansJP", fontData),
    template.WithDefaultFont("NotoSansJP", 12),
)

Multiple Font Families

Register multiple fonts for different use cases:

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),
)

Use FontFamily() to switch fonts in text elements:

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 Support

gpdf has first-class support for Japanese, Chinese, and Korean text. CJK features include automatic line breaking with kinsoku rules (prohibition of certain characters at line starts/ends).

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テキストの改行やレイアウトを正しく処理します。")
    })
})

Multi-language CJK Documents

For documents with multiple CJK languages, register each font separately and switch with 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("NotoSansJP", 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"))
    })
})

QR Codes with CJK Content

QR codes support Unicode content including Japanese:

c.QRCode("こんにちは世界", template.QRSize(document.Mm(30)))

Font Subsetting

gpdf automatically subsets TrueType fonts — only the glyphs actually used in the document are embedded. This significantly reduces file size, especially for CJK fonts that can contain tens of thousands of glyphs.

Supported Font Formats

FormatSupport
TrueType (.ttf)Fully supported
OpenType (.otf)Partial support (TrueType outlines)
WOFF / WOFF2Not supported
Type 1Not supported

Text Shaping Limitations

gpdf maps each character to a glyph one-to-one and does not include a text shaping engine. Writing systems that require contextual glyph substitution or bidirectional reordering are not laid out correctly.

ScriptStatus
Latin, Cyrillic, GreekFully supported
CJK (Japanese, Chinese, Korean)Fully supported
Arabic, Hebrew, Persian, UrduGlyphs embed correctly, but no cursive joining and no RTL reordering
Devanagari, Thai, and other complex scriptsNo contextual shaping

For Arabic and other cursive scripts, letters are drawn in their isolated forms from left to right instead of joined and running right to left. Ligatures and kerning defined in a font's GSUB / GPOS tables are not applied either.

This does not affect text extraction. Characters are embedded with a ToUnicode CMap, so copying from the PDF or running pdftotext returns the original string. If you only need the text to be searchable and selectable — overlaying OCR results onto a scanned page, for example — this limitation does not affect the result.

FontUse Case
Noto Sans JPJapanese text
Noto Sans SCSimplified Chinese
Noto Sans KRKorean text
Noto SansLatin/Western text
InterModern UI-style documents
Roboto MonoCode and monospaced text

JSON Schema Font Usage

When using JSON schema, fonts are registered via Go options:

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),
)

Next Steps