1 // Copyright (c) 2023 Huawei Device Co., Ltd. All rights reserved
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "SkFontMgr_ohos.h"
6 
7 #include "include/core/SkData.h"
8 #include "SkTypeface_ohos.h"
9 
10 using namespace ErrorCode;
11 
12 /*! Constructor
13  * \param path the full path of system font configuration document
14  */
SkFontMgr_OHOS(const char* path)15 SkFontMgr_OHOS::SkFontMgr_OHOS(const char* path)
16 {
17     fontConfig = std::make_shared<FontConfig_OHOS>(fontScanner, path);
18     familyCount = fontConfig->getFamilyCount();
19 }
20 
21 /*! To get the count of families
22  * \return The count of families in the system
23  */
onCountFamilies() const24 int SkFontMgr_OHOS::onCountFamilies() const
25 {
26     return familyCount;
27 }
28 
29 /*! To get the family name for a font style set
30  * \param index the index of a font style set
31  * \param[out] familyName the family name returned to the caller
32  * \n          The family name will be reset to "", if index is out of range
33  */
onGetFamilyName(int index, SkString* familyName) const34 void SkFontMgr_OHOS::onGetFamilyName(int index, SkString* familyName) const
35 {
36     if (fontConfig == nullptr || familyName == nullptr) {
37         return;
38     }
39     fontConfig->getFamilyName(index, familyName);
40 }
41 
42 /*! To create an object of SkFontStyleSet
43  * \param index the index of a font style set
44  * \return The pointer of SkFontStyleSet
45  * \n      Return null, if index is out of range
46  * \note   The caller must call unref() on the returned object if it's not null
47  */
onCreateStyleSet(int index) const48 SkFontStyleSet* SkFontMgr_OHOS::onCreateStyleSet(int index) const
49 {
50     if (fontConfig == nullptr) {
51         return nullptr;
52     }
53     if (index < 0 || index >= this->countFamilies()) {
54         return nullptr;
55     }
56     return new SkFontStyleSet_OHOS(fontConfig, index);
57 }
58 
59 /*! To get a matched object of SkFontStyleSet
60  * \param familyName the family name of a font style set
61  * \return The pointer of SkFontStyleSet
62  * \n      Return the default font style set, if family name is null
63  * \n      Return null, if family name is not found
64  * \note   The caller must call unref() on the returned object if it's not null
65  */
onMatchFamily(const char familyName[]) const66 SkFontStyleSet* SkFontMgr_OHOS::onMatchFamily(const char familyName[]) const
67 {
68     if (fontConfig == nullptr) {
69         return nullptr;
70     }
71     // return default system font when familyName is null
72     if (familyName == nullptr) {
73         return new SkFontStyleSet_OHOS(fontConfig, 0);
74     }
75 
76     bool isFallback = false;
77     int index = fontConfig->getStyleIndex(familyName, isFallback);
78     if (index == -1) {
79         return nullptr;
80     }
81     return new SkFontStyleSet_OHOS(fontConfig, index, isFallback);
82 }
83 
84 /*! To get a matched typeface
85  * \param familyName the family name of a font style set
86  * \param style the font style to be matched
87  * \return An object of typeface which is closest matching to 'style'
88  * \n      Return the typeface in the default font style set, if family name is null
89  * \n      Return null, if family name is not found
90  * \note   The caller must call unref() on the returned object if it's not null
91  */
onMatchFamilyStyle(const char familyName[], const SkFontStyle& style) const92 SkTypeface* SkFontMgr_OHOS::onMatchFamilyStyle(const char familyName[], const SkFontStyle& style) const
93 {
94     if (fontConfig == nullptr) {
95         return nullptr;
96     }
97     bool isFallback = false;
98     int styleIndex = 0;
99     if (familyName) {
100         styleIndex = fontConfig->getStyleIndex(familyName, isFallback);
101     }
102     return SkSafeRef(fontConfig->getTypeface(styleIndex, style, isFallback));
103 }
104 
105 /*! To get a matched typeface
106  * \n Use the system fallback to find a typeface for the given character.
107  * \param familyName the family name which the typeface is fallback For
108  * \param style the font style to be matched
109  * \param bcp47 an array of languages which indicate the language of 'character'
110  * \param bcp47Count the array size of bcp47
111  * \param character a UTF8 value to be matched
112  * \return An object of typeface which is for the given character
113  * \return Return the typeface in the default fallback set, if familyName is null
114  * \return Return null, if the typeface is not found for the given character
115  * \note The caller must call unref() on the returned object if it's not null
116  */
onMatchFamilyStyleCharacter(const char familyName[], const SkFontStyle& style, const char* bcp47[], int bcp47Count, SkUnichar character) const117 SkTypeface* SkFontMgr_OHOS::onMatchFamilyStyleCharacter(const char familyName[], const SkFontStyle& style,
118     const char* bcp47[], int bcp47Count, SkUnichar character) const
119 {
120     if (fontConfig == nullptr) {
121         return nullptr;
122     }
123     const FallbackForMap& fallbackForMap = fontConfig->getFallbackForMap();
124     const FallbackSet& fallbackSet = fontConfig->getFallbackSet();
125     SkString defaultFamily("");
126     SkString key = defaultFamily;
127     FallbackSetPos* item = nullptr;
128     if (familyName == nullptr) {
129         item = fallbackForMap.find(defaultFamily);
130     } else {
131         item = fallbackForMap.find(SkString(familyName));
132         if (item) {
133             key = SkString(familyName);
134         } else {
135             item = fallbackForMap.find(defaultFamily);
136         }
137     }
138     if (item == nullptr) {
139         LOGE("%s : '%s' must be a fallback key in the config file\n",
140             FontConfig_OHOS::errToString(ERROR_FAMILY_NOT_FOUND), defaultFamily.c_str());
141         return nullptr;
142     }
143     while (true) {
144         if (bcp47Count > 0) {
145             SkTypeface* retTp = findTypeface(*item, style, bcp47, bcp47Count, character);
146             if (retTp) {
147                 return retTp;
148             }
149             if (key == defaultFamily) {
150                 bcp47Count = 0;
151                 continue;
152             }
153             item = fallbackForMap.find(defaultFamily);
154             key = defaultFamily;
155         } else {
156             for (unsigned int i = item->index; i < item->index + item->count && i < fallbackSet.size(); i++) {
157                 const TypefaceSet& tpSet = *(fallbackSet[i]->typefaceSet.get());
158                 if (tpSet.size() > 0 && tpSet[0]->unicharToGlyph(character) != 0) {
159                     sk_sp<SkTypeface> typeface = FontConfig_OHOS::matchFontStyle(tpSet, style);
160                     return SkSafeRef(typeface.get());
161                 }
162             }
163             if (key == defaultFamily) {
164                 break;
165             }
166             item = fallbackForMap.find(defaultFamily);
167             key = defaultFamily;
168         }
169     }
170     return nullptr;
171 }
172 
173 /*! To find the matched typeface for the given parameters
174  * \n Use the system fallback to find a typeface for the given character.
175  * \param fallbackItem the fallback items in which to find the typeface
176  * \param style the font style to be matched
177  * \param bcp47 an array of languages which indicate the language of 'character'
178  * \param bcp47Count the array size of bcp47
179  * \param character a UTF8 value to be matched
180  * \return An object of typeface which is for the given character
181  * \return Return null, if the typeface is not found for the given character
182  */
findTypeface(const FallbackSetPos& fallbackItem, const SkFontStyle& style, const char* bcp47[], int bcp47Count, SkUnichar character) const183 SkTypeface* SkFontMgr_OHOS::findTypeface(const FallbackSetPos& fallbackItem, const SkFontStyle& style,
184     const char* bcp47[], int bcp47Count, SkUnichar character) const
185 {
186     if (bcp47Count == 0) {
187         return nullptr;
188     }
189 
190     const FallbackSet& fallbackSet = fontConfig->getFallbackSet();
191     // example bcp47 code : 'zh-Hans' : ('zh' : iso639 code, 'Hans' : iso15924 code)
192     // iso639 code will be taken from bcp47 code, so that we can try to match
193     // bcp47 or only iso639. Therefore totalCount need to be 'bcp47Count * 2'
194     int totalCount = bcp47Count * 2;
195     int tps[totalCount];
196     for (int i = 0; i < totalCount; i++) {
197         tps[i] = -1;
198     }
199     // find the families matching the bcp47 list
200     for (unsigned int i = fallbackItem.index; i < fallbackItem.index + fallbackItem.count
201         && i < fallbackSet.size(); i++) {
202         int ret = compareLangs(fallbackSet[i]->langs, bcp47, bcp47Count, tps);
203         if (ret == -1) {
204             continue;
205         }
206         tps[ret] = i;
207     }
208     // match typeface in families
209     for (int i = bcp47Count - 1; i >= 0; i--) {
210         if (tps[i] == -1) {
211             continue;
212         }
213         const TypefaceSet& tpSet = *(fallbackSet[tps[i]]->typefaceSet.get());
214         if (tpSet.size() > 0 && tpSet[0]->unicharToGlyph(character) != 0) {
215             sk_sp<SkTypeface> typeface = FontConfig_OHOS::matchFontStyle(tpSet, style);
216             return SkSafeRef(typeface.get());
217         }
218     }
219     for (int i = totalCount - 1; i >= bcp47Count; i--) {
220         if (tps[i] == -1) {
221             continue;
222         }
223         const TypefaceSet& tpSet = *(fallbackSet[tps[i]]->typefaceSet.get());
224         if (tpSet.size() > 0 && tpSet[0]->unicharToGlyph(character) != 0) {
225             sk_sp<SkTypeface> typeface = FontConfig_OHOS::matchFontStyle(tpSet, style);
226             return SkSafeRef(typeface.get());
227         }
228     }
229     return nullptr;
230 }
231 
232 /*! To compare the languages of an typeface with a bcp47 list
233  * \param langs the supported languages by an typeface
234  * \param bcp47 the array of bcp47 language to be matching
235  * \param bcp47Count the array size of bcp47
236  * \param tps an array of the index of typeface which is matching one value of bcp47
237  * \return The index of language in bcp47, if matching happens
238  * \n      Return -1, if no language matching happens
239  */
compareLangs(const SkString& langs, const char* bcp47[], int bcp47Count, const int tps[]) const240 int SkFontMgr_OHOS::compareLangs(const SkString& langs, const char* bcp47[],
241     int bcp47Count, const int tps[]) const
242 {
243     /*
244      * zh-Hans : ('zh' : iso639 code, 'Hans' : iso15924 code)
245      */
246     if (bcp47 == nullptr || bcp47Count == 0) {
247         return -1;
248     }
249     for (int i = bcp47Count - 1; i >= 0; i--) {
250         if (tps[i] != -1) {
251             continue;
252         }
253         if (langs.find(bcp47[i]) != -1) {
254             return i;
255         } else {
256             const char* iso15924 = strrchr(bcp47[i], '-');
257             if (iso15924 == nullptr) {
258                 continue;
259             }
260             iso15924++;
261             int len = iso15924 - 1 - bcp47[i];
262             SkString country(bcp47[i], len);
263             if (langs.find(iso15924) != -1 ||
264                 (strncmp(bcp47[i], "und", strlen("und")) && langs.find(country.c_str()) != -1)) {
265                 return i + bcp47Count;
266             }
267         }
268     }
269     return -1;
270 }
271 
272 /*! To get a matched typeface
273  * \param typeface the given typeface with which the returned object should be in the same style set
274  * \param style the font style to be matching
275  * \return The object of typeface which is closest matching to the given 'style'
276  * \n      Return null, if the family name of the given typeface is not found in the system
277  * \note The caller must call unref() on the returned object if it's not null
278  */
onMatchFaceStyle(const SkTypeface* typeface, const SkFontStyle& style) const279 SkTypeface* SkFontMgr_OHOS::onMatchFaceStyle(const SkTypeface* typeface, const SkFontStyle& style) const
280 {
281     if (typeface == nullptr) {
282         return nullptr;
283     }
284     SkString familyName;
285     typeface->getFamilyName(&familyName);
286     return this->onMatchFamilyStyle(familyName.c_str(), style);
287 }
288 
289 /*! To create a typeface from the specified data and TTC index
290  * \param data the data to be parsed
291  * \param index the index of typeface. 0 for none
292  * \return The object of typeface, if successful
293  * \n      Return null if the data is not recognized.
294  * \note The caller must call unref() on the returned object if it's not null
295  */
onMakeFromData(sk_sp<SkData> data, int ttcIndex) const296 sk_sp<SkTypeface> SkFontMgr_OHOS::onMakeFromData(sk_sp<SkData> data, int ttcIndex) const
297 {
298     if (data == nullptr) {
299         return nullptr;
300     }
301     std::unique_ptr<SkMemoryStream> memoryStream = std::make_unique<SkMemoryStream>(data);
302     SkFontArguments args;
303     args.setCollectionIndex(ttcIndex);
304     return this->makeTypeface(std::move(memoryStream), args, nullptr);
305 }
306 
307 /*! To create a typeface from the specified stream and TTC index
308  * \param data the stream to be parsed
309  * \param index the index of typeface. 0 for none
310  * \return The object of typeface, if successful
311  * \n      Return null if the stream is not recognized.
312  * \note The caller must call unref() on the returned object if it's not null
313  */
onMakeFromStreamIndex(std::unique_ptr<SkStreamAsset> stream, int ttcIndex) const314 sk_sp<SkTypeface> SkFontMgr_OHOS::onMakeFromStreamIndex(std::unique_ptr<SkStreamAsset> stream,
315     int ttcIndex) const
316 {
317     if (stream == nullptr) {
318         return nullptr;
319     }
320     SkFontArguments args;
321     args.setCollectionIndex(ttcIndex);
322     return this->makeTypeface(std::move(stream), args, nullptr);
323 }
324 
325 /*! To create a typeface from the specified stream and font arguments
326  * \param data the stream to be parsed
327  * \param args the arguments of font
328  * \return The object of typeface, if successful
329  * \n      Return null if the stream is not recognized.
330  * \note The caller must call unref() on the returned object if it's not null
331  */
onMakeFromStreamArgs(std::unique_ptr<SkStreamAsset> stream, const SkFontArguments& args) const332 sk_sp<SkTypeface> SkFontMgr_OHOS::onMakeFromStreamArgs(std::unique_ptr<SkStreamAsset> stream,
333     const SkFontArguments& args) const
334 {
335     if (stream == nullptr) {
336         return nullptr;
337     }
338 
339     return this->makeTypeface(std::move(stream), args, nullptr);
340 }
341 
342 /*! To create a typeface from the specified font file and TTC index
343  * \param path the full path of the given font file
344  * \param ttcIndex the index of typeface in a ttc font file. 0 means none.
345  * \return The object of typeface, if successful
346  * \n      Return null if the font file is not found or the content of file is not recognized.
347  * \note The caller must call unref() on the returned object if it's not null
348  */
onMakeFromFile(const char path[], int ttcIndex) const349 sk_sp<SkTypeface> SkFontMgr_OHOS::onMakeFromFile(const char path[], int ttcIndex) const
350 {
351     if (fontConfig == nullptr) {
352         return nullptr;
353     }
354 
355     std::unique_ptr<SkStreamAsset> stream = SkStreamAsset::MakeFromFile(path);
356     if (stream == nullptr) {
357         LOGE("%s : %s\n", FontConfig_OHOS::errToString(ERROR_FONT_NOT_EXIST), path);
358         return nullptr;
359     }
360     SkFontArguments args;
361     args.setCollectionIndex(ttcIndex);
362     return this->makeTypeface(std::move(stream), args, path);
363 }
364 
365 /*! To get a typeface matching the specified family and style
366  * \param familyName the specified name to be matching
367  * \param style the specified style to be matching
368  * \return The object of typeface which is the closest matching 'style' when the familyName is found
369  * \return Return a typeface from the default family, if familyName is not found
370  * \return Return null, if there is no any typeface in the system
371  * \note The caller must caller unref() on the returned object is it's not null
372  */
onLegacyMakeTypeface(const char familyName[], SkFontStyle style) const373 sk_sp<SkTypeface> SkFontMgr_OHOS::onLegacyMakeTypeface(const char familyName[], SkFontStyle style) const
374 {
375     SkTypeface* typeface = this->onMatchFamilyStyle(familyName, style);
376     // if familyName is not found, then try the default family
377     if (typeface == nullptr && familyName != nullptr) {
378         typeface = this->onMatchFamilyStyle(nullptr, style);
379     }
380 
381     if (typeface) {
382         return sk_sp<SkTypeface>(typeface);
383     }
384     LOGE("%s\n", FontConfig_OHOS::errToString(ERROR_NO_AVAILABLE_FAMILY));
385     return nullptr;
386 }
387 
388 #ifdef OHOS_SUPPORT
onGetSystemFonts() const389 std::vector<sk_sp<SkTypeface>> SkFontMgr_OHOS::onGetSystemFonts() const
390 {
391     if (fontConfig == nullptr) {
392         return;
393     }
394     std::vector<sk_sp<SkTypeface>> skTypefaces;
395     int familyCount = fontConfig->getFamilyCount();
396     for (int i = 0; i < familyCount; ++i) {
397         int typefaceCount = fontConfig->getTypefaceCount(i);
398         for (int j = 0; j < typefaceCount; ++j) {
399             sk_sp<SkTypeface_OHOS> typeface = fontConfig->getTypefaceSP(i, j);
400             if (typeface == nullptr) {
401                 continue;
402             }
403             skTypefaces.emplace_back(typeface);
404         }
405     }
406 
407     for (auto& item : fontConfig->getFallbackSet()) {
408         if (item->typefaceSet != nullptr) {
409             for (auto& iter : *(item->typefaceSet)) {
410                 skTypefaces.emplace_back(iter);
411             }
412         }
413     }
414     return std::move(skTypefaces);
415 }
416 #endif
417 
418 /*! To make a typeface from the specified stream and font arguments
419  * \param stream the specified stream to be parsed to get font information
420  * \param args the arguments of index or axis values
421  * \param path the fullname of font file
422  * \return The object of typeface if successful
423  * \n      Return null, if the stream is not recognized
424  */
makeTypeface(std::unique_ptr<SkStreamAsset> stream, const SkFontArguments& args, const char path[]) const425 sk_sp<SkTypeface> SkFontMgr_OHOS::makeTypeface(std::unique_ptr<SkStreamAsset> stream,
426     const SkFontArguments& args, const char path[]) const
427 {
428     FontInfo fontInfo;
429     int ttcIndex = args.getCollectionIndex();
430     int axisCount = args.getVariationDesignPosition().coordinateCount;
431 
432     if (path) {
433         fontInfo.fname.set(path);
434     }
435     if (axisCount == 0) {
436         if (!fontScanner.scanFont(stream.get(), ttcIndex, &fontInfo.familyName, &fontInfo.style,
437             &fontInfo.isFixedWidth, nullptr)) {
438             LOGE("%s\n", FontConfig_OHOS::errToString(ERROR_FONT_INVALID_STREAM));
439             return nullptr;
440         }
441     } else {
442         AxisDefinitions axisDef;
443         if (!fontScanner.scanFont(stream.get(), ttcIndex, &fontInfo.familyName, &fontInfo.style,
444             &fontInfo.isFixedWidth, &axisDef)) {
445             LOGE("%s\n", FontConfig_OHOS::errToString(ERROR_FONT_INVALID_STREAM));
446             return nullptr;
447         }
448         if (axisDef.count() > 0) {
449             SkFixed axis[axisDef.count()];
450             fontScanner.computeAxisValues(axisDef, args.getVariationDesignPosition(),
451                 axis, fontInfo.familyName);
452             fontInfo.setAxisSet(axisCount, axis, axisDef.data());
453             fontInfo.style = fontInfo.computeFontStyle();
454         }
455     }
456 
457     fontInfo.stream = std::move(stream);
458     fontInfo.index = ttcIndex;
459     return sk_make_sp<SkTypeface_OHOS>(fontInfo);
460 }
461 
462 /*! Get the fullname of font
463  * \param fontFd      The file descriptor for the font file
464  * \param fullnameVec Read the font fullname list
465  * \return Returns Whether the fullnameVec was successfully obtained, 0 means success, see FontCheckCode for details
466  */
GetFontFullName(int fontFd, std::vector<SkByteArray> &fullnameVec)467 int SkFontMgr_OHOS::GetFontFullName(int fontFd, std::vector<SkByteArray> &fullnameVec)
468 {
469     std::unique_ptr<SkMemoryStream> stream = std::make_unique<SkMemoryStream>(SkData::MakeFromFD(fontFd));
470     int errorCode = SUCCESSED;
471     int numFaces = 0;
472     if (!fontScanner.recognizedFont(stream.get(), &numFaces)) {
473         SkDebugf("Failed to recognizedFont");
474         return ERROR_TYPE_OTHER;
475     }
476     for (int faceIndex = 0; faceIndex < numFaces; ++faceIndex) {
477         bool isFixedPitch = false;
478         SkString realname;
479         SkFontStyle style = SkFontStyle(); // avoid uninitialized warning
480         if (!fontScanner.scanFont(stream.get(), faceIndex, &realname, &style, &isFixedPitch, nullptr)) {
481             SkDebugf("Failed to scanFont, faceIndex:%d", faceIndex);
482             errorCode = ERROR_TYPE_OTHER;
483             break;
484         }
485         SkByteArray skFullName = {nullptr, 0};
486         if (!fontScanner.GetTypefaceFullname(stream.get(), faceIndex, skFullName)) {
487             SkDebugf("Failed to get fullname, faceIndex:%d", faceIndex);
488             errorCode = ERROR_TYPE_OTHER;
489             break;
490         } else {
491             fullnameVec.push_back(std::move(skFullName));
492         }
493     }
494     SkDebugf("GetFontFullName end, errorCode:%d, numFaces:%d, size:%zu", errorCode, numFaces, fullnameVec.size());
495     return errorCode;
496 }
497 
498 /*! To create SkFontMgr object for Harmony platform
499  * \param fname the full name of system font configuration documents
500  * \return The object of SkFontMgr_OHOS
501  */
SkFontMgr_New_OHOS(const char* fname)502 sk_sp<SkFontMgr> SkFontMgr_New_OHOS(const char* fname)
503 {
504     return sk_make_sp<SkFontMgr_OHOS>(fname);
505 }
506