Skip to content

Commit fa219e0

Browse files
QuLogicanntzer
andcommitted
ft2font: Convert kerning mode into an enum
This follows some of the idea from #9763. Co-authored-by: Antony Lee <anntzer.lee@gmail.com>
1 parent 1b404a0 commit fa219e0

File tree

9 files changed

+110
-32
lines changed

9 files changed

+110
-32
lines changed

galleries/examples/misc/font_indexing.py

+5-6
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@
99
import os
1010

1111
import matplotlib
12-
from matplotlib.ft2font import (KERNING_DEFAULT, KERNING_UNFITTED,
13-
KERNING_UNSCALED, FT2Font)
12+
from matplotlib.ft2font import FT2Font, Kerning
1413

1514
font = FT2Font(
1615
os.path.join(matplotlib.get_data_path(), 'fonts/ttf/DejaVuSans.ttf'))
@@ -31,7 +30,7 @@
3130
glyph = font.load_char(code)
3231
print(glyph.bbox)
3332
print(glyphd['A'], glyphd['V'], coded['A'], coded['V'])
34-
print('AV', font.get_kerning(glyphd['A'], glyphd['V'], KERNING_DEFAULT))
35-
print('AV', font.get_kerning(glyphd['A'], glyphd['V'], KERNING_UNFITTED))
36-
print('AV', font.get_kerning(glyphd['A'], glyphd['V'], KERNING_UNSCALED))
37-
print('AT', font.get_kerning(glyphd['A'], glyphd['T'], KERNING_UNSCALED))
33+
print('AV', font.get_kerning(glyphd['A'], glyphd['V'], Kerning.DEFAULT))
34+
print('AV', font.get_kerning(glyphd['A'], glyphd['V'], Kerning.UNFITTED))
35+
print('AV', font.get_kerning(glyphd['A'], glyphd['V'], Kerning.UNSCALED))
36+
print('AT', font.get_kerning(glyphd['A'], glyphd['T'], Kerning.UNSCALED))

lib/matplotlib/_mathtext.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
from ._mathtext_data import (
3030
latex_to_bakoma, stix_glyph_fixes, stix_virtual_fonts, tex2uni)
3131
from .font_manager import FontProperties, findfont, get_font
32-
from .ft2font import FT2Font, FT2Image, KERNING_DEFAULT
32+
from .ft2font import FT2Font, FT2Image, Kerning
3333

3434
from packaging.version import parse as parse_version
3535
from pyparsing import __version__ as pyparsing_version
@@ -426,7 +426,7 @@ def get_kern(self, font1: str, fontclass1: str, sym1: str, fontsize1: float,
426426
info1 = self._get_info(font1, fontclass1, sym1, fontsize1, dpi)
427427
info2 = self._get_info(font2, fontclass2, sym2, fontsize2, dpi)
428428
font = info1.font
429-
return font.get_kerning(info1.num, info2.num, KERNING_DEFAULT) / 64
429+
return font.get_kerning(info1.num, info2.num, Kerning.DEFAULT) / 64
430430
return super().get_kern(font1, fontclass1, sym1, fontsize1,
431431
font2, fontclass2, sym2, fontsize2, dpi)
432432

lib/matplotlib/_text_helpers.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import dataclasses
88

99
from . import _api
10-
from .ft2font import KERNING_DEFAULT, LOAD_NO_HINTING, FT2Font
10+
from .ft2font import LOAD_NO_HINTING, FT2Font, Kerning
1111

1212

1313
@dataclasses.dataclass(frozen=True)
@@ -43,7 +43,7 @@ def warn_on_missing_glyph(codepoint, fontnames):
4343
f"Matplotlib currently does not support {block} natively.")
4444

4545

