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