summaryrefslogtreecommitdiff
path: root/sphinx/ext/graphviz.py
diff options
context:
space:
mode:
Diffstat (limited to 'sphinx/ext/graphviz.py')
-rw-r--r--sphinx/ext/graphviz.py92
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')