Beautiful SVG diagrams with TikZ and dvisvgm

28 Jan '18

Using dvisvgm, you can produce beautiful SVGs, for example the diagrams in my git merge vs rebase tutorial.

Installing LaTeX is the first step. Windows users want MikTeX, and can follow the Ghostscript installation instructions on the dvisvgm FAQ.

On macOS, it’s a bit more involved. First, use MacTeX, which can be installed with homebrew. You’ll also need Ghostscript if you want things like fonts to work properly with dvisvgm.

$ brew cask install mactex
$ brew install ghostscript

dvisvgm is installed with MacTeX. In the current version (2.1.3), you also need to tell dvisvgm where to find Ghostscript. I’ve submitted a patch for it to work automatically, but that isn’t in MacTeX yet. To do this, add the following to your .bash_profile:

export LIBGS="$(brew --prefix ghostscript)/lib/libgs.9.dylib"

Or, for Fish shell fans, put this in your ~/.config/fish/ file:

set -x LIBGS (brew --prefix ghostscript)/lib/libgs.9.dylib

Then, make sure dvisvgm finds Ghostscript. You’ll have to make sure your shell reloads the changes to .bash_profile.

$ source ~/.bash_profile
$ dvisvgm -l | grep ps
ps         dvips PostScript specials

If you don’t see any output, dvisvgm can’t find Ghostscript. But if you do, you’re ready.

Using the standalone class, each TikZ picture is rendered on a separate page, and each page is the size of the TikZ picture. Here’s a basic example:

\node {Hello World};

First, margin=5pt sets a margin, otherwise standalone really does only set the picture size to what is required. And here’s the result, converted to PNG with a border added to emphasise the margin:

The rendered TeX file that reads "Hello World", with a border added afterwards

However, if you compiled this, you’ll still get a PDF out. To get an SVG, add the dvisvgm option to the document class options. Here’s another example with that:

\begin{tikzpicture}[very thick]
\useasboundingbox (2,2) -- (-2,-2);
\node (a) at (-1,-1) {A};
\node (b) at (1,1) {B};
\path (a) edge[->] (b);

A pitfall is that if you add the dvisvgm option, you lose the ability to make PDF files properly. This isn’t a huge issue, LaTeX packages usually provide a DVI viewer. But if you want PDF files while you’re making the graphics, you can omit the dvisvgm option, and then add it in when you’re ready to make the SVG file. (Of course, if you forget dvisvgm in the options, the output won’t work well with dvisvgm.)

I’ve also used this to demonstrate how to set the font to sans-serif (\renewcommand{\familydefault}{\sfdefault}), and how to use \useasboundingbox to set the output page size instead of margin. To create the SVG file, first have LaTeX make a DVI file, and then convert it to SVG with dvisvgm:

$ latex example.tex
$ dvisvgm \
    --clipjoin \
    --bbox=papersize \
    --page=1- \

The dvisvgm options are important. --clipjoin prevents clipping issues. --bbox=papersize instructs dvisvgm to use the papersize information in the DVI file. Some other options for --box, dvi and preview do not work reliably when I tried them, especially if your TikZ coordinates can be negative (easy to do). Finally, and this is awesome, since you can have multiple TikZ pictures in one TeX file, --page=1- tells dvisvgm to process all pages, and output them to separate SVG files. Nice.

And this is the resulting SVG:

The example TeX file converted to an SVG file. The content is meaningless

I have performed one extra operation on the file. SVGO optimised SVG files by stripping out unnecessary rubbish and greatly reduces the filesize of SVG files. The only downside is you’ll need npm installed. (I hate NPM and Javascript, but SVGO is really the best thing out there to optimise SVG files.)

$ brew install npm
$ npm install -g svgo

SVGO has loads of options, too many to specify on the CLI. You can download the config.yml file I use, or use this SVGO GUI to try them out yourself.

$ svgo --config=config.yml example.svg

And that’s it!

TeX, macOS