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

Skip to content

Commit c719d89

Browse files
committed
print fontname in missing glyph warning + all fallback fonts missing glyph
catch missing glyph errors in font tests
1 parent e394940 commit c719d89

File tree

6 files changed

+70
-40
lines changed

6 files changed

+70
-40
lines changed

lib/matplotlib/_text_helpers.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,12 @@
1212
"LayoutItem", ["ft_object", "char", "glyph_idx", "x", "prev_kern"])
1313

1414

15-
def warn_on_missing_glyph(codepoint):
15+
def warn_on_missing_glyph(codepoint, font_name):
1616
_api.warn_external(
17-
"Glyph {} ({}) missing from current font.".format(
17+
"Glyph {} ({}) missing from current font {}.".format(
1818
codepoint,
19-
chr(codepoint).encode("ascii", "namereplace").decode("ascii")))
19+
chr(codepoint).encode("ascii", "namereplace").decode("ascii"),
20+
font_name))
2021
block = ("Hebrew" if 0x0590 <= codepoint <= 0x05ff else
2122
"Arabic" if 0x0600 <= codepoint <= 0x06ff else
2223
"Devanagari" if 0x0900 <= codepoint <= 0x097f else

lib/matplotlib/tests/test_backend_pdf.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,3 +443,17 @@ def test_multi_font_type42():
443443

444444
fig = plt.figure()
445445
fig.text(0.15, 0.475, "There are 几个汉字 in between!")
446+
447+
448+
def test_otf_font_smoke():
449+
# checks that there's no segfault
450+
fp = fm.FontProperties(family=["Noto Sans TC"])
451+
if Path(fm.findfont(fp)).name != "NotoSansTC-Regular.otf":
452+
pytest.skip("Font may be missing")
453+
454+
plt.rc('font', family=['DejaVu Sans', "Noto Sans TC"], size=27)
455+
plt.rc('pdf', fonttype=42)
456+
457+
fig = plt.figure()
458+
fig.text(0.15, 0.475, "There are 几个汉字 in between!")
459+
fig.canvas.draw()

lib/matplotlib/tests/test_ft2font.py

Lines changed: 34 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -30,25 +30,21 @@ def test_ft2font_positive_hinting_factor():
3030
ft2font.FT2Font(file_name, 0)
3131

3232

33-
def test_fallback_smoke():
34-
fp = fm.FontProperties(family=["WenQuanYi Zen Hei"])
35-
if Path(fm.findfont(fp)).name != "wqy-zenhei.ttc":
36-
pytest.skip("Font wqy-zenhei.ttc may be missing")
37-
38-
fp = fm.FontProperties(family=["Noto Sans CJK JP"])
39-
if Path(fm.findfont(fp)).name != "NotoSansCJK-Regular.ttc":
40-
pytest.skip("Noto Sans CJK JP font may be missing.")
41-
33+
@pytest.mark.parametrize('family_name, file_name',
34+
[("WenQuanYi Zen Hei", "wqy-zenhei.ttc"),
35+
("Noto Sans CJK JP", "NotoSansCJK.ttc"),
36+
("Noto Sans TC", "NotoSansTC-Regular.otf")]
37+
)
38+
def test_fallback_smoke(family_name, file_name):
39+
fp = fm.FontProperties(family=[family_name])
40+
if Path(fm.findfont(fp)).name != file_name:
41+
pytest.skip(f"Font {family_name} ({file_name}) is missing")
4242
plt.rcParams['font.size'] = 20
4343
fig = plt.figure(figsize=(4.75, 1.85))
4444
fig.text(0.05, 0.45, "There are 几个汉字 in between!",
45-
family=['DejaVu Sans', "Noto Sans CJK JP"])
46-
fig.text(0.05, 0.25, "There are 几个汉字 in between!",
47-
family=['DejaVu Sans', "WenQuanYi Zen Hei"])
48-
fig.text(0.05, 0.65, "There are 几个汉字 in between!",
49-
family=["Noto Sans CJK JP"])
45+
family=['DejaVu Sans', family_name])
5046
fig.text(0.05, 0.85, "There are 几个汉字 in between!",
51-
family=["WenQuanYi Zen Hei"])
47+
family=[family_name])
5248

5349
# TODO enable fallback for other backends!
5450
for fmt in ['png', 'raw']: # ["svg", "pdf", "ps"]:
@@ -57,7 +53,8 @@ def test_fallback_smoke():
5753

