diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 7d7e87e..05a9928 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -4,8 +4,8 @@ on: [push, pull_request]
jobs:
- build:
- name: test
+ test-non-docker:
+ name: test non-docker
runs-on: ubuntu-latest
strategy:
matrix:
@@ -28,4 +28,31 @@ jobs:
uses: actions/checkout@v4
- name: Test package
- run: go test -v ./...
+ run: go test -skip "^TestDocker.*$" -v ./...
+
+ test-docker:
+ name: test docker
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ go-version: ['1.23.x']
+ typst-docker-image:
+ - 'ghcr.io/typst/typst:v0.12.0'
+ - 'ghcr.io/typst/typst:v0.13.0'
+ - 'ghcr.io/typst/typst:v0.13.1'
+ - 'ghcr.io/typst/typst:0.14.0'
+ - '' # Also include the default image, just in case.
+
+ steps:
+ - name: Set up Go ${{ matrix.go-version }}
+ uses: actions/setup-go@v5
+ with:
+ go-version: ${{ matrix.go-version }}
+
+ - name: Check out code
+ uses: actions/checkout@v4
+
+ - name: Test package
+ run: go test -run "^TestDocker.*$" -v ./...
+ env:
+ TYPST_DOCKER_IMAGE: ${{ matrix.typst-docker-image }}
diff --git a/README.md b/README.md
index 57762a6..3c44e43 100644
--- a/README.md
+++ b/README.md
@@ -20,22 +20,35 @@ Use at your own discretion for production systems.
## Features
-- PDF, SVG and PNG generation.
-- All Typst parameters are discoverable and documented in [cli-options.go](cli-options.go).
-- Go-to-Typst Value Encoder: Seamlessly inject any Go values.
-- Encode and inject images as a Typst markup simply by [wrapping](image.go) `image.Image` types or byte slices with raw JPEG or PNG data.
+- PDF, SVG, PNG and HTML generation.
+- All Typst parameters are discoverable and documented in [options.go](options.go).
+- Go-to-Typst Value Encoder: Seamlessly encode any Go values as Typst markup.
+- Encode and inject images as a Typst markup simply by [wrapping](image.go) `image.Image` types or raw image data.
- Errors from Typst CLI are returned as structured Go error objects with detailed information, such as line numbers and file paths.
- Uses stdio; No temporary files will be created.
+- Supports native Typst installations and the official Docker image.
- Good unit test coverage.
## Installation
-1. Use `go get github.com/Dadido3/go-typst` inside of your project to add this module to your project.
-2. Install Typst by following [the instructions in the Typst repository].
+Use `go get github.com/Dadido3/go-typst` inside of your project to add this module to your project.
-## Runtime requirements
+## Usage
-This module assumes that the Typst executable is accessible from your system's PATH.
+This module needs either a native installation of Typst, or a working Docker installation.
+The following subsections will show how to use this library in detail.
+
+### Native Typst installation
+
+The basic usage pattern for calling a natively installed Typst executable looks like this:
+
+```go
+typstCaller := typst.CLI{}
+
+err := typstCaller.Compile(input, output, options)
+```
+
+In this case the module assumes that the Typst executable is accessible from your system's PATH.
Ensure that you have [Typst] installed on any machine that your project will be executed.
You can install it by following [the instructions in the Typst repository].
@@ -43,7 +56,7 @@ Alternatively you can pack the Typst executable with your application.
In this case you have to provide the path to the executable when setting up the `typst.CLI` object:
```go
-typstCLI := typst.CLI{
+typstCaller := typst.CLI{
ExecutablePath: "./typst", // Relative path to executable.
}
```
@@ -51,13 +64,79 @@ typstCLI := typst.CLI{
> [!NOTE]
> Make sure to follow the Typst license requirements when you pack and distribute the Typst executable with your software.
-## Usage
+### Docker
-Here we will create a simple PDF document by passing a reader with Typst markup into `typstCLI.Compile` and then let it write the resulting PDF data into a file:
+To use the official Typst docker image ensure that you have a working Docker installation.
+
+This module will automatically pull and run a Docker container with the latest supported Typst image.
+The basic usage pattern is similar to the CLI variant:
+
+```go
+typstCaller := typst.Docker{}
+
+err := typstCaller.Compile(input, output, options)
+```
+
+#### Tips and tricks
+
+As the Typst instance that's running inside the container is fully encapsulated, you have pass through any resources manually.
+This requires a bit more set up than using a native installation.
+
+Let's say you want to compile a document which imports other local Typst markup files.
+In this case you have to ensure that you mount any needed folders and files to the Docker container.
+You also have to set up the root of Typst to that mounted directory, or a parent of it:
+
+```go
+typstCaller := typst.Docker{
+ Volumes: []string{"./test-files:/markup"},
+}
+
+r := bytes.NewBufferString(`#include "hello-world.typ"`)
+
+var w bytes.Buffer
+err := typstCaller.Compile(r, &w, &typst.OptionsCompile{Root: "/markup"})
+```
+
+This will mount `./test-files` to `/markup` inside the Docker container.
+When Typst compiles the input buffer `r`, it expects `./test-files/hello-world.typ` to exist outside of the container.
+
+Another thing is that the Dockerized version of Typst doesn't see your system fonts.
+To get all your system fonts mounted into the container, you can add the volume parameter `/usr/share/fonts:/usr/share/fonts` to your `typst.Docker`.
+For example:
+
+```go
+typstCaller := typst.Docker{
+ Volumes: []string{
+ "./test-files:/markup",
+ "/usr/share/fonts:/usr/share/fonts",
+ },
+}
+```
+
+The same applies when you want to use custom fonts.
+You need to mount the folder containing the fonts, and then you have to tell Typst where the fonts are mounted to inside the container:
+
+```go
+typstCaller := typst.Docker{
+ Volumes: []string{"./test-files:/fonts"},
+}
+
+err := typstCaller.Compile(input, output, &typst.OptionsCompile{FontPaths: []string{"/fonts"}})
+```
+
+## Caller interface
+
+`typst.CLI` and `typst.Docker` both implement the `typst.Caller` interface.
+
+## Examples
+
+### Simple document
+
+Here we will create a simple PDF document by passing a reader with Typst markup into `typstCaller.Compile` and then let it write the resulting PDF data into a file:
```go
func main() {
- r := bytes.NewBufferString(`#set page(width: 100mm, height: auto, margin: 5mm)
+ markup := bytes.NewBufferString(`#set page(width: 100mm, height: auto, margin: 5mm)
= go-typst
A library to generate documents and reports by utilizing the command line version of Typst.
@@ -70,23 +149,78 @@ A library to generate documents and reports by utilizing the command line versio
- Uses stdio; No temporary files need to be created.
- Test coverage of most features.`)
- typstCLI := typst.CLI{}
+ typstCaller := typst.CLI{}
- f, err := os.Create("output.pdf")
+ f, err := os.Create(filepath.Join(".", "documentation", "images", "readme-example-simple.svg"))
if err != nil {
t.Fatalf("Failed to create output file: %v.", err)
}
defer f.Close()
- if err := typstCLI.Compile(r, f, nil); err != nil {
+ if err := typstCaller.Compile(markup, f, &typst.OptionsCompile{Format: typst.OutputFormatSVG}); err != nil {
t.Fatalf("Failed to compile document: %v.", err)
}
}
```
-The resulting document will look like this:
+Output:
-
+
+
+### Value injection
+
+If you need to create documents that rely on data coming from your Go application, you can use `typst.InjectValues` to encode any Go variables, structures, maps, arrays, slices into their respective Typst markup counterparts:
+
+```go
+func main() {
+ customValues := map[string]any{
+ "time": time.Now(),
+ "customText": "Hey there!",
+ "struct": struct {
+ Foo int
+ Bar []string
+ }{
+ Foo: 123,
+ Bar: []string{"this", "is", "a", "string", "slice"},
+ },
+ }
+
+ // Inject Go values as Typst markup.
+ var markup bytes.Buffer
+ if err := typst.InjectValues(&markup, customValues); err != nil {
+ t.Fatalf("Failed to inject values into Typst markup: %v.", err)
+ }
+
+ // Add some Typst markup using the previously injected values.
+ markup.WriteString(`#set page(width: 100mm, height: auto, margin: 5mm)
+#customText Today's date is #time.display("[year]-[month]-[day]") and the time is #time.display("[hour]:[minute]:[second]").
+
+#struct`)
+
+ f, err := os.Create(filepath.Join(".", "documentation", "images", "readme-example-injection.svg"))
+ if err != nil {
+ t.Fatalf("Failed to create output file: %v.", err)
+ }
+ defer f.Close()
+
+ typstCaller := typst.CLI{}
+ if err := typstCaller.Compile(&markup, f, &typst.OptionsCompile{Format: typst.OutputFormatSVG}); err != nil {
+ t.Fatalf("Failed to compile document: %v.", err)
+ }
+}
+```
+
+Output:
+
+
+
+### More examples
+
+It's possible to write custom Typst templates that can be called.
+Th
+A tutorial for the Typst side can be found in the [Typst documentation: Making a Template](https://typst.app/docs/tutorial/making-a-template/).
+
+An example on how to invoke Typst templates can be found in the [passing-values example package](examples/passing-values).
[the instructions in the Typst repository]: https://github.com/typst/typst?tab=readme-ov-file#installation
[Typst]: https://typst.app/
diff --git a/caller.go b/caller.go
new file mode 100644
index 0000000..190be86
--- /dev/null
+++ b/caller.go
@@ -0,0 +1,26 @@
+// Copyright (c) 2025 David Vogel
+//
+// This software is released under the MIT License.
+// https://opensource.org/licenses/MIT
+
+package typst
+
+import "io"
+
+// TODO: Add WASM caller
+
+// TODO: Add special type "Filename" (or similar) that implements a io.Reader/io.Writer that can be plugged into the input and output parameters of the Compile method to signal the use of input/output files instead of readers/writers
+
+// Caller contains all Typst commands that are supported by this library.
+type Caller interface {
+ // VersionString returns the Typst version as a string.
+ VersionString() (string, error)
+
+ // Fonts returns all fonts that are available to Typst.
+ // The options parameter is optional, and can be nil.
+ Fonts(options *OptionsFonts) ([]string, error)
+
+ // Compile takes a Typst document from the supplied input reader, and renders it into the output writer.
+ // The options parameter is optional, and can be nil.
+ Compile(input io.Reader, output io.Writer, options *OptionsCompile) error
+}
diff --git a/cli-options.go b/cli-options.go
index 3b4910c..72045af 100644
--- a/cli-options.go
+++ b/cli-options.go
@@ -1,159 +1,9 @@
-// Copyright (c) 2024-2025 David Vogel
+// Copyright (c) 2025 David Vogel
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
package typst
-import (
- "os"
- "strconv"
- "time"
-)
-
-type OutputFormat string
-
-const (
- OutputFormatAuto OutputFormat = ""
-
- OutputFormatPDF OutputFormat = "pdf"
- OutputFormatPNG OutputFormat = "png"
- OutputFormatSVG OutputFormat = "svg"
- OutputFormatHTML OutputFormat = "html" // this format is only available since 0.13.0
-)
-
-type PDFStandard string
-
-const (
- PDFStandard1_4 PDFStandard = "1.4" // PDF 1.4 (Available since Typst 0.14.0)
- PDFStandard1_5 PDFStandard = "1.5" // PDF 1.5 (Available since Typst 0.14.0)
- PDFStandard1_6 PDFStandard = "1.6" // PDF 1.6 (Available since Typst 0.14.0)
- PDFStandard1_7 PDFStandard = "1.7" // PDF 1.7
- PDFStandard2_0 PDFStandard = "2.0" // PDF 2.0 (Available since Typst 0.14.0)
-
- PDFStandardA_1B PDFStandard = "a-1b" // PDF/A-1b (Available since Typst 0.14.0)
- PDFStandardA_1A PDFStandard = "a-1a" // PDF/A-1a (Available since Typst 0.14.0)
- PDFStandardA_2B PDFStandard = "a-2b" // PDF/A-2b
- PDFStandardA_2U PDFStandard = "a-2u" // PDF/A-2u (Available since Typst 0.14.0)
- PDFStandardA_2A PDFStandard = "a-2a" // PDF/A-2a (Available since Typst 0.14.0)
- PDFStandardA_3B PDFStandard = "a-3b" // PDF/A-3b (Available since Typst 0.13.0)
- PDFStandardA_3U PDFStandard = "a-3u" // PDF/A-3u (Available since Typst 0.14.0)
- PDFStandardA_3A PDFStandard = "a-3a" // PDF/A-3a (Available since Typst 0.14.0)
- PDFStandardA_4 PDFStandard = "a-4" // PDF/A-4 (Available since Typst 0.14.0)
- PDFStandardA_4F PDFStandard = "a-4f" // PDF/A-4f (Available since Typst 0.14.0)
- PDFStandardA_4E PDFStandard = "a-4e" // PDF/A-4e (Available since Typst 0.14.0)
- PDFStandardUA_1 PDFStandard = "ua-1" // PDF/UA-1 (Available since Typst 0.14.0)
-)
-
-type CLIOptions struct {
- Root string // Configures the project root (for absolute paths).
- Input map[string]string // String key-value pairs visible through `sys.inputs`.
- FontPaths []string // Adds additional directories that are recursively searched for fonts.
- IgnoreSystemFonts bool // Ensures system fonts won't be searched, unless explicitly included via FontPaths.
- IgnoreEmbeddedFonts bool // Disables the use of fonts embedded into the Typst binary. (Available since Typst 0.14.0)
- NoPDFTags bool // Disables the automatic generation of accessibility tags. These are emitted when no particular standard like PDF/UA-1 is selected to provide a baseline of accessibility. (Available since Typst 0.14.0)
- CreationTime time.Time // The document's creation date. For more information, see https://reproducible-builds.org/specs/source-date-epoch/.
- PackagePath string // Custom path to local packages, defaults to system-dependent location.
- PackageCachePath string // Custom path to package cache, defaults to system-dependent location.
- Jobs int // Number of parallel jobs spawned during compilation, defaults to number of CPUs. Setting it to 1 disables parallelism.
-
- // Which pages to export. When unspecified, all document pages are exported.
- //
- // Pages to export are separated by commas, and can be either simple page numbers (e.g. '2,5' to export only pages 2 and 5) or page ranges (e.g. '2,3-6,8-' to export page 2, pages 3 to 6 (inclusive), page 8 and any pages after it).
- //
- // Page numbers are one-indexed and correspond to real page numbers in the document (therefore not being affected by the document's page counter).
- Pages string
-
- Format OutputFormat // The format of the output file, inferred from the extension by default.
- PPI int // The PPI (pixels per inch) to use for PNG export. Defaults to 144.
-
- // One (or multiple) PDF standards that Typst will enforce conformance with.
- //
- // See typst.PDFStandard for possible values.
- PDFStandards []PDFStandard
-
- Custom []string // Custom command line options go here.
-}
-
-// Args returns a list of CLI arguments that should be passed to the executable.
-func (c *CLIOptions) Args() (result []string) {
- if c.Root != "" {
- result = append(result, "--root", c.Root)
- }
-
- for key, value := range c.Input {
- result = append(result, "--input", key+"="+value)
- }
-
- if len(c.FontPaths) > 0 {
- var paths string
- for i, path := range c.FontPaths {
- if i > 0 {
- paths += string(os.PathListSeparator)
- }
- paths += path
- }
- result = append(result, "--font-path", paths)
- }
-
- if c.IgnoreSystemFonts {
- result = append(result, "--ignore-system-fonts")
- }
-
- if c.IgnoreEmbeddedFonts {
- result = append(result, "--ignore-embedded-fonts")
- }
-
- if c.NoPDFTags {
- result = append(result, "--no-pdf-tags")
- }
-
- if !c.CreationTime.IsZero() {
- result = append(result, "--creation-timestamp", strconv.FormatInt(c.CreationTime.Unix(), 10))
- }
-
- if c.PackagePath != "" {
- result = append(result, "--package-path", c.PackagePath)
- }
-
- if c.PackageCachePath != "" {
- result = append(result, "--package-cache-path", c.PackageCachePath)
- }
-
- if c.Jobs > 0 {
- result = append(result, "-j", strconv.FormatInt(int64(c.Jobs), 10))
- }
-
- if c.Pages != "" {
- result = append(result, "--pages", c.Pages)
- }
-
- if c.Format != OutputFormatAuto {
- result = append(result, "-f", string(c.Format))
- if c.Format == OutputFormatHTML {
- // this is specific to version 0.13.0 where html
- // is a feature than need explicit activation
- // we must remove this when html becomes standard
- result = append(result, "--features", "html")
- }
- }
-
- if c.PPI > 0 {
- result = append(result, "--ppi", strconv.FormatInt(int64(c.PPI), 10))
- }
-
- if len(c.PDFStandards) > 0 {
- var standards string
- for i, standard := range c.PDFStandards {
- if i > 0 {
- standards += ","
- }
- standards += string(standard)
- }
- result = append(result, "--pdf-standard", standards)
- }
-
- result = append(result, c.Custom...)
-
- return
-}
+// Deprecated: Use typst.OptionsCompile instead.
+type CLIOptions = OptionsCompile
diff --git a/cli-options_test.go b/cli-options_test.go
deleted file mode 100644
index 0e0ad36..0000000
--- a/cli-options_test.go
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright (c) 2025 David Vogel
-//
-// This software is released under the MIT License.
-// https://opensource.org/licenses/MIT
-
-package typst_test
-
-import (
- "os"
- "testing"
-
- "github.com/Dadido3/go-typst"
-)
-
-func TestCliOptions(t *testing.T) {
- o := typst.CLIOptions{
- FontPaths: []string{"somepath/to/somewhere", "another/to/somewhere"},
- }
- args := o.Args()
- if len(args) != 2 {
- t.Errorf("wrong number of arguments, expected 2, got %d", len(args))
- }
- if args[0] != "--font-path" {
- t.Error("wrong font path option, expected --font-path, got", args[0])
- }
- if args[1] != "somepath/to/somewhere"+string(os.PathListSeparator)+"another/to/somewhere" {
- t.Error("wrong font path option, expected my two paths concatenated, got", args[1])
- }
-}
diff --git a/cli.go b/cli.go
index 36aefa9..2801b98 100644
--- a/cli.go
+++ b/cli.go
@@ -6,24 +6,23 @@
package typst
import (
+ "bufio"
"bytes"
"fmt"
"io"
"os/exec"
)
-// TODO: Add docker support to CLI, by calling docker run instead
-
-// TODO: Add an interface for the Typst caller and let CLI (and later docker and WASM) be implementations of that
-
+// CLI allows you to invoke commands on a native Typst executable.
type CLI struct {
ExecutablePath string // The Typst executable path can be overridden here. Otherwise the default path will be used.
WorkingDirectory string // The path where the Typst executable is run in. When left empty, the Typst executable will be run in the process's current directory.
}
-// TODO: Add method for querying the Typst version resulting in a semver object
+// Ensure that CLI implements the Caller interface.
+var _ Caller = CLI{}
-// VersionString returns the version string as returned by Typst.
+// VersionString returns the Typst version as a string.
func (c CLI) VersionString() (string, error) {
// Get path of executable.
execPath := ExecutablePath
@@ -31,7 +30,7 @@ func (c CLI) VersionString() (string, error) {
execPath = c.ExecutablePath
}
if execPath == "" {
- return "", fmt.Errorf("go-typst doesn't support this platform")
+ return "", fmt.Errorf("not supported on this platform")
}
cmd := exec.Command(execPath, "--version")
@@ -53,25 +52,64 @@ func (c CLI) VersionString() (string, error) {
return output.String(), nil
}
-// Compile takes a Typst document from input, and renders it into the output writer.
-// The options parameter is optional.
-func (c CLI) Compile(input io.Reader, output io.Writer, options *CLIOptions) error {
- args := []string{"c"}
- if options != nil {
- args = append(args, options.Args()...)
- }
- args = append(args, "--diagnostic-format", "human", "-", "-")
-
+// Fonts returns all fonts that are available to Typst.
+// The options parameter is optional, and can be nil.
+func (c CLI) Fonts(options *OptionsFonts) ([]string, error) {
// Get path of executable.
execPath := ExecutablePath
if c.ExecutablePath != "" {
execPath = c.ExecutablePath
}
if execPath == "" {
- return fmt.Errorf("go-typst doesn't support this platform")
+ return nil, fmt.Errorf("not supported on this platform")
}
- cmd := exec.Command(execPath, args...)
+ if options == nil {
+ options = new(OptionsFonts)
+ }
+
+ cmd := exec.Command(execPath, options.Args()...)
+ cmd.Dir = c.WorkingDirectory
+
+ var output, errBuffer bytes.Buffer
+ cmd.Stdout = &output
+ cmd.Stderr = &errBuffer
+
+ if err := cmd.Run(); err != nil {
+ switch err := err.(type) {
+ case *exec.ExitError:
+ return nil, ParseStderr(errBuffer.String(), err)
+ default:
+ return nil, err
+ }
+ }
+
+ var result []string
+ scanner := bufio.NewScanner(&output)
+ for scanner.Scan() {
+ result = append(result, scanner.Text())
+ }
+
+ return result, nil
+}
+
+// Compile takes a Typst document from input, and renders it into the output writer.
+// The options parameter is optional, and can be nil.
+func (c CLI) Compile(input io.Reader, output io.Writer, options *OptionsCompile) error {
+ // Get path of executable.
+ execPath := ExecutablePath
+ if c.ExecutablePath != "" {
+ execPath = c.ExecutablePath
+ }
+ if execPath == "" {
+ return fmt.Errorf("not supported on this platform")
+ }
+
+ if options == nil {
+ options = new(OptionsCompile)
+ }
+
+ cmd := exec.Command(execPath, options.Args()...)
cmd.Dir = c.WorkingDirectory
cmd.Stdin = input
cmd.Stdout = output
@@ -91,13 +129,8 @@ func (c CLI) Compile(input io.Reader, output io.Writer, options *CLIOptions) err
return nil
}
-// CompileWithVariables takes a Typst document from input, and renders it into the output writer.
-// The options parameter is optional.
-//
-// Additionally this will inject the given map of variables into the global scope of the Typst document.
-//
-// Deprecated: You should use InjectValues in combination with the normal Compile method instead.
-func (c CLI) CompileWithVariables(input io.Reader, output io.Writer, options *CLIOptions, variables map[string]any) error {
+// Deprecated: You should use typst.InjectValues in combination with the normal Compile method instead.
+func (c CLI) CompileWithVariables(input io.Reader, output io.Writer, options *OptionsCompile, variables map[string]any) error {
varBuffer := bytes.Buffer{}
if err := InjectValues(&varBuffer, variables); err != nil {
diff --git a/cli_test.go b/cli_test.go
index c7158c7..b314c40 100644
--- a/cli_test.go
+++ b/cli_test.go
@@ -19,10 +19,48 @@ import (
func TestCLI_VersionString(t *testing.T) {
cli := typst.CLI{}
- _, err := cli.VersionString()
+ v, err := cli.VersionString()
if err != nil {
t.Fatalf("Failed to get typst version: %v.", err)
}
+
+ t.Logf("VersionString: %s", v)
+}
+
+func TestCLI_Fonts(t *testing.T) {
+ typstCaller := typst.CLI{}
+
+ result, err := typstCaller.Fonts(nil)
+ if err != nil {
+ t.Fatalf("Failed to get available fonts: %v.", err)
+ }
+ if len(result) < 4 {
+ t.Errorf("Unexpected number of detected fonts. Got %d, want >= %d.", len(result), 4)
+ }
+}
+
+func TestCLI_FontsWithOptions(t *testing.T) {
+ typstCaller := typst.CLI{}
+
+ result, err := typstCaller.Fonts(&typst.OptionsFonts{IgnoreSystemFonts: true})
+ if err != nil {
+ t.Fatalf("Failed to get available fonts: %v.", err)
+ }
+ if len(result) != 4 {
+ t.Errorf("Unexpected number of detected fonts. Got %d, want %d.", len(result), 4)
+ }
+}
+
+func TestCLI_FontsWithFontPaths(t *testing.T) {
+ typstCaller := typst.CLI{}
+
+ result, err := typstCaller.Fonts(&typst.OptionsFonts{IgnoreSystemFonts: true, FontPaths: []string{filepath.Join(".", "test-files")}})
+ if err != nil {
+ t.Fatalf("Failed to get available fonts: %v.", err)
+ }
+ if len(result) != 5 {
+ t.Errorf("Unexpected number of detected fonts. Got %d, want %d.", len(result), 5)
+ }
}
// Test basic compile functionality.
@@ -37,7 +75,7 @@ func TestCLI_Compile(t *testing.T) {
#lorem(5)`)
- opts := typst.CLIOptions{
+ opts := typst.OptionsCompile{
Format: typst.OutputFormatPNG,
PPI: ppi,
}
diff --git a/docker.go b/docker.go
new file mode 100644
index 0000000..2c398c5
--- /dev/null
+++ b/docker.go
@@ -0,0 +1,155 @@
+// Copyright (c) 2025 David Vogel
+//
+// This software is released under the MIT License.
+// https://opensource.org/licenses/MIT
+
+package typst
+
+import (
+ "bufio"
+ "bytes"
+ "fmt"
+ "io"
+ "os/exec"
+)
+
+// Theoretically it's possible to use the Docker SDK directly:
+// https://docs.docker.com/reference/api/engine/sdk/examples/
+// But that dependency is unnecessarily huge, therefore we will just call the Docker executable.
+
+// The default Docker image to use.
+// This is the latest supported version of Typst.
+const DockerDefaultImage = "ghcr.io/typst/typst:0.14.0"
+
+// Docker allows you to invoke commands on a Typst Docker image.
+type Docker struct {
+ Image string // The image to use, defaults to the latest supported offical Typst Docker image if left empty. See: typst.DockerDefaultImage.
+ WorkingDirectory string // The working directory of Docker. When left empty, Docker will be run with the process's current working directory.
+
+ // Additional bind-mounts or volumes that are passed via "--volume" flag to Docker.
+ // For details, see: https://docs.docker.com/engine/storage/volumes/#syntax
+ //
+ // Example:
+ // typst.Docker{Volumes: []string{".:/markup"}} // This bind mounts the current working directory to "/markup" inside the container.
+ // typst.Docker{Volumes: []string{"/usr/share/fonts:/usr/share/fonts"}} // This makes all system fonts available to Typst running inside the container.
+ Volumes []string
+}
+
+// Ensure that Docker implements the Caller interface.
+var _ Caller = Docker{}
+
+// args returns docker related arguments.
+func (d Docker) args() []string {
+ image := DockerDefaultImage
+ if d.Image != "" {
+ image = d.Image
+ }
+
+ // Argument -i is needed for stdio to work.
+ args := []string{"run", "-i"}
+
+ // Add mounts.
+ for _, volume := range d.Volumes {
+ args = append(args, "-v", volume)
+ }
+
+ // Which docker image to use.
+ args = append(args, image)
+
+ return args
+}
+
+// VersionString returns the Typst version as a string.
+func (d Docker) VersionString() (string, error) {
+ args := append(d.args(), "--version")
+
+ cmd := exec.Command("docker", args...)
+
+ var output, errBuffer bytes.Buffer
+ cmd.Stdout = &output
+ cmd.Stderr = &errBuffer
+
+ if err := cmd.Run(); err != nil {
+ switch err := err.(type) {
+ case *exec.ExitError:
+ return "", ParseStderr(errBuffer.String(), err)
+ default:
+ return "", err
+ }
+ }
+
+ return output.String(), nil
+}
+
+// Fonts returns all fonts that are available to Typst.
+// The options parameter is optional, and can be nil.
+func (d Docker) Fonts(options *OptionsFonts) ([]string, error) {
+ args := d.args()
+
+ if options == nil {
+ options = new(OptionsFonts)
+ }
+ args = append(args, options.Args()...)
+
+ cmd := exec.Command("docker", args...)
+
+ var output, errBuffer bytes.Buffer
+ cmd.Stdout = &output
+ cmd.Stderr = &errBuffer
+
+ if err := cmd.Run(); err != nil {
+ switch err := err.(type) {
+ case *exec.ExitError:
+ return nil, ParseStderr(errBuffer.String(), err)
+ default:
+ return nil, err
+ }
+ }
+
+ var result []string
+ scanner := bufio.NewScanner(&output)
+ for scanner.Scan() {
+ result = append(result, scanner.Text())
+ }
+
+ return result, nil
+}
+
+// Compile takes a Typst document from input, and renders it into the output writer.
+// The options parameter is optional, and can be nil.
+func (d Docker) Compile(input io.Reader, output io.Writer, options *OptionsCompile) error {
+ args := d.args()
+
+ // From here on come Typst arguments.
+
+ if options == nil {
+ options = new(OptionsCompile)
+ }
+ args = append(args, options.Args()...)
+
+ cmd := exec.Command("docker", args...)
+ cmd.Dir = d.WorkingDirectory
+ cmd.Stdin = input
+ cmd.Stdout = output
+
+ errBuffer := bytes.Buffer{}
+ cmd.Stderr = &errBuffer
+
+ if err := cmd.Run(); err != nil {
+ switch err := err.(type) {
+ case *exec.ExitError:
+ if err.ExitCode() >= 125 {
+ // Most likely docker related error.
+ // TODO: Find a better way to distinguish between Typst or Docker errors.
+ return fmt.Errorf("exit code %d: %s", err.ExitCode(), errBuffer.String())
+ } else {
+ // Typst related error.
+ return ParseStderr(errBuffer.String(), err)
+ }
+ default:
+ return err
+ }
+ }
+
+ return nil
+}
diff --git a/docker_test.go b/docker_test.go
new file mode 100644
index 0000000..74cbdbe
--- /dev/null
+++ b/docker_test.go
@@ -0,0 +1,139 @@
+// Copyright (c) 2025 David Vogel
+//
+// This software is released under the MIT License.
+// https://opensource.org/licenses/MIT
+
+package typst_test
+
+import (
+ "bytes"
+ "image"
+ "os"
+ "path/filepath"
+ "strconv"
+ "testing"
+
+ "github.com/Dadido3/go-typst"
+)
+
+// Returns the TYPST_DOCKER_IMAGE environment variable.
+// If that's not set, it will return an empty string, which makes the tests default to typst.DockerDefaultImage.
+func typstDockerImage() string {
+ return os.Getenv("TYPST_DOCKER_IMAGE")
+}
+
+func TestDocker_VersionString(t *testing.T) {
+ typstCaller := typst.Docker{
+ Image: typstDockerImage(),
+ }
+
+ v, err := typstCaller.VersionString()
+ if err != nil {
+ t.Fatalf("Failed to get typst version: %v.", err)
+ }
+
+ t.Logf("VersionString: %s", v)
+}
+
+func TestDocker_Fonts(t *testing.T) {
+ typstCaller := typst.Docker{
+ Image: typstDockerImage(),
+ }
+
+ result, err := typstCaller.Fonts(nil)
+ if err != nil {
+ t.Fatalf("Failed to get available fonts: %v.", err)
+ }
+ if len(result) < 4 {
+ t.Errorf("Unexpected number of detected fonts. Got %d, want >= %d.", len(result), 4)
+ }
+}
+
+func TestDocker_FontsWithOptions(t *testing.T) {
+ typstCaller := typst.Docker{
+ Image: typstDockerImage(),
+ }
+
+ result, err := typstCaller.Fonts(&typst.OptionsFonts{IgnoreSystemFonts: true})
+ if err != nil {
+ t.Fatalf("Failed to get available fonts: %v.", err)
+ }
+ if len(result) != 4 {
+ t.Errorf("Unexpected number of detected fonts. Got %d, want %d.", len(result), 4)
+ }
+}
+
+func TestDocker_FontsWithFontPaths(t *testing.T) {
+ typstCaller := typst.Docker{
+ Image: typstDockerImage(),
+ Volumes: []string{"./test-files:/fonts"},
+ }
+
+ result, err := typstCaller.Fonts(&typst.OptionsFonts{IgnoreSystemFonts: true, FontPaths: []string{"/fonts"}})
+ if err != nil {
+ t.Fatalf("Failed to get available fonts: %v.", err)
+ }
+ if len(result) != 5 {
+ t.Errorf("Unexpected number of detected fonts. Got %d, want %d.", len(result), 5)
+ }
+}
+
+// Test basic compile functionality.
+func TestDocker_Compile(t *testing.T) {
+ const inches = 1
+ const ppi = 144
+
+ typstCaller := typst.Docker{
+ Image: typstDockerImage(),
+ }
+
+ r := bytes.NewBufferString(`#set page(width: ` + strconv.FormatInt(inches, 10) + `in, height: ` + strconv.FormatInt(inches, 10) + `in, margin: (x: 1mm, y: 1mm))
+= Test
+
+#lorem(5)`)
+
+ opts := typst.OptionsCompile{
+ Format: typst.OutputFormatPNG,
+ PPI: ppi,
+ }
+
+ var w bytes.Buffer
+ if err := typstCaller.Compile(r, &w, &opts); err != nil {
+ t.Fatalf("Failed to compile document: %v.", err)
+ }
+
+ imgConf, imgType, err := image.DecodeConfig(&w)
+ if err != nil {
+ t.Fatalf("Failed to decode image: %v.", err)
+ }
+ if imgType != "png" {
+ t.Fatalf("Resulting image is of type %q, expected %q.", imgType, "png")
+ }
+ if imgConf.Width != inches*ppi {
+ t.Fatalf("Resulting image width is %d, expected %d.", imgConf.Width, inches*ppi)
+ }
+ if imgConf.Height != inches*ppi {
+ t.Fatalf("Resulting image height is %d, expected %d.", imgConf.Height, inches*ppi)
+ }
+}
+
+// Test basic compile functionality with a given working directory.
+func TestDocker_CompileWithWorkingDir(t *testing.T) {
+ typstCaller := typst.Docker{
+ Image: typstDockerImage(),
+ WorkingDirectory: filepath.Join(".", "test-files"),
+ Volumes: []string{".:/markup"},
+ }
+
+ r := bytes.NewBufferString(`#import "hello-world-template.typ": template
+#show: doc => template()`)
+
+ var w bytes.Buffer
+ err := typstCaller.Compile(r, &w, &typst.OptionsCompile{Root: "/markup"})
+ if err != nil {
+ t.Fatalf("Failed to compile document: %v.", err)
+ }
+ if w.Available() == 0 {
+ t.Errorf("No output was written.")
+ }
+}
diff --git a/documentation/images/readme-1.svg b/documentation/images/readme-1.svg
deleted file mode 100644
index 6b2fb72..0000000
--- a/documentation/images/readme-1.svg
+++ /dev/null
@@ -1,584 +0,0 @@
-
diff --git a/documentation/images/readme-example-injection.svg b/documentation/images/readme-example-injection.svg
new file mode 100644
index 0000000..c96bd54
--- /dev/null
+++ b/documentation/images/readme-example-injection.svg
@@ -0,0 +1,313 @@
+
diff --git a/documentation/images/readme-example-simple.svg b/documentation/images/readme-example-simple.svg
new file mode 100644
index 0000000..e046513
--- /dev/null
+++ b/documentation/images/readme-example-simple.svg
@@ -0,0 +1,571 @@
+
diff --git a/errors_test.go b/errors_test.go
index 0486ae2..35a8ce8 100644
--- a/errors_test.go
+++ b/errors_test.go
@@ -63,7 +63,7 @@ func TestErrors1(t *testing.T) {
func TestErrors2(t *testing.T) {
cli := typst.CLI{}
- opts := typst.CLIOptions{
+ opts := typst.OptionsCompile{
Pages: "a",
}
diff --git a/examples/passing-values/main.go b/examples/passing-values/main.go
index 6c75bd3..0cd7fe3 100644
--- a/examples/passing-values/main.go
+++ b/examples/passing-values/main.go
@@ -46,8 +46,8 @@ func main() {
}
defer f.Close()
- typstCLI := typst.CLI{}
- if err := typstCLI.Compile(&markup, f, nil); err != nil {
+ typstCaller := typst.CLI{}
+ if err := typstCaller.Compile(&markup, f, nil); err != nil {
log.Panicf("Failed to compile document: %v.", err)
}
}
diff --git a/examples/simple/main.go b/examples/simple/main.go
index 2b2f092..964dd32 100644
--- a/examples/simple/main.go
+++ b/examples/simple/main.go
@@ -30,8 +30,8 @@ This document was created at #%s.display() using typst-go.`, date)
}
defer f.Close()
- typstCLI := typst.CLI{}
- if err := typstCLI.Compile(&markup, f, nil); err != nil {
+ typstCaller := typst.CLI{}
+ if err := typstCaller.Compile(&markup, f, nil); err != nil {
log.Panic("failed to compile document: %w", err)
}
}
diff --git a/options.go b/options.go
new file mode 100644
index 0000000..f6b87d3
--- /dev/null
+++ b/options.go
@@ -0,0 +1,214 @@
+// Copyright (c) 2024-2025 David Vogel
+//
+// This software is released under the MIT License.
+// https://opensource.org/licenses/MIT
+
+package typst
+
+import (
+ "os"
+ "strconv"
+ "time"
+)
+
+type OutputFormat string
+
+const (
+ OutputFormatAuto OutputFormat = ""
+
+ OutputFormatPDF OutputFormat = "pdf"
+ OutputFormatPNG OutputFormat = "png"
+ OutputFormatSVG OutputFormat = "svg"
+ OutputFormatHTML OutputFormat = "html" // this format is only available since 0.13.0
+)
+
+type PDFStandard string
+
+const (
+ PDFStandard1_4 PDFStandard = "1.4" // PDF 1.4 (Available since Typst 0.14.0)
+ PDFStandard1_5 PDFStandard = "1.5" // PDF 1.5 (Available since Typst 0.14.0)
+ PDFStandard1_6 PDFStandard = "1.6" // PDF 1.6 (Available since Typst 0.14.0)
+ PDFStandard1_7 PDFStandard = "1.7" // PDF 1.7
+ PDFStandard2_0 PDFStandard = "2.0" // PDF 2.0 (Available since Typst 0.14.0)
+
+ PDFStandardA_1B PDFStandard = "a-1b" // PDF/A-1b (Available since Typst 0.14.0)
+ PDFStandardA_1A PDFStandard = "a-1a" // PDF/A-1a (Available since Typst 0.14.0)
+ PDFStandardA_2B PDFStandard = "a-2b" // PDF/A-2b
+ PDFStandardA_2U PDFStandard = "a-2u" // PDF/A-2u (Available since Typst 0.14.0)
+ PDFStandardA_2A PDFStandard = "a-2a" // PDF/A-2a (Available since Typst 0.14.0)
+ PDFStandardA_3B PDFStandard = "a-3b" // PDF/A-3b (Available since Typst 0.13.0)
+ PDFStandardA_3U PDFStandard = "a-3u" // PDF/A-3u (Available since Typst 0.14.0)
+ PDFStandardA_3A PDFStandard = "a-3a" // PDF/A-3a (Available since Typst 0.14.0)
+ PDFStandardA_4 PDFStandard = "a-4" // PDF/A-4 (Available since Typst 0.14.0)
+ PDFStandardA_4F PDFStandard = "a-4f" // PDF/A-4f (Available since Typst 0.14.0)
+ PDFStandardA_4E PDFStandard = "a-4e" // PDF/A-4e (Available since Typst 0.14.0)
+ PDFStandardUA_1 PDFStandard = "ua-1" // PDF/UA-1 (Available since Typst 0.14.0)
+)
+
+// OptionsFonts contains all supported parameters for the fonts command.
+type OptionsFonts struct {
+ FontPaths []string // Adds additional directories that are recursively searched for fonts.
+ IgnoreSystemFonts bool // Ensures system fonts won't be searched, unless explicitly included via FontPaths.
+ IgnoreEmbeddedFonts bool // Disables the use of fonts embedded into the Typst binary. (Available since Typst 0.14.0)
+ Variants bool // Also lists style variants of each font family.
+
+ Custom []string // Custom command line options go here.
+}
+
+// Args returns a list of CLI arguments that should be passed to the executable.
+func (o *OptionsFonts) Args() (result []string) {
+ // The first argument is the command we want to run.
+ result = []string{"fonts"}
+
+ if len(o.FontPaths) > 0 {
+ var paths string
+ for i, path := range o.FontPaths {
+ if i > 0 {
+ paths += string(os.PathListSeparator)
+ }
+ paths += path
+ }
+ result = append(result, "--font-path", paths)
+ }
+
+ if o.IgnoreSystemFonts {
+ result = append(result, "--ignore-system-fonts")
+ }
+
+ if o.IgnoreEmbeddedFonts {
+ result = append(result, "--ignore-embedded-fonts")
+ }
+
+ if o.Variants {
+ result = append(result, "--variants")
+ }
+
+ result = append(result, o.Custom...)
+
+ return
+}
+
+// OptionsCompile contains all supported parameters for the compile command.
+type OptionsCompile struct {
+ Root string // Configures the project root (for absolute paths).
+ Input map[string]string // String key-value pairs visible through `sys.inputs`.
+ FontPaths []string // Adds additional directories that are recursively searched for fonts.
+ IgnoreSystemFonts bool // Ensures system fonts won't be searched, unless explicitly included via FontPaths.
+ IgnoreEmbeddedFonts bool // Disables the use of fonts embedded into the Typst binary. (Available since Typst 0.14.0)
+ NoPDFTags bool // Disables the automatic generation of accessibility tags. These are emitted when no particular standard like PDF/UA-1 is selected to provide a baseline of accessibility. (Available since Typst 0.14.0)
+ CreationTime time.Time // The document's creation date. For more information, see https://reproducible-builds.org/specs/source-date-epoch/.
+ PackagePath string // Custom path to local packages, defaults to system-dependent location.
+ PackageCachePath string // Custom path to package cache, defaults to system-dependent location.
+ Jobs int // Number of parallel jobs spawned during compilation, defaults to number of CPUs. Setting it to 1 disables parallelism.
+
+ // Which pages to export. When unspecified, all document pages are exported.
+ //
+ // Pages to export are separated by commas, and can be either simple page numbers (e.g. '2,5' to export only pages 2 and 5) or page ranges (e.g. '2,3-6,8-' to export page 2, pages 3 to 6 (inclusive), page 8 and any pages after it).
+ //
+ // Page numbers are one-indexed and correspond to real page numbers in the document (therefore not being affected by the document's page counter).
+ Pages string
+
+ Format OutputFormat // The format of the output file, inferred from the extension by default.
+ PPI int // The PPI (pixels per inch) to use for PNG export. Defaults to 144.
+
+ // One (or multiple) PDF standards that Typst will enforce conformance with.
+ //
+ // See typst.PDFStandard for possible values.
+ PDFStandards []PDFStandard
+
+ Custom []string // Custom command line options go here.
+}
+
+// Args returns a list of CLI arguments that should be passed to the executable.
+func (o *OptionsCompile) Args() (result []string) {
+ // The first argument is the command we want to run.
+ result = []string{"c"}
+
+ if o.Root != "" {
+ result = append(result, "--root", o.Root)
+ }
+
+ for key, value := range o.Input {
+ result = append(result, "--input", key+"="+value)
+ }
+
+ if len(o.FontPaths) > 0 {
+ var paths string
+ for i, path := range o.FontPaths {
+ if i > 0 {
+ paths += string(os.PathListSeparator)
+ }
+ paths += path
+ }
+ result = append(result, "--font-path", paths)
+ }
+
+ if o.IgnoreSystemFonts {
+ result = append(result, "--ignore-system-fonts")
+ }
+
+ if o.IgnoreEmbeddedFonts {
+ result = append(result, "--ignore-embedded-fonts")
+ }
+
+ if o.NoPDFTags {
+ result = append(result, "--no-pdf-tags")
+ }
+
+ if !o.CreationTime.IsZero() {
+ result = append(result, "--creation-timestamp", strconv.FormatInt(o.CreationTime.Unix(), 10))
+ }
+
+ if o.PackagePath != "" {
+ result = append(result, "--package-path", o.PackagePath)
+ }
+
+ if o.PackageCachePath != "" {
+ result = append(result, "--package-cache-path", o.PackageCachePath)
+ }
+
+ if o.Jobs > 0 {
+ result = append(result, "-j", strconv.FormatInt(int64(o.Jobs), 10))
+ }
+
+ if o.Pages != "" {
+ result = append(result, "--pages", o.Pages)
+ }
+
+ if o.Format != OutputFormatAuto {
+ result = append(result, "-f", string(o.Format))
+ if o.Format == OutputFormatHTML {
+ // this is specific to version 0.13.0 where html
+ // is a feature than need explicit activation
+ // we must remove this when html becomes standard
+ result = append(result, "--features", "html")
+ }
+ }
+
+ if o.PPI > 0 {
+ result = append(result, "--ppi", strconv.FormatInt(int64(o.PPI), 10))
+ }
+
+ if len(o.PDFStandards) > 0 {
+ var standards string
+ for i, standard := range o.PDFStandards {
+ if i > 0 {
+ standards += ","
+ }
+ standards += string(standard)
+ }
+ result = append(result, "--pdf-standard", standards)
+ }
+
+ // Use human diagnostic format, as that's the format that we support right now.
+ // TODO: Switch to a different diagnostic format in the future
+ result = append(result, "--diagnostic-format", "human")
+
+ result = append(result, o.Custom...)
+
+ // Use stdio for input and output.
+ // TODO: Add Args parameters for when we want to use files instead
+ result = append(result, "-", "-")
+
+ return
+}
diff --git a/readme_test.go b/readme_test.go
index 0ae4480..bcf23d9 100644
--- a/readme_test.go
+++ b/readme_test.go
@@ -8,13 +8,83 @@ package typst_test
import (
"bytes"
"os"
+ "path/filepath"
"testing"
+ "time"
"github.com/Dadido3/go-typst"
)
func TestREADME1(t *testing.T) {
- r := bytes.NewBufferString(`#set page(width: 100mm, height: auto, margin: 5mm)
+ input, output, options := new(bytes.Reader), new(bytes.Buffer), new(typst.OptionsCompile)
+ // -----------------------
+ typstCaller := typst.CLI{}
+
+ err := typstCaller.Compile(input, output, options)
+ // -----------------------
+ if err != nil {
+ t.Fatalf("Failed to compile document: %v.", err)
+ }
+}
+
+func TestREADME3(t *testing.T) {
+ input, output, options := new(bytes.Reader), new(bytes.Buffer), new(typst.OptionsCompile)
+ // -----------------------
+ typstCaller := typst.Docker{}
+
+ err := typstCaller.Compile(input, output, options)
+ // -----------------------
+ if err != nil {
+ t.Fatalf("Failed to compile document: %v.", err)
+ }
+}
+
+func TestREADME4(t *testing.T) {
+ // -----------------------
+ typstCaller := typst.Docker{
+ Volumes: []string{"./test-files:/markup"},
+ }
+
+ r := bytes.NewBufferString(`#include "hello-world.typ"`)
+
+ var w bytes.Buffer
+ err := typstCaller.Compile(r, &w, &typst.OptionsCompile{Root: "/markup"})
+ // -----------------------
+ if err != nil {
+ t.Fatalf("Failed to compile document: %v.", err)
+ }
+}
+
+func TestREADME5(t *testing.T) {
+ // -----------------------
+ typstCaller := typst.Docker{
+ Volumes: []string{
+ "./test-files:/markup",
+ "/usr/share/fonts:/usr/share/fonts",
+ },
+ }
+ // -----------------------
+
+ if _, err := typstCaller.Fonts(nil); err != nil {
+ t.Fatalf("Failed to get available fonts: %v.", err)
+ }
+}
+func TestREADME6(t *testing.T) {
+ input, output := new(bytes.Reader), new(bytes.Buffer)
+ // -----------------------
+ typstCaller := typst.Docker{
+ Volumes: []string{"./test-files:/fonts"},
+ }
+
+ err := typstCaller.Compile(input, output, &typst.OptionsCompile{FontPaths: []string{"/fonts"}})
+ // -----------------------
+ if err != nil {
+ t.Fatalf("Failed to compile document: %v.", err)
+ }
+}
+
+func TestREADME7(t *testing.T) {
+ markup := bytes.NewBufferString(`#set page(width: 100mm, height: auto, margin: 5mm)
= go-typst
A library to generate documents and reports by utilizing the command line version of Typst.
@@ -27,15 +97,52 @@ A library to generate documents and reports by utilizing the command line versio
- Uses stdio; No temporary files need to be created.
- Test coverage of most features.`)
- typstCLI := typst.CLI{}
+ typstCaller := typst.CLI{}
- f, err := os.Create("output.pdf")
+ f, err := os.Create(filepath.Join(".", "documentation", "images", "readme-example-simple.svg"))
if err != nil {
t.Fatalf("Failed to create output file: %v.", err)
}
defer f.Close()
- if err := typstCLI.Compile(r, f, nil); err != nil {
+ if err := typstCaller.Compile(markup, f, &typst.OptionsCompile{Format: typst.OutputFormatSVG}); err != nil {
+ t.Fatalf("Failed to compile document: %v.", err)
+ }
+}
+
+func TestREADME8(t *testing.T) {
+ customValues := map[string]any{
+ "time": time.Now(),
+ "customText": "Hey there!",
+ "struct": struct {
+ Foo int
+ Bar []string
+ }{
+ Foo: 123,
+ Bar: []string{"this", "is", "a", "string", "slice"},
+ },
+ }
+
+ // Inject Go values as Typst markup.
+ var markup bytes.Buffer
+ if err := typst.InjectValues(&markup, customValues); err != nil {
+ t.Fatalf("Failed to inject values into Typst markup: %v.", err)
+ }
+
+ // Add some Typst markup using the previously injected values.
+ markup.WriteString(`#set page(width: 100mm, height: auto, margin: 5mm)
+#customText Today's date is #time.display("[year]-[month]-[day]") and the time is #time.display("[hour]:[minute]:[second]").
+
+#struct`)
+
+ f, err := os.Create(filepath.Join(".", "documentation", "images", "readme-example-injection.svg"))
+ if err != nil {
+ t.Fatalf("Failed to create output file: %v.", err)
+ }
+ defer f.Close()
+
+ typstCaller := typst.CLI{}
+ if err := typstCaller.Compile(&markup, f, &typst.OptionsCompile{Format: typst.OutputFormatSVG}); err != nil {
t.Fatalf("Failed to compile document: %v.", err)
}
}
diff --git a/test-files/Delius-Regular.ttf b/test-files/Delius-Regular.ttf
new file mode 100644
index 0000000..2cd4c9a
Binary files /dev/null and b/test-files/Delius-Regular.ttf differ
diff --git a/value-encoder_test.go b/value-encoder_test.go
index 657a8a8..9d088ea 100644
--- a/value-encoder_test.go
+++ b/value-encoder_test.go
@@ -195,10 +195,10 @@ func TestValueEncoder(t *testing.T) {
// Compile to test parsing.
if !tt.wantErr {
- typstCLI := typst.CLI{}
+ typstCaller := typst.CLI{}
input := strings.NewReader("#" + result.String())
var output bytes.Buffer
- if err := typstCLI.Compile(input, &output, nil); err != nil {
+ if err := typstCaller.Compile(input, &output, nil); err != nil {
t.Errorf("Failed to compile generated Typst markup: %v", err)
}
}