46-
def layout(string, font, *, kern_mode=KERNING_DEFAULT):
46+
def layout(string, font, *, kern_mode=Kerning.DEFAULT):
4747
"""
4848
Render *string* with *font*.
4949
@@ -56,7 +56,7 @@ def layout(string, font, *, kern_mode=KERNING_DEFAULT):
5656
The string to be rendered.
5757
font : FT2Font
5858
The font.
59-
kern_mode : int
59+
kern_mode : Kerning
6060
A FreeType kerning mode.
6161
6262
Yields

lib/matplotlib/backends/backend_pdf.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
from matplotlib.font_manager import get_font, fontManager as _fontManager
3737
from matplotlib._afm import AFM
3838
from matplotlib.ft2font import (FIXED_WIDTH, ITALIC, LOAD_NO_SCALE,
39-
LOAD_NO_HINTING, KERNING_UNFITTED, FT2Font)
39+
LOAD_NO_HINTING, FT2Font, Kerning)
4040
from matplotlib.transforms import Affine2D, BboxBase
4141
from matplotlib.path import Path
4242
from matplotlib.dates import UTC
@@ -2378,7 +2378,7 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None):
23782378
multibyte_glyphs = []
23792379
prev_was_multibyte = True
23802380
prev_font = font
2381-
for item in _text_helpers.layout(s, font, kern_mode=KERNING_UNFITTED):
2381+
for item in _text_helpers.layout(s, font, kern_mode=Kerning.UNFITTED):
23822382
if _font_supports_glyph(fonttype, ord(item.char)):
23832383
if prev_was_multibyte or item.ft_object != prev_font:
23842384
singlebyte_chunks.append((item.ft_object, item.x, []))

lib/matplotlib/ft2font.pyi

+7-4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from enum import Enum
12
import sys
23
from typing import BinaryIO, Literal, TypedDict, final, overload
34
from typing_extensions import Buffer # < Py 3.12
@@ -16,9 +17,6 @@ GLYPH_NAMES: int
1617
HORIZONTAL: int
1718
ITALIC: int
1819
KERNING: int
19-
KERNING_DEFAULT: int
20-
KERNING_UNFITTED: int
21-
KERNING_UNSCALED: int
2220
LOAD_CROP_BITMAP: int
2321
LOAD_DEFAULT: int
2422
LOAD_FORCE_AUTOHINT: int
@@ -44,6 +42,11 @@ SCALABLE: int
4442
SFNT: int
4543
VERTICAL: int
4644

45+
class Kerning(Enum):
46+
DEFAULT = ...
47+
UNFITTED = ...
48+
UNSCALED = ...
49+
4750
class _SfntHeadDict(TypedDict):
4851
version: tuple[int, int]
4952
fontRevision: tuple[int, int]
@@ -184,7 +187,7 @@ class FT2Font(Buffer):
184187
def get_descent(self) -> int: ...
185188
def get_glyph_name(self, index: int) -> str: ...
186189
def get_image(self) -> NDArray[np.uint8]: ...
187-
def get_kerning(self, left: int, right: int, mode: int) -> int: ...
190+
def get_kerning(self, left: int, right: int, mode: Kerning) -> int: ...
188191
def get_name_index(self, name: str) -> int: ...
189192
def get_num_glyphs(self) -> int: ...
190193
def get_path(self) -> tuple[NDArray[np.float64], NDArray[np.int8]]: ...

lib/matplotlib/tests/test_ft2font.py

+28-3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import numpy as np
66
import pytest
77

8+
import matplotlib as mpl
89
from matplotlib import ft2font
910
from matplotlib.testing.decorators import check_figures_equal
1011
import matplotlib.font_manager as fm
@@ -714,13 +715,37 @@ def test_ft2font_get_kerning(left, right, unscaled, unfitted, default):
714715
font.set_size(100, 100)
715716
assert font.get_kerning(font.get_char_index(ord(left)),
716717
font.get_char_index(ord(right)),
717-
ft2font.KERNING_UNSCALED) == unscaled
718+
ft2font.Kerning.UNSCALED) == unscaled
718719
assert font.get_kerning(font.get_char_index(ord(left)),
719720
font.get_char_index(ord(right)),
720-
ft2font.KERNING_UNFITTED) == unfitted
721+
ft2font.Kerning.UNFITTED) == unfitted
721722
assert font.get_kerning(font.get_char_index(ord(left)),
722723
font.get_char_index(ord(right)),
723-
ft2font.KERNING_DEFAULT) == default
724+
ft2font.Kerning.DEFAULT) == default
725+
with pytest.warns(mpl.MatplotlibDeprecationWarning,
726+
match='Use Kerning.UNSCALED instead'):
727+
k = ft2font.KERNING_UNSCALED
728+
with pytest.warns(mpl.MatplotlibDeprecationWarning,
729+
match='Use Kerning enum values instead'):
730+
assert font.get_kerning(font.get_char_index(ord(left)),
731+
font.get_char_index(ord(right)),
732+
int(k)) == unscaled
733+
with pytest.warns(mpl.MatplotlibDeprecationWarning,
734+
match='Use Kerning.UNFITTED instead'):
735+
k = ft2font.KERNING_UNFITTED
736+
with pytest.warns(mpl.MatplotlibDeprecationWarning,
737+
match='Use Kerning enum values instead'):
738+
assert font.get_kerning(font.get_char_index(ord(left)),
739+
font.get_char_index(ord(right)),
740+
int(k)) == unfitted
741+
with pytest.warns(mpl.MatplotlibDeprecationWarning,
742+
match='Use Kerning.DEFAULT instead'):
743+
k = ft2font.KERNING_DEFAULT
744+
with pytest.warns(mpl.MatplotlibDeprecationWarning,
745+
match='Use Kerning enum values instead'):
746+
assert font.get_kerning(font.get_char_index(ord(left)),
747+
font.get_char_index(ord(right)),
748+
int(k)) == default
724749

725750

726751
def test_ft2font_set_text():

src/ft2font.cpp

+4-2
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,8 @@ void FT2Font::select_charmap(unsigned long i)
354354
}
355355
}
356356

357-
int FT2Font::get_kerning(FT_UInt left, FT_UInt right, FT_UInt mode, bool fallback = false)
357+
int FT2Font::get_kerning(FT_UInt left, FT_UInt right, FT_Kerning_Mode mode,
358+
bool fallback = false)
358359
{
359360
if (fallback && glyph_to_font.find(left) != glyph_to_font.end() &&
360361
glyph_to_font.find(right) != glyph_to_font.end()) {
@@ -375,7 +376,8 @@ int FT2Font::get_kerning(FT_UInt left, FT_UInt right, FT_UInt mode, bool fallbac
375376
}
376377
}
377378

378-
int FT2Font::get_kerning(FT_UInt left, FT_UInt right, FT_UInt mode, FT_Vector &delta)
379+
int FT2Font::get_kerning(FT_UInt left, FT_UInt right, FT_Kerning_Mode mode,
380+
FT_Vector &delta)
379381
{
380382
if (!FT_HAS_KERNING(face)) {
381383
return 0;

src/ft2font.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,8 @@ class FT2Font
7979
void select_charmap(unsigned long i);
8080
void set_text(std::u32string_view codepoints, double angle, FT_Int32 flags,
8181
std::vector<double> &xys);
82-
int get_kerning(FT_UInt left, FT_UInt right, FT_UInt mode, bool fallback);
83-
int get_kerning(FT_UInt left, FT_UInt right, FT_UInt mode, FT_Vector &delta);
82+
int get_kerning(FT_UInt left, FT_UInt right, FT_Kerning_Mode mode, bool fallback);
83+
int get_kerning(FT_UInt left, FT_UInt right, FT_Kerning_Mode mode, FT_Vector &delta);
8484
void set_kerning_factor(int factor);
8585
void load_char(long charcode, FT_Int32 flags, FT2Font *&ft_object, bool fallback);
8686
bool load_char_with_fallback(FT2Font *&ft_object_with_glyph,

src/ft2font_wrapper.cpp

+57-8
Original file line numberDiff line numberDiff line change
@@ -359,12 +359,12 @@ const char *PyFT2Font_get_kerning__doc__ = R"""(
359359
The glyph indices. Note these are not characters nor character codes.
360360
Use `.get_char_index` to convert character codes to glyph indices.
361361
362-
mode : int
362+
mode : Kerning
363363
A kerning mode constant:
364364
365-
- ``KERNING_DEFAULT`` - Return scaled and grid-fitted kerning distances
366-
- ``KERNING_UNFITTED`` - Return scaled but un-grid-fitted kerning distances
367-
- ``KERNING_UNSCALED`` - Return the kerning vector in original font units
365+
- ``DEFAULT`` - Return scaled and grid-fitted kerning distances.
366+
- ``UNFITTED`` - Return scaled but un-grid-fitted kerning distances.
367+
- ``UNSCALED`` - Return the kerning vector in original font units.
368368
369369
Returns
370370
-------
@@ -373,9 +373,21 @@ const char *PyFT2Font_get_kerning__doc__ = R"""(
373373
)""";
374374

375375
static int
376-
PyFT2Font_get_kerning(PyFT2Font *self, FT_UInt left, FT_UInt right, FT_UInt mode)
376+
PyFT2Font_get_kerning(PyFT2Font *self, FT_UInt left, FT_UInt right,
377+
std::variant<FT_Kerning_Mode, FT_UInt> mode_or_int)
377378
{
378379
bool fallback = true;
380+
FT_Kerning_Mode mode;
381+
382+
if (auto value = std::get_if<FT_UInt>(&mode_or_int)) {
383+
auto api = py::module_::import("matplotlib._api");
384+
auto warn = api.attr("warn_deprecated");
385+
warn("since"_a="3.10", "name"_a="mode", "obj_type"_a="parameter as int",
386+
"alternative"_a="Kerning enum values");
387+
mode = static_cast<FT_Kerning_Mode>(*value);
388+
} else {
389+
mode = std::get<0>(mode_or_int);
390+
}
379391

380392
return self->x->get_kerning(left, right, mode, fallback);
381393
}
@@ -1270,6 +1282,29 @@ PyFT2Font_fname(PyFT2Font *self)
12701282
}
12711283
}
12721284

