JSON Schema
Overview
gpdf supports defining documents entirely in JSON. This is ideal for:
- API-driven PDF generation
- Dynamic documents from external data
- Template systems without Go code
- Configuration-based document creation
Basic Usage
schema := []byte(`{
"page": {"size": "A4", "margins": "20mm"},
"body": [
{"row": {"cols": [
{"span": 12, "text": "Hello from JSON!", "style": {"size": 24, "bold": true}}
]}}
]
}`)
doc, err := template.FromJSON(schema, nil)
if err != nil {
log.Fatal(err)
}
data, err := doc.Generate()
┌─ A4 ──────────────────────────────────┐
│ │
│ Hello from JSON! ← 24pt, Bold │
│ │
└───────────────────────────────────────┘
Data Binding with Go Templates
JSON schemas support Go template expressions for dynamic content:
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}}"}
]}}
]
}`)
data := map[string]any{
"Title": "Quarterly Report",
"Author": "ACME Corporation",
}
doc, err := template.FromJSON(schema, data)
Schema Structure
{
"page": {
"size": "A4",
"margins": "20mm"
},
"metadata": {
"title": "Document Title",
"author": "Author Name",
"subject": "Subject",
"creator": "Creator"
},
"header": [ /* rows */ ],
"footer": [ /* rows */ ],
"body": [ /* rows */ ]
}
Page Configuration
| Field | Values | Default |
|---|---|---|
size | "A4", "A3", "Letter", "Legal" | "A4" |
margins | Dimension string: "20mm", "1in", "15pt" | — |
Rows and Columns
{"row": {"cols": [
{"span": 6, "text": "Left column"},
{"span": 6, "text": "Right column"}
]}}
Complete Example
schema := []byte(`{
"page": {"size": "A4", "margins": "20mm"},
"metadata": {"title": "JSON Schema Example", "author": "gpdf"},
"header": [
{"row": {"cols": [
{"span": 6, "text": "gpdf JSON Schema", "style": {"size": 16, "bold": true, "color": "#1A237E"}},
{"span": 6, "text": "Document Header", "style": {"align": "right", "italic": true}}
]}},
{"row": {"cols": [
{"span": 12, "line": {"color": "#1A237E", "thickness": "1pt"}}
]}}
],
"footer": [
{"row": {"cols": [
{"span": 12, "elements": [
{"type": "line"},
{"type": "pageNumber", "style": {"align": "center"}}
]}
]}}
],
"body": [
{"row": {"cols": [
{"span": 12, "text": "JSON Schema Generation", "style": {"size": 24, "bold": true}}
]}},
{"row": {"cols": [
{"span": 12, "spacer": "5mm"}
]}},
{"row": {"cols": [
{"span": 12, "text": "This PDF was generated from a JSON schema. No Go builder code needed!"}
]}},
{"row": {"cols": [
{"span": 12, "spacer": "10mm"}
]}},
{"row": {"cols": [
{"span": 6, "elements": [
{"type": "text", "content": "Features", "style": {"size": 16, "bold": true}},
{"type": "list", "list": {"items": [
"Declarative document definition",
"All element types supported",
"Style options",
"Header and footer support"
]}}
]},
{"span": 6, "elements": [
{"type": "text", "content": "Supported Elements", "style": {"size": 16, "bold": true}},
{"type": "list", "list": {"type": "ordered", "items": [
"Text with styles",
"Tables with headers",
"Lists (ordered/unordered)",
"Lines and spacers",
"QR codes and barcodes",
"Images (base64)"
]}}
]}
]}},
{"row": {"cols": [
{"span": 12, "spacer": "10mm"}
]}},
{"row": {"cols": [
{"span": 12, "table": {
"header": ["Feature", "Format", "Status"],
"rows": [
["Text styling", "JSON style object", "Supported"],
["Tables", "header + rows arrays", "Supported"],
["Lists", "ordered/unordered", "Supported"],
["Images", "base64 encoded", "Supported"],
["QR codes", "data string", "Supported"],
["Barcodes", "Code128", "Supported"]
],
"columnWidths": [35, 35, 30],
"headerStyle": {"bold": true, "color": "white", "background": "#1A237E"},
"stripeColor": "#F5F5F5"
}}
]}},
{"row": {"cols": [
{"span": 12, "spacer": "10mm"}
]}},
{"row": {"cols": [
{"span": 6, "qrcode": {"data": "https://gpdf.dev", "size": "25mm"}},
{"span": 6, "barcode": {"data": "GPDF-JSON-001", "format": "code128"}}
]}}
]
}`)
doc, err := template.FromJSON(schema, nil)
┌─ A4 ──────────────────────────────────────────────┐
│ gpdf JSON Schema Document Header │ ← header
│ ──────────────────────────────────────────────── │
│ │
│ JSON Schema Generation ← 24pt bold │
│ │
│ This PDF was generated from a JSON schema. │
│ │
│ Features Supported Elements │
│ • Declarative... 1. Text with styles │
│ • All element... 2. Tables with headers │
│ • Style options 3. Lists │
│ • Header/footer 4. Lines and spacers │
│ 5. QR codes and barcodes │
│ 6. Images (base64) │
│ │
│ ┌──────────┬──────────────────┬──────────┐ │
│ │ Feature │ Format │ Status │ │
│ ├──────────┼──────────────────┼──────────┤ │
│ │ Text │ JSON style obj │Supported │ │
│ │ Tables │ header + rows │Supported │ │
│ │ ... │ ... │ ... │ │
│ └──────────┴──────────────────┴──────────┘ │
│ │
│ ┌────┐ ║║│║║│║║║│║║│ │
│ │ QR │ GPDF-JSON-001 │
│ └────┘ │
│ │
│ ──────────────────────────────────────────────── │
│ Page 1 │ ← footer
└───────────────────────────────────────────────────┘
Element Types in JSON
Column Shorthand
For single elements, use shorthand directly on the column:
{"span": 12, "text": "Hello", "style": {"bold": true}}
{"span": 12, "spacer": "10mm"}
{"span": 12, "line": {"color": "#FF0000"}}
Elements Array
For multiple elements in one column, use the elements array:
{"span": 12, "elements": [
{"type": "text", "content": "Title", "style": {"size": 18, "bold": true}},
{"type": "spacer", "height": "5mm"},
{"type": "text", "content": "Body text"},
{"type": "line"},
{"type": "pageNumber", "style": {"align": "center"}}
]}
Style Object
{
"size": 16,
"bold": true,
"italic": true,
"color": "#1A237E",
"background": "#F5F5F5",
"align": "center",
"font": "NotoSansJP"
}
| Field | Type | Description |
|---|---|---|
size | number | Font size in points |
bold | boolean | Bold weight |
italic | boolean | Italic style |
color | string | Text color ("#RRGGBB", "red", "blue", etc.) |
background | string | Background color |
align | string | "left", "center", "right" |
font | string | Font family name |
Table Object
{
"header": ["Col A", "Col B", "Col C"],
"rows": [
["A1", "B1", "C1"],
["A2", "B2", "C2"]
],
"columnWidths": [40, 30, 30],
"columnAlign": ["left", "right", "right"],
"headerStyle": {"bold": true, "color": "white", "background": "#1A237E"},
"stripeColor": "#F5F5F5",
"border": {"width": "1pt", "color": "#1A237E"},
"cellBorder": {"width": "0.5pt", "color": "gray(0.5)", "style": "dashed"},
"borderCollapse": true,
"background": "#FAFAFA"
}
Since v1.0.10 columnAlign sets per-column horizontal text alignment ("left", "center", "right") for both header and body cells. Columns without an entry fall back to left alignment.
Since v1.0.7 border draws an outer frame, cellBorder adds the same border around every header + body cell (grid lines), borderCollapse merges adjacent cell borders, and background fills the table's outer box. See Border Object for the shape of border / cellBorder.
Image Object
{"span": 6, "image": {
"src": "data:image/png;base64,...",
"width": "60mm",
"minWidth": "40mm",
"border": {"widths": ["2pt", "2pt", "2pt", "2pt"], "color": "#E53935"},
"background": "#FFF8E1"
}}
| Field | Description |
|---|---|
src | Base64, data URI, or file path |
width / height | Display dimension |
minWidth / minHeight | Since v1.0.6 Overflow to next page when shrinking below this would be required |
fit | "contain" (default), "cover", "stretch", "original" |
align | "left", "center", "right" |
border | Since v1.0.7 Border around the image (see Border Object) |
background | Since v1.0.7 Fill color behind the image (handy for transparent PNGs) |
Border Object
Since v1.0.7Used by table.border, table.cellBorder, and image.border:
{
"width": "1pt",
"color": "#1A237E",
"style": "solid"
}
For per-edge widths in CSS order (top, right, bottom, left), use widths instead of width:
{"widths": ["2pt", "1pt", "2pt", "1pt"], "color": "#1A237E", "style": "dashed"}
| Field | Type | Description |
|---|---|---|
width | string | Uniform edge width (e.g. "1pt", "0.5mm") |
widths | array of 4 strings | Per-edge widths [top, right, bottom, left] (wins over width) |
color | string | Edge color ("#RRGGBB", named, or "gray(0.5)") |
style | string | "solid" (default), "dashed", "dotted", "none" |
List Object
{"type": "list", "list": {"items": ["Item 1", "Item 2", "Item 3"]}}
{"type": "list", "list": {"type": "ordered", "items": ["First", "Second", "Third"]}}
QR Code Object
{"span": 6, "qrcode": {"data": "https://gpdf.dev", "size": "25mm", "minSize": "20mm"}}
Since v1.0.6 minSize moves the QR code to the next page instead of shrinking it below the given dimension when the remaining space is too small.
Barcode Object
{"span": 6, "barcode": {"data": "INV-001", "format": "code128"}}
Absolute Positioning
Place elements at exact XY coordinates, outside the normal grid flow:
{
"absolute": [
{
"x": "120mm",
"y": "20mm",
"elements": [
{"type": "text", "content": "CONFIDENTIAL", "style": {"size": 20, "bold": true, "color": "red"}}
]
},
{
"x": "10mm",
"y": "250mm",
"width": "25mm",
"height": "25mm",
"elements": [
{"type": "qrcode", "qrcode": {"data": "https://gpdf.dev", "size": "20mm"}}
]
},
{
"x": "0mm",
"y": "0mm",
"origin": "page",
"elements": [
{"type": "text", "content": "Page origin"}
]
}
]
}
The absolute array can appear at the top level (applies to all pages) or inside individual page definitions.
| Field | Type | Required | Description |
|---|---|---|---|
x | string | Yes | X coordinate (dimension string) |
y | string | Yes | Y coordinate (dimension string) |
width | string | No | Explicit width (default: remaining space) |
height | string | No | Explicit height (default: remaining space) |
origin | string | No | "content" (default) or "page" |
elements | array | Yes | Array of element objects |
Dimension Strings
| Format | Example |
|---|---|
| Millimeters | "20mm" |
| Points | "12pt" |
| Centimeters | "2.5cm" |
| Inches | "1in" |
Named Colors
JSON schemas support named colors: "red", "green", "blue", "yellow", "cyan", "magenta", "black", "white", "gray".
Or use hex format: "#FF6B6B", "#1A237E".
Combining with Go Options
Override or extend JSON schemas with Go options:
fontData, _ := os.ReadFile("fonts/NotoSansJP-Regular.ttf")
doc, err := template.FromJSON(schema, data,
template.WithFont("NotoSansJP", fontData),
template.WithDefaultFont("NotoSansJP", 12),
)