Skip to content
⚠️ This document is AI-translated. The Chinese version is the authoritative source.

Template Developer Guide

A template is a compiled executable that communicates with Presto via stdin/stdout. The user's Markdown content is passed through standard input, and the template converts it into Typst source code output to standard output. Presto then invokes the Typst compiler to generate the final PDF.

For the detailed communication specification, see the Binary Protocol. For core terminology, see the Glossary.


Part 1: 5-Minute Quick Start

bash
npx create-presto-template

The interactive wizard will ask 7 questions:

  1. Template name (kebab-case, e.g., my-report)
  2. Display name (e.g., "My Report Template")
  3. Description (one sentence describing its purpose)
  4. Development language (Go recommended / Rust / TypeScript)
  5. Category (up to 20 characters, e.g., "official-doc", "resume")
  6. GitHub username
  7. License (MIT / Apache-2.0 / GPL-3.0)

Manual Setup

Fork the corresponding language Starter repository:

  • Go: presto-template-starter-go
  • Rust: presto-template-starter-rust
  • TypeScript: presto-template-starter-typescript

Project Structure (Go Example)

text
my-template/
  main.go           # Entry point: protocol implementation + Markdown to Typst conversion
  manifest.json     # Template metadata
  example.md        # Example document
  template_head.typ # Typst template header (optional)
  Makefile          # Build / test / preview commands
  CLAUDE.md         # AI development configuration

Build and Preview

bash
make build    # Compile the binary
make preview  # Install to Presto and preview

Part 2: Binary Protocol Quick Reference

For the full specification, see binary-protocol.md. Below is a practical summary.

Four Invocation Methods

CommandstdinstdoutPurpose
cat input.md | ./binaryMarkdownTypst sourceCore conversion
./binary --manifestNonemanifest JSONMetadata query
./binary --exampleNoneExample MarkdownExample document
./binary --versionNoneVersion numberVersion query

No flags outside the protocol may be added.

Three-Language Implementation Comparison

FeatureGoRustTypeScript
Markdown parsergoldmarkpulldown-cmarkmarked
Parsing modeAST traversalEvent-drivenToken traversal
Embedding method//go:embedinclude_str!()import with { type: "text" }
Build commandgo buildcargo build --releasebun build --compile

manifest.json Field Reference

FieldRequiredDescription
nameYesTemplate unique identifier, kebab-case
displayNameYesUser-visible template name
versionYesSemantic version number (semver)
descriptionNoTemplate purpose description
authorNoAuthor information
licenseNoOpen-source license
categoryNoCategory tag, up to 20 characters
keywordsNoSearch keywords array
minPrestoVersionNoMinimum compatible Presto version
requiredFontsNoRequired font list (array of objects with name, displayName, url)
frontmatterSchemaNoUser-configurable frontmatter field definitions

Types supported by frontmatterSchema:

typeDescriptionExample default
"string"Text"Untitled Paper"
"boolean"Booleanfalse
"number"Number1.5

The optional format field provides a hint for UI rendering (e.g., "date" displays a date picker).

Example 1: Starter Default Manifest

json
{
  "name": "my-template",
  "displayName": "My Template",
  "description": "Replace with your template description",
  "version": "0.1.0",
  "author": "your-github-username",
  "license": "MIT",
  "category": "general",
  "keywords": ["template"],
  "minPrestoVersion": "0.1.0",
  "requiredFonts": [],
  "frontmatterSchema": {
    "title": { "type": "string", "default": "Enter title" }
  }
}

Example 2: gongwen Official Template Manifest

json
{
  "name": "gongwen",
  "displayName": "Official Document Template",
  "description": "Official document typesetting following the GB/T 9704-2012 standard",
  "version": "1.0.0",
  "author": "Presto-io",
  "license": "MIT",
  "category": "official-doc",
  "keywords": ["official document", "notice", "report", "government", "GB/T 9704"],
  "minPrestoVersion": "0.1.0",
  "requiredFonts": [
    { "name": "FZXiaoBiaoSong-B05", "displayName": "FZ XiaoBiaoSong", "url": "https://www.foundertype.com/..." },
    { "name": "STHeiti", "displayName": "STHeiti", "url": "https://www.foundertype.com/..." },
    { "name": "STFangsong", "displayName": "STFangsong", "url": "https://www.foundertype.com/..." },
    { "name": "STKaiti", "displayName": "STKaiti", "url": "https://www.foundertype.com/..." },
    { "name": "STSong", "displayName": "STSong", "url": "https://www.foundertype.com/..." }
  ],
  "frontmatterSchema": {
    "title": { "type": "string", "default": "Enter text" },
    "author": { "type": "string", "default": "Enter text" },
    "date": { "type": "string", "format": "YYYY-MM-DD" },
    "signature": { "type": "boolean", "default": false }
  }
}

Part 3: Typst Quick Reference

Typst is the underlying typesetting engine used by Presto. The output of a template is Typst source code. Below are the most commonly used syntax elements for template development.

