| /* |
| * Copyright 2018 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "SkFontMgr_fuchsia.h" |
| |
| #include <fuchsia/fonts/cpp/fidl.h> |
| #include <lib/zx/vmar.h> |
| #include <strings.h> |
| #include <memory> |
| #include <unordered_map> |
| |
| #include "third_party/skia/src/core/SkFontDescriptor.h" |
| #include "third_party/skia/src/ports/SkFontMgr_custom.h" |
| |
| #include "SkFontMgr.h" |
| #include "SkStream.h" |
| #include "SkTypeface.h" |
| #include "SkTypefaceCache.h" |
| |
| void UnmapMemory(const void* buffer, uint64_t size) { |
| static_assert(sizeof(void*) == sizeof(uint64_t), "pointers aren't 64-bit"); |
| zx::vmar::root_self()->unmap(reinterpret_cast<uintptr_t>(buffer), size); |
| } |
| |
| struct ReleaseSkDataContext { |
| uint64_t fBufferSize; |
| std::function<void()> releaseProc; |
| |
| ReleaseSkDataContext(uint64_t bufferSize, const std::function<void()>& releaseProc) |
| : fBufferSize(bufferSize), releaseProc(releaseProc) {} |
| }; |
| |
| void ReleaseSkData(const void* buffer, void* context) { |
| auto releaseSkDataContext = reinterpret_cast<ReleaseSkDataContext*>(context); |
| SkASSERT(releaseSkDataContext); |
| UnmapMemory(buffer, releaseSkDataContext->fBufferSize); |
| releaseSkDataContext->releaseProc(); |
| delete releaseSkDataContext; |
| } |
| |
| sk_sp<SkData> MakeSkDataFromBuffer(const fuchsia::mem::Buffer& data, |
| std::function<void()> release_proc) { |
| uint64_t size = data.size; |
| uintptr_t buffer = 0; |
| zx_status_t status = zx::vmar::root_self()->map(0, data.vmo, 0, size, ZX_VM_PERM_READ, &buffer); |
| if (status != ZX_OK) return nullptr; |
| auto context = new ReleaseSkDataContext(size, release_proc); |
| return SkData::MakeWithProc(reinterpret_cast<void*>(buffer), size, ReleaseSkData, context); |
| } |
| |
| fuchsia::fonts::Slant SkToFuchsiaSlant(SkFontStyle::Slant slant) { |
| switch (slant) { |
| case SkFontStyle::kOblique_Slant: |
| return fuchsia::fonts::Slant::OBLIQUE; |
| case SkFontStyle::kItalic_Slant: |
| return fuchsia::fonts::Slant::ITALIC; |
| case SkFontStyle::kUpright_Slant: |
| default: |
| return fuchsia::fonts::Slant::UPRIGHT; |
| } |
| } |
| |
| SkFontStyle::Slant FuchsiaToSkSlant(fuchsia::fonts::Slant slant) { |
| switch (slant) { |
| case fuchsia::fonts::Slant::OBLIQUE: |
| return SkFontStyle::kOblique_Slant; |
| case fuchsia::fonts::Slant::ITALIC: |
| return SkFontStyle::kItalic_Slant; |
| case fuchsia::fonts::Slant::UPRIGHT: |
| default: |
| return SkFontStyle::kUpright_Slant; |
| } |
| } |
| |
| constexpr struct { |
| const char* fName; |
| fuchsia::fonts::FallbackGroup fFallbackGroup; |
| } kFallbackGroupsByName[] = { |
| {"serif", fuchsia::fonts::FallbackGroup::SERIF}, |
| {"sans", fuchsia::fonts::FallbackGroup::SANS_SERIF}, |
| {"sans-serif", fuchsia::fonts::FallbackGroup::SANS_SERIF}, |
| {"mono", fuchsia::fonts::FallbackGroup::MONOSPACE}, |
| {"monospace", fuchsia::fonts::FallbackGroup::MONOSPACE}, |
| {"cursive", fuchsia::fonts::FallbackGroup::CURSIVE}, |
| {"fantasy", fuchsia::fonts::FallbackGroup::FANTASY}, |
| }; |
| |
| fuchsia::fonts::FallbackGroup GetFallbackGroupByName(const char* name) { |
| if (!name) return fuchsia::fonts::FallbackGroup::NONE; |
| for (auto& group : kFallbackGroupsByName) { |
| if (strcasecmp(group.fName, name) == 0) { |
| return group.fFallbackGroup; |
| } |
| } |
| return fuchsia::fonts::FallbackGroup::NONE; |
| } |
| |
| struct TypefaceId { |
| uint32_t bufferId; |
| uint32_t ttcIndex; |
| |
| bool operator==(TypefaceId& other) { |
| return std::tie(bufferId, ttcIndex) == std::tie(other.bufferId, other.ttcIndex); |
| } |
| } |
| |
| constexpr kNullTypefaceId = {0xFFFFFFFF, 0xFFFFFFFF}; |
| |
| class SkTypeface_Fuchsia : public SkTypeface_Stream { |
| public: |
| SkTypeface_Fuchsia(std::unique_ptr<SkFontData> fontData, const SkFontStyle& style, |
| bool isFixedPitch, const SkString familyName, TypefaceId id) |
| : SkTypeface_Stream(std::move(fontData), style, isFixedPitch, |
| /*sys_font=*/true, familyName) |
| , fId(id) {} |
| |
| TypefaceId id() { return fId; } |
| |
| private: |
| TypefaceId fId; |
| }; |
| |
| sk_sp<SkTypeface> CreateTypefaceFromSkStream(std::unique_ptr<SkStreamAsset> stream, |
| const SkFontArguments& args, TypefaceId id) { |
| using Scanner = SkTypeface_FreeType::Scanner; |
| Scanner scanner; |
| bool isFixedPitch; |
| SkFontStyle style; |
| SkString name; |
| Scanner::AxisDefinitions axisDefinitions; |
| if (!scanner.scanFont(stream.get(), args.getCollectionIndex(), &name, &style, &isFixedPitch, |
| &axisDefinitions)) { |
| return nullptr; |
| } |
| |
| const SkFontArguments::VariationPosition position = args.getVariationDesignPosition(); |
| SkAutoSTMalloc<4, SkFixed> axisValues(axisDefinitions.count()); |
| Scanner::computeAxisValues(axisDefinitions, position, axisValues, name); |
| |
| auto fontData = std::make_unique<SkFontData>(std::move(stream), args.getCollectionIndex(), |
| axisValues.get(), axisDefinitions.count()); |
| return sk_make_sp<SkTypeface_Fuchsia>(std::move(fontData), style, isFixedPitch, name, id); |
| } |
| |
| sk_sp<SkTypeface> CreateTypefaceFromSkData(sk_sp<SkData> data, TypefaceId id) { |
| return CreateTypefaceFromSkStream(std::make_unique<SkMemoryStream>(std::move(data)), |
| SkFontArguments().setCollectionIndex(id.ttcIndex), id); |
| } |
| |
| class SkFontMgr_Fuchsia final : public SkFontMgr { |
| public: |
| SkFontMgr_Fuchsia(fuchsia::fonts::ProviderSyncPtr provider); |
| ~SkFontMgr_Fuchsia() override; |
| |
| protected: |
| // SkFontMgr overrides. |
| int onCountFamilies() const override; |
| void onGetFamilyName(int index, SkString* familyName) const override; |
| SkFontStyleSet* onMatchFamily(const char familyName[]) const override; |
| SkFontStyleSet* onCreateStyleSet(int index) const override; |
| SkTypeface* onMatchFamilyStyle(const char familyName[], const SkFontStyle&) const override; |
| SkTypeface* onMatchFamilyStyleCharacter(const char familyName[], const SkFontStyle&, |
| const char* bcp47[], int bcp47Count, |
| SkUnichar character) const override; |
| SkTypeface* onMatchFaceStyle(const SkTypeface*, const SkFontStyle&) const override; |
| sk_sp<SkTypeface> onMakeFromData(sk_sp<SkData>, int ttcIndex) const override; |
| sk_sp<SkTypeface> onMakeFromStreamIndex(std::unique_ptr<SkStreamAsset>, |
| int ttcIndex) const override; |
| sk_sp<SkTypeface> onMakeFromStreamArgs(std::unique_ptr<SkStreamAsset>, |
| const SkFontArguments&) const override; |
| sk_sp<SkTypeface> onMakeFromFile(const char path[], int ttcIndex) const override; |
| sk_sp<SkTypeface> onLegacyMakeTypeface(const char familyName[], SkFontStyle) const override; |
| |
| private: |
| friend class SkFontStyleSet_Fuchsia; |
| |
| sk_sp<SkTypeface> FetchTypeface(const char familyName[], const SkFontStyle& style, |
| const char* bcp47[], int bcp47Count, SkUnichar character, |
| bool allow_fallback, bool exact_style_match) const; |
| |
| sk_sp<SkData> GetOrCreateSkData(int bufferId, const fuchsia::mem::Buffer& buffer) const; |
| void OnSkDataDeleted(int bufferId) const; |
| |
| sk_sp<SkTypeface> GetOrCreateTypeface(TypefaceId id, const fuchsia::mem::Buffer& buffer) const; |
| |
| mutable fuchsia::fonts::ProviderSyncPtr fFontProvider; |
| |
| mutable SkMutex fCacheMutex; |
| |
| // Must be accessed only with fCacheMutex acquired. |
| mutable std::unordered_map<int, SkData*> fBufferCache; |
| mutable SkTypefaceCache fTypefaceCache; |
| }; |
| |
| class SkFontStyleSet_Fuchsia : public SkFontStyleSet { |
| public: |
| SkFontStyleSet_Fuchsia(sk_sp<SkFontMgr_Fuchsia> font_manager, std::string familyName, |
| std::vector<SkFontStyle> styles) |
| : fFontManager(font_manager), fFamilyName(familyName), fStyles(styles) {} |
| |
| ~SkFontStyleSet_Fuchsia() override = default; |
| |
| int count() override { return fStyles.size(); } |
| |
| void getStyle(int index, SkFontStyle* style, SkString* styleName) override { |
| SkASSERT(index >= 0 && index < static_cast<int>(fStyles.size())); |
| if (style) *style = fStyles[index]; |
| |
| // We don't have style names. Return an empty name. |
| if (styleName) styleName->reset(); |
| } |
| |
| SkTypeface* createTypeface(int index) override { |
| SkASSERT(index >= 0 && index < static_cast<int>(fStyles.size())); |
| |
| if (fTypefaces.empty()) fTypefaces.resize(fStyles.size()); |
| |
| if (!fTypefaces[index]) { |
| fTypefaces[index] = fFontManager->FetchTypeface( |
| fFamilyName.c_str(), fStyles[index], /*bcp47=*/nullptr, |
| /*bcp47Count=*/0, /*character=*/0, |
| /*allow_fallback=*/false, /*exact_style_match=*/true); |
| } |
| |
| return SkSafeRef(fTypefaces[index].get()); |
| } |
| |
| SkTypeface* matchStyle(const SkFontStyle& pattern) override { return matchStyleCSS3(pattern); } |
| |
| private: |
| sk_sp<SkFontMgr_Fuchsia> fFontManager; |
| std::string fFamilyName; |
| std::vector<SkFontStyle> fStyles; |
| std::vector<sk_sp<SkTypeface>> fTypefaces; |
| }; |
| |
| SkFontMgr_Fuchsia::SkFontMgr_Fuchsia(fuchsia::fonts::ProviderSyncPtr provider) |
| : fFontProvider(std::move(provider)) {} |
| |
| SkFontMgr_Fuchsia::~SkFontMgr_Fuchsia() = default; |
| |
| int SkFontMgr_Fuchsia::onCountFamilies() const { |
| // Family enumeration is not supported. |
| return 0; |
| } |
| |
| void SkFontMgr_Fuchsia::onGetFamilyName(int index, SkString* familyName) const { |
| // Family enumeration is not supported. |
| familyName->reset(); |
| } |
| |
| SkFontStyleSet* SkFontMgr_Fuchsia::onCreateStyleSet(int index) const { |
| // Family enumeration is not supported. |
| return nullptr; |
| } |
| |
| SkFontStyleSet* SkFontMgr_Fuchsia::onMatchFamily(const char familyName[]) const { |
| fuchsia::fonts::FamilyInfoPtr familyInfo; |
| int result = fFontProvider->GetFamilyInfo(familyName, &familyInfo); |
| if (result != ZX_OK || !familyInfo) return nullptr; |
| |
| std::vector<SkFontStyle> styles; |
| #ifdef USE_STD_FOR_NON_NULLABLE_FIDL_FIELDS |
| for (auto& style : familyInfo->styles) { |
| #else |
| for (auto& style : *(familyInfo->styles)) { |
| #endif |
| styles.push_back(SkFontStyle(style.weight, style.width, FuchsiaToSkSlant(style.slant))); |
| } |
| |
| return new SkFontStyleSet_Fuchsia(sk_ref_sp(this), familyInfo->name, std::move(styles)); |
| } |
| |
| SkTypeface* SkFontMgr_Fuchsia::onMatchFamilyStyle(const char familyName[], |
| const SkFontStyle& style) const { |
| sk_sp<SkTypeface> typeface = |
| FetchTypeface(familyName, style, /*bcp47=*/nullptr, |
| /*bcp47Count=*/0, /*character=*/0, |
| /*allow_fallback=*/false, /*exact_style_match=*/false); |
| return typeface.release(); |
| } |
| |
| SkTypeface* SkFontMgr_Fuchsia::onMatchFamilyStyleCharacter(const char familyName[], |
| const SkFontStyle& style, |
| const char* bcp47[], int bcp47Count, |
| SkUnichar character) const { |
| sk_sp<SkTypeface> typeface = |
| FetchTypeface(familyName, style, bcp47, bcp47Count, character, /*allow_fallback=*/true, |
| /*exact_style_match=*/false); |
| return typeface.release(); |
| } |
| |
| SkTypeface* SkFontMgr_Fuchsia::onMatchFaceStyle(const SkTypeface*, const SkFontStyle&) const { |
| return nullptr; |
| } |
| |
| sk_sp<SkTypeface> SkFontMgr_Fuchsia::onMakeFromData(sk_sp<SkData>, int ttcIndex) const { |
| SkASSERT(false); |
| return nullptr; |
| } |
| |
| sk_sp<SkTypeface> SkFontMgr_Fuchsia::onMakeFromStreamIndex(std::unique_ptr<SkStreamAsset> asset, |
| int ttcIndex) const { |
| return makeFromStream(std::move(asset), SkFontArguments().setCollectionIndex(ttcIndex)); |
| } |
| |
| sk_sp<SkTypeface> SkFontMgr_Fuchsia::onMakeFromStreamArgs(std::unique_ptr<SkStreamAsset> asset, |
| const SkFontArguments& args) const { |
| return CreateTypefaceFromSkStream(std::move(asset), args, kNullTypefaceId); |
| } |
| |
| sk_sp<SkTypeface> SkFontMgr_Fuchsia::onMakeFromFile(const char path[], int ttcIndex) const { |
| return makeFromStream(std::make_unique<SkFILEStream>(path), ttcIndex); |
| } |
| |
| sk_sp<SkTypeface> SkFontMgr_Fuchsia::onLegacyMakeTypeface(const char familyName[], |
| SkFontStyle style) const { |
| return sk_sp<SkTypeface>(matchFamilyStyle(familyName, style)); |
| } |
| |
| sk_sp<SkTypeface> SkFontMgr_Fuchsia::FetchTypeface(const char familyName[], |
| const SkFontStyle& style, const char* bcp47[], |
| int bcp47Count, SkUnichar character, |
| bool allow_fallback, |
| bool exact_style_match) const { |
| fuchsia::fonts::Request request; |
| request.weight = style.weight(); |
| request.width = style.width(); |
| request.slant = SkToFuchsiaSlant(style.slant()); |
| #ifdef USE_STD_FOR_NON_NULLABLE_FIDL_FIELDS |
| request.language.reset(std::vector<std::string>(bcp47, bcp47 + bcp47Count)); |
| #else |
| request.language.reset(std::vector<fidl::StringPtr>(bcp47, bcp47 + bcp47Count)); |
| #endif |
| request.character = character; |
| request.fallback_group = GetFallbackGroupByName(familyName); |
| |
| // If family name is not specified or it is a generic fallback group name (e.g. "serif") then |
| // enable fallback, otherwise pass the family name as is. |
| if (!familyName || *familyName == '\0' || |
| request.fallback_group != fuchsia::fonts::FallbackGroup::NONE) { |
| request.family = ""; |
| allow_fallback = true; |
| } else { |
| request.family = familyName; |
| } |
| |
| request.flags = 0; |
| if (!allow_fallback) request.flags |= fuchsia::fonts::REQUEST_FLAG_NO_FALLBACK; |
| if (exact_style_match) request.flags |= fuchsia::fonts::REQUEST_FLAG_EXACT_MATCH; |
| |
| fuchsia::fonts::ResponsePtr response; |
| int result = fFontProvider->GetFont(std::move(request), &response); |
| if (result != ZX_OK) return nullptr; |
| |
| // The service may return null response if there is no font matching the request. |
| if (!response) return nullptr; |
| |
| return GetOrCreateTypeface(TypefaceId{response->buffer_id, response->font_index}, |
| response->buffer); |
| } |
| |
| sk_sp<SkData> SkFontMgr_Fuchsia::GetOrCreateSkData(int bufferId, |
| const fuchsia::mem::Buffer& buffer) const { |
| fCacheMutex.assertHeld(); |
| |
| auto iter = fBufferCache.find(bufferId); |
| if (iter != fBufferCache.end()) { |
| return sk_ref_sp(iter->second); |
| } |
| auto font_mgr = sk_ref_sp(this); |
| auto data = MakeSkDataFromBuffer(buffer, |
| [font_mgr, bufferId]() { font_mgr->OnSkDataDeleted(bufferId); }); |
| if (!data) { |
| return nullptr; |
| } |
| fBufferCache[bufferId] = data.get(); |
| return data; |
| } |
| |
| void SkFontMgr_Fuchsia::OnSkDataDeleted(int bufferId) const { |
| SK_UNUSED bool wasFound = fBufferCache.erase(bufferId) != 0; |
| SkASSERT(wasFound); |
| } |
| |
| static bool FindByTypefaceId(SkTypeface* cachedTypeface, void* ctx) { |
| SkTypeface_Fuchsia* cachedFuchsiaTypeface = static_cast<SkTypeface_Fuchsia*>(cachedTypeface); |
| TypefaceId* id = static_cast<TypefaceId*>(ctx); |
| |
| return cachedFuchsiaTypeface->id() == *id; |
| } |
| |
| sk_sp<SkTypeface> SkFontMgr_Fuchsia::GetOrCreateTypeface(TypefaceId id, |
| const fuchsia::mem::Buffer& buffer) const { |
| SkAutoMutexAcquire mutexLock(fCacheMutex); |
| |
| SkTypeface* cached = fTypefaceCache.findByProcAndRef(FindByTypefaceId, &id); |
| if (cached) return sk_sp<SkTypeface>(cached); |
| |
| sk_sp<SkData> data = GetOrCreateSkData(id.bufferId, buffer); |
| if (!data) return nullptr; |
| |
| auto result = CreateTypefaceFromSkData(std::move(data), id); |
| fTypefaceCache.add(result.get()); |
| return result; |
| } |
| |
| SK_API sk_sp<SkFontMgr> SkFontMgr_New_Fuchsia(fuchsia::fonts::ProviderSyncPtr provider) { |
| return sk_make_sp<SkFontMgr_Fuchsia>(std::move(provider)); |
| } |