1 /*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "font_config.h"
17
18 #include "cJSON.h"
19 #include <dirent.h>
20 #include <fstream>
21 #include <libgen.h>
22 #include <sys/stat.h>
23 #include <sys/types.h>
24 #include <unistd.h>
25
26 #include "utils/text_log.h"
27 #ifdef BUILD_NON_SDK_VER
28 #include "securec.h"
29 #endif
30 #include "texgine/utils/exlog.h"
31
32 namespace OHOS {
33 namespace Rosen {
34 namespace TextEngine {
35 #define SUCCESSED 0
36 #define FAILED 1
37
38 const char* FONT_DEFAULT_CONFIG = "/system/etc/fontconfig.json";
39 constexpr const char* FALLBACK_VARIATIONS_KEY = "variations";
40 constexpr const char* FALLBACK_INDEX_KEY = "index";
41
FontConfig(const char * fname)42 FontConfig::FontConfig(const char* fname)
43 {
44 int err = ParseConfig(fname);
45 if (err != 0) {
46 LOGSO_FUNC_LINE(ERROR) << "parse config err";
47 }
48 }
49
GetFileData(const char * fname,int & size)50 char* FontConfig::GetFileData(const char* fname, int& size)
51 {
52 #ifdef BUILD_NON_SDK_VER
53 char realPath[PATH_MAX] = {0};
54 if (fname == nullptr || realpath(fname, realPath) == NULL) {
55 LOGSO_FUNC_LINE(ERROR) << "path or realPath is NULL";
56 return nullptr;
57 }
58 #endif
59 std::ifstream file(fname);
60 if (file.good()) {
61 FILE* fp = fopen(fname, "r");
62 if (fp == nullptr) {
63 return nullptr;
64 }
65 fseek(fp, 0L, SEEK_END);
66 size = ftell(fp) + 1;
67 rewind(fp);
68 char* data = static_cast<char*>(malloc(size));
69 if (data == nullptr) {
70 fclose(fp);
71 return nullptr;
72 }
73 #ifdef BUILD_NON_SDK_VER
74 if (memset_s(data, size, 0, size) != EOK) {
75 LOGSO_FUNC_LINE(ERROR) << "memset failed";
76 free(data);
77 data = nullptr;
78 fclose(fp);
79 return nullptr;
80 }
81 #else
82 memset(data, 0, size);
83 #endif
84 (void)fread(data, size, 1, fp);
85 fclose(fp);
86 return data;
87 }
88
89 return nullptr;
90 }
CheckConfigFile(const char * fname) const91 cJSON* FontConfig::CheckConfigFile(const char* fname) const
92 {
93 int size = 0;
94 char* data = GetFileData(fname, size);
95 if (data == nullptr) {
96 LOGSO_FUNC_LINE(ERROR) << "data is NULL";
97 return nullptr;
98 }
99 std::string pramsString;
100 pramsString.assign(data, size);
101 free(data);
102 return cJSON_Parse(pramsString.c_str());
103 }
104
ParseFont(const cJSON * root)105 int FontConfig::ParseFont(const cJSON* root)
106 {
107 const char* tag = "font";
108 cJSON* filters = cJSON_GetObjectItem(root, tag);
109 if (filters == nullptr) {
110 LOGSO_FUNC_LINE(ERROR) << "parse font failed";
111 return FAILED;
112 }
113 int size = cJSON_GetArraySize(filters);
114 for (int i = 0; i < size;i++) {
115 cJSON* item = cJSON_GetArrayItem(filters, i);
116 if (item != nullptr && cJSON_IsString(item)) {
117 fontSet_.emplace_back(rootPath_ + std::string(item->valuestring));
118 }
119 }
120 return SUCCESSED;
121 }
122
ParseConfig(const char * fname)123 int FontConfig::ParseConfig(const char* fname)
124 {
125 if (fname == nullptr) {
126 LOGSO_FUNC_LINE(ERROR) << "fname is null";
127 return FAILED;
128 }
129
130 std::string rootPath(fname);
131 size_t idx = rootPath.rfind('/');
132 if (idx == 0 || idx == std::string::npos) {
133 LOGSO_FUNC_LINE(ERROR) << "fname is illegal";
134 return FAILED;
135 }
136 rootPath_.assign(rootPath.substr(0, idx) + "/");
137 cJSON* root = CheckConfigFile(fname);
138 if (root == nullptr) {
139 LOGSO_FUNC_LINE(ERROR) << "check config file failed";
140 return FAILED;
141 }
142 return ParseFont(root);
143 }
144
Dump() const145 void FontConfig::Dump() const
146 {
147 for (auto it : fontSet_) {
148 LOGSO_FUNC_LINE(INFO) << "fname:" << it;
149 }
150 }
151
GetFontSet() const152 std::vector<std::string> FontConfig::GetFontSet() const
153 {
154 return fontSet_;
155 }
156
ParseFile(const char * fname)157 int FontConfigJson::ParseFile(const char* fname)
158 {
159 if (fname == nullptr) {
160 LOGSO_FUNC_LINE(DEBUG) << "ParseFile fname is nullptr";
161 fname = FONT_DEFAULT_CONFIG;
162 }
163
164 LOGSO_FUNC_LINE(INFO) << "ParseFile fname is: " << fname;
165 fontPtr = std::make_shared<FontConfigJsonInfo>();
166 int err = ParseConfigList(fname);
167 if (err != 0) {
168 LOGSO_FUNC_LINE(ERROR) << "ParseFile ParseConfigList failed";
169 return err;
170 }
171 return SUCCESSED;
172 }
ParseFontFileMap(const char * fname)173 int FontConfigJson::ParseFontFileMap(const char* fname)
174 {
175 if (fname == nullptr) {
176 LOGSO_FUNC_LINE(DEBUG) << "ParseFontFileMap fname is nullptr";
177 fname = FONT_DEFAULT_CONFIG;
178 }
179
180 LOGSO_FUNC_LINE(INFO) << "ParseFontFileMap fname is: " << fname;
181 fontFileMap = std::make_shared<FontFileMap>();
182 int err = ParseConfigListPath(fname);
183 if (err != 0) {
184 LOGSO_FUNC_LINE(ERROR) << "ParseFontFileMap ParseConfigList failed";
185 return err;
186 }
187 return SUCCESSED;
188 }
189
AnalyseFontDir(const cJSON * root)190 void FontConfigJson::AnalyseFontDir(const cJSON* root)
191 {
192 if (root == nullptr) {
193 return;
194 }
195 int size = cJSON_GetArraySize(root);
196 for (int i = 0; i < size; i++) {
197 cJSON* item = cJSON_GetArrayItem(root, i);
198 if (item != nullptr && cJSON_IsString(item)) {
199 fontPtr->fontDirSet.emplace_back(std::string(item->valuestring));
200 }
201 }
202 return;
203 }
204
ParseDir(const cJSON * root)205 int FontConfigJson::ParseDir(const cJSON* root)
206 {
207 if (root == nullptr) {
208 LOGSO_FUNC_LINE(ERROR) << "parse dir failed";
209 return FAILED;
210 }
211 const char* key = "fontdir";
212 cJSON* item = cJSON_GetObjectItem(root, key);
213 if (item != nullptr) {
214 AnalyseFontDir(item);
215 }
216 return SUCCESSED;
217 }
218
ParseConfigList(const char * fname)219 int FontConfigJson::ParseConfigList(const char* fname)
220 {
221 if (fname == nullptr) {
222 LOGSO_FUNC_LINE(ERROR) << "ParseConfigList fname is nullptr";
223 return FAILED;
224 }
225 cJSON* root = CheckConfigFile(fname);
226 if (root == nullptr) {
227 LOGSO_FUNC_LINE(ERROR) << "ParseConfigList CheckConfigFile failed";
228 return FAILED;
229 }
230 // "generic", "fallback" - font attribute
231 const char* keys[] = {"generic", "fallback", "fontdir", nullptr};
232 int index = 0;
233 while (true) {
234 if (keys[index] == nullptr) {
235 break;
236 }
237 const char* key = keys[index++];
238 if (!strcmp(key, "fontdir")) {
239 ParseDir(root);
240 } else if (!strcmp(key, "generic")) {
241 ParseGeneric(root, key);
242 } else if (!strcmp(key, "fallback")) {
243 ParseFallback(root, key);
244 }
245 }
246 cJSON_Delete(root);
247 return SUCCESSED;
248 }
249
ParseConfigListPath(const char * fname)250 int FontConfigJson::ParseConfigListPath(const char* fname)
251 {
252 if (fname == nullptr) {
253 LOGSO_FUNC_LINE(ERROR) << "ParseConfigListPath fname is nullptr";
254 return FAILED;
255 }
256 cJSON* root = CheckConfigFile(fname);
257 if (root == nullptr) {
258 LOGSO_FUNC_LINE(ERROR) << "ParseConfigListPath CheckConfigFile failed";
259 return FAILED;
260 }
261 ParseFontMap(root, "font_file_map");
262 cJSON_Delete(root);
263 return SUCCESSED;
264 }
265
ParseAdjustArr(const cJSON * arr,FontGenericInfo & genericInfo)266 int FontConfigJson::ParseAdjustArr(const cJSON* arr, FontGenericInfo &genericInfo)
267 {
268 if (arr == nullptr) {
269 LOGSO_FUNC_LINE(ERROR) << "parse adjust arr failed";
270 return FAILED;
271 }
272 int size = cJSON_GetArraySize(arr);
273 for (int i = 0; i < size; i++) {
274 cJSON* item = cJSON_GetArrayItem(arr, i);
275 if (item == nullptr) {
276 continue;
277 }
278 ParseAdjust(item, genericInfo);
279 }
280 return SUCCESSED;
281 }
282
ParseAliasArr(const cJSON * arr,FontGenericInfo & genericInfo)283 int FontConfigJson::ParseAliasArr(const cJSON* arr, FontGenericInfo &genericInfo)
284 {
285 if (arr == nullptr) {
286 LOGSO_FUNC_LINE(ERROR) << "ParseAliasArr failed";
287 return FAILED;
288 }
289 int size = cJSON_GetArraySize(arr);
290 for (int i = 0; i < size; i++) {
291 cJSON* item = cJSON_GetArrayItem(arr, i);
292 if (item == nullptr) {
293 continue;
294 }
295 ParseAlias(item, genericInfo);
296 }
297 return SUCCESSED;
298 }
299
ParseGeneric(const cJSON * root,const char * key)300 int FontConfigJson::ParseGeneric(const cJSON* root, const char* key)
301 {
302 if (root == nullptr) {
303 LOGSO_FUNC_LINE(ERROR) << "root is nullptr";
304 return FAILED;
305 }
306 cJSON* filters = cJSON_GetObjectItem(root, key);
307 if (filters == nullptr || !cJSON_IsArray(filters)) {
308 LOGSO_FUNC_LINE(ERROR) << "ParseGeneric failed";
309 return FAILED;
310 }
311 int size = cJSON_GetArraySize(filters);
312 for (int i = 0; i < size; i++) {
313 cJSON* item = cJSON_GetArrayItem(filters, i);
314 if (item == nullptr) {
315 continue;
316 }
317 FontGenericInfo genericInfo;
318 cJSON* family = cJSON_GetObjectItem(item, "family");
319 if (family != nullptr && cJSON_IsString(family)) {
320 genericInfo.familyName = std::string(family->valuestring);
321 }
322
323 cJSON* alias = cJSON_GetObjectItem(item, "alias");
324 if (alias != nullptr && cJSON_IsArray(alias)) {
325 ParseAliasArr(alias, genericInfo);
326 }
327
328 cJSON* adjust = cJSON_GetObjectItem(item, "adjust");
329 if (adjust != nullptr && cJSON_IsArray(adjust)) {
330 ParseAdjustArr(adjust, genericInfo);
331 }
332
333 fontPtr->genericSet.push_back(genericInfo);
334 }
335
336 return SUCCESSED;
337 }
338
ParseAlias(const cJSON * root,FontGenericInfo & genericInfo)339 int FontConfigJson::ParseAlias(const cJSON* root, FontGenericInfo &genericInfo)
340 {
341 if (root == nullptr) {
342 LOGSO_FUNC_LINE(ERROR) << "root is nullptr";
343 return FAILED;
344 }
345
346 int size = cJSON_GetArraySize(root);
347 for (int i = 0; i < size; i++) {
348 cJSON* item = cJSON_GetArrayItem(root, i);
349 if (item == nullptr) {
350 continue;
351 }
352 std::string aliasName = std::string(item->string);
353 if (!cJSON_IsNumber(item)) {
354 continue;
355 }
356 int weight = item->valueint;
357 AliasInfo info = {aliasName, weight};
358 genericInfo.aliasSet.emplace_back(std::move(info));
359 }
360
361 return SUCCESSED;
362 }
363
ParseAdjust(const cJSON * root,FontGenericInfo & genericInfo)364 int FontConfigJson::ParseAdjust(const cJSON* root, FontGenericInfo &genericInfo)
365 {
366 if (root == nullptr) {
367 LOGSO_FUNC_LINE(ERROR) << "root is nullptr";
368 return FAILED;
369 }
370 int size = cJSON_GetArraySize(root);
371 const int count = 2; // the adjust item is 2
372 int value[count] = { 0 };
373 for (int i = 0; i < size; i++) {
374 if (i >= count) {
375 break;
376 }
377 cJSON* item = cJSON_GetArrayItem(root, i);
378 if (item == nullptr || !cJSON_IsNumber(item)) {
379 continue;
380 }
381 value[i] = item->valueint;
382 }
383
384 AdjustInfo info = {value[0], value[1]};
385 genericInfo.adjustSet.emplace_back(std::move(info));
386 return SUCCESSED;
387 }
388
ParseFallback(const cJSON * root,const char * key)389 int FontConfigJson::ParseFallback(const cJSON* root, const char* key)
390 {
391 if (root == nullptr) {
392 LOGSO_FUNC_LINE(ERROR) << "root is nullptr";
393 return FAILED;
394 }
395 cJSON* filters = cJSON_GetObjectItem(root, key);
396 if (filters == nullptr || !cJSON_IsArray(filters)) {
397 LOGSO_FUNC_LINE(ERROR) << "cJSON_GetObjectItem failed";
398 return FAILED;
399 }
400 cJSON* forItem = cJSON_GetArrayItem(cJSON_GetArrayItem(filters, 0), 0);
401 int size = cJSON_GetArraySize(forItem);
402 FallbackGroup fallbackGroup;
403 fallbackGroup.groupName = std::string("");
404 for (int i = 0; i < size; i++) {
405 cJSON* item = cJSON_GetArrayItem(forItem, i);
406 if (item == nullptr) {
407 continue;
408 }
409 // refer to FontConfig_OHOS::parseFallbackItem
410 int itemSize = cJSON_GetArraySize(item);
411 for (int j = itemSize - 1; j >= 0; --j) {
412 cJSON* item2 = cJSON_GetArrayItem(item, j);
413 if (item2 == nullptr || item2->valuestring == nullptr || item2->string == nullptr ||
414 strcmp(item2->string, FALLBACK_VARIATIONS_KEY) == 0 ||
415 strcmp(item2->string, FALLBACK_INDEX_KEY) == 0) {
416 continue;
417 }
418 FallbackInfo fallbackInfo;
419 fallbackInfo.familyName = item2->valuestring;
420 fallbackInfo.font = item2->string;
421 fallbackGroup.fallbackInfoSet.emplace_back(std::move(fallbackInfo));
422 break;
423 }
424 }
425 fontPtr->fallbackGroupSet.emplace_back(std::move(fallbackGroup));
426 return SUCCESSED;
427 }
428
ParseFontMap(const cJSON * root,const char * key)429 int FontConfigJson::ParseFontMap(const cJSON* root, const char* key)
430 {
431 if (root == nullptr) {
432 LOGSO_FUNC_LINE(ERROR) << "root is nullptr";
433 return FAILED;
434 }
435 cJSON* filters = cJSON_GetObjectItem(root, key);
436 if (filters == nullptr || !cJSON_IsArray(filters)) {
437 LOGSO_FUNC_LINE(ERROR) << "cJSON_GetObjectItem failed";
438 return FAILED;
439 }
440 int size = cJSON_GetArraySize(filters);
441 for (int i = 0; i < size; i++) {
442 cJSON* item = cJSON_GetArrayItem(filters, i);
443 if (item == nullptr) {
444 continue;
445 }
446 cJSON* item2 = cJSON_GetArrayItem(item, 0);
447 if (item2 == nullptr || item2->valuestring == nullptr || item2->string == nullptr) {
448 continue;
449 }
450 (*fontFileMap)[item2->string] = item2->valuestring;
451 }
452 return SUCCESSED;
453 }
454
ParseInstallFont(const cJSON * root,std::vector<std::string> & fontPathList)455 int FontConfigJson::ParseInstallFont(const cJSON* root, std::vector<std::string>& fontPathList)
456 {
457 const char* tag = "fontlist";
458 cJSON* rootObj = cJSON_GetObjectItem(root, tag);
459 if (rootObj == nullptr) {
460 TEXT_LOGE("Failed to get json object");
461 return FAILED;
462 }
463 int size = cJSON_GetArraySize(rootObj);
464 if (size <= 0) {
465 TEXT_LOGE("Failed to get json array size");
466 return FAILED;
467 }
468 fontPathList.reserve(size);
469 for (int i = 0; i < size; i++) {
470 cJSON* item = cJSON_GetArrayItem(rootObj, i);
471 if (item == nullptr) {
472 TEXT_LOGE("Failed to get json item");
473 return FAILED;
474 }
475 cJSON* fullPath = cJSON_GetObjectItem(item, "fontfullpath");
476 if (fullPath == nullptr || !cJSON_IsString(fullPath) || fullPath->valuestring == nullptr) {
477 TEXT_LOGE("Failed to get fullPath");
478 return FAILED;
479 }
480 fontPathList.emplace_back(std::string(fullPath->valuestring));
481 }
482 return SUCCESSED;
483 }
484
ParseInstallConfig(const char * fontPath,std::vector<std::string> & fontPathList)485 int FontConfigJson::ParseInstallConfig(const char* fontPath, std::vector<std::string>& fontPathList)
486 {
487 if (fontPath == nullptr) {
488 TEXT_LOGE("Font path is null");
489 return FAILED;
490 }
491
492 cJSON* root = CheckConfigFile(fontPath);
493 if (root == nullptr) {
494 TEXT_LOGE("Failed to check config file");
495 return FAILED;
496 }
497 if (ParseInstallFont(root, fontPathList) != SUCCESSED) {
498 cJSON_Delete(root);
499 return FAILED;
500 }
501 cJSON_Delete(root);
502 return SUCCESSED;
503 }
504
DumpAlias(const AliasSet & aliasSet) const505 void FontConfigJson::DumpAlias(const AliasSet &aliasSet) const
506 {
507 if (!aliasSet.empty()) {
508 const std::string space = " ";
509 LOGSO_FUNC_LINE(INFO) << " \"alias\": [";
510 for (auto it : aliasSet) {
511 LOGSO_FUNC_LINE(INFO) << " {";
512 LOGSO_FUNC_LINE(INFO) << space << " \"" <<
513 it.familyName << "\" : " << it.weight;
514 LOGSO_FUNC_LINE(INFO) << " },";
515 }
516 LOGSO_FUNC_LINE(INFO) << " ],";
517 }
518 }
519
DumpAjdust(const AdjustSet & adjustSet) const520 void FontConfigJson::DumpAjdust(const AdjustSet &adjustSet) const
521 {
522 if (!adjustSet.empty()) {
523 LOGSO_FUNC_LINE(INFO) << " \"adjust\": [";
524 const std::string space = " ";
525 for (auto it : adjustSet) {
526 LOGSO_FUNC_LINE(INFO) << " {";
527 LOGSO_FUNC_LINE(INFO) << space << " \"weght\" :" <<
528 it.origValue << " , " << "\"to\" :" << it.newValue;
529 LOGSO_FUNC_LINE(INFO) << " },";
530 }
531 LOGSO_FUNC_LINE(INFO) << " ],";
532 }
533 }
534
DumpGeneric() const535 void FontConfigJson::DumpGeneric() const
536 {
537 LOGSO_FUNC_LINE(INFO) << "generic : [";
538 if (!fontPtr->genericSet.empty()) {
539 for (auto it : fontPtr->genericSet) {
540 LOGSO_FUNC_LINE(INFO) << " \"family\": [\""<< it.familyName << "\"],";
541 DumpAlias(it.aliasSet);
542 DumpAjdust(it.adjustSet);
543 }
544 }
545 LOGSO_FUNC_LINE(INFO) << "]";
546 }
547
DumpForbak() const548 void FontConfigJson::DumpForbak() const
549 {
550 if (!fontPtr->fallbackGroupSet.empty()) {
551 LOGSO_FUNC_LINE(INFO) << "\"fallback\": [";
552 LOGSO_FUNC_LINE(INFO) << "{";
553 for (auto group : fontPtr->fallbackGroupSet) {
554 LOGSO_FUNC_LINE(INFO) << " \"" << group.groupName << "\" : [";
555 if (group.fallbackInfoSet.empty()) continue;
556 const std::string space = " ";
557 for (auto it : group.fallbackInfoSet) {
558 LOGSO_FUNC_LINE(INFO) << " {";
559 LOGSO_FUNC_LINE(INFO) << space << it.font << "\" : \""
560 << it.familyName << "\"";
561 LOGSO_FUNC_LINE(INFO) << " },";
562 }
563 LOGSO_FUNC_LINE(INFO) << " ]";
564 }
565 LOGSO_FUNC_LINE(INFO) << "}";
566 LOGSO_FUNC_LINE(INFO) << "]";
567 }
568 }
569
DumpFontDir() const570 void FontConfigJson::DumpFontDir() const
571 {
572 LOGSO_FUNC_LINE(INFO) << "fontdir : [";
573 if (!fontPtr->fontDirSet.empty()) {
574 for (auto it : fontPtr->fontDirSet) {
575 LOGSO_FUNC_LINE(INFO) << "\""<< it << "\",";
576 }
577 }
578 LOGSO_FUNC_LINE(INFO) << "]";
579 }
580
DumpFontFileMap() const581 void FontConfigJson::DumpFontFileMap() const
582 {
583 for (auto it : (*fontFileMap)) {
584 LOGSO_FUNC_LINE(INFO) << "\"" << it.first << "\": \""
585 << it.second << "\"";
586 }
587 }
588
Dump() const589 void FontConfigJson::Dump() const
590 {
591 if (fontPtr != nullptr) {
592 LOGSO_FUNC_LINE(INFO) << "font config dump fontPtr in";
593 DumpFontDir();
594 DumpGeneric();
595 DumpForbak();
596 LOGSO_FUNC_LINE(INFO) << "font config dump fontPtr out";
597 }
598 if (fontFileMap != nullptr) {
599 LOGSO_FUNC_LINE(INFO) << "font config dump fontFileMap in";
600 DumpFontFileMap();
601 LOGSO_FUNC_LINE(INFO) << "font config dump fontFileMap out";
602 }
603 }
604 } // namespace TextEngine
605 } // namespace Rosen
606 } // namespace OHOS
607