r/typst

Image 1 — An Open Source Desktop Visual Editor for Typst (self-promo)
Image 2 — An Open Source Desktop Visual Editor for Typst (self-promo)
Image 3 — An Open Source Desktop Visual Editor for Typst (self-promo)
▲ 3 r/typst

An Open Source Desktop Visual Editor for Typst (self-promo)

Hi r/typst,

I believe that this project will bring something rather new compare to a traditional editor.

Texpile is an open source visual and source editor for Typst (and also LaTeX and Markdown), and it runs on Windows, macOS, and Linux.

Here are some features:

  • It uses Tinymist for IntelliSense.
  • The visual editor supports includes, images, tables (with cell merging), code blocks, and other features.
  • Texpile supports visual comments, like Google Docs.
  • Texpile supports real time collaboration, and your connections are end to end encrypted to your collaborators. If you use VSCode Live Share, this will be a significantly better experience.
  • Zotero Integration
  • Git Support

You can see more features here: https://texpile.com/ ( Texpile does not bundle Tinymist, please install it by following this https://texpile.com/docs/installation/typst )

Typst support is new, so if there are any bugs, please let me know or open an issue.

u/PuzzleheadedShirt139 — 15 hours ago
▲ 11 r/typst

Is there an automated way to transfer a large amount of .md files to .typ?

I have all of my school notes in markdown from obsidian but am heavily considering switching to typst, as it seems like it has many many more features and would better integrate in the future with websites and other forms of distribution for other projects as well.

Wondering if there is a simple way to get the md syntax translated before I make some of my own changes to the docs with some more of the typst specific features.

Thanks all!

reddit.com
u/diddys_favorite — 2 days ago
▲ 2 r/typst

Need help setting up #outline() in Bundle

Edit: solved

#document("index.html", title: [Home])[
#title()
#set heading(numbering: "1.")
#outline()

#link(<blog>)[Go to blog]
]


#document("chapter-01.html", title: [Blog])[
#title()
Welcome to my blog!
= Heading 01
== Heading 02
=== Heading 03
==== Heading 04
===== Heading 05
This blog also exists as a
#link(<blog-pdf>)[single PDF].
] <blog>

#document("book.pdf", title: [Blog])[
#set heading(numbering: "1.")
#outline()
Welcome to my blog!
= Heading 01
== Heading 02
=== Heading 03
==== Heading 04
===== Heading 05
include "other chapters..."
] <blog-pdf>

#asset(
"favicon.ico",
read("images/favicon.ico", encoding: none),
)

cmd: typst compile main.typ dist --format bundle

The problem I am facing is that the outline produced in index.html contains TOC of both html and pdf. while I only want TOC of appropriate format.

reddit.com
u/abhinandh_s_ — 4 days ago
▲ 6 r/typst

A single function for equal-height stacking

Inspired by oasis-align https://www.reddit.com/r/typst/comments/1vnrcrp/oasisalign_v040_create_cleanly_aligned_layouts/ and automosaic https://www.reddit.com/r/typst/comments/1vksmw5/automosaic_automatic_aspectpreserving_photo/ I thought it would be nice to have a single function that performs this automatic sizing in the rawest form possible.

I've written a single function, a couple hundred lines of code, that extends the built-in `stack` function with automatic sizing:
- horizontal stacking with common height, or vertical with common width,

- stacking of any number of images,

- stacking of any number of paragraphs,

- stacking of one image with one paragraph (you can't generally stack multiple images and multiple paragraphs because of the way the maths works out: an image's height scales proportionally with its width and a paragraph's height scales inversely with its width, so a mixture with more than 2 items doesn't generally have a solution, I think!),

- it potentially works for any type of content you want, but this does have the catch that you need to know (and tell the function) two things about the content you pass to the function:

- does its width drive its size (this is the case for most content), or does its height drive its size

- does its height scale prop. or inv. prop. to its width

Here is the raw source, including a function that typesets proper documentation:

