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 — Add support for loading all fonts from collections by QuLogic · Pull Request #30334 · matplotlib/matplotlib

Conversation

@QuLogic

@QuLogic QuLogic commented Jul 19, 2025 •
edited
Loading

Uh oh!

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

Copy link
Copy Markdown
Member

PR summary

This turned out to be more straightforward than I expected, but it will probably need a few API decisions to be fully complete.

From bottom to top of the API:

  1. FT2Font accepts a face_index parameter to specify which face to load in a collection, and a corresponding face_index property to check what's loaded.
  2. For backwards-compatibility, FontManager.findfont returns a str-like class FontPath (name up for debate) which has a face_index attribute and is accepted by get_font. If anyone uses them as strings though, it should act pretty much the same.

For example, now I can see all variants of Noto Sans CJK:

>>> import matplotlib.font_manager
>>> for fe in matplotlib.font_manager.fontManager.ttflist:
...     if fe.name.startswith('Noto Sans Mono CJK'):
...         print(fe)
...         
FontEntry(fname='/usr/share/fonts/google-noto-sans-mono-cjk-vf-fonts/NotoSansMonoCJK-VF.ttc', index=0, name='Noto Sans Mono CJK JP', style='normal', variant='normal', weight=400, stretch='normal', size='scalable')
FontEntry(fname='/usr/share/fonts/google-noto-sans-mono-cjk-vf-fonts/NotoSansMonoCJK-VF.ttc', index=1, name='Noto Sans Mono CJK KR', style='normal', variant='normal', weight=400, stretch='normal', size='scalable')
FontEntry(fname='/usr/share/fonts/google-noto-sans-mono-cjk-vf-fonts/NotoSansMonoCJK-VF.ttc', index=2, name='Noto Sans Mono CJK SC', style='normal', variant='normal', weight=400, stretch='normal', size='scalable')
FontEntry(fname='/usr/share/fonts/google-noto-sans-mono-cjk-vf-fonts/NotoSansMonoCJK-VF.ttc', index=3, name='Noto Sans Mono CJK TC', style='normal', variant='normal', weight=400, stretch='normal', size='scalable')
FontEntry(fname='/usr/share/fonts/google-noto-sans-mono-cjk-vf-fonts/NotoSansMonoCJK-VF.ttc', index=4, name='Noto Sans Mono CJK HK', style='normal', variant='normal', weight=400, stretch='normal', size='scalable')
FontEntry(fname='/usr/share/fonts/google-noto-sans-cjk-fonts/NotoSansCJK-Regular.ttc', index=5, name='Noto Sans Mono CJK JP', style='normal', variant='normal', weight=400, stretch='normal', size='scalable')
FontEntry(fname='/usr/share/fonts/google-noto-sans-cjk-fonts/NotoSansCJK-Regular.ttc', index=6, name='Noto Sans Mono CJK KR', style='normal', variant='normal', weight=400, stretch='normal', size='scalable')
FontEntry(fname='/usr/share/fonts/google-noto-sans-cjk-fonts/NotoSansCJK-Regular.ttc', index=7, name='Noto Sans Mono CJK SC', style='normal', variant='normal', weight=400, stretch='normal', size='scalable')
FontEntry(fname='/usr/share/fonts/google-noto-sans-cjk-fonts/NotoSansCJK-Regular.ttc', index=8, name='Noto Sans Mono CJK TC', style='normal', variant='normal', weight=400, stretch='normal', size='scalable')
FontEntry(fname='/usr/share/fonts/google-noto-sans-cjk-fonts/NotoSansCJK-Regular.ttc', index=9, name='Noto Sans Mono CJK HK', style='normal', variant='normal', weight=400, stretch='normal', size='scalable')
FontEntry(fname='/usr/share/fonts/google-noto-sans-cjk-fonts/NotoSansCJK-Bold.ttc', index=5, name='Noto Sans Mono CJK JP', style='normal', variant='normal', weight=700, stretch='normal', size='scalable')
FontEntry(fname='/usr/share/fonts/google-noto-sans-cjk-fonts/NotoSansCJK-Bold.ttc', index=6, name='Noto Sans Mono CJK KR', style='normal', variant='normal', weight=700, stretch='normal', size='scalable')
FontEntry(fname='/usr/share/fonts/google-noto-sans-cjk-fonts/NotoSansCJK-Bold.ttc', index=7, name='Noto Sans Mono CJK SC', style='normal', variant='normal', weight=700, stretch='normal', size='scalable')
FontEntry(fname='/usr/share/fonts/google-noto-sans-cjk-fonts/NotoSansCJK-Bold.ttc', index=8, name='Noto Sans Mono CJK TC', style='normal', variant='normal', weight=700, stretch='normal', size='scalable')
FontEntry(fname='/usr/share/fonts/google-noto-sans-cjk-fonts/NotoSansCJK-Bold.ttc', index=9, name='Noto Sans Mono CJK HK', style='normal', variant='normal', weight=700, stretch='normal', size='scalable')

