~unpingco/ipvision/ipvision-trunk-dev

« back to all changes in this revision

Viewing changes to doc/tools/sphinxext/mathmpl.py

  • Committer: Fernando Perez
  • Date: 2008-10-08 06:59:03 UTC
  • Revision ID: fernando.perez@berkeley.edu-20081008065903-3hkufrmiomv0fqpb
Initial project commit with documentation skeleton using Sphinx.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
"""matplotlib-based directive for math rendering in reST using sphinx.
 
2
 
 
3
To use this extension, add ``mathmpl`` to the list of extensions in
 
4
:file:`conf.py`.
 
5
 
 
6
*Warning*: this code is currently untested.  ***MAY NOT WORK***
 
7
 
 
8
Note:
 
9
 
 
10
Current SVN versions of Sphinx now include built-in support for math.
 
11
There are two flavors:
 
12
 
 
13
  - pngmath: uses dvipng to render the equation
 
14
 
 
15
  - jsmath: renders the math in the browser using Javascript
 
16
 
 
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
 
19
:file:`conf.py`.
 
20
 
 
21
All three of these options for math are designed to behave in the same
 
22
way.
 
23
"""
 
24
 
 
25
import os
 
26
try:
 
27
    from hashlib import md5
 
28
except ImportError:
 
29
    from md5 import md5
 
30
 
 
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
 
35
import warnings
 
36
 
 
37
# Constants
 
38
STATIC_DIR='_static'
 
39
 
 
40
# Define LaTeX math node:
 
41
class latex_math(nodes.General, nodes.Element):
 
42
    pass
 
43
 
 
44
def fontset_choice(arg):
 
45
    return directives.choice(arg, ['cm', 'stix', 'stixsans'])
 
46
 
 
47
options_spec = {'fontset': fontset_choice}
 
48
 
 
49
def math_role(role, rawtext, text, lineno, inliner,
 
50
              options={}, content=[]):
 
51
    i = rawtext.find('`')
 
52
    latex = rawtext[i+1:-1]
 
53
    node = latex_math(rawtext)
 
54
    node['latex'] = latex
 
55
    node['fontset'] = options.get('fontset', 'cm')
 
56
    return [node], []
 
57
math_role.options = options_spec
 
58
 
 
59
def math_directive_run(content, block_text, options):
 
60
    latex = ''.join(content)
 
61
    node = latex_math(block_text)
 
62
    node['latex'] = latex
 
63
    node['fontset'] = options.get('fontset', 'cm')
 
64
    return [node]
 
65
 
 
66
try:
 
67
    from docutils.parsers.rst import Directive
 
68
except ImportError:
 
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
 
78
else:
 
79
    class math_directive(Directive):
 
80
        has_content = True
 
81
        option_spec = options_spec
 
82
 
 
83
        def run(self):
 
84
            return math_directive_run(self.content, self.block_text,
 
85
                                      self.options)
 
86
    from docutils.parsers.rst import directives
 
87
    directives.register_directive('math', math_directive)
 
88
 
 
89
def setup(app):
 
90
    app.add_node(latex_math)
 
91
    app.add_role('math', math_role)
 
92
 
 
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):
 
98
            pass
 
99
    HTMLTranslator.visit_latex_math = visit_latex_math_html
 
100
    HTMLTranslator.depart_latex_math = depart_latex_math_html
 
101
 
 
102
    # Add visit/depart methods to LaTeX-Translator:
 
103
    def visit_latex_math_latex(self, node):
 
104
        inline = isinstance(node.parent, nodes.TextElement)
 
105
        if inline:
 
106
            self.body.append('$%s$' % node['latex'])
 
107
        else:
 
108
            self.body.extend(['\\begin{equation}',
 
109
                              node['latex'],
 
110
                              '\\end{equation}'])
 
111
    def depart_latex_math_latex(self, node):
 
112
            pass
 
113
    LaTeXTranslator.visit_latex_math = visit_latex_math_latex
 
114
    LaTeXTranslator.depart_latex_math = depart_latex_math_latex
 
115
 
 
116
from matplotlib import rcParams
 
117
from matplotlib.mathtext import MathTextParser
 
118
rcParams['mathtext.fontset'] = 'cm'
 
119
mathtext_parser = MathTextParser("Bitmap")
 
120
 
 
121
 
 
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)
 
129
    else:
 
130
        print latex.encode("ascii", "backslashreplace")
 
131
        try:
 
132
            depth = mathtext_parser.to_png(filename, latex, dpi=100)
 
133
        except:
 
134
            warnings.warn("Could not render math expression %s" % latex,
 
135
                          Warning)
 
136
            depth = 0
 
137
    rcParams['mathtext.fontset'] = orig_fontset
 
138
    return depth
 
139
 
 
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")
 
146
 
 
147
    depth = latex2png(latex, dest, node.get('fontset',
 
148
                                            rcParams['mathtext.fontset']))
 
149
 
 
150
    path = STATIC_DIR
 
151
    count = source.split('/doc/')[-1].count('/')
 
152
    for i in range(count):
 
153
        if os.path.exists(path): break
 
154
        path = '../'+path
 
155
    path = '../'+path #specifically added for matplotlib
 
156
    if inline:
 
157
        cls = ''
 
158
    else:
 
159
        cls = 'class="center" '
 
160
    if inline and depth != 0:
 
161
        style = 'style="position: relative; bottom: -%dpx"' % (depth + 1)
 
162
    else:
 
163
        style = ''
 
164
 
 
165
    return '<img src="%s/%s.png" %s%s/>' % (path, name, cls, style)
 
166