# Generating ConTeXt documents from Julia with multiple dispatch

**URL:** https://discourse.julialang.org/t/generating-context-documents-from-julia-with-multiple-dispatch/139217
**Category:** General Usage
**Tags:** latex, idea, pdf
**Created:** [September 5, 2026, 1:14am UTC](https://discourse.julialang.org/t/generating-context-documents-from-julia-with-multiple-dispatch/139217 "2026-09-05T01:14:21Z")
**Posts on this page:** 3
**Page:** 1

<div class="post-metadata">

### Author: ![gxono](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gxono/32/221100_2.png) [@gxono](https://discourse.julialang.org/u/gxono)
#### Post date: [September 5, 2026, 1:14am UTC](https://discourse.julialang.org/t/generating-context-documents-from-julia-with-multiple-dispatch/139217/1 "2026-09-05T01:14:22Z")

</div>

I built a Julia pipeline that processes survey data and produces a PDF report for each course: pull the data, build frequency tables and charts with CairoMakie, then compile the final report with ConTeXt. I already knew ConTeXt from before this project, so it was the obvious choice for the typesetting side.

For anyone who doesn’t know it (I’d guess most people here come from LaTeX): ConTeXt is another TeX-based typesetting system, written by Hans Hagen, with a different macro syntax. Instead of `\begin{itemize}...\end{itemize}` it uses `\startitemize`/`\stopitemize` pairs, and the naming is fairly predictable: `\setupXXX` configures something globally, `\defineXXX` creates a new environment. A simple example:

```julia-auto
\starttext
  \startitemize
    \item One
    \item Two
  \stopitemize
\stoptext

```

A table:

```julia-auto
\bTABLE
  \bTABLEhead
    \bTR
      \bTH Name \eTH
      \bTH Age \eTH
    \eTR
  \eTABLEhead
  \bTABLEbody
    \bTR
      \bTD Ana \eTD
      \bTD 30 \eTD
    \eTR
  \eTABLEbody
\eTABLE

```

You can also define your own environments. In my report I needed label/value pairs where the value sometimes runs long and wraps to a second line, and I wanted that second line to stay aligned under the first instead of falling back to the margin. `\definedescription` handles that out of the box:

```julia-auto
\definedescription[metadato][alternative=left, width=6cm, headstyle=\bold]

\startmetadato{Course}
Long course name that might wrap onto a second line
\stopmetadato

```

One practical advantage over LaTeX I noticed right away: character handling. My whole report is in Spanish, accented vowels, ñ, ¿, ¡ everywhere, and with ConTeXt (the LuaMetaTeX engine) that just works, no encoding declarations needed. With traditional LaTeX you eventually run into `inputenc`/`fontenc`, or have to switch to xelatex/lualatex for the same thing.

## The problem I had

Generating all of this from Julia by just hacking together strings, things like `"\\startbloque[title={$titulo}]" * content * "\\stopbloque"`, works at first but falls apart fast once the document has any real structure: nested blocks, tables, figures, conditional cases. I’d end up with brackets and parens everywhere and no idea if it was balanced until the compile failed.

## What I built

A module (`ConTeXtJuliaDocument`) with one function per macro I use, each with two methods via multiple dispatch: one that returns the markup as a `String`, and one that takes an `IO` and writes straight to it (so I’m not holding giant strings in memory for long documents).

```julia
startbloque(setup::String)::String = "\\startbloque[$setup]"
startbloque(io::IO, setup::String) = (write(io, startbloque(setup)); io)

stopbloque()::String = "\\stopbloque"
stopbloque(io::IO) = (write(io, stopbloque()); io)

```

To be clear, each function’s implementation is trivial, it’s literally a one-line string interpolation. There’s nothing clever in the code itself, what I want to talk about is the general pattern, not the implementation.

Higher-level functions are built by composing the low-level ones, for example a paragraph:

```julia
paragraph(content::String)::String = startparagraph() * content * stopparagraph()
paragraph(io::IO, content::String) = (write(io, paragraph(content)); io)

```

And here’s what actually using it looks like:

```julia
const C = ConTeXtJuliaDocument

C.startbloque(io, "title={Results}")
C.paragraph(io, "Block text.")
C.stopbloque(io)

```

On top of that I built higher-level things, like a `natural_table` function that assembles a whole table from a header, a matrix, and a footer, without the caller needing to know anything about `\bTABLE`.

Something I really liked about multiple dispatch for this specific case: ConTeXt macros don’t have one fixed way of being called, and it’s not just “more or fewer arguments.” Sometimes the meaning changes depending on how many bracket groups you pass. I have a real example of this in my code with `definecolorgroup`: `\definecolorgroup[name]` references a group that’s already defined, but `\definecolorgroup[name][specification]` defines it from scratch, and there the first argument stops being “the settings” and becomes a name/tag instead:

```julia
definecolorgroup(name::String) = "\\definecolorgroup[$name]"
definecolorgroup(name::String, setup::String) = "\\definecolorgroup[$name][$setup]"

```

In another language I’d handle this with optional arguments and an `if` inside the function checking how many showed up. In Julia each form is just another method, with a different arity, under the same name, and dispatch picks the right one without me asking anything.

One honest note: this isn’t an original idea of mine. ConTeXt already has something similar in Lua (the LuaMetaTeX engine has its own interface for building documents by calling functions that mirror each macro, things like `context.startbloque()`). What I did is basically the same thing in Julia, leaning on multiple dispatch instead of whatever Lua does under the hood.

## Something that’s been nagging at me

I never thought of this as something reusable, it was just for my own project. At some point I wondered if this would actually fit better as Julia macros instead of functions, so I wouldn’t have to pair up start/stop by hand. For example, for a paragraph:

```julia-auto
\startparagraph
Paragraph content.
\stopparagraph

```

could be something like

```julia
@paragraph begin
    "Paragraph content."
end

```

And for something smaller, like a list item (`\startitem Content \stopitem`), just

```julia
@item "Content"

```

But I never actually tried it, it’s just an idea that’s been rattling around.

One more thing I’m clear on: I only wrapped the handful of macros I actually use in my report. ConTeXt has an enormous number of macros (hundreds, between the core and all the modules), so if this were ever turned into a real library it would need to be extremely consistent in how each wrapper is named and structured, because writing them all by hand one by one doesn’t scale. It would probably need to generate them somehow from ConTeXt’s own definitions, instead of copying macro by macro the way I did.

---

<div class="post-metadata">

### Author: ![GunnarFarneback](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gunnarfarneback/32/1827_2.png) [@GunnarFarneback](https://discourse.julialang.org/u/GunnarFarneback)
#### Post date: [September 5, 2026, 5:53pm UTC](https://discourse.julialang.org/t/generating-context-documents-from-julia-with-multiple-dispatch/139217/2 "2026-09-05T17:53:17Z")

</div>

If you are happy with and invested in ConTeXt, go for it. For other people I’ll note that some more common ways to automatically create a PDF report is via

- Markdown + Pandoc
- Quarto
- Typst

For my part I’ve entirely switched over to Typst.

---

<div class="post-metadata">

### Author: ![icweaver](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/icweaver/32/45764_2.png) [@icweaver](https://discourse.julialang.org/u/icweaver)
#### Post date: [September 5, 2026, 11:27pm UTC](https://discourse.julialang.org/t/generating-context-documents-from-julia-with-multiple-dispatch/139217/3 "2026-09-05T23:27:38Z")

</div>

Same, Typst has been so nice to work with.