#let _width-for-height(element, l, max-width, volumes-fixed) = {
  let lo = 3em.to-absolute()
  let hi = max-width - lo
  for _ in range(30) {
    let mid = (lo + hi) / 2
    let h = measure(width: mid, element).height
    // != is XOR, which inverts the lhs if the rhs is true; this is because fixed-volume behaves inversely to fixed-ratio
    if (h <= l) != (volumes-fixed) {
      lo = mid
    } else {
      hi = mid
    }
  }
  if volumes-fixed { hi } else { lo }
}

#let _height-for-width(element, l, max-height, volumes-fixed) = {
  let lo = 3em.to-absolute()
  let hi = max-height - lo
  for _ in range(30) {
    let mid = (lo + hi) / 2
    let w = measure(height: mid, element).width
    // != is XOR, which inverts the lhs if the rhs is true; this is because fixed-volume behaves inversely to fixed-ratio
    if (w <= l) != (volumes-fixed) {
      lo = mid
    } else {
      hi = mid
    }
  }
  if volumes-fixed { hi } else { lo }
}

#let _evaluate_lengths(ratios, total-length) = {
  let length0 = total-length / (1.0 + ratios.slice(1).sum() / ratios.at(0))
  (length0,) + ratios.slice(1).map(r => r * length0 / ratios.at(0))
}

// ----------------------
// ----- Public API -----
// ----------------------

// Stack the given content in the given direction, automatically sizing the content such that they all have the same length perpendicular to the stack direction. The default direction is `ltr`, so the content is sized automatically to have the same height.
// Note that this will only work correctly with content whose size in the direction parallel to the stack direction can be driven, and whose size in the perpendicular direction depends on the size parallel. Generally speaking, this applies to content with a fixed aspect ratio (images), or a fixed volume (text):
//   - Any number of fixed aspect ratio contents may be stacked together.
//   - Any number of fixed volume contents may be stacked together; in such a case, set the parameter `volumes-fixed` to `true`.
//   - If mixing the two, it is only mathematically feasible with one of each; in such a case, leave the parameter `volumes-fixed` set to `false`.
// In general, the height of content is not driven, instead it is determined by a driving width. As such, by default, when stacking horizontally the heights can be measured directly, and when stacking vertically, widths must be solved for using binary search. If using content whose height drives its width, indicate this by setting the parameter `heights-drive` to `true` if all the given content is as such, or an array of equal length to `..args` with a boolean for each arg in `..args`.
#let stack-equalised(
  spacing: 0.5em,
  dir: ltr,
  volumes-fixed: false,
  heights-drive: false,
  ..args
) = layout(size => {
  if spacing == none {
    spacing = 0em
  }
  assert(type(spacing) == length, message: "`spacing` must be a length or none.")
  
  assert(type(volumes-fixed) == bool, message: "`volumes-fixed` must be a bool")
  
  assert(type(dir) == direction, message: "`dir` must be a direction")
  let vertical = dir.axis() == "vertical"
  
  assert(args.named().len() == 0, message: "Unrecognized named arguments: " + args.named().keys().join(", "))
  let elements = args.pos()
  let n = elements.len()
  assert(n > 1, message: "At least two elements must be provided.")
  
  let msg = "`heights-drive` must be a bool or an array of bools of equal length to `..args`"
  let heights-drive = if type(heights-drive) == array {
    assert(heights-drive.len() == n, message: msg)
    assert(heights-drive.map(e => type(e) == bool).fold(true, (a, b) => a and b), message: msg)
    heights-drive
  } else {
    assert(type(heights-drive) == bool, message: msg)
    (heights-drive,) * n
  }
  
  let total-length = ((if vertical { size.height } else { size.width }) - (n - 1) * spacing).to-absolute()
  
  let meausure_ratios(lengths) = {
    return elements.zip(lengths, heights-drive).map(t => {
      let element = t.at(0)
      let l = t.at(1)
      let height-drives = t.at(2)
      let measured = if vertical {
        if height-drives {
          measure(height: l, element).width
        } else {
          _width-for-height(element, l, size.width, volumes-fixed)
        }
      } else {
        if height-drives {
          _height-for-width(element, l, size.height, volumes-fixed)
        } else {
          measure(width: l, element).height
        }
      }
      if volumes-fixed {
        // volume conserved, so use the volume
        l.pt() * measured.pt()
      } else {
        // ratio conserved, so use the ratio
        l / measured
      }
    })
  }
  
  let iterate(ls) = {
    let ret = _evaluate_lengths(meausure_ratios(ls), total-length)
    let alpha = 0.5
    let ret = ls.zip(ret).map(t => (1.0 - alpha) * t.at(0) + alpha * t.at(1))
    let deltas = ls.zip(ret).map(t => t.at(0) - t.at(1))
    let mean-square-delta = deltas.map(e => e.pt() * e.pt()).sum() / n
    (mean-square-delta > 0.001, ret)
  }
  let (cont, lengths) = iterate((total-length / n,) * n)
  let max-iterations = 50
  let i = 0
  while cont and i < max-iterations {
    let t = iterate(lengths)
    cont = t.at(0)
    lengths = t.at(1)
    i += 1
  }
  
  stack(
    dir: dir,
    spacing: spacing,
    ..elements.zip(lengths, heights-drive).map(t => {
      let element = t.at(0)
      let l = t.at(1)
      let height-drives = t.at(2)
      if vertical {
        if height-drives {
          box(height: l, element)
        } else {
          box(width: _width-for-height(element, l, size.width, volumes-fixed), element)
        }
      } else {
        if height-drives {
          box(height: _height-for-width(element, l, size.height, volumes-fixed), element)
        } else {
          box(width: l, element)
        }
      }
    })
  )
})

