Hello. I've created a tasty sphinx extension to include embeded Message Sequence Charts (MSC) using mscgen[1]. I based the plugin in the grpahviz extension (as mscgen was inspired in graphviz too =) and I hope mscgen extension can make it into sphinx, because it's a great tool and it spreading quickly (for example Doxygen alredy support msggen embeded charts, as well as graphviz graphs).
Attached is the simple module. It requires epstopdf tool for now because mscgen can't create PDFs directly yet (this is needed only if you want latex/PDF output). I've attached a complete test project too as an example (I hope it's OK). Thanks for the great tool =) [1] http://www.mcternan.me.uk/mscgen/ -- Leandro Lucarella (luca) | Blog colectivo: http://www.mazziblog.com.ar/blog/ ---------------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------------- --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "sphinx-dev" group. To post to this group, send email to [email protected] To unsubscribe from this group, send email to [email protected] For more options, visit this group at http://groups.google.com/group/sphinx-dev?hl=en -~----------~----~----~----~------~----~------~--~---
mscgen-example.tar.gz
Description: application/tar-gz
# -*- coding: utf-8 -*-
"""
sphinx.ext.mscgen
~~~~~~~~~~~~~~~~~
Allow mscgen-formatted Message Sequence Chart graphs to be included in
Sphinx-generated documents inline.
:copyright: Copyright 2007-2009 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import sys
import posixpath
from os import path
from subprocess import Popen, PIPE
try:
from hashlib import sha1 as sha
except ImportError:
from sha import sha
from docutils import nodes
from sphinx.errors import SphinxError
from sphinx.util import ensuredir
from sphinx.util.compat import Directive
class MscgenError(SphinxError):
category = 'mscgen error'
class mscgen(nodes.General, nodes.Element):
pass
class Mscgen(Directive):
"""
Directive to insert arbitrary mscgen markup.
"""
has_content = True
required_arguments = 0
optional_arguments = 0
final_argument_whitespace = False
option_spec = {}
def run(self):
node = mscgen()
node['code'] = '\n'.join(self.content)
node['options'] = []
return [node]
class MscgenSimple(Directive):
"""
Directive to insert arbitrary mscgen markup.
"""
has_content = True
required_arguments = 0
optional_arguments = 0
final_argument_whitespace = False
option_spec = {}
def run(self):
node = mscgen()
node['code'] = 'msc {\n%s\n}\n' % ('\n'.join(self.content))
node['options'] = []
return [node]
def run_cmd(builder, cmd, cmd_name, cfg_name, stdin=''):
try:
p = Popen(cmd, stdout=PIPE, stdin=PIPE, stderr=PIPE)
except OSError, err:
if err.errno != 2: # No such file or directory
raise
builder.warn('%s command %r cannot be run (needed for mscgen '
'output), check the %s setting' %
cmd_name, builder.config.mscgen, cfg_name)
builder._mscgen_warned = True
return False
stdout, stderr = p.communicate(stdin)
if p.returncode != 0:
raise MscgenError('%s exited with error:\n[stderr]\n%s\n'
'[stdout]\n%s' % (cmd_name, stderr, stdout))
return True
def eps_to_pdf(builder, epsfn, pdffn):
epstopdf_args = [builder.config.mscgen_epstopdf]
epstopdf_args.extend(builder.config.mscgen_epstopdf_args)
epstopdf_args.extend(['--outfile=' + pdffn, epsfn])
return run_cmd(builder, epstopdf_args, 'epstopdf', 'mscgen_epstopdf')
def render_msc(self, code, options, format, prefix='mscgen'):
"""
Render mscgen code into a PNG or PDF output file.
"""
hashkey = code.encode('utf-8') + str(options) + \
str(self.builder.config.mscgen_args)
fname = '%s-%s.%s' % (prefix, sha(hashkey).hexdigest(), format)
if hasattr(self.builder, 'imgpath'):
# HTML
relfn = posixpath.join(self.builder.imgpath, fname)
outfn = path.join(self.builder.outdir, '_images', fname)
epsfn = outfn
else:
# LaTeX
relfn = fname
outfn = path.join(self.builder.outdir, fname)
format = 'eps'
epsfn = outfn[:-3] + format
if path.isfile(outfn):
return relfn, outfn
if hasattr(self.builder, '_mscgen_warned'):
return None
ensuredir(path.dirname(outfn))
# mscgen don't support encodings very well. ISO-8859-1 seems to work best,
# at least for PNG.
if isinstance(code, unicode):
code = code.encode('iso-8859-1')
mscgen_args = [self.builder.config.mscgen]
mscgen_args.extend(self.builder.config.mscgen_args)
mscgen_args.extend(options)
mscgen_args.extend(['-T', format, '-o', epsfn])
#if format == 'png': TODO
# mscgen_args.extend(['-Tismap', '-o%s.map' % outfn])
if not run_cmd(self.builder, mscgen_args, 'mscgen', 'mscgen', code):
return None
if epsfn != outfn and not eps_to_pdf(self.builder, epsfn, outfn):
return None
return relfn, outfn
def render_msc_html(self, node, code, options, prefix='mscgen', imgcls=None):
try:
fname, outfn = render_msc(self, code, options, 'png', prefix)
except MscgenError, exc:
self.builder.warn('mscgen code %r: ' % code + str(exc))
raise nodes.SkipNode
self.body.append(self.starttag(node, 'p', CLASS='mscgen'))
if fname is None:
self.body.append(self.encode(code))
else:
#mapfile = open(outfn + '.map', 'rb')
#try:
# imgmap = mapfile.readlines()
#finally:
# mapfile.close()
imgcss = imgcls and 'class="%s"' % imgcls or ''
#TODO: if not imgmap:
if True:
# nothing in image map
self.body.append('<img src="%s" alt="%s" %s/>\n' %
(fname, self.encode(code).strip(), imgcss))
else:
#TODO: mscgen ismap return a space separated list of mappings,
# the whole <map>...</map> should be generated by this
# plug-in.
# has a map: get the name of the map and connect the parts
mapname = mapname_re.match(imgmap[0]).group(1)
self.body.append('<img src="%s" alt="%s" usemap="#%s" %s/>\n' %
(fname, self.encode(code).strip(),
mapname, imgcss))
self.body.extend(imgmap)
self.body.append('</p>\n')
raise nodes.SkipNode
def html_visit_mscgen(self, node):
render_msc_html(self, node, node['code'], node['options'])
def render_msc_latex(self, node, code, options, prefix='mscgen'):
try:
fname, outfn = render_msc(self, code, options, 'pdf', prefix)
except MscgenError, exc:
self.builder.warn('mscgen code %r: ' % code + str(exc))
raise nodes.SkipNode
if fname is not None:
self.body.append('\\includegraphics{%s}' % fname)
raise nodes.SkipNode
def latex_visit_mscgen(self, node):
render_msc_latex(self, node, node['code'], node['options'])
def setup(app):
app.add_node(mscgen,
html=(html_visit_mscgen, None),
latex=(latex_visit_mscgen, None))
app.add_directive('mscgen', Mscgen)
app.add_directive('msc', MscgenSimple)
app.add_config_value('mscgen', 'mscgen', 'html')
app.add_config_value('mscgen_args', [], 'html')
app.add_config_value('mscgen_epstopdf', 'epstopdf', 'html')
app.add_config_value('mscgen_epstopdf_args', [], 'html')