Page Setup

typst
#set page(paper: "a4", margin: (top: 2.54cm, bottom: 2.54cm, left: 2.58cm, right: 2.08cm))

Fonts and Paragraphs

typst
#set text(font: "SimSun", size: 12pt, lang: "zh")
#set par(leading: 1.5em, first-line-indent: 2em)

Common Functions

FunctionPurposeExample
pagePage setup#set page(paper: "a4")
textFont style#set text(font: "SimSun", size: 12pt)
parParagraph#set par(leading: 1.5em)
headingHeading#heading(level: 1)[Title]
tableTable#table(columns: 3, [...], [...])
imageImage#image("photo.png", width: 80%)
vVertical space#v(1em)
hHorizontal space#h(2em)
alignAlignment#align(center)[content]
boxInline box#box(width: 5cm)[...]
blockBlock box#block(fill: luma(230))[...]
gridGrid layout#grid(columns: 2, gutter: 1em, [...], [...])
pagebreakPage break#pagebreak()
lineLine#line(length: 100%)

Chinese Font Mapping

Typst Font NameChinese Name
SimSunSong (宋体)
SimHeiHei (黑体)
FangSongFangSong (仿宋)
KaiTiKai (楷体)
Microsoft YaHeiMicrosoft YaHei (微软雅黑)
FZXiaoBiaoSong-B05FZ XiaoBiaoSong (方正小标宋)
FZFangSong-Z02FZ FangSong (方正仿宋)

Markdown to Typst Conversion Reference

MarkdownTypst
frontmatter field#let variable = "value"
# heading#heading(level: 1)[...]
**bold**#strong[...]
*italic*#emph[...]
`code`#raw("...")
- item- item
---#line(length: 100%)

Part 4: Pattern Recipes

The following 6 patterns are all extracted from real code in official templates.

Recipe 1: Custom Heading Numbering

Requirement: Official document headings use four levels of numbering in Chinese format.

The gongwen template defines 5-level counters in template_head.typ:

typst
#let h2-counter = counter("h2")
#let h3-counter = counter("h3")
#let h4-counter = counter("h4")
#let h5-counter = counter("h5")

