blob: 23775577b651784a1569ac72aed459f03f61b89b [file] [log] [blame]
// 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 "base/json/json_reader.h"
#include "base/values.h"
#include "flutter/assets/zip_asset_store.h"
#include "flutter/sky/engine/core/script/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 "flutter/sky/shell/ui/flutter_font_selector.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 sky {
namespace shell {
using base::DictionaryValue;
using base::JSONReader;
using base::ListValue;
using base::StringValue;
using base::Value;
using blink::FontCacheKey;
using blink::FontData;
using blink::FontDescription;
using blink::FontFaceCreationParams;
using blink::FontPlatformData;
using blink::FontStyle;
using blink::FontWeight;
using blink::SimpleFontData;
// Style attributes of a Flutter font asset.
struct FlutterFontSelector::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 FlutterFontSelector::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 = FlutterFontSelector::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 FlutterFontSelector::Install(
ftl::RefPtr<blink::ZipAssetStore> asset_store) {
RefPtr<FlutterFontSelector> font_selector =
adoptRef(new FlutterFontSelector(std::move(asset_store)));
font_selector->parseFontManifest();
blink::UIDartState::Current()->set_font_selector(font_selector);
}
FlutterFontSelector::FlutterFontSelector(
ftl::RefPtr<blink::ZipAssetStore> asset_store)
: asset_store_(std::move(asset_store)) {}
FlutterFontSelector::~FlutterFontSelector() {}
FlutterFontSelector::TypefaceAsset::TypefaceAsset() {}
FlutterFontSelector::TypefaceAsset::~TypefaceAsset() {}
FlutterFontSelector::FlutterFontAttributes::FlutterFontAttributes(
const std::string& path)
: asset_path(path),
weight(kFontWeightNormal),
style(FontStyle::FontStyleNormal) {}
FlutterFontSelector::FlutterFontAttributes::~FlutterFontAttributes() {}
void FlutterFontSelector::parseFontManifest() {
std::vector<uint8_t> font_manifest_data;
if (!asset_store_->GetAsBuffer(kFontManifestAssetPath, &font_manifest_data))
return;
base::StringPiece font_manifest_str(
reinterpret_cast<const char*>(font_manifest_data.data()),
font_manifest_data.size());
scoped_ptr<Value> font_manifest_json = JSONReader::Read(font_manifest_str);
if (font_manifest_json == nullptr)
return;
ListValue* family_list;
if (!font_manifest_json->GetAsList(&family_list))
return;
for (auto family : *family_list) {
DictionaryValue* family_dict;
if (!family->GetAsDictionary(&family_dict))
continue;
std::string family_name;
if (!family_dict->GetString("family", &family_name))
continue;
ListValue* font_list;
if (!family_dict->GetList("fonts", &font_list))
continue;
AtomicString family_key = AtomicString::fromUTF8(family_name.c_str());
auto set_result =
font_family_map_.set(family_key, std::vector<FlutterFontAttributes>());
std::vector<FlutterFontAttributes>& family_assets =
set_result.storedValue->value;
for (Value* list_entry : *font_list) {
DictionaryValue* font_dict;
if (!list_entry->GetAsDictionary(&font_dict))
continue;
std::string asset_path;
if (!font_dict->GetString("asset", &asset_path))
continue;
FlutterFontAttributes attributes(asset_path);
font_dict->GetInteger("weight", &attributes.weight);
std::string style;
if (font_dict->GetString("style", &style)) {
if (style == "italic")
attributes.style = FontStyle::FontStyleItalic;
}
family_assets.push_back(attributes);
}
}
}
PassRefPtr<FontData> FlutterFontSelector::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() >= blink::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> FlutterFontSelector::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 FlutterFontSelector::willUseFontData(
const FontDescription& font_description,
const AtomicString& family,
UChar32 character) {}
unsigned FlutterFontSelector::version() const {
return 0;
}
void FlutterFontSelector::fontCacheInvalidated() {}
} // namespace shell
} // namespace sky