#let explain() = [

#block(breakable: false)[
#let mono-font = "DejaVu Sans Mono"
#set highlight(extent: 2pt, radius: 4pt, top-edge: 1em, bottom-edge: -0.3em)
#let t-none = highlight(fill: yellow.mix(fuchsia).lighten(75%))[none]
#let t-length = highlight(fill: yellow.lighten(50%))[length]
#let t-direction = highlight(fill: aqua.mix(blue).lighten(75%))[direction]
#let t-bool = highlight(fill: yellow.lighten(50%))[bool]
#let t-array = highlight(fill: fuchsia.lighten(75%))[array]
#let t-content = highlight(fill: teal.lighten(50%))[content]

= Parameters

#block(stroke: gray, width: 100%, inset: 1em)[
  #set text(font: mono-font)
  #text(blue.mix(navy).lighten(25%))[stack-equalised]#text()[(]\ 
  #h(2em) spacing: #t-none #t-length,\
  #h(2em) dir: #t-direction,\
  #h(2em) volumes-fixed: #t-bool,\
  #h(2em) heights-driven: #t-bool #t-array,\
  #h(2em) ..#t-content,\
  ) $arrow.r$ #t-content
]

#let typ(content) = text(font: mono-font, content)

*`spacing`* #h(0.5em) #typ(t-none)  or #typ(t-length) #h(1fr) Default: `none`

The spacing between pieces of content.

#v(0.5em)

*`dir`* #h(0.5em) #typ(t-direction) #h(1fr) Default: `ltr`

The direction in which to stack the content.

#v(0.5em)

*`volumes-fixed`* #h(0.5em) #typ(t-bool) #h(1fr) Default: `false`

Whether the contents a have approximately fixed aspect-ratios (`false`) or volumes (`true`).

#v(0.5em)

*`heights-driven`* #h(0.5em) #typ(t-bool) or #typ(t-array) #h(1fr) Default: `false`

Whether the sizes of the pieces of content are driven by their width (`false`) or height (`true`). If a #typ(t-bool) is given, this is used for all pieces of content, otherwise an #typ(t-array) must be given of the same length as #typ([..#t-content]) containing a #typ(t-bool) for each piece of content.

