#typst #template #templating #file #lib #resolver

typst-as-lib

Small wrapper for typst that makes it easier to use it as a templating engine

23 releases (13 breaking)

0.14.1 Feb 22, 2025
0.13.0 Feb 22, 2025
0.11.1 Nov 11, 2024
0.8.0 Jul 22, 2024

#43 in Template engine

Download history 115/week @ 2024-11-17 88/week @ 2024-11-24 156/week @ 2024-12-01 157/week @ 2024-12-08 93/week @ 2024-12-15 47/week @ 2024-12-22 48/week @ 2024-12-29 136/week @ 2025-01-05 88/week @ 2025-01-12 150/week @ 2025-01-19 106/week @ 2025-01-26 178/week @ 2025-02-02 177/week @ 2025-02-09 686/week @ 2025-02-16 349/week @ 2025-02-23 200/week @ 2025-03-02

1,423 downloads per month
Used in mathlikeanim-rs

MIT license

125KB
1.5K SLoC

Typst as lib

Small wrapper around Typst that makes it easier to use it as a templating engine. This API is currently not really stable, although I try to implement the features with as little change to the API as possible.

Usage

rust code

// main.rs
static TEMPLATE_FILE: &str = include_str!("./templates/template.typ");
static FONT: &[u8] = include_bytes!("./fonts/texgyrecursor-regular.otf");
static OUTPUT: &str = "./examples/output.pdf";
static IMAGE: &[u8] = include_bytes!("./templates/images/typst.png");

fn main() {
    // Read in fonts and the main source file.
    // We can use this template more than once, if needed (Possibly
    // with different input each time).
    let template = TypstEngine::builder()
        .main_file(TEMPLATE_FILE)
        .fonts([FONT])
        .build();

    // Run it
    let doc = template
        .compile_with_input(dummy_data())
        .output
        .expect("typst::compile() returned an error!");

    // Create pdf
    let options = Default::default();
    let pdf = typst_pdf::pdf(&doc, &options)
        .expect("Could not generate pdf.");
    fs::write(OUTPUT, pdf).expect("Could not write pdf.");
}

Full example file

Typst code

// template.typ
#import sys: inputs

#set page(paper: "a4")
#set text(font: "TeX Gyre Cursor", 11pt)

#let content = inputs.v
#let last_index = content.len() - 1

#for (i, elem) in content.enumerate() [
  == #elem.heading
  Text: #elem.text \
  Num1: #elem.num1 \
  Num2: #elem.num2 \
  #if elem.image != none [#image.decode(elem.image, height: 40pt)]
  #if i < last_index [
    #pagebreak()
  ]
]

Run example with:

cargo r --example=small_example

Resolving files

Binaries and Sources

Use TypstEngineBuilder::with_static_file_resolver and add the binaries as key value pairs ((file_name, &[u8])) to add static binary files, that typst can use.

Use TypstEngineBuilder::with_static_source_file_resolver and add the sources as key value pairs ((file_name, String)) to add static Sources.

See example resolve_static which uses the static file resolvers.

cargo r --example=resolve_static

Local files

Resolving local files can be enabled with TypstEngineBuilder::with_file_system_resolver. The root should be the template folder. Files cannot be resolved, if they are outside of root.

Can be enabled like this:

let template = TypstEngine::builder()
    .main_file(TEMPLATE_FILE)
    .fonts([font])
    .with_file_system_resolver("./examples/templates")
    .build();

If you want to use another local package install path, use:

let template = TypstEngine::builder()
    .main_file(TEMPLATE_FILE)
    .fonts([font])
    .add_file_resolver(
        FileSystemResolver::new("./examples/templates")
            .local_package_root("local/packages")
            .into_cached()
    )
    .build();

Remote packages

The features package and one of ureq or reqwest need to be enabled.

Can be enabled like this:

let template = TypstEngine::builder()
    .main_file(TEMPLATE_FILE)
    .fonts([font])
    .with_package_file_resolver()
    .build();

This uses the file system as a cache.

If you want to use another cache root path, use:

let template = TypstEngine::builder()
    .main_file(TEMPLATE_FILE)
    .fonts([font])
    .add_file_resolver(PackageResolver::new()
        .cache(
            FileSystemCache(PathBuf::from("cache/root"))
        )
        .build()
        .into_cached()
    )
    .build();

Note that the Cache Wrapper created with the call to into_cached(self) creates a in memory cache for Binary files and Source files.

If you want to instead use the memory as (binary) cache, use:

let template = TypstEngine::build()
    .main_file(TEMPLATE_FILE)
    .add_fonts([font])
    .add_file_resolver(PackageResolver::builder()
        .cache(
            InMemoryCache::new()
        )
        .build()
        .into_cached()
    )
    .build();

Note that the Cache Wrapper created with the call to into_cached(self) only caches the Source files (each single file lazily) here and the InMemoryCache caches all binaries (eagerly after the first download of the whole package, which is triggered (lazily), when requested in a typst script).

Local files and remote packages example

See example resolve_packages which uses the file and the package resolver.

cargo r --example=resolve_files --features=package

Custom file resolver

You can also write your own file resolver. You need to implement the Trait FileResolver and pass it to the TypstEngineBuilder::add_file_resolver function. There you can also have some additional config options for the PackageFileResolver, FileSystemResolver, and so on.

Loading fonts

You can simply add fonts to the TypstEngine with TypstEngineBuilder::fonts. You can also activate the feature typst-kit-fonts that adds search_fonts_with to TypstEngineBuilder, which uses the typst-kit library to resolve system fonts. You also might additionally use the feature typst-kit-embed-fonts, that activates the feature embed-fonts for typst-kit. This causes typst-kit to also embed fonts from typst-assets at compile time.

See example font_searcher.

cargo r --example=font_searcher --features=typst-kit-fonts,typst-kit-embed-fonts

main file

The TypstEngine::main_file call is not needed, it's just for conveniance. You can omit it, and then you pass it to the TypstEngine::compile call later. (See example resolve_static)

TODO

  • maybe packages WASM support, if possible...

Previous work

Maybe useful

Dependencies

~51–69MB
~1M SLoC