summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonathan Waltman <jonathan.waltman@gmail.com>2011-03-05 23:59:34 -0600
committerJonathan Waltman <jonathan.waltman@gmail.com>2011-03-05 23:59:34 -0600
commit20dcad36a3fd50aa541581c87f54b9e786018570 (patch)
tree6d1ec8d99601f132294d562ca6476930fb7a1a59
parentd26bc39ec77149112129c1f34eab88af1d12362a (diff)
downloadsphinx-git-20dcad36a3fd50aa541581c87f54b9e786018570.tar.gz
Add Texinfo support in ext.graphviz, ext.inheritance_diagram and ext.mathbase.
-rw-r--r--sphinx/builders/texinfo.py4
-rw-r--r--sphinx/ext/graphviz.py22
-rw-r--r--sphinx/ext/inheritance_diagram.py20
-rw-r--r--sphinx/ext/mathbase.py13
-rw-r--r--sphinx/writers/texinfo.py205
-rw-r--r--tests/test_build_texinfo.py1
6 files changed, 153 insertions, 112 deletions
diff --git a/sphinx/builders/texinfo.py b/sphinx/builders/texinfo.py
index 747be28f5..535c527e0 100644
--- a/sphinx/builders/texinfo.py
+++ b/sphinx/builders/texinfo.py
@@ -86,8 +86,8 @@ class TexinfoBuilder(Builder):
"""
name = 'texinfo'
format = 'texinfo'
- supported_image_types = ['application/pdf', 'image/png',
- 'image/gif', 'image/jpeg']
+ supported_image_types = ['image/png', 'image/jpeg',
+ 'image/gif',]
def init(self):
self.docnames = []
diff --git a/sphinx/ext/graphviz.py b/sphinx/ext/graphviz.py
index 57a08375d..90874f0b2 100644
--- a/sphinx/ext/graphviz.py
+++ b/sphinx/ext/graphviz.py
@@ -299,10 +299,30 @@ def render_dot_latex(self, node, code, options, prefix='graphviz'):
def latex_visit_graphviz(self, node):
render_dot_latex(self, node, node['code'], node['options'])
+
+def render_dot_texinfo(self, node, code, options, prefix='graphviz'):
+ try:
+ fname, outfn = render_dot(self, code, options, 'png', prefix)
+ except GraphvizError, exc:
+ self.builder.warn('dot code %r: ' % code + str(exc))
+ raise nodes.SkipNode
+ if fname is not None:
+ self.body.append('\n\n@float\n')
+ if node.get('caption'):
+ self.body.append('@caption{%s}\n' % self.escape_arg(caption))
+ self.body.append('@image{%s,,,[graphviz],png}\n'
+ '@end float\n\n' % fname[:-4])
+ raise nodes.SkipNode
+
+def texinfo_visit_graphviz(self, node):
+ render_dot_texinfo(self, node, node['code'], node['options'])
+
+
def setup(app):
app.add_node(graphviz,
html=(html_visit_graphviz, None),
- latex=(latex_visit_graphviz, None))
+ latex=(latex_visit_graphviz, None),
+ texinfo=(texinfo_visit_graphviz, None))
app.add_directive('graphviz', Graphviz)
app.add_directive('graph', GraphvizSimple)
app.add_directive('digraph', GraphvizSimple)
diff --git a/sphinx/ext/inheritance_diagram.py b/sphinx/ext/inheritance_diagram.py
index fba083bf4..a2490486a 100644
--- a/sphinx/ext/inheritance_diagram.py
+++ b/sphinx/ext/inheritance_diagram.py
@@ -47,7 +47,8 @@ except ImportError:
from docutils import nodes
from docutils.parsers.rst import directives
-from sphinx.ext.graphviz import render_dot_html, render_dot_latex
+from sphinx.ext.graphviz import render_dot_html, render_dot_latex, \
+ render_dot_texinfo
from sphinx.util.compat import Directive
@@ -354,6 +355,21 @@ def latex_visit_inheritance_diagram(self, node):
raise nodes.SkipNode
+def texinfo_visit_inheritance_diagram(self, node):
+ """
+ Output the graph for Texinfo. This will insert a PNG.
+ """
+ graph = node['graph']
+
+ graph_hash = get_graph_hash(node)
+ name = 'inheritance%s' % graph_hash
+
+ dotcode = graph.generate_dot(name, env=self.builder.env,
+ graph_attrs={'size': '"6.0,6.0"'})
+ render_dot_texinfo(self, node, dotcode, [], 'inheritance')
+ raise nodes.SkipNode
+
+
def skip(self, node):
raise nodes.SkipNode
@@ -366,7 +382,7 @@ def setup(app):
html=(html_visit_inheritance_diagram, None),
text=(skip, None),
man=(skip, None),
- texinfo=(skip, None))
+ texinfo=(texinfo_visit_inheritance_diagram, None))
app.add_directive('inheritance-diagram', InheritanceDiagram)
app.add_config_value('inheritance_graph_attrs', {}, False),
app.add_config_value('inheritance_node_attrs', {}, False),
diff --git a/sphinx/ext/mathbase.py b/sphinx/ext/mathbase.py
index d021c60bd..b5473449e 100644
--- a/sphinx/ext/mathbase.py
+++ b/sphinx/ext/mathbase.py
@@ -12,7 +12,6 @@
from docutils import nodes, utils
from docutils.parsers.rst import directives
-from sphinx.writers import texinfo
from sphinx.util.compat import Directive
@@ -127,16 +126,20 @@ def man_visit_eqref(self, node):
def texinfo_visit_math(self, node):
- self.body.append('@math{' + texinfo.escape_arg(node['latex']) + '}')
+ self.body.append('@math{' + self.escape_arg(node['latex']) + '}')
raise nodes.SkipNode
def texinfo_visit_displaymath(self, node):
- self.visit_paragraph(node)
+ if node.get('label'):
+ self.add_anchor(node['label'], node)
+ self.body.append('\n\n@example\n%s\n@end example\n\n' %
+ self.escape_arg(node['latex']))
def texinfo_depart_displaymath(self, node):
- self.depart_paragraph(node)
+ pass
def texinfo_visit_eqref(self, node):
- self.body.append(node['target'])
+ self.add_xref(node['docname'] + ':' + node['target'],
+ node['target'], node)
raise nodes.SkipNode
diff --git a/sphinx/writers/texinfo.py b/sphinx/writers/texinfo.py
index c686a78ea..e4e1d7bb8 100644
--- a/sphinx/writers/texinfo.py
+++ b/sphinx/writers/texinfo.py
@@ -85,45 +85,6 @@ def find_subsections(section):
return result
-## Escaping
-# Which characters to escape depends on the context. In some cases,
-# namely menus and node names, it's not possible to escape certain
-# characters.
-
-def escape(s):
- """Return a string with Texinfo command characters escaped."""
- s = s.replace('@', '@@')
- s = s.replace('{', '@{')
- s = s.replace('}', '@}')
- # Prevent "--" from being converted to an "em dash"
- # s = s.replace('-', '@w{-}')
- return s
-
-def escape_arg(s):
- """Return an escaped string suitable for use as an argument
- to a Texinfo command."""
- s = escape(s)
- # Commas are the argument delimeters
- s = s.replace(',', '@comma{}')
- # Normalize white space
- s = ' '.join(s.split()).strip()
- return s
-
-def escape_id(s):
- """Return an escaped string suitable for node names and xrefs anchors."""
- bad_chars = ',:.()'
- for bc in bad_chars:
- s = s.replace(bc, ' ')
- s = ' '.join(s.split()).strip()
- return escape(s)
-
-def escape_menu(s):
- """Return an escaped string suitable for menu entries."""
- s = escape_arg(s)
- s = s.replace(':', ';')
- s = ' '.join(s.split()).strip()
- return s
-
class TexinfoWriter(writers.Writer):
"""Texinfo writer for generating Texinfo documents."""
supported = ('texinfo', 'texi')
@@ -202,6 +163,7 @@ class TexinfoTranslator(nodes.NodeVisitor):
self.curfilestack = []
self.footnotestack = []
self.in_footnote = 0
+ self.handled_abbrs = set()
def finish(self):
if self.previous_section is None:
@@ -232,19 +194,19 @@ class TexinfoTranslator(nodes.NodeVisitor):
'author': settings.author,
# if empty, use basename of input file
'filename': settings.texinfo_filename,
- 'release': escape(self.builder.config.release),
- 'project': escape(self.builder.config.project),
- 'copyright': escape(self.builder.config.copyright),
- 'date': escape(self.builder.config.today or
- ustrftime(self.builder.config.today_fmt
- or _('%B %d, %Y')))
+ 'release': self.escape(self.builder.config.release),
+ 'project': self.escape(self.builder.config.project),
+ 'copyright': self.escape(self.builder.config.copyright),
+ 'date': self.escape(self.builder.config.today or
+ ustrftime(self.builder.config.today_fmt
+ or _('%B %d, %Y')))
})
# title
title = elements['title']
if not title:
title = self.document.next_node(nodes.title)
title = (title and title.astext()) or '<untitled>'
- elements['title'] = escape_id(title) or '<untitled>'
+ elements['title'] = self.escape_id(title) or '<untitled>'
# filename
if not elements['filename']:
elements['filename'] = self.document.get('source') or 'untitled'
@@ -254,14 +216,14 @@ class TexinfoTranslator(nodes.NodeVisitor):
# direntry
if settings.texinfo_dir_entry:
entry = self.format_menu_entry(
- escape_menu(settings.texinfo_dir_entry),
+ self.escape_menu(settings.texinfo_dir_entry),
'(%s)' % elements['filename'],
- escape_arg(settings.texinfo_dir_description))
+ self.escape_arg(settings.texinfo_dir_description))
elements['direntry'] = ('@dircategory %s\n'
'@direntry\n'
'%s'
'@end direntry\n') % (
- escape_id(settings.texinfo_dir_category), entry)
+ self.escape_id(settings.texinfo_dir_category), entry)
elements['copying'] = COPYING % elements
# allow the user to override them all
elements.update(settings.texinfo_elements)
@@ -282,7 +244,7 @@ class TexinfoTranslator(nodes.NodeVisitor):
for section in self.document.traverse(nodes.section):
title = section.next_node(nodes.Titular)
name = (title and title.astext()) or '<untitled>'
- node_id = escape_id(name) or '<untitled>'
+ node_id = self.escape_id(name) or '<untitled>'
assert node_id and name
nth, suffix = 1, ''
while node_id + suffix in self.written_ids:
@@ -347,6 +309,45 @@ class TexinfoTranslator(nodes.NodeVisitor):
rellinks['Top'][0] = first
rellinks[first][1] = 'Top'
+ ## Escaping
+ # Which characters to escape depends on the context. In some cases,
+ # namely menus and node names, it's not possible to escape certain
+ # characters.
+
+ def escape(self, s):
+ """Return a string with Texinfo command characters escaped."""
+ s = s.replace('@', '@@')
+ s = s.replace('{', '@{')
+ s = s.replace('}', '@}')
+ # prevent "--" from being converted to an "em dash"
+ # s = s.replace('-', '@w{-}')
+ return s
+
+ def escape_arg(self, s):
+ """Return an escaped string suitable for use as an argument
+ to a Texinfo command."""
+ s = self.escape(s)
+ # commas are the argument delimeters
+ s = s.replace(',', '@comma{}')
+ # normalize white space
+ s = ' '.join(s.split()).strip()
+ return s
+
+ def escape_id(self, s):
+ """Return an escaped string suitable for node names and anchors."""
+ bad_chars = ',:.()'
+ for bc in bad_chars:
+ s = s.replace(bc, ' ')
+ s = ' '.join(s.split()).strip()
+ return self.escape(s)
+
+ def escape_menu(self, s):
+ """Return an escaped string suitable for menu entries."""
+ s = self.escape_arg(s)
+ s = s.replace(':', ';')
+ s = ' '.join(s.split()).strip()
+ return s
+
def format_menu_entry(self, name, node_name, desc):
if name == node_name:
s = '* %s:: ' % (name,)
@@ -366,8 +367,8 @@ class TexinfoTranslator(nodes.NodeVisitor):
name, desc = parts
else:
desc = ''
- name = escape_menu(name)
- desc = escape(desc)
+ name = self.escape_menu(name)
+ desc = self.escape(desc)
self.body.append(self.format_menu_entry(name, entry, desc))
def add_menu(self, node_name):
@@ -384,7 +385,7 @@ class TexinfoTranslator(nodes.NodeVisitor):
entries = self.node_menus[name]
if not entries:
return
- self.body.append('\n%s\n\n' % (escape(self.node_names[name],)))
+ self.body.append('\n%s\n\n' % (self.escape(self.node_names[name],)))
self.add_menu_entries(entries)
for subentry in entries:
_add_detailed_menu(subentry)
@@ -420,9 +421,9 @@ class TexinfoTranslator(nodes.NodeVisitor):
for entry in entries:
if not entry[3]:
continue
- name = escape_menu(entry[0])
+ name = self.escape_menu(entry[0])
sid = self.get_short_id('%s:%s' % (entry[2], entry[3]))
- desc = escape_arg(entry[6])
+ desc = self.escape_arg(entry[6])
me = self.format_menu_entry(name, sid, desc)
ret.append(me)
ret.append('@end menu\n')
@@ -440,7 +441,7 @@ class TexinfoTranslator(nodes.NodeVisitor):
self.builder.docnames)
if not content:
continue
- node_name = escape_id(indexcls.localname)
+ node_name = self.escape_id(indexcls.localname)
self.indices.append((node_name,
generate(content, collapsed)))
self.indices.append((_('Index'), '\n@printindex ge\n'))
@@ -481,7 +482,7 @@ class TexinfoTranslator(nodes.NodeVisitor):
if id.startswith('index-'):
return
id = self.curfilestack[-1] + ':' + id
- eid = escape_id(id)
+ eid = self.escape_id(id)
sid = self.get_short_id(id)
for id in (eid, sid):
if id not in self.written_ids:
@@ -489,11 +490,11 @@ class TexinfoTranslator(nodes.NodeVisitor):
self.written_ids.add(id)
def add_xref(self, id, name, node):
- name = escape_menu(name)
+ name = self.escape_menu(name)
sid = self.get_short_id(id)
self.body.append('@pxref{%s,,%s}' % (sid, name))
self.referenced_ids.add(sid)
- self.referenced_ids.add(escape_id(id))
+ self.referenced_ids.add(self.escape_id(id))
## Visiting
@@ -507,7 +508,7 @@ class TexinfoTranslator(nodes.NodeVisitor):
self.curfilestack.pop()
def visit_Text(self, node):
- s = escape(node.astext())
+ s = self.escape(node.astext())
if self.escape_newlines:
s = s.replace('\n', ' ')
self.body.append(s)
@@ -633,8 +634,8 @@ class TexinfoTranslator(nodes.NodeVisitor):
if not uri:
return
if uri.startswith('mailto:'):
- uri = escape_arg(uri[7:])
- name = escape_arg(name)
+ uri = self.escape_arg(uri[7:])
+ name = self.escape_arg(name)
if not name or name == uri:
self.body.append('@email{%s}' % uri)
else:
@@ -656,19 +657,19 @@ class TexinfoTranslator(nodes.NodeVisitor):
elif uri.startswith('info:'):
# references to an external Info file
uri = uri[5:].replace('_', ' ')
- uri = escape_arg(uri)
+ uri = self.escape_arg(uri)
id = 'Top'
if '#' in uri:
uri, id = uri.split('#', 1)
- id = escape_id(id)
- name = escape_menu(name)
+ id = self.escape_id(id)
+ name = self.escape_menu(name)
if name == id:
self.body.append('@pxref{%s,,,%s}' % (id, uri))
else:
self.body.append('@pxref{%s,,%s,%s}' % (id, name, uri))
else:
- uri = escape_arg(uri)
- name = escape_arg(name)
+ uri = self.escape_arg(uri)
+ name = self.escape_arg(name)
show_urls = 'footnote'
if self.in_footnote:
show_urls = 'inline'
@@ -687,7 +688,7 @@ class TexinfoTranslator(nodes.NodeVisitor):
def visit_title_reference(self, node):
text = node.astext()
- self.body.append('@cite{%s}' % escape_arg(text))
+ self.body.append('@cite{%s}' % self.escape_arg(text))
raise nodes.SkipNode
## Blocks
@@ -968,7 +969,7 @@ class TexinfoTranslator(nodes.NodeVisitor):
def visit_admonition(self, node, name=''):
if not name:
- name = escape(node[0].astext())
+ name = self.escape(node[0].astext())
self.body.append('\n@cartouche\n'
'@quotation %s\n' % name)
def depart_admonition(self, node):
@@ -978,7 +979,7 @@ class TexinfoTranslator(nodes.NodeVisitor):
def _make_visit_admonition(typ):
def visit(self, node):
self.body.append('\n@cartouche\n'
- '@quotation %s\n' % escape(_(typ)))
+ '@quotation %s\n' % self.escape(_(typ)))
return visit
visit_attention = _make_visit_admonition('Attention')
@@ -1030,7 +1031,7 @@ class TexinfoTranslator(nodes.NodeVisitor):
raise nodes.SkipNode
title = node[0]
self.visit_rubric(title)
- self.body.append('%s\n' % escape(title.astext()))
+ self.body.append('%s\n' % self.escape(title.astext()))
def depart_topic(self, node):
pass
@@ -1078,10 +1079,10 @@ class TexinfoTranslator(nodes.NodeVisitor):
return
name, ext = path.splitext(uri)
attrs = node.attributes
- # ignored in non-tex output
+ # width and height ignored in non-tex output
width = self.tex_image_length(attrs.get('width', ''))
height = self.tex_image_length(attrs.get('height', ''))
- alt = escape_arg(attrs.get('alt', ''))
+ alt = self.escape_arg(attrs.get('alt', ''))
self.body.append('\n\n@image{%s,%s,%s,%s,%s}\n\n' %
(name, width, height, alt, ext[1:]))
def depart_image(self, node):
@@ -1116,11 +1117,11 @@ class TexinfoTranslator(nodes.NodeVisitor):
raise nodes.SkipNode
def visit_system_message(self, node):
- self.body.append('\n\n@w{----------- System Message: %s/%s -----------} '
+ self.body.append('\n@w{----------- System Message: %s/%s -----------} '
'(%s, line %s)\n' % (
node.get('type', '?'),
node.get('level', '?'),
- escape(node.get('source', '?')),
+ self.escape(node.get('source', '?')),
node.get('line', '?')))
def depart_system_message(self, node):
pass
@@ -1161,8 +1162,8 @@ class TexinfoTranslator(nodes.NodeVisitor):
lastname = production['tokenname']
else:
s = '%s ' % (' '*maxlen)
- self.body.append(escape(s))
- self.body.append(escape(production.astext() + '\n'))
+ self.body.append(self.escape(s))
+ self.body.append(self.escape(production.astext() + '\n'))
self.depart_literal_block(None)
raise nodes.SkipNode
@@ -1179,7 +1180,7 @@ class TexinfoTranslator(nodes.NodeVisitor):
def visit_index(self, node):
for entry in node['entries']:
typ, text, tid, text2 = entry
- text = escape_menu(text)
+ text = self.escape_menu(text)
self.body.append('@geindex %s\n' % text)
def visit_refcount(self, node):
@@ -1193,7 +1194,7 @@ class TexinfoTranslator(nodes.NodeVisitor):
intro += ': '
else:
intro += '.'
- self.body.append('\n\n%s' % escape(intro))
+ self.body.append('\n\n%s' % self.escape(intro))
def depart_versionmodified(self, node):
self.body.append('\n\n')
@@ -1207,7 +1208,7 @@ class TexinfoTranslator(nodes.NodeVisitor):
self.footnotestack.pop()
def visit_centered(self, node):
- txt = escape_arg(node.astext())
+ txt = self.escape_arg(node.astext())
self.body.append('\n\n@center %s\n\n' % txt)
raise nodes.SkipNode
@@ -1225,9 +1226,11 @@ class TexinfoTranslator(nodes.NodeVisitor):
pass
def visit_acks(self, node):
- pass
- def depart_acks(self, node):
- pass
+ self.body.append('\n\n')
+ self.body.append(', '.join(n.astext()
+ for n in node.children[0].children) + '.')
+ self.body.append('\n\n')
+ raise nodes.SkipNode
def visit_highlightlang(self, node):
pass
@@ -1237,22 +1240,18 @@ class TexinfoTranslator(nodes.NodeVisitor):
## Desc
desc_map = {
- 'function' : 'Function',
- 'class': 'Class',
- 'method': 'Method',
- 'classmethod': 'Class Method',
+ 'cfunction': 'C Function',
+ 'classmethod': 'Class Method',
+ 'cmacro': 'C Macro',
+ 'cmdoption': 'Command Option',
+ 'cmember': 'C Member',
+ 'confval': 'Configuration Value',
+ 'ctype': 'C Type',
+ 'cvar': 'C Variable',
+ 'describe': 'Description',
+ 'envvar': 'Environment Variable',
'staticmethod': 'Static Method',
- 'exception': 'Exception',
- 'data': 'Data',
- 'attribute': 'Attribute',
- 'opcode': 'Opcode',
- 'cfunction': 'C Function',
- 'cmember': 'C Member',
- 'cmacro': 'C Macro',
- 'ctype': 'C Type',
- 'cvar': 'C Variable',
- 'cmdoption': 'Option',
- 'describe': 'Description',
+ 'var': 'Variable',
}
def visit_desc(self, node):
@@ -1266,7 +1265,7 @@ class TexinfoTranslator(nodes.NodeVisitor):
self.add_anchor(id, node)
typ = _(self.desc_map.get(self.desctype,
self.desctype.capitalize()))
- self.body.append('\n%s {%s} ' % (self.at_deffnx, escape_arg(typ)))
+ self.body.append('\n%s {%s} ' % (self.at_deffnx, self.escape_arg(typ)))
self.at_deffnx = '@deffnx'
def depart_desc_signature(self, node):
self.body.append("\n")
@@ -1302,7 +1301,7 @@ class TexinfoTranslator(nodes.NodeVisitor):
self.body.append(', ')
else:
self.first_param = 0
- self.body.append(escape(node.astext()))
+ self.body.append(self.escape(node.astext()))
raise nodes.SkipNode
def visit_desc_optional(self, node):
@@ -1324,9 +1323,11 @@ class TexinfoTranslator(nodes.NodeVisitor):
pass
def visit_abbreviation(self, node):
+ abbr = node.astext()
self.body.append('@abbr{')
- if node.hasattr('explanation'):
- self.context.append(', %s}' % escape_arg(node['explanation']))
+ if node.hasattr('explanation') and abbr not in self.handled_abbrs:
+ self.context.append(',%s}' % self.escape_arg(node['explanation']))
+ self.handled_abbrs.add(abbr)
else:
self.context.append('}')
def depart_abbreviation(self, node):
diff --git a/tests/test_build_texinfo.py b/tests/test_build_texinfo.py
index ce689a696..eedfd1fc7 100644
--- a/tests/test_build_texinfo.py
+++ b/tests/test_build_texinfo.py
@@ -29,6 +29,7 @@ texinfo_warnfile = StringIO()
TEXINFO_WARNINGS = ENV_WARNINGS + """\
None:None: WARNING: no matching candidate for image URI u'foo.\\*'
+None:None: WARNING: no matching candidate for image URI u'svgimg.\\*'
"""
if sys.version_info >= (3, 0):