Skip to content

Instantly share code, notes, and snippets.

@esle
Created February 20, 2012 06:08
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save esle/1868078 to your computer and use it in GitHub Desktop.
Save esle/1868078 to your computer and use it in GitHub Desktop.
dot directive for reStructuredText, with this you can write ..dot:: things
"""
dot directive (require graphviz)
"""
from docutils import nodes
from docutils.parsers.rst import directives, Directive
import subprocess as sp
nthUnnamed = 0
class Dot(Directive):
required_arguments = 0
optional_arguments = 1
has_content = True
final_argument_whitespace = True
'''dot image generator'''
def run(self):
self.assert_has_content()
global nthUnnamed
try:
filename = self.arguments[0]
except:
filename = ('dot%d.png' % nthUnnamed)
nthUnnamed += 1
content = '\n'.join(self.content)
filetype = filename[filename.rfind('.')+1:]
args = ['dot', '-o'+filename, '-T'+filetype]
dot = sp.Popen(args, 0, None, sp.PIPE)
dot.stdin.write( content )
dot.stdin.close()
ret = dot.wait()
if ret:
return [nodes.error('some error occured')]
else:
return [nodes.raw('', '<img src="%s" alt="%s"/>'%(filename, filename), format='html')]
@esle
Copy link
Author

esle commented Feb 20, 2012

try this:

.. dot:: X.png 

    digraph X {
        a -> b;
        b -> c;
        c -> d;
        b -> d;
        a -> d;
    }

so above is a dot image, let's see how it works


.. dot:: Y.png

    digraph Y {
        x->y;
        y->x;
        x->z;
        z->x;
        y->z;
        z->y;
        a->x;
        a->y;
        a->z;
    }

this is another one

contributions are welcomed!

@esle
Copy link
Author

esle commented Feb 20, 2012

to use it, put dot.py in site-packages/docutils/parsers/rst/directives and add following to site-packages/docutils/parsers/rst/directives/__init__.py :

_directive_registry = { 
      ......
      'dot': ('dot', 'Dot'),
      ......
}

@fradeve
Copy link

fradeve commented Aug 19, 2014

Looks amazing, thank you!

@dasbh
Copy link

dasbh commented Nov 27, 2014

Slight modification to use inline images thus avoiding files created on disk.

"""
dot directive (require graphviz)
"""

import base64
from docutils import nodes
from docutils.parsers.rst import directives, Directive

import subprocess as sp

nthUnnamed = 0
class Dot(Directive):
    required_arguments = 0
    optional_arguments = 1
    has_content = True
    final_argument_whitespace = True

    '''dot image generator'''
    def run(self):
        self.assert_has_content()
        global nthUnnamed
        try:
            filename = self.arguments[0]
        except:
            filename = ('dot%d.png' % nthUnnamed)
            nthUnnamed += 1
        content = '\n'.join(self.content)

        return self.render(content, filename)

    def render(self, content, filename):
        filetype = filename[filename.rfind('.')+1:]
        args = ['dot', '-T'+filetype]
        try:
                dot = sp.Popen(args, stdin=sp.PIPE, stderr=sp.PIPE,stdout=sp.PIPE)
                dot.stdin.write( content )
                dot.stdin.close()

                output = ''
                while True:
                    b = dot.stdout.read()
                    if b :
                        output = output + str(b)
                    else :
                        break

                err = ''
                while True:
                    b= dot.stderr.readline()
                    if not b :
                        break
                    err = err + b

                ret = dot.wait()

                encoded = base64.b64encode(output)

                html = '<img src="data:image/%s;base64,%s" >' % (filetype, encoded)

        except Exception as e:
                err = str(e ) + ' : ' +  str(args)

        if html:
                return [nodes.raw('', html, format='html')]
        else:
                return [nodes.raw('', '<p>Error : ' + err + '</p><pre>' + content + '</pre>', format='html')]

@simon-gunacker
Copy link

Nice work. Any idea how I can make this run for pdf files?

@gbuzogany
Copy link

gbuzogany commented Jan 14, 2019

For python3 (so you can use with OmniMarkup for example):

"""
dot directive (require graphviz)
"""

import base64
from docutils import nodes
from docutils.parsers.rst import directives, Directive

import subprocess as sp

nthUnnamed = 0
class Dot(Directive):
    required_arguments = 0
    optional_arguments = 1
    has_content = True
    final_argument_whitespace = True

    '''dot image generator'''
    def run(self):
        self.assert_has_content()
        global nthUnnamed
        try:
            filename = self.arguments[0]
        except:
            filename = ('dot%d.png' % nthUnnamed)
            nthUnnamed += 1
        content = '\n'.join(self.content)

        return self.render(content, filename)

    def render(self, content, filename):
        filetype = filename[filename.rfind('.')+1:]
        args = ['dot', '-T'+filetype]
        html = None

        dot = sp.Popen(args, stdin=sp.PIPE, stderr=sp.PIPE,stdout=sp.PIPE)
        output = dot.communicate( content.encode('utf-8'))

        encoded = base64.b64encode(output[0]).decode('utf-8')

        html = '<img src="data:image/%s;base64,%s" >' % (filetype, encoded)

        if html:
                return [nodes.raw('', html, format='html')]
        else:
                return [nodes.raw('', '<p>Error : ' + err + '</p><pre>' + content + '</pre>', format='html')]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment