#309: The `graphviz` extension can now output SVG instead of PNG

images, controlled by the ``graphviz_output_format`` config value.
Patch by Henrique Bastos.
This commit is contained in:
Georg Brandl 2010-01-02 21:38:27 +01:00
parent 82fa1f3cfe
commit d32bd22ae7
4 changed files with 76 additions and 20 deletions

View File

@ -5,6 +5,7 @@ Substantial parts of the templates were written by Armin Ronacher
Other contributors, listed alphabetically, are: Other contributors, listed alphabetically, are:
* Henrique Bastos -- SVG support for graphviz extension
* Daniel Bültmann -- todo extension * Daniel Bültmann -- todo extension
* Michael Droettboom -- inheritance_diagram extension * Michael Droettboom -- inheritance_diagram extension
* Charles Duffy -- original graphviz extension * Charles Duffy -- original graphviz extension

View File

@ -3,6 +3,9 @@ Release 1.0 (in development)
* Added Epub builder. * Added Epub builder.
* #309: The ``graphviz`` extension can now output SVG instead of PNG
images, controlled by the ``graphviz_output_format`` config value.
* #284: All docinfo metadata is now put into the document metadata, not * #284: All docinfo metadata is now put into the document metadata, not
just the author. just the author.

View File

@ -25,8 +25,9 @@ It adds these directives:
"bar" -> "baz"; "bar" -> "baz";
} }
In HTML output, the code will be rendered to a PNG image. In LaTeX output, In HTML output, the code will be rendered to a PNG or SVG image (see
the code will be rendered to an embeddable PDF file. :confval:`graphviz_output_format`). In LaTeX output, the code will be
rendered to an embeddable PDF file.
.. directive:: graph .. directive:: graph
@ -75,3 +76,11 @@ There are also these new config values:
Additional command-line arguments to give to dot, as a list. The default is Additional command-line arguments to give to dot, as a list. The default is
an empty list. This is the right place to set global graph, node or edge an empty list. This is the right place to set global graph, node or edge
attributes via dot's ``-G``, ``-N`` and ``-E`` options. attributes via dot's ``-G``, ``-N`` and ``-E`` options.
.. confval:: graphviz_output_format
The output format for Graphviz when building HTML files. This must be either
``'png'`` or ``'svg'``; the default is ``'png'``.
.. versionadded:: 1.0
Previously, output always was PNG.

View File

@ -13,6 +13,7 @@
import re import re
import posixpath import posixpath
from os import path from os import path
from math import ceil
from subprocess import Popen, PIPE from subprocess import Popen, PIPE
try: try:
from hashlib import sha1 as sha from hashlib import sha1 as sha
@ -27,6 +28,7 @@ from sphinx.util.compat import Directive
mapname_re = re.compile(r'<map id="(.*?)"') 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): class GraphvizError(SphinxError):
@ -128,9 +130,44 @@ def render_dot(self, code, options, format, prefix='graphviz'):
return relfn, outfn return relfn, outfn
def render_dot_html(self, node, code, options, prefix='graphviz', imgcls=None): 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: try:
fname, outfn = render_dot(self, code, options, 'png', prefix) 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):
format = self.builder.config.graphviz_output_format
try:
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: except GraphvizError, exc:
self.builder.warn('dot code %r: ' % code + str(exc)) self.builder.warn('dot code %r: ' % code + str(exc))
raise nodes.SkipNode raise nodes.SkipNode
@ -139,23 +176,28 @@ def render_dot_html(self, node, code, options, prefix='graphviz', imgcls=None):
if fname is None: if fname is None:
self.body.append(self.encode(code)) self.body.append(self.encode(code))
else: else:
mapfile = open(outfn + '.map', 'rb') if format == 'svg':
try: svgtag = get_svg_tag(fname, outfn, imgcls)
imgmap = mapfile.readlines() self.body.append(svgtag)
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, self.encode(code).strip(), imgcss))
else: else:
# has a map: get the name of the map and connect the parts mapfile = open(outfn + '.map', 'rb')
mapname = mapname_re.match(imgmap[0]).group(1) try:
self.body.append('<img src="%s" alt="%s" usemap="#%s" %s/>\n' % imgmap = mapfile.readlines()
(fname, self.encode(code).strip(), finally:
mapname, imgcss)) mapfile.close()
self.body.extend(imgmap) 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, self.encode(code).strip(), 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, self.encode(code).strip(),
mapname, imgcss))
self.body.extend(imgmap)
self.body.append('</p>\n') self.body.append('</p>\n')
raise nodes.SkipNode raise nodes.SkipNode
@ -188,3 +230,4 @@ def setup(app):
app.add_directive('digraph', GraphvizSimple) app.add_directive('digraph', GraphvizSimple)
app.add_config_value('graphviz_dot', 'dot', 'html') app.add_config_value('graphviz_dot', 'dot', 'html')
app.add_config_value('graphviz_dot_args', [], 'html') app.add_config_value('graphviz_dot_args', [], 'html')
app.add_config_value('graphviz_output_format', 'png', 'html')