1
+ r"""
2
+ A role and directive to display mathtext in Sphinx
3
+ ==================================================
4
+
5
+ .. warning::
6
+ In most cases, you will likely want to use one of `Sphinx's builtin Math
7
+ extensions
8
+ <https://www.sphinx-doc.org/en/master/usage/extensions/math.html>`__
9
+ instead of this one.
10
+
11
+ Mathtext may be included in two ways:
12
+
13
+ 1. Inline, using the role::
14
+
15
+ This text uses inline math: :mathmpl:`\alpha > \beta`.
16
+
17
+ which produces:
18
+
19
+ This text uses inline math: :mathmpl:`\alpha > \beta`.
20
+
21
+ 2. Standalone, using the directive::
22
+
23
+ Here is some standalone math:
24
+
25
+ .. mathmpl::
26
+
27
+ \alpha > \beta
28
+
29
+ which produces:
30
+
31
+ Here is some standalone math:
32
+
33
+ .. mathmpl::
34
+
35
+ \alpha > \beta
36
+
37
+ Options
38
+ -------
39
+
40
+ The ``mathmpl`` role and directive both support the following options:
41
+
42
+ fontset : str, default: 'cm'
43
+ The font set to use when displaying math. See :rc:`mathtext.fontset`.
44
+
45
+ fontsize : float
46
+ The font size, in points. Defaults to the value from the extension
47
+ configuration option defined below.
48
+
49
+ Configuration options
50
+ ---------------------
51
+
52
+ The mathtext extension has the following configuration options:
53
+
54
+ mathmpl_fontsize : float, default: 10.0
55
+ Default font size, in points.
56
+
57
+ mathmpl_srcset : list of str, default: []
58
+ Additional image sizes to generate when embedding in HTML, to support
59
+ `responsive resolution images
60
+ <https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images>`__.
61
+ The list should contain additional x-descriptors (``'1.5x'``, ``'2x'``,
62
+ etc.) to generate (1x is the default and always included.)
63
+
64
+ """
65
+
1
66
import hashlib
2
67
from pathlib import Path
3
68
4
69
from docutils import nodes
5
70
from docutils .parsers .rst import Directive , directives
6
71
import sphinx
72
+ from sphinx .errors import ConfigError , ExtensionError
7
73
8
74
import matplotlib as mpl
9
75
from matplotlib import _api , mathtext
76
+ from matplotlib .rcsetup import validate_float_or_None
10
77
11
78
12
79
# Define LaTeX math node:
@@ -25,32 +92,40 @@ def math_role(role, rawtext, text, lineno, inliner,
25
92
node = latex_math (rawtext )
26
93
node ['latex' ] = latex
27
94
node ['fontset' ] = options .get ('fontset' , 'cm' )
95
+ node ['fontsize' ] = options .get ('fontsize' ,
96
+ setup .app .config .mathmpl_fontsize )
28
97
return [node ], []
29
- math_role .options = {'fontset' : fontset_choice }
98
+ math_role .options = {'fontset' : fontset_choice ,
99
+ 'fontsize' : validate_float_or_None }
30
100
31
101
32
102
class MathDirective (Directive ):
103
+ """
104
+ The ``.. mathmpl::`` directive, as documented in the module's docstring.
105
+ """
33
106
has_content = True
34
107
required_arguments = 0
35
108
optional_arguments = 0
36
109
final_argument_whitespace = False
37
- option_spec = {'fontset' : fontset_choice }
110
+ option_spec = {'fontset' : fontset_choice ,
111
+ 'fontsize' : validate_float_or_None }
38
112
39
113
def run (self ):
40
114
latex = '' .join (self .content )
41
115
node = latex_math (self .block_text )
42
116
node ['latex' ] = latex
43
117
node ['fontset' ] = self .options .get ('fontset' , 'cm' )
118
+ node ['fontsize' ] = self .options .get ('fontsize' ,
119
+ setup .app .config .mathmpl_fontsize )
44
120
return [node ]
45
121
46
122
47
123
# This uses mathtext to render the expression
48
- def latex2png (latex , filename , fontset = 'cm' ):
49
- latex = "$%s$" % latex
50
- with mpl .rc_context ({'mathtext.fontset' : fontset }):
124
+ def latex2png (latex , filename , fontset = 'cm' , fontsize = 10 , dpi = 100 ):
125
+ with mpl .rc_context ({'mathtext.fontset' : fontset , 'font.size' : fontsize }):
51
126
try :
52
127
depth = mathtext .math_to_image (
53
- latex , filename , dpi = 100 , format = "png" )
128
+ f"$ { latex } $" , filename , dpi = dpi , format = "png" )
54
129
except Exception :
55
130
_api .warn_external (f"Could not render math expression { latex } " )
56
131
depth = 0
@@ -62,14 +137,26 @@ def latex2html(node, source):
62
137
inline = isinstance (node .parent , nodes .TextElement )
63
138
latex = node ['latex' ]
64
139
fontset = node ['fontset' ]
140
+ fontsize = node ['fontsize' ]
65
141
name = 'math-{}' .format (
66
- hashlib .md5 (( latex + fontset ) .encode ()).hexdigest ()[- 10 :])
142
+ hashlib .md5 (f' { latex } { fontset } { fontsize } ' .encode ()).hexdigest ()[- 10 :])
67
143
68
144
destdir = Path (setup .app .builder .outdir , '_images' , 'mathmpl' )
69
145
destdir .mkdir (parents = True , exist_ok = True )
70
- dest = destdir / f'{ name } .png'
71
146
72
- depth = latex2png (latex , dest , fontset )
147
+ dest = destdir / f'{ name } .png'
148
+ depth = latex2png (latex , dest , fontset , fontsize = fontsize )
149
+
150
+ srcset = []
151
+ for size in setup .app .config .mathmpl_srcset :
152
+ filename = f'{ name } -{ size .replace ("." , "_" )} .png'
153
+ latex2png (latex , destdir / filename , fontset , fontsize = fontsize ,
154
+ dpi = 100 * float (size [:- 1 ]))
155
+ srcset .append (
156
+ f'{ setup .app .builder .imgpath } /mathmpl/{ filename } { size } ' )
157
+ if srcset :
158
+ srcset = (f'srcset="{ setup .app .builder .imgpath } /mathmpl/{ name } .png, ' +
159
+ ', ' .join (srcset ) + '" ' )
73
160
74
161
if inline :
75
162
cls = ''
@@ -81,11 +168,35 @@ def latex2html(node, source):
81
168
style = ''
82
169
83
170
return (f'<img src="{ setup .app .builder .imgpath } /mathmpl/{ name } .png"'
84
- f' { cls } { style } />' )
171
+ f' { srcset } { cls } { style } />' )
172
+
173
+
174
+ def _config_inited (app , config ):
175
+ # Check for srcset hidpi images
176
+ for i , size in enumerate (app .config .mathmpl_srcset ):
177
+ if size [- 1 ] == 'x' : # "2x" = "2.0"
178
+ try :
179
+ float (size [:- 1 ])
180
+ except ValueError :
181
+ raise ConfigError (
182
+ f'Invalid value for mathmpl_srcset parameter: { size !r} . '
183
+ 'Must be a list of strings with the multiplicative '
184
+ 'factor followed by an "x". e.g. ["2.0x", "1.5x"]' )
185
+ else :
186
+ raise ConfigError (
187
+ f'Invalid value for mathmpl_srcset parameter: { size !r} . '
188
+ 'Must be a list of strings with the multiplicative '
189
+ 'factor followed by an "x". e.g. ["2.0x", "1.5x"]' )
85
190
86
191
87
192
def setup (app ):
88
193
setup .app = app
194
+ app .add_config_value ('mathmpl_fontsize' , 10.0 , True )
195
+ app .add_config_value ('mathmpl_srcset' , [], True )
196
+ try :
197
+ app .connect ('config-inited' , _config_inited ) # Sphinx 1.8+
198
+ except ExtensionError :
199
+ app .connect ('env-updated' , lambda app , env : _config_inited (app , None ))
89
200
90
201
# Add visit/depart methods to HTML-Translator:
91
202
def visit_latex_math_html (self , node ):
0 commit comments