#v(0.5em)

*`children`* #h(0.5em) #typ(t-content) #h(0.5em)  _Required_ #h(0.5em) _Positional_ #h(0.5em) _Variadic_

The children to stack along the axis.
]

= Summary

The function `stack-equalised` stacks content using the built-in Typst function `stack`, but sizes the content such that it all has the same height (if stacking horizontally) or width (if stacking vertically).

The extent to which is it possible to do this for some given pieces of content depends on how the widths and heights of the pieces of content depend on one another. Specifically, is the relationship positive or negative?
- Content mostly containing images whose aspect ratios are fixed will generally be taller if given more width, and _vice versa_;
- Content containing mostly text whose total space on the page is roughly fixed will generally be shorter if given more width, and _vice versa_.

Generally speaking, it is possible to stack any number of pieces of content in this way if _all_ of them have approximately fixed aspect ratios. This can be done by calling `stack-equalised` with `volumes-fixed: false` (the default).

It is also generally possible to stack any number of pieces of content in this way if _all_ of them have approximately fixed volumes. This can be done by calling `stack-equalised` with `volumes-fixed: true`.

It is generally possible to stack one fixed-aspect-ratio content next to one fixed-volume content in this way, but not more. This can be done by calling `stack-equalised` with `volumes-fixed: false`, passing the two pieces of content. I think the implementation can be improved upon for this particular use case, but it seems to work as-is, and this use case is already done very well by `oasis-align` (https://github.com/jdpieck/oasis-align/tree/main).

Generally speaking, content is sized by a given width, rather than by a given height, but this is now always the case. For the solver to correctly measure the size of the content it must know the driving dimension of each piece of content. As such, if passing a piece of content to the function whose height is drive, rather than its width, this must be indicated by giving a value to the parameter `heights-drive`:
- If all pieces of content have their sizes driven along the same axis, this can be given a single boolean value: `false` for the width drives (the default) or `true` for the height drives.
- If it is different for different pieces of content, pass an array of booleans, one for each piece of content in order.

= Equalised horizontal stacking of images

Consider that we have $N$ pieces of content whose aspect ratios are fixed (e.g. images). We wish to horizontally stack these pieces of content with appropriate sizes such that they fill the width of the page, and all have equal height.
  
This has an easy analytical solution as follows. Let
- $w_i$, $h_i$ and $r_i$ denote the width, height and aspect ratio of the $i$th piece of content respectively,
- $W$ denote the total available width in page.

Then,
$
  frac(w_i, h_i) = r_i, space.quad
  h_1 = h_2 = dots = h_N, space.quad
  arrow.r.double frac(w_1, r_1) = frac(w_2, r_2) = dots = frac(w_N, r_N), space.quad
  arrow.r.double w_i = frac(r_i, r_1) w_1,
$
and the widths of all the pieces of content must sum to $w$:
$
  sum_(i=1)^N w_i = w_1 (1 + frac(1, r_1) sum_(i=2)^N r_i) = W, space.quad
  arrow.r.double w_1 = frac(W, 1 + frac(1, r_1) sum_(i=2)^N r_i).
$
Given ${r_1, dots, r_N}$ and $W$, ${w_1, dots, w_N}$ can then be evaluated:
$
  w_i = frac(r_i, r_1) frac(W, 1 + frac(1, r_1) sum_(i=2)^N r_i).
$

In reality, however, many pieces of content we may wish to stack as such will not have exactly fixed aspect ratios. For example, if a figure has a caption below it, the caption's height will be fixed, so although the aspect ratio of the image above will be fixed, the aspect ratio of the figure and caption together will be function of the width available: $frac(w, h) = frac(w, h_"image" + h_"caption")$.

In such a case, we can make the assumption that the aspect ratios of the pieces of content are _approximately_ fixed (e.g. that captions are small relative to the resulting sizes of the images), and we can apply the same equations to solve the problem iteratively, re-evaluating the aspect ratios of pieces of content at each iteration. To evaluate the aspect ratio of piece of content, we use the `measure` function. In general, the width of content can be driven, not the height, so the aspect ratio is measured as:
```typst
#context {
  width / measure(width: width, content).height
}
```
Denoting iteration $j$ with superscript in brackets, and the function that measures the aspect ratio of piece of content $i$, given a width $w_i$ as $hat(r)_i (w_i)$, one iteration is evaluated as follows:
$
  w_i^((j + 1)) = (1 - alpha) w_i^((j))  + alpha frac(hat(r)_i (w_i^((j))), hat(r)_1 (w_1^((j)))) frac(W, 1 + frac(1, hat(r)_1 (w_1^((j)))) sum_(i=2)^N hat(r)_i (w_i^((j)))),
$
with the parameter $alpha in (0, 1]$ to control the speed of convergence.

= Equalised horizontal stacking of text

Consider now that we wish to achieve the same layout with pieces of content containing primarily text. Such content does not have a fixed aspect ratio, rather an approximately fixed _volume_. In such a case, denoting the volume of piece of content $i$ with $v_i$, we instead start with:
$
  w_i h_i = v_i, space.quad
  arrow.r.double frac(v_1, w_1) = frac(v_2, w_2) = dots = frac(v_N, w_N), space.quad
  arrow.r.double w_i = frac(v_i, v_1) w_1,
$
which is the same expression, as above, with $v_i$ substituted for $r_i$! As such, we can immediately arrive at the corresponding solution, changing only the evaluation of the volume of a piece of content to $hat(v)_i (w_i)$, implemented as:
```typst
#context {
  width.pt() * measure(width: width, content).height.pt()
}
```
(using `.pt()` as Typst cannot handle units of $"length"^2$);
$
  w_i^((j + 1)) = (1 - alpha) w_i^((j))  + alpha frac(hat(v)_i (w_i^((j))), hat(v)_1 (w_1^((j)))) frac(W, 1 + frac(1, hat(v)_1 (w_1^((j)))) sum_(i=2)^N hat(v)_i (w_i^((j)))).
$

= Equalised vertical stacking

Consider now that we wish to achieve the same layout but along a vertical axis, with equal width shared by all pieces of content. We may simply apply the same solutions, swapping the variables $w_i$ for $h_i$ and $W$ for the available height $H$, and making yet another change to the ratio/volume measurement. This solution now requires that the ratio/volume of each piece of content be measured given a driving value for height instead of width. As mentioned above, most content cannot have its height driven, so in such a case we must take an iterative approach.

We wish to estimate the width $w_i^*$ occupied by piece of content $i$ given a fixed height $h_i$, but we only have the ability to measure the height of that piece of content given a fixed width, denoted by the function $hat(h)_i (w)$. To do this for approximately fixed-aspect-ratio content, we use binary search (i.e. compute by bisection) to find the maximum width we can give to the content such that its height is no greater than $h_i$:
$
  w_i^* = sup { w in (0, W] | hat(h)_i (w) <= h_i },
$
and for approximately fixed-volume-content, the _minimum_ width we can give to the content such that its height is no greater than $h_i$:
$
  w_i^* = inf { w in (0, W] | hat(h)_i (w) <= h_i }.
$

In some rare cases we may have content whose height is driven, rather than its width. In such a case, this iterative approach may be employed (swapping widths for heights) when stacking horizontally, and the simple direct measurement may be employed when stacking vertically. A simple example of piece of content whose height is driven, rather than its width is as follows:
```typst
#let height-driven-box(aspect-ratio) = layout(size => {
  box(height: size.height, width: size.height / aspect-ratio, content)
})
```

]
reddit.com
u/_e_d_ — 4 days ago
▲ 122 r/typst

