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 font feature API to Text by QuLogic · Pull Request #29695 · matplotlib/matplotlib

Conversation

@QuLogic

@QuLogic QuLogic commented Mar 1, 2025

Copy link
Copy Markdown
Member

PR summary

Font features allow font designers to provide alternate glyphs or shaping within a single font. These features may be accessed via special tags corresponding to internal tables of glyphs.

The mplcairo backend supports font features via an elaborate re-use of the font file path. This commit adds the API to make this officially supported in the main user API.

At this time, nothing in Matplotlib itself uses these settings, but they will have an effect with libraqm. I am opening this PR early for review of the API while I work through some issues with the latter. Consequently, the What's New note will not show the effect of this API, and there are only smoke tests.

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.

All reactions
@QuLogic QuLogic added the topic: text/fonts label Mar 1, 2025
@QuLogic QuLogic added this to the v3.11.0 milestone Mar 1, 2025
@github-actions github-actions Bot added topic: text backend: agg backend: ps backend: pdf labels Mar 1, 2025
@QuLogic
QuLogic force-pushed the font-features branch from ec912b1 to a5a8956 Compare March 1, 2025 09:52
@QuLogic

QuLogic commented Mar 1, 2025

Copy link
Copy Markdown
Member Author

The What's New entry with libraqm will look something more like this (minus a bug with the kerning, hopefully):
image

All reactions

Sorry, something went wrong.

Uh oh!

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

@anntzer

anntzer commented Mar 1, 2025

Copy link
Copy Markdown
Contributor

A difficulty that I've not really handled with mplcairo but warrants at least discussion is what you want to do with subranges. Harfbuzz supports toggling a feature only for some of the characters (see https://harfbuzz.github.io/harfbuzz-hb-common.html#hb-feature-from-string, e.g. aalt[3:5]=2), which mplcairo just forwards directly to harfbuzz, but this can(?) become problematic for multiline inputs, which get fed (by matplotlib) one-line-at-a-time to the rendering machinery, so something like aalt[3:5] likely(?) gets interpreted as "characters 3-to-5 of each line" rather than "characters 3-to-5 of the entire string".

The two main alternatives I can think of are either to do nothing, like mplcairo (subranges are interpreted as "repeated over each line"), or to reparse ranges and reinterpret them as "indices over the full string" (after line splitting, matplotlib tweaks the actual ranges than get fed to harfbuzz when shaping each line).

All reactions

Sorry, something went wrong.

Uh oh!

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

@anntzer

anntzer commented Mar 2, 2025

Copy link
Copy Markdown
Contributor

