Skip to content

Commit 738cddb

Browse files
committed
Implement font fallback for libraqm
1 parent b183d30 commit 738cddb

File tree

2 files changed

+58
-4
lines changed

2 files changed

+58
-4
lines changed

lib/matplotlib/tests/test_text.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,10 @@ def test_complex_shaping():
122122
'Arabic: \N{Arabic Letter REH}\N{Arabic FATHA}\N{Arabic Letter QAF}'
123123
'\N{Arabic SUKUN}\N{Arabic Letter MEEM}')
124124
fig = plt.figure(figsize=(3, 1))
125-
fig.text(0.5, 0.5, text, size=32, ha='center', va='center')
125+
fig.text(0.5, 0.75, text, size=32, ha='center', va='center')
126+
# Also check fallback behaviour.
127+
fig.text(0.5, 0.25, text, size=32, ha='center', va='center',
128+
family=['cmr10', 'DejaVu Sans'])
126129

127130

128131
@image_comparison(['multiline'])

src/ft2font.cpp

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -360,13 +360,64 @@ void FT2Font::set_text(
360360
throw std::runtime_error("failed to set text flags for layout");
361361
}
362362

363-
std::set<FT_String*> glyph_seen_fonts;
364-
glyph_seen_fonts.insert(face->family_name);
365-
366363
if (!raqm_layout(rq)) {
367364
throw std::runtime_error("failed to layout text");
368365
}
369366

367+
std::vector<std::pair<size_t, const FT_Face&>> face_substitutions;
368+
std::set<FT_String*> glyph_seen_fonts;
369+
glyph_seen_fonts.insert(face->family_name);
370+
371+
// Attempt to use fallback fonts if necessary.
372+
for (auto const& fallback : fallbacks) {
373+
size_t num_glyphs = 0;
374+
auto const& rq_glyphs = raqm_get_glyphs(rq, &num_glyphs);
375+
bool new_fallback_used = false;
376+
377+
for (size_t i = 0; i < num_glyphs; i++) {
378+
auto const& rglyph = rq_glyphs[i];
379+
380+
if (rglyph.index == 0) {
381+
auto end = (i < num_glyphs - 1) ? rq_glyphs[i + 1].cluster : text.size();
382+
for (auto j = rglyph.cluster; j < end; j++) {
383+
face_substitutions.emplace_back(j, fallback->face);
384+
}
385+
new_fallback_used = true;
386+
}
387+
}
388+
389+
if (new_fallback_used) {
390+
// If a fallback was used, then re-attempt the layout with the new fonts.
391+
if (!fallback->warn_if_used) {
392+
glyph_seen_fonts.insert(fallback->face->family_name);
393+
}
394+
395+
raqm_clear_contents(rq);
396+
if (!raqm_set_text(rq,
397+
reinterpret_cast<const uint32_t *>(text.data()),
398+
text.size()))
399+
{
400+
throw std::runtime_error("failed to set text for layout");
401+
}
402+
if (!raqm_set_freetype_face(rq, face)) {
403+
throw std::runtime_error("failed to set text face for layout");
404+
}
405+
for (auto [cluster, fallback] : face_substitutions) {
406+
raqm_set_freetype_face_range(rq, fallback, cluster, 1);
407+
}
408+
if (!raqm_set_freetype_load_flags(rq, flags)) {
409+
throw std::runtime_error("failed to set text flags for layout");
410+
}
411+
412+
if (!raqm_layout(rq)) {
413+
throw std::runtime_error("failed to layout text");
414+
}
415+
} else {
416+
// If we never used a fallback, then we're good to go with the existing
417+
// layout we have already made.
418+
break;
419+
}
420+
}
370421

371422
size_t num_glyphs = 0;
372423
auto const& rq_glyphs = raqm_get_glyphs(rq, &num_glyphs);

0 commit comments

Comments
 (0)