Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit7901fee

Browse files
committed
Add font feature API to FontProperties and Text
Font features allow font designers to provide alternate glyphs orshaping within a single font. These features may be accessed via specialtags corresponding to internal tables of glyphs.The mplcairo backend supports font features via an elaborate re-use ofthe font file path [1]. This commit adds the API to make this officiallysupported in the main user API.At this time, nothing in Matplotlib itself uses these settings, butthey will have an effect with libraqm.[1]https://github.com/matplotlib/mplcairo/blob/v0.6.1/README.rst#font-formats-and-features
1 parentf017a0e commit7901fee

File tree

13 files changed

+189
-3
lines changed

13 files changed

+189
-3
lines changed
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
Specifying font feature tags
2+
----------------------------
3+
4+
OpenType fonts may support feature tags that specify alternate glyph shapes or
5+
substitutions to be made optionally. The text API now supports setting a list of feature
6+
tags to be used with the associated font. Feature tags can be set/get with:
7+
8+
- `matplotlib.text.Text.set_fontfeatures` / `matplotlib.text.Text.get_fontfeatures`
9+
- `matplotlib.font_manager.FontProperties.set_features` /
10+
`matplotlib.font_manager.FontProperties.get_features`
11+
- Any API that creates a `.Text` object by passing the *fontfeatures* argument (e.g.,
12+
``plt.xlabel(..., fontfeatures=...)``)
13+
14+
Font feature strings are eventually passed to HarfBuzz, and so all `string formats
15+
supported by hb_feature_from_string()
16+
<https://harfbuzz.github.io/harfbuzz-hb-common.html#hb-feature-from-string>`__ are
17+
supported.
18+
19+
For example, the default font ``DejaVu Sans`` enables Standard Ligatures (the ``'liga'``
20+
tag) by default, and also provides optional Discretionary Ligatures (the ``dlig`` tag.)
21+
These may be toggled with ``+`` or ``-``.
22+
23+
..plot::
24+
:include-source:
25+
26+
fig = plt.figure(figsize=(7, 3))
27+
28+
fig.text(0.5, 0.85, 'Ligatures', fontsize=40, horizontalalignment='center')
29+
30+
# Default has Standard Ligatures (liga).
31+
fig.text(0, 0.6, 'Default: fi ffi fl st', fontsize=40)
32+
33+
# Disable Standard Ligatures with -liga.
34+
fig.text(0, 0.35, 'Disabled: fi ffi fl st', fontsize=40,
35+
fontfeatures=['-liga'])
36+
37+
# Enable Discretionary Ligatures with dlig.
38+
fig.text(0, 0.1, 'Discretionary: fi ffi fl st', fontsize=40,
39+
fontfeatures=['dlig'])
40+
41+
Available font feature tags may be found at
42+
https://learn.microsoft.com/en-us/typography/opentype/spec/featurelist

