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 <string>
8
9 #include "SkTypeface_ohos.h"
10 #include "include/core/SkData.h"
11
12 using namespace ErrorCode;
13
14 /*! Constructor
15 * \param path the full path of system font configuration document
16 */
SkFontMgr_OHOS(const char * path)17 SkFontMgr_OHOS::SkFontMgr_OHOS(const char* path)
18 {
19 fFontConfig = std::make_shared<FontConfig_OHOS>(fFontScanner, path);
20 fFamilyCount = fFontConfig->getFamilyCount();
21 }
22
23 /*! To get the count of families
24 * \return The count of families in the system
25 */
onCountFamilies() const26 int SkFontMgr_OHOS::onCountFamilies() const
27 {
28 return fFamilyCount;
29 }
30
31 /*! To get the family name for a font style set
32 * \param index the index of a font style set
33 * \param[out] familyName the family name returned to the caller
34 * \n The family name will be reset to "", if index is out of range
35 */
onGetFamilyName(int index,SkString * familyName) const36 void SkFontMgr_OHOS::onGetFamilyName(int index, SkString* familyName) const
37 {
38 if (fFontConfig == nullptr || familyName == nullptr) {
39 return;
40 }
41 fFontConfig->getFamilyName(static_cast<size_t>(index), *familyName);
42 }
43
44 /*! To create an object of SkFontStyleSet
45 * \param index the index of a font style set
46 * \return The pointer of SkFontStyleSet
47 * \n Return null, if index is out of range
48 * \note The caller must call unref() on the returned object if it's not null
49 */
onCreateStyleSet(int index) const50 SkFontStyleSet* SkFontMgr_OHOS::onCreateStyleSet(int index) const
51 {
52 if (fFontConfig == nullptr) {
53 return nullptr;
54 }
55 if (index < 0 || index >= this->countFamilies()) {
56 return nullptr;
57 }
58 return new SkFontStyleSet_OHOS(fFontConfig, index);
59 }
60
61 /*! To get a matched object of SkFontStyleSet
62 * \param familyName the family name of a font style set
63 * \return The pointer of SkFontStyleSet
64 * \n Return the default font style set, if family name is null
65 * \n Return null, if family name is not found
66 * \note The caller must call unref() on the returned object if it's not null
67 */
onMatchFamily(const char familyName[]) const68 SkFontStyleSet* SkFontMgr_OHOS::onMatchFamily(const char familyName[]) const
69 {
70 if (fFontConfig == nullptr) {
71 return nullptr;
72 }
73 // return default system font when familyName is null
74 if (familyName == nullptr) {
75 return new SkFontStyleSet_OHOS(fFontConfig, 0);
76 }
77
78 bool isFallback = false;
79 size_t index = 0;
80 if (!fFontConfig->getStyleIndex(familyName, isFallback, index)) {
81 return nullptr;
82 }
83 return new SkFontStyleSet_OHOS(fFontConfig, index, isFallback);
84 }
85
86 /*! To get a matched typeface
87 * \param familyName the family name of a font style set
88 * \param style the font style to be matched
89 * \return An object of typeface which is closest matching to 'style'
90 * \n Return the typeface in the default font style set, if family name is null
91 * \n Return null, if family name is not found
92 * \note The caller must call unref() on the returned object if it's not null
93 */
onMatchFamilyStyle(const char familyName[],const SkFontStyle & style) const94 SkTypeface* SkFontMgr_OHOS::onMatchFamilyStyle(const char familyName[], const SkFontStyle& style) const
95 {
96 if (fFontConfig == nullptr) {
97 return nullptr;
98 }
99 bool isFallback = false;
100 size_t styleIndex = 0;
101 if (!fFontConfig->getStyleIndex(familyName, isFallback, styleIndex)) {
102 return nullptr;
103 }
104 return SkSafeRef(fFontConfig->getTypeface(styleIndex, style, isFallback));
105 }
106
107 struct SpecialUnicodeFamilyName {
108 SkUnichar unicode;
109 std::string familyName;
110 };
111
findSpecialTypeface(SkUnichar character,const SkFontStyle & style) const112 SkTypeface* SkFontMgr_OHOS::findSpecialTypeface(SkUnichar character, const SkFontStyle& style) const
113 {
114 // The key values in this list are Unicode that support the identification characters
115 // of several high-frequency languages in the fallback list corresponding to Chinese, Uyghur, and Tibetan
116 static std::vector<SpecialUnicodeFamilyName> specialLists = {
117 {0x0626, "HarmonyOS Sans Naskh Arabic UI"},
118 {0x0F56, "Noto Serif Tibetan"}
119 };
120
121 std::string name;
122
123 // base chinese unicode range is 0x4E00-0x9FA5
124 if (character >= 0x4E00 && character <= 0x9FA5) {
125 name.assign("HarmonyOS Sans SC");
126 }
127
128 for (int i = 0; i < specialLists.size() && name.empty(); i++) {
129 if (character == specialLists[i].unicode) {
130 name.assign(specialLists[i].familyName);
131 break;
132 }
133 }
134
135 if (name.empty()) {
136 return nullptr;
137 }
138
139 SkString fname(name.c_str());
140 sk_sp<SkTypeface_OHOS> typeface = fFontConfig->getFallbackTypeface(fname, style);
141 return SkSafeRef(typeface.get());
142 }
143
144 /*! To get a matched typeface
145 * \n Use the system fallback to find a typeface for the given character.
146 * \param familyName the family name which the typeface is fallback For
147 * \param style the font style to be matched
148 * \param bcp47 an array of languages which indicate the language of 'character'
149 * \param bcp47Count the array size of bcp47
150 * \param character a UTF8 value to be matched
151 * \return An object of typeface which is for the given character
152 * \return Return the typeface in the default fallback set, if familyName is null
153 * \return Return null, if the typeface is not found for the given character
154 * \note The caller must call unref() on the returned object if it's not null
155 */
onMatchFamilyStyleCharacter(const char familyName[],const SkFontStyle & style,const char * bcp47[],int bcp47Count,SkUnichar character) const156 SkTypeface* SkFontMgr_OHOS::onMatchFamilyStyleCharacter(const char familyName[], const SkFontStyle& style,
157 const char* bcp47[], int bcp47Count, SkUnichar character) const
158 {
159 if (fFontConfig == nullptr) {
160 return nullptr;
161 }
162
163 auto res = findTypeface(style, bcp47, bcp47Count, character);
164 if (res != nullptr) {
165 return res;
166 }
167 res = findSpecialTypeface(character, style);
168 if (res != nullptr) {
169 return res;
170 }
171 return fFontConfig->matchFallback(character, style);
172 }
173
174 /*! To find the matched typeface for the given parameters
175 * \n Use the system fallback to find a typeface for the given character.
176 * \param fallbackItem the fallback items in which to find the typeface
177 * \param style the font style to be matched
178 * \param bcp47 an array of languages which indicate the language of 'character'
179 * \param bcp47Count the array size of bcp47
180 * \param character a UTF8 value to be matched
181 * \return An object of typeface which is for the given character
182 * \return Return null, if the typeface is not found for the given character
183 */
findTypeface(const SkFontStyle & style,const char * bcp47[],int bcp47Count,SkUnichar character) const184 SkTypeface* SkFontMgr_OHOS::findTypeface(const SkFontStyle& style,
185 const char* bcp47[], int bcp47Count, SkUnichar character) const
186 {
187 if (bcp47Count == 0 || fFontConfig == nullptr) {
188 return nullptr;
189 }
190
191 std::vector<std::function<int(const std::string& langs)>> funcs = {
192 [&bcp47, &bcp47Count](const std::string& langs) -> int {
193 for (int i = 0; i < bcp47Count; i++) {
194 if (langs == bcp47[i]) {
195 return i;
196 }
197 }
198 return -1;
199 },
200 [&bcp47, &bcp47Count](const std::string& langs) -> int {
201 return SkFontMgr_OHOS::compareLangs(langs, bcp47, bcp47Count);
202 }
203 };
204
205 for (auto& func : funcs) {
206 auto set = fFontConfig->matchFallbackByBCP47(func);
207 for (auto& index : set) {
208 auto res = fFontConfig->matchFallback(index, character, style);
209 if (res != nullptr) {
210 return res;
211 }
212 }
213 }
214
215 return nullptr;
216 }
217
218 /*! To compare the languages of an typeface with a bcp47 list
219 * \param langs the supported languages by an typeface
220 * \param bcp47 the array of bcp47 language to be matching
221 * \param bcp47Count the array size of bcp47
222 * \return The index of language in bcp47, if matching happens
223 * \n Return -1, if no language matching happens
224 */
compareLangs(const std::string & langs,const char * bcp47[],int bcp47Count)225 int SkFontMgr_OHOS::compareLangs(const std::string& langs, const char* bcp47[], int bcp47Count)
226 {
227 /*
228 * zh-Hans : ('zh' : iso639 code, 'Hans' : iso15924 code)
229 */
230 if (bcp47 == nullptr || bcp47Count == 0) {
231 return -1;
232 }
233 for (int i = bcp47Count - 1; i >= 0; i--) {
234 if (langs.find(bcp47[i]) != -1) {
235 return i;
236 } else {
237 const char* iso15924 = strrchr(bcp47[i], '-');
238 if (iso15924 == nullptr) {
239 continue;
240 }
241 iso15924++;
242 int len = iso15924 - 1 - bcp47[i];
243 SkString country(bcp47[i], len);
244 if (langs.find(iso15924) != std::string::npos ||
245 (strncmp(bcp47[i], "und", strlen("und")) != 0 && langs.find(country.c_str()) != -1)) {
246 return i + bcp47Count;
247 }
248 }
249 }
250 return -1;
251 }
252
253 /*! To get a matched typeface
254 * \param typeface the given typeface with which the returned object should be in the same style set
255 * \param style the font style to be matching
256 * \return The object of typeface which is closest matching to the given 'style'
257 * \n Return null, if the family name of the given typeface is not found in the system
258 * \note The caller must call unref() on the returned object if it's not null
259 */
onMatchFaceStyle(const SkTypeface * typeface,const SkFontStyle & style) const260 SkTypeface* SkFontMgr_OHOS::onMatchFaceStyle(const SkTypeface* typeface, const SkFontStyle& style) const
261 {
262 if (typeface == nullptr) {
263 return nullptr;
264 }
265 SkString familyName;
266 typeface->getFamilyName(&familyName);
267 return this->onMatchFamilyStyle(familyName.c_str(), style);
268 }
269
270 /*! To create a typeface from the specified data and TTC index
271 * \param data the data to be parsed
272 * \param index the index of typeface. 0 for none
273 * \return The object of typeface, if successful
274 * \n Return null if the data is not recognized.
275 * \note The caller must call unref() on the returned object if it's not null
276 */
onMakeFromData(sk_sp<SkData> data,int ttcIndex) const277 sk_sp<SkTypeface> SkFontMgr_OHOS::onMakeFromData(sk_sp<SkData> data, int ttcIndex) const
278 {
279 if (data == nullptr) {
280 return nullptr;
281 }
282 std::unique_ptr<SkMemoryStream> memoryStream = std::make_unique<SkMemoryStream>(data);
283 SkFontArguments args;
284 args.setCollectionIndex(ttcIndex);
285 return this->makeTypeface(std::move(memoryStream), args, nullptr);
286 }
287
288 /*! To create a typeface from the specified stream and TTC index
289 * \param data the stream 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 stream is not recognized.
293 * \note The caller must call unref() on the returned object if it's not null
294 */
onMakeFromStreamIndex(std::unique_ptr<SkStreamAsset> stream,int ttcIndex) const295 sk_sp<SkTypeface> SkFontMgr_OHOS::onMakeFromStreamIndex(std::unique_ptr<SkStreamAsset> stream,
296 int ttcIndex) const
297 {
298 if (stream == nullptr) {
299 return nullptr;
300 }
301 SkFontArguments args;
302 args.setCollectionIndex(ttcIndex);
303 return this->makeTypeface(std::move(stream), args, nullptr);
304 }
305
306 /*! To create a typeface from the specified stream and font arguments
307 * \param data the stream to be parsed
308 * \param args the arguments of font
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 */
onMakeFromStreamArgs(std::unique_ptr<SkStreamAsset> stream,const SkFontArguments & args) const313 sk_sp<SkTypeface> SkFontMgr_OHOS::onMakeFromStreamArgs(std::unique_ptr<SkStreamAsset> stream,
314 const SkFontArguments& args) const
315 {
316 if (stream == nullptr) {
317 return nullptr;
318 }
319
320 return this->makeTypeface(std::move(stream), args, nullptr);
321 }
322
323 /*! To create a typeface from the specified font file and TTC index
324 * \param path the full path of the given font file
325 * \param ttcIndex the index of typeface in a ttc font file. 0 means none.
326 * \return The object of typeface, if successful
327 * \n Return null if the font file is not found or the content of file is not recognized.
328 * \note The caller must call unref() on the returned object if it's not null
329 */
onMakeFromFile(const char path[],int ttcIndex) const330 sk_sp<SkTypeface> SkFontMgr_OHOS::onMakeFromFile(const char path[], int ttcIndex) const
331 {
332 if (fFontConfig == nullptr) {
333 return nullptr;
334 }
335
336 std::unique_ptr<SkStreamAsset> stream = SkStreamAsset::MakeFromFile(path);
337 if (stream == nullptr) {
338 return nullptr;
339 }
340 SkFontArguments args;
341 args.setCollectionIndex(ttcIndex);
342 return this->makeTypeface(std::move(stream), args, path);
343 }
344
345 /*! To get a typeface matching the specified family and style
346 * \param familyName the specified name to be matching
347 * \param style the specified style to be matching
348 * \return The object of typeface which is the closest matching 'style' when the familyName is found
349 * \return Return a typeface from the default family, if familyName is not found
350 * \return Return null, if there is no any typeface in the system
351 * \note The caller must caller unref() on the returned object is it's not null
352 */
onLegacyMakeTypeface(const char familyName[],SkFontStyle style) const353 sk_sp<SkTypeface> SkFontMgr_OHOS::onLegacyMakeTypeface(const char familyName[], SkFontStyle style) const
354 {
355 SkTypeface* typeface = this->onMatchFamilyStyle(familyName, style);
356 // if familyName is not found, then try the default family
357 if (typeface == nullptr && familyName != nullptr) {
358 typeface = this->onMatchFamilyStyle(nullptr, style);
359 }
360
361 if (typeface) {
362 return sk_sp<SkTypeface>(typeface);
363 }
364 return nullptr;
365 }
366
367 #ifdef OHOS_SUPPORT
onGetSystemFonts() const368 std::vector<sk_sp<SkTypeface>> SkFontMgr_OHOS::onGetSystemFonts() const
369 {
370 if (fFontConfig == nullptr) {
371 return {};
372 }
373 std::vector<sk_sp<SkTypeface>> skTypefaces;
374 fFontConfig->forAll([&skTypefaces](const auto& f) {
375 for (auto& iter : f.typefaces) {
376 skTypefaces.emplace_back(iter);
377 }
378 });
379
380 return skTypefaces;
381 }
382 #endif
383
384 /*! To make a typeface from the specified stream and font arguments
385 * \param stream the specified stream to be parsed to get font information
386 * \param args the arguments of index or axis values
387 * \param path the fullname of font file
388 * \return The object of typeface if successful
389 * \n Return null, if the stream is not recognized
390 */
makeTypeface(std::unique_ptr<SkStreamAsset> stream,const SkFontArguments & args,const char path[]) const391 sk_sp<SkTypeface> SkFontMgr_OHOS::makeTypeface(std::unique_ptr<SkStreamAsset> stream,
392 const SkFontArguments& args, const char path[]) const
393 {
394 FontInfo fontInfo;
395 int ttcIndex = args.getCollectionIndex();
396 int axisCount = args.getVariationDesignPosition().coordinateCount;
397
398 if (path) {
399 fontInfo.fname.set(path);
400 }
401 if (axisCount == 0) {
402 if (!fFontScanner.scanFont(stream.get(), ttcIndex, &fontInfo.familyName,
403 &fontInfo.style, &fontInfo.isFixedWidth, nullptr)) {
404 return nullptr;
405 }
406 } else {
407 SkTypeface_FreeType::Scanner::AxisDefinitions axisDef;
408 if (!fFontScanner.scanFont(stream.get(), ttcIndex, &fontInfo.familyName,
409 &fontInfo.style, &fontInfo.isFixedWidth, &axisDef)) {
410 return nullptr;
411 }
412 if (axisDef.count() > 0) {
413 SkFixed axis[axisDef.count()];
414 SkTypeface_FreeType::Scanner::computeAxisValues(
415 axisDef, args.getVariationDesignPosition(), axis, fontInfo.familyName);
416 fontInfo.setAxisSet(axisCount, axis, axisDef.data());
417 fontInfo.style = fontInfo.computeFontStyle();
418 }
419 }
420
421 fontInfo.stream = std::move(stream);
422 fontInfo.index = ttcIndex;
423 return sk_make_sp<SkTypeface_OHOS>(fontInfo);
424 }
425
426 /*! Get the fullname of font
427 * \param fontFd The file descriptor for the font file
428 * \param fullnameVec Read the font fullname list
429 * \return Returns Whether the fullnameVec was successfully obtained, 0 means success, see FontCheckCode for details
430 */
GetFontFullName(int fontFd,std::vector<SkByteArray> & fullnameVec)431 int SkFontMgr_OHOS::GetFontFullName(int fontFd, std::vector<SkByteArray> &fullnameVec)
432 {
433 std::unique_ptr<SkMemoryStream> stream = std::make_unique<SkMemoryStream>(SkData::MakeFromFD(fontFd));
434 int errorCode = SUCCESSED;
435 int numFaces = 0;
436 if (!fFontScanner.recognizedFont(stream.get(), &numFaces)) {
437 return ERROR_TYPE_OTHER;
438 }
439 for (int faceIndex = 0; faceIndex < numFaces; ++faceIndex) {
440 bool isFixedPitch = false;
441 SkString realname;
442 SkFontStyle style = SkFontStyle(); // avoid uninitialized warning
443 if (!fFontScanner.scanFont(stream.get(), faceIndex, &realname, &style, &isFixedPitch, nullptr)) {
444 errorCode = ERROR_TYPE_OTHER;
445 break;
446 }
447 SkByteArray skFullName = {nullptr, 0};
448 if (!fFontScanner.GetTypefaceFullname(stream.get(), faceIndex, skFullName)) {
449 errorCode = ERROR_TYPE_OTHER;
450 break;
451 } else {
452 fullnameVec.push_back(std::move(skFullName));
453 }
454 }
455 return errorCode;
456 }
457
458 /*! To create SkFontMgr object for Harmony platform
459 * \param fname the full name of system font configuration documents
460 * \return The object of SkFontMgr_OHOS
461 */
SkFontMgr_New_OHOS(const char * fname)462 sk_sp<SkFontMgr> SkFontMgr_New_OHOS(const char* fname)
463 {
464 return sk_make_sp<SkFontMgr_OHOS>(fname);
465 }
466