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 c76b653c33fb..000000000000
Binary files a/lib/matplotlib/tests/baseline_images/test_axes/log_scales.pdf and /dev/null differ
diff --git a/lib/matplotlib/tests/baseline_images/test_axes/log_scales.png b/lib/matplotlib/tests/baseline_images/test_axes/log_scales.png
deleted file mode 100644
index e305de1d9ac7..000000000000
Binary files a/lib/matplotlib/tests/baseline_images/test_axes/log_scales.png and /dev/null differ
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