‎lib/matplotlib/backends/_backend_pdf_ps.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ def _get_font_ttf(self, prop):
181181
font=font_manager.get_font(fnames)
182182
font.clear()
183183
font.set_size(prop.get_size_in_points(),72)
184+
font.set_features(prop.get_features())
184185
returnfont
185186
exceptRuntimeError:
186187
logging.getLogger(__name__).warning(

‎lib/matplotlib/backends/backend_agg.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,7 @@ def _prepare_font(self, font_prop):
253253
font.clear()
254254
size=font_prop.get_size_in_points()
255255
font.set_size(size,self.dpi)
256+
font.set_features(font_prop.get_features())
256257
returnfont
257258

258259
defpoints_to_pixels(self,points):

‎lib/matplotlib/font_manager.py

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -536,7 +536,7 @@ def afmFontProperty(fontpath, font):
536536

537537
def_cleanup_fontproperties_init(init_method):
538538
"""
539-
A decorator to limit the call signature tosingle a positional argument
539+
A decorator to limit the call signature toa single positional argument
540540
or alternatively only keyword arguments.
541541
542542
We still accept but deprecate all other call signatures.
@@ -624,6 +624,13 @@ class FontProperties:
624624
Supported values are: 'dejavusans', 'dejavuserif', 'cm',
625625
'stix', 'stixsans' and 'custom'. Default: :rc:`mathtext.fontset`
626626
627+
- features: A list of advanced font feature tags to enable. Font features are a
628+
component of OpenType fonts that allows picking from multiple stylistic variations
629+
within a single font. This may include small caps, ligatures, alternate forms of
630+
commonly-confused glyphs (e.g., capital I vs. lower-case l), and various other
631+
options. A `list of feature tags may be found here
632+
<https://learn.microsoft.com/en-us/typography/opentype/spec/featurelist>`__.
633+
627634
Alternatively, a font may be specified using the absolute path to a font
628635
file, by using the *fname* kwarg. However, in this case, it is typically
629636
simpler to just pass the path (as a `pathlib.Path`, not a `str`) to the
@@ -657,7 +664,8 @@ class FontProperties:
657664
def__init__(self,family=None,style=None,variant=None,weight=None,
658665
stretch=None,size=None,
659666
fname=None,# if set, it's a hardcoded filename to use
660-
math_fontfamily=None):
667+
math_fontfamily=None,
668+
features=None):
661669
self.set_family(family)
662670
self.set_style(style)
663671
self.set_variant(variant)
@@ -666,6 +674,7 @@ def __init__(self, family=None, style=None, variant=None, weight=None,
666674
self.set_file(fname)
667675
self.set_size(size)
668676
self.set_math_fontfamily(math_fontfamily)
677+
self.set_features(features)
669678
# Treat family as a fontconfig pattern if it is the only parameter
670679
# provided. Even in that case, call the other setters first to set
671680
# attributes not specified by the pattern to the rcParams defaults.
@@ -705,7 +714,8 @@ def __hash__(self):
705714
self.get_stretch(),
706715
self.get_size(),
707716
self.get_file(),
708-
self.get_math_fontfamily())
717+
self.get_math_fontfamily(),
718+
self.get_features())
709719
returnhash(l)
710720

711721
def__eq__(self,other):
@@ -952,6 +962,41 @@ def set_math_fontfamily(self, fontfamily):
952962
_api.check_in_list(valid_fonts,math_fontfamily=fontfamily)
953963
self._math_fontfamily=fontfamily
954964

965+
defget_features(self):
966+
"""Return a tuple of font feature tags to enable."""
967+
returnself._features
968+
969+
defset_features(self,features):
970+
"""
971+
Set the font feature tags to enable on this font.
972+
973+
Parameters
974+
----------
975+
features : list[str]
976+
A list of feature tags to be used with the associated font. These strings
977+
are eventually passed to HarfBuzz, and so all `string formats supported by
978+
hb_feature_from_string()
979+
<https://harfbuzz.github.io/harfbuzz-hb-common.html#hb-feature-from-string>`__
980+
are supported.
981+
982+
For example, if your desired font includes Stylistic Sets which enable
983+
various typographic alternates including one that you do not wish to use
984+
(e.g., Contextual Ligatures), then you can pass the following to enable one
985+
and not the other::
986+
987+
fp.set_features([
988+
'ss01', # Use Stylistic Set 1.
989+
'-clig', # But disable Contextural Ligatures.
990+
])
991+
992+
Available font feature tags may be found at
993+
https://learn.microsoft.com/en-us/typography/opentype/spec/featurelist
994+
"""
995+
_api.check_isinstance((list,tuple,None),features=features)
996+
iffeaturesisnotNone:
997+
features=tuple(features)
998+
self._features=features
999+
9551000
defcopy(self):
9561001
"""Return a copy of self."""
9571002
returncopy.copy(self)

‎lib/matplotlib/font_manager.pyi

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ class FontProperties:
5252
size:float|str|None= ...,
5353
fname:str|os.PathLike|Path|None= ...,
5454
math_fontfamily:str|None= ...,
55+
features:list[str]|None= ...,
5556
)->None: ...
5657
def__hash__(self)->int: ...
5758
def__eq__(self,other:object)->bool: ...
@@ -76,6 +77,8 @@ class FontProperties:
7677
defset_fontconfig_pattern(self,pattern:str)->None: ...
7778
defget_math_fontfamily(self)->str: ...
7879
defset_math_fontfamily(self,fontfamily:str|None)->None: ...
80+
defget_features(self)->tuple[str, ...]: ...
81+
defset_features(self,features:list[str]|tuple[str, ...]|None)->None: ...
7982
defcopy(self)->FontProperties: ...
8083
# Aliases
8184
set_name=set_family

‎lib/matplotlib/ft2font.pyi

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,7 @@ class FT2Font(Buffer):
234234
defload_glyph(self,glyphindex:int,flags:LoadFlags= ...)->Glyph: ...
235235
defselect_charmap(self,i:int)->None: ...
236236
defset_charmap(self,i:int)->None: ...
237+
defset_features(self,features:tuple[str, ...])->None: ...
237238
defset_size(self,ptsize:float,dpi:float)->None: ...
238239
defset_text(
239240
self,string:str,angle:float= ...,flags:LoadFlags= ...

‎lib/matplotlib/tests/test_ft2font.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,19 @@ def test_ft2font_set_size():
198198
assertfont.get_width_height()==tuple(pytest.approx(2*x,1e-1)forxinorig)
199199

200200

201+
deftest_ft2font_features():
202+
# Smoke test that these are accepted as intended.
203+
file=fm.findfont('DejaVu Sans')
204+
font=ft2font.FT2Font(file)
205+
font.set_features(None)# unset
206+
font.set_features(['calt','dlig'])# list
207+
font.set_features(('calt','dlig'))# tuple
208+
withpytest.raises(TypeError):
209+
font.set_features(123)
210+
withpytest.raises(TypeError):
211+
font.set_features([123,456])
212+
213+
201214
deftest_ft2font_charmaps():
202215
defenc(name):
203216
# We don't expose the encoding enum from FreeType, but can generate it here.

‎lib/matplotlib/text.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -847,6 +847,16 @@ def get_fontfamily(self):
847847
"""
848848
returnself._fontproperties.get_family()
849849

850+
defget_fontfeatures(self):
851+
"""
852+
Return a tuple of font feature tags to enable.
853+
854+
See Also
855+
--------
856+
.font_manager.FontProperties.get_features
857+
"""
858+
returnself._fontproperties.get_features()
859+
850860
defget_fontname(self):
851861
"""
852862
Return the font name as a string.
@@ -1094,6 +1104,39 @@ def set_fontfamily(self, fontname):
10941104
self._fontproperties.set_family(fontname)
10951105
self.stale=True
10961106

1107+
defset_fontfeatures(self,features):
1108+
"""
1109+
Set the feature tags to enable on the font.
1110+
1111+
Parameters
1112+
----------
1113+
features : list[str]
1114+
A list of feature tags to be used with the associated font. These strings
1115+
are eventually passed to HarfBuzz, and so all `string formats supported by
1116+
hb_feature_from_string()
1117+
<https://harfbuzz.github.io/harfbuzz-hb-common.html#hb-feature-from-string>`__
1118+
are supported.
1119+
1120+
For example, if your desired font includes Stylistic Sets which enable
1121+
various typographic alternates including one that you do not wish to use
1122+
(e.g., Contextual Ligatures), then you can pass the following to enable one
1123+
and not the other::
1124+
1125+
fp.set_features([
1126+
'ss01', # Use Stylistic Set 1.
1127+
'-clig', # But disable Contextural Ligatures.
1128+
])
1129+
1130+
Available font feature tags may be found at
1131+
https://learn.microsoft.com/en-us/typography/opentype/spec/featurelist
1132+
1133+
See Also
1134+
--------
1135+
.font_manager.FontProperties.set_features
1136+
"""
1137+
self._fontproperties.set_features(features)
1138+
self.stale=True
1139+
10971140
defset_fontvariant(self,variant):
10981141
"""
10991142
Set the font variant.

‎lib/matplotlib/text.pyi

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ class Text(Artist):
5656
defget_color(self)->ColorType: ...
5757
defget_fontproperties(self)->FontProperties: ...
5858
defget_fontfamily(self)->list[str]: ...
59+
defget_fontfeatures(self)->tuple[str, ...]|None: ...
5960
defget_fontname(self)->str: ...
6061
defget_fontstyle(self)->Literal["normal","italic","oblique"]: ...
6162
defget_fontsize(self)->float|str: ...
@@ -80,6 +81,7 @@ class Text(Artist):
8081
defset_multialignment(self,align:Literal["left","center","right"])->None: ...
8182
defset_linespacing(self,spacing:float)->None: ...
8283
defset_fontfamily(self,fontname:str|Iterable[str])->None: ...
84+
defset_fontfeatures(self,features:list[str]|tuple[str, ...]|None)->None: ...
8385
defset_fontvariant(self,variant:Literal["normal","small-caps"])->None: ...
8486
defset_fontstyle(
8587
self,fontstyle:Literal["normal","italic","oblique"]

‎lib/matplotlib/textpath.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ def _get_font(self, prop):
3434
filenames=_fontManager._find_fonts_by_props(prop)
3535
font=get_font(filenames)
3636
font.set_size(self.FONT_SCALE,self.DPI)
37+
font.set_features(prop.get_features())
3738
returnfont
3839

3940
def_get_hinting_flag(self):

‎src/ft2font.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,11 @@ void FT2Font::set_size(double ptsize, double dpi)
336336
}
337337
}
338338

