emfeltham / Typst.jl

Include Julia outputs as formatted elements of Typst documents.
MIT License
4 stars 0 forks source link
julia typst

Typst.jl

Include Julia outputs as formatted elements of Typst documents.

Introduction

Typst is a new system for markup-based typesetting, touted as an alternative to LaTeX. Typst is desirable as a system that is very fast to compile (near instantaneous live rendering), relatively simple to use with clear and clean syntax, and easily extensible with user-contributed packages. It has also recently been incorporated into Quarto. However, do note that this system is new and limited in various ways (e.g., Typst currently only renders to PDF).

This package will be useful to Julia users engaged in academic and professional writing, and may be a more parsimonious alternative to other common approaches to writing documents that rely on the outputs of programs (e.g, Jupyter[^rdme-1], Markdown-based solutions[^rdme-2], LaTeX).

[^rdme-1]: Which I find to be slow, and difficult to work with. Perhaps more importantly, they have been found difficult to replicate.

[^rdme-2]: These solutions, e.g., Quarto requires conversion of Markdown to TeX to render to PDF which may complicate detailed formatting, and is consequently subject to slow compilation. While Quarto now supports Typst, users may find that that indirectly producing Typst documents may cause similar complications and may defeat the purpose of Typst's clean user-end syntax and customizability.

The underlying philosophy here is to provide a simple set of functions that produce properly formatted Typst documents with captions, labels, and other features that may be easily embedded into larger Typst documents. These documents should be easily updated as models are re-estimated, figures and tables are changed, or reported values in the text change as the research and writing process take their course. The point is to provide a relatively simple framework, that does more with less.

The above-mentioned solutions make it possible to do this[^rdme-3]. However, I believe that it is useful to provide an option that does not additionally conflate program execution and writing. Quarto or RMarkdown documents may become unwieldy when execution times extend beyond those of simple toy examples. Generally, one does not want to re-run large models or execute long-running code to render a document. The outputs of programs will reasonably be updated asynchronously in relation to changes to the text.

Subsequently, the goal here is both to provide a set of ready-made functions common to an academic writing workflow (e.g., creating regression tables, including figures) along with more general means to customize outputs to Typst documents (e.g., export a custom table using simple defined types).

[^rdme-3]: Though, the support seems much greater for R than for Julia. For example, it is much more awkward to write Quarto documents that include text that updates based on the values of variables.

Tables

Tables are built using the typst-tablex package, which allows for the construction of more sophisticated tables than those made in base Typst.

Table construction for Typst is built around structs that correspond to tablex functions, subset under the TableX abstract type, including CellX, and HLineX, which constitute the building blocks of tables.

Regression tables[^rdme-4]

[^rdme-4]: Example adapted from GLM.jl.

Set up simple data set, and execute OLS.

using Typst
using DataFrames
using GLM

data = DataFrame(X=[1,2,3], Y=[2,4,7], Z = [4,5,6])

ols1 = lm(@formula(Y ~ X), data)
ols2 = lm(@formula(Y ~ Z), data)
ols3 = lm(@formula(Y ~ X + Z), data)

ms = [ols1, ols2, ols3];

Construct a formatted regression table for Typst:

regtable_typ(
    ms, "exampletable";
    caption = "Models of Y."
)

Produces the following output saved to a specified ".typ file (here, "exampletable.typ"):

The output contains code that imports the typst-tablex package, and defines two variables that control table column widths, which may be easily altered by the user. The table itself[^rdme-5] is embedded in a figure object, and is specified as a table type[^rdme-6].

[^rdme-5]: gridx objects are identical to tablex objects, but do not include vertical and horizontal lines by default.

[^rdme-6]: Which is useful if you want to, for example, set captions above the all table objects in your document, but not for other sorts of figures.

#import"@preview/tablex:0.0.8": tablex, gridx, hlinex, vlinex, colspanx, rowspanx, cellx
#let col1width = 12em
#let coliwidth = auto

