diff --git a/lib/matplotlib/_mathtext.py b/lib/matplotlib/_mathtext.py index b85cdffd6d88..d628b18aebf4 100644 --- a/lib/matplotlib/_mathtext.py +++ b/lib/matplotlib/_mathtext.py @@ -482,60 +482,40 @@ def _get_glyph(self, fontname: str, font_class: str, else: return self._stix_fallback._get_glyph(fontname, font_class, sym) - # The Bakoma fonts contain many pre-sized alternatives for the - # delimiters. The AutoSizedChar class will use these alternatives - # and select the best (closest sized) glyph. + # The Bakoma fonts contain many pre-sized alternatives for the delimiters. The + # Auto(Height|Width)Char classes will use these alternatives and select the best + # (closest sized) glyph. + _latex_sizes = ('big', 'Big', 'bigg', 'Bigg') _size_alternatives = { - '(': [('rm', '('), ('ex', '\xa1'), ('ex', '\xb3'), - ('ex', '\xb5'), ('ex', '\xc3')], - ')': [('rm', ')'), ('ex', '\xa2'), ('ex', '\xb4'), - ('ex', '\xb6'), ('ex', '\x21')], - '{': [('cal', '{'), ('ex', '\xa9'), ('ex', '\x6e'), - ('ex', '\xbd'), ('ex', '\x28')], - '}': [('cal', '}'), ('ex', '\xaa'), ('ex', '\x6f'), - ('ex', '\xbe'), ('ex', '\x29')], - # The fourth size of '[' is mysteriously missing from the BaKoMa - # font, so I've omitted it for both '[' and ']' - '[': [('rm', '['), ('ex', '\xa3'), ('ex', '\x68'), - ('ex', '\x22')], - ']': [('rm', ']'), ('ex', '\xa4'), ('ex', '\x69'), - ('ex', '\x23')], - r'\lfloor': [('ex', '\xa5'), ('ex', '\x6a'), - ('ex', '\xb9'), ('ex', '\x24')], - r'\rfloor': [('ex', '\xa6'), ('ex', '\x6b'), - ('ex', '\xba'), ('ex', '\x25')], - r'\lceil': [('ex', '\xa7'), ('ex', '\x6c'), - ('ex', '\xbb'), ('ex', '\x26')], - r'\rceil': [('ex', '\xa8'), ('ex', '\x6d'), - ('ex', '\xbc'), ('ex', '\x27')], - r'\langle': [('ex', '\xad'), ('ex', '\x44'), - ('ex', '\xbf'), ('ex', '\x2a')], - r'\rangle': [('ex', '\xae'), ('ex', '\x45'), - ('ex', '\xc0'), ('ex', '\x2b')], - r'\__sqrt__': [('ex', '\x70'), ('ex', '\x71'), - ('ex', '\x72'), ('ex', '\x73')], - r'\backslash': [('ex', '\xb2'), ('ex', '\x2f'), - ('ex', '\xc2'), ('ex', '\x2d')], - r'/': [('rm', '/'), ('ex', '\xb1'), ('ex', '\x2e'), - ('ex', '\xcb'), ('ex', '\x2c')], - r'\widehat': [('rm', '\x5e'), ('ex', '\x62'), ('ex', '\x63'), - ('ex', '\x64')], - r'\widetilde': [('rm', '\x7e'), ('ex', '\x65'), ('ex', '\x66'), - ('ex', '\x67')], - r'<': [('cal', 'h'), ('ex', 'D')], - r'>': [('cal', 'i'), ('ex', 'E')] - } + '(': [('rm', '('), *[('ex', fr'\__parenleft{s}__') for s in _latex_sizes]], + ')': [('rm', ')'), *[('ex', fr'\__parenright{s}__') for s in _latex_sizes]], + '{': [('ex', fr'\__braceleft{s}__') for s in _latex_sizes], + '}': [('ex', fr'\__braceright{s}__') for s in _latex_sizes], + '[': [('rm', '['), *[('ex', fr'\__bracketleft{s}__') for s in _latex_sizes]], + ']': [('rm', ']'), *[('ex', fr'\__bracketright{s}__') for s in _latex_sizes]], + '<': [('cal', r'\__angbracketleft__'), + *[('ex', fr'\__angbracketleft{s}__') for s in _latex_sizes]], + '>': [('cal', r'\__angbracketright__'), + *[('ex', fr'\__angbracketright{s}__') for s in _latex_sizes]], + r'\lfloor': [('ex', fr'\__floorleft{s}__') for s in _latex_sizes], + r'\rfloor': [('ex', fr'\__floorright{s}__') for s in _latex_sizes], + r'\lceil': [('ex', fr'\__ceilingleft{s}__') for s in _latex_sizes], + r'\rceil': [('ex', fr'\__ceilingright{s}__') for s in _latex_sizes], + r'\__sqrt__': [('ex', fr'\__radical{s}__') for s in _latex_sizes], + r'\backslash': [('ex', fr'\__backslash{s}__') for s in _latex_sizes], + r'/': [('rm', '/'), *[('ex', fr'\__slash{s}__') for s in _latex_sizes]], + r'\widehat': [('rm', '\x5e'), ('ex', r'\__hatwide__'), ('ex', r'\__hatwider__'), + ('ex', r'\__hatwidest__')], + r'\widetilde': [('rm', '\x7e'), ('ex', r'\__tildewide__'), + ('ex', r'\__tildewider__'), ('ex', r'\__tildewidest__')], + } - for alias, target in [(r'\leftparen', '('), - (r'\rightparen', ')'), - (r'\leftbrace', '{'), - (r'\rightbrace', '}'), - (r'\leftbracket', '['), - (r'\rightbracket', ']'), - (r'\{', '{'), - (r'\}', '}'), - (r'\[', '['), - (r'\]', ']')]: + for alias, target in [(r'\leftparen', '('), (r'\rightparen', ')'), + (r'\leftbrace', '{'), (r'\rightbrace', '}'), + (r'\leftbracket', '['), (r'\rightbracket', ']'), + (r'\langle', '<'), (r'\rangle', '>'), + (r'\{', '{'), (r'\}', '}'), + (r'\[', '['), (r'\]', ']')]: _size_alternatives[alias] = _size_alternatives[target] def get_sized_alternatives_for_symbol(self, fontname: str, @@ -1531,7 +1511,7 @@ class AutoHeightChar(Hlist): """ def __init__(self, c: str, height: float, depth: float, state: ParserState, - always: bool = False, factor: float | None = None): + factor: float | None = None): alternatives = state.fontset.get_sized_alternatives_for_symbol(state.font, c) x_height = state.fontset.get_xheight(state.font, state.fontsize, state.dpi) @@ -1568,7 +1548,7 @@ class AutoWidthChar(Hlist): always just return a scaled version of the glyph. """ - def __init__(self, c: str, width: float, state: ParserState, always: bool = False, + def __init__(self, c: str, width: float, state: ParserState, char_class: type[Char] = Char): alternatives = state.fontset.get_sized_alternatives_for_symbol(state.font, c) @@ -2706,7 +2686,7 @@ def sqrt(self, toks: ParseResults) -> T.Any: # the height so it doesn't seem cramped height = body.height - body.shift_amount + 5 * thickness depth = body.depth + body.shift_amount - check = AutoHeightChar(r'\__sqrt__', height, depth, state, always=True) + check = AutoHeightChar(r'\__sqrt__', height, depth, state) height = check.height - check.shift_amount depth = check.depth + check.shift_amount diff --git a/lib/matplotlib/_mathtext_data.py b/lib/matplotlib/_mathtext_data.py index 0451791e9f26..f8b7c9ac2c33 100644 --- a/lib/matplotlib/_mathtext_data.py +++ b/lib/matplotlib/_mathtext_data.py @@ -36,6 +36,75 @@ '{' : ('cmex10', 0xa9), '}' : ('cmex10', 0xaa), + '\\__angbracketleft__' : ('cmsy10', 0x68), + '\\__angbracketright__' : ('cmsy10', 0x69), + '\\__angbracketleftbig__' : ('cmex10', 0xad), + '\\__angbracketleftBig__' : ('cmex10', 0x44), + '\\__angbracketleftbigg__' : ('cmex10', 0xbf), + '\\__angbracketleftBigg__' : ('cmex10', 0x2a), + '\\__angbracketrightbig__' : ('cmex10', 0xae), + '\\__angbracketrightBig__' : ('cmex10', 0x45), + '\\__angbracketrightbigg__' : ('cmex10', 0xc0), + '\\__angbracketrightBigg__' : ('cmex10', 0x2b), + '\\__backslashbig__' : ('cmex10', 0xb2), + '\\__backslashBig__' : ('cmex10', 0x2f), + '\\__backslashbigg__' : ('cmex10', 0xc2), + '\\__backslashBigg__' : ('cmex10', 0x2d), + '\\__braceleftbig__' : ('cmex10', 0xa9), + '\\__braceleftBig__' : ('cmex10', 0x6e), + '\\__braceleftbigg__' : ('cmex10', 0xbd), + '\\__braceleftBigg__' : ('cmex10', 0x28), + '\\__bracerightbig__' : ('cmex10', 0xaa), + '\\__bracerightBig__' : ('cmex10', 0x6f), + '\\__bracerightbigg__' : ('cmex10', 0xbe), + '\\__bracerightBigg__' : ('cmex10', 0x29), + '\\__bracketleftbig__' : ('cmex10', 0xa3), + '\\__bracketleftBig__' : ('cmex10', 0x68), + '\\__bracketleftbigg__' : ('cmex10', 0x2219), + '\\__bracketleftBigg__' : ('cmex10', 0x22), + '\\__bracketrightbig__' : ('cmex10', 0xa4), + '\\__bracketrightBig__' : ('cmex10', 0x69), + '\\__bracketrightbigg__' : ('cmex10', 0xb8), + '\\__bracketrightBigg__' : ('cmex10', 0x23), + '\\__ceilingleftbig__' : ('cmex10', 0xa7), + '\\__ceilingleftBig__' : ('cmex10', 0x6c), + '\\__ceilingleftbigg__' : ('cmex10', 0xbb), + '\\__ceilingleftBigg__' : ('cmex10', 0x26), + '\\__ceilingrightbig__' : ('cmex10', 0xa8), + '\\__ceilingrightBig__' : ('cmex10', 0x6d), + '\\__ceilingrightbigg__' : ('cmex10', 0xbc), + '\\__ceilingrightBigg__' : ('cmex10', 0x27), + '\\__floorleftbig__' : ('cmex10', 0xa5), + '\\__floorleftBig__' : ('cmex10', 0x6a), + '\\__floorleftbigg__' : ('cmex10', 0xb9), + '\\__floorleftBigg__' : ('cmex10', 0x24), + '\\__floorrightbig__' : ('cmex10', 0xa6), + '\\__floorrightBig__' : ('cmex10', 0x6b), + '\\__floorrightbigg__' : ('cmex10', 0xba), + '\\__floorrightBigg__' : ('cmex10', 0x25), + '\\__hatwide__' : ('cmex10', 0x62), + '\\__hatwider__' : ('cmex10', 0x63), + '\\__hatwidest__' : ('cmex10', 0x64), + '\\__parenleftbig__' : ('cmex10', 0xa1), + '\\__parenleftBig__' : ('cmex10', 0xb3), + '\\__parenleftbigg__' : ('cmex10', 0xb5), + '\\__parenleftBigg__' : ('cmex10', 0xc3), + '\\__parenrightbig__' : ('cmex10', 0xa2), + '\\__parenrightBig__' : ('cmex10', 0xb4), + '\\__parenrightbigg__' : ('cmex10', 0xb6), + '\\__parenrightBigg__' : ('cmex10', 0x21), + '\\__radicalbig__' : ('cmex10', 0x70), + '\\__radicalBig__' : ('cmex10', 0x71), + '\\__radicalbigg__' : ('cmex10', 0x72), + '\\__radicalBigg__' : ('cmex10', 0x73), + '\\__slashbig__' : ('cmex10', 0xb1), + '\\__slashBig__' : ('cmex10', 0x2e), + '\\__slashbigg__' : ('cmex10', 0xc1), + '\\__slashBigg__' : ('cmex10', 0x2c), + '\\__tildewide__' : ('cmex10', 0x65), + '\\__tildewider__' : ('cmex10', 0x66), + '\\__tildewidest__' : ('cmex10', 0x67), + ',' : ('cmmi10', 0x3b), '.' : ('cmmi10', 0x3a), '/' : ('cmmi10', 0x3d), @@ -1112,6 +1181,8 @@ '|' : 0x2016, '}' : 0x7d, } +tex2uni['__angbracketleft__'] = tex2uni['langle'] +tex2uni['__angbracketright__'] = tex2uni['rangle'] # Each element is a 4-tuple of the form: # src_start, src_end, dst_font, dst_start diff --git a/lib/matplotlib/tests/test_mathtext.py b/lib/matplotlib/tests/test_mathtext.py index 4fc04a627dd5..5d0245bc5049 100644 --- a/lib/matplotlib/tests/test_mathtext.py +++ b/lib/matplotlib/tests/test_mathtext.py @@ -125,12 +125,21 @@ r'$,$ $.$ $1{,}234{, }567{ , }890$ and $1,234,567,890$', # github issue 5799 r'$\left(X\right)_{a}^{b}$', # github issue 7615 r'$\dfrac{\$100.00}{y}$', # github issue #1888 - r'$a=-b-c$' # github issue #28180 + r'$a=-b-c$', # github issue #28180 ] # 'svgastext' tests switch svg output to embed text as text (rather than as # paths). svgastext_math_tests = [ r'$-$-', + # Check all AutoHeightChar substitutions. + *[ + r'$\left' + lc + r' M \middle/ ? \middle\backslash ? \right' + rc + ' ' + # Normal size. + r'\left' + lc + r' \frac{M}{B} \middle/ ? \middle\backslash ? \right' + rc + ' ' + # big size. + r'\left' + lc + r' \frac{\frac{M}{I}}{B} \middle/ ? \middle\backslash ? \right' + rc + ' ' + # bigg size. + r'\left' + lc + r' \frac{\frac{M}{I}}{\frac{B}{U}} \middle/ ? \middle\backslash ? \right' + rc + ' ' + # Big size. + r'\left' + lc + r'\frac{\frac{\frac{M}{I}}{N}}{\frac{\frac{B}{U}}{G}} \middle/ ? \middle\backslash ? \right' + rc + '$' # Bigg size. + for lc, rc in ['()', '[]', '<>', (r'\{', r'\}'), (r'\lfloor', r'\rfloor'), (r'\lceil', r'\rceil')] + ], ] # 'lightweight' tests test only a single fontset (dejavusans, which is the # default) and only png outputs, in order to minimize the size of baseline @@ -237,7 +246,7 @@ def test_mathtext_rendering_svgastext(baseline_images, fontset, index, text): mpl.rcParams['svg.fonttype'] = 'none' # Minimize image size. fig = plt.figure(figsize=(5.25, 0.75)) fig.patch.set(visible=False) # Minimize image size. - fig.text(0.5, 0.5, text, + fig.text(0.5, 0.5, text, fontsize=16, horizontalalignment='center', verticalalignment='center')