diff --git a/doc/release/next_whats_new/text_language.rst b/doc/release/next_whats_new/text_language.rst
new file mode 100644
index 000000000000..d79510ae61a4
--- /dev/null
+++ b/doc/release/next_whats_new/text_language.rst
@@ -0,0 +1,37 @@
+Specifying text language
+------------------------
+
+OpenType fonts may support language systems which can be used to select different
+typographic conventions, e.g., localized variants of letters that share a single Unicode
+code point, or different default font features. The text API now supports setting a
+language to be used and may be set/get with:
+
+- `matplotlib.text.Text.set_language` / `matplotlib.text.Text.get_language`
+- Any API that creates a `.Text` object by passing the *language* argument (e.g.,
+ ``plt.xlabel(..., language=...)``)
+
+The language of the text must be in a format accepted by libraqm, namely `a BCP47
+language code `_. If None or
+unset, then no particular language will be implied, and default font settings will be
+used.
+
+For example, Matplotlib's default font ``DejaVu Sans`` supports language-specific glyphs
+in the Serbian and Macedonian languages in the Cyrillic alphabet, or the Sámi family of
+languages in the Latin alphabet.
+
+.. plot::
+ :include-source:
+
+ fig = plt.figure(figsize=(7, 3))
+
+ char = '\U00000431'
+ fig.text(0.5, 0.8, f'\\U{ord(char):08x}', fontsize=40, horizontalalignment='center')
+ fig.text(0, 0.6, f'Serbian: {char}', fontsize=40, language='sr')
+ fig.text(1, 0.6, f'Russian: {char}', fontsize=40, language='ru',
+ horizontalalignment='right')
+
+ char = '\U0000014a'
+ fig.text(0.5, 0.3, f'\\U{ord(char):08x}', fontsize=40, horizontalalignment='center')
+ fig.text(0, 0.1, f'English: {char}', fontsize=40, language='en')
+ fig.text(1, 0.1, f'Inari Sámi: {char}', fontsize=40, language='smn',
+ horizontalalignment='right')
diff --git a/lib/matplotlib/_text_helpers.py b/lib/matplotlib/_text_helpers.py
index a874c8f4bf81..bb4c50f34a25 100644
--- a/lib/matplotlib/_text_helpers.py
+++ b/lib/matplotlib/_text_helpers.py
@@ -26,7 +26,7 @@ def warn_on_missing_glyph(codepoint, fontnames):
f"missing from font(s) {fontnames}.")
-def layout(string, font, *, kern_mode=Kerning.DEFAULT):
+def layout(string, font, *, kern_mode=Kerning.DEFAULT, language=None):
"""
Render *string* with *font*.
@@ -41,6 +41,9 @@ def layout(string, font, *, kern_mode=Kerning.DEFAULT):
The font.
kern_mode : Kerning
A FreeType kerning mode.
+ language : str, optional
+ The language of the text in a format accepted by libraqm, namely `a BCP47
+ language code `_.
Yields
------
@@ -48,7 +51,7 @@ def layout(string, font, *, kern_mode=Kerning.DEFAULT):
"""
x = 0
prev_glyph_idx = None
- char_to_font = font._get_fontmap(string)
+ char_to_font = font._get_fontmap(string) # TODO: Pass in language.
base_font = font
for char in string:
# This has done the fallback logic
diff --git a/lib/matplotlib/backends/backend_agg.py b/lib/matplotlib/backends/backend_agg.py
index feb4b0c8be01..2da422a88e84 100644
--- a/lib/matplotlib/backends/backend_agg.py
+++ b/lib/matplotlib/backends/backend_agg.py
@@ -190,7 +190,8 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None):
font = self._prepare_font(prop)
# We pass '0' for angle here, since it will be rotated (in raster
# space) in the following call to draw_text_image).
- font.set_text(s, 0, flags=get_hinting_flag())
+ font.set_text(s, 0, flags=get_hinting_flag(),
+ language=mtext.get_language() if mtext is not None else None)
font.draw_glyphs_to_bitmap(
antialiased=gc.get_antialiased())
d = font.get_descent() / 64.0
diff --git a/lib/matplotlib/backends/backend_pdf.py b/lib/matplotlib/backends/backend_pdf.py
index ff351e301176..2bf0c2c4bd4b 100644
--- a/lib/matplotlib/backends/backend_pdf.py
+++ b/lib/matplotlib/backends/backend_pdf.py
@@ -2345,6 +2345,7 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None):
return self.draw_mathtext(gc, x, y, s, prop, angle)
fontsize = prop.get_size_in_points()
+ language = mtext.get_language() if mtext is not None else None
if mpl.rcParams['pdf.use14corefonts']:
font = self._get_font_afm(prop)
@@ -2355,7 +2356,7 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None):
fonttype = mpl.rcParams['pdf.fonttype']
if gc.get_url() is not None:
- font.set_text(s)
+ font.set_text(s, language=language)
width, height = font.get_width_height()
self.file._annotations[-1][1].append(_get_link_annotation(
gc, x, y, width / 64, height / 64, angle))
@@ -2389,7 +2390,8 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None):
multibyte_glyphs = []
prev_was_multibyte = True
prev_font = font
- for item in _text_helpers.layout(s, font, kern_mode=Kerning.UNFITTED):
+ for item in _text_helpers.layout(s, font, kern_mode=Kerning.UNFITTED,
+ language=language):
if _font_supports_glyph(fonttype, ord(item.char)):
if prev_was_multibyte or item.ft_object != prev_font:
singlebyte_chunks.append((item.ft_object, item.x, []))
diff --git a/lib/matplotlib/backends/backend_ps.py b/lib/matplotlib/backends/backend_ps.py
index 368564a1518d..a3ba63ccf079 100644
--- a/lib/matplotlib/backends/backend_ps.py
+++ b/lib/matplotlib/backends/backend_ps.py
@@ -794,9 +794,10 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None):
thisx += width * scale
else:
+ language = mtext.get_language() if mtext is not None else None
font = self._get_font_ttf(prop)
self._character_tracker.track(font, s)
- for item in _text_helpers.layout(s, font):
+ for item in _text_helpers.layout(s, font, language=language):
ps_name = (item.ft_object.postscript_name
.encode("ascii", "replace").decode("ascii"))
glyph_name = item.ft_object.get_glyph_name(item.glyph_idx)
diff --git a/lib/matplotlib/ft2font.pyi b/lib/matplotlib/ft2font.pyi
index 55c076bb68b6..91d8d6a38818 100644
--- a/lib/matplotlib/ft2font.pyi
+++ b/lib/matplotlib/ft2font.pyi
@@ -243,7 +243,12 @@ class FT2Font(Buffer):
def set_charmap(self, i: int) -> None: ...
def set_size(self, ptsize: float, dpi: float) -> None: ...
def set_text(
- self, string: str, angle: float = ..., flags: LoadFlags = ...
+ self,
+ string: str,
+ angle: float = ...,
+ flags: LoadFlags = ...,
+ *,
+ language: str | list[tuple[str, int, int]] | None = ...,
) -> NDArray[np.float64]: ...
@property
def ascender(self) -> int: ...
diff --git a/lib/matplotlib/mpl-data/matplotlibrc b/lib/matplotlib/mpl-data/matplotlibrc
index 223eed396535..66a2569ca6f7 100644
--- a/lib/matplotlib/mpl-data/matplotlibrc
+++ b/lib/matplotlib/mpl-data/matplotlibrc
@@ -292,6 +292,11 @@
## for more information on text properties
#text.color: black
+## The language of the text in a format accepted by libraqm, namely `a BCP47 language
+## code `_. If None, then no
+## particular language will be implied, and default font settings will be used.
+#text.language: None
+
## FreeType hinting flag ("foo" corresponds to FT_LOAD_FOO); may be one of the
## following (Proprietary Matplotlib-specific synonyms are given in parentheses,
## but their use is discouraged):
diff --git a/lib/matplotlib/rcsetup.py b/lib/matplotlib/rcsetup.py
index b4224d169815..586365dcf3f2 100644
--- a/lib/matplotlib/rcsetup.py
+++ b/lib/matplotlib/rcsetup.py
@@ -1045,6 +1045,7 @@ def _convert_validator_spec(key, conv):
"text.kerning_factor": validate_int_or_None,
"text.antialiased": validate_bool,
"text.parse_math": validate_bool,
+ "text.language": validate_string_or_None,
"mathtext.cal": validate_font_properties,
"mathtext.rm": validate_font_properties,
diff --git a/lib/matplotlib/tests/baseline_images/test_text/language.png b/lib/matplotlib/tests/baseline_images/test_text/language.png
new file mode 100644
index 000000000000..32e7f67b5664
Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_text/language.png differ
diff --git a/lib/matplotlib/tests/test_ft2font.py b/lib/matplotlib/tests/test_ft2font.py
index 70e611e17bcc..c464dddc051f 100644
--- a/lib/matplotlib/tests/test_ft2font.py
+++ b/lib/matplotlib/tests/test_ft2font.py
@@ -783,6 +783,37 @@ def test_ft2font_set_text():
assert font.get_bitmap_offset() == (6, 0)
+@pytest.mark.parametrize(
+ 'input',
+ [
+ [1, 2, 3],
+ [(1, 2)],
+ [('en', 'foo', 2)],
+ [('en', 1, 'foo')],
+ ],
+ ids=[
+ 'nontuple',
+ 'wrong length',
+ 'wrong start type',
+ 'wrong end type',
+ ],
+)
+def test_ft2font_language_invalid(input):
+ file = fm.findfont('DejaVu Sans')
+ font = ft2font.FT2Font(file, hinting_factor=1)
+ with pytest.raises(TypeError):
+ font.set_text('foo', language=input)
+
+
+def test_ft2font_language():
+ # This is just a smoke test.
+ file = fm.findfont('DejaVu Sans')
+ font = ft2font.FT2Font(file, hinting_factor=1)
+ font.set_text('foo')
+ font.set_text('foo', language='en')
+ font.set_text('foo', language=[('en', 1, 2)])
+
+
def test_ft2font_loading():
file = fm.findfont('DejaVu Sans')
font = ft2font.FT2Font(file, hinting_factor=1)
diff --git a/lib/matplotlib/tests/test_text.py b/lib/matplotlib/tests/test_text.py
index 8dba63eeef32..4d8a7a59c731 100644
--- a/lib/matplotlib/tests/test_text.py
+++ b/lib/matplotlib/tests/test_text.py
@@ -1202,3 +1202,60 @@ def test_ytick_rotation_mode():
tick.set_rotation(angle)
plt.subplots_adjust(left=0.4, right=0.6, top=.99, bottom=.01)
+
+
+@pytest.mark.parametrize(
+ 'input, match',
+ [
+ ([1, 2, 3], 'must be list of tuple'),
+ ([(1, 2)], 'must be list of tuple'),
+ ([('en', 'foo', 2)], 'start location must be int'),
+ ([('en', 1, 'foo')], 'end location must be int'),
+ ],
+)
+def test_text_language_invalid(input, match):
+ with pytest.raises(TypeError, match=match):
+ Text(0, 0, 'foo', language=input)
+
+
+@image_comparison(baseline_images=['language.png'], remove_text=False, style='mpl20')
+def test_text_language():
+ fig = plt.figure(figsize=(5, 3))
+
+ t = fig.text(0, 0.8, 'Default', fontsize=32)
+ assert t.get_language() is None
+ t = fig.text(0, 0.55, 'Lang A', fontsize=32)
+ assert t.get_language() is None
+ t = fig.text(0, 0.3, 'Lang B', fontsize=32)
+ assert t.get_language() is None
+ t = fig.text(0, 0.05, 'Mixed', fontsize=32)
+ assert t.get_language() is None
+
+ # DejaVu Sans supports language-specific glyphs in the Serbian and Macedonian
+ # languages in the Cyrillic alphabet.
+ cyrillic = '\U00000431'
+ t = fig.text(0.4, 0.8, cyrillic, fontsize=32)
+ assert t.get_language() is None
+ t = fig.text(0.4, 0.55, cyrillic, fontsize=32, language='sr')
+ assert t.get_language() == 'sr'
+ t = fig.text(0.4, 0.3, cyrillic, fontsize=32)
+ t.set_language('ru')
+ assert t.get_language() == 'ru'
+ t = fig.text(0.4, 0.05, cyrillic * 4, fontsize=32,
+ language=[('ru', 0, 1), ('sr', 1, 2), ('ru', 2, 3), ('sr', 3, 4)])
+ assert t.get_language() == (('ru', 0, 1), ('sr', 1, 2), ('ru', 2, 3), ('sr', 3, 4))
+
+ # Or the Sámi family of languages in the Latin alphabet.
+ latin = '\U0000014a'
+ t = fig.text(0.7, 0.8, latin, fontsize=32)
+ assert t.get_language() is None
+ with plt.rc_context({'text.language': 'en'}):
+ t = fig.text(0.7, 0.55, latin, fontsize=32)
+ assert t.get_language() == 'en'
+ t = fig.text(0.7, 0.3, latin, fontsize=32, language='smn')
+ assert t.get_language() == 'smn'
+ # Tuples are not documented, but we'll allow it.
+ t = fig.text(0.7, 0.05, latin * 4, fontsize=32)
+ t.set_language((('en', 0, 1), ('smn', 1, 2), ('en', 2, 3), ('smn', 3, 4)))
+ assert t.get_language() == (
+ ('en', 0, 1), ('smn', 1, 2), ('en', 2, 3), ('smn', 3, 4))
diff --git a/lib/matplotlib/text.py b/lib/matplotlib/text.py
index acde4fb179a2..4d80f9874941 100644
--- a/lib/matplotlib/text.py
+++ b/lib/matplotlib/text.py
@@ -2,6 +2,7 @@
Classes for including text in a figure.
"""
+from collections.abc import Sequence
import functools
import logging
import math
@@ -136,6 +137,7 @@ def __init__(self,
super().__init__()
self._x, self._y = x, y
self._text = ''
+ self.set_language(None)
self._reset_visual_defaults(
text=text,
color=color,
@@ -1422,6 +1424,42 @@ def _va_for_angle(self, angle):
return 'baseline' if anchor_at_left else 'top'
return 'top' if anchor_at_left else 'baseline'
+ def get_language(self):
+ """Return the language this Text is in."""
+ return self._language
+
+ def set_language(self, language):
+ """
+ Set the language of the text.
+
+ Parameters
+ ----------
+ language : str or None
+ The language of the text in a format accepted by libraqm, namely `a BCP47
+ language code `_.
+
+ If None, then defaults to :rc:`text.language`.
+ """
+ _api.check_isinstance((Sequence, str, None), language=language)
+ language = mpl._val_or_rc(language, 'text.language')
+
+ if not cbook.is_scalar_or_string(language):
+ language = tuple(language)
+ for val in language:
+ if not isinstance(val, tuple) or len(val) != 3:
+ raise TypeError('language must be list of tuple, not {language!r}')
+ sublang, start, end = val
+ if not isinstance(sublang, str):
+ raise TypeError(
+ 'sub-language specification must be str, not {sublang!r}')
+ if not isinstance(start, int):
+ raise TypeError('start location must be int, not {start!r}')
+ if not isinstance(end, int):
+ raise TypeError('end location must be int, not {end!r}')
+
+ self._language = language
+ self.stale = True
+
class OffsetFrom:
"""Callable helper class for working with `Annotation`."""
diff --git a/lib/matplotlib/text.pyi b/lib/matplotlib/text.pyi
index 41c7b761ae32..eb3c076b1c5c 100644
--- a/lib/matplotlib/text.pyi
+++ b/lib/matplotlib/text.pyi
@@ -14,7 +14,7 @@ from .transforms import (
Transform,
)
-from collections.abc import Iterable
+from collections.abc import Iterable, Sequence
from typing import Any, Literal
from .typing import ColorType, CoordsType
@@ -108,6 +108,8 @@ class Text(Artist):
def set_antialiased(self, antialiased: bool) -> None: ...
def _ha_for_angle(self, angle: Any) -> Literal['center', 'right', 'left'] | None: ...
def _va_for_angle(self, angle: Any) -> Literal['center', 'top', 'baseline'] | None: ...
+ def get_language(self) -> str | tuple[tuple[str, int, int], ...] | None: ...
+ def set_language(self, language: str | Sequence[tuple[str, int, int]] | None) -> None: ...
class OffsetFrom:
def __init__(
diff --git a/lib/matplotlib/textpath.py b/lib/matplotlib/textpath.py
index b57597ded363..039b0a5e416e 100644
--- a/lib/matplotlib/textpath.py
+++ b/lib/matplotlib/textpath.py
@@ -69,7 +69,7 @@ def get_text_width_height_descent(self, s, prop, ismath):
d /= 64.0
return w * scale, h * scale, d * scale
- def get_text_path(self, prop, s, ismath=False):
+ def get_text_path(self, prop, s, ismath=False, *, language=None):
"""
Convert text *s* to path (a tuple of vertices and codes for
matplotlib.path.Path).
@@ -82,6 +82,9 @@ def get_text_path(self, prop, s, ismath=False):
The text to be converted.
ismath : {False, True, "TeX"}
If True, use mathtext parser. If "TeX", use tex for rendering.
+ language : str, optional
+ The language of the text in a format accepted by libraqm, namely `a BCP47
+ language code `_.
Returns
-------
@@ -109,7 +112,8 @@ def get_text_path(self, prop, s, ismath=False):
glyph_info, glyph_map, rects = self.get_glyphs_tex(prop, s)
elif not ismath:
font = self._get_font(prop)
- glyph_info, glyph_map, rects = self.get_glyphs_with_font(font, s)
+ glyph_info, glyph_map, rects = self.get_glyphs_with_font(font, s,
+ language=language)
else:
glyph_info, glyph_map, rects = self.get_glyphs_mathtext(prop, s)
@@ -130,7 +134,7 @@ def get_text_path(self, prop, s, ismath=False):
return verts, codes
def get_glyphs_with_font(self, font, s, glyph_map=None,
- return_new_glyphs_only=False):
+ return_new_glyphs_only=False, *, language=None):
"""
Convert string *s* to vertices and codes using the provided ttf font.
"""
@@ -145,7 +149,7 @@ def get_glyphs_with_font(self, font, s, glyph_map=None,
xpositions = []
glyph_ids = []
- for item in _text_helpers.layout(s, font):
+ for item in _text_helpers.layout(s, font, language=language):
char_id = self._get_char_id(item.ft_object, ord(item.char))
glyph_ids.append(char_id)
xpositions.append(item.x)
diff --git a/lib/matplotlib/textpath.pyi b/lib/matplotlib/textpath.pyi
index 34d4e92ac47e..b83b337aa541 100644
--- a/lib/matplotlib/textpath.pyi
+++ b/lib/matplotlib/textpath.pyi
@@ -16,7 +16,8 @@ class TextToPath:
self, s: str, prop: FontProperties, ismath: bool | Literal["TeX"]
) -> tuple[float, float, float]: ...
def get_text_path(
- self, prop: FontProperties, s: str, ismath: bool | Literal["TeX"] = ...
+ self, prop: FontProperties, s: str, ismath: bool | Literal["TeX"] = ..., *,
+ language: str | list[tuple[str, int, int]] | None = ...,
) -> list[np.ndarray]: ...
def get_glyphs_with_font(
self,
@@ -24,6 +25,8 @@ class TextToPath:
s: str,
glyph_map: dict[str, tuple[np.ndarray, np.ndarray]] | None = ...,
return_new_glyphs_only: bool = ...,
+ *,
+ language: str | list[tuple[str, int, int]] | None = ...,
) -> tuple[
list[tuple[str, float, float, float]],
dict[str, tuple[np.ndarray, np.ndarray]],
diff --git a/src/ft2font.cpp b/src/ft2font.cpp
index 890fc61974b0..341422ad8018 100644
--- a/src/ft2font.cpp
+++ b/src/ft2font.cpp
@@ -319,7 +319,9 @@ void FT2Font::set_kerning_factor(int factor)
}
void FT2Font::set_text(
- std::u32string_view text, double angle, FT_Int32 flags, std::vector &xys)
+ std::u32string_view text, double angle, FT_Int32 flags,
+ LanguageType languages,
+ std::vector &xys)
{
FT_Matrix matrix; /* transformation matrix */
@@ -358,6 +360,16 @@ void FT2Font::set_text(
if (!raqm_set_freetype_load_flags(rq, flags)) {
throw std::runtime_error("failed to set text flags for layout");
}
+ if (languages) {
+ for (auto & [lang_str, start, end] : *languages) {
+ if (!raqm_set_language(rq, lang_str.c_str(), start, end - start)) {
+ throw std::runtime_error(
+ "failed to set language from {} "_s
+ "for {} characters to {!r} for layout"_s.format(
+ start, end, lang_str));
+ }
+ }
+ }
if (!raqm_layout(rq)) {
throw std::runtime_error("failed to layout text");
}
@@ -422,6 +434,16 @@ void FT2Font::set_text(
if (!raqm_set_freetype_load_flags(rq, flags)) {
throw std::runtime_error("failed to set text flags for layout");
}
+ if (languages) {
+ for (auto & [lang_str, start, end] : *languages) {
+ if (!raqm_set_language(rq, lang_str.c_str(), start, end - start)) {
+ throw std::runtime_error(
+ "failed to set language from {} "_s
+ "for {} characters to {!r} for layout"_s.format(
+ start, end, lang_str));
+ }
+ }
+ }
if (!raqm_layout(rq)) {
throw std::runtime_error("failed to layout text");
}
diff --git a/src/ft2font.h b/src/ft2font.h
index ffaf511ab9ca..b468804b4830 100644
--- a/src/ft2font.h
+++ b/src/ft2font.h
@@ -9,6 +9,7 @@
#include
#include
+#include
#include
#include
#include
@@ -100,6 +101,9 @@ extern FT_Library _ft2Library;
class FT2Font
{
public:
+ using LanguageRange = std::tuple;
+ using LanguageType = std::optional>;
+
FT2Font(long hinting_factor, std::vector &fallback_list,
bool warn_if_used);
virtual ~FT2Font();
@@ -110,7 +114,7 @@ class FT2Font
void set_charmap(int i);
void select_charmap(unsigned long i);
void set_text(std::u32string_view codepoints, double angle, FT_Int32 flags,
- std::vector &xys);
+ LanguageType languages, std::vector &xys);
int get_kerning(FT_UInt left, FT_UInt right, FT_Kerning_Mode mode);
void set_kerning_factor(int factor);
void load_char(long charcode, FT_Int32 flags, FT2Font *&ft_object, bool fallback);
diff --git a/src/ft2font_wrapper.cpp b/src/ft2font_wrapper.cpp
index 65fcb4b7e013..186bf7864dc2 100644
--- a/src/ft2font_wrapper.cpp
+++ b/src/ft2font_wrapper.cpp
@@ -693,7 +693,8 @@ const char *PyFT2Font_set_text__doc__ = R"""(
static py::array_t
PyFT2Font_set_text(PyFT2Font *self, std::u32string_view text, double angle = 0.0,
- std::variant flags_or_int = LoadFlags::FORCE_AUTOHINT)
+ std::variant flags_or_int = LoadFlags::FORCE_AUTOHINT,
+ std::variant languages_or_str = nullptr)
{
std::vector xys;
LoadFlags flags;
@@ -713,7 +714,21 @@ PyFT2Font_set_text(PyFT2Font *self, std::u32string_view text, double angle = 0.0
throw py::type_error("flags must be LoadFlags or int");
}
- self->set_text(text, angle, static_cast(flags), xys);
+ FT2Font::LanguageType languages;
+ if (auto value = std::get_if(&languages_or_str)) {
+ languages = std::move(*value);
+ } else if (auto value = std::get_if(&languages_or_str)) {
+ languages = std::vector{
+ FT2Font::LanguageRange{*value, 0, text.size()}
+ };
+ } else {
+ // NOTE: this can never happen as pybind11 would have checked the type in the
+ // Python wrapper before calling this function, but we need to keep the
+ // std::get_if instead of std::get for macOS 10.12 compatibility.
+ throw py::type_error("languages must be str or list of tuple");
+ }
+
+ self->set_text(text, angle, static_cast(flags), languages, xys);
py::ssize_t dims[] = { static_cast(xys.size()) / 2, 2 };
py::array_t result(dims);
@@ -1534,7 +1549,8 @@ PYBIND11_MODULE(ft2font, m, py::mod_gil_not_used())
.def("get_kerning", &PyFT2Font_get_kerning, "left"_a, "right"_a, "mode"_a,
PyFT2Font_get_kerning__doc__)
.def("set_text", &PyFT2Font_set_text,
- "string"_a, "angle"_a=0.0, "flags"_a=LoadFlags::FORCE_AUTOHINT,
+ "string"_a, "angle"_a=0.0, "flags"_a=LoadFlags::FORCE_AUTOHINT, py::kw_only(),
+ "language"_a=nullptr,
PyFT2Font_set_text__doc__)
.def("_get_fontmap", &PyFT2Font_get_fontmap, "string"_a,
PyFT2Font_get_fontmap__doc__)