/MakieTeX.jl

TeX integration in Makie

Primary LanguageJuliaMIT LicenseMIT

MakieTeX.jl

Stable Dev Build Status

LaTeX integration for Makie.jl

MakieTeX allows you to draw and visualize arbitrary vector documents (TEX, Typst, PDF, SVG) in Makie! You can insert anything from a single line of math to a large and complex TikZ diagram.

It works by compiling a stand-alone $\LaTeX$ document to PDF. For CairoMakie, the PDF is read and rendered directly, and a raster image is rendered in GLMakie.

Quick start

using Makie, MakieTeX
using CairoMakie # or whichever other backend
fig = Figure()
l1 = LTeX(
    fig[1, 1], L"A \emph{convex} function $f \in C$ is \textcolor{blue}{denoted} as \tikz{\draw[line width=1pt, >->] (0, -2pt) arc (-180:0:8pt);}";
    tellwidth = false, tellheight = true
)
ax1 = Axis(
    fig[2, 1];
)
heatmap!(ax1, Makie.peaks())
fig

You can also plot SVGs and PDFs in a similar manner using the SVGDocument and PDFDocument types. The easiest way to construct these is to use constructors of the form:

SVGDocument(read("file.svg", String))
PDFDocument(read("file.pdf", String))

and you can pass them in the same places you would pass CachedTeX.

Some examples of using PDF and SVG are in the documentation linked at the top of the README, as well as in other packages like SwarmMakie.

You need not install anything for MakieTeX to work, since we ship a minimal TeX renderer called tectonic (based on XeLaTeX). This will download any missing packages when it encounters them the first time. However, it will likely not know about any local packages or TEXMF paths, nor will it be able to render advanced features like TikZ graphs which require LuaTeX. The latexmk/lualatex combination will also likely be faster, and able to use advanced features like calling to other languages with pythontex (oh, the heresy!)

Scaling TeX

MakieTeX always plots TeX to its stated dimensions. This allows it to be extremely accurate and easily match font sizes, but it comes at a price - when using the standard text pipeline, latex results may look very small, especially with the default figure resolution.

We provide a layoutable object, LTeX, which aims to solve this. LTeXs are for all intents and purposes the same as Labels, with two extra keywords:

  • scale = 1, which literally scales the generated PDF by the provided factor;
  • render_density = 5, which specifies how densely any PDF must be rendered for the image fallback. This only really affects GLMakie and WGLMakie, so feel free to ignore it or set it to 1 for CairoMakie purposes.

An example follows:

fig = Figure(size = (400, 300));
tex1 = LTeX(fig[1, 1], L"\int \mathbf E \cdot d\mathbf a = \frac{Q_{encl}}{4\pi\epsilon_0}", scale=1);
tex2 = LTeX(fig[2, 1], L"\int \mathbf E \cdot d\mathbf a = \frac{Q_{encl}}{4\pi\epsilon_0}", scale=2);
fig

latex

Inner workings

MakieTeX provides high-level dispatches on LaTeXStrings from the LaTeXStrings.jl package, as well as some lower level types.

Any input is converted to a TeXDocument, which is then compiled to CachedTeX. This last type contains the compiled PDF and some pointers to in-memory versions of the PDF. These are what MakieTeX eventually uses to plot to the screen.

When plotting arrays of LaTeXStrings, MakieTeX takes a more efficient pathway by batching the array into a multi-page standalone document. This allows the relevant packages in LaTeX to be loaded once per array instead of once per string, and decreases the runtime of the README example by a sixth.

In general, we use the packages amsmath, amssymb, amsfonts, esint, lmodern, fontenc, xcolor in rendered latexstrings. However, work is ongoing on a good API for users to provide arbitrary preamble code.

Configuration

There are several configuration constants you can set in MakieTeX, stored as const Refs. These are:

CURRENT_TEX_ENGINE[] = `lualatex` - The current TeX engine which MakieTeX uses. Will default to tectonic if latexmk and lualatex are inaccessible on your system. RENDER_EXTRASAFE[] = false - Render with Poppler pipeline (false) or Cairo pipeline (true). The Poppler pipeline offers better rendering but may be slightly slower. _PDFCROP_DEFAULT_MARGINS[] = [0,0,0,0] - Default margins for pdfcrop. Feel free to set this to fill(1, 4) or higher if you need looser margins. The numbers are in the order [<left>, <top>, <right>, <bottom>]. TEXT_RENDER_DENSITY[] = 5 - Default density when rendering from calls to text. Useful only for GLMakie.

using GLMakie, Makie, MakieTeX
fig = Figure(resolution = (400, 300));
ax = Axis(fig[1, 1]);
lines!(rand(10), color = 1:10);
tex = LTeX(fig[2, 1], L"\int \mathbf E \cdot d\mathbf a = \frac{Q_{encl}}{4\pi\epsilon_0}", scale=2);
fig

Including full LaTeX documents

With the TeXDocument struct, you can feed in a String which contains a full LaTeX document, and show it at real scale in Makie!

This example is from Texample.net

using MakieTeX, CairoMakie, Makie
td = TeXDocument(read(download("https://texample.net/media/tikz/examples/TEX/title-graphics.tex"), String))
fig = Figure()
lt = LTeX(fig[1, 1], td; tellheight=false)
ax = Axis(fig[1, 2])
lines!(ax, rand(10); color = 1:10)
fig

makietex

Cool graphics!

2plots

cielab

dominoes

or-gate

planets

rotated-triangle

Installation

Simply run:

import Pkg; Pkg.add("MakieTeX")

and watch the magic happen!

The rendering pipeline

The standard rendering pipeline works as follows: the string is converted in to a TeXDocument, which is compiled to pdf using the given renderer, or tectonic if it does not exist. The pdf file is then cropped using the pdfcrop Perl script (this is what requires Perl_jll and Ghostscript_jll).

This cropped PDF is then loaded by Poppler, accessed through Poppler_jll.jl and libpoppler-glib. If using the Cairo backend, it is plotted directly to a surface; if using another backend, it is rasterized to an image, and plotted using scatter with image markers. The alignment is in this case depicted by marker_offset.

Thus, you only need a TeX engine, preferably LuaTeX, installed on your system. We don't automatically detect the TeX engine, though, so if you want to change that, set MakieTeX.CURRENT_TEX_ENGINE[] = `xelatex` (note the backticks in place of quotes) or some other engine, as long as it is compatible with latexmk.

TODOS and planned features

  • Better rasterization for GLMakie
  • Math and text font switching ability (preferably arbitrary)
  • Better font size algorithms
  • Multithreaded latex compilation if possible
  • Faster latex compilation (perhaps through latex caching?)