1
"""matplotlib-based directive for math rendering in reST using sphinx.
3
To use this extension, add ``mathmpl`` to the list of extensions in
6
*Warning*: this code is currently untested. ***MAY NOT WORK***
10
Current SVN versions of Sphinx now include built-in support for math.
11
There are two flavors:
13
- pngmath: uses dvipng to render the equation
15
- jsmath: renders the math in the browser using Javascript
17
To use these extensions instead of the code in this module, add
18
``sphinx.ext.pngmath`` or ``sphinx.ext.jsmath`` to the list of extensions in
21
All three of these options for math are designed to behave in the same
27
from hashlib import md5
31
from docutils import nodes
32
from docutils.parsers.rst import directives
33
from docutils.writers.html4css1 import HTMLTranslator
34
from sphinx.latexwriter import LaTeXTranslator
40
# Define LaTeX math node:
41
class latex_math(nodes.General, nodes.Element):
44
def fontset_choice(arg):
45
return directives.choice(arg, ['cm', 'stix', 'stixsans'])
47
options_spec = {'fontset': fontset_choice}
49
def math_role(role, rawtext, text, lineno, inliner,
50
options={}, content=[]):
52
latex = rawtext[i+1:-1]
53
node = latex_math(rawtext)
55
node['fontset'] = options.get('fontset', 'cm')
57
math_role.options = options_spec
59
def math_directive_run(content, block_text, options):
60
latex = ''.join(content)
61
node = latex_math(block_text)
63
node['fontset'] = options.get('fontset', 'cm')
67
from docutils.parsers.rst import Directive
69
# Register directive the old way:
70
from docutils.parsers.rst.directives import _directives
71
def math_directive(name, arguments, options, content, lineno,
72
content_offset, block_text, state, state_machine):
73
return math_directive_run(content, block_text, options)
74
math_directive.arguments = None
75
math_directive.options = options_spec
76
math_directive.content = 1
77
_directives['math'] = math_directive
79
class math_directive(Directive):
81
option_spec = options_spec
84
return math_directive_run(self.content, self.block_text,
86
from docutils.parsers.rst import directives
87
directives.register_directive('math', math_directive)
90
app.add_node(latex_math)
91
app.add_role('math', math_role)
93
# Add visit/depart methods to HTML-Translator:
94
def visit_latex_math_html(self, node):
95
source = self.document.attributes['source']
96
self.body.append(latex2html(node, source))
97
def depart_latex_math_html(self, node):
99
HTMLTranslator.visit_latex_math = visit_latex_math_html
100
HTMLTranslator.depart_latex_math = depart_latex_math_html
102
# Add visit/depart methods to LaTeX-Translator:
103
def visit_latex_math_latex(self, node):
104
inline = isinstance(node.parent, nodes.TextElement)
106
self.body.append('$%s$' % node['latex'])
108
self.body.extend(['\\begin{equation}',
111
def depart_latex_math_latex(self, node):
113
LaTeXTranslator.visit_latex_math = visit_latex_math_latex
114
LaTeXTranslator.depart_latex_math = depart_latex_math_latex
116
from matplotlib import rcParams
117
from matplotlib.mathtext import MathTextParser
118
rcParams['mathtext.fontset'] = 'cm'
119
mathtext_parser = MathTextParser("Bitmap")
122
# This uses mathtext to render the expression
123
def latex2png(latex, filename, fontset='cm'):
124
latex = "$%s$" % latex
125
orig_fontset = rcParams['mathtext.fontset']
126
rcParams['mathtext.fontset'] = fontset
127
if os.path.exists(filename):
128
depth = mathtext_parser.get_depth(latex, dpi=100)
130
print latex.encode("ascii", "backslashreplace")
132
depth = mathtext_parser.to_png(filename, latex, dpi=100)
134
warnings.warn("Could not render math expression %s" % latex,
137
rcParams['mathtext.fontset'] = orig_fontset
140
# LaTeX to HTML translation stuff:
141
def latex2html(node, source):
142
inline = isinstance(node.parent, nodes.TextElement)
143
latex = node['latex']
144
name = 'math-%s' % md5(latex).hexdigest()[-10:]
145
dest = os.path.join(STATIC_DIR, name + ".png")
147
depth = latex2png(latex, dest, node.get('fontset',
148
rcParams['mathtext.fontset']))
151
count = source.split('/doc/')[-1].count('/')
152
for i in range(count):
153
if os.path.exists(path): break
155
path = '../'+path #specifically added for matplotlib
159
cls = 'class="center" '
160
if inline and depth != 0:
161
style = 'style="position: relative; bottom: -%dpx"' % (depth + 1)
165
return '<img src="%s/%s.png" %s%s/>' % (path, name, cls, style)