Leandro Lucarella, el 15 de mayo a las 17:45 me escribiste:
> 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 finished the module to support image maps for HTML output.

I'd like to know if there is any interest in adding this extension to
Sphinx, because if there is no interest I would set up a project to it (of
course I will love that the module would be part of Sphinx).

Thanks!

-- 
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
-~----------~----~----~----~------~----~------~--~---

# -*- 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 get_map_code(mapfn, id):
    mapfile = open(mapfn)
    try:
        lines = mapfile.readlines()
    finally:
        mapfile.close()
    mapcode = '<map id="%s" name="%s">' % (id, id)
    for line in lines:
        (type, name, coords) = line.split(' ', 2)
        mapcode += '<area shape="%s" href="%s" alt="" coords="%s" />' % (
                    type, name, coords)
    mapcode += '</map>'
    return mapcode


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)
    id = sha(hashkey).hexdigest()
    fname = '%s-%s.%s' % (prefix, id, format)
    if hasattr(self.builder, 'imgpath'):
        # HTML
        relfn = posixpath.join(self.builder.imgpath, fname)
        outfn = path.join(self.builder.outdir, '_images', fname)
        tmpfn = outfn
        mapfn = outfn + '.map'
    else:
        # LaTeX
        relfn = fname
        outfn = path.join(self.builder.outdir, fname)
        format = 'eps'
        tmpfn = outfn[:-3] + format

    if path.isfile(outfn):
        return relfn, outfn, id

    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', tmpfn])
    if not run_cmd(self.builder, mscgen_args, 'mscgen', 'mscgen', code):
        return None

    if format == 'png':
        mscgen_args = mscgen_args[:-4] + ['-T', 'ismap', '-o', mapfn]
        if not run_cmd(self.builder, mscgen_args, 'mscgen', 'mscgen', code):
            return None
    else: # PDF/EPS
        if not eps_to_pdf(self.builder, tmpfn, outfn):
            return None

    return relfn, outfn, id


def render_msc_html(self, node, code, options, prefix='mscgen', imgcls=None):
    try:
        fname, outfn, id = 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:
        imgmap = get_map_code(outfn + '.map', id)
        imgcss = imgcls and 'class="%s"' % imgcls or ''
        if not imgmap:
            # nothing in image map
            self.body.append('<img src="%s" alt="%s" %s/>\n' %
                             (fname, self.encode(code).strip(), imgcss))
        else:
            # has a map
            self.body.append('<img src="%s" alt="%s" usemap="#%s" %s/>\n' %
                             (fname, self.encode(code).strip(), id, 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, id = 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')

Reply via email to