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+
166import hashlib
267from pathlib import Path
368
469from docutils import nodes
570from docutils .parsers .rst import Directive , directives
671import sphinx
72+ from sphinx .errors import ConfigError , ExtensionError
773
874import matplotlib as mpl
975from matplotlib import _api , mathtext
76+ from matplotlib .rcsetup import validate_float_or_None
1077
1178
1279# Define LaTeX math node:
@@ -25,32 +92,40 @@ def math_role(role, rawtext, text, lineno, inliner,
2592 node = latex_math (rawtext )
2693 node ['latex' ] = latex
2794 node ['fontset' ] = options .get ('fontset' , 'cm' )
95+ node ['fontsize' ] = options .get ('fontsize' ,
96+ setup .app .config .mathmpl_fontsize )
2897 return [node ], []
29- math_role .options = {'fontset' : fontset_choice }
98+ math_role .options = {'fontset' : fontset_choice ,
99+ 'fontsize' : validate_float_or_None }
30100
31101
32102class MathDirective (Directive ):
103+ """
104+ The ``.. mathmpl::`` directive, as documented in the module's docstring.
105+ """
33106 has_content = True
34107 required_arguments = 0
35108 optional_arguments = 0
36109 final_argument_whitespace = False
37- option_spec = {'fontset' : fontset_choice }
110+ option_spec = {'fontset' : fontset_choice ,
111+ 'fontsize' : validate_float_or_None }
38112
39113 def run (self ):
40114 latex = '' .join (self .content )
41115 node = latex_math (self .block_text )
42116 node ['latex' ] = latex
43117 node ['fontset' ] = self .options .get ('fontset' , 'cm' )
118+ node ['fontsize' ] = self .options .get ('fontsize' ,
119+ setup .app .config .mathmpl_fontsize )
44120 return [node ]
45121
46122
47123# 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 }):
51126 try :
52127 depth = mathtext .math_to_image (
53- latex , filename , dpi = 100 , format = "png" )
128+ f"$ { latex } $" , filename , dpi = dpi , format = "png" )
54129 except Exception :
55130 _api .warn_external (f"Could not render math expression { latex } " )
56131 depth = 0
@@ -62,14 +137,26 @@ def latex2html(node, source):
62137 inline = isinstance (node .parent , nodes .TextElement )
63138 latex = node ['latex' ]
64139 fontset = node ['fontset' ]
140+ fontsize = node ['fontsize' ]
65141 name = 'math-{}' .format (
66- hashlib .md5 (( latex + fontset ) .encode ()).hexdigest ()[- 10 :])
142+ hashlib .md5 (f' { latex } { fontset } { fontsize } ' .encode ()).hexdigest ()[- 10 :])
67143
68144 destdir = Path (setup .app .builder .outdir , '_images' , 'mathmpl' )
69145 destdir .mkdir (parents = True , exist_ok = True )
70- dest = destdir / f'{ name } .png'
71146
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 ) + '" ' )
73160
74161 if inline :
75162 cls = ''
@@ -81,11 +168,35 @@ def latex2html(node, source):
81168 style = ''
82169
83170 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"]' )
85190
86191
87192def setup (app ):
88193 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 ))
89200
90201 # Add visit/depart methods to HTML-Translator:
91202 def visit_latex_math_html (self , node ):
0 commit comments