---
title: "Introduction to gridmicrotex"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Introduction to gridmicrotex}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r setup, include = FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>",
  dpi = 300,
  dev = "ragg_png"
)
```

## What is gridmicrotex?

**gridmicrotex** renders LaTeX math as native R `grid` graphics objects.
It embeds the [MicroTeX](https://github.com/NanoMichael/MicroTeX) C++
engine: MicroTeX parses the LaTeX, builds the TeX box model and computes
exact glyph coordinates, and the package maps that layout onto grid
primitives (`pathGrob`, `segmentsGrob`, `rectGrob`, `textGrob`),
returning a `gTree`.

No LaTeX installation is required, and the result is resolution
independent on every R device.

- Full math: fractions, roots, integrals, matrices, Greek, accents,
  delimiters, and colour via `\textcolor{}`
- Two bundled math fonts, plus any OpenType math font through
  `load_math_font()`
- CJK, RTL and multilingual text inside `\text{}`
- ggplot2 integration --- `geom_latex()` and `element_latex()`, see
  `vignette("ggplot2-integration")`
- Markdown with inline math --- see `vignette("markdown")`

## Quick start

`latex_grob()` returns a grob; `grid.latex()` builds and draws one.

```{r basic, fig.height=1, fig.width=2.4, out.width="45%"}
library(gridmicrotex)
library(grid)

grid.newpage()
grid.latex(r"($\frac{\textcolor{red}{-b} \pm \sqrt{b^2 - 4ac}}{2a}$)",
           gp = gpar(fontsize = 24))
```

**Write LaTeX in a raw string.** `r"(...)"` passes backslashes through
untouched, so what you paste is what renders. Every example here uses
one. In an ordinary `"..."` string each `\` has to be doubled --- and
LaTeX's own row separator `\\` becomes a bewildering `\\\\`.

### Mixing text and math

The default `input_mode = "mixed"` reads the string as prose and
typesets only what sits inside `$…$` or `\(…\)` as math, which is why
`Famous:` below needs no markup. It saves typing, but the split is
heuristic. `input_mode = "math"` treats the whole string as math, so
prose must be wrapped in `\text{}`; that suits heavy math and pasted
LaTeX. These two render identically:

```{r modes, fig.height=1.2, fig.width=3.2, out.width="45%"}
grid.newpage()
grid.latex(r"(Famous: $E = mc^2$)",
           x = 0.05, y = 0.7, hjust = 0, gp = gpar(fontsize = 22))
grid.latex(r"(\text{Famous: } E = mc^2)", input_mode = "math",
           x = 0.05, y = 0.3, hjust = 0, gp = gpar(fontsize = 22))