339+
voidFT2Font::set_features(std::vector<std::string> features)
340+
{
341+
feature_tags =std::move(features);
342+
}
343+
339344
voidFT2Font::set_charmap(int i)
340345
{
341346
if (i >= face->num_charmaps) {

‎src/ft2font.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ class FT2Font
7575
virtual~FT2Font();
7676
voidclear();
7777
voidset_size(double ptsize,double dpi);
78+
voidset_features(std::vector<std::string> features);
7879
voidset_charmap(int i);
7980
voidselect_charmap(unsignedlong i);
8081
voidset_text(std::u32string_view codepoints,double angle, FT_Int32 flags,
@@ -150,6 +151,7 @@ class FT2Font
150151
FT_Pos advance;
151152
long hinting_factor;
152153
int kerning_factor;
154+
std::vector<std::string> feature_tags;
153155

154156
// prevent copying
155157
FT2Font(const FT2Font &);

‎src/ft2font_wrapper.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -534,6 +534,31 @@ PyFT2Font_set_size(PyFT2Font *self, double ptsize, double dpi)
534534
self->x->set_size(ptsize, dpi);
535535
}
536536

537+
constchar *PyFT2Font_set_features__doc__ =R"""(
538+
Set the font feature tags used for the font.
539+
540+
Parameters
541+
----------
542+
features : tuple[str, ...]
543+
The font feature tags to use for the font.
544+
545+
Available font feature tags may be found at
546+
https://learn.microsoft.com/en-us/typography/opentype/spec/featurelist
547+
)""";
548+
549+
staticvoid
550+
PyFT2Font_set_features(PyFT2Font *self, py::object features_obj)
551+
{
552+
std::vector<std::string> features;
553+
if (!features_obj.is_none()) {
554+
auto features_list = py::cast<py::tuple>(features_obj);
555+
for (auto &feature : features_list) {
556+
features.push_back(feature.cast<std::string>());
557+
}
558+
}
559+
self->x->set_features(std::move(features));
560+
}
561+
537562
constchar *PyFT2Font_set_charmap__doc__ =R"""(
538563
Make the i-th charmap current.
539564
@@ -1744,6 +1769,8 @@ PYBIND11_MODULE(ft2font, m, py::mod_gil_not_used())
17441769
.def("clear", &PyFT2Font_clear, PyFT2Font_clear__doc__)
17451770
.def("set_size", &PyFT2Font_set_size,"ptsize"_a,"dpi"_a,
17461771
PyFT2Font_set_size__doc__)
1772+
.def("set_features", &PyFT2Font_set_features,"features"_a,
1773+
PyFT2Font_set_features__doc__)
17471774
.def("set_charmap", &PyFT2Font_set_charmap,"i"_a,
17481775
PyFT2Font_set_charmap__doc__)
17491776
.def("select_charmap", &PyFT2Font_select_charmap,"i"_a,

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp