iTranslated by AI

The content below is an AI-generated translation. This is an experimental feature, and may contain errors. View original article
📝

Early Deep Dive into Typst 0.12.0

に公開

Introduction

This article introduces the new features and changes in Typst 0.12.0, released on 2024/10/19.

Release Notes and Deep Dive Article List

Release Date Version Official Release Notes Deep Dive Article
2024/10/18 0.12.0 Official Release Notes Early Deep Dive into Typst 0.12.0
2025/02/19 0.13.0 Official Release Notes Early Deep Dive into Typst 0.13.0
2025/10/24 0.14.0 Official Release Notes Early Deep Dive into Typst 0.14.0

Pickups

Floating Figures Can Now Span Columns in Multi-column Documents

This is a featured addition highlighted by the official team. Prior to v0.11, using #figure() in a multi-column body only allowed placing floating figures within a single column. In v0.12, by specifying the scope property of figure() as "parent", it is now possible to place floating figures spanning across columns.


Example of placing a floating figure across columns. From the official blog post.

As an aside, the layout engine on the backend was reportedly rewritten to realize this feature. They also have future plans for a feature to place floating figures side-by-side with text wrapping around them. It's something to look forward to.

Official Emoji Support in PDF Output for Typst CLI

Until now, when outputting emojis using notations like #emoji.snowman in the Typst CLI, they were only displayed as outlines, as shown on the left in the figure below. Because of this, as of v0.11, workarounds such as using the svg-emojis package were necessary. In v0.12, they can now be displayed properly, as shown on the right in the figure below. This seems like it will be very useful when creating casual slides.

Note that in the author's environment, emojis were displayed correctly even when written directly as (U+26C4) instead of #emoji.snowman.


Emoji rendering results. Left: v0.11.0, Right: v0.12.0.

Paragraphs Can Now Have Line Numbers

By specifying the numbering property of the par.line function, it is now possible to add line numbers to the paragraph margins. This is mainly intended for use cases such as academic papers.


Example of adding line numbers to paragraphs. From the official blog post.

  • By default, numbers appear on the start side of the sentence (the left side for left-to-right documents). This behavior can be changed with the number-margin property.
  • Specifying the numbering-scope property allows you to change whether the count is "incremented throughout the entire document" or "reset per page."

[Breaking Change] Paragraph Spacing Can Now Be Specified Directly via par() Function Properties

Previously, when specifying paragraph spacing, it was necessary to do something like:

#show par: set block(spacing: 2em)

However, in v0.12, the new spacing property added to par() is used like this:

#set par(spacing: 2em)

This change is treated as a breaking change, so caution is required. Those who were setting paragraph spacing with the former method should update their settings to use the latter.

[Breaking Change] Block-level Elements Are Now Affected by block() Properties

Blocks created by functions such as list(), grid(), and stack() are now collectively affected by properties specified for the block() function. This behavior differs from previous versions, so the layout of existing documents may change.

#set page(height: auto)
#set block(stroke: 1pt + red, spacing: 100pt)

#lorem(20)

#lorem(20)

- Poem
  - Roses are red.
  - Violets are blue.


Changes in behavior when block properties are modified via set block. Left: v0.11.0, Right: v0.12.0.

Added a Property to Suppress Page Breaks Immediately After a Block

Using the block.sticky property, you can now suppress page breaks immediately following a block. This is useful in cases where you want to avoid a page break right after a heading.

Additional Variations for import Syntax

  1. In nested module structures, it is now possible to directly import grandchild elements.

    // Prior to v0.11, it was necessary to write:
    #import "@local/my-classfile:0.1.0": component; #import component: colors
    
    // From v0.12, you can write:
    #import "@local/my-classfile:0.1.0": component.colors
    
  2. Imported elements can now be enclosed in parentheses, allowing import statements to span multiple lines. This is convenient when you have many functions or modules to import.

    #import "@local/my-classfile:0.1.0": (
        component,
        layout,
    )
    

Addition of the std Module

Typst defines various built-in functions, including high-frequency English words like text() and align(). Because of this, when programming in Typst, built-in functions were sometimes unintentionally overwritten (shadowed) by other variables.

// We want to define a function that defaults to center alignment, or uses the explicitly specified alignment.
// However, the code below fails to behave as intended because the built-in `align()` function is overwritten by the `align` argument.
#let myalign(align: center, body) = align(align, body)

// Therefore, it was necessary to avoid this by changing the name or other methods.
#let myalign(align_: center, body) = align(align_, body)