oasis-align v0.4.0 - Create Cleanly Aligned Layouts Within Your Documents

Hello everyone! I am happy to share another update for my Typst package oasis-align!

For those unfamiliar, oasis-align provides you with the tools to cleanly align content side-by-side. This is particularly useful for when you want to place to figures right next to each other with a common baseline, or want to create brief aside to main body of text.

Attached are some examples! Check out the README for the source.

What's New?

oasis-align() is now content aware, and will use the best strategy that best matches the kind of content its aligning. This includes being able to identify and align images within figures and ignoring long figure captions. Long caption example from README

I have also added a built in padding parameter for those times that you want the images to take a little less space on the page.

How does it work?

Fixed Aspect Ratio Content (Images & Figures)

This is the simplest and most common case. Here, we can create ratio of the widths and heights of the images, and then use that to determine the new widths using a set of equations.

Variable Aspect Ration Content (Text)

When we introduce text into the mix, it immediately becomes a much more challenging process. When changing the width of a block of text, the height does not scale linearly, but instead behaves as a step function that follows an exponential trend (the graph below has a simplified visualization of this). This prevents the use of an analytical methodology and thus must be solved using an iterative approach. Here I choose to use the bi-section method, though there are most likely better techniques out there.

Closing

Its been almost two years since I first published this package, and I am incredibly grateful to be able to give back to a community and project that I adore.

