blob: c1f94600371e465f63469239b16728ee47ff98e9 [file] [log] [blame]
// Copyright 2018 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 "skia/ext/fontmgr_fuchsia.h"
#include <lib/zx/vmar.h>
#include <unordered_map>
#include "base/bind.h"
#include "base/callback.h"
#include "base/containers/small_map.h"
#include "base/fuchsia/fuchsia_logging.h"
#include "base/logging.h"
#include "base/strings/string_util.h"
#include "base/threading/thread_checker.h"
#include "third_party/icu/source/common/unicode/uchar.h"
#include "third_party/skia/src/core/SkFontDescriptor.h"
#include "third_party/skia/src/core/SkMakeUnique.h"
#include "third_party/skia/src/ports/SkFontHost_FreeType_common.h"
#include "third_party/skia/src/ports/SkFontMgr_custom.h"
namespace skia {
namespace {
constexpr char kDefaultFont[] = "Roboto";
// Currently FontProvider doesn't support font aliases. The map below is used to
// map common web fonts to font families that are expected to be present in
// FontProvider.
constexpr struct {
const char* font_name_in;
const char* font_name_out;
} kFontMap[] = {{"sans", "Roboto"},
{"sans-serif", "Roboto"},
{"arial", "Roboto"},
{"helvetica", "Roboto"},
{"roboto", "Roboto"},
{"roboto slab", "RobotoSlab"},
{"serif", "RobotoSlab"},
{"georgia", "RobotoSlab"},
{"times", "RobotoSlab"},
{"times roman", "RobotoSlab"},
{"times new roman", "RobotoSlab"},
{"roboto mono", "RobotoMono"},
{"consolas", "RobotoMono"},
{"courier", "RobotoMono"},
{"courier new", "RobotoMono"},
{"monospace", "RobotoMono"}};
fuchsia::fonts::FontSlant ToFontSlant(SkFontStyle::Slant slant) {
return (slant == SkFontStyle::kItalic_Slant)
? fuchsia::fonts::FontSlant::ITALIC
: fuchsia::fonts::FontSlant::UPRIGHT;
}
void UnmapMemory(const void* buffer, void* context) {
const uintptr_t size = reinterpret_cast<uintptr_t>(context);
zx::vmar::root_self()->unmap(reinterpret_cast<uintptr_t>(buffer), size);
}
sk_sp<SkData> BufferToSkData(fuchsia::mem::Buffer data) {
uintptr_t buffer = 0;
zx_status_t status = zx::vmar::root_self()->map(
0, data.vmo, 0, data.size, ZX_VM_FLAG_PERM_READ, &buffer);
if (status != ZX_OK) {
ZX_DLOG(ERROR, status) << "zx_vmar_map";
return nullptr;
}
return SkData::MakeWithProc(reinterpret_cast<void*>(buffer), data.size,
UnmapMemory, reinterpret_cast<void*>(data.size));
}
// SkTypeface that notifies FontCache when it is being destroyed.
class CachedTypeface : public SkTypeface_Stream {
public:
CachedTypeface(std::unique_ptr<SkFontData> font_data,
const SkFontStyle& style,
bool is_fixed_pitch,
const SkString family_name,
base::OnceClosure on_deleted)
: SkTypeface_Stream(std::move(font_data),
style,
is_fixed_pitch,
/*sys_font=*/true,
family_name),
on_deleted_(std::move(on_deleted)) {}
~CachedTypeface() override {
if (on_deleted_)
std::move(on_deleted_).Run();
}
private:
base::OnceClosure on_deleted_;
DISALLOW_COPY_AND_ASSIGN(CachedTypeface);
};
sk_sp<SkTypeface> CreateTypefaceFromSkStream(
std::unique_ptr<SkStreamAsset> stream,
const SkFontArguments& args,
base::OnceClosure on_deleted) {
using Scanner = SkTypeface_FreeType::Scanner;
Scanner scanner;
bool is_fixed_pitch;
SkFontStyle style;
SkString name;
Scanner::AxisDefinitions axis_definitions;
if (!scanner.scanFont(stream.get(), args.getCollectionIndex(), &name, &style,
&is_fixed_pitch, &axis_definitions)) {
return nullptr;
}
const SkFontArguments::VariationPosition position =
args.getVariationDesignPosition();
SkAutoSTMalloc<4, SkFixed> axis_values(axis_definitions.count());
Scanner::computeAxisValues(axis_definitions, position, axis_values, name);
auto font_data =
std::make_unique<SkFontData>(std::move(stream), args.getCollectionIndex(),
axis_values.get(), axis_definitions.count());
return sk_make_sp<CachedTypeface>(std::move(font_data), style, is_fixed_pitch,
name, std::move(on_deleted));
}
sk_sp<SkTypeface> CreateTypefaceFromFontData(fuchsia::fonts::FontData font_data,
base::OnceClosure on_deleted) {
sk_sp<SkData> data = BufferToSkData(std::move(font_data.buffer));
if (!data)
return nullptr;
// TODO(https://crbug.com/800156): Initialize font arguments with font index
// when font collection support is implemented in FontProvider.
SkFontArguments args;
return CreateTypefaceFromSkStream(
std::make_unique<SkMemoryStream>(std::move(data)), args,
std::move(on_deleted));
}
} // namespace
class FuchsiaFontManager::FontCache {
public:
FontCache();
~FontCache();
sk_sp<SkTypeface> GetTypefaceFromFontData(fuchsia::fonts::FontData font_data);
private:
void OnTypefaceDeleted(zx_koid_t vmo_koid);
THREAD_CHECKER(thread_checker_);
// SkTypeface cache. They key is koid of the VMO that contains the typeface.
// This allows to reuse previously-created SkTypeface when FontProvider
// returns FontData with the same VMO.
base::small_map<std::unordered_map<zx_koid_t, SkTypeface*>> typefaces_;
base::WeakPtrFactory<FontCache> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(FontCache);
};
FuchsiaFontManager::FontCache::FontCache() : weak_factory_(this) {}
FuchsiaFontManager::FontCache::~FontCache() = default;
sk_sp<SkTypeface> FuchsiaFontManager::FontCache::GetTypefaceFromFontData(
fuchsia::fonts::FontData font_data) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
zx_info_handle_basic_t vmo_info;
zx_status_t status = font_data.buffer.vmo.get_info(
ZX_INFO_HANDLE_BASIC, &vmo_info, sizeof(vmo_info), nullptr, nullptr);
if (status != ZX_OK) {
ZX_DLOG(ERROR, status) << "zx_object_get_info";
return nullptr;
}
sk_sp<SkTypeface> result;
SkTypeface** cached_typeface = &(typefaces_[vmo_info.koid]);
if (*cached_typeface) {
result = sk_ref_sp(*cached_typeface);
} else {
result = CreateTypefaceFromFontData(
std::move(font_data),
base::BindOnce(&FontCache::OnTypefaceDeleted,
weak_factory_.GetWeakPtr(), vmo_info.koid));
*cached_typeface = result.get();
}
return result;
}
void FuchsiaFontManager::FontCache::OnTypefaceDeleted(zx_koid_t vmo_koid) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
bool was_found = typefaces_.erase(vmo_koid) != 0;
DCHECK(was_found);
}
FuchsiaFontManager::FuchsiaFontManager(
fuchsia::fonts::FontProviderSyncPtr font_provider)
: font_provider_(std::move(font_provider)), font_cache_(new FontCache()) {
for (auto& m : kFontMap) {
font_map_[m.font_name_in] = m.font_name_out;
}
// Get default font.
default_typeface_.reset(onMatchFamilyStyle(kDefaultFont, SkFontStyle()));
if (!default_typeface_) {
default_typeface_ = sk_make_sp<SkTypeface_Empty>();
LOG(ERROR) << "Failed to get default font from the FontProvider.";
}
}
FuchsiaFontManager::~FuchsiaFontManager() = default;
int FuchsiaFontManager::onCountFamilies() const {
NOTREACHED();
return 0;
}
void FuchsiaFontManager::onGetFamilyName(int index,
SkString* family_name) const {
NOTREACHED();
}
SkFontStyleSet* FuchsiaFontManager::onCreateStyleSet(int index) const {
NOTREACHED();
return nullptr;
}
SkFontStyleSet* FuchsiaFontManager::onMatchFamily(
const char family_name[]) const {
NOTREACHED();
return nullptr;
}
SkTypeface* FuchsiaFontManager::onMatchFamilyStyle(
const char family_name[],
const SkFontStyle& style) const {
std::string family_name_lowercase = base::ToLowerASCII(family_name);
fuchsia::fonts::FontRequest request;
auto it = font_map_.find(family_name_lowercase);
request.family = (it != font_map_.end()) ? it->second.c_str() : family_name;
request.weight = style.weight();
request.width = style.width();
request.slant = ToFontSlant(style.slant());
fuchsia::fonts::FontResponsePtr response;
zx_status_t status = font_provider_->GetFont(std::move(request), &response);
if (status != ZX_OK) {
ZX_DLOG(ERROR, status) << "Failed to query font provider.";
} else if (response) {
sk_sp<SkTypeface> result =
font_cache_->GetTypefaceFromFontData(std::move(response->data));
if (result)
return result.release();
LOG(ERROR) << "FontProvider returned invalid FontData for " << family_name;
}
// If Sans was requested and we failed to get a valid response from
// FontProvider then return |default_typeface_|. blink::FontCache queries Sans
// as a last-resort font. Returning |default_typeface_| here ensures that the
// renderer doesn't crash when FontProvider stops working.
if (family_name_lowercase == "sans") {
// Copy |default_typeface_| to increment ref-count before returning it.
sk_sp<SkTypeface> result = default_typeface_;
return result.release();
}
return nullptr;
}
SkTypeface* FuchsiaFontManager::onMatchFamilyStyleCharacter(
const char family_name[],
const SkFontStyle& style,
const char* bcp47[],
int bcp47Count,
SkUnichar character) const {
if (u_hasBinaryProperty(character, UCHAR_EMOJI))
return onMatchFamilyStyle("Noto Color Emoji", style);
return nullptr;
}
SkTypeface* FuchsiaFontManager::onMatchFaceStyle(
const SkTypeface* typeface,
const SkFontStyle& style) const {
NOTREACHED();
return nullptr;
}
sk_sp<SkTypeface> FuchsiaFontManager::onMakeFromData(sk_sp<SkData> data,
int ttc_index) const {
return makeFromStream(std::make_unique<SkMemoryStream>(std::move(data)),
ttc_index);
}
sk_sp<SkTypeface> FuchsiaFontManager::onMakeFromStreamIndex(
std::unique_ptr<SkStreamAsset> stream,
int ttc_index) const {
return makeFromStream(std::move(stream),
SkFontArguments().setCollectionIndex(ttc_index));
}
sk_sp<SkTypeface> FuchsiaFontManager::onMakeFromStreamArgs(
std::unique_ptr<SkStreamAsset> stream,
const SkFontArguments& args) const {
return CreateTypefaceFromSkStream(std::move(stream), args,
/*on_deleted=*/base::OnceClosure());
}
sk_sp<SkTypeface> FuchsiaFontManager::onMakeFromFile(const char path[],
int ttc_index) const {
NOTREACHED();
return nullptr;
}
sk_sp<SkTypeface> FuchsiaFontManager::onLegacyMakeTypeface(
const char family_name[],
SkFontStyle style) const {
sk_sp<SkTypeface> typeface;
if (family_name)
typeface.reset(this->onMatchFamilyStyle(family_name, style));
return typeface;
}
} // namespace skia