# `Typster`
[🔗](https://github.com/mylanconnolly/typster/blob/v0.7.2/lib/typster.ex#L1)

High-level API for rendering Typst templates to PDF, SVG, and PNG formats.

Typster is an Elixir wrapper for the Typst document preparation system,
providing easy-to-use functions for compiling Typst templates with variable
binding, package support, and metadata injection.

## Quick Start

    # Simple PDF rendering
    source = "#set page(width: 200pt, height: 100pt)\n= Hello World"
    {:ok, pdf} = Typster.render_pdf(source)
    File.write!("output.pdf", pdf)

    # With variables
    template = "= Invoice for #customer_name"
    {:ok, pdf} = Typster.render_pdf(template, %{customer_name: "Acme Corp"})

    # With metadata
    {:ok, pdf} = Typster.render_pdf(template, %{},
      metadata: %{title: "Invoice", author: "Billing System"})

## Formats

Typster supports three output formats:
- **PDF**: Single binary output
- **SVG**: List of SVG strings (one per page)
- **PNG**: List of PNG binaries (one per page)

## Options

All render functions accept an options keyword list:
- `:variables` - Map of variables to bind into the template
- `:package_paths` - List of local package directories
- `:metadata` - Map of PDF metadata (title, author, description, keywords, date)
- `:pixel_per_pt` - PNG resolution (default: 2.0)

## Concurrency

Typster is fully thread-safe and supports concurrent rendering from multiple processes.
The underlying Rust NIFs can handle parallel execution efficiently:

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

    results = Task.await_many(tasks)

**Performance**: Tested with 100 concurrent renders completing in ~22ms total.

**Package Downloads**: Concurrent downloads of the same package are safely handled
with a mutex lock to prevent race conditions. The first process downloads, subsequent
processes wait and then use the cached package.

# `metadata`

```elixir
@type metadata() :: %{
  optional(:title) =&gt; String.t(),
  optional(:author) =&gt; String.t(),
  optional(:description) =&gt; String.t(),
  optional(:keywords) =&gt; String.t(),
  optional(:date) =&gt; String.t()
}
```

# `package_paths`

```elixir
@type package_paths() :: [String.t()]
```

# `pdf_binary`

```elixir
@type pdf_binary() :: binary()
```

# `png_pages`

```elixir
@type png_pages() :: [binary()]
```

# `render_options`

```elixir
@type render_options() :: [
  metadata: metadata(),
  package_paths: package_paths(),
  pixel_per_pt: float(),
  root_path: root_path(),
  variables: variables()
]
```

# `root_path`

```elixir
@type root_path() :: String.t()
```

# `svg_pages`

```elixir
@type svg_pages() :: [String.t()]
```

# `variables`

```elixir
@type variables() :: map()
```

# `check`

```elixir
@spec check(String.t(), render_options()) :: :ok | {:error, [String.t()]}
```

Check the syntax of a Typst template without rendering.

This function validates the template syntax by attempting to compile it,
but doesn't produce any output. It's useful for validating templates before
rendering or providing syntax feedback to users.

## Parameters
- `source` - The Typst template source code
- `opts` - Keyword list of options

## Options
- `:package_paths` - List of local package directories (default: [])
- `:root_path` - Root path for resolving relative imports (default: ".")
- `:variables` - Map of variables to bind (default: %{})

## Returns
- `:ok` if the template syntax is valid
- `{:error, errors}` where errors is a list of error messages

## Examples

    # Valid template
    :ok = Typster.check("= Hello World")

    # Invalid template
    {:error, errors} = Typster.check("= Unclosed #for")
    # errors will contain a list of error messages

    # With variables
    template = "= Report for #year"
    :ok = Typster.check(template, %{year: 2025})

    # With packages
    template = ~S(#import "@preview/tiaoma:0.3.0": qrcode)
    :ok = Typster.check(template, %{}, package_paths: [])

# `check!`

```elixir
@spec check!(String.t(), render_options()) :: :ok
```

Check the syntax of a Typst template, raising on error.

Same as `check/3` but raises `Typster.CompileError` if there are syntax errors.

## Examples

    # Valid template
    :ok = Typster.check!("= Hello World")

    # Invalid template - raises
    try do
      Typster.check!("= Invalid #syntax")
    rescue
      e in Typster.CompileError ->
        IO.puts("Syntax error: #{e.message}")
    end

# `render_pdf`

```elixir
@spec render_pdf(String.t(), render_options()) ::
  {:ok, pdf_binary()} | {:error, String.t()}
```

Render a Typst template to PDF format.

## Parameters
- `source` - The Typst template source code
- `opts` - Keyword list of options

## Options
- `:metadata` - Map of PDF metadata (default: %{})
- `:package_paths` - List of local package directories (default: [])
- `:root_path` - Root path for resolving relative imports (default: ".")
- `:variables` - Map of variables to bind (default: %{})

## Examples

    # Simple rendering
    {:ok, pdf} = Typster.render_pdf("= Hello World")

    # With variables
    template = "= Report for #year"
    {:ok, pdf} = Typster.render_pdf(template, variables: %{year: 2025})

    # With metadata
    {:ok, pdf} = Typster.render_pdf(
      template,
      variables: %{year: 2025},
      metadata: %{title: "Annual Report", author: "Corp"}
    )

    # With packages
    template = ~S(#import "@preview/tiaoma:0.3.0": qrcode
    #qrcode("https://example.com"))
    {:ok, pdf} = Typster.render_pdf(template, package_paths: [])

# `render_pdf!`

```elixir
@spec render_pdf!(String.t(), render_options()) :: pdf_binary()
```

Render a Typst template to PDF format, raising on error.

Same as `render_pdf/3` but raises `Typster.CompileError` on failure.

## Examples

    pdf = Typster.render_pdf!(template)
    pdf = Typster.render_pdf!(template, %{year: 2025})

# `render_png`

```elixir
@spec render_png(String.t(), render_options()) ::
  {:ok, png_pages()} | {:error, String.t()}
```

Render a Typst template to PNG format.

Returns a list of PNG binaries, one for each page in the document.

## Parameters
- `source` - The Typst template source code
- `opts` - Keyword list of options

## Options
- `:package_paths` - List of local package directories (default: [])
- `:pixel_per_pt` - Resolution in pixels per point (default: 2.0, higher = better quality)
- `:root_path` - Root path for resolving relative imports (default: ".")
- `:variables` - Map of variables to bind (default: %{})

## Examples

    {:ok, png_pages} = Typster.render_png("= Hello World")

    # High resolution
    {:ok, png_pages} = Typster.render_png(template, pixel_per_pt: 4.0)

    # Multi-page document
    template = "= Page 1\n#pagebreak()\n= Page 2"
    {:ok, [png1, png2]} = Typster.render_png(template)

# `render_png!`

```elixir
@spec render_png!(String.t(), render_options()) :: png_pages()
```

Render a Typst template to PNG format, raising on error.

Same as `render_png/3` but raises `Typster.CompileError` on failure.

# `render_svg`

```elixir
@spec render_svg(String.t(), render_options()) ::
  {:ok, svg_pages()} | {:error, String.t()}
```

Render a Typst template to SVG format.

Returns a list of SVG strings, one for each page in the document.

## Parameters
- `source` - The Typst template source code
- `opts` - Keyword list of options

## Options
- `:package_paths` - List of local package directories (default: [])
- `:root_path` - Root path for resolving relative imports (default: ".")
- `:variables` - Map of variables to bind (default: %{})

## Examples

    {:ok, svg_pages} = Typster.render_svg("= Hello World")
    # svg_pages is a list like ["<svg>...</svg>"]

    # Multi-page document
    template = "= Page 1\n#pagebreak()\n= Page 2"
    {:ok, [svg1, svg2]} = Typster.render_svg(template)

# `render_svg!`

```elixir
@spec render_svg!(String.t(), render_options()) :: svg_pages()
```

Render a Typst template to SVG format, raising on error.

Same as `render_svg/3` but raises `Typster.CompileError` on failure.

# `render_to_file`

```elixir
@spec render_to_file(String.t(), String.t(), render_options()) ::
  :ok | {:error, String.t()}
```

Render a Typst template and save to a file.

The output format is determined by the file extension:
- `.pdf` - PDF format
- `.svg` - SVG format (first page only for multi-page documents)
- `.png` - PNG format (first page only for multi-page documents)

## Parameters
- `source` - The Typst template source code
- `output_path` - Path to save the output file
- `opts` - Keyword list of options (same as format-specific functions)

## Examples

    Typster.render_to_file(template, "output.pdf")
    Typster.render_to_file(template, "output.svg", variables: %{title: "Report"})
    Typster.render_to_file(template, "output.png", pixel_per_pt: 4.0)

# `render_to_file!`

```elixir
@spec render_to_file!(String.t(), String.t(), render_options()) :: :ok
```

Render a Typst template to a file, raising on error.

Same as `render_to_file/4` but raises on failure.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