Until next time!
- Jason

u/jdpieck — 7 days ago
▲ 13 r/typst+3 crossposts

📢 TeXstudio 4.9.6 Stable Release - Qt6 Builds Available!

The latest stable version 4.9.6 of TeXstudio (Qt6) is now ready!

✅ Full-featured LaTeX editor ✅ Qt6-based builds ✅ Available in DEB and AppImage formats ✅ Ready for modern Linux distributions

🔗 Get it here: https://github.com/mlmateos/texstudio-qt6-builds

Happy TeXing! 🎉

#LaTeX #TeXstudio #OpenSource #Linux

u/Ok-Conclusion7016 — 5 days ago
▲ 57 r/typst

Package for creating Physics diagrams

Hello everyone!

I have released a new package called `typed-physics`, a drawing library for creating Physics diagrams in Typst.

The objective of this library is that, apart from drawing, it also understands physics. Therefore, the drawing of the diagrams, as well as the code used to create them, follows the physical relationships between the elements.

Here is an example of the package: the ramps, grounds, objects and forces interact together nicely instead of having to manually position them like another library would do.

https://preview.redd.it/8a314vggz5jh1.jpg?width=1902&format=pjpg&auto=webp&s=cd29e280e469af72189aed916f12e200ce58dad1

Here is also an example for springs. Multiple objects, forces and surfaces can be composed nicely.

https://preview.redd.it/axo8xrisz5jh1.jpg?width=1992&format=pjpg&auto=webp&s=92b31af6281c8923d90752d7a8f5ea75d85b0172

It currently supports: surfaces, blocks, pulleys, ropes, springs, applied forces, friction, and single-body mechanics on horizontal or inclined surfaces.

Every object and surface can be individually customized with colors, shapes, etc.

The package also offers a way to symbolically solve certain mechanics situations, although it does not support any arbitrary situation.

I’d love to hear your feedback, suggestions, or ideas for situations it should support next!

GitHub: https://github.com/GeronimoCastano/typed-physics
Typst Universe: https://typst.app/universe/package/typed-physics

Disclaimer: The package was developed with the help of Artificial Intelligence.

reddit.com
u/CordeElCrack — 7 days ago
▲ 65 r/typst

Making tiny booklets with Typst

I finally found a good excuse to try out Typst: Automating the layout of content for tiny 8-page booklets (or zines? not sure what they are called).

My approach was to break up the process into two stages:

  1. Render the content of the booklet to a normal document.
  2. Arrange the pages of this document onto one A4 page for printing and cutting a booklet.

On my blog I've written up details and links to an example project.

This is a pretty niche use case but it was an interesting first project and taught me a lot. You could probably use the same approach to layout a 16-page booklet like the one shown here.

u/25A0 — 9 days ago
▲ 137 r/typst

Mosaic: Create beautiful slides in Typst

Hi everyone!

