Better layout, better PDFs, better performance, better internals. Typst 0.12 is the result of over six months of hard work by us and many open-source contributors.

From its very beginnings in early 2019, our goal with Typst was to develop a fully-fledged typesetting engine that scales from the simplest to the most complex documents. We wanted to combine the best aspects of existing systems, learning lessons from the past, while mixing in new and unique ideas. Typst already delivers on many of its promises, yet a lot of work remains ahead of us.

With Typst 0.12, we set out to make meaningful progress on Typst's foundations. We took the last six months to work not just on shiny things, but to truly move the core systems of Typst forward. A lot of this work bears fruit today: Typst 0.12 ships with long-requested layout features, improved PDF output, and even better performance. But more happened than is visible at the surface: We also laid important foundations for upcoming work on things like HTML output, accessible PDFs, and more powerful layout.

In this blog post, I'll first walk you through the highlights of this release, then showcase some of the most exciting internal improvements, and finally explain how to migrate your existing documents to Typst 0.12.

Better layout

The headline feature of this release are floating figures spanning across multiple columns. By specifying scope: "parent" on a figure (or any floatingly placed element), you instruct Typst to place it across all columns.

Delivering this seemingly innocuous feature forced us to make a choice: Hack it in or build it right. We chose the latter option and fully re-engineered the core layout engine. This was obviously a lot of effort (and the main reason for this very slow release cycle). The good news is that it brings many more benefits than just multi-column floats. For example, the new engine brings improvements to widow/orphan prevention for headings (no more headings at the bottom of the page!) and fixes various issues with footnotes. It also lays the groundwork for future features like side floats and text flowing around shapes and images.

#set page(columns: 2)
#set columns(gutter: 15pt)
#set par(justify: true)

// In a real document, this would of course
// be in your template!
#place(
  top + center,
  float: true,
  scope: "parent",
  text(13pt)[*Essay on rectangles*]
)

#figure(
  placement: bottom,
  scope: "parent",
  rect(width: 70%),
  caption: [A normal rectangle],
)

Rectangles are often considered a superior
shape because of their versatility,
simplicity, and practicality. Their four
right angles and straight sides make them
easy to stack, arrange, and tile without
wasting space, making them ideal for
architecture, design, and packaging.
Rectangles also provide a stable and
balanced structure, whether in frames,
screens, or rooms, maximizing usable area
...
Preview

Another widely requested layout feature we're shipping in Typst 0.12 are line numbers. Typst can now add numbering in the margin for all text in your document, no matter how deeply nested. With line numbers, other people always know exactly which line you're talking about when you're reviewing their work. Line numbers can be flexibly enabled, tuned, and disabled for individual parts of your document. Refer to the par.line documentation for all the details.

#set par.line(numbering: "1")

Line numbers are useful when you're
submitting a document for review.

They are a great way to review PDFs as
reviewers can easily state which piece of
the document their comment applies to.

Though, thinking about it, wouldn't it be
nice if they could just leave a comment
directly --- let's say in a collaborative
web app?
Preview

There are various other layout improvements and I encourage you to read the changelog for all the details. There's one more thing I'd like to highlight here because it seems like a minor detail, but it actually improves Typst's output quite meaningfully: Tuned hyphenation.

When justification is enabled, Typst uses a paragraph layout algorithm that optimizes for a uniform visual appearance of paragraphs, only hyphenating words when necessary. In previous Typst versions, this hyphenation was somewhat over-eager, resulting in too many hyphens being inserted. In Typst 0.12, we tuned the costs involved in the layout engine's decision making and added new logic that penalizes hyphens close to the edges of a word.

Better PDFs

Typst 0.12 brings crucial improvements to PDF output:

  • Reduced file sizes: PDFs typically embed the necessary fonts for rendering. Fonts can be quite large, so it's important to reduce the size of fonts by removing all glyphs that aren't used in the document. Typst 0.12 ships with a new, much more space-saving subsetting implementation (particularly for .otf fonts) that also fixes a few important bugs leading to wrong printer output.

  • Support for emoji: Due to its age, PDF unfortunately does not natively support emoji fonts. They must instead be emulated with native PDF mechanisms. There are ✨ four ✨ competing standards for emoji fonts, but the good news is that Typst 0.12 ships with support for all of them.

  • PDF/A: The various PDF/A standards define stricter rules for PDFs to ensure long-term compatibility. More and more institutions require PDFs to comply with these rules. Typst can now emit files that comply with the PDF/A-2b profile. In the web app, there's a new PDF/A checkbox in the "File > Export As > PDF" menu and on the command line, you can pass the argument --pdf-standard a-2b. Note that this is only a first step; we want to support more PDF profiles in the future. In particular, we plan to ship PDF/UA support, which makes PDFs friendly to people who rely on assistive technologies.

A screenshot of the Typst app's PDF export modal with the PDF/A checkbox checked

Better performance

There is one fundamental law of compiler development: Your compiler can never be too fast. So we made Typst even faster with 0.12.

The most decisive improvement comes from multi-threading. Typst 0.11.1 was almost fully single-threaded. Meanwhile, Typst 0.12 can, under the right circumstances, make use of all your CPU cores. For now, this multi-threading operates on explicit pagebreak boundaries (note that the page breaks can be generated by show rules, they just need to be there). The speedup doesn't quite scale linearly with the number of CPUs, but we've observed typical speedups of 2-3x on normal hardware. But if you're a diligent writer and start your chapters on a new page, you can look forward to saturating your 16-core CPU. 🏎️

Aside from multi-threading, we've implemented a novel approach for faster yet still optimal layout of justified paragraphs. Depending on the paragraph length (it's better for shorter paragraphs), this can bring quite the speedup. We've observed up to 6x performance increases for documents consisting primarily of justified paragraphs.

We also tweaked incremental compilation to alleviate some missed caching opportunities, keeping your live preview snappy.

Better internals

While many things are going on at the surface, maybe even more changed internally. We used the last six months to rework and rewrite various subsystems of the Typst compiler, making them ready for the future:

Migrating your documents

The release contains a few breaking changes, which might affect your documents. The most important change: If you have two-column documents that use the columns function at the top level, you should migrate those to using a page set rule instead. This ensures that columns interact appropriately with floating figures, footnotes, and line numbers. See the relevant section of the page layout guide for more details on this, in particular on how to create a two-column title section in the new system.

// Typst 0.12 ✅
#set page(columns: 2)
#set columns(gutter: 20pt)

// Typst 0.11 and below ❌
#show: columns.with(2, gutter: 20pt)

The second important change relates to paragraph spacing. Instead of needing a show-set rule for block spacing, you can now set paragraph spacing directly. This simplifies a very common configuration option and brings it in line with how line spacing is already configured. Note that spacing for non-paragraph blocks (like headings) continues to be configured through show-set rules. For the curious ones, further motivation for these changes is given on GitHub.

// Typst 0.12 ✅
#set par(spacing: 1.5em)

// Typst 0.11 and below ❌
#show par: set block(spacing: 1.5em)

Typst 0.12 also deprecates various introspection functions that were superseded by Typst 0.11's context system.

// Typst 0.11+ ✅
#context query(heading)
#context here().position()

// Typst 0.10 and below ❌
#locate(loc => query(heading, loc))
#locate(loc => loc.position())

Many packages are affected by these deprecation warnings. Visit Typst Universe to check whether your favorite packages have already released a new version that switches away from the deprecated functionality. Note that warnings in packages are automatically hidden by the web app, but can be shown by clicking on "Show warnings in packages" in the Improve Panel.

That's it for this overview! Visit the comprehensive changelog for all the details about what changed in Typst 0.12.