matplotlib font not found - python

I'm trying to use the font "Heuristica" in my matplotlib plots, but it won't show up.
I defined "Heuristica" on the first spot in the rcParameter font.serif --> no result
I changed font.family to "Heuristica" and got the message
findfont: FontFamily not found
that got me thinking, because Heuristica is installed and I can access it from other software without problems. So I used the fontManager and did:
import pylab as pl
la = pl.matplotlib.font_manager.FontManager()
lu = pl.matplotlib.font_manager.FontProperties(family = 'Heuristica')
la.findfont(lu)
and got:
Out[7]: 'C:\\Windows\\Fonts\\Heuristica-Regular.otf'
So obviously Heuristica can be found.
I looked up the available ttf-Fonts (How can i get list of font family(or Name of Font) in matplotlib) but Heuristica is not in this list.
I'd be glad about any help.

Well, mdboom solved the problem over at github, all the credit belongs to him:
When you add new fonts to your system, you need to delete your fontList.cache file in order for matplotlib to find them.
The reason it works on lines 4/5 in your example is because you are creating a FontManager from scratch (which goes out to the filesystem and hunts down all the fonts). Internally, when matplotlib later does its own font lookup, it is using a FontManager that has been loaded from a cache on disk in the fontList.cache file.
Long term, we have plans to switch to using the font lookup mechanisms of the OS to get around this problem, (see MEP14), but in the meantime, you'll need to remove the fontList.cache file everytime you want matplotlib to discover new fonts.
The file fontList.cache is located at your Userfolder --> .matplotlib/fontList.cache, for Windows that would normally be C:\Users\yourUsername\.matplotlib\fontList.cache

For some versions of Matplotlib, it may be necessary to clear the LRU cache of _get_fontconfig_fonts() (next to removing the fontList.cache file).
fm = matplotlib.font_manager
fm._get_fontconfig_fonts.cache_clear()
This function is responsible for calling, and caching, fc-list on a Linux/Unix system. If your font appears in fc-list, and not in Matplotlib's fonts, even after removing the fontList.cache file, this may be the culprit.

Related

Use matplotlib.pyplot.rcparams with a custom font which is not installed

I'm trying to use a custom ttf font not installed in the system for text element in the matplotlib figure.
with plt.style.context('mplparams.mplstyle'):
plt.plot(np.sin(np.linspace(0, 3 * np.pi)), '-o')
I know I can change the text properties with FontManager but I'm looking for a solution which only involves an external config file.
At the moment I only know that i can change font.sans-serif to a font name, not font path.
Is this possible?
The font to be used has to be known to the Fontmanager, otherwise you cannot get it into the plot. In order to specify a font through rcParams this font must be found in a folder matplotlib would look for it. In case you don't want to install anything, you may copy the .ttf file to the matplotlib font folder. In my case this is
python\Lib\site-packages\matplotlib\mpl-data\fonts
Then you need to clear the font.chache. Find out its path via print(matplotlib.get_cachedir()) and delete the fontList files. (Or make a backup first if you like).
Then run your script which has the rcParam specified
font.sans-serif : <name of font>
or use
plt.rcParams['font.sans-serif'] = "<name of font>"
Also see this question.

How to permanently set matplotlib pyplot style?

