Skip to content

Add HiDPI-related config for mathmpl #20659

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Aug 1, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/api/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ Alphabetical list of modules:
rcsetup_api.rst
sankey_api.rst
scale_api.rst
sphinxext_mathmpl_api.rst
sphinxext_plot_directive_api.rst
spines_api.rst
style_api.rst
Expand Down
7 changes: 7 additions & 0 deletions doc/api/sphinxext_mathmpl_api.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
================================
``matplotlib.sphinxext.mathmpl``
================================

.. automodule:: matplotlib.sphinxext.mathmpl
:exclude-members: latex_math
:no-undoc-members:
2 changes: 2 additions & 0 deletions doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,8 @@ def _check_dependencies():
}

plot_gallery = 'True'
mathmpl_fontsize = 11.0
mathmpl_srcset = ['2x']

# Monkey-patching gallery signature to include search keywords
gen_rst.SPHX_GLR_SIG = """\n
Expand Down
13 changes: 13 additions & 0 deletions doc/users/next_whats_new/mathmpl_hidpi.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
More configuration of ``mathmpl:`` sphinx extension
---------------------------------------------------

The `matplotlib.sphinxext.mathmpl` sphinx extension supports two new
configuration options that may be specified in your ``conf.py``:

- ``mathmpl_fontsize`` (float), which sets the font size of the math text in
points;
- ``mathmpl_srcset`` (list of str), which provides a list of sizes to support
`responsive resolution images
<https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images>`__
The list should contain additional x-descriptors (``'1.5x'``, ``'2x'``, etc.)
to generate (1x is the default and always included.)
131 changes: 121 additions & 10 deletions lib/matplotlib/sphinxext/mathmpl.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,79 @@
r"""
A role and directive to display mathtext in Sphinx
==================================================

.. warning::
In most cases, you will likely want to use one of `Sphinx's builtin Math
extensions
<https://www.sphinx-doc.org/en/master/usage/extensions/math.html>`__
instead of this one.

Mathtext may be included in two ways:

1. Inline, using the role::

This text uses inline math: :mathmpl:`\alpha > \beta`.

which produces:

This text uses inline math: :mathmpl:`\alpha > \beta`.

2. Standalone, using the directive::

Here is some standalone math:

.. mathmpl::

\alpha > \beta

which produces:

Here is some standalone math:

.. mathmpl::

\alpha > \beta

Options
-------

The ``mathmpl`` role and directive both support the following options:

fontset : str, default: 'cm'
The font set to use when displaying math. See :rc:`mathtext.fontset`.

fontsize : float
The font size, in points. Defaults to the value from the extension
configuration option defined below.

Configuration options
---------------------

The mathtext extension has the following configuration options:

mathmpl_fontsize : float, default: 10.0
Default font size, in points.

mathmpl_srcset : list of str, default: []
Additional image sizes to generate when embedding in HTML, to support
`responsive resolution images
<https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images>`__.
The list should contain additional x-descriptors (``'1.5x'``, ``'2x'``,
etc.) to generate (1x is the default and always included.)

"""

import hashlib
from pathlib import Path

from docutils import nodes
from docutils.parsers.rst import Directive, directives
import sphinx
from sphinx.errors import ConfigError, ExtensionError

import matplotlib as mpl
from matplotlib import _api, mathtext
from matplotlib.rcsetup import validate_float_or_None


# Define LaTeX math node:
Expand All @@ -25,32 +92,40 @@ def math_role(role, rawtext, text, lineno, inliner,
node = latex_math(rawtext)
node['latex'] = latex
node['fontset'] = options.get('fontset', 'cm')
node['fontsize'] = options.get('fontsize',
setup.app.config.mathmpl_fontsize)
return [node], []
math_role.options = {'fontset': fontset_choice}
math_role.options = {'fontset': fontset_choice,
'fontsize': validate_float_or_None}


class MathDirective(Directive):
"""
The ``.. mathmpl::`` directive, as documented in the module's docstring.
"""
has_content = True
required_arguments = 0
optional_arguments = 0
final_argument_whitespace = False
option_spec = {'fontset': fontset_choice}
option_spec = {'fontset': fontset_choice,
'fontsize': validate_float_or_None}

def run(self):
latex = ''.join(self.content)
node = latex_math(self.block_text)
node['latex'] = latex
node['fontset'] = self.options.get('fontset', 'cm')
node['fontsize'] = self.options.get('fontsize',
setup.app.config.mathmpl_fontsize)
return [node]


# This uses mathtext to render the expression
def latex2png(latex, filename, fontset='cm'):
latex = "$%s$" % latex
with mpl.rc_context({'mathtext.fontset': fontset}):
def latex2png(latex, filename, fontset='cm', fontsize=10, dpi=100):
with mpl.rc_context({'mathtext.fontset': fontset, 'font.size': fontsize}):
try:
depth = mathtext.math_to_image(
latex, filename, dpi=100, format="png")
f"${latex}$", filename, dpi=dpi, format="png")
except Exception:
_api.warn_external(f"Could not render math expression {latex}")
depth = 0
Expand All @@ -62,14 +137,26 @@ def latex2html(node, source):
inline = isinstance(node.parent, nodes.TextElement)
latex = node['latex']
fontset = node['fontset']
fontsize = node['fontsize']
name = 'math-{}'.format(
hashlib.md5((latex + fontset).encode()).hexdigest()[-10:])
hashlib.md5(f'{latex}{fontset}{fontsize}'.encode()).hexdigest()[-10:])

destdir = Path(setup.app.builder.outdir, '_images', 'mathmpl')
destdir.mkdir(parents=True, exist_ok=True)
dest = destdir / f'{name}.png'

depth = latex2png(latex, dest, fontset)
dest = destdir / f'{name}.png'
depth = latex2png(latex, dest, fontset, fontsize=fontsize)

srcset = []
for size in setup.app.config.mathmpl_srcset:
filename = f'{name}-{size.replace(".", "_")}.png'
latex2png(latex, destdir / filename, fontset, fontsize=fontsize,
dpi=100 * float(size[:-1]))
srcset.append(
f'{setup.app.builder.imgpath}/mathmpl/{filename} {size}')
if srcset:
srcset = (f'srcset="{setup.app.builder.imgpath}/mathmpl/{name}.png, ' +
', '.join(srcset) + '" ')

if inline:
cls = ''
Expand All @@ -81,11 +168,35 @@ def latex2html(node, source):
style = ''

return (f'<img src="{setup.app.builder.imgpath}/mathmpl/{name}.png"'
f' {cls}{style}/>')
f' {srcset}{cls}{style}/>')


def _config_inited(app, config):
# Check for srcset hidpi images
for i, size in enumerate(app.config.mathmpl_srcset):
if size[-1] == 'x': # "2x" = "2.0"
try:
float(size[:-1])
except ValueError:
raise ConfigError(
f'Invalid value for mathmpl_srcset parameter: {size!r}. '
'Must be a list of strings with the multiplicative '
'factor followed by an "x". e.g. ["2.0x", "1.5x"]')
else:
raise ConfigError(
f'Invalid value for mathmpl_srcset parameter: {size!r}. '
'Must be a list of strings with the multiplicative '
'factor followed by an "x". e.g. ["2.0x", "1.5x"]')


def setup(app):
setup.app = app
app.add_config_value('mathmpl_fontsize', 10.0, True)
app.add_config_value('mathmpl_srcset', [], True)
try:
app.connect('config-inited', _config_inited) # Sphinx 1.8+
except ExtensionError:
app.connect('env-updated', lambda app, env: _config_inited(app, None))

# Add visit/depart methods to HTML-Translator:
def visit_latex_math_html(self, node):
Expand Down