GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — mathtext: add support for unicode mathematics fonts by llohse · Pull Request #31064 · matplotlib/matplotlib

Conversation

@llohse

@llohse llohse commented Feb 3, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor

PR summary

supersedes #31048
Add basic support for generic unicode OpenType mathematics fonts such as STIX Two Math, Cambria Math, DejaVu Math, etc.

Currently, mathematics text rendering through mathtext in matplotlib supports a hard-coded number of fonts (configured via mathtext.fontset). Its design presumably predates the specification of mathematics alphabets in the unicode standard. While it is possible to configure custom fonts (mathtext.fontset: custom), this requires to set separate fonts for upright, italic, fraktur, double-struck, etc. variants -- which is fundamentally incompatible with the way modern mathematics fonts are designed.

Unicode defines mathematical alphanumeric symbols as unique codepoints (see https://en.wikipedia.org/wiki/Mathematical_Alphanumeric_Symbols), in contrast to different fonts all defining different styles for the same ASCII characters/codepoints.
One relatively modern way to render mathematical formulas uses mathematics fonts such as STIX Two Math or Cambria Math, Asana Math, etc.. For LaTeX, this is implemented in the unicode-math package.
Instead of choosing a font based on the style (as it is currently done in matplotlib) to render the same codepoints, this maps alphanumeric characters to different codepoints based on the style, and render them from a single font.

Shortcomings of the status quo:

  • It is not currently possible to use modern mathematics fonts when using mathtext for rendering of mathematical equations in matplotlib. Consequently, it is difficult to configure fonts that are visually consistent and may require the use of latex rendering.
  • Font configuration may be perceived as somewhat complicated
  • The mathtext codebase and font selection logic is relatively complex and inaccessible, with all the hardcoded fonts, fixes for their quirks, and fallbacks

Changes

This change implements basic functionality to use any installed unicode OpenType mathematics fonts for use in mathtext in a portable way. Currently, this can be enabled by setting the rcparams

mathtext.fontset: unicodemath
mathtext.mathfont: STIX Two Math # or any other font name that can be loaded by fontmanager

I could think of different ways to configure this, though.

Internally, I have implemented a separate class UnicodeMathFonts(TruetypeFonts) to no interfere with the existing fontsets.

Running the test currently requires STIX Two Math to be installed on the system. For that reason, I have added it to the test data. One may think about vendoring STIX Two Math or DejaVu Math via mpl-data instead.

Examples

import matplotlib.pyplot as plt
import matplotlib as mpl

mpl.rcParams['mathtext.fontset'] = 'unicodemath'

font_names = ['STIX Two Math', 'TeX Gyre Pagella Math', 'TeX Gyre Schola Math', 'Noto Sans Math', 'Latin Modern Math', 'TeX Gyre DejaVu Math']

for font in font_names:
    with mpl.rc_context({'mathtext.mathfont' : font}):
        fig = plt.figure(figsize=(3, 1.5), dpi=150)
        fig.suptitle(f'{font}')
        fig.text(.1, .2, r'$\frac{1}{2 \pi \mathrm{i}} \int_{\partial G} f(z) \mathrm{d}z = \sum_{\nu = 1}^n \left(\mathrm{res} f\right)(z_\nu)$')
        fig.text(.1, .45, r'$\bigcup_{i \in I} A_i := \left\{ x \mid \bigvee_i\left( i \in I \wedge x \in A_i\right)\right\}$')
        fig.text(.1, .7, r'$\mathbb{Q} = \left. \left(\mathbb{Z} \times \mathbb{Z} \setminus \{ 0\} \right) \right/ \sim $')
        fig.tight_layout()
        fig.savefig(f'{font}.png')
Latin Modern Math Noto Sans Math STIX Two Math TeX Gyre DejaVu Math TeX Gyre Schola Math

PR checklist

  • "closes #0000" is in the body of the PR description to link the related issue
  • new and changed code is tested
  • Plotting related features are demonstrated in an example
  • New Features and API Changes are noted with a directive and release note
  • Documentation complies with general and docstring guidelines

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

scottshambaugh and daniilS reacted with hooray emoji
All reactions
  • 🎉 2 reactions
@github-actions github-actions Bot added topic: rcparams topic: text/mathtext labels Feb 3, 2026
@llohse
llohse force-pushed the mathtext-unicode-textoverhaul branch from 8009a09 to b8c28d2 Compare February 3, 2026 08:44
@llohse

llohse commented Feb 4, 2026

Copy link
Copy Markdown
Contributor Author

I opened a new PR, because this one is based on the text-overhaul branch and I messed up rebasing the old one.

@QuLogic: Would you kindly take a look at this? Is this something you would consider for merging?

For further discussion in case you don't reject this feature alltogether: How should the math font be configured? This also depends a bit on how prominent it should be visible. I could not think of a good way without introducing a new parameter in rcparams.
Viable options I could think of are mathtext.mathfont (as implemented in the PR) or fonts.math..
One could either require a single font or a list, like the font selection for normal text.

rcParams['mathtext.mathfont'] = 'TeX Gyre DejaVu Math'
rcParams['font.math'] = ['TeX Gyre DejaVu Math', 'Latin Modern Math', 'STIX Two Math']
All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

This was referenced Feb 4, 2026
Implement TeX's fraction and script alignment #31046
Merged
[ENH]: OpenType fonts for mathematical formulas #31104
Open
QuLogic
QuLogic reviewed Feb 7, 2026
View reviewed changes
Comment thread lib/matplotlib/_mathtext_data.py
0x22d3: 0x22d2,
}

