matplotlib / matplotlib

matplotlib: plotting with Python
https://matplotlib.org/stable/
20.32k stars 7.66k forks source link

Computer Modern Glyph Error #17007

Closed roussel-ryan closed 3 years ago

roussel-ryan commented 4 years ago

Bug report

Trying to use computer modern roman (cmr10) font in plot. Getting an error which prevents minus sign rendering in plot.

import matplotlib as mpl
import matplotlib.pyplot as plt

mpl.rc('font',family = 'serif', serif = 'cmr10')
fig, ax = plt.subplots()

ax.set_title('This is a special font:')
ax.set_xlabel(r'This is the default font $\alpha\beta\gamma$')
ax.set_yticks([-1,0,1])
plt.show()

Figure_1

Output:

C:\ProgramData\Anaconda3\lib\site-packages\matplotlib\backends\backend_agg.py:214: RuntimeWarning: Glyph 8722 missing from current font.
  font.set_text(s, 0.0, flags=flags)
C:\ProgramData\Anaconda3\lib\site-packages\matplotlib\backends\backend_agg.py:183: RuntimeWarning: Glyph 8722 missing from current font.
  font.set_text(s, 0, flags=flags)

Expected outcome Correct rendering of symbols

Matplotlib version

Installed matplotlib via anaconda on default channel

anntzer commented 4 years ago

Likely due to https://github.com/matplotlib/matplotlib/pull/14567? Seems reasonable to special case unicode minus to map to normal minus given that we already special case it for other purposes and also https://github.com/matplotlib/matplotlib/pull/16366, just needs to figure out what are the fonts that need that and can also do it correctly (not mapping to a dash).

anntzer commented 4 years ago

Ah, actually that's not a regression, the unicode minus comes from https://github.com/matplotlib/matplotlib/blob/bb75f737a28f620fe023742f59dc6ed4f53b094f/lib/matplotlib/mathtext.py#L63 (of course we can still fix it...).

I'm extremely confused as to why using cmr causes an warning in a StixFonts instance, though (as can be checked by breakpoint()ing at the warning position) :/

roussel-ryan commented 4 years ago

Ok thank you for the effort! I'm not sure if I can do anything to help, but please let me know if there is

casperdcl commented 4 years ago

Same issue here.

Setting font as Computer Modern Roman (matplotlib.rcParams.update({'font.family': 'cmr10'})) results in negative signs in ticklabels not rendering. Oddly, negative signs in titles & labels render fine.

image

Code for reproduction

```python import matplotlib matplotlib.rcParams.update({ #'mathtext.fontset': 'cm', 'font.family': 'cmr10' }) import matplotlib.pyplot as plt plt.figure() plt.plot(range(-1, 1), range(-1, 1)) plt.title(r"dash (-) $mathtext:negative (-)\bf{mathtext.bf:negative (-)}$") plt.xlabel(r"dash (-) $mathtext:negative (-)\bf{mathtext.bf:negative (-)}$") ```
casperdcl commented 4 years ago

unfortunately the only workaround I could come up with is:

plt.gca().set_xticklabels([i.get_text().replace('−', '$-$') for i in ax.get_xticklabels()])
plt.gca().set_yticklabels([i.get_text().replace('−', '$-$') for i in ax.get_yticklabels()])

which is annoying since 'font.family': ('cmr10', 'STIXGeneral') should really work.

anntzer commented 4 years ago

I looked a bit more into this. Fundamentally, the problem is that we are relying on the old computer modern fonts, where symbols (e.g. minus) and letters/digits are in different font files (cmr10/cmsy10); moreover the "standard" (non-mathtext) Text object is only able to use a single font file at a time (and it would be quite a big surgery to change that limitation).

Fortunately, when using mathtext, you can use multiple fonts at the same time; so one needs to force the ticks to use mathtext with rcParams["axes.formatter.use_mathtext"] = True, and then we're not done yet, because the ticker uses \mathdefault to wrap ticks, i.e. use the "non-mathtext" font... so then we need to add a special-case to mathtext, when using cmr10 as the non-mathtext font, to look up the minus sign in cmsy10 instead:

diff --git i/lib/matplotlib/mathtext.py w/lib/matplotlib/mathtext.py
index 8b9df7e19..8fdc29cad 100644
--- i/lib/matplotlib/mathtext.py
+++ w/lib/matplotlib/mathtext.py
@@ -842,6 +842,10 @@ class UnicodeFonts(TruetypeFonts):
             found_symbol = False
             font = self._get_font(new_fontname)
             if font is not None:
+                if font.family_name == "cmr10" and uniindex == 0x2212:
+                    font = get_font(
+                        cbook._get_data_path("fonts/ttf/cmsy10.ttf"))
+                    uniindex = 0xa1
                 glyphindex = font.get_char_index(uniindex)
                 if glyphindex != 0:
                     found_symbol = True

(probably a real patch would need to add a comment there). Together with rcParams["axes.formatter.use_mathtext"] = True, I think this is the only reasonable short-term fix. Brownie points for whoever makes a PR out of this.

Long term I would like to get rid of the numerous cmXX10 fonts and replace them by the monolithic Latin Modern Math font, which should avoid all these problems. I guess that the cmr fonts shipped by Matplotlib should not be considered as "usable" for anything else than rcParams["mathtext.fontset"] = "cm" (using them for font.family will cause lots of trouble, like here); if you want to use CM for regular text consider just downloading Latin Modern Math (for example) and using that.

ehremington commented 4 years ago

This also happens with the xkcd plotting style using the "Humor Sans" font.

x = np.linspace(0,10,20)
y = 2*x-3

with plt.xkcd():
    fig = plt.figure()
    ax = fig.add_subplot()
    ax.plot(x,y)
adiazmont commented 4 years ago

I was able to get rid of the RuntimeWarning by declaring the font usage with:

plt.rcParams["font.serif"] = "cmr10"

Hence:

import matplotlib.pyplot as plt
plt.rcParams["font.serif"] = "cmr10"
fig, ax = plt.subplots()

ax.set_title('This is a special font:')
ax.set_xlabel(r'This is the default font $\alpha\beta\gamma$')
ax.set_yticks([-1, 0, 1])
plt.show()

tmp1

And, it continues to work despite the declaration of the yticks, as in:

import numpy as np
import matplotlib.pyplot as plt
plt.rcParams["font.serif"] = "cmr10"
fig, ax = plt.subplots()

ax.set_title('This is a special font:')
ax.set_xlabel(r'This is the default font $\alpha\beta\gamma$')
ax.set_yticks(np.arange(-1, 1.25, 0.25))
plt.show()

tmp2

Hope this helps!

jonas-eschle commented 4 years ago

Are there any news on this?

casperdcl commented 4 years ago

just rebased #18397

story645 commented 1 year ago

This also happens with the xkcd plotting style using the "Humor Sans" font.

So uh, this is why #26854 is failing (the plt.xkcd examples seem to work because they don't have a negative in the labels anywhere): throws "glyph missing" warning for title, omits hyphen for ticks: Figure_1