Set matplotlib font by family name when there are multiple fonts with same name

HotDogCannon

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()

enter image description here

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:

enter image description here

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:

  • Ubuntu-20.04 on WSL2 on Windows 10 Pro 19042
  • Python 3.8.5 / [GCC 9.3.0] on linux
  • matplotlib 3.4.1
Yannik Suhre

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()

enter image description here

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).

enter image description here

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.

edited at
0

Comments

0 comments
Login to comment

Related

How to use font-family with same name?

Multiple Fonts in Same Family

What is the significance of giving multiple font family name

How can i get list of font family(or Name of Font) in matplotlib

How to set a CSS font-family using JQuery when the font name has a space

how to set the UIFont family and name with display font on the UITableView?

How to set a font family to "erewhon" when using Latex in matplotlib?

How to install cursive fonts for matplotlib: Font family ['cursive'] not found

Get font family name from QFontDatabase::addApplicationFont

Ubuntu Font Family and Mac OS X confusing font name and font

WPF multiple Font files of the same family

Retrofit — Multiple query parameters of same name where name is set dynamically

Add custom fonts in C++ and call them by their family name in QML

custom fonts with Font-Family

Can I define multiple Font-Family names for the same font?

UIFont doesn't set the correct font name when pointSize is 0

Can I set an LLDB breakpoint when multiple Rust source files share the same name?

Multiple divs with same name

Multiple functions with the same name

Multiple inputs with same name

How to set the multiple same name query in javascript ajax

Where does Chrome "Rendered Fonts" get the name of the font from?

Assign different font family to an Icon fonts, possible?

Adding Hindi Fonts to Ubuntu Font Family

How to set the font-family as specified when editing post in wordpress?

HTML layout breaks when font family is changed from "Open Sans" to other fonts

Specify the scope in EL when exist multiple attributes with the same name

Garbage collection when using multiple local variables with the same name

Merge fails when dataframe has multiple columns with same name

TOP Ranking

HotTag

Archive