```

Set the mode for a whole session with `latex_options(input_mode = )`.

## What it can render

Two figures before the details, because the range is easier to see than
to describe. A coloured `array` with `\multicolumn`, `\rowcolor`,
`\cellcolor`, a custom column type and a nested matrix:

```{r showcase-table, fig.height = 3, fig.width = 8, out.width="70%"}
grid.newpage()
grid.latex(r"(
\newcolumntype{s}{>{\color{#1234B6}}c}
\begin{array}{|c|c|c|s|}
  \hline
  \rowcolor{Tan}\multicolumn{4}{|c|}{\textcolor{white}{\bold{\text{Table Head}}}}\\
  \hline
  \text{Matrix}&\multicolumn{2}{|c|}{\text{Multicolumns}}&\text{Font size commands}\\
  \hline
  \begin{pmatrix}
      \alpha_{11}&\cdots&\alpha_{1n}\\
      \hdotsfor{3}\\
      \alpha_{n1}&\cdots&\alpha_{nn}
  \end{pmatrix}
  &\large \text{Left}&\cellcolor{#00bde5}\small \textcolor{white}{\text{\bold{Right}}}
  &\small \text{small Small}\\
  \hline
  \multicolumn{4}{|c|}{\text{Table Foot}}\\
  \hline
\end{array}
)", gp = gpar(fontsize = 22))
```

And a page of assorted notation --- `split` alignment, fraktur, stacked
delimiters, `\sideset`, extensible arrows, `\rotatebox`, `\reflectbox`
and a boxed result:

```{r showcase-equation, fig.height = 9, fig.width = 9, out.width="70%"}
grid.newpage()
grid.latex(r"(
\definecolor{gris}{gray}{0.9}
\definecolor{noir}{rgb}{0,0,0}
\fatalIfCmdConflict{false}
\newcommand{\pa}{\left|}
\begin{array}{c}
  \LaTeX\\
  \begin{split}
      |I_2| &= \pa\int_0^T\psi(t)\left\{ u(a,t)-\int_{\gamma(t)}^a \frac{d\theta}{k} (\theta,t) \int_a^\theta c(\xi)
          u_t (\xi,t)\,d\xi\right\}dt\right|\\
      &\le C_6 \Bigg|\pa f \int_\Omega \pa\widetilde{S}^{-1,0}_{a,-}
          W_2(\Omega, \Gamma_1)\right|\ \right|\left| |u|\overset{\circ}{\to} W_2^{\widetilde{A}}(\Omega\Gamma_r,T)\right|\Bigg|\\
      &\\
      &\begin{pmatrix}
          \alpha&\beta&\gamma&\delta\\
          \aleph&\beth&\gimel&\daleth\\
          \mathfrak{A}&\mathfrak{B}&\mathfrak{C}&\mathfrak{D}\\
          \boldsymbol{\mathfrak{a}}&\boldsymbol{\mathfrak{b}}&\boldsymbol{\mathfrak{c}}&\boldsymbol{\mathfrak{d}}
      \end{pmatrix}
      \quad{(a+b)}^{\frac{n}{2}}=\sqrt{\sum_{k=0}^n\tbinom{n}{k}a^kb^{n-k}}\quad
          \Biggl(\biggl(\Bigl(\bigl(()\bigr)\Bigr)\biggr)\Biggr)\\
      &\forall\varepsilon\in\mathbb{R}_+^*\ \exists\eta>0\ |x-x_0|\leq\eta\Longrightarrow|f(x)-f(x_0)|\leq\varepsilon\\
      &\det
      \begin{bmatrix}
          a_{11}&a_{12}&\cdots&a_{1n}\\
          a_{21}&\ddots&&\vdots\\
          \vdots&&\ddots&\vdots\\
          a_{n1}&\cdots&\cdots&a_{nn}
      \end{bmatrix}
      \overset{\mathrm{def}}{=}\sum_{\sigma\in\mathfrak{S}_n}\varepsilon(\sigma)\prod_{k=1}^n a_{k\sigma(k)}\\
      &\Delta f(x,y)=\frac{\partial^2f}{\partial x^2}+\frac{\partial^2f}{\partial y^2}\qquad\qquad \fcolorbox{noir}{gris}
          {n!\underset{n\rightarrow+\infty}{\sim} {\left(\frac{n}{e}\right)}^n\sqrt{2\pi n}}\\
      &\sideset{_\alpha^\beta}{_\gamma^\delta}{
      \begin{pmatrix}
          a&b\\
          c&d
      \end{pmatrix}}
      \xrightarrow[T]{n\pm i-j}\sideset{^t}{}A\xleftarrow{\overrightarrow{u}\wedge\overrightarrow{v}}
          \underleftrightarrow{\iint_{\mathds{R}^2}e^{-\left(x^2+y^2\right)}\,\mathrm{d}x\mathrm{d}y}
  \end{split}\\
  \rotatebox{30}{\sum_{n=1}^{+\infty}}\quad\mbox{Mirror rorriM}\reflectbox{\mbox{Mirror rorriM}}
\end{array}
)", gp = gpar(fontsize = 22), render_mode = "path")
```

Both are ordinary LaTeX, pasted unchanged. The rest of this vignette is
about placing such things, choosing fonts, and knowing where the
supported set ends.


### Aligning to the math baseline

`hjust` and `vjust` also take names. The useful one is
`vjust = "baseline"`, which puts the formula's *math baseline* --- not
the bounding-box centre --- on the anchor, so a formula sits beside
running text the way it would in a typeset document.

```{r baseline-align, fig.height=0.8, fig.width=4.5, out.width="60%"}
grid.newpage()
y <- 0.5
grid.segments(unit(0, "npc"), unit(y, "npc"),
              unit(1, "npc"), unit(y, "npc"), gp = gpar(col = "grey80"))
grid.text("if ", x = 0.10, y = y, just = c(0, 0.5), gp = gpar(fontsize = 16))
grid.latex(r"($x \geq \sqrt{2\pi}$)",
           x = 0.22, y = y, hjust = "left", vjust = "baseline",
           gp = gpar(fontsize = 16))
grid.text(", then proceed.", x = 0.62, y = y, just = c(0, 0.5),
          gp = gpar(fontsize = 16))
```

`hjust` accepts `"left"`/`"bbleft"`, `"center"`/`"centre"`/`"middle"`/
`"bbcentre"`, and `"right"`/`"bbright"`. `vjust` accepts `"bottom"`,
`"center"`/`"centre"`/`"middle"`, `"top"`, and `"baseline"`.

### Named anchors with `\mark{}`

`\mark{name}` records a named anchor inside the formula, and `grobMark()`
resolves it to a pair of grid units ready to drive an arrow or a callout.
Marks work at any nesting level --- even inside a superscript or a
fraction --- and inherit the surrounding transform (font shrink, scaling,
rotation), so the anchor lands on the rendered glyph.

```{r mark, fig.height=2.4, fig.width=5, out.width="70%"}
g <- latex_grob(r"($a^2 + b\mark{term}^2 \mark{equals}= c^2$)",
                x = 0.5, y = 0.4, gp = gpar(fontsize = 28))
grid.newpage()
grid.draw(g)

# The "=" sign, pointed at from above.
mk_eq <- grobMark(g, "equals")
grid.segments(mk_eq$x, mk_eq$y + unit(15, "mm"),
              mk_eq$x, mk_eq$y + unit(3, "mm"),
              arrow = arrow(length = unit(2, "mm"), type = "closed"),
              gp = gpar(col = "red"))
grid.text("equals", x = mk_eq$x, y = mk_eq$y + unit(18, "mm"),
          gp = gpar(col = "red", fontsize = 11))

# The b^2 term, from below -- the mark sits at the end of the term,
# including the superscript's smaller scale.
mk_bsq <- grobMark(g, "term")
grid.segments(mk_bsq$x - unit(6, "mm"), mk_bsq$y - unit(15, "mm"),
              mk_bsq$x - unit(2, "mm"), mk_bsq$y - unit(3, "mm"),
              arrow = arrow(length = unit(2, "mm"), type = "closed"),
              gp = gpar(col = "blue"))
grid.text("b² term", x = mk_bsq$x - unit(7, "mm"),
          y = mk_bsq$y - unit(18, "mm"), just = "right",
          gp = gpar(col = "blue", fontsize = 11))
```

The returned units carry the grob's viewport position and `hjust`/`vjust`,
so they go straight into any grid drawing function with no offset
arithmetic. A mark is a single point, not a span: to centre a callout over
a multi-glyph term, use a pair (`\mark{l}…\mark{r}`) and take the midpoint.

## Display and inline style

TeX sets the same expression two ways. In *display* style a `\sum` is
drawn at full size with its limits above and below; in *text* (inline)
style it shrinks and the limits become scripts beside it. This is a
choice about spacing and layout, not about size --- size is always
`gp$fontsize`.

By default the delimiters decide, exactly as in a LaTeX document: `$…$`
gives text style and `$$…$$` gives display style. A label written without
delimiters --- a plot title, an axis title, an `element_latex()` --- has
nothing for the parser to go on, so it gets text style. `tex_style`
overrides that for the whole expression, and takes all four of TeX's
styles:

```{r style-options, fig.height=1.9, fig.width=6.5, out.width="100%"}
sum_expr <- r"(\sum_{i=1}^{n} \frac{x_i}{n})"
styles <- c("display", "text", "script", "scriptscript")
labels <- c('"display"  ($$...$$)', '"text"  ($...$)',
            '"script"', '"scriptscript"')

grid.newpage()
for (i in seq_along(styles)) {
  pushViewport(viewport(x = (i - 0.5) / 4, width = 1 / 4))
  grid.text(labels[i], y = 0.88, gp = gpar(cex = 0.75, fontface = "bold"))
  grid.latex(sum_expr, y = 0.42, input_mode = "math",
             tex_style = styles[i], gp = gpar(fontsize = 20))
  grid.rect(gp = gpar(col = "grey85", fill = NA))
  popViewport()
}
```

Every panel is `gp = gpar(fontsize = 20)`. The display version is taller
and narrower because the limits moved above and below the operator
instead of sitting beside it --- worth knowing when you are budgeting
space for a label, and `latex_dims()` reports the difference if you need
the numbers. `"script"` and `"scriptscript"` are the sizes TeX uses for
first- and second-level scripts; they are occasionally handy for a
compact sub-label, and rarely needed otherwise.

To change style for part of an expression rather than all of it, use the
inline commands `\displaystyle`, `\textstyle`, `\scriptstyle` and
`\scriptscriptstyle` instead. `latex_options(tex_style = )` sets a
session default.

## Wrapping and justification

`max_width`, in big points, wraps a label over as many lines as it needs.

```{r wrap, fig.height = 2, fig.width = 4, out.width = "65%"}
prose <- paste(rep(
  r"(The quick brown fox jumps over the lazy dog, and $x^2$ too.)", 3),
  collapse = " ")

grid.newpage()
pushViewport(viewport(layout = grid.layout(2, 1)))
pushViewport(viewport(layout.pos.row = 1))
grid.text("ragged (default)", x = 0.02, y = 0.98, hjust = 0, vjust = 1,
          gp = gpar(fontsize = 8, col = "grey40"))
grid.latex(prose, x = 0.02, y = 0.78, hjust = 0, vjust = 1,
           max_width = 3.6 * 72, gp = gpar(fontsize = 11))
popViewport()
pushViewport(viewport(layout.pos.row = 2))
grid.text("justified + optimal", x = 0.02, y = 0.98, hjust = 0, vjust = 1,
          gp = gpar(fontsize = 8, col = "grey40"))
grid.latex(prose, x = 0.02, y = 0.78, hjust = 0, vjust = 1,
           max_width = 3.6 * 72, justify = TRUE, line_break = "optimal",
           gp = gpar(fontsize = 11))
popViewport(2)
```

Both refinements are off by default. `justify = TRUE` fills every line but
the last; `line_break = "optimal"` chooses the breaks by total fit rather
than one line at a time. Justifying a narrow column opens wide word gaps,
since nothing may break inside a word --- mark the words that may with
`\-`, as in `in\-ter\-na\-tion\-al`.

## Including a figure

`\includegraphics` draws an image inside the formula. PNG, JPEG and SVG
are supported, sized with `width`, `height` or `scale` in any LaTeX
length; give one of width/height and the aspect ratio is kept, or both
plus `keepaspectratio` to fit inside them. As in LaTeX the extension may
be omitted (`{plots/fig}` finds `plots/fig.svg` or `plots/fig.png`), and
`\graphicspath{{figs/}}` adds a directory to search. `angle` rotates the
figure, like `\rotatebox`; `trim` and `clip` are recognised but not
applied, and say so.

```{r img-basic, fig.height = 1.6, fig.width = 4, out.width = "60%"}
fig <- tempfile(fileext = ".svg")
svglite::svglite(fig, width = 2, height = 1.2)
grid.newpage()
grid.circle(r = 0.35, gp = gpar(fill = "steelblue", col = NA))
dev.off()

grid.newpage()
grid.latex(sprintf(r"(\text{before }\includegraphics[width=1in]{%s}\text{ after})", fig),
           input_mode = "math", gp = gpar(fontsize = 16))
```

Inline, the image sits **on the baseline**, as in LaTeX --- `\raisebox`
moves it. A paragraph holding only an image is a **block**, flush left
unless `halign` or a `text-align` rule on `img` says otherwise.

```{r img-raise, fig.height = 1.2, fig.width = 4.5, out.width = "70%"}
icon <- sprintf(r"(\includegraphics[width=14bp]{%s})", fig)
grid.newpage()
grid.latex(sprintf(r"(\text{baseline }%s\text{, raised }\raisebox{4bp}{%s})",
                   icon, icon),
           input_mode = "math", gp = gpar(fontsize = 18))
```

### A caption

`\caption` renders where it is written. A caption after `\includegraphics`
therefore sits below the figure, and one written before a `tabular` sits
above the table --- which is what makes raw `xtable` and `kable` output
come out the right way round without any float logic.

`\centering` is dropped, because a grob has no page to centre against. A
one-column `array` does the job instead: it centres the figure and its
caption on each other.

```{r img-caption, fig.height = 1.4, fig.width = 3, out.width = "45%"}
logo <- system.file("img", "Rlogo.png", package = "png")
grid.newpage()
grid.latex(sprintf(r"(\begin{array}{c}
  \includegraphics[width=0.6in]{%s}\\
  \caption{Figure 1: the R logo}
\end{array})", logo), gp = gpar(fontsize = 11))
```

Remove the `array` and the same two lines come out flush left. LaTeX
numbers captions from a counter; here the number is yours to write.

**Prefer SVG.** An SVG is drawn as real grid primitives, so it stays sharp
at any output resolution — there is no dpi to choose. A PNG or JPEG is a
fixed grid of pixels, so if you display one much larger than it was saved
you get a blurry result; the package warns when the effective resolution
falls below 150 dpi and tells you what pixel size would reach 300. The
rule is `pixels = display_inches * output_dpi`.

PDF and EPS are **not** supported. Save the figure as SVG instead: it needs
no external tool and, unlike a rasterised PDF, its text stays text.


## Fonts

Two layers are in play. **MicroTeX** chooses the math glyphs and their
metrics; **grid** draws everything inside `\text{}` using `gp$fontfamily`,
so prose follows R's ordinary font handling --- Latin, CJK, Cyrillic and
anything else the device supports.

Two math fonts ship with the package and load automatically:

| Alias | Font | Style | Pairs with |
|-------|------|-------|------------|
| `"lete"` (default) | Lete Sans Math | Sans-serif | `fontfamily = "sans"` |
| `"stix"` | STIX Two Math | Serif | `fontfamily = "serif"` |

```{r fonts-list}
available_math_fonts()
```

Set one per call with `math_font`, or for the session with
`latex_options(math_font = )`. Both rows below are the same formula ---
only the math font and its paired text family differ:

```{r fonts, fig.height=1.1, fig.width=3.4, out.width="60%"}
formula <- r"(Theorem: $\int_0^1 f(x)\,dx \geq 0$)"

grid.newpage()
pushViewport(viewport(layout = grid.layout(2, 1)))
pushViewport(viewport(layout.pos.row = 1))
grid.latex(formula, gp = gpar(fontsize = 15, fontfamily = "sans"))
upViewport()
pushViewport(viewport(layout.pos.row = 2))
grid.latex(formula, math_font = "stix",
           gp = gpar(fontsize = 15, fontfamily = "serif"))
upViewport(2)
```

`check_math_fonts()` gives a diagnostic report. Any font available to R
works for the text half --- base families like `"sans"`, `"serif"` and
`"mono"`, or anything registered through **systemfonts**:

```{r cjk, fig.height=0.8, fig.width=5, out.width="55%"}
grid.newpage()
grid.latex(r"(如果 $x > 0$ 则 $y = x^2$)",
           gp = gpar(fontsize = 24, fontfamily = "sans"))
```

Right-to-left scripts work the same way, mixed with Latin included. Two
things do not: wrapping a right-to-left paragraph across lines needs the
optional [FriBidi](https://github.com/fribidi/fribidi) library, and a
math atom between two right-to-left runs --- `\text{…}$x^2$\text{…}` ---
falls back to left-to-right order.

### Naming a font for one run

`gp$fontfamily` applies to the whole grob. For a *single run*, use
`\gmfontfamily{family}{content}` --- a gridmicrotex extension, not
standard LaTeX. `\textrm{…}` goes the other way, returning content to
`gp$fontfamily` even inside a `\textsf{…}`, `\texttt{…}` or
`\gmfontfamily{…}{…}` group, and without disturbing bold or italic:

```{r gmfontfamily, fig.height=0.5, fig.width=4.5, out.width="85%"}
grid.newpage()
grid.latex(
  r"(\textsf{sans \textrm{body} sans} \quad \gmfontfamily{mono}{mono})",
  gp = gpar(fontsize = 16, fontfamily = "serif")
)
```

`family` is anything `gp$fontfamily` accepts: a generic (`"sans"`,
`"serif"`, `"mono"`) or a specific name such as `"Georgia"`, which
`\textsf{…}` / `\texttt{…}` cannot express. Unresolvable names fall back
silently. The content is typeset as text, so this styles prose, not math
--- math glyphs follow `math_font`. It composes with emphasis in either
nesting order, but a nested `\gmfontfamily` *replaces* the family it sits
inside.

The name is deliberately not `\fontfamily`: LaTeX's takes one argument,
does nothing until `\selectfont`, and wants an NFSS code (`ptm`) rather
than a font name. Since gridmicrotex accepts pasted LaTeX, claiming that
name would silently misparse real input. This is also what markdown's
`font-family` CSS compiles to --- see `vignette("markdown")`.

### Loading a custom math font

`load_math_font()` adds any OpenType math font. The OpenType MATH table
is parsed directly in C++, so no companion metrics file and no external
toolchain are needed:

```r
load_math_font("path/to/MyFont.otf")
```

This is only for **math** fonts. Text fonts need no loading at all --- set
`gp$fontfamily`, or name one for a run with `\gmfontfamily{}{}`.

## Devices and render modes

- **`"typeface"`** (default) draws glyphs as native text, so PDF and SVG
  output stays selectable and searchable: a line of prose is emitted as a
  single element, so a viewer finds a phrase and not merely a word. Text
  given a `max_width` is the exception --- it is emitted one word per
  element, because the spaces are where the lines break. Fonts are read
  straight from their OTF files,
  with no system-wide install --- but this needs a device with the R
  \eqn{\geq} 4.3 glyph engine (`ragg`, `svglite`, `cairo_pdf`). On
  others, such as the base `pdf()` device, it falls back to path mode
  with a warning.
- **`"path"`** draws each glyph as a filled vector path. Works on every
  device; the text is not selectable, and files are larger.

```r
grid.latex(r"($E = mc^2$)", gp = gpar(fontsize = 24))                        # typeface
grid.latex(r"($E = mc^2$)", gp = gpar(fontsize = 24), render_mode = "path")  # path
```

Prefer `ragg::agg_png()`, `svglite::svglite()` or `grDevices::cairo_pdf()`.
The default devices on Windows and macOS may not find the bundled math
fonts and will warn `font family not found in Windows font database`; the
README covers the setup, and compares the package with `tikzDevice`,
`xdvir`, `latex2exp` and `plotmath`.

> **Do not use `showtext::showtext_auto()` with typeface mode.** showtext
> intercepts all text rendering and converts it to paths, silently
> defeating typeface mode even on `svglite` and `ragg`. Call
> `showtext::showtext_auto(FALSE)` before drawing formulas.

## Utilities

### Measuring

`latex_dims()` returns the bounding box of an expression, for layout
arithmetic and for checking that a label fits:

```{r dims}
latex_dims(r"(\frac{a}{b})", gp = gpar(fontsize = 20))
```

### Session defaults

`latex_options()` sets `math_font`, `render_mode` and `input_mode` for
calls that don't supply them; explicit arguments always win. Size stays at
the grob level, via `gp$fontsize` / `gp$lineheight`.

```{r options, eval=FALSE}
latex_options(math_font = "stix", render_mode = "typeface")
latex_options()        # query
reset_latex_options()  # back to built-in defaults
```

### User-defined macros

`define_macro()` registers zero-argument shorthands, expanded by text
substitution before the expression reaches MicroTeX:

```{r macros, fig.height=0.7, fig.width=3, out.width="50%"}
define_macro("RR", r"(\mathbb{R})")
define_macro("eps", r"(\varepsilon)")

grid.newpage()
grid.latex(r"(\forall \eps > 0, \eps \in \RR)", gp = gpar(fontsize = 24))

clear_macros()
```

Names must be ASCII letters, and expansion iterates to a fixed point so
macros can reference each other. `list_macros()` shows what is registered;
`clear_macros()` drops everything.

For parameterised macros (0--9 arguments) MicroTeX also accepts plain-TeX
`\def`. These live only for the expression they appear in, so use them for
an abbreviation local to one label and `define_macro()` for one that
should persist:

```{r def-inline, fig.height=0.7, fig.width=4, out.width="60%"}
grid.newpage()
grid.latex(
  r"(\def\norm#1{\left\lVert #1 \right\rVert}
      \norm{\vec{v}} = \sqrt{\langle \vec{v}, \vec{v} \rangle})",
  gp = gpar(fontsize = 24)
)
```

### Caching and introspection

Parsed layouts are memoised by `(tex, fontsize, math_font, render_mode,
…)`, so a repeated axis label is laid out once:

```{r cache, eval=FALSE}
latex_cache_info()       # size / max_size / hits / misses
latex_cache_limit(1024)  # LRU capacity; 0 disables caching
latex_cache_clear()      # wipe (e.g. after re-loading fonts)
```

`latex_tree()` returns the raw draw records plus bbox metadata, and
`debug = TRUE` overlays the bounding box, baseline and record origins ---
both useful when checking alignment:

```{r debug, fig.height=1, fig.width=3, out.width="60%"}
grid.newpage()
grid.latex(r"($x^{2} + y_{i}$)", gp = gpar(fontsize = 30), debug = TRUE)
```

## LaTeX reference

MicroTeX is a **math formula renderer**, not a document typesetter. It
covers the vast majority of notation used in plots and figures, but does
not replace a LaTeX installation. This section is the boundary.

### Lists

`itemize` and `enumerate` lay their items out as a left-aligned column,
one per row --- `itemize` prefixes a bullet, `enumerate` numbers them:

```{r lists, fig.height=1.4, fig.width=3, out.width="45%"}
grid.newpage()
grid.latex(r"(\begin{enumerate}
  \item e^{i\pi} + 1 = 0
  \item \begin{itemize}
          \item \alpha \item \beta
        \end{itemize}
\end{enumerate})", gp = gpar(fontsize = 20))
```

An optional `[…]` argument customises the marker. For `itemize` it is the
literal marker (`\begin{itemize}[\star]`); for `enumerate` it is a counter
template containing one of `\arabic*`, `\alph*`, `\Alph*`, `\roman*` or
`\Roman*` (e.g. `\begin{enumerate}[\Roman*.]`). Lists nest, and an item
may contain any math, including a `\begin{array}` table.

Because MicroTeX is a math engine, each item is a **math-mode,
single-line** expression: no paragraph flow, no line wrapping, and prose
inside an item needs `\text{}` (`\item \text{First point}`). The
`description` environment is not supported.

### Pasting LaTeX from other sources

Input generated by other tools --- ready-to-compile `tabular` snippets,
fragments copied out of a `.tex` file --- usually arrives wrapped in
document-level constructs MicroTeX does not implement. Rather than
refusing it, gridmicrotex rewrites or removes a small set of well-known
wrappers before parsing, so `knitr::kable(format = "latex")` and
`xtable::print.xtable()` output can be pasted in unedited:

```{r pasted, fig.height = 1.1, fig.width = 3.2, out.width = "55%"}
snippet <- r"(
% latex table generated by kable()
\begin{table}[ht]
\centering
\caption{Model coefficients}
\begin{tabular}{lrr}
\toprule
Term & Estimate & \emph{p} \\
\midrule
Intercept & 2.14 & 0.003 \\
Slope & 0.42 & 0.001 \\
\bottomrule
\end{tabular}
\end{table}
)"

grid.newpage()
grid.latex(snippet, input_mode = "mixed", gp = gpar(fontsize = 11))
```

Six things were handled without any editing: the `%` comment and the
`table` float were dropped, `\centering` removed, `\caption` set as a line
above the table, `\toprule`/`\bottomrule` became thick rules, `\midrule` a
plain one, and `\emph` became italic.

Note that the cell text is set in math italics. A `tabular` is a math
environment, so its contents are math whichever `input_mode` you choose ---
the two modes give an identical layout for the snippet above. What
`"mixed"` buys you here is the prose *outside* the environment, such as the
caption. For upright cell text, wrap the cells in `\text{}`.

**Removed silently (no visual effect):**

| Construct | Why |
|---|---|
| `%`-to-end-of-line comments (`\%` is preserved) | comments are non-visual in LaTeX too |
| `\documentclass[…]{…}`, `\usepackage[…]{…}` | preamble metadata |
| `\begin{document}` / `\end{document}` | document boundary, structural only |
| `\maketitle`, `\title{…}`, `\author{…}` | title-page metadata, no body output |
| `\label{…}` | cross-reference target, never rendered in LaTeX either |
| `\begin{table}[…]` / `\end{table}`, `\begin{figure}[…]` / `\end{figure}` (and starred variants) | float wrappers; the contents stay |
| `\centering`, `\raggedright`, `\raggedleft`, `\flushleft`, `\flushright` | alignment scope declarations |
| `\noindent`, `\relax` | content-free declarations |

**Rewritten to a MicroTeX equivalent:**

| Construct | Becomes |
|---|---|
| `\emph{X}` | `\textit{X}` |
| `\textnormal{X}` | `\text{X}` |
| `\par`, `\newline` | `\\` (line break) |
| `\toprule`, `\bottomrule` | `\thickhline` (rendered ~2× thickness) |
| `\midrule` | `\hline` |
| `\cmidrule[trim]?(parenarg)?{a-b}` | `\cline{a-b}` --- partial-column rule |
| `\caption[short]{X}` | `\text{X}` plus a line break, at its source position --- so a caption written above the table stays above it |
| `\smallskip`, `\medskip`, `\bigskip` | `\vspace{0.25em}` / `\vspace{0.5em}` / `\vspace{1em}` --- em-relative so they scale with `gp$fontsize` |
| `\hfill`, `\vfill` | `\quad` / `\vspace{1em}` --- static proxies for rubber lengths |
| `\url{X}` | coloured monospace text --- a grob cannot be a hyperlink, so only the appearance survives |
| `\href{U}{X}` | `X`, coloured. Matches `hyperref` with `colorlinks=true`; markdown links use HTML's blue-and-underlined convention instead |

Three of those are approximations. `\caption` renders where it appears in
the source, not where LaTeX's float machinery would move it, so
caption-above or caption-below follows whatever your tool emits. The skips
are em-relative rather than LaTeX's absolute 3/6/12 pt, so they stay
visible at any `gp$fontsize`; use `\vspace{Xpt}` for an exact amount. And
`\hfill` / `\vfill` are *rubber* lengths with nothing to fill in a
fixed-size grob, so they become a static 1 em gap --- right position, no
elasticity.

**Not honored** --- rendered as literal text, which is intentional: it
makes unsupported markup easy to spot.

- Declarative font scopes: `\bfseries`, `\itshape`, `\ttfamily`,
  `\sffamily`, `\rmfamily`. These affect text within their group in LaTeX,
  which needs scope tracking we do not implement. Use the
  argument-bearing forms instead --- `\textbf{…}`, `\textit{…}`,
  `\texttt{…}`, `\textsf{…}`, `\textrm{…}` --- all of which nest. To
  choose the text font itself, set `gp$fontfamily` or use
  `\gmfontfamily{…}{…}`.
- References: `\ref{…}`, `\cite{…}` --- there is nothing to resolve
  against.
- Footnotes: `\footnote{…}` --- the positioning machinery is page-bound.
- Small caps: `\textsc{…}` --- MicroTeX has no small-caps glyphs.

### What is not supported

These need a real document compiler and are outside a formula renderer's
scope: document structure (`\section`, page layout, `\tableofcontents`);
automatic hyphenation (`\-` marks a break point yourself, and
`max_width` / `justify` do handle line breaking); TikZ/PGF;
cross-references and bibliographies; theorem
environments; the `description` list environment (`itemize` and
`enumerate` *are* supported); and `\tag` / equation numbering.
`\usepackage{…}` is accepted but loads nothing --- every supported command
is built into MicroTeX.

For axis labels, annotations, legends and in-plot formulas, the supported
set is more than sufficient.
