Skip to content

Commit 9813523

Browse files
committed
Implement font fallback for libraqm
1 parent b0ded3a commit 9813523

File tree

2 files changed

+73
-2
lines changed

2 files changed

+73
-2
lines changed

lib/matplotlib/tests/test_text.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,16 @@ def test_complex_shaping():
121121
text = (
122122
'Arabic: \N{Arabic Letter REH}\N{Arabic FATHA}\N{Arabic Letter QAF}'
123123
'\N{Arabic SUKUN}\N{Arabic Letter MEEM}')
124-
fig = plt.figure(figsize=(3, 1))
125-
fig.text(0.5, 0.5, text, size=32, ha='center', va='center')
124+
math_signs = '\N{N-ary Product}\N{N-ary Coproduct}\N{N-ary summation}\N{Integral}'
125+
text = math_signs + text + math_signs
126+
fig = plt.figure(figsize=(6, 2))
127+
fig.text(0.5, 0.75, text, size=32, ha='center', va='center')
128+
# Also check fallback behaviour:
129+
# - English should use cmr10
130+
# - Math signs should use DejaVu Sans Display (and thus be larger than the rest)
131+
# - Arabic should use DejaVu Sans
132+
fig.text(0.5, 0.25, text, size=32, ha='center', va='center',
133+
family=['cmr10', 'DejaVu Sans Display', 'DejaVu Sans'])
126134

127135

128136
@image_comparison(['multiline'])

src/ft2font.cpp

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <algorithm>
77
#include <cstdio>
88
#include <iterator>
9+
#include <map>
910
#include <set>
1011
#include <stdexcept>
1112
#include <string>
@@ -361,9 +362,71 @@ void FT2Font::set_text(
361362
throw std::runtime_error("failed to layout text");
362363
}
363364

365+
std::vector<std::pair<size_t, const FT_Face&>> face_substitutions;
364366
std::set<FT_String*> glyph_seen_fonts;
365367
glyph_seen_fonts.insert(face->family_name);
366368

369+
// Attempt to use fallback fonts if necessary.
370+
for (auto const& fallback : fallbacks) {
371+
size_t num_glyphs = 0;
372+
auto const& rq_glyphs = raqm_get_glyphs(rq, &num_glyphs);
373+
bool new_fallback_used = false;
374+
375+
// Sort clusters (n.b. std::map is ordered), as RTL text will be returned in
376+
// display, not source, order.
377+
std::map<decltype(raqm_glyph_t::cluster), bool> cluster_missing;
378+
for (size_t i = 0; i < num_glyphs; i++) {
379+
auto const& rglyph = rq_glyphs[i];
380+
381+
// Sometimes multiple glyphs are necessary for a single cluster; if any are
382+
// not found, we want to "poison" the whole set and keep them missing.
383+
cluster_missing[rglyph.cluster] |= (rglyph.index == 0);
384+
}
385+
386+
for (auto it = cluster_missing.cbegin(); it != cluster_missing.cend(); ) {
387+
auto [cluster, missing] = *it;
388+
++it; // Early change so we can access the next cluster below.
389+
if (missing) {
390+
auto next = (it != cluster_missing.cend()) ? it->first : text.size();
391+
for (auto i = cluster; i < next; i++) {
392+
face_substitutions.emplace_back(i, fallback->face);
393+
}
394+
new_fallback_used = true;
395+
}
396+
}
397+
398+
if (!new_fallback_used) {
399+
// If we never used a fallback, then we're good to go with the existing
400+
// layout we have already made.
401+
break;
402+
}
403+
404+
// If a fallback was used, then re-attempt the layout with the new fonts.
405+
if (!fallback->warn_if_used) {
406+
glyph_seen_fonts.insert(fallback->face->family_name);
407+
}
408+
409+
raqm_clear_contents(rq);
410+
if (!raqm_set_text(rq,
411+
reinterpret_cast<const uint32_t *>(text.data()),
412+
text.size()))
413+
{
414+
throw std::runtime_error("failed to set text for layout");
415+
}
416+
if (!raqm_set_freetype_face(rq, face)) {
417+
throw std::runtime_error("failed to set text face for layout");
418+
}
419+
for (auto [cluster, fallback] : face_substitutions) {
420+
raqm_set_freetype_face_range(rq, fallback, cluster, 1);
421+
}
422+
if (!raqm_set_freetype_load_flags(rq, flags)) {
423+
throw std::runtime_error("failed to set text flags for layout");
424+
}
425+
if (!raqm_layout(rq)) {
426+
throw std::runtime_error("failed to layout text");
427+
}
428+
}
429+
367430
size_t num_glyphs = 0;
368431
auto const& rq_glyphs = raqm_get_glyphs(rq, &num_glyphs);
369432

0 commit comments

Comments
 (0)