How can I tell matplotlib
to use a specific variant of a font when they both have the same name and characteristics?
For example, I can tell matplotlib
to use Latin Modern Roman:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import numpy as np
from pathlib import Path
import matplotlib as mpl
import PyQt5
mpl.use('Qt5Agg')
import matplotlib.pyplot as plt
mpl.rcParams['font.family'] = 'Latin Modern Roman'
mpl.rcParams['font.weight'] = '400'
mpl.rcParams['font.style'] = 'normal'
mpl.rcParams['font.variant'] = 'normal'
mpl.rcParams['mathtext.fontset'] = 'custom'
mpl.rcParams['mathtext.default'] = 'it'
mpl.rcParams['mathtext.rm'] = 'Latin Modern Roman:normal'
mpl.rcParams['mathtext.it'] = 'Latin Modern Roman:italic'
mpl.rcParams['mathtext.bf'] = 'Latin Modern Roman:bold'
mpl.rcParams['xtick.labelsize'] = 8
mpl.rcParams['ytick.labelsize'] = 8
mpl.rcParams['axes.titlesize'] = 10
mpl.rcParams['axes.labelsize'] = 10
x = np.linspace(0,2*np.pi,100)
y = np.sin(x)
fig1 = plt.figure(figsize=(3, 3*(9/16)), dpi=300)
ax1 = fig1.gca()
ax1.plot(x, y, c='r', linewidth=1.0, zorder=20)
ax1.set_xlabel(r'$x\ label$')
ax1.set_ylabel(r'$y\ label$')
fig1.tight_layout(pad=0.15)
plt.show()
OK, that's nice. But what if I want to use a specific "substyle" of Latin Modern Roman? For example, on my system, when I list the available font entries having name='Latin Modern Roman'
and weight=400
and style='normal'
and variant='normal'
through:
fonts = mpl.font_manager.fontManager.ttflist
for f in fonts:
if (f.name=='Latin Modern Roman'):
if all([(f.style=='normal'),(f.weight==400),(f.style=='normal'),(f.variant=='normal')]):
print(f.name)
print(Path(f.fname).stem)
print(f.fname)
print('weight : %s'%str(f.weight))
print('style : %s'%str(f.style))
print('stretch : %s'%str(f.stretch))
print('variant : %s'%str(f.variant))
print('\n')
I get:
Latin Modern Roman
lmroman9-regular
/usr/share/texmf/fonts/opentype/public/lm/lmroman9-regular.otf
weight : 400
style : normal
stretch : normal
variant : normal
...
...
Latin Modern Roman
lmroman10-regular
/usr/share/texmf/fonts/opentype/public/lm/lmroman10-regular.otf
weight : 400
style : normal
stretch : normal
variant : normal
So how would I tell matplotlib
that I want to use lmroman9-regular.otf
vs lmroman10-regular.otf
? In mpl.rcParams
I can only specify font.family
,font.weight
,font.style
and font.variant
so if the two .otf
files have all the same values, how can I tell it to use one .otf
rather than the other? There are actually differences between the fonts, for example:
I've tried referencing the .otf
file directly with a matplotlib.FontProperties
instance, then renaming it, then pointing to the new name like:
prop = mpl.font_manager.FontProperties(fname='/usr/share/fonts/opentype/lmodern/lmroman10-regular.otf')
prop.set_name('Latin Modern Roman Type 10')
mpl.rcParams['font.family'] = 'Latin Modern Roman Type 10'
but then I get findfont: Font family ['Latin Modern Roman Type 10'] not found. Falling back to DejaVu Sans.
because prop.get_name()
still (mysteriously) returns Latin Modern Roman
and not Latin Modern Roman Type 10
How can I tell matplotlib
that I want to use a one over the other?
It's possible to explicitly use fontproperties
to point to a font_manager.FontProperties()
instance in most set_label
and set_ticklabel
calls (like in this answer), but I'm looking for a way to set this globally through rcParams
.
Note: I would NOT like to use LaTeX
rendering (text.usetex
) in matplotlib
.
System:
That is a really good question. I will provide the way I found the solution to that specific problem. In order to reproduce it we first have to download the given fonts from here (I downloaded the version 10 regular and the 17 version regular to see a clearer difference). Once installed we can check if our new font is found by matplotlib
:
from matplotlib import font_manager
font_manager.font_manager.findSystemFonts(fontext='otf') # Matplotlib associates with
# the .otf extension two more extensions viz. '.ttc' and '.ttf', as can be obtain over
# font_manager.get_fontext_synonyms('otf')
Once you have found your specified path to your .otf
file, we can use the font_manager
to make that specific version our selected font.
# This will create an object which contains your file and give it a custom name
# Here is the idea of this: https://stackoverflow.com/questions/35668219/how-to-set-up-a-custom-font-with-custom-path-to-matplotlib-global-font
fe = font_manager.FontEntry(
fname='C:\\Users\\<your_user_name>\\AppData\\Local\\Microsoft\\Windows\\Fonts\\latin-modern-roman.mroman10-regular.otf',
name='10erLatin')
font_manager.fontManager.ttflist.insert(0, fe)
mpl.rcParams['font.family'] = fe.name
Lets plot it so that we can later see whether it worked or not.
import numpy as np
from pathlib import Path
import matplotlib as mpl
# import PyQt5
# mpl.use('Qt5Agg') # I want to plot it inline ;)
import matplotlib.pyplot as plt
mpl.rcParams['font.family'] = fe.name
mpl.rcParams['font.weight'] = '400'
mpl.rcParams['font.style'] = 'normal'
mpl.rcParams['font.variant'] = 'normal'
mpl.rcParams['mathtext.fontset'] = 'custom'
mpl.rcParams['mathtext.default'] = 'it'
mpl.rcParams['mathtext.rm'] = fe.name + ':normal'
mpl.rcParams['mathtext.it'] = fe.name + ':italic'
mpl.rcParams['mathtext.bf'] = fe.name + ':bold'
mpl.rcParams['xtick.labelsize'] = 8
mpl.rcParams['ytick.labelsize'] = 8
mpl.rcParams['axes.titlesize'] = 10
mpl.rcParams['axes.labelsize'] = 10
x = np.linspace(0,2*np.pi,100)
y = np.sin(x)
fig1 = plt.figure(figsize=(3, 3*(9/16)), dpi=300)
ax1 = fig1.gca()
ax1.plot(x, y, c='r', linewidth=1.0, zorder=20)
ax1.set_xlabel(r'$x\ label$')
ax1.set_ylabel(r'$y\ label$')
fig1.tight_layout(pad=0.15)
plt.show()
Lets change it up to the 17 version of the regular font.
fe = font_manager.FontEntry(
fname='C:\\Users\\<your_user_name>\\AppData\\Local\\Microsoft\\Windows\\Fonts\\latin-modern-roman.mroman17-regular.otf',
name='17erLatin')
font_manager.fontManager.ttflist.insert(0, fe)
mpl.rcParams['font.family'] = fe.name
Again, lets plot it (used the exact same code as above to create that plot, just executed the given code beforehand).
If my hindsight is not too bad, then the text changed globally. There are some other functions which might be useful to look at, however, they are really hidden in the source code. Please let me know, if you run into problems.
Collected from the Internet
Please contact [email protected] to delete if infringement.
Comments