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