// It's a bit unsatisfying that the keyword argument name cannot be "align"...
#myalign(align_: right)[Example Text.]

In v0.12, the std module is available by default, resolving this issue.

// While `align` is shadowed, `std.align` remains accessible.
#let myalign(align: center, body) = std.align(align, body)

// Now you can smoothly use a keyword argument named "align"!
#myalign(align: right)[Example Text.]

Performance and File Size Improvements

While not new features, these are subtle yet welcome improvements.

  • The layout engine (the process of placing text and figures on the page) now operates with multi-threading. Placement can be performed in parallel starting from locations where explicit page breaks are made using functions like pagebreak(). Performance improvements can be expected in large-scale documents.

  • Paragraph justification, the process of aligning both ends of a paragraph, has been accelerated. It is particularly effective when there are many short paragraphs, reportedly up to six times faster.

  • By improving font subsetting methods, the file size of PDF outputs has been reduced. This is a modest but pleasing change.

Other Changes

Due to the large number of changes, we will highlight some of them here, focusing mainly on functional additions. Bug fixes and other minor updates are omitted; if you want to know all the changes, please refer to the official Changelog.

  • Addition of the place.flush() function

    • By calling #place.flush(), you can display all preceding floating figures before showing the subsequent content.
  • Addition of the skew() function

    • Combined with scale() and rotate(), any affine transformation can be performed.
  • auto can now be specified for page.header / page.footer properties

    • Specifying auto automatically adds page numbers according to the page.numbering and page.number-align properties.
    • This behavior is the same as when the property is omitted, but prior to v0.11, once a property was explicitly specified, it couldn't be reset to the default behavior. With the addition of auto, it is now possible to return to the default.
  • Addition of repeat.gap and repeat.justify properties

    • When repeating characters like periods, you can now explicitly specify the spacing between characters. You can also specify whether to extend the repetition to fit the width.
  • fr units can now be used for the height property of block, image, rect, square, ellipse, and circle

    • Fractions (fr) are units that indicate the ratio when dividing the length of the parent element, similar to how they are used for grid widths. If you place two blocks with height: 1fr side-by-side, they will fill the gap so that both blocks have the same height.
    Example
    #set page(width: 200pt, height: 200pt, margin: 10pt)
    
    #block(width: 100%, height: 1fr, fill: red.lighten(80%), radius: 5pt)[
      #align(center + horizon)[#lorem(5)]
    ]
    
    #block(width: 100%, height: 1fr, fill: blue.lighten(80%), radius: 5pt)[
      #align(center + horizon)[#lorem(5)]
    ]
    

  • Lengths (not relative values) can now be directly specified for the x and y properties of the scale() function

    • In addition to specifications like "expand the width by 3 times," you can now specify "expand so that the width becomes 300pt."
  • The values of block.above and block.below can now be retrieved within context expressions.

  • The default font is now "Libertinus Serif"

    • This is the successor font to "Linux Libertine," which was the original default.
    • This change may cause slight variations in typesetting results.
  • Warnings are now issued when an unavailable font family is specified

    • Previously, such specifications were simply ignored.
  • Improved the algorithm for the smart quotes feature

  • Added the text.costs property to allow customization of cost function weights in the line-breaking algorithm

    • You can specify weight ratios for the following costs:
    • hyphenation (cost of breaking a single word with hyphenation)
    • runt (cost of a paragraph ending with a single word)
    • widow (cost of placing only the final line of a paragraph on the next page)
    • orphan (cost of placing only the first line of a paragraph on the previous page)
  • "Typst math mode" can now be specified as a highlighting language for code blocks

    • By specifying typm as the language, you can write and highlight only the math portions of Typst.
  • Basic internationalization (basic i18n) for the following languages:

    • Galician
    • Catalan
    • Latin
    • Icelandic
    • Hebrew
  • Implemented "hyphenation duplication" for some languages

    • Specifically for the following languages:
      • Czech
      • Croatian
      • Lower Sorbian
      • Polish
      • Portuguese
      • Slovak
      • Spanish
    • In these languages, when hyphenating, it is reportedly necessary to place a hyphen at the beginning of the next line as well as the end of the current line. https://github.com/typst/typst/issues/3235
  • smallcaps() is now an element function that can be specified with show or set rules

    • This makes it possible to do things like changing the font only when smallcaps() is used.
  • Syntax highlighting can now be turned off by specifying none for the raw.theme property

  • Multiple numbers can now be specified for the text.stylistic-set property