unicode_math_lut: dict[str, dict[CharacterCodeType, CharacterCodeType]] = {

@QuLogic QuLogic Feb 7, 2026

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most of these are 1-to-1 mappings of a block; I wonder if there is a more compact representation that could be used? Something like:

# (start, end, new_start)
# up digits
(0x30, 0x39, 0x30),
...
# bf latin lower case
(0x61, 0x7a, 0x1d41a),

maybe plus a small dictionary with some of the exceptions, depending on how they fit into the blocks.

The lookup table could be generated from those if necessary.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@llohse llohse Feb 7, 2026

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is exactly the way I generated the lookup table, offline:

  1. map the entire range
  2. fix missing/moved codepoints based on a smaller lookup table

At some point I did consider writing special mapping functions but I figured that a lookup table might be preferable for performance.

Do you prefer to generate the lookup table (for example on module load) instead of hardcoding the entire table?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread lib/matplotlib/_mathtext_data.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread lib/matplotlib/_mathtext.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread lib/matplotlib/_mathtext.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread lib/matplotlib/_mathtext.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread lib/matplotlib/_mathtext.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread lib/matplotlib/_mathtext.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread lib/matplotlib/_mathtext.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread lib/matplotlib/_mathtext.py
}


class UnicodeMathFonts(TruetypeFonts):

@QuLogic QuLogic Feb 7, 2026

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIUC, math fonts should have tables with various layout metrics. We currently have those hard-coded in the various FontsConstantsBase subclasses, and they are likely incorrect for an arbitrary math font.

So this will likely need to parse this data out of the font and implement at least get_axis_height that was added in #31046, get_xheight maybe using #31050, and get_quad from #31110. But it is likely that you will want to refactor some of those remaining uses of the constants so that they fetch the information from the fonts as well.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@llohse llohse Feb 7, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I fully agree. Doing this may involve some refactoring though, because the FontsConstantsBase subclass could not be determined purely from fontname but would be dynamically populated based on the loaded OpenType font.

That said, I made some experiments locally. Unfortunately, Freetype does not parse the MATH table. We could use fonttools, which is a hard dependency anyway.
There are several open questions how to map the OpenType layout metrics to the legacy TeX-inspired variables used in mathtext. Does it make sense to postpone that to a separate PR and focus on the basics here?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@QuLogic QuLogic Mar 17, 2026

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While I would like it to happen, I'm not sure this will be ready for 3.11. So if we're aiming for 3.12, I think it's okay to spend some time getting everything worked out in separate PRs.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@llohse
llohse force-pushed the mathtext-unicode-textoverhaul branch from a8083e3 to 5988fac Compare February 8, 2026 08:09
@llohse
llohse force-pushed the mathtext-unicode-textoverhaul branch from 5988fac to 2a66d49 Compare March 10, 2026 07:46
@llohse

llohse commented Mar 10, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor Author

I have just rebased the branch, split the baseline images into a separate commit, and added logic to handle mathnormal from #31121 in the new UnicodeMathFonts class.

From my perspective, it is ready for another review.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