#let custom-heading(level, body, numbering: auto) = {
  if level == 2 {
    h2-counter.step()
    h3-counter.update(0)
    text(font: FONT_HEI, size: zh(3))[#context h2-counter.display("一、")#body]
  } else if level == 3 {
    h3-counter.step()
    text(font: FONT_KAI, size: zh(3))[#context h3-counter.display("(一)")#body]
  } else if level == 4 {
    h4-counter.step()
    let number = h4-counter.get().first()
    text(size: zh(3))[#number. #body]
  } else if level == 5 {
    h5-counter.step()
    let number = h5-counter.get().first()
    text(size: zh(3))[(#number#body]
  }
}

#show heading: it => {
  custom-heading(it.level, it.body, numbering: it.numbering)
}

The Go code in the template converts Markdown headings to Typst heading syntax, and the #show heading rule automatically applies the numbering format.

Recipe 2: Table Layout

Requirement: The jiaoan-shicao template uses landscape A4 + fixed column widths + rowspan merging.

Typst page setup:

typst
#set page(paper: "a4", flipped: true, margin: (top: 2.54cm, bottom: 2.54cm, left: 2.58cm, right: 2.08cm))

#table(
  columns: (2.3cm, 4.2cm, auto, auto, 2.2cm, 1.1cm),
  stroke: 0.5pt,
  align: center + horizon,
  // rowspan merging
  table.cell(rowspan: 3)[Activity Name],
  [Content 1], [Content 2], [Content 3], [Method], [Hours],
)

The Go code implements rowspan merging by detecting a "ditto" marker: when a cell's content is the ditto marker, it automatically merges with the cell above, outputting table.cell(rowspan: N)[...].

Recipe 3: Font Declaration

Requirement: Declare dependent fonts in the manifest and use them in Typst.

Declare in manifest.json:

json
{
  "requiredFonts": [
    { "name": "FZXiaoBiaoSong-B05", "displayName": "FZ XiaoBiaoSong", "url": "https://..." },
    { "name": "STFangsong", "displayName": "STFangsong", "url": "https://..." }
  ]
}

Define font constants and use them in Typst:

typst
#let FONT_XBS = "FZXiaoBiaoSong-B05"
#let FONT_FS = "STFangsong"

#set text(font: FONT_FS, size: zh(3))
// Use XiaoBiaoSong for titles
#text(font: FONT_XBS, size: zh(2), weight: "bold")[Title content]

When Presto installs a template, it reads requiredFonts and prompts the user to install any missing fonts.

Recipe 4: Image Scaling

Requirement: Smart image scaling in the gongwen template (max 13.4cm), with grid layout for multiple images.

Single image scaling (Typst code generated by Go):

typst
#figure(
  context {
    let img = image("photo.png")
    let img-size = measure(img)
    let x = img-size.width
    let y = img-size.height
    let max-size = 13.4cm

    let new-x = x
    let new-y = y

    if x > max-size {
      let scale = max-size / x
      new-x = max-size
      new-y = y * scale
    }
    if new-y > max-size {
      let scale = max-size / new-y
      new-x = new-x * scale
      new-y = max-size
    }

    image("photo.png", width: new-x, height: new-y)
  },
  caption: [photo],
)

For multiple images, grid is used for automatic arrangement, calculating how many fit per row based on aspect ratio.

Recipe 5: Odd/Even Page Footers

Requirement: In the gongwen template, odd page numbers are right-aligned, even page numbers are left-aligned, formatted as "--- N ---".

typst
#set page(
  footer-descent: 7mm,
  footer: context {
    let page-num = here().page()
    let is-even = calc.even(page-num)
    let num = str(page-num)
    let pm = text(font: FONT_SONG, size: zh(4))[— #num —]

    if is-even {
      align(left, [#h(1em) #pm])
    } else {
      align(right, [#pm #h(1em)])
    }
  },
)

Key point: Use context to get the current page number and calc.even() to determine odd/even.

Recipe 6: Custom Markers

Requirement: Use special markers in Markdown to control typesetting.

The gongwen template supports three custom markers:

Markdown MarkerEffectGenerated Typst
{v} or {v:N}Insert N blank lines#linebreak(justify: false)
{pagebreak}Force page break#pagebreak()
{.noindent}Remove paragraph indent#set par(first-line-indent: 0pt)

Processing logic in Go code:

go
// processMarker checks whether the text is a standalone marker
func processMarker(text string) (string, bool) {
    text = strings.TrimSpace(text)
    if m := vMarkerRe.FindStringSubmatch(text); m != nil {
        count := 1
        if m[1] != "" {
            count, _ = strconv.Atoi(m[1])
        }
        var lines []string
        for i := 0; i < count; i++ {
            lines = append(lines, "#linebreak(justify: false)")
        }
        return strings.Join(lines, "\n") + "\n", true
    }
    if text == "{pagebreak}" {
        return "#pagebreak()\n", true
    }
    return "", false
}

{.noindent} can be used as a trailing marker on a paragraph, or with the Pandoc-style ::: {.noindent} block syntax to wrap multiple paragraphs.


Part 5: Development Workflow

Local Development Loop

bash
make build    # Compile the binary
make test     # Run tests (including security checks)
make preview  # Install to Presto and preview

Test Coverage

make test automatically performs the following checks:

  1. Whether manifest is valid JSON
  2. Example round-trip: whether --example output can be correctly converted by the template
  3. Whether --version outputs correctly
  4. Category field validation (non-empty, max 20 characters, valid characters)
  5. Three-layer security testing:
    • Static analysis: checks for imports of prohibited packages
    • Network isolation: runs the template in a sandbox to confirm no network access
    • Output validation: confirms stdout contains only valid Typst code

AI-Assisted Development

Starter repositories come pre-configured with CLAUDE.md, containing project rules, tech stack constraints, and development workflows. For detailed AI workflow patterns, see ai-workflow-patterns.md.

Recommended four-step approach:

  1. Provide reference documents (PDF/DOCX) to AI for analyzing typesetting characteristics
  2. Confirm typesetting parameters and configurable options
  3. Generate code step by step (manifest first, then conversion logic, finally edge case handling)
  4. Run make preview to visually inspect the PDF output

Security Essentials

Templates run in a strict sandbox (30-second timeout, no network, no file writes). During development, be aware of Typst injection protection:

  • When embedding user input in Typst strings ("..."), escape \, ", #
  • When embedding user input in Typst content blocks ([...]), escape \, ], #

Go's internal/typst package provides two escape functions:

go
typst.EscapeString(s)  // For "..." context
typst.EscapeContent(s) // For [...] context

Prohibited packages: net, net/*, os/exec, plugin, debug/*.


Part 6: Release Checklist

Pre-Release Checks

  • [ ] make test passes completely (including security tests)
  • [ ] manifest.json version field has been updated
  • [ ] example.md converts correctly
  • [ ] Preview in Presto looks correct
  • [ ] category is 20 characters or fewer

Release Steps

bash
# 1. Update the version in manifest.json
# 2. Commit the changes
git add manifest.json
git commit -m "chore: bump version to 1.1.0"

# 3. Tag and push
git tag v1.1.0
git push origin main --tags

CI will automatically perform 6-platform builds (darwin/linux/windows x arm64/amd64) and create a GitHub Release. For the detailed release process and CI configuration, see release-process.md.

Registry Listing

  1. Add the presto-template topic to your GitHub repository settings
  2. The registry automatically scans daily at UTC 08:00
  3. You will automatically receive the community trust level
  4. For verified level, submit a PR to the verified-templates.json file in the template-registry repository

Presto — Markdown to PDF