diff --git a/examples/text_labels_and_annotations/mathtext_fontfamily_example.py b/examples/text_labels_and_annotations/mathtext_fontfamily_example.py new file mode 100644 index 000000000000..2b417cfcac91 --- /dev/null +++ b/examples/text_labels_and_annotations/mathtext_fontfamily_example.py @@ -0,0 +1,37 @@ +""" +=============== +Math fontfamily +=============== + +A simple example showcasing the new *math_fontfamily* parameter that can +be used to change the family of fonts for each individual text +element in a plot. + +If no parameter is set, the global value +:rc:`mathtext.fontset` will be used. +""" + +import matplotlib.pyplot as plt + +plt.figure(figsize=(6, 5)) + +# A simple plot for the background. +plt.plot(range(11), color="0.9") + +# A text mixing normal text and math text. +msg = (r"Normal Text. $Text\ in\ math\ mode:\ " + r"\int_{0}^{\infty } x^2 dx$") + +# Set the text in the plot. +plt.text(1, 7, msg, size=12, math_fontfamily='cm') + +# Set another font for the next text. +plt.text(1, 3, msg, size=12, math_fontfamily='dejavuserif') + +# *math_fontfamily* can be used in most places where there is text, +# like in the title: +plt.title(r"$Title\ in\ math\ mode:\ \int_{0}^{\infty } x^2 dx$", + math_fontfamily='stixsans', size=14) + +# Note that the normal text is not changed by *math_fontfamily*. +plt.show() diff --git a/lib/matplotlib/font_manager.py b/lib/matplotlib/font_manager.py index 5952b8bd190c..5769d6d33fe0 100644 --- a/lib/matplotlib/font_manager.py +++ b/lib/matplotlib/font_manager.py @@ -41,6 +41,7 @@ from matplotlib import afm, cbook, ft2font, rcParams from matplotlib.fontconfig_pattern import ( parse_fontconfig_pattern, generate_fontconfig_pattern) +from matplotlib.rcsetup import _validators _log = logging.getLogger(__name__) @@ -624,10 +625,10 @@ class FontProperties: """ A class for storing and manipulating font properties. - The font properties are those described in the `W3C Cascading - Style Sheet, Level 1 + The font properties are the six properties described in the + `W3C Cascading Style Sheet, Level 1 `_ font - specification. The six properties are: + specification and *math_fontfamily* for math fonts: - family: A list of font names in decreasing order of priority. The items may include a generic font family name, either @@ -653,6 +654,12 @@ class FontProperties: 'small', 'medium', 'large', 'x-large', 'xx-large' or an absolute font size, e.g., 12. + - math_fontfamily: The family of fonts used to render math text; overrides + :rc:`mathtext.fontset`. Supported values are the same as the ones + supported by :rc:`mathtext.fontset` :: + + 'dejavusans', 'dejavuserif', 'cm', 'stix', 'stixsans' and 'custom'. + The default font property for TrueType fonts (as specified in the default rcParams) is :: @@ -690,6 +697,7 @@ def __init__(self, stretch= None, size = None, fname = None, # if set, it's a hardcoded filename to use + math_fontfamily = None, ): self._family = _normalize_font_family(rcParams['font.family']) self._slant = rcParams['font.style'] @@ -698,6 +706,7 @@ def __init__(self, self._stretch = rcParams['font.stretch'] self._size = rcParams['font.size'] self._file = None + self._math_fontfamily = None if isinstance(family, str): # Treat family as a fontconfig pattern if it is the only @@ -714,6 +723,7 @@ def __init__(self, self.set_stretch(stretch) self.set_file(fname) self.set_size(size) + self.set_math_fontfamily(math_fontfamily) @classmethod def _from_any(cls, arg): @@ -745,7 +755,8 @@ def __hash__(self): self.get_weight(), self.get_stretch(), self.get_size_in_points(), - self.get_file()) + self.get_file(), + self.get_math_fontfamily()) return hash(l) def __eq__(self, other): @@ -934,6 +945,45 @@ def set_fontconfig_pattern(self, pattern): else: getattr(self, "set_" + key)(val) + def get_math_fontfamily(self): + """ + Return the name of the font family used for math text. + + The default font is :rc:`mathtext.fontset`. + """ + if self._math_fontfamily is None: + return rcParams['mathtext.fontset'] + return self._math_fontfamily + + def set_math_fontfamily(self, fontfamily): + """ + Set the font family for text in math mode. + + If not set explicitly, :rc:`mathtext.fontset` will be used. + + Parameters + ---------- + fontfamily : str + The name of the font family. + + Available font families are defined in the + matplotlibrc.template file + :ref:`here ` + + See Also + -------- + .text.Text.get_math_fontfamily + """ + if fontfamily is None: + self._math_fontfamily = None + return + + valid_fonts = _validators['mathtext.fontset'].valid.values() + # _check_in_list() Validates the parameter math_fontfamily as + # if it were passed to rcParams['mathtext.fontset'] + cbook._check_in_list(valid_fonts, math_fontfamily=fontfamily) + self._math_fontfamily = fontfamily + def copy(self): """Return a copy of self.""" new = type(self)() diff --git a/lib/matplotlib/mathtext.py b/lib/matplotlib/mathtext.py index 8641a4416f41..8b9df7e19203 100644 --- a/lib/matplotlib/mathtext.py +++ b/lib/matplotlib/mathtext.py @@ -3351,12 +3351,10 @@ def parse(self, s, dpi=72, prop=None, *, _force_standard_ps_fonts=False): # lru_cache can't decorate parse() directly because the ps.useafm and # mathtext.fontset rcParams also affect the parse (e.g. by affecting # the glyph metrics). - return self._parse_cached(s, dpi, prop, _force_standard_ps_fonts, - rcParams['mathtext.fontset']) + return self._parse_cached(s, dpi, prop, _force_standard_ps_fonts) @functools.lru_cache(50) - def _parse_cached(self, s, dpi, prop, force_standard_ps_fonts, fontset): - + def _parse_cached(self, s, dpi, prop, force_standard_ps_fonts): if prop is None: prop = FontProperties() @@ -3365,7 +3363,8 @@ def _parse_cached(self, s, dpi, prop, force_standard_ps_fonts, fontset): else: backend = self._backend_mapping[self._output]() fontset_class = cbook._check_getitem( - self._font_type_mapping, fontset=fontset) + self._font_type_mapping, + fontset=prop.get_math_fontfamily()) font_output = fontset_class(prop, backend) fontsize = prop.get_size_in_points() diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/math_fontfamily_image.png b/lib/matplotlib/tests/baseline_images/test_mathtext/math_fontfamily_image.png new file mode 100644 index 000000000000..feb34b1793db Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_mathtext/math_fontfamily_image.png differ diff --git a/lib/matplotlib/tests/test_mathtext.py b/lib/matplotlib/tests/test_mathtext.py index 285841f7277b..1b4d3336f19e 100644 --- a/lib/matplotlib/tests/test_mathtext.py +++ b/lib/matplotlib/tests/test_mathtext.py @@ -364,3 +364,13 @@ def test_mathtext_to_png(tmpdir): mt = mathtext.MathTextParser('bitmap') mt.to_png(str(tmpdir.join('example.png')), '$x^2$') mt.to_png(io.BytesIO(), '$x^2$') + + +@image_comparison(baseline_images=['math_fontfamily_image.png'], + savefig_kwarg={'dpi': 40}) +def test_math_fontfamily(): + fig = plt.figure(figsize=(10, 3)) + fig.text(0.2, 0.7, r"$This\ text\ should\ have\ one\ font$", + size=24, math_fontfamily='dejavusans') + fig.text(0.2, 0.3, r"$This\ text\ should\ have\ another$", + size=24, math_fontfamily='stix') diff --git a/lib/matplotlib/text.py b/lib/matplotlib/text.py index 75ffafe1a0b3..a1d475c1b948 100644 --- a/lib/matplotlib/text.py +++ b/lib/matplotlib/text.py @@ -1061,6 +1061,40 @@ def set_fontsize(self, fontsize): self._fontproperties.set_size(fontsize) self.stale = True + def get_math_fontfamily(self): + """ + Return the font family name for math text rendered by Matplotlib. + + The default value is :rc:`mathtext.fontset`. + + See Also + -------- + set_math_fontfamily + """ + return self._fontproperties.get_math_fontfamily() + + def set_math_fontfamily(self, fontfamily): + """ + Set the font family for math text rendered by Matplotlib. + + This does only affect Matplotlib's own math renderer. It has no effect + when rendering with TeX (``usetex=True``). + + Parameters + ---------- + fontfamily : str + The name of the font family. + + Available font families are defined in the + :ref:`matplotlibrc.template file + `. + + See Also + -------- + get_math_fontfamily + """ + self._fontproperties.set_math_fontfamily(fontfamily) + def set_fontweight(self, weight): """ Set the font weight.