Also, looking at this again, I wonder how well this will interact with font fallback: it seems not unreasonable that two entries in the font fallback list would want to use different font features (e.g., a latin script and a chinese script likely want very different font features). Perhaps the real question here is whether font fallback should really have been implemented by stashing (references to) multiple fonts in a single FontProperties (though I'm not sure I can immediately design something much better), but the PR here makes the question more salient, I'd say.

All reactions

Sorry, something went wrong.

Uh oh!

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

@QuLogic QuLogic mentioned this pull request Mar 22, 2025
Add language parameter to Text objects #29794
Merged
1 of 4 tasks
@QuLogic

QuLogic commented Mar 22, 2025

Copy link
Copy Markdown
Member Author

Since we have automatic wrapping (Text(..., wrap=True)), I think the only possible implementation is to reparse the ranges ourselves.

For settings across font fallback, I guess that is possible to do, but it might require quite a bit of bookkeeping work.

All reactions

Sorry, something went wrong.

Uh oh!

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

anntzer
anntzer reviewed Mar 22, 2025
View reviewed changes
Comment thread src/ft2font_wrapper.cpp Resolved

Uh oh!

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

@anntzer

anntzer commented Mar 22, 2025

Copy link
Copy Markdown
Contributor

The comments at #29794 (comment) also apply, but because this PR doesn't actually touch the rendering API, I guess it's fine.

All reactions

Sorry, something went wrong.

Uh oh!

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

@QuLogic
QuLogic force-pushed the font-features branch from a5a8956 to 7901fee Compare April 17, 2025 08:50
tacaswell
tacaswell reviewed Apr 18, 2025
View reviewed changes
Comment thread lib/matplotlib/font_manager.pyi Resolved

Uh oh!

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

tacaswell
tacaswell reviewed Apr 18, 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 commented Apr 18, 2025

Copy link
Copy Markdown
Member

Discussed on a developer call we decided to not implement the sub-range application yet. Promoting a tuple of strings to Dict[tuple[int, int], Tuple[str, ...]] is something we can do unambiguously later and given that we expect most strings to be "short", hopefully the demand for mixed language within one Text object will be low.

All reactions

Sorry, something went wrong.

Uh oh!

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

@QuLogic QuLogic mentioned this pull request May 2, 2025
Implement text shaping with libraqm #30000
Merged
3 of 5 tasks
@QuLogic
QuLogic force-pushed the font-features branch from 7901fee to 0bcb662 Compare May 7, 2025 10:26
@anntzer

anntzer commented May 7, 2025

Copy link
Copy Markdown
Contributor

Even if we don't explicitly support sub-ranges, we still need to decide what happens if someone writes e.g. fontfeatures=["+kern[3:5]"] (where the whole syntax gets interpreted by harfbuzz). It's probably fine to just document the limitation for now ("the interaction of subranges and multiline text is currently unspecified and behavior may change in the future").

tacaswell and timhoffm reacted with thumbs up emoji
All reactions
  • 👍 2 reactions

Sorry, something went wrong.

Uh oh!

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

@QuLogic

QuLogic commented May 12, 2025

Copy link
Copy Markdown
Member Author

Since we moved the information from FontProperties to Text, I was actually going to implement the splitting. However, I ran into an issue in that the Text object is only supplied to Renderer.draw_text if the string is not multiline:

matplotlib/lib/matplotlib/text.py

Line 805 in b4cb934

mtext = self if len(info) == 1 else None

I tried a small change:

diff --git a/lib/matplotlib/text.py b/lib/matplotlib/text.py
index 3b0de58814..b255a93c52 100644
--- a/lib/matplotlib/text.py
+++ b/lib/matplotlib/text.py
@@ -800,9 +800,7 @@ class Text(Artist):
 
             angle = self.get_rotation()
 
             for line, wh, x, y in info:
-
-                mtext = self if len(info) == 1 else None
                 x = x + posx
                 y = y + posy
                 if renderer.flipy():
@@ -816,14 +814,19 @@ class Text(Artist):
                 else:
                     textrenderer = renderer
 
-                if self.get_usetex():
-                    textrenderer.draw_tex(gc, x, y, clean_line,
-                                          self._fontproperties, angle,
-                                          mtext=mtext)
-                else:
-                    textrenderer.draw_text(gc, x, y, clean_line,
-                                           self._fontproperties, angle,
-                                           ismath=ismath, mtext=mtext)
+                xt, yt = self.get_transform().inverted().transform((x, y))
+                with cbook._setattr_cm(self, _x=xt, _y=yt, _text=clean_line,
+                                       convert_xunits=lambda x: x,
+                                       convert_yunits=lambda y: y,
+                                       _horizontalalignment='left', _verticalalignment='bottom'):
+                    if self.get_usetex():
+                        textrenderer.draw_tex(gc, x, y, clean_line,
+                                              self._fontproperties, angle,
+                                              mtext=self)
+                    else:
+                        textrenderer.draw_text(gc, x, y, clean_line,
+                                               self._fontproperties, angle,
+                                               ismath=ismath, mtext=self)
 
         gc.restore()
         renderer.close_group('text')

AFAICT, only the PGF backend uses mtext and under certain conditions will place the text using the original position and alignment instead of the x/y passed to draw_text. Unfortunately, these don't seem to match (I assume the x/y passed in accounts for the descenders and other flourishes), so this breaks the PGF tests. I wonder if there's a better condition to be put in here:

matplotlib/lib/matplotlib/backends/backend_pgf.py

Lines 697 to 700 in 6db18d5

if mtext and (
(angle == 0 or
mtext.get_rotation_mode() == "anchor") and
mtext.get_verticalalignment() != "center_baseline"):

All reactions

Sorry, something went wrong.

Uh oh!

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

@QuLogic
QuLogic force-pushed the font-features branch from 0bcb662 to fbc4fbe Compare May 13, 2025 03:18
@github-actions github-actions Bot added status: needs rebase and removed status: needs rebase labels May 29, 2025
@QuLogic
QuLogic changed the base branch from main to text-overhaul June 5, 2025 01:38
@QuLogic QuLogic added this to Font and text overhaul Jun 5, 2025
@QuLogic QuLogic moved this to Waiting for other PR in Font and text overhaul Jun 5, 2025
@QuLogic QuLogic moved this from Waiting for other PR to Todo in Font and text overhaul Jun 5, 2025
@QuLogic QuLogic linked an issue Jul 1, 2025 that may be closed by this pull request
OTF feature support (alternate figure styles, etc.) #16566
Closed
@github-actions github-actions Bot removed status: needs rebase topic: mplot3d labels Sep 3, 2025
@github-actions github-actions Bot added the status: needs rebase label Sep 16, 2025
@QuLogic
QuLogic force-pushed the font-features branch from 34e7f8c to 2cb7a92 Compare September 20, 2025 04:22
@github-actions github-actions Bot removed topic: rcparams status: needs rebase labels Sep 20, 2025
@QuLogic QuLogic moved this from Waiting for other PR to Ready for Review in Font and text overhaul Sep 20, 2025
@github-actions github-actions Bot added the status: needs rebase label Sep 25, 2025
@QuLogic
QuLogic force-pushed the font-features branch from 2cb7a92 to c8704a3 Compare September 25, 2025 17:54
@github-actions github-actions Bot removed the status: needs rebase label Sep 25, 2025
tacaswell
tacaswell reviewed Sep 25, 2025
View reviewed changes
Comment thread lib/matplotlib/tests/test_text.py Outdated
@image_comparison(baseline_images=['features.png'], remove_text=False, style='mpl20')
def test_text_features():
fig = plt.figure(figsize=(5, 1.5))
t = fig.text(0, 0.7, 'Default: fi ffi fl st', fontsize=32)

@tacaswell tacaswell Sep 25, 2025

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.

Can you pad out the label strings to the same size so that the first letters of the example text line up? Does not matter for the machine to check, but makes it a bit easier for the humans to check the differences.

Sorry, something went wrong.

Uh oh!

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

All reactions

@QuLogic QuLogic Sep 25, 2025

Copy link
Copy Markdown
Member 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.

Do you want it aligned in the input text, or in the resulting image? If I align it in the input, then it's not quite right in the result because it's not monospace:
features
but I could change to right alignment, which would be close, I think?

Sorry, something went wrong.

Uh oh!

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

All reactions

@QuLogic QuLogic Sep 25, 2025

Copy link
Copy Markdown
Member 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.

Right alignment looks good, I think:
features

Sorry, something went wrong.

Uh oh!

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

tacaswell reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

@tacaswell tacaswell Sep 25, 2025

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 spend so much time looking at monospace fonts I forget not all fonts are monospace 🤦🏻 .

Right-align is exactly what I wanted.

Sorry, something went wrong.

Uh oh!

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

All reactions
tacaswell
tacaswell approved these changes Sep 25, 2025
View reviewed changes
@QuLogic
QuLogic force-pushed the font-features branch 2 times, most recently from b474c3e to 062b130 Compare September 26, 2025 02:40
@QuLogic QuLogic mentioned this pull request Sep 26, 2025
Implement libraqm for vector outputs #30607
Merged
1 of 5 tasks
ksunden
ksunden approved these changes Oct 2, 2025
View reviewed changes

@ksunden ksunden 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.

Approving but not merging because some image change squashing/interactions with other PRs can be handled by @QuLogic

Sorry, something went wrong.

Uh oh!

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

All reactions
@github-actions github-actions Bot added the status: needs rebase label Oct 2, 2025
@QuLogic
QuLogic force-pushed the font-features branch from 062b130 to d3cc8ce Compare October 3, 2025 01:43
@QuLogic
Add font feature API to FontProperties and Text
972a688
Font features allow font designers to provide alternate glyphs or
shaping within a single font. These features may be accessed via special
tags corresponding to internal tables of glyphs.

The mplcairo backend supports font features via an elaborate re-use of
the font file path [1]. This commit adds the API to make this officially
supported in the main user API.

[1] https://github.com/matplotlib/mplcairo/blob/v0.6.1/README.rst#font-formats-and-features
@QuLogic
QuLogic force-pushed the font-features branch from d3cc8ce to 972a688 Compare October 3, 2025 02:10
@github-actions github-actions Bot removed the status: needs rebase label Oct 3, 2025
@QuLogic

QuLogic commented Oct 3, 2025

Copy link
Copy Markdown
Member Author

Rebased without the image change (moved to text-overhaul-figures branch) so this can be merged. Going to ignore the linting error, as that's known #30626.

All reactions

Sorry, something went wrong.

Uh oh!

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

@QuLogic
QuLogic merged commit a6ac58f into matplotlib:text-overhaul Oct 3, 2025
3 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 Oct 3, 2025
@QuLogic
QuLogic deleted the font-features branch October 3, 2025 03:05
@story645 story645 mentioned this pull request Dec 24, 2025
[ENH]: Built-in support for multi-color and multi-style text. #30890
Open
@anntzer anntzer mentioned this pull request Mar 2, 2026
Should we use font metrics for line height instead of "lp"? #31220
Closed
wavebyrd pushed a commit to wavebyrd/matplotlib that referenced this pull request Mar 13, 2026
@QuLogic
Merge pull request matplotlib#29695 from QuLogic/font-features
b39a7dd
Add font feature API to Text
QuLogic added a commit to QuLogic/matplotlib that referenced this pull request Apr 10, 2026
@QuLogic
Update test images for font/text overhaul
12d2a4d
This includes images changes for the following pull requests / commits:

* [Fix center of rotation with
  rotation_mode='anchor'](matplotlib#29199)
  (c44db77)
* [Remove ttconv backwards-compatibility
  code](matplotlib#30145)
  (8caff88)
* [Remove kerning_factor from
  tests](matplotlib#29816)
  (7b4d725)
* [Set text hinting to
  defaults](matplotlib#29816)
  (8255ae2)
* [Update FreeType to
  2.13.3](matplotlib#29816)
  (89c054d)
* [Implement text shaping with
  libraqm](matplotlib#30000)
  (b0ded3a,
  9813523)
* [Add language parameter to Text
  objects](matplotlib#29794)
  (7ce8eae)
* [Fix auto-sized glyphs with BaKoMa
  fonts](matplotlib#29936)
  (3ba2c13)
* [pdf: Improve text with characters outside embedded font
  limits](matplotlib#30512)
  (b70fb88,
  6cedcf7)
* [Prepare `CharacterTracker` for advanced font
  features](matplotlib#30608)
  (8274e17,
  70dc388,
  df670cf,
  ed5e074)
* [Add font feature API to
  Text](matplotlib#29695)
  (972a688)
* [Fix spacing in r"$\max
  f$"](matplotlib#30715)
  (4a99a83)
* [Implement libraqm for vector
  outputs](matplotlib#30607)
  (bd17cd4)
* [Drop the FT2Font intermediate
  buffer](matplotlib#30059)
  (9d7d7b4)
* [Rasterize dvi files without
  dvipng](matplotlib#30039)
  (7627118)
* [Update bundled FreeType and HarfBuzz
  libraries](matplotlib#30938)
  (a161658,
  9619bcc)
* [Fix positioning of wide mathtext
  accents](matplotlib#31069)
  (c2fa7ba)
* [Refactor RendererAgg.draw_{mathtext,text,tex} to use same base
  algorithm](matplotlib#31085)
  (931bcf3)
* [Implement TeX's fraction and script
  alignment](matplotlib#31046)
  (94ff452,
  4bfa0f9,
  1cd8510)
* [Fix confusion between text height and ascent in metrics
  calculations](matplotlib#31107)
  (60f2310)
* [mathtext: Fetch quad width & axis height from font
  metrics](matplotlib#31110)
  (692df3f,
  383028b)
* [mathtext: add mathnormal and distinguish between normal and italic
  family](matplotlib#31121)
  (a6913f3)
* [ENH: Ignore empty text for
  tightbbox](matplotlib#31285)
  (d772043)
* [Drop axis_artist tickdir image compat, due to text-overhaul
  merge](matplotlib#31281)
  (2057583)
* [text: Use font metrics to determine line
  heights](matplotlib#31291)
  (3ab6a27,
  d961462,
  97f4943)
* [ps/pdf: Override font height metrics to support AFM
  files](matplotlib#31371)
  (e0913d4)
* [TST: Cleanup back-compat code in tests touched by text
  overhaul](matplotlib#31295)
  (7c33379)
* [TST: Set tests touched by text overhaul to mpl20
  style](matplotlib#31300)
  (41c4d8d)
@QuLogic QuLogic mentioned this pull request Apr 10, 2026
Font and text overhaul #30161
Merged
4 tasks done
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
@anntzer anntzer anntzer left review comments
@ksunden ksunden ksunden approved these changes

Uh oh!

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

Assignees

No one assigned

Labels

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

Projects

Font and text overhaul
Status: Done

Milestone

v3.11.0

Development

Successfully merging this pull request may close these issues.

OTF feature support (alternate figure styles, etc.)

Uh oh!

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

7 participants

@QuLogic @anntzer @tacaswell @timhoffm @story645 @khaledhosny @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 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.