Mathematics

  • Block-level equations can now span across multiple pages

    • This behavior can be disabled with show math.equation: set block(breakable: false).
  • Matrix and vector sizes are now more consistent

    • I haven't grasped all the details, but it seems they are less affected by height differences caused by scripts and other elements.


      Appearance when composing a matrix. Note the difference in bracket sizes. Left: v0.11.0, Right: v0.12.0.

  • The stretch function was added, allowing glyphs like equal signs and arrows to be stretched.

    • By writing $ H stretch(=)^"define" U + p V $, you can create an equal sign stretched to the width of "define".
    • Only certain glyphs can be stretched, and this depends on the math font.
  • The mat.delim, vec.delim, and cases.delim properties now accept any Unicode character that can be considered a delimiter or fence.

    • A fence is a character such as "|" (U+007c), for example.
    • However, specifications like delim: "||" are no longer possible. It needs to be replaced with something like delim: bar.double.
  • The vec.align and mat.align properties have been added, allowing you to specify the alignment of matrix and vector elements.

    • Vectors composed of integers, for example, might look better when right-aligned.
  • Addition of underparen, overparen, undershell, and overshell functions

    • These are part of the underbrace family.
    • "Shell brackets" are what are known as tortoise shell brackets 〔〕.
  • ~ is now interpreted as a binary relation

    • ~ can now be used as a shorthand for what was previously written as tilde.op.
    • Note that parts previously written simply as tildes will now also be interpreted as tilde.op.

Other Changes in Notation and Typesetting

  • Restrictions on the #set document() rule have been relaxed, allowing it to be written outside the beginning of the document.

  • The spacing property in list(), enum(), and term() is now reflected even in tight lists.

  • In tight lists, the way spacing before the list is added has changed; it now becomes tight only when preceded by a paragraph.

  • The quote() element is now locatable (searchable via typst query, etc.).

  • The hanging-indent property was added to the heading() function to adjust the appearance of headings spanning multiple lines.

    • Default headings have also been improved; when spanning multiple lines, they are now automatically indented by the width of the numbering, such as "1."
  • The fill-rule property was added to the path() and polygon() functions.

    • The behavior when filling polygons with intersecting edges, like a star, can be adjusted using two values: "non-zero" or "even-odd".
  • Addition of the decimal type

    • Previously, the only type for decimals was the float type (floating-point numbers), but a new decimal type (fixed-point numbers) has been added. This allows for precise decimal calculations.
  • Addition of methods to the array type

    • to-dict(): Converts an array of the form ((k1, v1), (k2, v2), ...) into a dictionary (k1: v1, k2: v2, ...).
    • reduce(): Similar to the fold method, but uses the first element of the array as the initial value.
    • windows(): Creates sliding windows ((1, 2, 3), (2, 3, 4), (3, 4, 5)) from (1, 2, 3, 4, 5,).
  • Added the exact argument to the array.zip() method

    • When true is specified for the exact argument, an error occurs if the number of elements in the two combined arrays do not match. When false is specified, the remainder of the longer array is ignored as before.

Exporting

  • Partial support for PDF/A

    • By enabling PDF/A during export, it is now possible to output PDFs in a format more suitable for long-term preservation.
      • In the CLI, this can be enabled by specifying the --pdf-standard a-2b option.
    • Currently, only PDF/A-2b is supported.
  • Setting the page.fill property to none now results in a transparent background when exporting to PDF or SVG

    • To specify the previous behavior (transparent background for PDF, white background for PNG and SVG), set it to auto.

CLI and Tools

  • Added the --pages option

    • It is now possible to specify specific page numbers when exporting.
  • Added --package-path and --package-cache-path options

  • Added the --ignore-system-fonts flag

    • Disables the loading of system fonts. This is effective for reducing system-dependent elements and creating documents with higher reproducibility.
  • When exporting to multiple image files, t (total pages), p (current page), and 0p (zero-padded page) can now be specified in the output filename

Conclusion

As this is the first version update in about five months, it feels like there were a very large number of changes. I have been preparing this article since the initial release of 0.12.0-rc1, but there were so many eye-catching feature additions and bug fixes that it was quite a task to summarize them all. It was mentioned that this release was delayed due to a large-scale re-implementation, so the next version might arrive a bit sooner.

If you find any errors in this article, please let me know in the comments.

References

Discussion