Skip to content

TeX labels

Math in titles, labels, legends and text, set by a TeX typesetter rather than approximated.

Any string go-plot draws — a title, an axis label, a legend entry, a colorbar label, Text, Annotate — may contain TeX math between dollar signs:

ax.SetXLabel(`time $t$ (s)`)
ax.Plot(x, y, plot.Label(`$e^{-t/4}\cos 2t$`))

Use Go’s raw strings (backquotes), so that backslashes reach TeX unescaped.

A TeX typesetter, not mathtext

matplotlib sets $...$ with mathtext, its own approximation of TeX. go-plot sets it with go-tex/math, a real TeX math typesetter using the OpenType MATH table of STIX Two Math: fractions, radicals, big operators, limits, matrices and \left...\right are laid out as TeX lays them out, which mathtext only approximates. go-tex/math’s output is parsed back into paths and scaled to the label’s size, so TeX is drawn as outlines in both PNG and SVG and needs no font installed.

The standard normal density with a TeX legend entry, a shaded area between -1 and 1, and an integral written as text
package main

import (
	"math"

	"github.com/go-plot/plot"
)

func init() { register("tex", tex) }

func tex() *plot.Figure {
	x := linspace(-4, 4, 400)
	y := make([]float64, len(x))
	for i, v := range x {
		y[i] = math.Exp(-v*v/2) / math.Sqrt(2*math.Pi)
	}
	fig, axs := plot.Subplots(1, 1)
	ax := axs[0][0]
	ax.Plot(x, y, plot.Label(`$\varphi(x) = \frac{1}{\sqrt{2\pi}}\, e^{-x^2/2}$`))
	ax.FillBetween(x, y, nil, plot.Where(between(x, -1, 1)), plot.Alpha(0.3))
	ax.Text(1.4, 0.25, `$\int_{-1}^{1} \varphi(x)\,dx \approx 0.683$`, plot.FontSize(12))
	ax.SetTitle(`The standard normal density $\varphi$`)
	ax.SetXLabel(`$x$`)
	ax.SetYLabel(`$\varphi(x)$`)
	ax.Legend(plot.Loc("upper left"))
	return fig
}

func between(x []float64, lo, hi float64) []bool {
	m := make([]bool, len(x))
	for i, v := range x {
		m[i] = v >= lo && v <= hi
	}
	return m
}

Tick labels keep matplotlib’s look

The one exception is \mathdefault{...}, which is what matplotlib’s tick formatters emit (10^{3}, 2\times10^{1}). It is set in DejaVu Sans with mathtext’s own superscript rules (shift = max(sup1 · x-height, depth + x-height/4), scripts at 0.7 size), so tick labels match matplotlib’s look and position. The decades on the log-log figure are set this way.

Plain text

Text outside dollar signs is set in DejaVu Sans, matplotlib’s default font, from go-opentype/fonts, with kerning from DejaVu’s GPOS and kern tables. Glyphs are drawn as filled outlines, so rotated labels and SVG output need no font. Positions follow matplotlib’s Text._get_layout (line metrics from the OS/2 table, alignment, rotation, anchoring), and like Agg the pen is put on a whole pixel. How the pixels of text differ from matplotlib’s is on Differences.

Cost

STIX Two Math is parsed only when a label contains TeX: a figure without any does not pay for it (performance).

Replacing the renderer

Figure.Text is a text.Renderer with two methods, Layout(s, size) and LineMetrics(size). Setting another one changes the fonts of the whole figure.

Created on by tannevaled

Modified on by tannevaled