or all variants of WenQuanYi that we use for tests:

>>> for fe in matplotlib.font_manager.fontManager.ttflist:
...     if fe.name.startswith('WenQuan'):
...         print(fe)
...         
FontEntry(fname='/usr/share/fonts/wqy-zenhei-fonts/wqy-zenhei.ttc', index=0, name='WenQuanYi Zen Hei', style='normal', variant='normal', weight=500, stretch='normal', size='scalable')
FontEntry(fname='/usr/share/fonts/wqy-zenhei-fonts/wqy-zenhei.ttc', index=1, name='WenQuanYi Zen Hei Mono', style='normal', variant='normal', weight=500, stretch='normal', size='scalable')
FontEntry(fname='/usr/share/fonts/wqy-zenhei-fonts/wqy-zenhei.ttc', index=2, name='WenQuanYi Zen Hei Sharp', style='normal', variant='normal', weight=500, stretch='normal', size='scalable')

Fixes #3135

TODO

  1. Font subsetting in vector formats still hard-codes face index 0. This is mostly because it passes around filenames everywhere, so that will have to be changed to include the face index.
  2. As you can see above, there are two versions of Noto Sans CJK that are found: one for each weight and a variable font. We don't seem to correctly read any differentiating metadata for the variable font, so whether it or the separate Regular version gets picked is a bit up in the air, I think.