5854
@pytest.mark.parametrize('family_name, file_name',
5955
[("WenQuanYi Zen Hei", "wqy-zenhei"),
60-
("Noto Sans CJK JP", "NotoSansCJK")]
56+
("Noto Sans CJK JP", "NotoSansCJK"),
57+
("Noto Sans TC", "NotoSansTC-Regular.otf")]
6158
)
6259
@check_figures_equal(extensions=["png", "pdf", "eps", "svg"])
6360
def test_font_fallback_chinese(fig_test, fig_ref, family_name, file_name):
@@ -78,25 +75,41 @@ def test_font_fallback_chinese(fig_test, fig_ref, family_name, file_name):
7875
fig_test.text(0.05, .85 - 0.15*j, txt, family=test_font)
7976

8077

78+
@pytest.mark.parametrize("font_list",
79+
[['DejaVu Serif', 'DejaVu Sans'],
80+
['DejaVu Serif']],
81+
ids=["two fonts", "one font"])
82+
def test_fallback_missing(recwarn, font_list):
83+
fig = plt.figure()
84+
fig.text(.5, .5, "Hello 🙃 World!", family=font_list)
85+
fig.canvas.draw()
86+
assert all(isinstance(warn.message, UserWarning) for warn in recwarn)
87+
e = "Glyph 128579 (\\N{{UPSIDE-DOWN FACE}}) missing from current font {}."
88+
assert ([(warn.message.args) for warn in recwarn] ==
89+
[(e.format(font),) for font in font_list[::-1]])
90+
91+
8192
@pytest.mark.parametrize(
8293
"family_name, file_name",
8394
[
8495
("WenQuanYi Zen Hei", "wqy-zenhei"),
8596
("Noto Sans CJK JP", "NotoSansCJK"),
97+
("Noto Sans TC", "NotoSansTC-Regular.otf")
8698
],
8799
)
88100
def test__get_fontmap(family_name, file_name):
89101
fp = fm.FontProperties(family=[family_name])
102+
90103
found_file_name = Path(fm.findfont(fp)).name
91104
if file_name not in found_file_name:
92105
pytest.skip(f"Font {family_name} ({file_name}) is missing")
93106

94107
text = "There are 几个汉字 in between!"
95-
ft = fm.get_font(
96-
fm.fontManager._find_fonts_by_props(
97-
fm.FontProperties(family=["DejaVu Sans", family_name])
98-
)
99-
)
108+
109+
# breaking this out to debug segfault
110+
font_properties = fm.FontProperties(family=["DejaVu Sans", family_name])
111+
fonts = fm.fontManager._find_fonts_by_props(font_properties)
112+
ft = fm.get_font(fonts)
100113

101114
fontmap = ft._get_fontmap(text)
102115
for char, font in fontmap.items():

lib/matplotlib/tests/test_text.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -817,12 +817,13 @@ def test_pdf_kerning():
817817

