Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 9674b53

Browse files
authored
Merge pull request #28785 from QuLogic/ft2font-pybind11
Convert ft2font extension to pybind11
2 parents c56c404 + a0649e7 commit 9674b53

File tree

9 files changed

+736
-1106
lines changed

9 files changed

+736
-1106
lines changed

doc/conf.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,20 @@ def tutorials_download_error(record):
230230
autodoc_docstring_signature = True
231231
autodoc_default_options = {'members': None, 'undoc-members': None}
232232

233+
234+
def autodoc_process_bases(app, name, obj, options, bases):
235+
"""
236+
Hide pybind11 base object from inheritance tree.
237+
238+
Note, *bases* must be modified in place.
239+
"""
240+
for cls in bases[:]:
241+
if not isinstance(cls, type):
242+
continue
243+
if cls.__module__ == 'pybind11_builtins' and cls.__name__ == 'pybind11_object':
244+
bases.remove(cls)
245+
246+
233247
# make sure to ignore warnings that stem from simply inspecting deprecated
234248
# class-level attributes
235249
warnings.filterwarnings('ignore', category=DeprecationWarning,
@@ -847,5 +861,6 @@ def setup(app):
847861
bld_type = 'rel'
848862
app.add_config_value('skip_sub_dirs', 0, '')
849863
app.add_config_value('releaselevel', bld_type, 'env')
864+
app.connect('autodoc-process-bases', autodoc_process_bases)
850865
if sphinx.version_info[:2] < (7, 1):
851866
app.connect('html-page-context', add_html_cache_busting, priority=1000)

doc/missing-references.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,9 @@
268268
"<unknown>:1",
269269
"doc/api/_as_gen/mpl_toolkits.axisartist.floating_axes.rst:32:<autosummary>:1"
270270
],
271+
"numpy.float64": [
272+
"doc/docstring of matplotlib.ft2font.PyCapsule.set_text:1"
273+
],
271274
"numpy.uint8": [
272275
"<unknown>:1"
273276
]
@@ -349,9 +352,6 @@
349352
"Figure.stale_callback": [
350353
"doc/users/explain/figure/interactive_guide.rst:333"
351354
],
352-
"Glyph": [
353-
"doc/gallery/misc/ftface_props.rst:25"
354-
],
355355
"Image": [
356356
"lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.gci:4"
357357
],

lib/matplotlib/ft2font.pyi

Lines changed: 66 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
import sys
12
from typing import BinaryIO, Literal, TypedDict, final, overload
3+
from typing_extensions import Buffer # < Py 3.12
24

35
import numpy as np
46
from numpy.typing import NDArray
@@ -159,28 +161,7 @@ class _SfntPcltDict(TypedDict):
159161
serifStyle: int
160162