1285+
static py::object
1286+
ft2font__getattr__(std::string name) {
1287+
auto api = py::module_::import("matplotlib._api");
1288+
auto warn = api.attr("warn_deprecated");
1289+
1290+
#define DEPRECATE_ATTR_FROM_ENUM(attr_, alternative_, real_value_) \
1291+
do { \
1292+
if (name == #attr_) { \
1293+
warn("since"_a="3.10", "name"_a=#attr_, "obj_type"_a="attribute", \
1294+
"alternative"_a=#alternative_); \
1295+
return py::cast(real_value_); \
1296+
} \
1297+
} while(0)
1298+
DEPRECATE_ATTR_FROM_ENUM(KERNING_DEFAULT, Kerning.DEFAULT, FT_KERNING_DEFAULT);
1299+
DEPRECATE_ATTR_FROM_ENUM(KERNING_UNFITTED, Kerning.UNFITTED, FT_KERNING_UNFITTED);
1300+
DEPRECATE_ATTR_FROM_ENUM(KERNING_UNSCALED, Kerning.UNSCALED, FT_KERNING_UNSCALED);
1301+
1302+
#undef DEPRECATE_ATTR_FROM_ENUM
1303+
1304+
throw py::attribute_error(
1305+
"module 'matplotlib.ft2font' has no attribute {!r}"_s.format(name));
1306+
}
1307+
12731308
PYBIND11_MODULE(ft2font, m)
12741309
{
12751310
auto ia = [m]() -> const void* {
@@ -1288,6 +1323,22 @@ PYBIND11_MODULE(ft2font, m)
12881323
FT_Library_Version(_ft2Library, &major, &minor, &patch);
12891324
snprintf(version_string, sizeof(version_string), "%d.%d.%d", major, minor, patch);
12901325

1326+
py::options options;
1327+
options.disable_enum_members_docstring();
1328+
1329+
py::enum_<FT_Kerning_Mode>(m, "Kerning", R"""(
1330+
Kerning modes for `.FT2Font.get_kerning`.
1331+
1332+
For more information, see `the FreeType documentation
1333+
<https://freetype.org/freetype2/docs/reference/ft2-glyph_retrieval.html#ft_kerning_mode>`_.
1334+
)""")
1335+
#define DECLARE_ENUM(name) .value(#name, FT_KERNING_##name)
1336+
DECLARE_ENUM(DEFAULT)
1337+
DECLARE_ENUM(UNFITTED)
1338+
DECLARE_ENUM(UNSCALED)
1339+
#undef DECLARE_ENUM
1340+
;
1341+
12911342
py::class_<FT2Image>(m, "FT2Image", py::is_final(), py::buffer_protocol(),
12921343
PyFT2Image__doc__)
12931344
.def(py::init<double, double>(), "width"_a, "height"_a, PyFT2Image_init__doc__)
@@ -1429,6 +1480,7 @@ PYBIND11_MODULE(ft2font, m)
14291480

14301481
m.attr("__freetype_version__") = version_string;
14311482
m.attr("__freetype_build_type__") = FREETYPE_BUILD_TYPE;
1483+
m.def("__getattr__", ft2font__getattr__);
14321484
m.attr("SCALABLE") = FT_FACE_FLAG_SCALABLE;
14331485
m.attr("FIXED_SIZES") = FT_FACE_FLAG_FIXED_SIZES;
14341486
m.attr("FIXED_WIDTH") = FT_FACE_FLAG_FIXED_WIDTH;
@@ -1442,9 +1494,6 @@ PYBIND11_MODULE(ft2font, m)
14421494
m.attr("EXTERNAL_STREAM") = FT_FACE_FLAG_EXTERNAL_STREAM;
14431495
m.attr("ITALIC") = FT_STYLE_FLAG_ITALIC;
14441496
m.attr("BOLD") = FT_STYLE_FLAG_BOLD;
1445-
m.attr("KERNING_DEFAULT") = (int)FT_KERNING_DEFAULT;
1446-
m.attr("KERNING_UNFITTED") = (int)FT_KERNING_UNFITTED;
1447-
m.attr("KERNING_UNSCALED") = (int)FT_KERNING_UNSCALED;
14481497
m.attr("LOAD_DEFAULT") = FT_LOAD_DEFAULT;
14491498
m.attr("LOAD_NO_SCALE") = FT_LOAD_NO_SCALE;
14501499
m.attr("LOAD_NO_HINTING") = FT_LOAD_NO_HINTING;

0 commit comments

Comments
 (0)