I like the ggplot style that is available in matplotlib. So when I'm in an interactive session I typically do
import matplotlib.pyplot as plt
plt.style.use('ggplot')
This produces very nice styles. Is there an easy way to make this setting persistent so I don't need to type the above command every time I start up Python?
You can specify your required style in a matplotlibrc format file in the installation directory.
Edit: on github we find
# from http://www.huyng.com/posts/sane-color-scheme-for-matplotlib/
patch.linewidth: 0.5
patch.facecolor: 348ABD # blue
patch.edgecolor: EEEEEE
patch.antialiased: True
font.size: 10.0
axes.facecolor: E5E5E5
axes.edgecolor: white
axes.linewidth: 1
axes.grid: True
axes.titlesize: x-large
axes.labelsize: large
axes.labelcolor: 555555
axes.axisbelow: True # grid/ticks are below elements (e.g., lines, text)
axes.prop_cycle: cycler('color', ['E24A33', '348ABD', '988ED5', '777777', 'FBC15E', '8EBA42', 'FFB5B8'])
# E24A33 : red
# 348ABD : blue
# 988ED5 : purple
# 777777 : gray
# FBC15E : yellow
# 8EBA42 : green
# FFB5B8 : pink
xtick.color: 555555
xtick.direction: out
ytick.color: 555555
ytick.direction: out
grid.color: white
grid.linestyle: - # solid line
figure.facecolor: white
figure.edgecolor: 0.50
You can append
use('ggplot')
to .../lib/python2.7/site-packages/matplotlib-2.0.0-py2.7-linux-x86_64.egg/matplotlib/style/__init__.py
Your specific path might look slightly different.
In principle, #Roelants answer is correct. Just to go a bit more in detail here:
Locate your matplotlibrc file as discussed here and make a backup of it.
Locate the ggplot.mplstyle (might be in a subfolder compared to the matplotlibrc file).
Replace the content of matplotlibrc file by the content of ggplot.mplstyle.
From that moment on, the default style will be identical to the one defined by the ggplot style.
I hope to expand upon the answer of #ImportanceOfBeingErnest in a bit more detail.
How to Permanently Change Default Matplotlib Style
This is a three step process where we 1. find out where the matplotlibrc file is located 2. copy that file to another location and 3. add the matplotlibrc parameters of a specific style. Let's go over each in a bit more detail.
Find where the default matplotlibrc file is in your computer with the command
import matplotlib
print(matplotlib.matplotlib_fname())
Copy the default matplotlibrc file into the directory $HOME/.config/matplotlib/matplotlibrc with the command
cp path_matplotlibrc $HOME/.config/matplotlib/matplotlibrc`
where path_matplotlibrc is the path where matplotlibrc is located, ie path_matplotlibrc is the result of step one.
Copy the code from one of the styles files and paste it in the '$HOME/.config/matplotlib/matplotlibrc` file.
Go to the directory matplotlib/lib/matplotlib/mpl-data/stylelib (or just go to the same spot in matplotlib's github here). Copy the code from one of the .mplstyle files in that directory. This code will be a list of matplotlibrc parameters that correspond to a specific style indicated by the file name. For example, going to github, you can just copy the code for the fivethirtyeight style which looks like this.
Paste the code with that style's matplotlibrc parameters at the bottom of the '$HOME/.config/matplotlib/matplotlibrcfile. So in this example we would paste the thefivethirtyeightstylematplotlibrc` parameter code depicted above at the end of the new file.
And that's it you are done. Just find out where the matplotlibrc file is originally located, copy that file to another location, and add the matplotlibrc parameters of a specific style. Pretty simple.
Why does this work?
The matplotlib documentation here explains:
Matplotlib uses matplotlibrc configuration files to customize all kinds of properties, which we call 'rc settings' or 'rc parameters'. You can control the defaults of almost every property in Matplotlib: figure size and DPI, line width, color and style, axes, axis and grid properties, text and font properties and so on. The matplotlibrc is read at startup to configure Matplotlib... When a style sheet is given with style.use('/.mplstyle'), settings specified in the style sheet take precedence over settings in the matplotlibrc file.
Each style is really a collection of parameters and properties that override the default matplotlibrc file found here. We can see this explicitly by looking at the directory matplotlib/lib/matplotlib/mpl-data/stylelib (found on github here) which contains a file for each built-in style. Click on a file in the directory and we can clearly see that the style is merely a list of matplotlibrc parameters. (This is what we showed above, the fivethirtyeight matplotlibrc parameters.)
So Matplotlib uses the matplotlibrc file to determine the style of the graphs. Now how does pasting the matplotlibrc settings of a certain style in a new file location override the default matplotlibrc settings?
Matplotlib looks for the matplotlibrc file in four locations in the following order:
matplotlibrc in the current working directory. It is just seeing if a file exists in the current working directory called matplotlibrc.
$MATPLOTLIBRC if it is a file, else $MATPLOTLIBRC/matplotlibrc.
On Linux and FreeBSD, it looks in .config/matplotlib/matplotlibrc (or $XDG_CONFIG_HOME/matplotlib/matplotlibrc) if you've customized your environment. On other platforms, it looks in .matplotlib/matplotlibrc.
INSTALL/matplotlib/mpl-data/matplotlibrc, where INSTALL is where you installed the matplotlib package. It may be something like /usr/lib/python3.7/site-packages on Linux, and maybe C:\Python37\Lib\site-packages on Windows.
Once a matplotlibrc file has been found, it will not search any of the other paths.
So by default matplotlibrcis only found in location four. This effectively means we can override the default settings found in location 4 by creating a new matplotlibrc in locations one, two, or three. By convention you should create a new matplotlibrc file in the highest numbered location possible so you can have more room to override that file if need be. In the instructions I just gave, we create a new matplotlibrc file in location 3. This is why and how we overrode the default matplotlibrc settings, thus creating new matplotlibrc settings that correspond to a specific style that is now used by default.
A bit more about Matplotlib Styles can be found here.
About Matplotlib Styles
matplotlib has several built-in styles to choose from. You can see here how each built-in style will change how your plots looks. To call a specific style use the command plt.style.use('stylename') where stylename is any arbitrary style name and to list all available styles, use print(plt.style.available).

Creating a lightweight fallback font with fontforge and fonttools

For a webapp I need a way to prevent that a browser falls back to another font if my web font doesn't include a character. It seems the only way to do this is to add another font to the fontstack which includes "all" possible characters 1.
There are already existing fallback fonts, but those are more debug helpers as they show the codepoint as number, therefore they are much to heavy (>2MB).
The fallback font for my usecase should just show something like a box to signal a missing character.
My idea was to generate a simple font with only one glyph and apply a feature file which will replace all glyphs with this one.
My script for fontforge:
import fontforge
import fontTools.feaLib.builder as feaLibBuilder
from fontTools.ttLib import TTFont
font_name = 'maeh.ttf'
font = fontforge.font()
glyph = font.createChar(33, "theone")
pen = glyph.glyphPen()
pen.moveTo((100,100))
pen.lineTo((100,500))
pen.lineTo((500,500))
pen.lineTo((500,100))
pen.closePath()
for i in range(34, 99):
glyph = font.createChar(i)
glyph.width=10
font.cidConvertTo('Adobe', 'Identity', 0) # doesn't make a difference
font.generate(font_name)
font = TTFont(font_name)
feaLibBuilder.addOpenTypeFeatures(font, 'fallback.fea')
font.save("fea_"+font_name)
My feature file:
languagesystem DFLT dflt;
#all=[\00035-\00039];
##all=[A-Z] this works
feature liga {
sub #all by theone;
} liga;
But the above results in a
KeyError: ('cid00037', 'SingleSubst[0]', 'Lookup[0]', 'LookupList')
with changing numbers for cid00037.
If I use the out commented A-Z from the Feature file it works, so this approach doesn't seem to be completely wrong.
Why can't fonttools find the glyphs if I specify the range in CID notation?
Is there another way to crate a class for the OpenType feature file which includes all glyphs?
While working on the above problem, somebody hinted me to the Adobe NotDef font, which is pretty much what I was looking for. For some reason I wasn't able to convert the .otf of the Adobe NotDef to woff or woff2 with fontforge. Also all the online tools to create the web font files like fontsquirrel failed. To create the woff file I used sfnt2woff from the woff-tools package. For the woff2 file I used https://github.com/google/woff2.

Matplotlib and non-ascii characters

Never ending story about Matplotlib and non-ascii characters continues, indeed:
1) Localhost: Mac OS X: By default, some characters are missing (replaced with []). OK, after adding matplotlib.rc('font', family='Verdana') characters are displayed fine. However, when deploying to a production server things get broken once again.
2) Server: Linux (openSUSE, using: matplotlib.use('Agg'), serving png's and pdf's, Django app): Calling matplotlib.rc('font', family='Verdana') does not help now (WHY?). I even copied some core fonts from Mac to openSUSE to make sure they are the same.
What does help a bit is this:
import matplotlib.font_manager as fm
fp = fm.FontProperties(fname="/usr/share/fonts/truetype/Verdana.ttf")
# matplotlib.rcParams['font.family'] = fp.get_name()
matplotlib.rc('font', family=fp.get_name())
and then using per partes plt.title(_title, fontproperties=fp) for each plot/block. This is very tiring… And what is worse, it is not usable when working with legends. For example there is no way (is there?) to use it like that:
ax.legend([charts], [_label], fontproperties=fp)
and
ax.legend([charts], [_label], prop={'family': 'Verdana'}
does not have any effect (it works on Mac OS X, though).
Funny thing is that most of non-ascii characters are displayed fine (ščřž…), problems occur only with ě, ť, ď… Why is Matplotlib still having these issues with unicode? I would not mind back in 90's but it is 2015 and not being able to use diacritics properly is pain.
Why do the “hooks” work under OS X, yet do not have any effect in openSUSE server?
matplotlib not finding the correct fonts can be caused by stale font cache (this is a problem we need to solve better).
To find were your cache is do
import matplotlib.font_manager as fm
print(fm.cachedir) # or other way of getting the text out
And then delete the font-related contents in that directory. They will be rebuilt the next time you import matplotlib and (should) now contain the updated fonts.

Matplotlib PDF export uses wrong font

I want to generate high-quality diagrams for a presentation. I’m using Python’s matplotlib to generate the graphics. Unfortunately, the PDF export seems to ignore my font settings.
I tried setting the font both by passing a FontProperties object to the text drawing functions and by setting the option globally. For the record, here is a MWE to reproduce the problem:
import scipy
import matplotlib
matplotlib.use('cairo')
import matplotlib.pylab as pylab
import matplotlib.font_manager as fm
data = scipy.arange(5)
for font in ['Helvetica', 'Gill Sans']:
fig = pylab.figure()
ax = fig.add_subplot(111)
ax.bar(data, data)
ax.set_xticks(data)
ax.set_xticklabels(data, fontproperties = fm.FontProperties(family = font))
pylab.savefig('foo-%s.pdf' % font)
In both cases, the produced output is identical and uses Helvetica (and yes, I do have both fonts installed).
Just to be sure, the following doesn’t help either:
matplotlib.rc('font', family = 'Gill Sans')
Finally, if I replace the backend, instead using the native viewer:
matplotlib.use('MacOSX')
I do get the correct font displayed – but only in the viewer GUI. The PDF output is once again wrong.
To be sure – I can set other fonts – but only other classes of font families: I can set serif fonts or fantasy or monospace. But all sans-serif fonts seem to default to Helvetica.
Basically, #Jouni’s is the right answer but since I still had some trouble getting it to work, here’s my final solution:
#!/usr/bin/env python2.6
import scipy
import matplotlib
matplotlib.use('cairo')
import matplotlib.pylab as pylab
import matplotlib.font_manager as fm
font = fm.FontProperties(
family = 'Gill Sans', fname = '/Library/Fonts/GillSans.ttc')
data = scipy.arange(5)
fig = pylab.figure()
ax = fig.add_subplot(111)
ax.bar(data, data)
ax.set_yticklabels(ax.get_yticks(), fontproperties = font)
ax.set_xticklabels(ax.get_xticks(), fontproperties = font)
pylab.savefig('foo.pdf')
Notice that the font has to be set explicitly using the fontproperties key. Apparently, there’s no rc setting for the fname property (at least I didn’t find it).
Giving a family key in the instantiation of font isn’t strictly necessary here, it will be ignored by the PDF backend.
This code works with the cairo backend only. Using MacOSX won’t work.
The "family" argument and the corresponding rc parameter are not meant to specify the name of the font can actually be used this way. There's an (arguably baroque) CSS-like font selection system that helps the same script work on different computers, selecting the closest font available. The usually recommended way to use e.g. Gill Sans is to add it to the front of the value of the rc parameter font.sans-serif (see sample rc file), and then set font.family to sans-serif.
This can be annoying if the font manager decides for some obscure reason that Gill Sans is not the closest match to your specification. A way to bypass the font selection logic is to use FontProperties(fname='/path/to/font.ttf') (docstring).
In your case, I suspect that the MacOSX backend uses fonts via the operating system's mechanisms and so automatically supports all kinds of fonts, but the pdf backend has its own font support code that doesn't support your version of Gill Sans.
This is an addition to the answers above if you came here for a non-cairo backend.
The pdf-backend of matplotlib does not yet support true type font collections (saved as .ttc files). See this issue.
The currently suggested workaround is to extract the font-of-interest from a .ttc file and save it as a .ttf file. And then use that font in the way described by Konrad Rudolph.
You can use the python-package fonttools to achieve this:
font = TTFont("/System/Library/Fonts/Helvetica.ttc", fontNumber=0)
font.save("Helvetica-regular.ttf")
As far as I can see, it is not possible to make this setting "global" by passing the path to this new .ttf file to the rc. If you are really desperate, you could try to extract all fonts from a .ttc into separate .ttf files, uninstall the .ttc and install the ttfs separately. To have the extracted font side-by-side with the original font from the .ttc, you need to change the font name with tools like FontForge. I haven't tested this, though.
Check if you are rendering the text with LaTeX, i.e., if text.usetex is set to True. Because LaTeX rendering only supports a few fonts, it largely ignores/overwrites your other fonts settings. This might be the cause.

Categories

Resources