font_fallback_skia_impl.cc 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. // Copyright 2019 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "ui/gfx/font_fallback_skia_impl.h"
  5. #include <set>
  6. #include <string>
  7. #include "third_party/icu/source/common/unicode/normalizer2.h"
  8. #include "third_party/icu/source/common/unicode/uchar.h"
  9. #include "third_party/icu/source/common/unicode/utf16.h"
  10. #include "third_party/skia/include/core/SkFontMgr.h"
  11. #include "third_party/skia/include/core/SkTypeface.h"
  12. namespace gfx {
  13. namespace {
  14. // Returns true when the codepoint has an unicode decomposition and store
  15. // the decomposed string into |output|.
  16. bool UnicodeDecomposeCodepoint(UChar32 codepoint, icu::UnicodeString* output) {
  17. static const icu::Normalizer2* normalizer = nullptr;
  18. UErrorCode error = U_ZERO_ERROR;
  19. if (!normalizer) {
  20. normalizer = icu::Normalizer2::getNFDInstance(error);
  21. if (U_FAILURE(error))
  22. return false;
  23. DCHECK(normalizer);
  24. }
  25. return normalizer->getDecomposition(codepoint, *output);
  26. }
  27. // Extracts every codepoint and its decomposed codepoints from unicode
  28. // decomposition. Inserts in |codepoints| the set of codepoints in |text|.
  29. void RetrieveCodepointsAndDecomposedCodepoints(base::StringPiece16 text,
  30. std::set<UChar32>* codepoints) {
  31. size_t offset = 0;
  32. while (offset < text.length()) {
  33. UChar32 codepoint;
  34. U16_NEXT(text.data(), offset, text.length(), codepoint);
  35. if (codepoints->insert(codepoint).second) {
  36. // For each codepoint, add the decomposed codepoints.
  37. icu::UnicodeString decomposed_text;
  38. if (UnicodeDecomposeCodepoint(codepoint, &decomposed_text)) {
  39. for (int i = 0; i < decomposed_text.length(); ++i) {
  40. codepoints->insert(decomposed_text[i]);
  41. }
  42. }
  43. }
  44. }
  45. }
  46. // Returns the amount of codepoint in |text| without a glyph representation in
  47. // |typeface|. A codepoint is present if there is a corresponding glyph in
  48. // typeface, or if there are glyphs for each of its decomposed codepoints.
  49. size_t ComputeMissingGlyphsForGivenTypeface(base::StringPiece16 text,
  50. sk_sp<SkTypeface> typeface) {
  51. // Validate that every character has a known glyph in the font.
  52. size_t missing_glyphs = 0;
  53. size_t i = 0;
  54. while (i < text.length()) {
  55. UChar32 codepoint;
  56. U16_NEXT(text.data(), i, text.length(), codepoint);
  57. // The glyph is present in the font.
  58. if (typeface->unicharToGlyph(codepoint) != 0)
  59. continue;
  60. // Do not count missing codepoints when they are ignorable as they will be
  61. // ignored by the shaping engine.
  62. if (u_hasBinaryProperty(codepoint, UCHAR_DEFAULT_IGNORABLE_CODE_POINT))
  63. continue;
  64. // No glyph is present in the font for the codepoint. Try the decomposed
  65. // codepoints instead.
  66. icu::UnicodeString decomposed_text;
  67. if (UnicodeDecomposeCodepoint(codepoint, &decomposed_text) &&
  68. !decomposed_text.isEmpty()) {
  69. // Check that every decomposed codepoint is in the font.
  70. bool every_codepoint_found = true;
  71. for (int offset = 0; offset < decomposed_text.length(); ++offset) {
  72. if (typeface->unicharToGlyph(decomposed_text[offset]) == 0) {
  73. every_codepoint_found = false;
  74. break;
  75. }
  76. }
  77. // The decomposed codepoints can be mapped to glyphs by the font.
  78. if (every_codepoint_found)
  79. continue;
  80. }
  81. // The current glyphs can't be find.
  82. ++missing_glyphs;
  83. }
  84. return missing_glyphs;
  85. }
  86. } // namespace
  87. sk_sp<SkTypeface> GetSkiaFallbackTypeface(const Font& template_font,
  88. const std::string& locale,
  89. base::StringPiece16 text) {
  90. if (text.empty())
  91. return nullptr;
  92. sk_sp<SkFontMgr> font_mgr(SkFontMgr::RefDefault());
  93. const char* bcp47_locales[] = {locale.c_str()};
  94. int num_locales = locale.empty() ? 0 : 1;
  95. const char** locales = locale.empty() ? nullptr : bcp47_locales;
  96. const int font_weight = (template_font.GetWeight() == Font::Weight::INVALID)
  97. ? static_cast<int>(Font::Weight::NORMAL)
  98. : static_cast<int>(template_font.GetWeight());
  99. const bool italic = (template_font.GetStyle() & Font::ITALIC) != 0;
  100. SkFontStyle skia_style(
  101. font_weight, SkFontStyle::kNormal_Width,
  102. italic ? SkFontStyle::kItalic_Slant : SkFontStyle::kUpright_Slant);
  103. std::set<SkFontID> tested_typeface;
  104. sk_sp<SkTypeface> fallback_typeface;
  105. size_t fewest_missing_glyphs = text.length() + 1;
  106. // Retrieve the set of codepoints (or unicode decomposed codepoints) from
  107. // the input text.
  108. std::set<UChar32> codepoints;
  109. RetrieveCodepointsAndDecomposedCodepoints(text, &codepoints);
  110. // Determine which fallback font is given the fewer missing glyphs.
  111. for (UChar32 codepoint : codepoints) {
  112. sk_sp<SkTypeface> typeface(font_mgr->matchFamilyStyleCharacter(
  113. template_font.GetFontName().c_str(), skia_style, locales, num_locales,
  114. codepoint));
  115. // If the typeface is not found or was already tested, skip it.
  116. if (!typeface || !tested_typeface.insert(typeface->uniqueID()).second)
  117. continue;
  118. // Validate that every codepoint has a known glyph in the font.
  119. size_t missing_glyphs =
  120. ComputeMissingGlyphsForGivenTypeface(text, typeface);
  121. if (missing_glyphs < fewest_missing_glyphs) {
  122. fewest_missing_glyphs = missing_glyphs;
  123. fallback_typeface = typeface;
  124. }
  125. // The font is a valid fallback font for the given text.
  126. if (missing_glyphs == 0)
  127. break;
  128. }
  129. return fallback_typeface;
  130. }
  131. } // namespace gfx