Installing Nimbus Roman fonts on Linux - firemodels/fds GitHub Wiki
Standardizing Serif Fonts for Matplotlib on RHEL 9
This document explains how to configure Matplotlib on RHEL 9 so that PDF output uses Times-style Type 1 PostScript core fonts through the system-provided URW Base35 font package. This produces compact PDFs with consistent serif rendering (Nimbus Roman) across all users, without embedding large TrueType fonts.
1. Check Whether Nimbus Roman Is Present
Run the following:
fc-list : family | grep -i "nimbus"
You should see entries such as:
Nimbus Roman
Nimbus Sans
Nimbus Mono PS
If Nimbus Roman appears, continue to Step 4.
2. Install URW Base35 Fonts (if missing)
Install the font package:
sudo dnf install urw-base35-fonts
Verify the font files exist:
ls /usr/share/fonts/urw-base35 | grep -i nimbusroman
Look for files like:
NimbusRoman-Regular.otf
NimbusRoman-Italic.otf
NimbusRoman-Bold.otf
NimbusRoman-BoldItalic.otf
3. Rebuild Fontconfig Cache
sudo fc-cache -f -v
This ensures new fonts are visible to applications using Fontconfig (including Matplotlib).
4. Confirm Nimbus Roman via Fontconfig and Matplotlib
Check again via Fontconfig:
fc-list : family | grep -i nimbus
Then verify through Matplotlib:
python3 - <<EOF
import matplotlib.font_manager as fm
families = sorted({f.name for f in fm.fontManager.ttflist})
print([f for f in families if "Nimbus" in f])
EOF
You should see something like:
['Nimbus Roman', 'Nimbus Sans', 'Nimbus Mono PS']
Also check font resolution:
python3 - <<EOF
from matplotlib.font_manager import findfont
print(findfont("Nimbus Roman"))
EOF
Expected path example:
/usr/share/fonts/urw-base35/NimbusRoman-Regular.otf
5. Clear Matplotlib Font Caches (per user)
Each user must run:
rm -f ~/.cache/matplotlib/fontlist-*.json
rm -rf ~/.cache/matplotlib/tex.cache
Caches regenerate automatically on next Matplotlib use.
6. Required Matplotlib Runtime Settings
Add the following before creating any figures (or just use configure_fds_fonts() from fdsplotlib):
plt.rcParams.update({
"pdf.use14corefonts": True,
"text.usetex": False,
"font.family": "serif",
"font.serif": [
"Nimbus Roman",
"Times",
"Times New Roman",
"serif",
],
"mathtext.fontset": "custom",
"mathtext.rm": "Nimbus Roman",
"mathtext.it": "Nimbus Roman:italic",
"mathtext.bf": "Nimbus Roman:bold",
"mathtext.cal": "Nimbus Roman:italic",
"mathtext.tt": "Courier",
"mathtext.default": "rm",
"axes.unicode_minus": False,
"pdf.compression": 9,
})
Results:
- serif text uses Nimbus Roman (Times-style)
- PDF uses core Type 1 fonts (Times/Helvetica/Courier)
- No embedded TTF fonts → tiny PDF sizes
- math text inherits Nimbus Roman styling
7. Validate Correct Font Output
Create a test PDF:
python3 - <<EOF
import matplotlib.pyplot as plt
plt.text(0.5, 0.5, r"$\\nabla \\cdot \\mathbf{u} = 0$", fontsize=20)
plt.savefig("font_check.pdf")
EOF
List fonts used:
pdffonts font_check.pdf
Expected result:
Times-Roman Type 1 (not embedded)
Times-Bold Type 1 (not embedded)
Courier Type 1 (not embedded)
Type 3 math paths may appear, which is normal.
No large embedded TTF entries should be present.
8. Why Nimbus Roman
- Installed from official RHEL repos
- Metrics-compatible with Times Roman
- Legally redistributable
- Works correctly with
pdf.use14corefonts=True - Ensures consistent output for all cluster users
- Produces compact PDFs ideal for publications
Summary Table
| Step | Action |
|---|---|
| 1 | Confirm Nimbus Roman exists |
| 2 | Install urw-base35-fonts if needed |
| 3 | Run sudo fc-cache -f -v |
| 4 | Verify via Fontconfig and Matplotlib |
| 5 | Clear per-user Matplotlib cache |
| 6 | Apply the provided rcParams |
| 7 | Confirm fonts using pdffonts |
| 8 | Adopt Nimbus Roman as Times replacement |
Once complete, all users will generate consistent, Times-style serif text in Matplotlib PDFs on RHEL 9 without embedding large TrueType fonts.