123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140 |
- r"""
- A module for parsing a subset of the TeX math syntax and rendering it to a
- Matplotlib backend.
- For a tutorial of its usage, see :ref:`mathtext`. This
- document is primarily concerned with implementation details.
- The module uses pyparsing_ to parse the TeX expression.
- .. _pyparsing: https://pypi.org/project/pyparsing/
- The Bakoma distribution of the TeX Computer Modern fonts, and STIX
- fonts are supported. There is experimental support for using
- arbitrary fonts, but results may vary without proper tweaking and
- metrics for those fonts.
- """
- import functools
- import logging
- import matplotlib as mpl
- from matplotlib import _api, _mathtext
- from matplotlib.ft2font import LOAD_NO_HINTING
- from matplotlib.font_manager import FontProperties
- from ._mathtext import ( # noqa: reexported API
- RasterParse, VectorParse, get_unicode_index)
- _log = logging.getLogger(__name__)
- get_unicode_index.__module__ = __name__
- ##############################################################################
- # MAIN
- class MathTextParser:
- _parser = None
- _font_type_mapping = {
- 'cm': _mathtext.BakomaFonts,
- 'dejavuserif': _mathtext.DejaVuSerifFonts,
- 'dejavusans': _mathtext.DejaVuSansFonts,
- 'stix': _mathtext.StixFonts,
- 'stixsans': _mathtext.StixSansFonts,
- 'custom': _mathtext.UnicodeFonts,
- }
- def __init__(self, output):
- """
- Create a MathTextParser for the given backend *output*.
- Parameters
- ----------
- output : {"path", "agg"}
- Whether to return a `VectorParse` ("path") or a
- `RasterParse` ("agg", or its synonym "macosx").
- """
- self._output_type = _api.check_getitem(
- {"path": "vector", "agg": "raster", "macosx": "raster"},
- output=output.lower())
- def parse(self, s, dpi=72, prop=None, *, antialiased=None):
- """
- Parse the given math expression *s* at the given *dpi*. If *prop* is
- provided, it is a `.FontProperties` object specifying the "default"
- font to use in the math expression, used for all non-math text.
- The results are cached, so multiple calls to `parse`
- with the same expression should be fast.
- Depending on the *output* type, this returns either a `VectorParse` or
- a `RasterParse`.
- """
- # lru_cache can't decorate parse() directly because prop
- # is mutable; key the cache using an internal copy (see
- # text._get_text_metrics_with_cache for a similar case).
- prop = prop.copy() if prop is not None else None
- antialiased = mpl._val_or_rc(antialiased, 'text.antialiased')
- return self._parse_cached(s, dpi, prop, antialiased)
- @functools.lru_cache(50)
- def _parse_cached(self, s, dpi, prop, antialiased):
- from matplotlib.backends import backend_agg
- if prop is None:
- prop = FontProperties()
- fontset_class = _api.check_getitem(
- self._font_type_mapping, fontset=prop.get_math_fontfamily())
- load_glyph_flags = {
- "vector": LOAD_NO_HINTING,
- "raster": backend_agg.get_hinting_flag(),
- }[self._output_type]
- fontset = fontset_class(prop, load_glyph_flags)
- fontsize = prop.get_size_in_points()
- if self._parser is None: # Cache the parser globally.
- self.__class__._parser = _mathtext.Parser()
- box = self._parser.parse(s, fontset, fontsize, dpi)
- output = _mathtext.ship(box)
- if self._output_type == "vector":
- return output.to_vector()
- elif self._output_type == "raster":
- return output.to_raster(antialiased=antialiased)
- def math_to_image(s, filename_or_obj, prop=None, dpi=None, format=None,
- *, color=None):
- """
- Given a math expression, renders it in a closely-clipped bounding
- box to an image file.
- Parameters
- ----------
- s : str
- A math expression. The math portion must be enclosed in dollar signs.
- filename_or_obj : str or path-like or file-like
- Where to write the image data.
- prop : `.FontProperties`, optional
- The size and style of the text.
- dpi : float, optional
- The output dpi. If not set, the dpi is determined as for
- `.Figure.savefig`.
- format : str, optional
- The output format, e.g., 'svg', 'pdf', 'ps' or 'png'. If not set, the
- format is determined as for `.Figure.savefig`.
- color : str, optional
- Foreground color, defaults to :rc:`text.color`.
- """
- from matplotlib import figure
- parser = MathTextParser('path')
- width, height, depth, _, _ = parser.parse(s, dpi=72, prop=prop)
- fig = figure.Figure(figsize=(width / 72.0, height / 72.0))
- fig.text(0, depth/height, s, fontproperties=prop, color=color)
- fig.savefig(filename_or_obj, dpi=dpi, format=format)
- return depth
|