Flutter Engine
font_collection.cc
Go to the documentation of this file.
1 /*
2  * Copyright 2017 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "font_collection.h"
18 
19 #include <algorithm>
20 #include <list>
21 #include <memory>
22 #include <mutex>
23 #include <set>
24 #include <string>
25 #include <unordered_map>
26 #include <vector>
27 #include "flutter/fml/logging.h"
28 #include "flutter/fml/trace_event.h"
29 #include "font_skia.h"
30 #include "minikin/Layout.h"
31 #include "txt/platform.h"
32 #include "txt/text_style.h"
33 
34 namespace txt {
35 
36 namespace {
37 
38 const std::shared_ptr<minikin::FontFamily> g_null_family;
39 
40 } // anonymous namespace
41 
42 FontCollection::FamilyKey::FamilyKey(const std::vector<std::string>& families,
43  const std::string& loc) {
44  locale = loc;
45 
46  std::stringstream stream;
47  for_each(families.begin(), families.end(),
48  [&stream](const std::string& str) { stream << str << ','; });
49  font_families = stream.str();
50 }
51 
53  const FontCollection::FamilyKey& other) const {
54  return font_families == other.font_families && locale == other.locale;
55 }
56 
58  const FontCollection::FamilyKey& key) const {
59  return std::hash<std::string>()(key.font_families) ^
60  std::hash<std::string>()(key.locale);
61 }
62 
65  public:
66  TxtFallbackFontProvider(std::shared_ptr<FontCollection> font_collection)
67  : font_collection_(font_collection) {}
68 
69  virtual const std::shared_ptr<minikin::FontFamily>& matchFallbackFont(
70  uint32_t ch,
71  std::string locale) {
72  std::shared_ptr<FontCollection> fc = font_collection_.lock();
73  if (fc) {
74  return fc->MatchFallbackFont(ch, locale);
75  } else {
76  return g_null_family;
77  }
78  }
79 
80  private:
81  std::weak_ptr<FontCollection> font_collection_;
82 };
83 
84 FontCollection::FontCollection() : enable_font_fallback_(true) {}
85 
88 
89 #if FLUTTER_ENABLE_SKSHAPER
90  if (skt_collection_) {
91  skt_collection_->clearCaches();
92  }
93 #endif
94 }
95 
97  return GetFontManagerOrder().size();
98 }
99 
101  default_font_manager_ = GetDefaultFontManager();
102 }
103 
104 void FontCollection::SetDefaultFontManager(sk_sp<SkFontMgr> font_manager) {
105  default_font_manager_ = font_manager;
106 }
107 
108 void FontCollection::SetAssetFontManager(sk_sp<SkFontMgr> font_manager) {
109  asset_font_manager_ = font_manager;
110 }
111 
112 void FontCollection::SetDynamicFontManager(sk_sp<SkFontMgr> font_manager) {
113  dynamic_font_manager_ = font_manager;
114 }
115 
116 void FontCollection::SetTestFontManager(sk_sp<SkFontMgr> font_manager) {
117  test_font_manager_ = font_manager;
118 }
119 
120 // Return the available font managers in the order they should be queried.
121 std::vector<sk_sp<SkFontMgr>> FontCollection::GetFontManagerOrder() const {
122  std::vector<sk_sp<SkFontMgr>> order;
123  if (dynamic_font_manager_)
124  order.push_back(dynamic_font_manager_);
125  if (asset_font_manager_)
126  order.push_back(asset_font_manager_);
127  if (test_font_manager_)
128  order.push_back(test_font_manager_);
129  if (default_font_manager_)
130  order.push_back(default_font_manager_);
131  return order;
132 }
133 
135  enable_font_fallback_ = false;
136 
137 #if FLUTTER_ENABLE_SKSHAPER
138  if (skt_collection_) {
139  skt_collection_->disableFontFallback();
140  }
141 #endif
142 }
143 
144 std::shared_ptr<minikin::FontCollection>
146  const std::vector<std::string>& font_families,
147  const std::string& locale) {
148  // Look inside the font collections cache first.
149  FamilyKey family_key(font_families, locale);
150  auto cached = font_collections_cache_.find(family_key);
151  if (cached != font_collections_cache_.end()) {
152  return cached->second;
153  }
154 
155  std::vector<std::shared_ptr<minikin::FontFamily>> minikin_families;
156 
157  // Search for all user provided font families.
158  for (size_t fallback_index = 0; fallback_index < font_families.size();
159  fallback_index++) {
160  std::shared_ptr<minikin::FontFamily> minikin_family =
161  FindFontFamilyInManagers(font_families[fallback_index]);
162  if (minikin_family != nullptr) {
163  minikin_families.push_back(minikin_family);
164  }
165  }
166  // Search for default font family if no user font families were found.
167  if (minikin_families.empty()) {
168  const auto default_font_families = GetDefaultFontFamilies();
169  for (const auto& family : default_font_families) {
170  std::shared_ptr<minikin::FontFamily> minikin_family =
171  FindFontFamilyInManagers(family);
172  if (minikin_family != nullptr) {
173  minikin_families.push_back(minikin_family);
174  break;
175  }
176  }
177  }
178  // Default font family also not found. We fail to get a FontCollection.
179  if (minikin_families.empty()) {
180  font_collections_cache_[family_key] = nullptr;
181  return nullptr;
182  }
183  if (enable_font_fallback_) {
184  for (const std::string& fallback_family :
185  fallback_fonts_for_locale_[locale]) {
186  auto it = fallback_fonts_.find(fallback_family);
187  if (it != fallback_fonts_.end()) {
188  minikin_families.push_back(it->second);
189  }
190  }
191  }
192  // Create the minikin font collection.
193  auto font_collection =
194  std::make_shared<minikin::FontCollection>(std::move(minikin_families));
195  if (enable_font_fallback_) {
196  font_collection->set_fallback_font_provider(
197  std::make_unique<TxtFallbackFontProvider>(shared_from_this()));
198  }
199 
200  // Cache the font collection for future queries.
201  font_collections_cache_[family_key] = font_collection;
202 
203  return font_collection;
204 }
205 
206 std::shared_ptr<minikin::FontFamily> FontCollection::FindFontFamilyInManagers(
207  const std::string& family_name) {
208  TRACE_EVENT0("flutter", "FontCollection::FindFontFamilyInManagers");
209  // Search for the font family in each font manager.
210  for (sk_sp<SkFontMgr>& manager : GetFontManagerOrder()) {
211  std::shared_ptr<minikin::FontFamily> minikin_family =
212  CreateMinikinFontFamily(manager, family_name);
213  if (!minikin_family)
214  continue;
215  return minikin_family;
216  }
217  return nullptr;
218 }
219 
220 void FontCollection::SortSkTypefaces(
221  std::vector<sk_sp<SkTypeface>>& sk_typefaces) {
222  std::sort(
223  sk_typefaces.begin(), sk_typefaces.end(),
224  [](const sk_sp<SkTypeface>& a, const sk_sp<SkTypeface>& b) {
225  SkFontStyle a_style = a->fontStyle();
226  SkFontStyle b_style = b->fontStyle();
227 
228  int a_delta = std::abs(a_style.width() - SkFontStyle::kNormal_Width);
229  int b_delta = std::abs(b_style.width() - SkFontStyle::kNormal_Width);
230 
231  if (a_delta != b_delta) {
232  // If a family name query is so generic it ends up bringing in fonts
233  // of multiple widths (e.g. condensed, expanded), opt to be
234  // conservative and select the most standard width.
235  //
236  // If a specific width is desired, it should be be narrowed down via
237  // the family name.
238  //
239  // The font weights are also sorted lightest to heaviest but Flutter
240  // APIs have the weight specified to narrow it down later. The width
241  // ordering here is more consequential since TextStyle doesn't have
242  // letter width APIs.
243  return a_delta < b_delta;
244  } else if (a_style.width() != b_style.width()) {
245  // However, if the 2 fonts are equidistant from the "normal" width,
246  // just arbitrarily but consistently return the more condensed font.
247  return a_style.width() < b_style.width();
248  } else if (a_style.weight() != b_style.weight()) {
249  return a_style.weight() < b_style.weight();
250  } else {
251  return a_style.slant() < b_style.slant();
252  }
253  // Use a cascade of conditions so results are consistent each time.
254  });
255 }
256 
257 std::shared_ptr<minikin::FontFamily> FontCollection::CreateMinikinFontFamily(
258  const sk_sp<SkFontMgr>& manager,
259  const std::string& family_name) {
260  TRACE_EVENT1("flutter", "FontCollection::CreateMinikinFontFamily",
261  "family_name", family_name.c_str());
262  sk_sp<SkFontStyleSet> font_style_set(
263  manager->matchFamily(family_name.c_str()));
264  if (font_style_set == nullptr || font_style_set->count() == 0) {
265  return nullptr;
266  }
267 
268  std::vector<sk_sp<SkTypeface>> skia_typefaces;
269  for (int i = 0; i < font_style_set->count(); ++i) {
270  TRACE_EVENT0("flutter", "CreateSkiaTypeface");
271  sk_sp<SkTypeface> skia_typeface(
272  sk_sp<SkTypeface>(font_style_set->createTypeface(i)));
273  if (skia_typeface != nullptr) {
274  skia_typefaces.emplace_back(std::move(skia_typeface));
275  }
276  }
277 
278  SortSkTypefaces(skia_typefaces);
279 
280  std::vector<minikin::Font> minikin_fonts;
281  for (const sk_sp<SkTypeface>& skia_typeface : skia_typefaces) {
282  // Create the minikin font from the skia typeface.
283  // Divide by 100 because the weights are given as "100", "200", etc.
284  minikin_fonts.emplace_back(
285  std::make_shared<FontSkia>(skia_typeface),
286  minikin::FontStyle{skia_typeface->fontStyle().weight() / 100,
287  skia_typeface->isItalic()});
288  }
289 
290  return std::make_shared<minikin::FontFamily>(std::move(minikin_fonts));
291 }
292 
293 const std::shared_ptr<minikin::FontFamily>& FontCollection::MatchFallbackFont(
294  uint32_t ch,
295  std::string locale) {
296  // Check if the ch's matched font has been cached. We cache the results of
297  // this method as repeated matchFamilyStyleCharacter calls can become
298  // extremely laggy when typing a large number of complex emojis.
299  auto lookup = fallback_match_cache_.find(ch);
300  if (lookup != fallback_match_cache_.end()) {
301  return *lookup->second;
302  }
303  const std::shared_ptr<minikin::FontFamily>* match =
304  &DoMatchFallbackFont(ch, locale);
305  fallback_match_cache_.insert(std::make_pair(ch, match));
306  return *match;
307 }
308 
309 const std::shared_ptr<minikin::FontFamily>& FontCollection::DoMatchFallbackFont(
310  uint32_t ch,
311  std::string locale) {
312  for (const sk_sp<SkFontMgr>& manager : GetFontManagerOrder()) {
313  std::vector<const char*> bcp47;
314  if (!locale.empty())
315  bcp47.push_back(locale.c_str());
316  sk_sp<SkTypeface> typeface(manager->matchFamilyStyleCharacter(
317  0, SkFontStyle(), bcp47.data(), bcp47.size(), ch));
318  if (!typeface)
319  continue;
320 
321  SkString sk_family_name;
322  typeface->getFamilyName(&sk_family_name);
323  std::string family_name(sk_family_name.c_str());
324 
325  if (std::find(fallback_fonts_for_locale_[locale].begin(),
326  fallback_fonts_for_locale_[locale].end(),
327  family_name) == fallback_fonts_for_locale_[locale].end())
328  fallback_fonts_for_locale_[locale].push_back(family_name);
329 
330  return GetFallbackFontFamily(manager, family_name);
331  }
332  return g_null_family;
333 }
334 
335 const std::shared_ptr<minikin::FontFamily>&
336 FontCollection::GetFallbackFontFamily(const sk_sp<SkFontMgr>& manager,
337  const std::string& family_name) {
338  TRACE_EVENT0("flutter", "FontCollection::GetFallbackFontFamily");
339  auto fallback_it = fallback_fonts_.find(family_name);
340  if (fallback_it != fallback_fonts_.end()) {
341  return fallback_it->second;
342  }
343 
344  std::shared_ptr<minikin::FontFamily> minikin_family =
345  CreateMinikinFontFamily(manager, family_name);
346  if (!minikin_family)
347  return g_null_family;
348 
349  auto insert_it =
350  fallback_fonts_.insert(std::make_pair(family_name, minikin_family));
351 
352  // Clear the cache to force creation of new font collections that will
353  // include this fallback font.
354  font_collections_cache_.clear();
355 
356  return insert_it.first->second;
357 }
358 
360  font_collections_cache_.clear();
361 
362 #if FLUTTER_ENABLE_SKSHAPER
363  if (skt_collection_) {
364  skt_collection_->clearCaches();
365  }
366 #endif
367 }
368 #if FLUTTER_ENABLE_SKSHAPER
369 
370 sk_sp<skia::textlayout::FontCollection>
371 FontCollection::CreateSktFontCollection() {
372  if (!skt_collection_) {
373  skt_collection_ = sk_make_sp<skia::textlayout::FontCollection>();
374 
375  skt_collection_->setDefaultFontManager(default_font_manager_,
376  GetDefaultFontFamilies()[0].c_str());
377  skt_collection_->setAssetFontManager(asset_font_manager_);
378  skt_collection_->setDynamicFontManager(dynamic_font_manager_);
379  skt_collection_->setTestFontManager(test_font_manager_);
380  if (!enable_font_fallback_) {
381  skt_collection_->disableFontFallback();
382  }
383  }
384 
385  return skt_collection_;
386 }
387 
388 #endif // FLUTTER_ENABLE_SKSHAPER
389 
390 } // namespace txt
void SetAssetFontManager(sk_sp< SkFontMgr > font_manager)
#define TRACE_EVENT0(category_group, name)
Definition: trace_event.h:75
void SetDynamicFontManager(sk_sp< SkFontMgr > font_manager)
size_t operator()(const FamilyKey &key) const
const std::shared_ptr< minikin::FontFamily > & MatchFallbackFont(uint32_t ch, std::string locale)
static void purgeCaches()
sk_sp< SkFontMgr > GetDefaultFontManager()
Definition: platform.cc:13
TxtFallbackFontProvider(std::shared_ptr< FontCollection > font_collection)
std::vector< std::string > GetDefaultFontFamilies()
Definition: platform.cc:9
void SetTestFontManager(sk_sp< SkFontMgr > font_manager)
bool operator==(const MockTexture::PaintCall &a, const MockTexture::PaintCall &b)
Definition: mock_texture.cc:21
#define TRACE_EVENT1(category_group, name, arg1_name, arg1_val)
Definition: trace_event.h:79
size_t GetFontManagersCount() const
std::shared_ptr< minikin::FontCollection > GetMinikinFontCollectionForFamilies(const std::vector< std::string > &font_families, const std::string &locale)
virtual const std::shared_ptr< minikin::FontFamily > & matchFallbackFont(uint32_t ch, std::string locale)
void SetDefaultFontManager(sk_sp< SkFontMgr > font_manager)