QuLogic
QuLogic reviewed Mar 17, 2026
View reviewed changes
Comment thread lib/matplotlib/_mathtext.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread lib/matplotlib/_mathtext.py

def __init__(self, default_font_prop: FontProperties, load_glyph_flags: LoadFlags):
TruetypeFonts.__init__(self, default_font_prop, load_glyph_flags)
prop = mpl.rcParams['mathtext.mathfont'] # type: ignore[index]

@QuLogic QuLogic Mar 17, 2026

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this will break the caching for MathTextParser._parse_cached, but I see it already doesn't really handle any of the other mathtext.* rcParams, so I guess that's something to work out at some point.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread lib/matplotlib/_mathtext.py Outdated
'tt': 'tt',
'sf': 'sfup',
'bf': 'bfup',
'bfit': 'bfup',

@QuLogic QuLogic Mar 17, 2026

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo or no such possibility? Probably could use a comment for the latter.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@llohse llohse Mar 18, 2026

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bold italic digits are not defined in the Unicode standard. Actually, neither are regular italic digits (which I need to fix in the code).
In unicode-math, both are mapped to their upright variants.

This is related to a bigger open question. unicode-math maps \mathit (and \mathbf, \mathrm, \mathsf and \mathtt) to the corresponding non-mathematics fonts, which support all alphanumeric symbols and apply normal text kerning rules (and ligatures).
I have not defined such a mechanism here to keep it simple. Mapping mathrm is trivial. In principle one could load italic and bold versions with their corresponding options, too. Sans serif and typewriter would be a bigger stretch. I am not sure it is worth it, though.
Note that this only matters because we now have proper italic support in the other font classes.

I see two options:

  1. simply map to their upright variants
  2. implement loading the corresponding text fonts and try to apply unicode-math's logic

I would prefer option 1 and leave option 2 as a project for the future.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread lib/matplotlib/_mathtext.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread lib/matplotlib/_mathtext.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread lib/matplotlib/_mathtext.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

@llohse
llohse force-pushed the mathtext-unicode-textoverhaul branch from 2a66d49 to 9455799 Compare March 18, 2026 09:34
@QuLogic

QuLogic commented Apr 10, 2026

Copy link
Copy Markdown
Member

Please rebase on, and re-target to, main now that the text-overhaul branch is merged.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@QuLogic QuLogic added the status: needs rebase label Apr 10, 2026
@github-actions github-actions Bot removed the status: needs rebase label Apr 10, 2026
llohse added 5 commits April 11, 2026 13:01
@llohse
mathtext: add basic support for OpenType mathematics fonts
d437313
Adds basic support for generic unicode OpenType mathematics fonts such as STIX Two Math or Cambria Math
to be used within the mathtext engine.
@llohse
add baseline images for testing the OpenType unicode math fonts
1037cb3
@llohse
bundle STIX Two Math in tests
38b00d5
@llohse
handle mathnormal
069859a
@llohse
revise comments
7e2dd3e
@llohse
llohse changed the base branch from text-overhaul to main April 11, 2026 11:01
@llohse
llohse force-pushed the mathtext-unicode-textoverhaul branch from 9455799 to 7e2dd3e Compare April 11, 2026 11:02
@llohse

llohse commented Apr 11, 2026

Copy link
Copy Markdown
Contributor Author

Rebased to main.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@anntzer anntzer mentioned this pull request May 18, 2026
with mathtext.fontset = "cm", \mathbf doesn't make *digits* bold #15338
Open
@github-actions github-actions Bot added the status: needs rebase label Aug 7, 2026
@QuLogic QuLogic mentioned this pull request Sep 5, 2026
[Bug]: Font lookup when rendering math text #32164
Closed
@llohse

llohse commented Oct 19, 2026

Copy link
Copy Markdown
Contributor Author

@QuLogic @anntzer @timhoffm I would like to follow up on this PR. Would you still be interested in merging basic OpenType math-font support along the lines implemented here? I would be happy to rebase it and address further comments. Before proceeding, though, I would appreciate your guidance on whether this approach is acceptable in principle and what would still be needed for a merge.

In particular, could support for OpenType MATH-table metrics be addressed in a subsequent PR, or would you consider it a prerequisite for merging this one?

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@QuLogic QuLogic QuLogic left review comments

At least 1 approving review is required to merge this pull request.

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

status: needs rebase topic: rcparams topic: text/mathtext

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

2 participants

@llohse @QuLogic
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.