#figure( 
    kind: table, 
    gridx( 
        columns: (col1width, coliwidth, coliwidth, coliwidth, ), 
        rows: (0.2em, 1.5em), 
        align: center + horizon, 
        hlinex(y: 0, stroke: 0.05em), 
        cellx(x: 0, y: 0, colspan: 4)[], 
        hlinex(y: 1, stroke: 0.05em), 
        cellx(x: 1, y: 1)[(1)], 
        cellx(x: 2, y: 1)[(2)], 
        cellx(x: 3, y: 1)[(3)], 
        hlinex(start: 1, y: 2, stroke: 0.05em), 
        cellx(x: 0, y: 6, align: left)[Z], 
        cellx(x: 2, y: 6)[2.5#super[$+$]], 
        cellx(x: 2, y: 7)[(0.289)], 
        cellx(x: 3, y: 6)[-0.222], 
        cellx(x: 3, y: 7)[(0.208)], 
        cellx(x: 0, y: 4, align: left)[X], 
        cellx(x: 1, y: 4)[2.5#super[$+$]], 
        cellx(x: 1, y: 5)[(0.289)], 
        cellx(x: 3, y: 4)[2.722], 
        cellx(x: 3, y: 5)[(0.487)], 
        cellx(x: 0, y: 2, align: left)[(Intercept)], 
        cellx(x: 1, y: 2)[-0.667], 
        cellx(x: 1, y: 3)[(0.624)], 
        cellx(x: 2, y: 2)[-8.167], 
        cellx(x: 2, y: 3)[(1.462)], 
        cellx(x: 3, y: 2)[0.0], 
        cellx(x: 3, y: 3)[(NaN)], 
        hlinex(start: 1, y: 8, stroke: 0.05em), 
        cellx(x: 0, y: 8, align: left)[N], 
        cellx(x: 0, y: 12, align: left)[BIC], 
        cellx(x: 0, y: 9, align: left)[$R^2$], 
        cellx(x: 0, y: 11, align: left)[AIC], 
        cellx(x: 0, y: 10, align: left)[Adjusted $R^2$], 
        cellx(x: 1, y: 8)[3], 
        cellx(x: 1, y: 12)[3.138], 
        cellx(x: 1, y: 9)[0.987], 
        cellx(x: 1, y: 11)[5.843], 
        cellx(x: 1, y: 10)[0.974], 
        cellx(x: 2, y: 8)[3], 
        cellx(x: 2, y: 12)[3.138], 
        cellx(x: 2, y: 9)[0.987], 
        cellx(x: 2, y: 11)[5.843], 
        cellx(x: 2, y: 10)[0.974], 
        cellx(x: 3, y: 8)[3], 
        cellx(x: 3, y: 12)[3.138], 
        cellx(x: 3, y: 9)[0.987], 
        cellx(x: 3, y: 11)[5.843], 
        cellx(x: 3, y: 10)[0.974], 
        hlinex(y: 13, stroke: 0.1em), 
        cellx(y: 13, colspan: 4, align: left)[_Note:_ $#super[+]p<0.10$; $#super[$star.op$]p<0.05$; $#super[$star.op star.op$]p<0.01$, $#super[$star.op star.op star.op$]p<0.001$] 
    ), 
    caption: [Models of Y.]
)

This file should be incorporated into your document via #include("exampletable.typ").

Figures

The package also provides a function to easily write a ".typ" file that will load a corresponding figure, formatted with a caption and label.

using DataFrames
using CairoMakie

data = DataFrame(X=[1,2,3], Y=[2,4,7], Z = [4,5,6]);

fg, ax, pl = scatter(data.X, data.Y)

The function will automatically handle directories on the path. N.B., the figure file extension is included in filename. Note also that output ".typ" file to load the figure expects the figure file to appear in the same directory (e.g., below, the "plot.png" should be saved in "dir/").

filename = "dir/plot.png"
caption = "Plot caption."

figure_typ(
    filename; caption, label = nothing, width_pct = 100
)

The following output is produced in "dir/plot.typ":

#figure(
   image("plot.png", width: 100%),
   caption: [Plot caption.],
) <plot>

This file should be incorporated into your document via #include("dir/plot.typ").

Tasks

Regression tables

Tables

Figures

Dynamic text

Types