Skip to content

Bundle export#7964

Merged
laurmaedje merged 45 commits intomainfrom
bundle-export
Mar 11, 2026
Merged

Bundle export#7964
laurmaedje merged 45 commits intomainfrom
bundle-export

Conversation

@laurmaedje
Copy link
Copy Markdown
Member

This pull request implements a new experimental export target that can emit multiple files from a single Typst project. The implementation mostly follows the design laid out in #7735.

The new export target is primarily useful for multi-page websites, but is not limited to that. You can create bundles containing any combination of HTML pages, PDFs, PNGs, SVGs, and arbitrary assets.

The primary motivation to ship this new target now is to use it in the ongoing work on porting the Typst documentation to Typst. However, it's also a big step in generally making HTML export more practically useful.

Example

typst compile main.typ --format bundle --features bundle,html
#document("index.html", title: "Home")[
  #title()
  - #link(<blog>)[Go to blog]
]

#document("blog.html", title: "Blog")[
  #title()
  Welcome to my blog!

  ...

  This blog also exists as a
  #link(<blog-pdf>)[single PDF].
] <blog>

#document("blog.pdf", title: "Blog")[
  ...
] <blog-pdf>

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

In the example, we create two HTML documents: A home page and a blog. The home page links to the blog through a label link. Typst's built-in linking mechanism natively supports cross-document links and resolves the correct relative paths for you. The bundle also contains a PDF version of the blog, which is linked from the HTML version. In practice, you could now share the content between the HTML and PDF version by storing it in a variable and using it in both. This is omitted here for brevity. Finally, the bundle contains an icon asset for the website. In this case, we're providing the asset's data by reading a file from disk. Alternatively, it's also possible to generate asset data from within Typst (e.g. via a function like json.encode).

Scope

Compared to the design issue, this is a slightly scoped down version. Specifically, for now, I omitted the ppi, pages, and pdf parameters to document. Instead, export of individual documents respects the usual CLI parameters. In principle, I still want to expose these configuration options at the Typst level, but it's not blocking for landing the work itself and also not necessary for the docs work. Moreover, to make CLI settings and in-document settings interact cleanly, a few things need sorting out first.1

As mentioned in the issue, I've also decided to accept that introspections are always global to the bundle for the time being. Often, this is exactly what you want, but in some cases, it's unwieldy. I don't think there's a simple clean "fix" for that. Rather, improving this will be more of a long-term process of evolving the introspection subsystem.

Notes

The history is carefully crafted with jj :), so if you want to take a look at what's changed / how it's implemented, I invite you to read the commits in isolation.

Linked issues

Closes #7735

Footnotes

  1. Primarily that SOURCE_DATE_EPOCH is not perfectly handled at the moment. It behaves exactly like --creation-timestamp while in reality it should behave more like if the system clock happened to be it at its timestamp. I'll open a separate issue about that.

@Andrew15-5
Copy link
Copy Markdown
Contributor

One step closer to docs in Typst!


After reading a bit about counters in #7735 (comment), I'm wondering if this "solves" #7342 as well. If you somehow can get counter(page.from(<doc>)).get().first(), then you can combine #7735 (comment) with #7342 (comment) and loop over document with different document.page and document.path. This should be enough to be a cumbersome workaround for the shell script, but run pages times faster, as the compiler is only run once.

@laurmaedje laurmaedje added interface PRs that add to or change Typst's user-facing interface as opposed to internals or docs changes. bundle Related to bundle export. labels Mar 11, 2026
This will be used in bundle export.
If you're using `typst` as a crate, you now need to depend on `typst-layout` separately to compile a paged document.
Not every output must be a document, c.f. the upcoming bundle output.
Documents are cloned due to memoization and deep-cloning the introspector isn't great.
The generic term `Position` being the more specific struct compared to `DocumentPosition` isn't great.
There is a name collision with link anchors here.
Previously, they were only emitted when linked to (at least since krilla). This was, however, not the original design intent.
In preparation of the bundle introspector working quite differently from the existing ones.
This will be useful for making paged documents hashable without hashing the introspector.
This will be useful for memoizing paged exports during bundle export.
@laurmaedje laurmaedje merged commit c93830c into main Mar 11, 2026
16 checks passed
@laurmaedje laurmaedje deleted the bundle-export branch March 11, 2026 11:56
/// window or the browser tab of the page.
///
/// By default, the configured title is not visibly rendered in the
/// document. You can add the title to the document's contents by using the
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// document. You can add the title to the document's contents by using the
/// document. You can add the title to the document's contents by using

Sorry, I know I'm late for this, but I thought I would point out this extra the.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bundle Related to bundle export. interface PRs that add to or change Typst's user-facing interface as opposed to internals or docs changes.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Multi-file output

6 participants