When trying to run a script (given below in the minimal working example) on Ubuntu 20.04 that uses the cursive fonts in matplotlib
I am getting the matplotlib
warnings:
findfont: Font family ['cursive'] not found. Falling back to DejaVu Sans.
which tells me that I don't have any of the cursive fonts that matplotlib
wants to use for the cursive font family.
This seems to be confirmed in the minimal failing example below when none of the cursive fonts can be found by matplotlib
's font manager or by fc-list
# on my local machine
$ fc-list : family | grep -i "chancery\|textile\|sand\|script\|felipa\|cursive"
URW Chancery L
Free Chancery
How can I programatically find and install these fonts on Ubuntu? I know that I could hunt down free versions on the internet, but if I wanted to get them on a Docker image how can I install them through a CLI API like apt-get
?
For the following Dockerfile
FROM ubuntu:20.04
RUN apt-get update -y && \
apt-get install -y \
fontconfig \
fonts-dejavu \
fonts-freefont-ttf \
python3 \
python3-dev \
python3-pip \
python3-venv \
vim && \
apt-get -y autoclean && \
apt-get -y autoremove && \
rm -rf /var/lib/apt/lists/*
RUN python3 -m pip install --upgrade --no-cache-dir pip setuptools wheel && \
python3 -m pip install --no-cache-dir "matplotlib~=3.3" && \
python3 -m pip list && \
python3 -c "import matplotlib.pyplot" # generate font list cache
ENV MPLCONFIGDIR /tmp/.config # make writeable to non-root user
WORKDIR /code
COPY example.py example.py
with example.py
of
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.font_manager
def make_plot(font_family):
fig, ax = plt.subplots()
x = np.linspace(0, 10, 101)
y = np.square(x)
ax.plot(x, y)
ax.semilogy()
ax.set_xlabel("$x$")
ax.set_ylabel("$x^2$")
ax.set_title(f"Default matplotlib settings for {font_family} font family")
return fig, ax
def main():
image_types = ["pdf", "png"]
for font_family in ["sans-serif", "serif", "cursive"]:
plt.rcParams.update({"font.family": font_family})
fig, ax = make_plot(font_family=font_family)
for type in image_types:
fig.savefig(f"family_{font_family}.{type}")
cursive_family = matplotlib.rcParams["font.cursive"]
print(f"\nmatplotlib cursive family: {cursive_family}")
tff_fonts = sorted(
set([font.name for font in matplotlib.font_manager.fontManager.ttflist])
)
afm_fonts = sorted(
set([font.name for font in matplotlib.font_manager.fontManager.afmlist])
)
all_fonts = sorted(set([*tff_fonts, *afm_fonts]))
print(f"\ntff fonts: {tff_fonts}")
print(f"\nafm fonts: {afm_fonts}")
print(f"\nall fonts: {all_fonts}")
found_cursive_fonts = [font for font in cursive_family if font in all_fonts]
print(f"\nfound cursive fonts: {found_cursive_fonts}")
if __name__ == "__main__":
main()
if built with
docker build . \
--pull \
-f Dockerfile \
-t matplotlib-cursive-fonts-question:debug-local
and then run with
$ docker run --rm --user 1000:1000 -v $PWD:$PWD -w $PWD matplotlib-cursive-fonts-question:debug-local /bin/bash -c "python3 /code/example.py"
findfont: Font family ['cursive'] not found. Falling back to DejaVu Sans.
findfont: Font family ['cursive'] not found. Falling back to DejaVu Sans.
matplotlib cursive family: ['Apple Chancery', 'Textile', 'Zapf Chancery', 'Sand', 'Script MT', 'Felipa', 'cursive']
tff fonts: ['DejaVu Math TeX Gyre', 'DejaVu Sans', 'DejaVu Sans Display', 'DejaVu Sans Mono', 'DejaVu Serif', 'DejaVu Serif Display', 'FreeMono', 'FreeSans', 'FreeSerif', 'STIXGeneral', 'STIXNonUnicode', 'STIXSizeFiveSym', 'STIXSizeFourSym', 'STIXSizeOneSym', 'STIXSizeThreeSym', 'STIXSizeTwoSym', 'cmb10', 'cmex10', 'cmmi10', 'cmr10', 'cmss10', 'cmsy10', 'cmtt10']
afm fonts: ['Computer Modern', 'Courier', 'Helvetica', 'ITC Avant Garde Gothic', 'ITC Bookman', 'ITC Zapf Chancery', 'ITC Zapf Dingbats', 'New Century Schoolbook', 'Palatino', 'Symbol', 'Times', 'Utopia', 'ZapfDingbats']
all fonts: ['Computer Modern', 'Courier', 'DejaVu Math TeX Gyre', 'DejaVu Sans', 'DejaVu Sans Display', 'DejaVu Sans Mono', 'DejaVu Serif', 'DejaVu Serif Display', 'FreeMono', 'FreeSans', 'FreeSerif', 'Helvetica', 'ITC Avant Garde Gothic', 'ITC Bookman', 'ITC Zapf Chancery', 'ITC Zapf Dingbats', 'New Century Schoolbook', 'Palatino', 'STIXGeneral', 'STIXNonUnicode', 'STIXSizeFiveSym', 'STIXSizeFourSym', 'STIXSizeOneSym', 'STIXSizeThreeSym', 'STIXSizeTwoSym', 'Symbol', 'Times', 'Utopia', 'ZapfDingbats', 'cmb10', 'cmex10', 'cmmi10', 'cmr10', 'cmss10', 'cmsy10', 'cmtt10']
found cursive fonts: []
and
docker run --rm --user 1000:1000 -v $PWD:$PWD -w $PWD matplotlib-cursive-fonts-question:debug-local /bin/bash -c "fc-list : family | grep -i 'chancery\|textile\|sand\|script\|felipa\|cursive'"
returns nothing.
How can I programatically find and install the required cursive fonts for matplotlib
(on Ubuntu 20.04)?
As it seems there is no clear way to get these fonts from any Ubuntu PPA, what can be done instead is to just directly download Felipa, one of the cursive font family fonts, from Google Fonts. This is what the
maptlotlib
team does in thempl-docker
testing Docker image.So for a programmatic solution on my local machine what I can do is just
and while it isn't required I also made a symlink to where the rest of my fonts are for bookkeeping
sudo ln -s "${HOME}/.local/share/fonts/truetype/felipa" /usr/share/fonts/truetype/felipa
Of course, while this is programmatic, if you're on a local machine and have interactive capabilities it is probably better/easier to just visit the Google Fonts page for Felipa, download the font family zip file locally and extract it, and then open the
Felipa-Regular.ttf
with the Ubuntu font manager and let it install it for you.To give a reproducible example of this working I've edited the original Dockerfile to
wget
Felipa as decribed above and then also create a non-root user "docker" to both have the container run as non-root by default and to avoid the problems of having aHOME
-less user when run with--user 1000:1000
. There are ways to make this Dockerfile much more compact, but I'm going for readability in this example over size optimization.rebuilding this Docker image as before then allows for the following to run without warnings or errors
producing this plot for
family_cursive.png
: