1 /*
2  * Copyright 2018 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "SkFontMgr_fuchsia.h"
9 
10 #include <fuchsia/fonts/cpp/fidl.h>
11 #include <lib/zx/vmar.h>
12 #include <strings.h>
13 #include <memory>
14 #include <unordered_map>
15 
16 #include "third_party/skia/src/core/SkFontDescriptor.h"
17 #include "third_party/skia/src/ports/SkFontMgr_custom.h"
18 
19 #include "SkFontMgr.h"
20 #include "SkStream.h"
21 #include "SkTypeface.h"
22 #include "SkTypefaceCache.h"
23 
UnmapMemory(const void * buffer,uint64_t size)24 void UnmapMemory(const void* buffer, uint64_t size) {
25     static_assert(sizeof(void*) == sizeof(uint64_t), "pointers aren't 64-bit");
26     zx::vmar::root_self()->unmap(reinterpret_cast<uintptr_t>(buffer), size);
27 }
28 
29 struct ReleaseSkDataContext {
30     uint64_t fBufferSize;
31     std::function<void()> releaseProc;
32 
ReleaseSkDataContextReleaseSkDataContext33     ReleaseSkDataContext(uint64_t bufferSize, const std::function<void()>& releaseProc)
34             : fBufferSize(bufferSize), releaseProc(releaseProc) {}
35 };
36 
ReleaseSkData(const void * buffer,void * context)37 void ReleaseSkData(const void* buffer, void* context) {
38     auto releaseSkDataContext = reinterpret_cast<ReleaseSkDataContext*>(context);
39     SkASSERT(releaseSkDataContext);
40     UnmapMemory(buffer, releaseSkDataContext->fBufferSize);
41     releaseSkDataContext->releaseProc();
42     delete releaseSkDataContext;
43 }
44 
MakeSkDataFromBuffer(const fuchsia::mem::Buffer & data,std::function<void ()> release_proc)45 sk_sp<SkData> MakeSkDataFromBuffer(const fuchsia::mem::Buffer& data,
46                                    std::function<void()> release_proc) {
47     uint64_t size = data.size;
48     uintptr_t buffer = 0;
49     zx_status_t status = zx::vmar::root_self()->map(0, data.vmo, 0, size, ZX_VM_PERM_READ, &buffer);
50     if (status != ZX_OK) return nullptr;
51     auto context = new ReleaseSkDataContext(size, release_proc);
52     return SkData::MakeWithProc(reinterpret_cast<void*>(buffer), size, ReleaseSkData, context);
53 }
54 
SkToFuchsiaSlant(SkFontStyle::Slant slant)55 fuchsia::fonts::Slant SkToFuchsiaSlant(SkFontStyle::Slant slant) {
56     switch (slant) {
57         case SkFontStyle::kOblique_Slant:
58             return fuchsia::fonts::Slant::OBLIQUE;
59         case SkFontStyle::kItalic_Slant:
60             return fuchsia::fonts::Slant::ITALIC;
61         case SkFontStyle::kUpright_Slant:
62         default:
63             return fuchsia::fonts::Slant::UPRIGHT;
64     }
65 }
66 
FuchsiaToSkSlant(fuchsia::fonts::Slant slant)67 SkFontStyle::Slant FuchsiaToSkSlant(fuchsia::fonts::Slant slant) {
68     switch (slant) {
69         case fuchsia::fonts::Slant::OBLIQUE:
70             return SkFontStyle::kOblique_Slant;
71         case fuchsia::fonts::Slant::ITALIC:
72             return SkFontStyle::kItalic_Slant;
73         case fuchsia::fonts::Slant::UPRIGHT:
74         default:
75             return SkFontStyle::kUpright_Slant;
76     }
77 }
78 
79 constexpr struct {
80     const char* fName;
81     fuchsia::fonts::FallbackGroup fFallbackGroup;
82 } kFallbackGroupsByName[] = {
83         {"serif", fuchsia::fonts::FallbackGroup::SERIF},
84         {"sans", fuchsia::fonts::FallbackGroup::SANS_SERIF},
85         {"sans-serif", fuchsia::fonts::FallbackGroup::SANS_SERIF},
86         {"mono", fuchsia::fonts::FallbackGroup::MONOSPACE},
87         {"monospace", fuchsia::fonts::FallbackGroup::MONOSPACE},
88         {"cursive", fuchsia::fonts::FallbackGroup::CURSIVE},
89         {"fantasy", fuchsia::fonts::FallbackGroup::FANTASY},
90 };
91 
GetFallbackGroupByName(const char * name)92 fuchsia::fonts::FallbackGroup GetFallbackGroupByName(const char* name) {
93     if (!name) return fuchsia::fonts::FallbackGroup::NONE;
94     for (auto& group : kFallbackGroupsByName) {
95         if (strcasecmp(group.fName, name) == 0) {
96             return group.fFallbackGroup;
97         }
98     }
99     return fuchsia::fonts::FallbackGroup::NONE;
100 }
101 
102 struct TypefaceId {
103     uint32_t bufferId;
104     uint32_t ttcIndex;
105 
operator ==TypefaceId106     bool operator==(TypefaceId& other) {
107         return std::tie(bufferId, ttcIndex) == std::tie(other.bufferId, other.ttcIndex);
108     }
109 }
110 
111 constexpr kNullTypefaceId = {0xFFFFFFFF, 0xFFFFFFFF};
112 
113 class SkTypeface_Fuchsia : public SkTypeface_Stream {
114 public:
SkTypeface_Fuchsia(std::unique_ptr<SkFontData> fontData,const SkFontStyle & style,bool isFixedPitch,const SkString familyName,TypefaceId id)115     SkTypeface_Fuchsia(std::unique_ptr<SkFontData> fontData, const SkFontStyle& style,
116                        bool isFixedPitch, const SkString familyName, TypefaceId id)
117             : SkTypeface_Stream(std::move(fontData), style, isFixedPitch,
118                                 /*sys_font=*/true, familyName)
119             , fId(id) {}
120 
id()121     TypefaceId id() { return fId; }
122 
123 private:
124     TypefaceId fId;
125 };
126 
CreateTypefaceFromSkStream(std::unique_ptr<SkStreamAsset> stream,const SkFontArguments & args,TypefaceId id)127 sk_sp<SkTypeface> CreateTypefaceFromSkStream(std::unique_ptr<SkStreamAsset> stream,
128                                              const SkFontArguments& args, TypefaceId id) {
129     using Scanner = SkTypeface_FreeType::Scanner;
130     Scanner scanner;
131     bool isFixedPitch;
132     SkFontStyle style;
133     SkString name;
134     Scanner::AxisDefinitions axisDefinitions;
135     if (!scanner.scanFont(stream.get(), args.getCollectionIndex(), &name, &style, &isFixedPitch,
136                           &axisDefinitions)) {
137         return nullptr;
138     }
139 
140     const SkFontArguments::VariationPosition position = args.getVariationDesignPosition();
141     SkAutoSTMalloc<4, SkFixed> axisValues(axisDefinitions.count());
142     Scanner::computeAxisValues(axisDefinitions, position, axisValues, name);
143 
144     auto fontData = std::make_unique<SkFontData>(std::move(stream), args.getCollectionIndex(),
145                                                  axisValues.get(), axisDefinitions.count());
146     return sk_make_sp<SkTypeface_Fuchsia>(std::move(fontData), style, isFixedPitch, name, id);
147 }
148 
CreateTypefaceFromSkData(sk_sp<SkData> data,TypefaceId id)149 sk_sp<SkTypeface> CreateTypefaceFromSkData(sk_sp<SkData> data, TypefaceId id) {
150     return CreateTypefaceFromSkStream(std::make_unique<SkMemoryStream>(std::move(data)),
151                                       SkFontArguments().setCollectionIndex(id.ttcIndex), id);
152 }
153 
154 class SkFontMgr_Fuchsia final : public SkFontMgr {
155 public:
156     SkFontMgr_Fuchsia(fuchsia::fonts::ProviderSyncPtr provider);
157     ~SkFontMgr_Fuchsia() override;
158 
159 protected:
160     // SkFontMgr overrides.
161     int onCountFamilies() const override;
162     void onGetFamilyName(int index, SkString* familyName) const override;
163     SkFontStyleSet* onMatchFamily(const char familyName[]) const override;
164     SkFontStyleSet* onCreateStyleSet(int index) const override;
165     SkTypeface* onMatchFamilyStyle(const char familyName[], const SkFontStyle&) const override;
166     SkTypeface* onMatchFamilyStyleCharacter(const char familyName[], const SkFontStyle&,
167                                             const char* bcp47[], int bcp47Count,
168                                             SkUnichar character) const override;
169     SkTypeface* onMatchFaceStyle(const SkTypeface*, const SkFontStyle&) const override;
170     sk_sp<SkTypeface> onMakeFromData(sk_sp<SkData>, int ttcIndex) const override;
171     sk_sp<SkTypeface> onMakeFromStreamIndex(std::unique_ptr<SkStreamAsset>,
172                                             int ttcIndex) const override;
173     sk_sp<SkTypeface> onMakeFromStreamArgs(std::unique_ptr<SkStreamAsset>,
174                                            const SkFontArguments&) const override;
175     sk_sp<SkTypeface> onMakeFromFile(const char path[], int ttcIndex) const override;
176     sk_sp<SkTypeface> onLegacyMakeTypeface(const char familyName[], SkFontStyle) const override;
177 
178 private:
179     friend class SkFontStyleSet_Fuchsia;
180 
181     sk_sp<SkTypeface> FetchTypeface(const char familyName[], const SkFontStyle& style,
182                                     const char* bcp47[], int bcp47Count, SkUnichar character,
183                                     bool allow_fallback, bool exact_style_match) const;
184 
185     sk_sp<SkData> GetOrCreateSkData(int bufferId, const fuchsia::mem::Buffer& buffer) const;
186     void OnSkDataDeleted(int bufferId) const;
187 
188     sk_sp<SkTypeface> GetOrCreateTypeface(TypefaceId id, const fuchsia::mem::Buffer& buffer) const;
189 
190     mutable fuchsia::fonts::ProviderSyncPtr fFontProvider;
191 
192     mutable SkMutex fCacheMutex;
193 
194     // Must be accessed only with fCacheMutex acquired.
195     mutable std::unordered_map<int, SkData*> fBufferCache;
196     mutable SkTypefaceCache fTypefaceCache;
197 };
198 
199 class SkFontStyleSet_Fuchsia : public SkFontStyleSet {
200 public:
SkFontStyleSet_Fuchsia(sk_sp<SkFontMgr_Fuchsia> font_manager,std::string familyName,std::vector<SkFontStyle> styles)201     SkFontStyleSet_Fuchsia(sk_sp<SkFontMgr_Fuchsia> font_manager, std::string familyName,
202                  std::vector<SkFontStyle> styles)
203             : fFontManager(font_manager), fFamilyName(familyName), fStyles(styles) {}
204 
205     ~SkFontStyleSet_Fuchsia() override = default;
206 
count()207     int count() override { return fStyles.size(); }
208 
getStyle(int index,SkFontStyle * style,SkString * styleName)209     void getStyle(int index, SkFontStyle* style, SkString* styleName) override {
210         SkASSERT(index >= 0 && index < static_cast<int>(fStyles.size()));
211         if (style) *style = fStyles[index];
212 
213         // We don't have style names. Return an empty name.
214         if (styleName) styleName->reset();
215     }
216 
createTypeface(int index)217     SkTypeface* createTypeface(int index) override {
218         SkASSERT(index >= 0 && index < static_cast<int>(fStyles.size()));
219 
220         if (fTypefaces.empty()) fTypefaces.resize(fStyles.size());
221 
222         if (!fTypefaces[index]) {
223             fTypefaces[index] = fFontManager->FetchTypeface(
224                     fFamilyName.c_str(), fStyles[index], /*bcp47=*/nullptr,
225                     /*bcp47Count=*/0, /*character=*/0,
226                     /*allow_fallback=*/false, /*exact_style_match=*/true);
227         }
228 
229         return SkSafeRef(fTypefaces[index].get());
230     }
231 
matchStyle(const SkFontStyle & pattern)232     SkTypeface* matchStyle(const SkFontStyle& pattern) override { return matchStyleCSS3(pattern); }
233 
234 private:
235     sk_sp<SkFontMgr_Fuchsia> fFontManager;
236     std::string fFamilyName;
237     std::vector<SkFontStyle> fStyles;
238     std::vector<sk_sp<SkTypeface>> fTypefaces;
239 };
240 
SkFontMgr_Fuchsia(fuchsia::fonts::ProviderSyncPtr provider)241 SkFontMgr_Fuchsia::SkFontMgr_Fuchsia(fuchsia::fonts::ProviderSyncPtr provider)
242         : fFontProvider(std::move(provider)) {}
243 
244 SkFontMgr_Fuchsia::~SkFontMgr_Fuchsia() = default;
245 
onCountFamilies() const246 int SkFontMgr_Fuchsia::onCountFamilies() const {
247     // Family enumeration is not supported.
248     return 0;
249 }
250 
onGetFamilyName(int index,SkString * familyName) const251 void SkFontMgr_Fuchsia::onGetFamilyName(int index, SkString* familyName) const {
252     // Family enumeration is not supported.
253     familyName->reset();
254 }
255 
onCreateStyleSet(int index) const256 SkFontStyleSet* SkFontMgr_Fuchsia::onCreateStyleSet(int index) const {
257     // Family enumeration is not supported.
258     return nullptr;
259 }
260 
onMatchFamily(const char familyName[]) const261 SkFontStyleSet* SkFontMgr_Fuchsia::onMatchFamily(const char familyName[]) const {
262     fuchsia::fonts::FamilyInfoPtr familyInfo;
263     int result = fFontProvider->GetFamilyInfo(familyName, &familyInfo);
264     if (result != ZX_OK || !familyInfo) return nullptr;
265 
266     std::vector<SkFontStyle> styles;
267     for (auto& style : familyInfo->styles) {
268         styles.push_back(SkFontStyle(style.weight, style.width, FuchsiaToSkSlant(style.slant)));
269     }
270 
271     return new SkFontStyleSet_Fuchsia(sk_ref_sp(this), familyInfo->name, std::move(styles));
272 }
273 
onMatchFamilyStyle(const char familyName[],const SkFontStyle & style) const274 SkTypeface* SkFontMgr_Fuchsia::onMatchFamilyStyle(const char familyName[],
275                                                   const SkFontStyle& style) const {
276     sk_sp<SkTypeface> typeface =
277             FetchTypeface(familyName, style, /*bcp47=*/nullptr,
278                           /*bcp47Count=*/0, /*character=*/0,
279                           /*allow_fallback=*/false, /*exact_style_match=*/false);
280     return typeface.release();
281 }
282 
onMatchFamilyStyleCharacter(const char familyName[],const SkFontStyle & style,const char * bcp47[],int bcp47Count,SkUnichar character) const283 SkTypeface* SkFontMgr_Fuchsia::onMatchFamilyStyleCharacter(const char familyName[],
284                                                            const SkFontStyle& style,
285                                                            const char* bcp47[], int bcp47Count,
286                                                            SkUnichar character) const {
287     sk_sp<SkTypeface> typeface =
288             FetchTypeface(familyName, style, bcp47, bcp47Count, character, /*allow_fallback=*/true,
289                           /*exact_style_match=*/false);
290     return typeface.release();
291 }
292 
onMatchFaceStyle(const SkTypeface *,const SkFontStyle &) const293 SkTypeface* SkFontMgr_Fuchsia::onMatchFaceStyle(const SkTypeface*, const SkFontStyle&) const {
294     return nullptr;
295 }
296 
onMakeFromData(sk_sp<SkData>,int ttcIndex) const297 sk_sp<SkTypeface> SkFontMgr_Fuchsia::onMakeFromData(sk_sp<SkData>, int ttcIndex) const {
298     SkASSERT(false);
299     return nullptr;
300 }
301 
onMakeFromStreamIndex(std::unique_ptr<SkStreamAsset> asset,int ttcIndex) const302 sk_sp<SkTypeface> SkFontMgr_Fuchsia::onMakeFromStreamIndex(std::unique_ptr<SkStreamAsset> asset,
303                                                            int ttcIndex) const {
304     return makeFromStream(std::move(asset), SkFontArguments().setCollectionIndex(ttcIndex));
305 }
306 
onMakeFromStreamArgs(std::unique_ptr<SkStreamAsset> asset,const SkFontArguments & args) const307 sk_sp<SkTypeface> SkFontMgr_Fuchsia::onMakeFromStreamArgs(std::unique_ptr<SkStreamAsset> asset,
308                                                           const SkFontArguments& args) const {
309     return CreateTypefaceFromSkStream(std::move(asset), args, kNullTypefaceId);
310 }
311 
onMakeFromFile(const char path[],int ttcIndex) const312 sk_sp<SkTypeface> SkFontMgr_Fuchsia::onMakeFromFile(const char path[], int ttcIndex) const {
313     return makeFromStream(std::make_unique<SkFILEStream>(path), ttcIndex);
314 }
315 
onLegacyMakeTypeface(const char familyName[],SkFontStyle style) const316 sk_sp<SkTypeface> SkFontMgr_Fuchsia::onLegacyMakeTypeface(const char familyName[],
317                                                           SkFontStyle style) const {
318     return sk_sp<SkTypeface>(matchFamilyStyle(familyName, style));
319 }
320 
FetchTypeface(const char familyName[],const SkFontStyle & style,const char * bcp47[],int bcp47Count,SkUnichar character,bool allow_fallback,bool exact_style_match) const321 sk_sp<SkTypeface> SkFontMgr_Fuchsia::FetchTypeface(const char familyName[],
322                                                    const SkFontStyle& style, const char* bcp47[],
323                                                    int bcp47Count, SkUnichar character,
324                                                    bool allow_fallback,
325                                                    bool exact_style_match) const {
326     fuchsia::fonts::Request request;
327     request.weight = style.weight();
328     request.width = style.width();
329     request.slant = SkToFuchsiaSlant(style.slant());
330     request.language.reset(std::vector<std::string>(bcp47, bcp47 + bcp47Count));
331     request.character = character;
332     request.fallback_group = GetFallbackGroupByName(familyName);
333 
334     // If family name is not specified or it is a generic fallback group name (e.g. "serif") then
335     // enable fallback, otherwise pass the family name as is.
336     if (!familyName || *familyName == '\0' ||
337         request.fallback_group != fuchsia::fonts::FallbackGroup::NONE) {
338         request.family = "";
339         allow_fallback = true;
340     } else {
341         request.family = familyName;
342     }
343 
344     request.flags = 0;
345     if (!allow_fallback) request.flags |= fuchsia::fonts::REQUEST_FLAG_NO_FALLBACK;
346     if (exact_style_match) request.flags |= fuchsia::fonts::REQUEST_FLAG_EXACT_MATCH;
347 
348     fuchsia::fonts::ResponsePtr response;
349     int result = fFontProvider->GetFont(std::move(request), &response);
350     if (result != ZX_OK) return nullptr;
351 
352     // The service may return null response if there is no font matching the request.
353     if (!response) return nullptr;
354 
355     return GetOrCreateTypeface(TypefaceId{response->buffer_id, response->font_index},
356                                response->buffer);
357 }
358 
GetOrCreateSkData(int bufferId,const fuchsia::mem::Buffer & buffer) const359 sk_sp<SkData> SkFontMgr_Fuchsia::GetOrCreateSkData(int bufferId,
360                                                    const fuchsia::mem::Buffer& buffer) const {
361     fCacheMutex.assertHeld();
362 
363     auto iter = fBufferCache.find(bufferId);
364     if (iter != fBufferCache.end()) {
365         return sk_ref_sp(iter->second);
366     }
367     auto font_mgr = sk_ref_sp(this);
368     auto data = MakeSkDataFromBuffer(buffer,
369                                      [font_mgr, bufferId]() { font_mgr->OnSkDataDeleted(bufferId); });
370     if (!data) {
371         return nullptr;
372     }
373     fBufferCache[bufferId] = data.get();
374     return data;
375 }
376 
OnSkDataDeleted(int bufferId) const377 void SkFontMgr_Fuchsia::OnSkDataDeleted(int bufferId) const {
378     SK_UNUSED bool wasFound = fBufferCache.erase(bufferId) != 0;
379     SkASSERT(wasFound);
380 }
381 
FindByTypefaceId(SkTypeface * cachedTypeface,void * ctx)382 static bool FindByTypefaceId(SkTypeface* cachedTypeface, void* ctx) {
383     SkTypeface_Fuchsia* cachedFuchsiaTypeface = static_cast<SkTypeface_Fuchsia*>(cachedTypeface);
384     TypefaceId* id = static_cast<TypefaceId*>(ctx);
385 
386     return cachedFuchsiaTypeface->id() == *id;
387 }
388 
GetOrCreateTypeface(TypefaceId id,const fuchsia::mem::Buffer & buffer) const389 sk_sp<SkTypeface> SkFontMgr_Fuchsia::GetOrCreateTypeface(TypefaceId id,
390                                                          const fuchsia::mem::Buffer& buffer) const {
391     SkAutoMutexAcquire mutexLock(fCacheMutex);
392 
393     sk_sp<SkTypeface> cached = fTypefaceCache.findByProcAndRef(FindByTypefaceId, &id);
394     if (cached) return cached;
395 
396     sk_sp<SkData> data = GetOrCreateSkData(id.bufferId, buffer);
397     if (!data) return nullptr;
398 
399     auto result = CreateTypefaceFromSkData(std::move(data), id);
400     fTypefaceCache.add(result);
401     return result;
402 }
403 
SkFontMgr_New_Fuchsia(fuchsia::fonts::ProviderSyncPtr provider)404 SK_API sk_sp<SkFontMgr> SkFontMgr_New_Fuchsia(fuchsia::fonts::ProviderSyncPtr provider) {
405     return sk_make_sp<SkFontMgr_Fuchsia>(std::move(provider));
406 }
407