Skip to content

Instantly share code, notes, and snippets.

@dvarrazzo
Last active June 8, 2022 04:05
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save dvarrazzo/3807373 to your computer and use it in GitHub Desktop.
Save dvarrazzo/3807373 to your computer and use it in GitHub Desktop.
ditaa ascii art diagrams in reStructuredText
#!/usr/bin/env python
"""Custom reST_ directive for ditaa_ integration.
.. _reST: http://docutils.sourceforge.net/rst.html
.. _ditaa: http://ditaa.sourceforge.net/
"""
import os
import tempfile
from zlib import adler32
from subprocess import Popen, PIPE
from docutils.nodes import image, literal_block
from docutils.parsers.rst import Directive, directives
class DiTAA(Directive):
required_arguments = 0
optional_arguments = 0
has_content = True
_ditaa_flags = [
'no-antialias', 'no-separation', 'round-corners', 'no-shadows', ]
option_spec = {
# 'name': directives.uri, # sanitize me
'class': directives.class_option,
'alt': directives.unchanged,
'scale': directives.percentage,
}
for f in _ditaa_flags:
option_spec[f] = directives.flag
def run(self):
settings = self.state.document.settings
path = getattr(settings, 'ditaa_dir', '.')
if not os.path.exists(path):
os.makedirs(path)
nodes = []
# Y U NO STDINNNNN...
body = '\n'.join(self.content)
tf = tempfile.NamedTemporaryFile()
tf.write(body.encode('utf8'))
tf.flush()
# make a name
name = self.options.pop('name', None)
if not name:
name = "%08x" % (adler32(body) & 0xffffffff)
if not name.endswith('.png'):
name += '.png'
alt = self.options.get('alt', 'ditaa diagram')
classes = self.options.pop('class', ['ditaa'])
cmdline = ['ditaa', tf.name, os.path.join(path, name),
'--overwrite', '--encoding', 'utf8']
if 'scale' in self.options:
cmdline.extend(['--scale',
"%f" % (float(self.options['scale']) / 100)])
for f in self._ditaa_flags:
if f in self.options:
cmdline.append("--" + f)
try:
p = Popen(cmdline, stdout=PIPE, stderr=PIPE)
out, err = p.communicate()
except Exception, exc:
error = self.state_machine.reporter.error(
'Failed to run ditaa: %s' % (exc, ),
literal_block(self.block_text, self.block_text),
line=self.lineno)
nodes.append(error)
else:
if p.returncode == 0:
url = name
urlpath = getattr(settings, 'ditaa_url_path')
if not urlpath and path != '.':
urlpath = path
if urlpath:
url = urlpath + '/' + name
imgnode = image(uri=url, classes=classes, alt=alt)
nodes.append(imgnode)
else:
error = self.state_machine.reporter.error(
'Error in "%s" directive: %s' % (self.name, err),
literal_block(self.block_text, self.block_text),
line=self.lineno)
nodes.append(error)
return nodes
def get_ditaa_settings_spec():
from docutils import SettingsSpec
ss = SettingsSpec()
ss.settings_spec = (
'Diagrams Generation Options',
None,
(
("Diagrams output directory. Default: '.'",
['--ditaa-dir'],
{'default': '.', 'metavar': 'DIR'}),
("Diagrams url path. Defaults to DIR",
['--ditaa-url-path'],
{'metavar': 'PATH'}),
))
return ss
def pubprog():
"""Just an example of how to pass options to publish_programmatically"""
directives.register_directive('ditaa', DiTAA)
from docutils.core import publish_string
print publish_string(open('ditaa_rst.txt').read(),
writer_name='html',
settings_spec=get_ditaa_settings_spec(),
settings_overrides={'ditaa_dir': 'qux'})
if __name__ == '__main__':
directives.register_directive('ditaa', DiTAA)
from docutils.core import publish_cmdline
publish_cmdline(writer_name='html', description="HTML with DiTAA",
settings_spec=get_ditaa_settings_spec())
Integrating reST_ with ditaa_
=============================
.. _reST: http://docutils.sourceforge.net/rst.html
.. _ditaa: http://ditaa.sourceforge.net/
This is a test, with a diagram:
.. ditaa::
:alt: Demo diagram
:scale: 150%
+--------+ +-------+ +-------+
| | --+ ditaa +--> | |
| Text | +-------+ |diagram|
|Document| |!magic!| | |
| {d}| | | | |
+---+----+ +-------+ +-------+
: ^
| Lots of work |
+-------------------------+
It should result in a diagram or something.
@sgh
Copy link

sgh commented Feb 9, 2013

Where should the file be placed to work?

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