Typster

Typster is an Elixir wrapper for the Typst document preparation system, providing powerful and ergonomic functions for rendering Typst templates to PDF, SVG, and PNG formats.

CIHex.pmDocumentation

Features

Installation

Add typster to your list of dependencies in mix.exs:

def deps do
  [
    {:typster, "~> 0.4.0"}
  ]
end

Then run:

mix deps.get

Note: Precompiled binaries are available for macOS (ARM64/x86_64) and Linux (ARM64/x86_64). If a precompiled binary is not available for your platform, Typster will automatically compile from source, which requires Rust to be installed (see rustup.rs). You can force compilation from source by setting TYPSTER_BUILD=1.

Quick Start

Simple PDF Rendering

# Create a simple template
template = """
#set page(width: 8.5in, height: 11in)
#set text(size: 11pt)

= Hello from Typster!

This is a simple document rendered with Typster.
"""

# Render to PDF
{:ok, pdf} = Typster.render_pdf(template)

# Save to file
File.write!("output.pdf", pdf)

# Or use the convenience function
Typster.render_to_file(template, "output.pdf")

Variable Binding

template = """
= Invoice for #customer_name

*Date:* #invoice_date
*Amount:* \\$#amount

Thank you for your business!
"""

variables = %{
  customer_name: "Acme Corp",
  invoice_date: "2025-10-03",
  amount: 1234.56
}

{:ok, pdf} = Typster.render_pdf(template, variables: variables)

Nested Data Structures

template = """
= #user.name's Profile

*Email:* #user.email
*Location:* #user.address.city, #user.address.state
"""

variables = %{
  user: %{
    name: "Alice Johnson",
    email: "alice@example.com",
    address: %{
      city: "Portland",
      state: "OR"
    }
  }
}

{:ok, pdf} = Typster.render_pdf(template, variables: variables)

Lists and Iteration

template = """
= Shopping List

#for item in items [
  - #item.name: \\$#item.price
]

*Total Items:* #items.len()
"""

variables = %{
  items: [
    %{name: "Apples", price: 3.99},
    %{name: "Bread", price: 2.49},
    %{name: "Milk", price: 4.29}
  ]
}

{:ok, pdf} = Typster.render_pdf(template, variables: variables)

PDF Metadata

metadata = %{
  title: "Annual Report 2025",
  author: "Analytics Team",
  description: "Comprehensive performance analysis",
  keywords: "report, analytics, 2025",
  date: "auto"  # Use current date
}

{:ok, pdf} = Typster.render_pdf(template, variables: variables, metadata: metadata)

SVG and PNG Output

# Render to SVG (returns list of SVG strings, one per page)
{:ok, svg_pages} = Typster.render_svg(template)

# Render to PNG with custom resolution
{:ok, png_pages} = Typster.render_png(template, pixel_per_pt: 4.0)

# Save first page
File.write!("page1.png", List.first(png_pages))

Using Typst Packages

# Use packages from the Typst registry
template = """
#import "@preview/tiaoma:0.3.0": qrcode

= Contact Information

#qrcode("https://example.com", width: 3cm)
"""

# Packages are automatically downloaded and cached
{:ok, pdf} = Typster.render_pdf(template)

# Use multiple packages together
template = """
#import "@preview/tiaoma:0.3.0": qrcode
#import "@preview/cetz:0.3.2": canvas, draw

= Document with Packages

#qrcode("https://example.com", width: 2cm)

#canvas({
  import draw: *
  rect((0, 0), (3, 2), fill: blue.lighten(80%))
})
"""

{:ok, pdf} = Typster.render_pdf(template)

Package Features:

Bang Functions

# Use bang (!) versions for cleaner code
# (raises Typster.CompileError on failure)

try do
  pdf = Typster.render_pdf!(template, variables: variables)
  File.write!("output.pdf", pdf)
rescue
  e in Typster.CompileError ->
    IO.puts("Compilation failed: #{e.message}")
end

API Reference

Core Functions

Bang Variants

Options

All render functions accept the following options:

Examples

See the examples/ directory for complete working examples:

Documentation

Full documentation is available at HexDocs or can be generated locally:

mix docs
open doc/index.html

Testing

Run the test suite:

mix test

The test suite includes:

Run concurrent tests separately:

mix test test/concurrent_test.exs

Performance

Typster uses native Rust code via NIFs for high performance:

Concurrency

Typster is fully thread-safe and designed for concurrent use:

# Render multiple documents in parallel
tasks = for i <- 1..10 do
  Task.async(fn ->
    template = get_template(i)
    Typster.render_pdf(template, variables: %{id: i})
  end)
end

results = Task.await_many(tasks)

Tested Performance:

Thread Safety:

Typst Resources

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Acknowledgments