From 85f30cbd485eddc93e3c9ff115ac21c0886909d5 Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Wed, 12 Jan 2022 19:49:37 +0100 Subject: [PATCH] Remove *math* parameter of various mathtext internal APIs. The *math* parameter is passed through many layers of the call stack but is ultimately only used for a single purpose: deciding whether to replace the ASCII hyphen by a (longer) unicode minus. Instead of doing that, just do the substitution at the parsing stage. In particular, this fixes problematic unicode minus support with the "cm" fontset. This patch also reverts a significant part of 52003e4, as LogFormatters no longer need to pass unicode minuses in mathtext -- everything gets converted by mathtext. Likewise, this change also invalidates the test_log_scales baseline image (old, buggy wrt. unicode minus); replace it by a test that the drawn ticks are as expected (which was the intent in 90c1aa3). --- .../deprecations/22507-AL.rst | 5 + lib/matplotlib/_mathtext.py | 60 +- lib/matplotlib/_mathtext_data.py | 2 +- .../baseline_images/test_axes/log_scales.pdf | Bin 6254 -> 0 bytes .../baseline_images/test_axes/log_scales.png | Bin 10312 -> 0 bytes .../baseline_images/test_axes/log_scales.svg | 670 ------------------ lib/matplotlib/tests/test_axes.py | 40 +- lib/matplotlib/tests/test_colorbar.py | 2 +- lib/matplotlib/tests/test_ticker.py | 15 +- lib/matplotlib/ticker.py | 9 +- 10 files changed, 84 insertions(+), 719 deletions(-) create mode 100644 doc/api/next_api_changes/deprecations/22507-AL.rst delete mode 100644 lib/matplotlib/tests/baseline_images/test_axes/log_scales.pdf delete mode 100644 lib/matplotlib/tests/baseline_images/test_axes/log_scales.png delete mode 100644 lib/matplotlib/tests/baseline_images/test_axes/log_scales.svg diff --git a/doc/api/next_api_changes/deprecations/22507-AL.rst b/doc/api/next_api_changes/deprecations/22507-AL.rst new file mode 100644 index 000000000000..c71c92e0ad93 --- /dev/null +++ b/doc/api/next_api_changes/deprecations/22507-AL.rst @@ -0,0 +1,5 @@ +The *math* parameter of ``mathtext.get_unicode_index`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In math mode, ASCII hyphens (U+002D) are now replaced by unicode minus signs +(U+2212) at the parsing stage. diff --git a/lib/matplotlib/_mathtext.py b/lib/matplotlib/_mathtext.py index 551898d546ac..eb849d8d4823 100644 --- a/lib/matplotlib/_mathtext.py +++ b/lib/matplotlib/_mathtext.py @@ -18,7 +18,7 @@ QuotedString, Regex, StringEnd, ZeroOrMore, pyparsing_common) import matplotlib as mpl -from . import cbook +from . import _api, cbook from ._mathtext_data import ( latex_to_bakoma, stix_glyph_fixes, stix_virtual_fonts, tex2uni) from .font_manager import FontProperties, findfont, get_font @@ -33,7 +33,8 @@ # FONTS -def get_unicode_index(symbol, math=True): +@_api.delete_parameter("3.6", "math") +def get_unicode_index(symbol, math=True): # Publicly exported. r""" Return the integer index (from the Unicode table) of *symbol*. @@ -45,15 +46,13 @@ def get_unicode_index(symbol, math=True): math : bool, default: True If False, always treat as a single Unicode character. """ - # for a non-math symbol, simply return its Unicode index - if not math: - return ord(symbol) # From UTF #25: U+2212 minus sign is the preferred # representation of the unary and binary minus sign rather than # the ASCII-derived U+002D hyphen-minus, because minus sign is # unambiguous and because it is rendered with a more desirable # length, usually longer than a hyphen. - if symbol == '-': + # Remove this block when the 'math' parameter is deleted. + if math and symbol == '-': return 0x2212 try: # This will succeed if symbol is a single Unicode char return ord(symbol) @@ -98,7 +97,7 @@ def get_kern(self, font1, fontclass1, sym1, fontsize1, """ return 0. - def get_metrics(self, font, font_class, sym, fontsize, dpi, math=True): + def get_metrics(self, font, font_class, sym, fontsize, dpi): r""" Parameters ---------- @@ -117,8 +116,6 @@ def get_metrics(self, font, font_class, sym, fontsize, dpi, math=True): Font size in points. dpi : float Rendering dots-per-inch. - math : bool - Whether we are currently in math mode or not. Returns ------- @@ -136,7 +133,7 @@ def get_metrics(self, font, font_class, sym, fontsize, dpi, math=True): - *slanted*: Whether the glyph should be considered as "slanted" (currently used for kerning sub/superscripts). """ - info = self._get_info(font, font_class, sym, fontsize, dpi, math) + info = self._get_info(font, font_class, sym, fontsize, dpi) return info.metrics def render_glyph(self, ox, oy, font, font_class, sym, fontsize, dpi): @@ -217,14 +214,14 @@ def _get_offset(self, font, glyph, fontsize, dpi): return (glyph.height / 64 / 2) + (fontsize/3 * dpi/72) return 0. - def _get_info(self, fontname, font_class, sym, fontsize, dpi, math=True): + def _get_info(self, fontname, font_class, sym, fontsize, dpi): key = fontname, font_class, sym, fontsize, dpi bunch = self.glyphd.get(key) if bunch is not None: return bunch font, num, slanted = self._get_glyph( - fontname, font_class, sym, fontsize, math) + fontname, font_class, sym, fontsize) font.set_size(fontsize, dpi) glyph = font.load_char( @@ -314,7 +311,7 @@ def __init__(self, *args, **kwargs): _slanted_symbols = set(r"\int \oint".split()) - def _get_glyph(self, fontname, font_class, sym, fontsize, math=True): + def _get_glyph(self, fontname, font_class, sym, fontsize): font = None if fontname in self.fontmap and sym in latex_to_bakoma: basename, num = latex_to_bakoma[sym] @@ -329,7 +326,7 @@ def _get_glyph(self, fontname, font_class, sym, fontsize, math=True): return font, num, slanted else: return self._stix_fallback._get_glyph( - fontname, font_class, sym, fontsize, math) + fontname, font_class, sym, fontsize) # The Bakoma fonts contain many pre-sized alternatives for the # delimiters. The AutoSizedChar class will use these alternatives @@ -442,9 +439,9 @@ def __init__(self, *args, **kwargs): def _map_virtual_font(self, fontname, font_class, uniindex): return fontname, uniindex - def _get_glyph(self, fontname, font_class, sym, fontsize, math=True): + def _get_glyph(self, fontname, font_class, sym, fontsize): try: - uniindex = get_unicode_index(sym, math) + uniindex = get_unicode_index(sym) found_symbol = True except ValueError: uniindex = ord('?') @@ -536,11 +533,10 @@ def __init__(self, *args, **kwargs): self.fontmap[key] = fullpath self.fontmap[name] = fullpath - def _get_glyph(self, fontname, font_class, sym, fontsize, math=True): + def _get_glyph(self, fontname, font_class, sym, fontsize): # Override prime symbol to use Bakoma. if sym == r'\prime': - return self.bakoma._get_glyph( - fontname, font_class, sym, fontsize, math) + return self.bakoma._get_glyph(fontname, font_class, sym, fontsize) else: # check whether the glyph is available in the display font uniindex = get_unicode_index(sym) @@ -548,11 +544,9 @@ def _get_glyph(self, fontname, font_class, sym, fontsize, math=True): if font is not None: glyphindex = font.get_char_index(uniindex) if glyphindex != 0: - return super()._get_glyph( - 'ex', font_class, sym, fontsize, math) + return super()._get_glyph('ex', font_class, sym, fontsize) # otherwise return regular glyph - return super()._get_glyph( - fontname, font_class, sym, fontsize, math) + return super()._get_glyph(fontname, font_class, sym, fontsize) class DejaVuSerifFonts(DejaVuFonts): @@ -913,7 +907,7 @@ class Char(Node): `Hlist`. """ - def __init__(self, c, state, math=True): + def __init__(self, c, state): super().__init__() self.c = c self.font_output = state.font_output @@ -921,7 +915,6 @@ def __init__(self, c, state, math=True): self.font_class = state.font_class self.fontsize = state.fontsize self.dpi = state.dpi - self.math = math # The real width, height and depth will be set during the # pack phase, after we know the real fontsize self._update_metrics() @@ -931,8 +924,7 @@ def __repr__(self): def _update_metrics(self): metrics = self._metrics = self.font_output.get_metrics( - self.font, self.font_class, self.c, self.fontsize, self.dpi, - self.math) + self.font, self.font_class, self.c, self.fontsize, self.dpi) if self.c == ' ': self.width = metrics.advance else: @@ -1624,8 +1616,9 @@ class _MathStyle(enum.Enum): SCRIPTSTYLE = enum.auto() SCRIPTSCRIPTSTYLE = enum.auto() - _binary_operators = set(r''' - + * - + _binary_operators = set( + '+ * - \N{MINUS SIGN}' + r''' \pm \sqcap \rhd \mp \sqcup \unlhd \times \vee \unrhd @@ -1922,7 +1915,7 @@ def math(self, s, loc, toks): def non_math(self, s, loc, toks): s = toks[0].replace(r'\$', '$') - symbols = [Char(c, self.get_state(), math=False) for c in s] + symbols = [Char(c, self.get_state()) for c in s] hlist = Hlist(symbols) # We're going into math now, so set font to 'it' self.push_state() @@ -1969,6 +1962,13 @@ def customspace(self, s, loc, toks): def symbol(self, s, loc, toks): c = toks["sym"] + if c == "-": + # "U+2212 minus sign is the preferred representation of the unary + # and binary minus sign rather than the ASCII-derived U+002D + # hyphen-minus, because minus sign is unambiguous and because it + # is rendered with a more desirable length, usually longer than a + # hyphen." (https://www.unicode.org/reports/tr25/) + c = "\N{MINUS SIGN}" try: char = Char(c, self.get_state()) except ValueError as err: diff --git a/lib/matplotlib/_mathtext_data.py b/lib/matplotlib/_mathtext_data.py index a60634731b6b..8dac9301ed81 100644 --- a/lib/matplotlib/_mathtext_data.py +++ b/lib/matplotlib/_mathtext_data.py @@ -132,7 +132,7 @@ ']' : ('cmr10', 0x5d), '*' : ('cmsy10', 0xa4), - '-' : ('cmsy10', 0xa1), + '\N{MINUS SIGN}' : ('cmsy10', 0xa1), '\\Downarrow' : ('cmsy10', 0x2b), '\\Im' : ('cmsy10', 0x3d), '\\Leftarrow' : ('cmsy10', 0x28), diff --git a/lib/matplotlib/tests/baseline_images/test_axes/log_scales.pdf b/lib/matplotlib/tests/baseline_images/test_axes/log_scales.pdf deleted file mode 100644 index c76b653c33fb119c3454bfa8d18aabf33cb805c8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6254 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 diff --git a/lib/matplotlib/tests/baseline_images/test_axes/log_scales.svg b/lib/matplotlib/tests/baseline_images/test_axes/log_scales.svg deleted file mode 100644 index 0a29c9d0af21..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_axes/log_scales.svg +++ /dev/null @@ -1,670 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py index 72e5f63cd2ab..9118e8d205f8 100644 --- a/lib/matplotlib/tests/test_axes.py +++ b/lib/matplotlib/tests/test_axes.py @@ -2628,16 +2628,48 @@ def test_pyplot_axes(): plt.close(fig2) -@image_comparison(['log_scales']) def test_log_scales(): - # Remove this if regenerating the image. - plt.rcParams['axes.unicode_minus'] = False - fig, ax = plt.subplots() ax.plot(np.log(np.linspace(0.1, 100))) ax.set_yscale('log', base=5.5) ax.invert_yaxis() ax.set_xscale('log', base=9.0) + xticks, yticks = [ + [(t.get_loc(), t.label1.get_text()) for t in axis._update_ticks()] + for axis in [ax.xaxis, ax.yaxis] + ] + assert xticks == [ + (1.0, '$\\mathdefault{9^{0}}$'), + (9.0, '$\\mathdefault{9^{1}}$'), + (81.0, '$\\mathdefault{9^{2}}$'), + (2.0, ''), + (3.0, ''), + (4.0, ''), + (5.0, ''), + (6.0, ''), + (7.0, ''), + (8.0, ''), + (18.0, ''), + (27.0, ''), + (36.0, ''), + (45.0, ''), + (54.0, ''), + (63.0, ''), + (72.0, ''), + ] + assert yticks == [ + (0.18181818181818182, '$\\mathdefault{5.5^{-1}}$'), + (1.0, '$\\mathdefault{5.5^{0}}$'), + (5.5, '$\\mathdefault{5.5^{1}}$'), + (0.36363636363636365, ''), + (0.5454545454545454, ''), + (0.7272727272727273, ''), + (0.9090909090909092, ''), + (2.0, ''), + (3.0, ''), + (4.0, ''), + (5.0, ''), + ] def test_log_scales_no_data(): diff --git a/lib/matplotlib/tests/test_colorbar.py b/lib/matplotlib/tests/test_colorbar.py index ae3ab92c0d43..33b92495b740 100644 --- a/lib/matplotlib/tests/test_colorbar.py +++ b/lib/matplotlib/tests/test_colorbar.py @@ -609,7 +609,7 @@ def test_colorbar_format(fmt): im.set_norm(LogNorm(vmin=0.1, vmax=10)) fig.canvas.draw() assert (cbar.ax.yaxis.get_ticklabels()[0].get_text() == - '$\\mathdefault{10^{\N{Minus Sign}2}}$') + '$\\mathdefault{10^{-2}}$') def test_colorbar_scale_reset(): diff --git a/lib/matplotlib/tests/test_ticker.py b/lib/matplotlib/tests/test_ticker.py index d8c8c7d9e764..a89a7634feda 100644 --- a/lib/matplotlib/tests/test_ticker.py +++ b/lib/matplotlib/tests/test_ticker.py @@ -769,7 +769,7 @@ class TestLogFormatterMathtext: @pytest.mark.parametrize('min_exponent, value, expected', test_data) def test_min_exponent(self, min_exponent, value, expected): with mpl.rc_context({'axes.formatter.min_exponent': min_exponent}): - assert self.fmt(value) == expected.replace('-', '\N{Minus Sign}') + assert self.fmt(value) == expected class TestLogFormatterSciNotation: @@ -798,7 +798,7 @@ def test_basic(self, base, value, expected): formatter = mticker.LogFormatterSciNotation(base=base) formatter.sublabel = {1, 2, 5, 1.2} with mpl.rc_context({'text.usetex': False}): - assert formatter(value) == expected.replace('-', '\N{Minus Sign}') + assert formatter(value) == expected class TestLogFormatter: @@ -1016,18 +1016,17 @@ def logit_deformatter(string): """ match = re.match( r"[^\d]*" - r"(?P1[-\N{Minus Sign}])?" + r"(?P1-)?" r"(?P\d*\.?\d*)?" r"(?:\\cdot)?" - r"(?:10\^\{(?P[-\N{Minus Sign}]?\d*)})?" + r"(?:10\^\{(?P-?\d*)})?" r"[^\d]*$", string, ) if match: comp = match["comp"] is not None mantissa = float(match["mant"]) if match["mant"] else 1 - expo = (int(match["expo"].replace("\N{Minus Sign}", "-")) - if match["expo"] is not None else 0) + expo = int(match["expo"]) if match["expo"] is not None else 0 value = mantissa * 10 ** expo if match["mant"] or match["expo"] is not None: if comp: @@ -1152,8 +1151,8 @@ def test_use_overline(self): Test the parameter use_overline """ x = 1 - 1e-2 - fx1 = "$\\mathdefault{1\N{Minus Sign}10^{\N{Minus Sign}2}}$" - fx2 = "$\\mathdefault{\\overline{10^{\N{Minus Sign}2}}}$" + fx1 = r"$\mathdefault{1-10^{-2}}$" + fx2 = r"$\mathdefault{\overline{10^{-2}}}$" form = mticker.LogitFormatter(use_overline=False) assert form(x) == fx1 form.use_overline(True) diff --git a/lib/matplotlib/ticker.py b/lib/matplotlib/ticker.py index 1610f55a74a9..38ed75f12fdd 100644 --- a/lib/matplotlib/ticker.py +++ b/lib/matplotlib/ticker.py @@ -1100,12 +1100,11 @@ def __call__(self, x, pos=None): base = '%s' % b if abs(fx) < min_exp: - s = r'$\mathdefault{%s%g}$' % (sign_string, x) + return r'$\mathdefault{%s%g}$' % (sign_string, x) elif not is_x_decade: - s = self._non_decade_format(sign_string, base, fx, usetex) + return self._non_decade_format(sign_string, base, fx, usetex) else: - s = r'$\mathdefault{%s%s^{%d}}$' % (sign_string, base, fx) - return self.fix_minus(s) + return r'$\mathdefault{%s%s^{%d}}$' % (sign_string, base, fx) class LogFormatterSciNotation(LogFormatterMathtext): @@ -1308,7 +1307,7 @@ def __call__(self, x, pos=None): s = self._one_minus(self._format_value(1-x, 1-self.locs)) else: s = self._format_value(x, self.locs, sci_notation=False) - return r"$\mathdefault{%s}$" % self.fix_minus(s) + return r"$\mathdefault{%s}$" % s def format_data_short(self, value): # docstring inherited