diff options
Diffstat (limited to 'sphinx/ext/graphviz.py')
| -rw-r--r-- | sphinx/ext/graphviz.py | 92 |
1 files changed, 72 insertions, 20 deletions
diff --git a/sphinx/ext/graphviz.py b/sphinx/ext/graphviz.py index b7cf93d2..106de7a6 100644 --- a/sphinx/ext/graphviz.py +++ b/sphinx/ext/graphviz.py @@ -13,6 +13,7 @@ import re import posixpath from os import path +from math import ceil from subprocess import Popen, PIPE try: from hashlib import sha1 as sha @@ -20,13 +21,15 @@ except ImportError: from sha import sha from docutils import nodes +from docutils.parsers.rst import directives from sphinx.errors import SphinxError -from sphinx.util import ensuredir, ENOENT, EPIPE +from sphinx.util.osutil import ensuredir, ENOENT, EPIPE from sphinx.util.compat import Directive mapname_re = re.compile(r'<map id="(.*?)"') +svg_dim_re = re.compile(r'<svg\swidth="(\d+)pt"\sheight="(\d+)pt"', re.M) class GraphvizError(SphinxError): @@ -45,7 +48,9 @@ class Graphviz(Directive): required_arguments = 0 optional_arguments = 0 final_argument_whitespace = False - option_spec = {} + option_spec = { + 'alt': directives.unchanged, + } def run(self): dotcode = '\n'.join(self.content) @@ -56,6 +61,8 @@ class Graphviz(Directive): node = graphviz() node['code'] = dotcode node['options'] = [] + if 'alt' in self.options: + node['alt'] = self.options['alt'] return [node] @@ -67,13 +74,17 @@ class GraphvizSimple(Directive): required_arguments = 1 optional_arguments = 0 final_argument_whitespace = False - option_spec = {} + option_spec = { + 'alt': directives.unchanged, + } def run(self): node = graphviz() node['code'] = '%s %s {\n%s\n}\n' % \ (self.name, self.arguments[0], '\n'.join(self.content)) node['options'] = [] + if 'alt' in self.options: + node['alt'] = self.options['alt'] return [node] @@ -139,10 +150,45 @@ def render_dot(self, code, options, format, prefix='graphviz'): return relfn, outfn +def get_svg_tag(svgref, svgfile, imgcls=None): + # Webkit can't figure out svg dimensions when using object tag + # so we need to get it from the svg file + fp = open(svgfile, 'r') + try: + for line in fp: + match = svg_dim_re.match(line) + if match: + dimensions = match.groups() + break + else: + dimensions = None + finally: + fp.close() + + # We need this hack to make WebKit show our object tag properly + def pt2px(x): + return int(ceil((96.0/72.0) * float(x))) + + if dimensions: + style = ' width="%s" height="%s"' % tuple(map(pt2px, dimensions)) + else: + style = '' + + # The object tag works fine on Firefox and WebKit + # Besides it's a hack, this strategy does not mess with templates. + imgcss = imgcls and ' class="%s"' % imgcls or '' + return '<object type="image/svg+xml" data="%s"%s%s/>\n' % \ + (svgref, imgcss, style) + + def render_dot_html(self, node, code, options, prefix='graphviz', imgcls=None, alt=None): + format = self.builder.config.graphviz_output_format try: - fname, outfn = render_dot(self, code, options, 'png', prefix) + if format not in ('png', 'svg'): + raise GraphvizError("graphviz_output_format must be one of 'png', " + "'svg', but is %r" % format) + fname, outfn = render_dot(self, code, options, format, prefix) except GraphvizError, exc: self.builder.warn('dot code %r: ' % code + str(exc)) raise nodes.SkipNode @@ -151,24 +197,29 @@ def render_dot_html(self, node, code, options, prefix='graphviz', 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 '' if alt is None: - alt = self.encode(code).strip() - if len(imgmap) == 2: - # nothing in image map (the lines are <map> and </map>) - self.body.append('<img src="%s" alt="%s" %s/>\n' % - (fname, alt, imgcss)) + alt = node.get('alt', self.encode(code).strip()) + if format == 'svg': + svgtag = get_svg_tag(fname, outfn, imgcls) + self.body.append(svgtag) else: - # 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, alt, mapname, imgcss)) - self.body.extend(imgmap) + mapfile = open(outfn + '.map', 'rb') + try: + imgmap = mapfile.readlines() + finally: + mapfile.close() + imgcss = imgcls and 'class="%s"' % imgcls or '' + if len(imgmap) == 2: + # nothing in image map (the lines are <map> and </map>) + self.body.append('<img src="%s" alt="%s" %s/>\n' % + (fname, alt, imgcss)) + else: + # 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, alt, mapname, imgcss)) + self.body.extend(imgmap) + self.body.append('</p>\n') raise nodes.SkipNode @@ -201,3 +252,4 @@ def setup(app): app.add_directive('digraph', GraphvizSimple) app.add_config_value('graphviz_dot', 'dot', 'html') app.add_config_value('graphviz_dot_args', [], 'html') + app.add_config_value('graphviz_output_format', 'png', 'html') |
