未验证 提交 975d4ead 编写于 作者: J Jason Simmons 提交者: GitHub

libtxt: support per-locale fallback font collections (#5434)

A given character may render differently in various locales, and fonts on the
host system may be tagged with locale metadata in order to support this.
If the text renderer needs to find a fallback font for a character, it should
pass the preferred locale to Skia's font manager which can use it to find the
best matching font.

The font collection also needs to maintain different caches of fallback fonts
for each locale.

See https://github.com/flutter/flutter/pull/17879
上级 ac7d442a
......@@ -44,6 +44,12 @@ const uint32_t TEXT_STYLE_VS = 0xFE0E;
uint32_t FontCollection::sNextId = 0;
// libtxt: return a locale string for a language list ID
std::string GetFontLocale(uint32_t langListId) {
const FontLanguages& langs = FontLanguageListCache::getById(langListId);
return langs.size() ? langs[0].getString() : "";
}
FontCollection::FontCollection(std::shared_ptr<FontFamily>&& typeface)
: mMaxChar(0) {
std::vector<std::shared_ptr<FontFamily>> typefaces;
......@@ -295,7 +301,8 @@ const std::shared_ptr<FontFamily>& FontCollection::getFamilyForChar(
// libtxt: check if the fallback font provider can match this character
if (mFallbackFontProvider) {
const std::shared_ptr<FontFamily>& fallback =
mFallbackFontProvider->matchFallbackFont(ch);
mFallbackFontProvider->matchFallbackFont(ch,
GetFontLocale(langListId));
if (fallback) {
return fallback;
}
......@@ -332,7 +339,8 @@ const std::shared_ptr<FontFamily>& FontCollection::getFamilyForChar(
// libtxt: check if the fallback font provider can match this character
if (mFallbackFontProvider) {
const std::shared_ptr<FontFamily>& fallback =
mFallbackFontProvider->matchFallbackFont(ch);
mFallbackFontProvider->matchFallbackFont(ch,
GetFontLocale(langListId));
if (fallback) {
return fallback;
}
......
......@@ -38,7 +38,8 @@ class FontCollection {
public:
virtual ~FallbackFontProvider() = default;
virtual const std::shared_ptr<FontFamily>& matchFallbackFont(
uint32_t ch) = 0;
uint32_t ch,
std::string locale) = 0;
};
struct Run {
......
......@@ -30,15 +30,16 @@
namespace txt {
namespace {
// Font families that will be used as a last resort if no font manager provides
// a font matching a particular character.
const std::vector<std::string> last_resort_fonts{
"Arial",
};
bool FontCollection::FamilyKey::operator==(
const FontCollection::FamilyKey& other) const {
return font_family == other.font_family && locale == other.locale;
}
} // anonymous namespace
size_t FontCollection::FamilyKey::Hasher::operator()(
const FontCollection::FamilyKey& key) const {
return std::hash<std::string>()(key.font_family) ^
std::hash<std::string>()(key.locale);
}
class TxtFallbackFontProvider
: public minikin::FontCollection::FallbackFontProvider {
......@@ -47,8 +48,9 @@ class TxtFallbackFontProvider
: font_collection_(font_collection) {}
virtual const std::shared_ptr<minikin::FontFamily>& matchFallbackFont(
uint32_t ch) {
return font_collection_->MatchFallbackFont(ch);
uint32_t ch,
std::string locale) {
return font_collection_->MatchFallbackFont(ch, locale);
}
private:
......@@ -92,15 +94,19 @@ void FontCollection::DisableFontFallback() {
}
std::shared_ptr<minikin::FontCollection>
FontCollection::GetMinikinFontCollectionForFamily(const std::string& family) {
FontCollection::GetMinikinFontCollectionForFamily(
const std::string& font_family,
const std::string& locale) {
// Look inside the font collections cache first.
auto cached = font_collections_cache_.find(family);
FamilyKey family_key(font_family, locale);
auto cached = font_collections_cache_.find(family_key);
if (cached != font_collections_cache_.end()) {
return cached->second;
}
for (sk_sp<SkFontMgr>& manager : GetFontManagerOrder()) {
sk_sp<SkFontStyleSet> font_style_set(manager->matchFamily(family.c_str()));
sk_sp<SkFontStyleSet> font_style_set(
manager->matchFamily(font_family.c_str()));
if (font_style_set == nullptr || font_style_set->count() == 0) {
continue;
}
......@@ -136,8 +142,8 @@ FontCollection::GetMinikinFontCollectionForFamily(const std::string& family) {
minikin_family,
};
if (enable_font_fallback_) {
for (const auto& fallback : fallback_fonts_)
minikin_families.push_back(fallback.second);
for (SkFontID font_id : fallback_fonts_for_locale_[locale])
minikin_families.push_back(fallback_fonts_[font_id]);
}
// Create the minikin font collection.
......@@ -149,16 +155,16 @@ FontCollection::GetMinikinFontCollectionForFamily(const std::string& family) {
}
// Cache the font collection for future queries.
font_collections_cache_[family] = font_collection;
font_collections_cache_[family_key] = font_collection;
return font_collection;
}
const auto default_font_family = GetDefaultFontFamily();
if (family != default_font_family) {
if (font_family != default_font_family) {
std::shared_ptr<minikin::FontCollection> default_collection =
GetMinikinFontCollectionForFamily(default_font_family);
font_collections_cache_[family] = default_collection;
GetMinikinFontCollectionForFamily(default_font_family, "");
font_collections_cache_[family_key] = default_collection;
return default_collection;
}
......@@ -167,21 +173,27 @@ FontCollection::GetMinikinFontCollectionForFamily(const std::string& family) {
}
const std::shared_ptr<minikin::FontFamily>& FontCollection::MatchFallbackFont(
uint32_t ch) {
uint32_t ch,
std::string locale) {
for (const sk_sp<SkFontMgr>& manager : GetFontManagerOrder()) {
sk_sp<SkTypeface> typeface(
manager->matchFamilyStyleCharacter(0, SkFontStyle(), nullptr, 0, ch));
std::vector<const char*> bcp47;
if (!locale.empty())
bcp47.push_back(locale.c_str());
sk_sp<SkTypeface> typeface(manager->matchFamilyStyleCharacter(
0, SkFontStyle(), bcp47.data(), bcp47.size(), ch));
if (!typeface)
continue;
return GetFontFamilyForTypeface(typeface);
fallback_fonts_for_locale_[locale].insert(typeface->uniqueID());
return GetFallbackFont(typeface);
}
return null_family_;
}
const std::shared_ptr<minikin::FontFamily>&
FontCollection::GetFontFamilyForTypeface(const sk_sp<SkTypeface>& typeface) {
FontCollection::GetFallbackFont(const sk_sp<SkTypeface>& typeface) {
SkFontID typeface_id = typeface->uniqueID();
auto fallback_it = fallback_fonts_.find(typeface_id);
if (fallback_it != fallback_fonts_.end()) {
......@@ -202,14 +214,4 @@ FontCollection::GetFontFamilyForTypeface(const sk_sp<SkTypeface>& typeface) {
return insert_it.first->second;
}
void FontCollection::UpdateFallbackFonts(sk_sp<SkFontMgr> manager) {
for (const std::string& family : last_resort_fonts) {
sk_sp<SkTypeface> typeface(
manager->matchFamilyStyle(family.c_str(), SkFontStyle()));
if (typeface) {
GetFontFamilyForTypeface(typeface);
}
}
}
} // namespace txt
......@@ -17,8 +17,8 @@
#ifndef LIB_TXT_SRC_FONT_COLLECTION_H_
#define LIB_TXT_SRC_FONT_COLLECTION_H_
#include <deque>
#include <memory>
#include <set>
#include <string>
#include <unordered_map>
#include "lib/fxl/macros.h"
......@@ -28,6 +28,7 @@
#include "third_party/skia/include/core/SkRefCnt.h"
#include "third_party/skia/include/ports/SkFontMgr.h"
#include "txt/asset_font_manager.h"
#include "txt/text_style.h"
namespace txt {
......@@ -44,32 +45,51 @@ class FontCollection : public std::enable_shared_from_this<FontCollection> {
void SetTestFontManager(sk_sp<SkFontMgr> font_manager);
std::shared_ptr<minikin::FontCollection> GetMinikinFontCollectionForFamily(
const std::string& family);
const std::string& family,
const std::string& locale);
const std::shared_ptr<minikin::FontFamily>& MatchFallbackFont(uint32_t ch);
const std::shared_ptr<minikin::FontFamily>& MatchFallbackFont(
uint32_t ch,
std::string locale);
// Do not provide alternative fonts that can match characters which are
// missing from the requested font family.
void DisableFontFallback();
private:
struct FamilyKey {
FamilyKey(const std::string& family, const std::string& loc)
: font_family(family), locale(loc) {}
std::string font_family;
std::string locale;
bool operator==(const FamilyKey& other) const;
struct Hasher {
size_t operator()(const FamilyKey& key) const;
};
};
sk_sp<SkFontMgr> default_font_manager_;
sk_sp<SkFontMgr> asset_font_manager_;
sk_sp<SkFontMgr> test_font_manager_;
std::unordered_map<std::string, std::shared_ptr<minikin::FontCollection>>
std::unordered_map<FamilyKey,
std::shared_ptr<minikin::FontCollection>,
FamilyKey::Hasher>
font_collections_cache_;
std::unordered_map<SkFontID, std::shared_ptr<minikin::FontFamily>>
fallback_fonts_;
std::unordered_map<std::string, std::set<SkFontID>>
fallback_fonts_for_locale_;
std::shared_ptr<minikin::FontFamily> null_family_;
bool enable_font_fallback_;
std::vector<sk_sp<SkFontMgr>> GetFontManagerOrder() const;
const std::shared_ptr<minikin::FontFamily>& GetFontFamilyForTypeface(
const std::shared_ptr<minikin::FontFamily>& GetFallbackFont(
const sk_sp<SkTypeface>& typeface);
void UpdateFallbackFonts(sk_sp<SkFontMgr> manager);
FXL_DISALLOW_COPY_AND_ASSIGN(FontCollection);
};
......
......@@ -160,16 +160,6 @@ void GetFontAndMinikinPaint(const TextStyle& style,
paint->paintFlags |= minikin::LinearTextFlag;
}
sk_sp<SkTypeface> GetDefaultSkiaTypeface(
const std::shared_ptr<txt::FontCollection>& font_collection,
const TextStyle& style) {
std::shared_ptr<minikin::FontCollection> collection =
font_collection->GetMinikinFontCollectionForFamily(style.font_family);
minikin::FakedFont faked_font =
collection->baseFontFaked(GetMinikinFontStyle(style));
return static_cast<FontSkia*>(faked_font.font)->GetSkTypeface();
}
void FindWords(const std::vector<uint16_t>& text,
size_t start,
size_t end,
......@@ -286,8 +276,7 @@ bool Paragraph::ComputeLineBreaks() {
minikin::MinikinPaint paint;
GetFontAndMinikinPaint(run.style, &font, &paint);
std::shared_ptr<minikin::FontCollection> collection =
font_collection_->GetMinikinFontCollectionForFamily(
run.style.font_family);
GetMinikinFontCollectionForStyle(run.style);
if (collection == nullptr) {
FXL_LOG(INFO) << "Could not find font collection for family \""
<< run.style.font_family << "\".";
......@@ -502,8 +491,7 @@ void Paragraph::Layout(double width, bool force) {
paint.setTextSize(run.style().font_size);
std::shared_ptr<minikin::FontCollection> minikin_font_collection =
font_collection_->GetMinikinFontCollectionForFamily(
run.style().font_family);
GetMinikinFontCollectionForStyle(run.style());
// Lay out this run.
uint16_t* text_ptr = text_.data();
......@@ -753,7 +741,7 @@ void Paragraph::Layout(double width, bool force) {
if (paint_records.empty()) {
SkPaint::FontMetrics metrics;
TextStyle style(paragraph_style_.GetTextStyle());
paint.setTypeface(GetDefaultSkiaTypeface(font_collection_, style));
paint.setTypeface(GetDefaultSkiaTypeface(style));
paint.setTextSize(style.font_size);
paint.getFontMetrics(&metrics);
update_line_metrics(metrics, style);
......@@ -855,6 +843,31 @@ void Paragraph::SetFontCollection(
font_collection_ = std::move(font_collection);
}
std::shared_ptr<minikin::FontCollection>
Paragraph::GetMinikinFontCollectionForStyle(const TextStyle& style) {
std::string locale;
if (!style.locale.empty()) {
uint32_t language_list_id =
minikin::FontStyle::registerLanguageList(style.locale);
const minikin::FontLanguages& langs =
minikin::FontLanguageListCache::getById(language_list_id);
if (langs.size()) {
locale = langs[0].getString();
}
}
return font_collection_->GetMinikinFontCollectionForFamily(style.font_family,
locale);
}
sk_sp<SkTypeface> Paragraph::GetDefaultSkiaTypeface(const TextStyle& style) {
std::shared_ptr<minikin::FontCollection> collection =
GetMinikinFontCollectionForStyle(style);
minikin::FakedFont faked_font =
collection->baseFontFaked(GetMinikinFontStyle(style));
return static_cast<FontSkia*>(faked_font.font)->GetSkTypeface();
}
// The x,y coordinates will be the very top left corner of the rendered
// paragraph.
void Paragraph::Paint(SkCanvas* canvas, double x, double y) {
......
......@@ -313,6 +313,13 @@ class Paragraph {
// Draws the background onto the canvas.
void PaintBackground(SkCanvas* canvas, const PaintRecord& record);
// Obtain a Minikin font collection matching this text style.
std::shared_ptr<minikin::FontCollection> GetMinikinFontCollectionForStyle(
const TextStyle& style);
// Get a default SkTypeface for a text style.
sk_sp<SkTypeface> GetDefaultSkiaTypeface(const TextStyle& style);
FXL_DISALLOW_COPY_AND_ASSIGN(Paragraph);
};
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册