Matplotlib can't find font installed in my Linux machine

Question:

I am trying to draw an xkcd-style plot with matplotlib (ver. 1.4.2) under Python 3.

When I try to run:

import matplotlib.pyplot as plt
plt.xkcd()
plt.plot([1,2,3,4], [1,4,9,16], 'bo')
plt.axis([0, 6, 0, 20])
plt.show()

It opens an empty window without any image and I get the error:

/usr/lib/python3/dist-packages/matplotlib/font_manager.py:1279: UserWarning: findfont: Font family ['Humor Sans', 'Comic Sans MS', 'StayPuft'] not found. Falling back to Bitstream Vera Sans
  (prop.get_family(), self.defaultFamily[fontext]))
/usr/lib/python3/dist-packages/matplotlib/font_manager.py:1289: UserWarning: findfont: Could not match :family=Bitstream Vera Sans_style=normal:variant=normal:weight=normal:stretch=normal:size=medium. Returning /usr/share/matplotlib/mpl-data/fonts/ttf/STIXSizOneSymReg.ttf
  UserWarning) Exception in Tkinter callback

I have Humor Sans installed. I checked it with fc-list | grep Humor. It can also be used within other programs, like Libre Office. I also have staypuft installed. Isn’t that enough?

The same code above but without the plt.xkcd() bit works flawlessly.

An alternative to plt.show(), like pylab.savefig() won’t work either for the xkcd code, but doesn’t have any problem with the same code without using xkcd.

Asked By: Pierre B

||

Answers:

If you add a new font after installing matplotlib then try to remove the font cache. Matplotlib will have to rebuild the cache, thereby adding the new font.

It may be located under ~/.matplotlib/fontList.cache or ~/.cache/matplotlib/fontList.json.

Answered By: Serenity

For Mac User: try to run this command in python: (or before the .py file)

import matplotlib

matplotlib.font_manager._rebuild()
Answered By: Zhihui Shao

Just in case somebody wants to choose a custom font for their chart. You can manually set up the font for your chart labels, title, legend, or tick labels. The following code demonstrates how to set a custom font for your chart. And the error you mentioned can disappear.

import matplotlib.font_manager as fm
import matplotlib.pyplot as plt

font_path = '/System/Library/Fonts/PingFang.ttc'  # the location of the font file
my_font = fm.FontProperties(fname=font_path)  # get the font based on the font_path

fig, ax = plt.subplots()

ax.bar(x, y, color='green')
ax.set_xlabel(u'Some text', fontproperties=my_font)
ax.set_ylabel(u'Some text', fontproperties=my_font)
ax.set_title(u'title', fontproperties=my_font)
for label in ax.get_xticklabels():
    label.set_fontproperties(my_font)
Answered By: Vigor

I installed fonts in Ubuntu under WSL2 using apt-get and they were not available to matplotlib.

Using matplotlib version 3.4.2 in JupyterLab I had to do the following procedure after installing a new font to make it available.

First, delete the matplotlib cache dir:

import shutil
import matplotlib

shutil.rmtree(matplotlib.get_cachedir())

The cache location can change depending on your installation. The code above guarantees that you will delete the correct one for your kernel.

Now restart your notebook kernel.

Then test if the new font appears using this command in a notebook cell:

import matplotlib.font_manager
from IPython.core.display import HTML

def make_html(fontname):
    return "<p>{font}: <span style='font-family:{font}; font-size: 24px;'>{font}</p>".format(font=fontname)

code = "n".join([make_html(font) for font in sorted(set([f.name for f in matplotlib.font_manager.fontManager.ttflist]))])

HTML("<div style='column-count: 2;'>{}</div>".format(code))

The code above will display all available fonts for matplotlib.

Answered By: neves

As @neves noted, matplotlib.font_manager._rebuild() no longer works. As an alternative to manually removing the cache dir, what works as of Matplotlib 3.4.3 is:

import matplotlib
matplotlib.font_manager._load_fontmanager(try_read_cache=False)

Here’s the Matplotlib source code for that function:

def _load_fontmanager(*, try_read_cache=True):
    fm_path = Path(
        mpl.get_cachedir(), f"fontlist-v{FontManager.__version__}.json")
    if try_read_cache:
        try:
            fm = json_load(fm_path)
        except Exception as exc:
            pass
        else:
            if getattr(fm, "_version", object()) == FontManager.__version__:
                _log.debug("Using fontManager instance from %s", fm_path)
                return fm
    fm = FontManager()
    json_dump(fm, fm_path)
    _log.info("generated new fontManager")
    return fm
Answered By: Cliff Kerr