Hacker News new | ask | show | jobs
by LAC-Tech 21 days ago
At the risk of making a fool of myself in-front of the rest of the class, I will come out and admit I don't know what the article is talking about.

> Last summer I spent a lot of time with Typst (at that point v0.11) and Pandoc, working on a flexible and reusable workflow to typeset markdown-formatted articles to PDF.

I understand that Typst is a markup language that can output a pdf file (big Typst fan btw).

I understand Pandoc is a thing that transforms documents of one kind to documents of another, ie markdown to html.

But the author wants to "typeset markdown-formatted article to PDF". Which makes me wonder what this has to do with typst at all.

2 comments

The author explain this in more detail in the article; I also was initially confused. The key details come from their broken down pandoc command. Specifically, these two flags:

  --pdf-engine=typst
  -V template=article.typ
Despite the input content being a .md file, we see that the .md contents populate the .typ file. Pandoc then understands how to convert the populated .typ file into a .pdf. The author also notes in their hyperlinked .typ document where the body content is placed:

  // THIS IS THE ACTUAL BODY:
What is not clear to me is the actual mechanism that tells pandoc where to place the body contents in the template. I presume it's some "magic" from the pandoc docs. The pandoc templates documentation[1] does reveal to us that `-V` is setting a variable called `template`. I don't have pandoc locally, but from another post[2] from the same author and a chatgpt query, it seems that the `body` symbol in the template is the chosen substitution symbol.

[1]: https://pandoc.org/demo/example33/6-templates.html

[2]: https://imaginarytext.ca/posts/2024/pandoc-typst-tutorial/

Pandoc templates use $...$ or ${...} for variable substitution, yes. body is one of the special default variables: the rest are documented in the manual. If you scroll to the bottom of the template linked from the article, you'll notice a $body$, along with a number of $if(...)$ $endif$ conditionals.

(This actually interferes with Typst's math mode. But you can manually construct math blocks, so no real problem. Pandoc variables are only valid within templates anyway.)

https://pandoc.org/MANUAL.html#variables-set-automatically

I'll give an example.

I write Python, software engineering, and data science books in Jupyter. (Because I want both text and code). I've written my own toolchain (multiple times, don't ask, yes, I've tried the one you're thinking of and it didn't work for me).

I need to convert the notebooks into chapters in my books (PDF so I can print them). In the past, I used code to convert to LaTeX. (It was horrible).

Now, I use code to convert the Jupyter file to markdown, then (I use pandoc too) to typst. (It is 100x better than LaTeX).

(I also use pandoc to convert markdown to epub).

> (multiple times, don't ask, yes, I've tried the one you're thinking of and it didn't work for me).

I know you didn't want questions, but maybe you can save me some trouble?

Assuming you're talking about quarto, may I ask what you didn't like about it? I've been converting some of my course materials to it and have been enjoying it immensely.

Quarto is one of them. When I checked it out, it wasn't usable for printed books (focused on papers). At that point (it's even easier now), I just painted my own bikeshed rather than dealing with others' weird angles and flooring.
For what's it's worth you can render Jupyter notebooks directly from Typst using the Callisto package. You can then style the notebook content as if it was written in Typst, using show rules, etc:

  #import "@preview/callisto:0.2.5"
  #callisto.render(nb: json("notebook.ipynb"))
though as the sibling comment says Quarto also works great for this, and Typst doesn't do epub (yet?)
Again, this is simplistic and not appropriate for a printed book...
I'm curious what would be a good example of something where this falls short of what you need?

I mean this Typst package just lets you import the notebook content in your Typst document. All the formatting is done in Typst which is also what you use for your final output...

Sidebars, front matter, index entries...
(Callisto author here)

Front matter would be fine I think. The static parts you would write directly in Typst. For the outline you would call #outline() as usual in the Typst document: notebook content is converted to Typst content (using cmarker), including the headings which become Typst headings so they show up as expected in the outline.

For sidebars I guess you use pandoc-Markdown extensions such as Divs that you transform with a Lua filter? What I would do then is use raw cells in the notebook to define sidebars (using Typst code in the raw cells) and configure Callisto to eval the raw cells.

For index entries, for example using the in-dexter package you could use HTML comments to insert index references where appropriate:

  <!--raw-typst #index[Entry]!-->
but I would just write #index[Entry] directly in the Markdown text, and use a show rule in the Typst document to convert them:

  #show regex("#index\[.*\]"): it => eval(it.text, mode: "markup", scope: in-dexter)
Overall, I think pandoc-Markdown + filters + Typst template is powerful and conceptually straightforward: you define the data structure with Markdown, you transform it with filters and render with Typst. Having the document as a well-defined data structure you can manipulate is especially powerful and something I wish we had in Typst.

In practice though it often feels overly complicated for little gains, when you can get things done with one tool and a single language (well, two since we're talking about including Jupyter notebooks).

Also working directly in Typst has advantages like live preview (also for content imported from notebooks), and some things that are a bit involved with pandoc, like maybe showing a cell output in a sidebar, become super easy: just add "#| label: my-cell" and "#| output: false" in the cell header, and #output("my-cell") in the side bar.