|
6 | 6 | #include <algorithm>
|
7 | 7 | #include <cstdio>
|
8 | 8 | #include <iterator>
|
| 9 | +#include <map> |
9 | 10 | #include <set>
|
10 | 11 | #include <stdexcept>
|
11 | 12 | #include <string>
|
@@ -361,9 +362,71 @@ void FT2Font::set_text(
|
361 | 362 | throw std::runtime_error("failed to layout text");
|
362 | 363 | }
|
363 | 364 |
|
| 365 | + std::vector<std::pair<size_t, const FT_Face&>> face_substitutions; |
364 | 366 | std::set<FT_String*> glyph_seen_fonts;
|
365 | 367 | glyph_seen_fonts.insert(face->family_name);
|
366 | 368 |
|
| 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 | + |
367 | 430 | size_t num_glyphs = 0;
|
368 | 431 | auto const& rq_glyphs = raqm_get_glyphs(rq, &num_glyphs);
|
369 | 432 |
|
|
0 commit comments