How to provide assets (image files) with a template so that they could eventually be overwritten?

Hello. I have question regarding templates and images.

I made this template. The code sits in this repo.

The template is a cover page and some parts of it are made up of images. A set of images is provided with the template (located in ./template/logos).

The package structure is:

.
├── lib.typ
├── template
│   ├── logos
│   │   ├── archi.png
│   │   ├── FNR.png
│   │   ├── FNRS-en.png
│   │   └── FNRS-fr.png
│   └── main.typ
└── typst.toml

When creating a new project from the template in the Typst app, the #show function in main.typ does not allow making use of images outside the template folder.

This is the working default:

#import "@preview/lumen:0.1.0": cover
#show: cover(
  logo: "template/logos/archi.png",
  [...]
  fund-logo: "template/logos/FNRS-fr.png",
)

But what I try to make possible is that the user can upload own images and use them in the #show function:

#import "@preview/lumen:0.1.0": cover
#show: cover(
  logo: "science.png",
  [...]
  fund-logo: "new-fund.png",
)

Basically I need some help to understand how assets and link to images work for templates.
Thanks !

Hi there,

I believe this PR contains the answer to your question. It’s not released yet but you can access it: packages/docs/resources.md at 07b827f8915838224b2ff9aee162cf0e0c86d092 · typst/packages · GitHub

The proper way to let the people using your template overwrite the file to use is to take content as an argument directly, not a string. For example, you should replace this:

#let cover-page(logo-path: "logo.png", title) = {
  image(logo-path)
  heading(title)
}

With something like:

#let cover-page(logo: image("logo.png"), title) = {
  logo
  heading(title)
}

EDIT: Also this:

3 Likes

This is true for basic usage, but for something more complex a closure hack by @SillyFreak can be used:

#let cover(logo: none, doc) = {
  [Logo: #image(logo(), width: 69%)]
  doc
}

#import "@local/name:0.0.0": cover
#show: cover.with(logo: () => read("tiger.jpg", encoding: none))

You either have to mandate that users provide the closure or check the type and do stuff accordingly to it.

To make it slightly better, define an additional read function:

#let read-image = read.with(encoding: none)

#let cover(logo: none, doc) = {
  [Logo: #image(logo(), width: 69%)]
  doc
}

#import "@local/name:0.0.0": cover, read-image
#show: cover.with(logo: () => read-image("tiger.jpg"))

This is still not very complex, so here, if you just need to edit image settings, you can pass image.with("file"):

#show: cover.with(logo: image.with("tiger.jpg"))

#let cover(logo: none, doc) = {
  [Logo: #logo(width: 69%)]
  doc
}

If you want, for example (package structure does have 4 predefined logo file names), to override a directory full of assets, then I’m pretty sure logo: image => read("localdir/" + image) should work and is complex enough for the hack to be actually useful.

Thanks, that is exactly the explanation I was looking for !

I changed the image path strings with an image object each in this commit. I’ll stil have to make tests to see if it works as expected.

1 Like

Hi @mononym, glad you got a satisfying answer! Please don’t forget to mark it as the solution, so that others can more easily find it later. Thanks!