I'm excited to announce the release of Mosaic, a new package to create slide shows in Typst.

Check out the documentation website. It hosts a ton of examples and tutorials: https://vincentarelbundock.github.io/mosaic

Here are some of the reasons you might want to try Mosaic:

  • Several polished, modern themes.
  • A simpler, more consistent API with very few functions to learn.
  • Every part of a slide is a native Typst layer with a stable label, so you style slides with ordinary set and show rules instead of learning a ton of framework-specific styling functions and arguments.
  • Every slide is a grid. Cells split horizontally or vertically and nest as deep as you need. This gives you a ton of control over layout.
  • Modular themes: A theme is a plain dictionary of independent parts which can be customized independently (layouts, colors, typography, etc.)
  • Batteries included: incremental reveals, callouts, cards, quotes, progress indicators, galleries, etc.

Please let me know if you try it, find bugs, or have feature requests. I'm very eager to improve the package!

A complete deck

#import "@preview/mosaic:0.0.1"

// Pick a theme by importing its facade. Every theme exposes the same API:
// swap `default` for `editorial`, `metropolis`, `manifesto`, or `mono`.
#import mosaic.themes.default as m

// `setup` is the only configuration call. It declares the deck and turns the
// rest of the document into slides.
#show: m.setup.with(title: [A short talk], authors: [Ada Lovelace])

// Styling is plain Typst: these rules apply to the whole deck.
#set text(font: "New Computer Modern", size: 26pt)
#show heading.where(depth: 1): set text(weight: "black")

// Every region of every slide carries a label, so an ordinary `show` rule can
// target one region. No Mosaic-specific styling function involved.
#show label("mosaic-cell-header"): set text(fill: red)

// An explicit slide, using the built-in title layout.
#m.slide(layout: "title")

// A level-one heading opens a section slide.
= Methods

// A slide defined implicitly by a level-two heading: everything until the next
// heading is its content.
== Data

+ One slide.
+ With stuff on it.

// Slides with more structure are explicit. Here we use the "content" layout
// preset and ask for two columns, then fill in the cells: header, then each
// column.
#m.slide(layout: m.layouts.content(variant: "header-body", columns: 2))[
  == Two columns
][
  Left column.
][
  Right column.
]

// A slide can also be an arbitrary tree of splits. Read it from the outside
// inward: three rows named "banner", a middle band, and "status"; the middle
// band splits into a "sidebar" column and a right column; that column splits
// into "chart" over "legend" and "notes".
#let custom = m.grids.rows(
  "banner",
  m.grids.columns("sidebar", m.grids.rows(
    "chart",
    m.grids.columns("legend", "notes"),
  )),
  "status",
)

// Cells are filled in tree order, and each one carries a label of its own.
#m.slide(layout: custom)[Banner][Sidebar][Chart][Legend][Notes][Status]
u/dudeski_robinson — 10 days ago
▲ 53 r/typst

automosaic: Automatic aspect-preserving (photo) layout engine

I created a (photo) layout package which preserves aspect-ratios and (optionally) computes an optimal layout automatically while filling the parent container as good as possible. You can flip through variations/"less optimal" layouts by changing a counter.

https://typst.app/universe/package/automosaic

I used it to create an 80-page photobook, so it is definitely very workable!

The call syntax is quite minimal:

#context display-auto-layout(

(

image("a.jpg"),

image("b.jpg"),

image("c.jpg"),

(body: image("d.jpg"), weight: 2), // this image gets more space in the auto-layout

// selector: "1",

)

u/shitHappensX — 10 days ago
▲ 33 r/typst+1 crossposts

Typst template for creating desk name tags for students

I’m usually very bad at retaining students names, so I’ve created this typst template the generates a PDF file with desk name tags for students. The input is a CSV file with the list of students.

Tags are easy to print and distribute among students. They only have to fold it in half and put it standing on the desk.

https://typst.app/universe/package/desk-tagger

Hope this is helpful!

u/Unlikely_Action_7893 — 13 days ago