| // Copyright 2016 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "flutter/runtime/asset_font_selector.h" |
| |
| #include "flutter/assets/zip_asset_store.h" |
| #include "flutter/lib/ui/ui_dart_state.h" |
| #include "flutter/sky/engine/platform/fonts/FontData.h" |
| #include "flutter/sky/engine/platform/fonts/FontFaceCreationParams.h" |
| #include "flutter/sky/engine/platform/fonts/SimpleFontData.h" |
| #include "lib/ftl/arraysize.h" |
| #include "third_party/rapidjson/rapidjson/document.h" |
| #include "third_party/skia/include/core/SkStream.h" |
| #include "third_party/skia/include/core/SkTypeface.h" |
| #include "third_party/skia/include/ports/SkFontMgr.h" |
| |
| namespace blink { |
| |
| // Style attributes of a Flutter font asset. |
| struct AssetFontSelector::FlutterFontAttributes { |
| FlutterFontAttributes(const std::string& path); |
| ~FlutterFontAttributes(); |
| std::string asset_path; |
| int weight; |
| FontStyle style; |
| }; |
| |
| // A Skia typeface along with a buffer holding the raw typeface asset data. |
| struct AssetFontSelector::TypefaceAsset { |
| TypefaceAsset(); |
| ~TypefaceAsset(); |
| sk_sp<SkTypeface> typeface; |
| std::vector<uint8_t> data; |
| }; |
| |
| namespace { |
| const char kFontManifestAssetPath[] = "FontManifest.json"; |
| |
| // Weight values corresponding to the members of the FontWeight enum. |
| const int kFontWeightValue[] = {100, 200, 300, 400, 500, 600, 700, 800, 900}; |
| |
| const int kFontWeightNormal = kFontWeightValue[FontWeight::FontWeightNormal]; |
| |
| int getFontWeightValue(FontWeight weight) { |
| size_t weight_index = weight; |
| return (weight_index < arraysize(kFontWeightValue)) |
| ? kFontWeightValue[weight_index] |
| : kFontWeightNormal; |
| } |
| |
| // Compares fonts within a family to determine which one most closely matches |
| // a FontDescription. |
| struct FontMatcher { |
| using FlutterFontAttributes = AssetFontSelector::FlutterFontAttributes; |
| |
| FontMatcher(const FontDescription& description) |
| : description_(description), |
| target_weight_(getFontWeightValue(description.weight())) {} |
| |
| bool operator()(const FlutterFontAttributes& font1, |
| const FlutterFontAttributes& font2) { |
| if (font1.style != font2.style) { |
| if (font1.style == description_.style()) |
| return true; |
| if (font2.style == description_.style()) |
| return false; |
| } |
| |
| int weight_delta1 = abs(font1.weight - target_weight_); |
| int weight_delta2 = abs(font2.weight - target_weight_); |
| return weight_delta1 < weight_delta2; |
| } |
| |
| private: |
| const FontDescription& description_; |
| int target_weight_; |
| }; |
| } |
| |
| void AssetFontSelector::Install(ftl::RefPtr<ZipAssetStore> asset_store) { |
| RefPtr<AssetFontSelector> font_selector = |
| adoptRef(new AssetFontSelector(std::move(asset_store))); |
| font_selector->parseFontManifest(); |
| UIDartState::Current()->set_font_selector(font_selector); |
| } |
| |
| AssetFontSelector::AssetFontSelector(ftl::RefPtr<ZipAssetStore> asset_store) |
| : asset_store_(std::move(asset_store)) {} |
| |
| AssetFontSelector::~AssetFontSelector() {} |
| |
| AssetFontSelector::TypefaceAsset::TypefaceAsset() {} |
| |
| AssetFontSelector::TypefaceAsset::~TypefaceAsset() {} |
| |
| AssetFontSelector::FlutterFontAttributes::FlutterFontAttributes( |
| const std::string& path) |
| : asset_path(path), |
| weight(kFontWeightNormal), |
| style(FontStyle::FontStyleNormal) {} |
| |
| AssetFontSelector::FlutterFontAttributes::~FlutterFontAttributes() {} |
| |
| void AssetFontSelector::parseFontManifest() { |
| std::vector<uint8_t> font_manifest_data; |
| if (!asset_store_->GetAsBuffer(kFontManifestAssetPath, &font_manifest_data)) |
| return; |
| |
| rapidjson::Document document; |
| document.Parse(reinterpret_cast<const char*>(font_manifest_data.data()), |
| font_manifest_data.size()); |
| |
| if (document.HasParseError()) |
| return; |
| |
| if (!document.IsArray()) |
| return; |
| |
| for (auto& family : document.GetArray()) { |
| if (!family.IsObject()) |
| continue; |
| |
| auto family_name = family.FindMember("family"); |
| if (family_name == family.MemberEnd() || !family_name->value.IsString()) |
| continue; |
| |
| auto font_list = family.FindMember("fonts"); |
| if (font_list == family.MemberEnd() || !font_list->value.IsArray()) |
| continue; |
| |
| AtomicString family_key = |
| AtomicString::fromUTF8(family_name->value.GetString()); |
| auto set_result = |
| font_family_map_.set(family_key, std::vector<FlutterFontAttributes>()); |
| std::vector<FlutterFontAttributes>& family_assets = |
| set_result.storedValue->value; |
| |
| for (auto& list_entry : font_list->value.GetArray()) { |
| if (!list_entry.IsObject()) |
| continue; |
| |
| auto asset_path = list_entry.FindMember("asset"); |
| if (asset_path == list_entry.MemberEnd() || !asset_path->value.IsString()) |
| continue; |
| |
| FlutterFontAttributes attributes(asset_path->value.GetString()); |
| |
| auto weight = list_entry.FindMember("weight"); |
| if (weight != list_entry.MemberEnd() && weight->value.IsInt()) |
| attributes.weight = weight->value.GetInt(); |
| |
| auto style = list_entry.FindMember("style"); |
| if (style != list_entry.MemberEnd() && style->value.IsString()) { |
| if (std::string(style->value.GetString()) == "italic") |
| attributes.style = FontStyle::FontStyleItalic; |
| } |
| |
| family_assets.push_back(attributes); |
| } |
| } |
| } |
| |
| PassRefPtr<FontData> AssetFontSelector::getFontData( |
| const FontDescription& font_description, |
| const AtomicString& family_name) { |
| FontFaceCreationParams creationParams(family_name); |
| FontCacheKey key = font_description.cacheKey(creationParams); |
| RefPtr<SimpleFontData> font_data = font_platform_data_cache_.get(key); |
| |
| if (font_data == nullptr) { |
| sk_sp<SkTypeface> typeface = |
| getTypefaceAsset(font_description, family_name); |
| if (!typeface) |
| return nullptr; |
| |
| bool synthetic_bold = |
| (font_description.weight() >= FontWeight600 && !typeface->isBold()) || |
| font_description.isSyntheticBold(); |
| bool synthetic_italic = |
| (font_description.style() && !typeface->isItalic()) || |
| font_description.isSyntheticItalic(); |
| FontPlatformData platform_data(typeface, family_name.utf8().data(), |
| font_description.effectiveFontSize(), |
| synthetic_bold, synthetic_italic, |
| font_description.orientation(), |
| font_description.useSubpixelPositioning()); |
| |
| font_data = SimpleFontData::create(platform_data); |
| font_platform_data_cache_.set(key, font_data); |
| } |
| |
| return font_data; |
| } |
| |
| sk_sp<SkTypeface> AssetFontSelector::getTypefaceAsset( |
| const FontDescription& font_description, |
| const AtomicString& family_name) { |
| auto family_iter = font_family_map_.find(family_name); |
| if (family_iter == font_family_map_.end()) |
| return nullptr; |
| |
| const std::vector<FlutterFontAttributes>& fonts = family_iter->value; |
| if (fonts.empty()) |
| return nullptr; |
| |
| std::vector<FlutterFontAttributes>::const_iterator font_iter; |
| if (fonts.size() == 1) { |
| font_iter = fonts.begin(); |
| } else { |
| font_iter = std::min_element(fonts.begin(), fonts.end(), |
| FontMatcher(font_description)); |
| } |
| |
| const std::string& asset_path = font_iter->asset_path; |
| auto typeface_iter = typeface_cache_.find(asset_path); |
| if (typeface_iter != typeface_cache_.end()) { |
| const TypefaceAsset* cache_asset = typeface_iter->second.get(); |
| return cache_asset ? cache_asset->typeface : nullptr; |
| } |
| |
| std::unique_ptr<TypefaceAsset> typeface_asset(new TypefaceAsset); |
| if (!asset_store_->GetAsBuffer(asset_path, &typeface_asset->data)) { |
| typeface_cache_.insert(std::make_pair(asset_path, nullptr)); |
| return nullptr; |
| } |
| |
| SkAutoTUnref<SkFontMgr> font_mgr(SkFontMgr::RefDefault()); |
| SkMemoryStream* typeface_stream = new SkMemoryStream( |
| typeface_asset->data.data(), typeface_asset->data.size()); |
| typeface_asset->typeface = |
| sk_sp<SkTypeface>(font_mgr->createFromStream(typeface_stream)); |
| if (typeface_asset->typeface == nullptr) { |
| typeface_cache_.insert(std::make_pair(asset_path, nullptr)); |
| return nullptr; |
| } |
| |
| sk_sp<SkTypeface> result = typeface_asset->typeface; |
| typeface_cache_.insert(std::make_pair(asset_path, std::move(typeface_asset))); |
| |
| return result; |
| } |
| |
| void AssetFontSelector::willUseFontData(const FontDescription& font_description, |
| const AtomicString& family, |
| UChar32 character) {} |
| |
| unsigned AssetFontSelector::version() const { |
| return 0; |
| } |
| |
| void AssetFontSelector::fontCacheInvalidated() {} |
| |
| } // namespace blink |