Go 模板

概述

gpdf 与 Go 的 text/template 包集成,支持动态内容生成。模板生成 JSON schema 输出,gpdf 将其渲染为 PDF。

有两种可用方式:

  1. FromJSON — 在 JSON 中嵌入 Go 模板表达式(更简单)
  2. FromTemplate — 使用预解析的 Go 模板实现完全控制(更灵活)

使用 FromJSON 嵌入模板表达式

更简单的方式 — 直接在 JSON 中嵌入 Go 模板表达式:

schema := []byte(`{
    "page": {"size": "A4", "margins": "20mm"},
    "metadata": {"title": "{{.Title}}"},
    "body": [
        {"row": {"cols": [
            {"span": 12, "text": "{{.Title}}", "style": {"size": 24, "bold": true}}
        ]}},
        {"row": {"cols": [
            {"span": 12, "text": "Author: {{.Author}}"}
        ]}}
    ]
}`)

doc, err := template.FromJSON(schema, map[string]any{
    "Title":  "Monthly Report",
    "Author": "ACME Corp",
})

使用 FromTemplate 预解析模板

对于循环和条件判断等复杂逻辑,使用 FromTemplate

import (
    gotemplate "text/template"
    "github.com/gpdf-dev/gpdf/template"
)

tmplStr := `{
    "page": {"size": "A4", "margins": "20mm"},
    "metadata": {"title": "{{.Title}}"},
    "body": [
        {"row": {"cols": [
            {"span": 12, "text": "{{.Title}}", "style": {"size": 24, "bold": true}}
        ]}},
        {"row": {"cols": [
            {"span": 12, "spacer": "5mm"}
        ]}},
        {{- range $i, $section := .Sections}}
        {{- if $i}},{{end}}
        {"row": {"cols": [
            {"span": 12, "elements": [
                {"type": "text", "content": "{{$section.Heading}}", "style": {"size": 16, "bold": true, "color": "#1A237E"}},
                {"type": "spacer", "height": "3mm"},
                {"type": "text", "content": "{{$section.Body}}"},
                {"type": "spacer", "height": "8mm"}
            ]}
        ]}}
        {{- end}}
    ]
}`

tmpl, err := gotemplate.New("report").Funcs(template.TemplateFuncMap()).Parse(tmplStr)
if err != nil {
    log.Fatal(err)
}

type section struct {
    Heading string
    Body    string
}

data := map[string]any{
    "Title": "Quarterly Report - Q1 2026",
    "Sections": []section{
        {
            Heading: "Executive Summary",
            Body:    "Revenue increased 25% year-over-year, driven by new enterprise offerings.",
        },
        {
            Heading: "Product Development",
            Body:    "The gpdf library reached v0.8 with JSON schema and Go template support.",
        },
        {
            Heading: "Market Analysis",
            Body:    "PDF generation market grows. Zero-dependency approach resonates with Go devs.",
        },
        {
            Heading: "Next Steps",
            Body:    "Focus on reusable components, fuzz testing, and v1.0 release.",
        },
    },
}

doc, err := template.FromTemplate(tmpl, data)
if err != nil {
    log.Fatal(err)
}
pdfData, err := doc.Generate()
┌─ A4 ──────────────────────────────────────────────┐
│                                                   │
│  Quarterly Report - Q1 2026      ← 24pt bold      │
│                                                   │
│  Executive Summary               ← 16pt bold blue │
│  Revenue increased 25% year-over-year, driven     │
│  by new enterprise offerings.                     │
│                                                   │
│  Product Development                              │
│  The gpdf library reached v0.8 with JSON schema   │
│  and Go template support.                         │
│                                                   │
│  Market Analysis                                  │
│  PDF generation market grows. Zero-dependency     │
│  approach resonates with Go devs.                 │
│                                                   │
│  Next Steps                                       │
│  Focus on reusable components, fuzz testing,      │
│  and v1.0 release.                                │
│                                                   │
└───────────────────────────────────────────────────┘

模板函数映射

解析模板时使用 TemplateFuncMap() — 它提供辅助函数:

tmpl, err := gotemplate.New("doc").Funcs(template.TemplateFuncMap()).Parse(tmplStr)

可用函数

函数说明
toJSON将 Go 值转换为 JSON 字符串(用于嵌入数据)

使用 Range 生成动态段落

循环遍历数据生成重复部分:

{{- range $i, $item := .Items}}
{{- if $i}},{{end}}
{"row": {"cols": [
    {"span": 6, "text": "{{$item.Name}}"},
    {"span": 6, "text": "{{$item.Value}}", "style": {"align": "right"}}
]}}
{{- end}}

条件判断

使用 if 实现条件内容:

{{- if .ShowHeader}}
{"row": {"cols": [
    {"span": 12, "text": "{{.HeaderText}}", "style": {"size": 20, "bold": true}}
]}},
{{- end}}

提示

  • 使用 {{--}} 去除模板动作周围的空白
  • range 循环中注意处理 JSON 逗号(使用 {{if $i}},{{end}}
  • 使用 TemplateFuncMap() 获取内置辅助函数
  • Go 选项(WithFontWithMargins 等)可以覆盖模板设置:
doc, err := template.FromTemplate(tmpl, data,
    template.WithFont("NotoSansJP", fontData),
    template.WithDefaultFont("NotoSansJP", 12),
)