diff --git a/doc/api/next_api_changes/deprecations/30322-ES.rst b/doc/api/next_api_changes/deprecations/30322-ES.rst new file mode 100644 index 000000000000..dfb7bad59b8a --- /dev/null +++ b/doc/api/next_api_changes/deprecations/30322-ES.rst @@ -0,0 +1,7 @@ +Font kerning factor is deprecated +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Due to internal changes to support complex text rendering, the kerning factor on fonts is +no longer used. Setting the ``text.kerning_factor`` rcParam (which existed only for +backwards-compatibility) to any value other than 0 is deprecated, and the rcParam will be +removed in the future. diff --git a/doc/users/prev_whats_new/whats_new_3.2.0.rst b/doc/users/prev_whats_new/whats_new_3.2.0.rst index 12d7fab3af90..3519245642a8 100644 --- a/doc/users/prev_whats_new/whats_new_3.2.0.rst +++ b/doc/users/prev_whats_new/whats_new_3.2.0.rst @@ -52,26 +52,15 @@ triangle meshes. Kerning adjustments now use correct values ------------------------------------------ -Due to an error in how kerning adjustments were applied, previous versions of -Matplotlib would under-correct kerning. This version will now correctly apply -kerning (for fonts supported by FreeType). To restore the old behavior (e.g., -for test images), you may set :rc:`text.kerning_factor` to 6 (instead of 0). -Other values have undefined behavior. - -.. plot:: - - import matplotlib.pyplot as plt - - # Use old kerning values: - plt.rcParams['text.kerning_factor'] = 6 - fig, ax = plt.subplots() - ax.text(0.0, 0.05, 'BRAVO\nAWKWARD\nVAT\nW.Test', fontsize=56) - ax.set_title('Before (text.kerning_factor = 6)') - -Note how the spacing between characters is uniform between their bounding boxes -(above). With corrected kerning (below), slanted characters (e.g., AV or VA) -will be spaced closer together, as well as various other character pairs, -depending on font support (e.g., T and e, or the period after the W). +Due to an error in how kerning adjustments were applied, previous versions of Matplotlib +would under-correct kerning. This version will now correctly apply kerning (for fonts +supported by FreeType). To restore the old behavior (e.g., for test images), you may set +the ``text.kerning_factor`` rcParam to 6 (instead of 0). Other values have undefined +behavior. + +With corrected kerning (below), slanted characters (e.g., AV or VA) will be spaced closer +together, as well as various other character pairs, depending on font support (e.g., T +and e, or the period after the W). .. plot:: diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index 008d4de77a3b..e343e60b5fa1 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -751,6 +751,8 @@ def __setitem__(self, key, val): f"a list of valid parameters)") from err except ValueError as ve: raise ValueError(f"Key {key}: {ve}") from None + if key == "text.kerning_factor" and cval is not None: + _api.warn_deprecated("3.11", name="text.kerning_factor", obj_type="rcParam") self._set(key, cval) def __getitem__(self, key): diff --git a/lib/matplotlib/ft2font.pyi b/lib/matplotlib/ft2font.pyi index a413cd3c1a76..5257893b380a 100644 --- a/lib/matplotlib/ft2font.pyi +++ b/lib/matplotlib/ft2font.pyi @@ -191,7 +191,7 @@ class FT2Font(Buffer): hinting_factor: int = ..., *, _fallback_list: list[FT2Font] | None = ..., - _kerning_factor: int = ... + _kerning_factor: int | None = ... ) -> None: ... if sys.version_info[:2] >= (3, 12): def __buffer__(self, flags: int) -> memoryview: ... diff --git a/lib/matplotlib/mpl-data/matplotlibrc b/lib/matplotlib/mpl-data/matplotlibrc index 0ab5cfadf291..fb96656c5303 100644 --- a/lib/matplotlib/mpl-data/matplotlibrc +++ b/lib/matplotlib/mpl-data/matplotlibrc @@ -306,10 +306,9 @@ #text.hinting_factor: 1 # Specifies the amount of softness for hinting in the # horizontal direction. A value of 1 will hint to full # pixels. A value of 2 will hint to half pixels etc. -#text.kerning_factor: 0 # Specifies the scaling factor for kerning values. This - # is provided solely to allow old test images to remain - # unchanged. Set to 6 to obtain previous behavior. - # Values other than 0 or 6 have no defined meaning. +#text.kerning_factor: None # Specifies the scaling factor for kerning values. Values + # other than 0, 6, or None have no defined meaning. + # This setting is deprecated. #text.antialiased: True # If True (default), the text will be antialiased. # This only affects raster outputs. #text.parse_math: True # Use mathtext if there is an even number of unescaped diff --git a/lib/matplotlib/rcsetup.py b/lib/matplotlib/rcsetup.py index 80d25659888e..b4224d169815 100644 --- a/lib/matplotlib/rcsetup.py +++ b/lib/matplotlib/rcsetup.py @@ -1042,7 +1042,7 @@ def _convert_validator_spec(key, conv): "text.hinting": ["default", "no_autohint", "force_autohint", "no_hinting", "auto", "native", "either", "none"], "text.hinting_factor": validate_int, - "text.kerning_factor": validate_int, + "text.kerning_factor": validate_int_or_None, "text.antialiased": validate_bool, "text.parse_math": validate_bool, diff --git a/lib/matplotlib/tests/test_ft2font.py b/lib/matplotlib/tests/test_ft2font.py index b39df1f52996..5dd96ce9cafe 100644 --- a/lib/matplotlib/tests/test_ft2font.py +++ b/lib/matplotlib/tests/test_ft2font.py @@ -168,6 +168,12 @@ def test_ft2font_invalid_args(tmp_path): # kerning_factor argument. with pytest.raises(TypeError, match='incompatible constructor arguments'): ft2font.FT2Font(file, _kerning_factor=1.3) + with pytest.warns(mpl.MatplotlibDeprecationWarning, + match='text.kerning_factor rcParam was deprecated .+ 3.11'): + mpl.rcParams['text.kerning_factor'] = 0 + with pytest.warns(mpl.MatplotlibDeprecationWarning, + match='_kerning_factor parameter was deprecated .+ 3.11'): + ft2font.FT2Font(file, _kerning_factor=123) def test_ft2font_clear(): @@ -188,7 +194,7 @@ def test_ft2font_clear(): def test_ft2font_set_size(): file = fm.findfont('DejaVu Sans') - font = ft2font.FT2Font(file, hinting_factor=1, _kerning_factor=1) + font = ft2font.FT2Font(file, hinting_factor=1) font.set_size(12, 72) font.set_text('ABabCDcd') orig = font.get_width_height() @@ -717,7 +723,7 @@ def test_ft2font_get_sfnt_table(font_name, header): def test_ft2font_get_kerning(left, right, unscaled, unfitted, default): file = fm.findfont('DejaVu Sans') # With unscaled, these settings should produce exact values found in FontForge. - font = ft2font.FT2Font(file, hinting_factor=1, _kerning_factor=0) + font = ft2font.FT2Font(file, hinting_factor=1) font.set_size(100, 100) assert font.get_kerning(font.get_char_index(ord(left)), font.get_char_index(ord(right)), @@ -756,7 +762,7 @@ def test_ft2font_get_kerning(left, right, unscaled, unfitted, default): def test_ft2font_set_text(): file = fm.findfont('DejaVu Sans') - font = ft2font.FT2Font(file, hinting_factor=1, _kerning_factor=0) + font = ft2font.FT2Font(file, hinting_factor=1) font.set_size(12, 72) xys = font.set_text('') np.testing.assert_array_equal(xys, np.empty((0, 2))) @@ -778,7 +784,7 @@ def test_ft2font_set_text(): def test_ft2font_loading(): file = fm.findfont('DejaVu Sans') - font = ft2font.FT2Font(file, hinting_factor=1, _kerning_factor=0) + font = ft2font.FT2Font(file, hinting_factor=1) font.set_size(12, 72) for glyph in [font.load_char(ord('M')), font.load_glyph(font.get_char_index(ord('M')))]: @@ -819,13 +825,13 @@ def test_ft2font_drawing(): ]) expected *= 255 file = fm.findfont('DejaVu Sans') - font = ft2font.FT2Font(file, hinting_factor=1, _kerning_factor=0) + font = ft2font.FT2Font(file, hinting_factor=1) font.set_size(12, 72) font.set_text('M') font.draw_glyphs_to_bitmap(antialiased=False) image = font.get_image() np.testing.assert_array_equal(image, expected) - font = ft2font.FT2Font(file, hinting_factor=1, _kerning_factor=0) + font = ft2font.FT2Font(file, hinting_factor=1) font.set_size(12, 72) glyph = font.load_char(ord('M')) image = np.zeros(expected.shape, np.uint8) @@ -835,7 +841,7 @@ def test_ft2font_drawing(): def test_ft2font_get_path(): file = fm.findfont('DejaVu Sans') - font = ft2font.FT2Font(file, hinting_factor=1, _kerning_factor=0) + font = ft2font.FT2Font(file, hinting_factor=1) font.set_size(12, 72) vertices, codes = font.get_path() assert vertices.shape == (0, 2) diff --git a/src/ft2font.cpp b/src/ft2font.cpp index 1d03ecf10b56..39aa83cd2f5b 100644 --- a/src/ft2font.cpp +++ b/src/ft2font.cpp @@ -7,7 +7,6 @@ #include #include #include -#include #include #include #include @@ -207,32 +206,42 @@ FT2Font::get_path(std::vector &vertices, std::vector &cod codes.push_back(CLOSEPOLY); } -FT2Font::FT2Font(FT_Open_Args &open_args, - long hinting_factor_, - std::vector &fallback_list, - FT2Font::WarnFunc warn, bool warn_if_used) - : ft_glyph_warn(warn), warn_if_used(warn_if_used), image({1, 1}), face(nullptr), +FT2Font::FT2Font(long hinting_factor_, std::vector &fallback_list, + bool warn_if_used) + : warn_if_used(warn_if_used), image({1, 1}), face(nullptr), fallbacks(fallback_list), hinting_factor(hinting_factor_), // set default kerning factor to 0, i.e., no kerning manipulation kerning_factor(0) { clear(); +} + +FT2Font::~FT2Font() +{ + close(); +} + +void FT2Font::open(FT_Open_Args &open_args) +{ FT_CHECK(FT_Open_Face, _ft2Library, &open_args, 0, &face); if (open_args.stream != nullptr) { face->face_flags |= FT_FACE_FLAG_EXTERNAL_STREAM; } - // Set fallbacks - std::copy(fallback_list.begin(), fallback_list.end(), std::back_inserter(fallbacks)); } -FT2Font::~FT2Font() +void FT2Font::close() { + // This should be idempotent, in case a user manually calls close before the + // destructor does. + for (auto & glyph : glyphs) { FT_Done_Glyph(glyph); } + glyphs.clear(); if (face) { FT_Done_Face(face); + face = nullptr; } } @@ -533,10 +542,9 @@ FT_UInt FT2Font::get_char_index(FT_ULong charcode, bool fallback = false) return FT_Get_Char_Index(ft_object->get_face(), charcode); } -void FT2Font::get_width_height(long *width, long *height) +std::tuple FT2Font::get_width_height() { - *width = advance; - *height = bbox.yMax - bbox.yMin; + return {advance, bbox.yMax - bbox.yMin}; } long FT2Font::get_descent() @@ -544,10 +552,9 @@ long FT2Font::get_descent() return -bbox.yMin; } -void FT2Font::get_bitmap_offset(long *x, long *y) +std::tuple FT2Font::get_bitmap_offset() { - *x = bbox.xMin; - *y = 0; + return {bbox.xMin, 0}; } void FT2Font::draw_glyphs_to_bitmap(bool antialiased) diff --git a/src/ft2font.h b/src/ft2font.h index 0881693e7557..ab440e2107f2 100644 --- a/src/ft2font.h +++ b/src/ft2font.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -96,13 +97,12 @@ extern FT_Library _ft2Library; class FT2Font { - typedef void (*WarnFunc)(FT_ULong charcode, std::set family_names); - public: - FT2Font(FT_Open_Args &open_args, long hinting_factor, - std::vector &fallback_list, - WarnFunc warn, bool warn_if_used); + FT2Font(long hinting_factor, std::vector &fallback_list, + bool warn_if_used); virtual ~FT2Font(); + void open(FT_Open_Args &open_args); + void close(); void clear(); void set_size(double ptsize, double dpi); void set_charmap(int i); @@ -123,8 +123,8 @@ class FT2Font std::set &glyph_seen_fonts, bool override); void load_glyph(FT_UInt glyph_index, FT_Int32 flags); - void get_width_height(long *width, long *height); - void get_bitmap_offset(long *x, long *y); + std::tuple get_width_height(); + std::tuple get_bitmap_offset(); long get_descent(); void draw_glyphs_to_bitmap(bool antialiased); void draw_glyph_to_bitmap( @@ -166,8 +166,9 @@ class FT2Font return FT_HAS_KERNING(face); } + protected: + virtual void ft_glyph_warn(FT_ULong charcode, std::set family_names) = 0; private: - WarnFunc ft_glyph_warn; bool warn_if_used; py::array_t image; FT_Face face; diff --git a/src/ft2font_wrapper.cpp b/src/ft2font_wrapper.cpp index cb816efff9a9..1ae129bd3c81 100644 --- a/src/ft2font_wrapper.cpp +++ b/src/ft2font_wrapper.cpp @@ -331,16 +331,35 @@ PyGlyph_get_bbox(PyGlyph *self) * FT2Font * */ -struct PyFT2Font +class PyFT2Font : public FT2Font { - FT2Font *x; + public: + using FT2Font::FT2Font; + py::object py_file; FT_StreamRec stream; py::list fallbacks; ~PyFT2Font() { - delete this->x; + // Because destructors are called from subclass up to base class, we need to + // explicitly close the font here. Otherwise, the instance attributes here will + // be destroyed before the font itself, but those are used in the close callback. + close(); + } + + void ft_glyph_warn(FT_ULong charcode, std::set family_names) + { + std::set::iterator it = family_names.begin(); + std::stringstream ss; + ss<<*it; + while(++it != family_names.end()){ + ss<<", "<<*it; + } + + auto text_helpers = py::module_::import("matplotlib._text_helpers"); + auto warn_on_missing_glyph = text_helpers.attr("warn_on_missing_glyph"); + warn_on_missing_glyph(charcode, ss.str()); } }; @@ -402,21 +421,6 @@ close_file_callback(FT_Stream stream) PyErr_Restore(type, value, traceback); } -static void -ft_glyph_warn(FT_ULong charcode, std::set family_names) -{ - std::set::iterator it = family_names.begin(); - std::stringstream ss; - ss<<*it; - while(++it != family_names.end()){ - ss<<", "<<*it; - } - - auto text_helpers = py::module_::import("matplotlib._text_helpers"); - auto warn_on_missing_glyph = text_helpers.attr("warn_on_missing_glyph"); - warn_on_missing_glyph(charcode, ss.str()); -} - const char *PyFT2Font_init__doc__ = R"""( Parameters ---------- @@ -432,12 +436,6 @@ const char *PyFT2Font_init__doc__ = R"""( .. warning:: This API is both private and provisional: do not use it directly. - _kerning_factor : int, optional - Used to adjust the degree of kerning. - - .. warning:: - This API is private: do not use it directly. - _warn_if_used : bool, optional Used to trigger missing glyph warnings. @@ -448,14 +446,30 @@ const char *PyFT2Font_init__doc__ = R"""( static PyFT2Font * PyFT2Font_init(py::object filename, long hinting_factor = 8, std::optional> fallback_list = std::nullopt, - int kerning_factor = 0, bool warn_if_used = false) + std::optional kerning_factor = std::nullopt, + bool warn_if_used = false) { if (hinting_factor <= 0) { throw py::value_error("hinting_factor must be greater than 0"); } + if (kerning_factor) { + auto api = py::module_::import("matplotlib._api"); + auto warn = api.attr("warn_deprecated"); + warn("since"_a="3.11", "name"_a="_kerning_factor", "obj_type"_a="parameter"); + } else { + kerning_factor = 0; + } + + std::vector fallback_fonts; + if (fallback_list) { + // go through fallbacks to add them to our lists + std::copy(fallback_list->begin(), fallback_list->end(), + std::back_inserter(fallback_fonts)); + } + + auto self = new PyFT2Font(hinting_factor, fallback_fonts, warn_if_used); + self->set_kerning_factor(*kerning_factor); - PyFT2Font *self = new PyFT2Font(); - self->x = nullptr; memset(&self->stream, 0, sizeof(FT_StreamRec)); self->stream.base = nullptr; self->stream.size = 0x7fffffff; // Unknown size. @@ -467,15 +481,10 @@ PyFT2Font_init(py::object filename, long hinting_factor = 8, open_args.flags = FT_OPEN_STREAM; open_args.stream = &self->stream; - std::vector fallback_fonts; if (fallback_list) { // go through fallbacks to add them to our lists for (auto item : *fallback_list) { self->fallbacks.append(item); - // Also (locally) cache the underlying FT2Font objects. As long as - // the Python objects are kept alive, these pointer are good. - FT2Font *fback = item->x; - fallback_fonts.push_back(fback); } } @@ -497,10 +506,7 @@ PyFT2Font_init(py::object filename, long hinting_factor = 8, self->stream.close = nullptr; } - self->x = new FT2Font(open_args, hinting_factor, fallback_fonts, ft_glyph_warn, - warn_if_used); - - self->x->set_kerning_factor(kerning_factor); + self->open(open_args); return self; } @@ -518,12 +524,6 @@ PyFT2Font_fname(PyFT2Font *self) const char *PyFT2Font_clear__doc__ = "Clear all the glyphs, reset for a new call to `.set_text`."; -static void -PyFT2Font_clear(PyFT2Font *self) -{ - self->x->clear(); -} - const char *PyFT2Font_set_size__doc__ = R"""( Set the size of the text. @@ -535,12 +535,6 @@ const char *PyFT2Font_set_size__doc__ = R"""( The DPI used for rendering the text. )"""; -static void -PyFT2Font_set_size(PyFT2Font *self, double ptsize, double dpi) -{ - self->x->set_size(ptsize, dpi); -} - const char *PyFT2Font_set_charmap__doc__ = R"""( Make the i-th charmap current. @@ -559,12 +553,6 @@ const char *PyFT2Font_set_charmap__doc__ = R"""( .get_charmap )"""; -static void -PyFT2Font_set_charmap(PyFT2Font *self, int i) -{ - self->x->set_charmap(i); -} - const char *PyFT2Font_select_charmap__doc__ = R"""( Select a charmap by its FT_Encoding number. @@ -583,12 +571,6 @@ const char *PyFT2Font_select_charmap__doc__ = R"""( .get_charmap )"""; -static void -PyFT2Font_select_charmap(PyFT2Font *self, unsigned long i) -{ - self->x->select_charmap(i); -} - const char *PyFT2Font_get_kerning__doc__ = R"""( Get the kerning between two glyphs. @@ -635,7 +617,7 @@ PyFT2Font_get_kerning(PyFT2Font *self, FT_UInt left, FT_UInt right, throw py::type_error("mode must be Kerning or int"); } - return self->x->get_kerning(left, right, mode); + return self->get_kerning(left, right, mode); } const char *PyFT2Font_get_fontmap__doc__ = R"""( @@ -669,7 +651,7 @@ PyFT2Font_get_fontmap(PyFT2Font *self, std::u32string text) py::object target_font; int index; - if (self->x->get_char_fallback_index(code, index)) { + if (self->get_char_fallback_index(code, index)) { if (index >= 0) { target_font = self->fallbacks[index]; } else { @@ -731,7 +713,7 @@ PyFT2Font_set_text(PyFT2Font *self, std::u32string_view text, double angle = 0.0 throw py::type_error("flags must be LoadFlags or int"); } - self->x->set_text(text, angle, static_cast(flags), xys); + self->set_text(text, angle, static_cast(flags), xys); py::ssize_t dims[] = { static_cast(xys.size()) / 2, 2 }; py::array_t result(dims); @@ -743,12 +725,6 @@ PyFT2Font_set_text(PyFT2Font *self, std::u32string_view text, double angle = 0.0 const char *PyFT2Font_get_num_glyphs__doc__ = "Return the number of loaded glyphs."; -static size_t -PyFT2Font_get_num_glyphs(PyFT2Font *self) -{ - return self->x->get_num_glyphs(); -} - const char *PyFT2Font_load_char__doc__ = R"""( Load character in current fontfile and set glyph. @@ -798,7 +774,7 @@ PyFT2Font_load_char(PyFT2Font *self, long charcode, throw py::type_error("flags must be LoadFlags or int"); } - self->x->load_char(charcode, static_cast(flags), ft_object, fallback); + self->load_char(charcode, static_cast(flags), ft_object, fallback); return PyGlyph_from_FT2Font(ft_object); } @@ -850,9 +826,9 @@ PyFT2Font_load_glyph(PyFT2Font *self, FT_UInt glyph_index, throw py::type_error("flags must be LoadFlags or int"); } - self->x->load_glyph(glyph_index, static_cast(flags)); + self->load_glyph(glyph_index, static_cast(flags)); - return PyGlyph_from_FT2Font(self->x); + return PyGlyph_from_FT2Font(self); } const char *PyFT2Font_get_width_height__doc__ = R"""( @@ -872,16 +848,6 @@ const char *PyFT2Font_get_width_height__doc__ = R"""( .get_descent )"""; -static py::tuple -PyFT2Font_get_width_height(PyFT2Font *self) -{ - long width, height; - - self->x->get_width_height(&width, &height); - - return py::make_tuple(width, height); -} - const char *PyFT2Font_get_bitmap_offset__doc__ = R"""( Get the (x, y) offset for the bitmap if ink hangs left or below (0, 0). @@ -899,16 +865,6 @@ const char *PyFT2Font_get_bitmap_offset__doc__ = R"""( .get_descent )"""; -static py::tuple -PyFT2Font_get_bitmap_offset(PyFT2Font *self) -{ - long x, y; - - self->x->get_bitmap_offset(&x, &y); - - return py::make_tuple(x, y); -} - const char *PyFT2Font_get_descent__doc__ = R"""( Get the descent of the current string set by `.set_text`. @@ -926,12 +882,6 @@ const char *PyFT2Font_get_descent__doc__ = R"""( .get_width_height )"""; -static long -PyFT2Font_get_descent(PyFT2Font *self) -{ - return self->x->get_descent(); -} - const char *PyFT2Font_draw_glyphs_to_bitmap__doc__ = R"""( Draw the glyphs that were loaded by `.set_text` to the bitmap. @@ -947,12 +897,6 @@ const char *PyFT2Font_draw_glyphs_to_bitmap__doc__ = R"""( .draw_glyph_to_bitmap )"""; -static void -PyFT2Font_draw_glyphs_to_bitmap(PyFT2Font *self, bool antialiased = true) -{ - self->x->draw_glyphs_to_bitmap(antialiased); -} - const char *PyFT2Font_draw_glyph_to_bitmap__doc__ = R"""( Draw a single glyph to the bitmap at pixel locations x, y. @@ -987,7 +931,7 @@ PyFT2Font_draw_glyph_to_bitmap(PyFT2Font *self, py::buffer &image, auto xd = _double_to_("x", vxd); auto yd = _double_to_("y", vyd); - self->x->draw_glyph_to_bitmap( + self->draw_glyph_to_bitmap( py::array_t{image}, xd, yd, glyph->glyphInd, antialiased); } @@ -1021,7 +965,7 @@ PyFT2Font_get_glyph_name(PyFT2Font *self, unsigned int glyph_number) std::string buffer; buffer.resize(128); - self->x->get_glyph_name(glyph_number, buffer); + self->get_glyph_name(glyph_number, buffer); return buffer; } @@ -1043,10 +987,10 @@ PyFT2Font_get_charmap(PyFT2Font *self) { py::dict charmap; FT_UInt index; - FT_ULong code = FT_Get_First_Char(self->x->get_face(), &index); + FT_ULong code = FT_Get_First_Char(self->get_face(), &index); while (index != 0) { charmap[py::cast(code)] = py::cast(index); - code = FT_Get_Next_Char(self->x->get_face(), code, &index); + code = FT_Get_Next_Char(self->get_face(), code, &index); } return charmap; } @@ -1058,6 +1002,8 @@ const char *PyFT2Font_get_char_index__doc__ = R"""( ---------- codepoint : int A character code point in the current charmap (which defaults to Unicode.) + _fallback : bool + Whether to enable fallback fonts while searching for a character. Returns ------- @@ -1072,14 +1018,6 @@ const char *PyFT2Font_get_char_index__doc__ = R"""( .get_name_index )"""; -static FT_UInt -PyFT2Font_get_char_index(PyFT2Font *self, FT_ULong ccode) -{ - bool fallback = true; - - return self->x->get_char_index(ccode, fallback); -} - const char *PyFT2Font_get_sfnt__doc__ = R"""( Load the entire SFNT names table. @@ -1096,17 +1034,17 @@ const char *PyFT2Font_get_sfnt__doc__ = R"""( static py::dict PyFT2Font_get_sfnt(PyFT2Font *self) { - if (!(self->x->get_face()->face_flags & FT_FACE_FLAG_SFNT)) { + if (!(self->get_face()->face_flags & FT_FACE_FLAG_SFNT)) { throw py::value_error("No SFNT name table"); } - size_t count = FT_Get_Sfnt_Name_Count(self->x->get_face()); + size_t count = FT_Get_Sfnt_Name_Count(self->get_face()); py::dict names; for (FT_UInt j = 0; j < count; ++j) { FT_SfntName sfnt; - FT_Error error = FT_Get_Sfnt_Name(self->x->get_face(), j, &sfnt); + FT_Error error = FT_Get_Sfnt_Name(self->get_face(), j, &sfnt); if (error) { throw py::value_error("Could not get SFNT name"); @@ -1141,12 +1079,6 @@ const char *PyFT2Font_get_name_index__doc__ = R"""( .get_glyph_name )"""; -static long -PyFT2Font_get_name_index(PyFT2Font *self, char *glyphname) -{ - return self->x->get_name_index(glyphname); -} - const char *PyFT2Font_get_ps_font_info__doc__ = R"""( Return the information in the PS Font Info structure. @@ -1171,7 +1103,7 @@ PyFT2Font_get_ps_font_info(PyFT2Font *self) { PS_FontInfoRec fontinfo; - FT_Error error = FT_Get_PS_Font_Info(self->x->get_face(), &fontinfo); + FT_Error error = FT_Get_PS_Font_Info(self->get_face(), &fontinfo); if (error) { throw py::value_error("Could not get PS font info"); } @@ -1223,7 +1155,7 @@ PyFT2Font_get_sfnt_table(PyFT2Font *self, std::string tagname) return std::nullopt; } - void *table = FT_Get_Sfnt_Table(self->x->get_face(), tag); + void *table = FT_Get_Sfnt_Table(self->get_face(), tag); if (!table) { return std::nullopt; } @@ -1406,7 +1338,7 @@ PyFT2Font_get_path(PyFT2Font *self) std::vector vertices; std::vector codes; - self->x->get_path(vertices, codes); + self->get_path(vertices, codes); py::ssize_t length = codes.size(); py::ssize_t vertices_dims[2] = { length, 2 }; @@ -1435,12 +1367,6 @@ const char *PyFT2Font_get_image__doc__ = R"""( .get_path )"""; -static py::array -PyFT2Font_get_image(PyFT2Font *self) -{ - return self->x->get_image(); -} - const char *PyFT2Font__get_type1_encoding_vector__doc__ = R"""( Return a list mapping CharString indices of a Type 1 font to FreeType glyph indices. @@ -1452,7 +1378,7 @@ const char *PyFT2Font__get_type1_encoding_vector__doc__ = R"""( static std::array PyFT2Font__get_type1_encoding_vector(PyFT2Font *self) { - auto face = self->x->get_face(); + auto face = self->get_face(); auto indices = std::array{}; for (auto i = 0u; i < indices.size(); ++i) { auto len = FT_Get_PS_Font_Value(face, PS_DICT_ENCODING_ENTRY, i, nullptr, 0); @@ -1605,15 +1531,15 @@ PYBIND11_MODULE(ft2font, m, py::mod_gil_not_used()) PyFT2Font__doc__) .def(py::init(&PyFT2Font_init), "filename"_a, "hinting_factor"_a=8, py::kw_only(), - "_fallback_list"_a=py::none(), "_kerning_factor"_a=0, + "_fallback_list"_a=py::none(), "_kerning_factor"_a=py::none(), "_warn_if_used"_a=false, PyFT2Font_init__doc__) - .def("clear", &PyFT2Font_clear, PyFT2Font_clear__doc__) - .def("set_size", &PyFT2Font_set_size, "ptsize"_a, "dpi"_a, + .def("clear", &PyFT2Font::clear, PyFT2Font_clear__doc__) + .def("set_size", &PyFT2Font::set_size, "ptsize"_a, "dpi"_a, PyFT2Font_set_size__doc__) - .def("set_charmap", &PyFT2Font_set_charmap, "i"_a, + .def("set_charmap", &PyFT2Font::set_charmap, "i"_a, PyFT2Font_set_charmap__doc__) - .def("select_charmap", &PyFT2Font_select_charmap, "i"_a, + .def("select_charmap", &PyFT2Font::select_charmap, "i"_a, PyFT2Font_select_charmap__doc__) .def("get_kerning", &PyFT2Font_get_kerning, "left"_a, "right"_a, "mode"_a, PyFT2Font_get_kerning__doc__) @@ -1622,19 +1548,20 @@ PYBIND11_MODULE(ft2font, m, py::mod_gil_not_used()) PyFT2Font_set_text__doc__) .def("_get_fontmap", &PyFT2Font_get_fontmap, "string"_a, PyFT2Font_get_fontmap__doc__) - .def("get_num_glyphs", &PyFT2Font_get_num_glyphs, PyFT2Font_get_num_glyphs__doc__) + .def("get_num_glyphs", &PyFT2Font::get_num_glyphs, + PyFT2Font_get_num_glyphs__doc__) .def("load_char", &PyFT2Font_load_char, "charcode"_a, "flags"_a=LoadFlags::FORCE_AUTOHINT, PyFT2Font_load_char__doc__) .def("load_glyph", &PyFT2Font_load_glyph, "glyph_index"_a, "flags"_a=LoadFlags::FORCE_AUTOHINT, PyFT2Font_load_glyph__doc__) - .def("get_width_height", &PyFT2Font_get_width_height, + .def("get_width_height", &PyFT2Font::get_width_height, PyFT2Font_get_width_height__doc__) - .def("get_bitmap_offset", &PyFT2Font_get_bitmap_offset, + .def("get_bitmap_offset", &PyFT2Font::get_bitmap_offset, PyFT2Font_get_bitmap_offset__doc__) - .def("get_descent", &PyFT2Font_get_descent, PyFT2Font_get_descent__doc__) - .def("draw_glyphs_to_bitmap", &PyFT2Font_draw_glyphs_to_bitmap, + .def("get_descent", &PyFT2Font::get_descent, PyFT2Font_get_descent__doc__) + .def("draw_glyphs_to_bitmap", &PyFT2Font::draw_glyphs_to_bitmap, py::kw_only(), "antialiased"_a=true, PyFT2Font_draw_glyphs_to_bitmap__doc__); // The generated docstring uses an unqualified "Buffer" as type hint, @@ -1653,23 +1580,24 @@ PYBIND11_MODULE(ft2font, m, py::mod_gil_not_used()) .def("get_glyph_name", &PyFT2Font_get_glyph_name, "index"_a, PyFT2Font_get_glyph_name__doc__) .def("get_charmap", &PyFT2Font_get_charmap, PyFT2Font_get_charmap__doc__) - .def("get_char_index", &PyFT2Font_get_char_index, "codepoint"_a, + .def("get_char_index", &PyFT2Font::get_char_index, + "codepoint"_a, py::kw_only(), "_fallback"_a=true, PyFT2Font_get_char_index__doc__) .def("get_sfnt", &PyFT2Font_get_sfnt, PyFT2Font_get_sfnt__doc__) - .def("get_name_index", &PyFT2Font_get_name_index, "name"_a, + .def("get_name_index", &PyFT2Font::get_name_index, "name"_a, PyFT2Font_get_name_index__doc__) .def("get_ps_font_info", &PyFT2Font_get_ps_font_info, PyFT2Font_get_ps_font_info__doc__) .def("get_sfnt_table", &PyFT2Font_get_sfnt_table, "name"_a, PyFT2Font_get_sfnt_table__doc__) .def("get_path", &PyFT2Font_get_path, PyFT2Font_get_path__doc__) - .def("get_image", &PyFT2Font_get_image, PyFT2Font_get_image__doc__) + .def("get_image", &PyFT2Font::get_image, PyFT2Font_get_image__doc__) .def("_get_type1_encoding_vector", &PyFT2Font__get_type1_encoding_vector, PyFT2Font__get_type1_encoding_vector__doc__) .def_property_readonly( "postscript_name", [](PyFT2Font *self) { - if (const char *name = FT_Get_Postscript_Name(self->x->get_face())) { + if (const char *name = FT_Get_Postscript_Name(self->get_face())) { return name; } else { return "UNAVAILABLE"; @@ -1677,11 +1605,11 @@ PYBIND11_MODULE(ft2font, m, py::mod_gil_not_used()) }, "PostScript name of the font.") .def_property_readonly( "num_faces", [](PyFT2Font *self) { - return self->x->get_face()->num_faces; + return self->get_face()->num_faces; }, "Number of faces in file.") .def_property_readonly( "family_name", [](PyFT2Font *self) { - if (const char *name = self->x->get_face()->family_name) { + if (const char *name = self->get_face()->family_name) { return name; } else { return "UNAVAILABLE"; @@ -1689,7 +1617,7 @@ PYBIND11_MODULE(ft2font, m, py::mod_gil_not_used()) }, "Face family name.") .def_property_readonly( "style_name", [](PyFT2Font *self) { - if (const char *name = self->x->get_face()->style_name) { + if (const char *name = self->get_face()->style_name) { return name; } else { return "UNAVAILABLE"; @@ -1697,77 +1625,77 @@ PYBIND11_MODULE(ft2font, m, py::mod_gil_not_used()) }, "Style name.") .def_property_readonly( "face_flags", [](PyFT2Font *self) { - return static_cast(self->x->get_face()->face_flags); + return static_cast(self->get_face()->face_flags); }, "Face flags; see `.FaceFlags`.") .def_property_readonly( "style_flags", [](PyFT2Font *self) { - return static_cast(self->x->get_face()->style_flags & 0xffff); + return static_cast(self->get_face()->style_flags & 0xffff); }, "Style flags; see `.StyleFlags`.") .def_property_readonly( "num_named_instances", [](PyFT2Font *self) { - return (self->x->get_face()->style_flags & 0x7fff0000) >> 16; + return (self->get_face()->style_flags & 0x7fff0000) >> 16; }, "Number of named instances in the face.") .def_property_readonly( "num_glyphs", [](PyFT2Font *self) { - return self->x->get_face()->num_glyphs; + return self->get_face()->num_glyphs; }, "Number of glyphs in the face.") .def_property_readonly( "num_fixed_sizes", [](PyFT2Font *self) { - return self->x->get_face()->num_fixed_sizes; + return self->get_face()->num_fixed_sizes; }, "Number of bitmap in the face.") .def_property_readonly( "num_charmaps", [](PyFT2Font *self) { - return self->x->get_face()->num_charmaps; + return self->get_face()->num_charmaps; }, "Number of charmaps in the face.") .def_property_readonly( "scalable", [](PyFT2Font *self) { - return bool(FT_IS_SCALABLE(self->x->get_face())); + return bool(FT_IS_SCALABLE(self->get_face())); }, "Whether face is scalable; attributes after this one " "are only defined for scalable faces.") .def_property_readonly( "units_per_EM", [](PyFT2Font *self) { - return self->x->get_face()->units_per_EM; + return self->get_face()->units_per_EM; }, "Number of font units covered by the EM.") .def_property_readonly( "bbox", [](PyFT2Font *self) { - FT_BBox bbox = self->x->get_face()->bbox; + FT_BBox bbox = self->get_face()->bbox; return py::make_tuple(bbox.xMin, bbox.yMin, bbox.xMax, bbox.yMax); }, "Face global bounding box (xmin, ymin, xmax, ymax).") .def_property_readonly( "ascender", [](PyFT2Font *self) { - return self->x->get_face()->ascender; + return self->get_face()->ascender; }, "Ascender in 26.6 units.") .def_property_readonly( "descender", [](PyFT2Font *self) { - return self->x->get_face()->descender; + return self->get_face()->descender; }, "Descender in 26.6 units.") .def_property_readonly( "height", [](PyFT2Font *self) { - return self->x->get_face()->height; + return self->get_face()->height; }, "Height in 26.6 units; used to compute a default line spacing " "(baseline-to-baseline distance).") .def_property_readonly( "max_advance_width", [](PyFT2Font *self) { - return self->x->get_face()->max_advance_width; + return self->get_face()->max_advance_width; }, "Maximum horizontal cursor advance for all glyphs.") .def_property_readonly( "max_advance_height", [](PyFT2Font *self) { - return self->x->get_face()->max_advance_height; + return self->get_face()->max_advance_height; }, "Maximum vertical cursor advance for all glyphs.") .def_property_readonly( "underline_position", [](PyFT2Font *self) { - return self->x->get_face()->underline_position; + return self->get_face()->underline_position; }, "Vertical position of the underline bar.") .def_property_readonly( "underline_thickness", [](PyFT2Font *self) { - return self->x->get_face()->underline_thickness; + return self->get_face()->underline_thickness; }, "Thickness of the underline bar.") .def_property_readonly( "fname", &PyFT2Font_fname, "The original filename for this object.") .def_buffer([](PyFT2Font &self) -> py::buffer_info { - return self.x->get_image().request(); + return self.get_image().request(); }); m.attr("__freetype_version__") = version_string;