818818
def test_unsupported_script(recwarn):
819819
fig = plt.figure()
820-
fig.text(.5, .5, "\N{BENGALI DIGIT ZERO}")
820+
t = fig.text(.5, .5, "\N{BENGALI DIGIT ZERO}")
821821
fig.canvas.draw()
822822
assert all(isinstance(warn.message, UserWarning) for warn in recwarn)
823823
assert (
824824
[warn.message.args for warn in recwarn] ==
825-
[(r"Glyph 2534 (\N{BENGALI DIGIT ZERO}) missing from current font.",),
825+
[(r"Glyph 2534 (\N{BENGALI DIGIT ZERO}) missing from current font "
826+
+ f"{t.get_fontname()}.",),
826827
(r"Matplotlib currently does not support Bengali natively.",)])
827828

828829

src/ft2font.cpp

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -184,11 +184,13 @@ FT2Image::draw_rect_filled(unsigned long x0, unsigned long y0, unsigned long x1,
184184
m_dirty = true;
185185
}
186186

187-
static void ft_glyph_warn(FT_ULong charcode)
187+
static void ft_glyph_warn(FT_ULong charcode, FT_String *family_name)
188188
{
189+
const char* name = (family_name != NULL)?family_name:"UNAVAILABLE";
189190
PyObject *text_helpers = NULL, *tmp = NULL;
191+
190192
if (!(text_helpers = PyImport_ImportModule("matplotlib._text_helpers")) ||
191-
!(tmp = PyObject_CallMethod(text_helpers, "warn_on_missing_glyph", "k", charcode))) {
193+
!(tmp = PyObject_CallMethod(text_helpers, "warn_on_missing_glyph", "(k, s)", charcode, name))) {
192194
goto exit;
193195
}
194196
exit:
@@ -207,7 +209,7 @@ ft_get_char_index_or_warn(FT_Face face, FT_ULong charcode, bool warn = true)
207209
return glyph_index;
208210
}
209211
if (warn) {
210-
ft_glyph_warn(charcode);
212+
ft_glyph_warn(charcode, face->family_name);
211213
}
212214
return 0;
213215
}
@@ -515,8 +517,6 @@ void FT2Font::set_text(
515517
char_to_font, glyph_to_font, codepoints[n], flags,
516518
charcode_error, glyph_error, false);
517519
if (!was_found) {
518-
ft_glyph_warn((FT_ULong)codepoints[n]);
519-
520520
// render missing glyph tofu
521521
// come back to top-most font
522522
ft_object_with_glyph = this;
@@ -582,7 +582,6 @@ void FT2Font::load_char(long charcode, FT_Int32 flags, FT2Font *&ft_object, bool
582582
bool was_found = load_char_with_fallback(ft_object_with_glyph, final_glyph_index, glyphs, char_to_font,
583583
glyph_to_font, charcode, flags, charcode_error, glyph_error, true);
584584
if (!was_found) {
585-
ft_glyph_warn(charcode);
586585
if (charcode_error) {
587586
throw_ft_error("Could not load charcode", charcode_error);
588587
}
@@ -609,7 +608,8 @@ void FT2Font::load_char(long charcode, FT_Int32 flags, FT2Font *&ft_object, bool
609608

610609
bool FT2Font::get_char_fallback_index(FT_ULong charcode, int& index) const
611610
{
612-
FT_UInt glyph_index = FT_Get_Char_Index(face, charcode);
611+
/** can segfault if set to true*/
612+
FT_UInt glyph_index = ft_get_char_index_or_warn(face, charcode, false);
613613
if (glyph_index) {
614614
// -1 means the host has the char and we do not need to fallback
615615
index = -1;
@@ -642,17 +642,15 @@ bool FT2Font::load_char_with_fallback(FT2Font *&ft_object_with_glyph,
642642
FT_Error &glyph_error,
643643
bool override = false)
644644
{
645-
FT_UInt glyph_index = FT_Get_Char_Index(face, charcode);
646-
645+
FT_UInt glyph_index = ft_get_char_index_or_warn(face, charcode, false);
646+
family_names.push_back(face->family_name);
647647
if (glyph_index || override) {
648-
charcode_error = FT_Load_Glyph(face, glyph_index, flags);
649-
if (charcode_error) {
648+
if ((charcode_error=FT_Load_Glyph(face, glyph_index, flags))) {
650649
return false;
651650
}
652651

653652
FT_Glyph thisGlyph;
654-
glyph_error = FT_Get_Glyph(face->glyph, &thisGlyph);
655-
if (glyph_error) {
653+
if ((glyph_error= FT_Get_Glyph(face->glyph, &thisGlyph))){
656654
return false;
657655
}
658656

@@ -667,7 +665,6 @@ bool FT2Font::load_char_with_fallback(FT2Font *&ft_object_with_glyph,
667665
parent_glyphs.push_back(thisGlyph);
668666
return true;
669667
}
670-
671668
else {
672669
for (size_t i = 0; i < fallbacks.size(); ++i) {
673670
bool was_found = fallbacks[i]->load_char_with_fallback(
@@ -677,6 +674,9 @@ bool FT2Font::load_char_with_fallback(FT2Font *&ft_object_with_glyph,
677674
return true;
678675
}
679676
}
677+
for (auto it = family_names.begin(); it != family_names.end(); ++it){
678+
ft_glyph_warn(charcode, *it);
679+
}
680680
return false;
681681
}
682682
}

src/ft2font.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ class FT2Font
144144
FT_Vector pen; /* untransformed origin */
145145
std::vector<FT_Glyph> glyphs;
146146
std::vector<FT2Font *> fallbacks;
147+
std::vector<FT_String *> family_names;
147148
std::unordered_map<FT_UInt, FT2Font *> glyph_to_font;
148149
std::unordered_map<long, FT2Font *> char_to_font;
149150
FT_BBox bbox;

0 commit comments

Comments
 (0)