API questions

  1. Considering the typing is quite long with str | bytes | Path, I wonder if we should change to os.PathLike?
  2. I originally started with the path-index tuple as an end goal, but after creating the backwards-compatible FontPath, I'm thinking maybe that's redundant and we should just stick with the FontPath class only. Do we want to accept the tuple form as well, or should I drop it?
  3. Currently, FontPath is a subclass of str which allows using it as a str as one normally would. That was the minimum implementation needed, but we probably want to flesh that out a bit. At minimum, I think we should implement __eq__ and __hash__ so that you can use it as a dictionary key without clashing with an equivalent str. But then do we want to add a deprecation warning when making those comparisons? And after thinking about it a bit more, would a namedtuple with __eq__ instead be a better choice?
  4. Do we want to figure out a way to group these somehow (in a way something like Add font.superfamily support with genre-aware resolution #30155)? Unfortunately, I think this is actually non-trivial. On Fedora for example, the Noto Sans package installs a fontconfig file that groups them and specifies which language corresponds to which font. It is likely not something embedded in the font that we can read ourselves, and we don't use fontconfig either.

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.

YDX-2147483647 reacted with thumbs up emoji
All reactions
  • 👍 1 reaction
@QuLogic QuLogic added this to the v3.11.0 milestone Jul 19, 2025
@QuLogic QuLogic added the status: needs comment/discussion needs consensus on next step label Jul 19, 2025
@QuLogic QuLogic added this to Font and text overhaul Jul 19, 2025
@github-project-automation github-project-automation Bot moved this to Waiting for other PR in Font and text overhaul Jul 19, 2025
@github-actions github-actions Bot added the topic: text/fonts label Jul 19, 2025
@QuLogic
QuLogic force-pushed the ttc-loading branch from 4133fba to 856e95f Compare July 19, 2025 05:45
@QuLogic QuLogic moved this from Waiting for other PR to Ready for Review in Font and text overhaul Jul 19, 2025
@github-actions github-actions Bot added the status: needs rebase label Jul 24, 2025
@QuLogic

QuLogic commented Jul 25, 2025

Copy link
Copy Markdown
Member Author

We had a discussion about this on the call earlier.

  • I originally started with the path-index tuple as an end goal, but after creating the backwards-compatible FontPath, I'm thinking maybe that's redundant and we should just stick with the FontPath class only. Do we want to accept the tuple form as well, or should I drop it?

We'll drop the tuple for now, as it seems the str-ish type should be fine.

  • Currently, FontPath is a subclass of str which allows using it as a str as one normally would. That was the minimum implementation needed, but we probably want to flesh that out a bit. At minimum, I think we should implement __eq__ and __hash__ so that you can use it as a dictionary key without clashing with an equivalent str. But then do we want to add a deprecation warning when making those comparisons? And after thinking about it a bit more, would a namedtuple with __eq__ instead be a better choice?

We'll not deprecate anything for now, and see how this works out. Since FontPath is fairly equivalent to str, there likely won't be much in the way of breakage. For methods, we'll implement at least __eq__, __hash__ and __repr__.

  • Do we want to figure out a way to group these somehow

Will leave this for later, if necessary.

All reactions

Sorry, something went wrong.

Uh oh!

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

@QuLogic
QuLogic force-pushed the ttc-loading branch from 856e95f to 2cf627b Compare July 25, 2025 10:47
@github-actions github-actions Bot added backend: ps backend: pdf and removed status: needs rebase labels Jul 25, 2025
@QuLogic
QuLogic force-pushed the ttc-loading branch from 2cf627b to 1bc92f0 Compare July 25, 2025 11:02
@QuLogic
QuLogic marked this pull request as ready for review July 25, 2025 11:02
@QuLogic QuLogic removed the status: needs comment/discussion needs consensus on next step label Jul 25, 2025
@QuLogic

QuLogic commented Jul 25, 2025

Copy link
Copy Markdown
Member Author

This should work almost everywhere now, I think. The only thing that doesn't support the face index is LaTeX, via PGF or DviFont (which just hard-codes index 0). I think the latter can be covered by the refactors in #29939

All reactions

Sorry, something went wrong.

Uh oh!

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

@QuLogic

QuLogic commented Jul 25, 2025

Copy link
Copy Markdown
Member Author

Also, tests use the WenQuanYi Zen Hei that we already conditionally use, but maybe we should generate a minimal .ttc file for testing instead.

All reactions

Sorry, something went wrong.

Uh oh!

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

@github-actions github-actions Bot added the backend: pgf label Jul 25, 2025
@QuLogic QuLogic mentioned this pull request Jul 26, 2025
[Bug]: Recent test_determinism_check flakiness #30360
Open
@github-actions github-actions Bot added the status: needs rebase label Aug 7, 2025
@QuLogic QuLogic moved this from Ready for Review to Waiting for other PR in Font and text overhaul Sep 3, 2025
@QuLogic
QuLogic marked this pull request as draft September 3, 2025 03:49
This was referenced Sep 17, 2025
Support TTC fonts by converting them to TTF in font cache #23293
Closed
Add os.PathLike support to FT2Font constructor, and FontManager #30573
Merged
@QuLogic
QuLogic force-pushed the ttc-loading branch from d86660b to 4865a9b Compare September 26, 2025 04:26
@github-actions github-actions Bot removed the status: needs rebase label Sep 26, 2025
@QuLogic QuLogic linked an issue Sep 26, 2025 that may be closed by this pull request
Please add support for ttc font files (PDF/PS output) #3135
Closed
@QuLogic

QuLogic commented Sep 26, 2025

Copy link
Copy Markdown
Member Author

I've rebased now that I think there should not be any other PRs that might cause conficts.

All reactions

Sorry, something went wrong.

Uh oh!

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

@QuLogic
QuLogic marked this pull request as ready for review September 26, 2025 04:47
@github-actions github-actions Bot removed the status: needs rebase label Oct 3, 2025
@QuLogic
QuLogic force-pushed the ttc-loading branch from 12394ac to b9fbfc4 Compare October 3, 2025 09:56
tacaswell
tacaswell reviewed Oct 16, 2025
View reviewed changes
Comment thread lib/matplotlib/font_manager.py Resolved

Uh oh!

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

@QuLogic
QuLogic force-pushed the ttc-loading branch 2 times, most recently from e751dc2 to a32ad5b Compare October 23, 2025 08:33
@QuLogic QuLogic added the status: needs review label Oct 24, 2025
@QuLogic
QuLogic force-pushed the ttc-loading branch 2 times, most recently from 44f03ea to 1a2bba2 Compare October 31, 2025 08:33
@QuLogic

QuLogic commented Oct 31, 2025

Copy link
Copy Markdown
Member Author

Just realized I didn't have any What's new docs here, so I've added a note.

All reactions

Sorry, something went wrong.

Uh oh!

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

tacaswell
tacaswell reviewed Oct 31, 2025
View reviewed changes
Comment thread src/ft2font.cpp Resolved

Uh oh!

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

tacaswell
tacaswell reviewed Oct 31, 2025
View reviewed changes
Comment thread src/ft2font_wrapper.cpp Resolved

Uh oh!

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

tacaswell
tacaswell approved these changes Oct 31, 2025
View reviewed changes

@tacaswell tacaswell left a comment

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 have one minor quibble about c++ function signatures, not critical.

Sorry, something went wrong.

Uh oh!

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

All reactions
@QuLogic
QuLogic force-pushed the ttc-loading branch from 1a2bba2 to 6521ddc Compare October 31, 2025 20:59
@QuLogic QuLogic mentioned this pull request Nov 4, 2025
Add support for (some) colour fonts #30725
Open
5 tasks
@github-actions github-actions Bot added the status: needs rebase label Nov 20, 2025
QuLogic added 7 commits November 20, 2025 15:46
@QuLogic
Expose face index when loading fonts
daae68a
This enables loading a non-initial font from collections (`.ttc` files).
Currently exposed for `FT2Font`, only.
@QuLogic
Parse data from all fonts within a collection
b839f87
This should allow listing the metadata from the whole collection, which
will also pick the right one if specified, though it will not load the
specific index yet.
@QuLogic
Implement loading of any font in a collection
6d2ae67
For backwards-compatibility, the path+index is passed around in a
lightweight subclass of `str`.
@QuLogic
pdf/ps: Support any font in a collection
ea80bbb
@QuLogic
pgf: Support any font in a collection
e991790
Note, this only has an effect if set as the global font. Otherwise, just
the font name is recorded, and the TeX engine's normal lookup is
performed.
@QuLogic
DOC: Add what's new note for TTC loading
9c20b0c
@QuLogic
Update test images for previous changes
b2aa1f2
@QuLogic
QuLogic force-pushed the ttc-loading branch from 6521ddc to b2aa1f2 Compare November 20, 2025 20:51
@github-actions github-actions Bot removed the status: needs rebase label Nov 20, 2025
@ksunden
ksunden merged commit e84ce2b into matplotlib:text-overhaul Dec 4, 2025
34 of 36 checks passed

Uh oh!

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

@github-project-automation github-project-automation Bot moved this from Ready for Review to Done in Font and text overhaul Dec 4, 2025
@QuLogic
QuLogic deleted the ttc-loading branch December 4, 2025 21:03
@QuLogic QuLogic removed the status: needs review label Dec 4, 2025
wavebyrd pushed a commit to wavebyrd/matplotlib that referenced this pull request Mar 13, 2026
@ksunden
Merge pull request matplotlib#30334 from QuLogic/ttc-loading
a6bdfa6
Add support for loading all fonts from collections
YDX-2147483647 added a commit to YDX-2147483647/mirrorz-docs that referenced this pull request Apr 11, 2026
@YDX-2147483647
记录 matplotlib 版本,因为预计下个版本 3.11.0 会支持 OTC
aa4f222
matplotlib/matplotlib#30334
https://matplotlib.org/devdocs/release/next_whats_new/ttc_fonts.html
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@tacaswell tacaswell tacaswell approved these changes

Uh oh!

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

Assignees

No one assigned

Labels

backend: pdf backend: pgf backend: ps topic: text/fonts

Projects

Font and text overhaul
Status: Done

Milestone

v3.11.0

Development

Successfully merging this pull request may close these issues.

Please add support for ttc font files (PDF/PS output)

Uh oh!

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

3 participants

@QuLogic @tacaswell @ksunden
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 01402520 — 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.