161163
@final
162-
class FT2Font:
163-
ascender: int
164-
bbox: tuple[int, int, int, int]
165-
descender: int
166-
face_flags: int
167-
family_name: str
168-
fname: str
169-
height: int
170-
max_advance_height: int
171-
max_advance_width: int
172-
num_charmaps: int
173-
num_faces: int
174-
num_fixed_sizes: int
175-
num_glyphs: int
176-
postscript_name: str
177-
scalable: bool
178-
style_flags: int
179-
style_name: str
180-
underline_position: int
181-
underline_thickness: int
182-
units_per_EM: int
183-
164+
class FT2Font(Buffer):
184165
def __init__(
185166
self,
186167
filename: str | BinaryIO,
@@ -189,6 +170,8 @@ class FT2Font:
189170
_fallback_list: list[FT2Font] | None = ...,
190171
_kerning_factor: int = ...
191172
) -> None: ...
173+
if sys.version_info[:2] >= (3, 12):
174+
def __buffer__(self, flags: int) -> memoryview: ...
192175
def _get_fontmap(self, string: str) -> dict[str, FT2Font]: ...
193176
def clear(self) -> None: ...
194177
def draw_glyph_to_bitmap(
@@ -232,23 +215,73 @@ class FT2Font:
232215
def set_text(
233216
self, string: str, angle: float = ..., flags: int = ...
234217
) -> NDArray[np.float64]: ...
218+
@property
219+
def ascender(self) -> int: ...
220+
@property
221+
def bbox(self) -> tuple[int, int, int, int]: ...
222+
@property
223+
def descender(self) -> int: ...
224+
@property
225+
def face_flags(self) -> int: ...
226+
@property
227+
def family_name(self) -> str: ...
228+
@property
229+
def fname(self) -> str: ...
230+
@property
231+
def height(self) -> int: ...
232+
@property
233+
def max_advance_height(self) -> int: ...
234+
@property
235+
def max_advance_width(self) -> int: ...
236+
@property
237+
def num_charmaps(self) -> int: ...
238+
@property
239+
def num_faces(self) -> int: ...
240+
@property
241+
def num_fixed_sizes(self) -> int: ...
242+
@property
243+
def num_glyphs(self) -> int: ...
244+
@property
245+
def postscript_name(self) -> str: ...
246+
@property
247+
def scalable(self) -> bool: ...
248+
@property
249+
def style_flags(self) -> int: ...
250+
@property
251+
def style_name(self) -> str: ...
252+
@property
253+
def underline_position(self) -> int: ...
254+
@property
255+
def underline_thickness(self) -> int: ...
256+
@property
257+
def units_per_EM(self) -> int: ...
235258

236259
@final
237-
class FT2Image: # TODO: When updating mypy>=1.4, subclass from Buffer.
260+
class FT2Image(Buffer):
238261
def __init__(self, width: float, height: float) -> None: ...
239262
def draw_rect_filled(self, x0: float, y0: float, x1: float, y1: float) -> None: ...
263+
if sys.version_info[:2] >= (3, 12):
264+
def __buffer__(self, flags: int) -> memoryview: ...
240265

241266
@final
242267
class Glyph:
243-
width: int
244-
height: int
245-
horiBearingX: int
246-
horiBearingY: int
247-
horiAdvance: int
248-
linearHoriAdvance: int
249-
vertBearingX: int
250-
vertBearingY: int
251-
vertAdvance: int
252-
268+
@property
269+
def width(self) -> int: ...
270+
@property
271+
def height(self) -> int: ...
272+
@property
273+
def horiBearingX(self) -> int: ...
274+
@property
275+
def horiBearingY(self) -> int: ...
276+
@property
277+
def horiAdvance(self) -> int: ...
278+
@property
279+
def linearHoriAdvance(self) -> int: ...
280+
@property
281+
def vertBearingX(self) -> int: ...
282+
@property
283+
def vertBearingY(self) -> int: ...
284+
@property
285+
def vertAdvance(self) -> int: ...
253286
@property
254287
def bbox(self) -> tuple[int, int, int, int]: ...

lib/matplotlib/tests/test_ft2font.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,8 @@ def test_ft2font_invalid_args(tmp_path):
130130
# filename argument.
131131
with pytest.raises(TypeError, match='to a font file or a binary-mode file object'):
132132
ft2font.FT2Font(None)
133+
with pytest.raises(TypeError, match='to a font file or a binary-mode file object'):
134+
ft2font.FT2Font(object()) # Not bytes or string, and has no read() method.
133135
file = tmp_path / 'invalid-font.ttf'
134136
file.write_text('This is not a valid font file.')
135137
with (pytest.raises(TypeError, match='to a font file or a binary-mode file object'),
@@ -145,19 +147,19 @@ def test_ft2font_invalid_args(tmp_path):
145147
file = fm.findfont('DejaVu Sans')
146148

147149
# hinting_factor argument.
148-
with pytest.raises(TypeError, match='cannot be interpreted as an integer'):
150+
with pytest.raises(TypeError, match='incompatible constructor arguments'):
149151
ft2font.FT2Font(file, 1.3)
150152
with pytest.raises(ValueError, match='hinting_factor must be greater than 0'):
151153
ft2font.FT2Font(file, 0)
152154

153-
with pytest.raises(TypeError, match='Fallback list must be a list'):
155+
with pytest.raises(TypeError, match='incompatible constructor arguments'):
154156
# failing to be a list will fail before the 0
155157
ft2font.FT2Font(file, _fallback_list=(0,)) # type: ignore[arg-type]
156-
with pytest.raises(TypeError, match='Fallback fonts must be FT2Font objects.'):
158+
with pytest.raises(TypeError, match='incompatible constructor arguments'):
157159
ft2font.FT2Font(file, _fallback_list=[0]) # type: ignore[list-item]
158160

159161
# kerning_factor argument.
160-
with pytest.raises(TypeError, match='cannot be interpreted as an integer'):
162+
with pytest.raises(TypeError, match='incompatible constructor arguments'):
161163
ft2font.FT2Font(file, _kerning_factor=1.3)
162164

163165

requirements/testing/mypy.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Extra pip requirements for the GitHub Actions mypy build
22

33
mypy>=1.9
4-
typing-extensions>=4.1
4+
typing-extensions>=4.6
55

66
# Extra stubs distributed separately from the main pypi package
77
pandas-stubs

src/ft2font.cpp

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* -*- mode: c++; c-basic-offset: 4 -*- */
22

33
#include <algorithm>
4-
#include <charconv>
4+
#include <cstdio>
55
#include <iterator>
66
#include <set>
77
#include <sstream>
@@ -397,7 +397,7 @@ void FT2Font::set_kerning_factor(int factor)
397397
}
398398

399399
void FT2Font::set_text(
400-
size_t N, uint32_t *codepoints, double angle, FT_Int32 flags, std::vector<double> &xys)
400+
std::u32string_view text, double angle, FT_Int32 flags, std::vector<double> &xys)
401401
{
402402
FT_Matrix matrix; /* transformation matrix */
403403

@@ -420,7 +420,7 @@ void FT2Font::set_text(
420420
FT_UInt previous = 0;
421421
FT2Font *previous_ft_object = NULL;
422422

423-
for (size_t n = 0; n < N; n++) {
423+
for (auto codepoint : text) {
424424
FT_UInt glyph_index = 0;
425425
FT_BBox glyph_bbox;
426426
FT_Pos last_advance;
@@ -429,14 +429,14 @@ void FT2Font::set_text(
429429
std::set<FT_String*> glyph_seen_fonts;
430430
FT2Font *ft_object_with_glyph = this;
431431
bool was_found = load_char_with_fallback(ft_object_with_glyph, glyph_index, glyphs,
432-
char_to_font, glyph_to_font, codepoints[n], flags,
432+
char_to_font, glyph_to_font, codepoint, flags,
433433
charcode_error, glyph_error, glyph_seen_fonts, false);
434434
if (!was_found) {
435-
ft_glyph_warn((FT_ULong)codepoints[n], glyph_seen_fonts);
435+
ft_glyph_warn((FT_ULong)codepoint, glyph_seen_fonts);
436436
// render missing glyph tofu
437437
// come back to top-most font
438438
ft_object_with_glyph = this;
439-
char_to_font[codepoints[n]] = ft_object_with_glyph;
439+
char_to_font[codepoint] = ft_object_with_glyph;
440440
glyph_to_font[glyph_index] = ft_object_with_glyph;
441441
ft_object_with_glyph->load_glyph(glyph_index, flags, ft_object_with_glyph, false);
442442
}
@@ -727,13 +727,20 @@ void FT2Font::get_glyph_name(unsigned int glyph_number, std::string &buffer,
727727
if (!FT_HAS_GLYPH_NAMES(face)) {
728728
/* Note that this generated name must match the name that
729729
is generated by ttconv in ttfont_CharStrings_getname. */
730-
buffer.replace(0, 3, "uni");
731-
std::to_chars(buffer.data() + 3, buffer.data() + buffer.size(),
732-
glyph_number, 16);
730+
auto len = snprintf(buffer.data(), buffer.size(), "uni%08x", glyph_number);
731+
if (len >= 0) {
732+
buffer.resize(len);
733+
} else {
734+
throw std::runtime_error("Failed to convert glyph to standard name");
735+
}
733736
} else {
734737
if (FT_Error error = FT_Get_Glyph_Name(face, glyph_number, buffer.data(), buffer.size())) {
735738
throw_ft_error("Could not get glyph names", error);
736739
}
740+
auto len = buffer.find('\0');
741+
if (len != buffer.npos) {
742+
buffer.resize(len);
743+
}
737744
}
738745
}
739746

src/ft2font.h

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,9 @@
66
#ifndef MPL_FT2FONT_H
77
#define MPL_FT2FONT_H
88

9-
#define PY_SSIZE_T_CLEAN
10-
#include <Python.h>
11-
12-
#include <cstdint>
139
#include <set>
1410
#include <string>
11+
#include <string_view>
1512
#include <unordered_map>
1613
#include <vector>
1714

@@ -80,8 +77,8 @@ class FT2Font
8077
void set_size(double ptsize, double dpi);
8178
void set_charmap(int i);
8279
void select_charmap(unsigned long i);
83-
void set_text(
84-
size_t N, uint32_t *codepoints, double angle, FT_Int32 flags, std::vector<double> &xys);
80+
void set_text(std::u32string_view codepoints, double angle, FT_Int32 flags,
81+
std::vector<double> &xys);
8582
int get_kerning(FT_UInt left, FT_UInt right, FT_UInt mode, bool fallback);
8683
int get_kerning(FT_UInt left, FT_UInt right, FT_UInt mode, FT_Vector &delta);
8784
void set_kerning_factor(int factor);

0 commit comments

Comments
 (0)