-
-
Notifications
You must be signed in to change notification settings - Fork 8k
Add font feature API to Text #29695
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: text-overhaul
Are you sure you want to change the base?
Add font feature API to Text #29695
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
Specifying font feature tags | ||
---------------------------- | ||
|
||
OpenType fonts may support feature tags that specify alternate glyph shapes or | ||
substitutions to be made optionally. The text API now supports setting a list of feature | ||
tags to be used with the associated font. Feature tags can be set/get with: | ||
|
||
- `matplotlib.text.Text.set_fontfeatures` / `matplotlib.text.Text.get_fontfeatures` | ||
- Any API that creates a `.Text` object by passing the *fontfeatures* argument (e.g., | ||
``plt.xlabel(..., fontfeatures=...)``) | ||
|
||
Font feature strings are eventually passed to HarfBuzz, and so all `string formats | ||
supported by hb_feature_from_string() | ||
<https://harfbuzz.github.io/harfbuzz-hb-common.html#hb-feature-from-string>`__ are | ||
supported. Note though that subranges are not explicitly supported and behaviour may | ||
change in the future. | ||
|
||
For example, the default font ``DejaVu Sans`` enables Standard Ligatures (the ``'liga'`` | ||
tag) by default, and also provides optional Discretionary Ligatures (the ``dlig`` tag.) | ||
These may be toggled with ``+`` or ``-``. | ||
|
||
.. plot:: | ||
:include-source: | ||
|
||
fig = plt.figure(figsize=(7, 3)) | ||
|
||
fig.text(0.5, 0.85, 'Ligatures', fontsize=40, horizontalalignment='center') | ||
|
||
# Default has Standard Ligatures (liga). | ||
fig.text(0, 0.6, 'Default: fi ffi fl st', fontsize=40) | ||
|
||
# Disable Standard Ligatures with -liga. | ||
fig.text(0, 0.35, 'Disabled: fi ffi fl st', fontsize=40, | ||
fontfeatures=['-liga']) | ||
|
||
# Enable Discretionary Ligatures with dlig. | ||
fig.text(0, 0.1, 'Discretionary: fi ffi fl st', fontsize=40, | ||
fontfeatures=['dlig']) | ||
|
||
Available font feature tags may be found at | ||
https://learn.microsoft.com/en-us/typography/opentype/spec/featurelist |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -136,6 +136,7 @@ def __init__(self, | |
super().__init__() | ||
self._x, self._y = x, y | ||
self._text = '' | ||
self._features = None | ||
self._reset_visual_defaults( | ||
text=text, | ||
color=color, | ||
|
@@ -847,6 +848,12 @@ def get_fontfamily(self): | |
""" | ||
return self._fontproperties.get_family() | ||
|
||
def get_fontfeatures(self): | ||
""" | ||
Return a tuple of font feature tags to enable. | ||
""" | ||
return self._features | ||
|
||
def get_fontname(self): | ||
""" | ||
Return the font name as a string. | ||
|
@@ -1094,6 +1101,39 @@ def set_fontfamily(self, fontname): | |
self._fontproperties.set_family(fontname) | ||
self.stale = True | ||
|
||
def set_fontfeatures(self, features): | ||
""" | ||
Set the feature tags to enable on the font. | ||
|
||
Parameters | ||
---------- | ||
features : list of str, or tuple of str, or None | ||
A list of feature tags to be used with the associated font. These strings | ||
are eventually passed to HarfBuzz, and so all `string formats supported by | ||
hb_feature_from_string() | ||
<https://harfbuzz.github.io/harfbuzz-hb-common.html#hb-feature-from-string>`__ | ||
are supported. Note though that subranges are not explicitly supported and | ||
behaviour may change in the future. | ||
|
||
For example, if your desired font includes Stylistic Sets which enable | ||
various typographic alternates including one that you do not wish to use | ||
(e.g., Contextual Ligatures), then you can pass the following to enable one | ||
and not the other:: | ||
|
||
fp.set_features([ | ||
'ss01', # Use Stylistic Set 1. | ||
'-clig', # But disable Contextural Ligatures. | ||
]) | ||
|
||
Available font feature tags may be found at | ||
https://learn.microsoft.com/en-us/typography/opentype/spec/featurelist | ||
""" | ||
_api.check_isinstance((list, tuple, None), features=features) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Slightly restrictive to have only list and tuple, but not actually too offended. This is a pretty niche feature, so probably fine, but in most cases I would tend to think "accept any Sequence", but that is mostly just a gut reaction. |
||
if features is not None: | ||
features = tuple(features) | ||
self._features = features | ||
self.stale = True | ||
|
||
def set_fontvariant(self, variant): | ||
""" | ||
Set the font variant. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -303,7 +303,9 @@ void FT2Font::set_kerning_factor(int factor) | |
} | ||
|
||
void FT2Font::set_text( | ||
std::u32string_view text, double angle, FT_Int32 flags, std::vector<double> &xys) | ||
std::u32string_view text, double angle, FT_Int32 flags, | ||
std::optional<std::vector<std::string>> features, // TODO: Apply features with libraqm. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same question, is the TODO intended to have been resolved here? (This one I think more explicitly looks like it requires the actual libraqm changes, but checking) |
||
std::vector<double> &xys) | ||
{ | ||
FT_Matrix matrix; /* transformation matrix */ | ||
|
||
|
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.
is this TODO intentionally left for a followup PR or is it supposed to be resolved here?