knitr has support for executing tikz
blocks in a Rmarkdown/quarto document and can even output an svg for html using the following block syntax.
```{R, engine = "tikz"}
#| fig-ext: svg
\begin{tikzpicture}[scale=2]
\node (A) at (0,0) {Hello};
\node (B) at (1,0.5) {Entire};
\node (C) at (1,-0.5) {World};
\draw[->] (A) -- (B);
\draw[->] (A) -- (C);
\end{tikzpicture}
```
The only issue when using svg output is that we cannot access system fonts using fontspec
. From knitr/R/engine.R in the knitr::eng_tikz
function, knitr
calls tinytex::latexmk
with the latex
backend since neither xelatex
or lualatex
support dvi
output, and then feeds the dvi
output to dvisvgm
to convert to the final svg product.
ext = dev2ext(options)
to_svg = ext == 'svg'
outf = if (to_svg) tinytex::latexmk(texf, 'latex') else tinytex::latexmk(texf)
fig = fig_path(if (to_svg) '.dvi' else '.pdf', options)
dir.create(dirname(fig), recursive = TRUE, showWarnings = FALSE)
file.rename(outf, fig)
fig2 = with_ext(fig, ext)
if (to_svg) {
# dvisvgm needs to be on the path
# dvisvgm for windows needs ghostscript bin dir on the path also
if (Sys.which('dvisvgm') == '') tinytex::tlmgr_install('dvisvgm')
if (system2('dvisvgm', c(
options$engine.opts$dvisvgm.opts, '-o', shQuote(fig2), fig
)) != 0) stop('Failed to compile ', fig, ' to ', fig2)
} else {
# convert to the desired output-format using magick
if (ext != 'pdf') magick::image_write(do.call(magick::image_convert, c(
list(magick::image_read_pdf(fig), ext), options$engine.opts$convert.opts
)), fig2)
}
Unfortunately, fontspec
doesn’t support latex
. But, dvisvgm
does accept xdv
(extended dvi) input, which we can create using xelatex
. This isn’t currently supported by knitr so either patch the eng_tikz
function or create a standalone TeX document for each diagram. For example, we could have a file diagram.tex
.
\documentclass[12pt,tikz,dvisvgm]{standalone}
\usepackage{fontspec}
\usepackage{tikz}
\setmainfont{Jost-400-Book.otf}
\begin{document}
\begin{tikzpicture}[scale=2]
\node (A) at (0,0) {Hello};
\node (B) at (1,0.5) {Entire};
\node (C) at (1,-0.5) {World};
\draw[->] (A) -- (B);
\draw[->] (A) -- (C);
\end{tikzpicture}
\end{document}
Compile to xdv
by disabling pdf output with xelatex and then convert using dvisvgm
with the bonus that we can embed our font into the svg.
$ xelatex -no-pdf diagram.tex
$ dvisvgm -c 3 --font-format=woff2 diagram.xdv
Finally, we can embed this in a markdown document.
![](diagram.svg)