# Typst as lib

Small wrapper around [Typst](https://github.com/typst/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

```rust
// 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() {
    let font = Font::new(Bytes::from(FONT), 0)
        .expect("Could not parse font!");

    // 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 = TypstTemplate::new(vec![font], TEMPLATE_FILE);

    // 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](https://github.com/Relacibo/typst-as-lib/blob/main/examples/small_example.rs)

#### Typst code

```typ
// 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:

```bash
cargo r --example=small_example
```

## Resolving files
### Binaries
Use `TypstTemplate::with_static_file_resolver` and add the binaries as key value pairs (`(file_name, &[u8])`).

### Sources
Use `TypstTemplate::with_static_source_file_resolver` and add the sources as key value pairs (`(file_name, String)`).



### Local files
Resolving local files can be enabled with `TypstTemplate::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:
```rust
let template = TypstTemplate::new(vec![font], TEMPLATE_FILE)
    .with_file_system_resolver("./examples/templates");
```

If you want to use another local package install path, use:
```rust
let template = TypstTemplate::new(vec![font], TEMPLATE_FILE)
    .add_file_resolver(
        FileSystemResolver::new("./examples/templates")
            .with_local_package_root("local/packages")
            .cached()
    );
```

### Remote Packages
The `package` feature needs to be enabled.

Can be enabled like this:
```rust
let template = TypstTemplate::new(vec![font], TEMPLATE_FILE)
    .with_package_file_resolver(None);
```

This uses the file system as a cache. 

If you want to use another cache root path, use:
```rust
let template = TypstTemplate::new(vec![font], TEMPLATE_FILE)
    .add_file_resolver(PackageResolverBuilder::new()
        .set_cache(
            FileSystemCache(PathBuf::from("cache/root"))
        )
        .build().cached()
    );
```

If you want to instead use the memory as cache, use:
```rust
let template = TypstTemplate::new(vec![font], TEMPLATE_FILE)
    .add_file_resolver(PackageResolverBuilder::new()
        .set_cache(
            InMemoryCache::new()
        )
        .build().cached()
    );
```

### Examples

#### Static binaries and sources

See [example](https://github.com/Relacibo/typst-as-lib/blob/main/examples/resolve_static.rs) which uses the static file resolvers.

```bash
cargo r --example=resolve_static
```

#### Local files and remote packages

See [example](https://github.com/Relacibo/typst-as-lib/blob/main/examples/resolve_packages.rs) which uses the file and the package resolver. 

```bash
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 `TypstTemplate::add_file_resolver` function.

## TypstTemplateCollection

If you want to compile multiple typst (main) source files you might want to use the `TypstTemplateCollection`, which allows you to specify the source file, when calling `TypstTemplateCollection::compile`, instead of passing it to new. The source file has to be added with `TypstTemplateCollection::add_static_file_resolver` first.
`TypstTemplate` is just a wrapper around `TypstTemplateCollection`, that also saves a `FileId` for the main source file.

## Loading fonts

Loading fonts is not in the scope of this library (yet?). If you are interested in that, write an issue.

- [This](https://github.com/typst/typst/blob/a2c980715958bc3fd71e1f0a5975fea3f5b63b85/crates/typst-cli/src/fonts.rs#L69) is how the typst-cli loads system fonts.
- Here is an [example](https://github.com/tfachmann/typst-as-library/blob/dd9a93379b486dc0a2916b956360db84b496822e/src/lib.rs#L216) of loading fonts from a folder.

## TODO
- allow usage of reqwest instead of ureq with a feature flag
- fonts

## Some links, idk

- [https://github.com/tfachmann/typst-as-library](https://github.com/tfachmann/typst-as-library)
- [https://github.com/KillTheMule/derive_typst_intoval](https://github.com/KillTheMule/derive_typst_intoval)
