I’m planning on writing about diffing soon, a problem space that is well-visualized by graphs. So this site needs to support graphs.

I hear a lot of nice things about Mermaid (which has Jekyll support via jekyll-mermaid), but I’ve been using Graphviz for the past two decades and I’m not about to leave an old friend for dead.

Not able to find an existing plugin (and not ready to publish my own gem1), I’ve hacked together a custom Liquid block to support rendering a graph from DOT source and I’m sharing it here in case you’d like to do the same.

Code

  1. Add gem "ruby-graphviz" to your Gemfile
  2. Create a new file _plugins/graphviz.md:
require 'digest/md5'
require 'ruby-graphviz'

module Jekyll
  class GraphvizBlock < Liquid::Block
    def initialize(tag_name, markup, options)
      super
      @tag = markup
    end

    def render(context)
      graph = GraphViz.parse_string(super)

      graph_id = @tag unless @tag.empty?
      graph_id ||= graph.id if graph.id.match?(/^[a-zA-Z0-9]+$/)
      graph_id ||= Digest::MD5.hexdigest(super)
      
      svg = graph.output(:svg => String).
        sub(/^.*<svg/m, "<svg class=\"graphviz\" id=\"#{ graph_id }\"").
        gsub(/<!--.*?-->/m, '')

      "#{svg}"
    end
  end
end

Liquid::Template.register_tag('graphviz', Jekyll::GraphvizBlock)

Demo

Assuming your host has the graphviz suite of tools installed2, the following markup:

{% graphviz %}
digraph {
    { rank=same; b, c }
    a -> b[label="0.2",weight="0.2"];
    a -> c[label="0.4",weight="0.4",color=red,penwidth=3.0];
    c -> b[label="0.6",weight="0.6",constraint=false];
    c -> e[label="0.6",weight="0.6",color=red,penwidth=3.0];
    e -> e[label="0.1",weight="0.1"];
    e -> b[label="0.7",weight="0.7",color=red,penwidth=3.0,constraint=false];
}
{% endgraphviz %}

Will render to:

b b c c c->b 0.6 e e c->e 0.6 e->b 0.7 e->e 0.1 a a a->b 0.2 a->c 0.4

Controlling the <svg>’s id

The generated <svg> tag includes an id attribute determined by, in order:

  1. A parameter passed to the Liquid tag (e.g. {% graphviz Id %})
  2. The name of the graph (e.g. graph Id { … })
  3. A hash of the graph’s source code

Caveat: Fonts

Even though it’s producing SVG output, graphviz’s layout engines will still render text in order to know their display size so only fonts available on the host can be used, which is a drag since most servers probably don’t even have fontconfig, leaving you with a pretty limited set of built-in fonts. Unfortunately the Javascript story is no better—none of the wasm builds of graphviz integrate with the browser’s available fonts.

If you’re used to using graphviz and you don’t mind everyone else knowing as well then I think it’s fair to embrace its iconic Times-Roman æsthetic, but if you’re inclined to go overboard you can extend the plugin to rewrite the font-family attribute.

%3 b b c c c->b 0.6 e e c->e 0.6 e->b 0.7 e->e 0.1 a a a->b 0.2 a->c 0.4

Why Strip the Comments?

HTML comments don’t nest, so it’s impossible to comment out a graph otherwise.

  1. Though this seems like the perfect opportunity for me to learn someday 

  2. Which Netlify does!