diff --git a/lib/matplotlib/_mathtext.py b/lib/matplotlib/_mathtext.py index 319d6f065389..62473c1d1f00 100644 --- a/lib/matplotlib/_mathtext.py +++ b/lib/matplotlib/_mathtext.py @@ -364,6 +364,12 @@ def __init__(self, default_font_prop: FontProperties, load_glyph_flags: LoadFlag self._fonts['default'] = default_font self._fonts['regular'] = default_font + def _normalize_font_pattern(self, pattern: str) -> str: + for option in ['stretch', 'style', 'variant', 'weight']: + if f':{option}=' not in pattern: + pattern = f'{pattern}:{option}=normal' + return pattern + def _get_font(self, font: str) -> FT2Font: basename = self.fontmap.get(font, font) cached_font = self._fonts.get(basename) @@ -492,7 +498,7 @@ def __init__(self, default_font_prop: FontProperties, load_glyph_flags: LoadFlag super().__init__(default_font_prop, load_glyph_flags) for key, val in self._fontmap.items(): - fullpath = findfont(val) + fullpath = findfont(self._normalize_font_pattern(val)) self.fontmap[key] = fullpath self.fontmap[val] = fullpath @@ -617,7 +623,7 @@ def __init__(self, default_font_prop: FontProperties, load_glyph_flags: LoadFlag } for size, name in stixsizedaltfonts.items(): - fullpath = findfont(name) + fullpath = findfont(self._normalize_font_pattern(name)) self.fontmap[size] = fullpath self.fontmap[name] = fullpath @@ -719,7 +725,7 @@ def __init__(self, default_font_prop: FontProperties, load_glyph_flags: LoadFlag '5': 'STIXSizeFiveSym', }) for key, name in self._fontmap.items(): - fullpath = findfont(name) + fullpath = findfont(self._normalize_font_pattern(name)) self.fontmap[key] = fullpath self.fontmap[name] = fullpath @@ -748,9 +754,9 @@ class DejaVuSerifFonts(DejaVuFonts): """ _fontmap = { 'rm': 'DejaVu Serif', - 'it': 'DejaVu Serif:italic', + 'it': 'DejaVu Serif:style=italic', 'bf': 'DejaVu Serif:weight=bold', - 'bfit': 'DejaVu Serif:italic:bold', + 'bfit': 'DejaVu Serif:style=italic:weight=bold', 'sf': 'DejaVu Sans', 'tt': 'DejaVu Sans Mono', 'ex': 'DejaVu Serif Display', @@ -769,9 +775,9 @@ class DejaVuSansFonts(DejaVuFonts): """ _fontmap = { 'rm': 'DejaVu Sans', - 'it': 'DejaVu Sans:italic', + 'it': 'DejaVu Sans:style=italic', 'bf': 'DejaVu Sans:weight=bold', - 'bfit': 'DejaVu Sans:italic:bold', + 'bfit': 'DejaVu Sans:style=italic:weight=bold', 'sf': 'DejaVu Sans', 'tt': 'DejaVu Sans Mono', 'ex': 'DejaVu Sans Display', @@ -796,11 +802,11 @@ class StixFonts(UnicodeFonts): """ _fontmap = { 'rm': 'STIXGeneral', - 'it': 'STIXGeneral:italic', + 'it': 'STIXGeneral:style=italic', 'bf': 'STIXGeneral:weight=bold', - 'bfit': 'STIXGeneral:italic:bold', + 'bfit': 'STIXGeneral:style=italic:weight=bold', 'nonunirm': 'STIXNonUnicode', - 'nonuniit': 'STIXNonUnicode:italic', + 'nonuniit': 'STIXNonUnicode:style=italic', 'nonunibf': 'STIXNonUnicode:weight=bold', '0': 'STIXGeneral', '1': 'STIXSizeOneSym', @@ -815,7 +821,7 @@ class StixFonts(UnicodeFonts): def __init__(self, default_font_prop: FontProperties, load_glyph_flags: LoadFlags): TruetypeFonts.__init__(self, default_font_prop, load_glyph_flags) for key, name in self._fontmap.items(): - fullpath = findfont(name) + fullpath = findfont(self._normalize_font_pattern(name)) self.fontmap[key] = fullpath self.fontmap[name] = fullpath diff --git a/lib/matplotlib/tests/test_mathtext.py b/lib/matplotlib/tests/test_mathtext.py index 1e1bf793cdae..4c6169f20083 100644 --- a/lib/matplotlib/tests/test_mathtext.py +++ b/lib/matplotlib/tests/test_mathtext.py @@ -1,6 +1,7 @@ from __future__ import annotations import io +import logging from pathlib import Path import platform import re @@ -292,14 +293,6 @@ def test_short_long_accents(fig_test, fig_ref): 0, .5, "$" + "".join(fr"\{l} a" for l in corresponding_long_accs) + "$") -def test_fontinfo(): - fontpath = mpl.font_manager.findfont("DejaVu Sans") - font = mpl.ft2font.FT2Font(fontpath) - table = font.get_sfnt_table("head") - assert table is not None - assert table['version'] == (1, 0) - - # See gh-26152 for more context on this xfail @pytest.mark.xfail(pyparsing_version.release == (3, 1, 0), reason="Error messages are incorrect for this version") @@ -379,6 +372,35 @@ def test_mathtext_exceptions(math, msg): parser.parse(math) +def test_mathtext_fonts(caplog): + # Changing these `font.*` rcParams should not affect mathtext, as that uses bundled + # fonts with a specific mapping to style/weight. TODO: font.stretch and font.variant + # are difficult to test as we don't have anything that might accidentally be + # substituted there. + mpl.rcParams['font.style'] = 'italic' + mpl.rcParams['font.weight'] = 100 + # Use explicitly available default so that it doesn't warn. + default_font = fm.FontProperties('DejaVu Sans:style=normal:weight=normal') + + _mathtext.BakomaFonts(default_font, LoadFlags.DEFAULT) + dejavusans = _mathtext.DejaVuSansFonts(default_font, LoadFlags.DEFAULT) + dejavuserif = _mathtext.DejaVuSerifFonts(default_font, LoadFlags.DEFAULT) + stix = _mathtext.StixFonts(default_font, LoadFlags.DEFAULT) + stixsans = _mathtext.StixSansFonts(default_font, LoadFlags.DEFAULT) + + # Setting font.style should not have accidentally changed any roman font to italic. + assert dejavusans.fontmap['rm'].endswith('DejaVuSans.ttf') + assert dejavuserif.fontmap['rm'].endswith('DejaVuSerif.ttf') + assert stix.fontmap['rm'].endswith('STIXGeneral.ttf') + assert stixsans.fontmap['rm'].endswith('STIXGeneral.ttf') + # No bakoma font is actually italic, so that setting won't affect it. + + # If `font.weight` leaked into the mathtext setup, then it should cause a warning to + # be logged from the font manager for a missing font weight. + records = [record for record in caplog.records if record.levelno == logging.WARNING] + assert records == [] + + def test_get_unicode_index_exception(): with pytest.raises(ValueError): _mathtext.get_unicode_index(r'\foo')