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 #ifdef BUILD_NON_SDK_VER
27 #include "securec.h"
28 #endif
29 #include "texgine/utils/exlog.h"
30
31 namespace OHOS {
32 namespace Rosen {
33 namespace TextEngine {
34 #define SUCCESSED 0
35 #define FAILED 1
36
37 const char* FONT_DEFAULT_CONFIG = "/system/etc/fontconfig.json";
38 constexpr const char* FALLBACK_VARIATIONS_KEY = "variations";
39 constexpr const char* FALLBACK_INDEX_KEY = "index";
40
FontConfig(const char * fname)41 FontConfig::FontConfig(const char* fname)
42 {
43 int err = ParseConfig(fname);
44 if (err != 0) {
45 LOGSO_FUNC_LINE(ERROR) << "parse config err";
46 }
47 }
48
GetFileData(const char * fname,int & size)49 char* FontConfig::GetFileData(const char* fname, int& size)
50 {
51 #ifdef BUILD_NON_SDK_VER
52 char realPath[PATH_MAX] = {0};
53 if (fname == nullptr || realpath(fname, realPath) == NULL) {
54 LOGSO_FUNC_LINE(ERROR) << "path or realPath is NULL";
55 return nullptr;
56 }
57 #endif
58 std::ifstream file(fname);
59 if (file.good()) {
60 FILE* fp = fopen(fname, "r");
61 if (fp == nullptr) {
62 return nullptr;
63 }
64 fseek(fp, 0L, SEEK_END);
65 size = ftell(fp) + 1;
66 rewind(fp);
67 char* data = static_cast<char*>(malloc(size));
68 if (data == nullptr) {
69 fclose(fp);
70 return nullptr;
71 }
72 #ifdef BUILD_NON_SDK_VER
73 if (memset_s(data, size, 0, size) != EOK) {
74 LOGSO_FUNC_LINE(ERROR) << "memset failed";
75 free(data);
76 data = nullptr;
77 fclose(fp);
78 return nullptr;
79 }
80 #else
81 memset(data, 0, size);
82 #endif
83 (void)fread(data, size, 1, fp);
84 fclose(fp);
85 return data;
86 }
87
88 return nullptr;
89 }
CheckConfigFile(const char * fname) const90 cJSON* FontConfig::CheckConfigFile(const char* fname) const
91 {
92 int size = 0;
93 char* data = GetFileData(fname, size);
94 if (data == nullptr) {
95 LOGSO_FUNC_LINE(ERROR) << "data is NULL";
96 return nullptr;
97 }
98 std::string pramsString;
99 pramsString.assign(data, size);
100 free(data);
101 return cJSON_Parse(pramsString.c_str());
102 }
103
ParseFont(const cJSON * root)104 int FontConfig::ParseFont(const cJSON* root)
105 {
106 const char* tag = "font";
107 cJSON* filters = cJSON_GetObjectItem(root, tag);
108 if (filters == nullptr) {
109 LOGSO_FUNC_LINE(ERROR) << "parse font failed";
110 return FAILED;
111 }
112 int size = cJSON_GetArraySize(filters);
113 for (int i = 0; i < size;i++) {
114 cJSON* item = cJSON_GetArrayItem(filters, i);
115 if (item != nullptr && cJSON_IsString(item)) {
116 fontSet_.emplace_back(rootPath_ + std::string(item->valuestring));
117 }
118 }
119 return SUCCESSED;
120 }
121
ParseConfig(const char * fname)122 int FontConfig::ParseConfig(const char* fname)
123 {
124 if (fname == nullptr) {
125 LOGSO_FUNC_LINE(ERROR) << "fname is null";
126 return FAILED;
127 }
128
129 std::string rootPath(fname);
130 size_t idx = rootPath.rfind('/');
131 if (idx == 0 || idx == std::string::npos) {
132 LOGSO_FUNC_LINE(ERROR) << "fname is illegal";
133 return FAILED;
134 }
135 rootPath_.assign(rootPath.substr(0, idx) + "/");
136 cJSON* root = CheckConfigFile(fname);
137 if (root == nullptr) {
138 LOGSO_FUNC_LINE(ERROR) << "check config file failed";
139 return FAILED;
140 }
141 return ParseFont(root);
142 }
143
Dump() const144 void FontConfig::Dump() const
145 {
146 for (auto it : fontSet_) {
147 LOGSO_FUNC_LINE(INFO) << "fname:" << it;
148 }
149 }
150
GetFontSet() const151 std::vector<std::string> FontConfig::GetFontSet() const
152 {
153 return fontSet_;
154 }
155
ParseFile(const char * fname)156 int FontConfigJson::ParseFile(const char* fname)
157 {
158 if (fname == nullptr) {
159 LOGSO_FUNC_LINE(DEBUG) << "ParseFile fname is nullptr";
160 fname = FONT_DEFAULT_CONFIG;
161 }
162
163 LOGSO_FUNC_LINE(INFO) << "ParseFile fname is: " << fname;
164 fontPtr = std::make_shared<FontConfigJsonInfo>();
165 int err = ParseConfigList(fname);
166 if (err != 0) {
167 LOGSO_FUNC_LINE(ERROR) << "ParseFile ParseConfigList failed";
168 return err;
169 }
170 return SUCCESSED;
171 }
ParseFontFileMap(const char * fname)172 int FontConfigJson::ParseFontFileMap(const char* fname)
173 {
174 if (fname == nullptr) {
175 LOGSO_FUNC_LINE(DEBUG) << "ParseFontFileMap fname is nullptr";
176 fname = FONT_DEFAULT_CONFIG;
177 }
178
179 LOGSO_FUNC_LINE(INFO) << "ParseFontFileMap fname is: " << fname;
180 fontFileMap = std::make_shared<FontFileMap>();
181 int err = ParseConfigListPath(fname);
182 if (err != 0) {
183 LOGSO_FUNC_LINE(ERROR) << "ParseFontFileMap ParseConfigList failed";
184 return err;
185 }
186 return SUCCESSED;
187 }
188
AnalyseFontDir(const cJSON * root)189 void FontConfigJson::AnalyseFontDir(const cJSON* root)
190 {
191 if (root == nullptr) {
192 return;
193 }
194 int size = cJSON_GetArraySize(root);
195 for (int i = 0; i < size; i++) {
196 cJSON* item = cJSON_GetArrayItem(root, i);
197 if (item != nullptr && cJSON_IsString(item)) {
198 fontPtr->fontDirSet.emplace_back(std::string(item->valuestring));
199 }
200 }
201 return;
202 }
203
ParseDir(const cJSON * root)204 int FontConfigJson::ParseDir(const cJSON* root)
205 {
206 if (root == nullptr) {
207 LOGSO_FUNC_LINE(ERROR) << "parse dir failed";
208 return FAILED;
209 }
210 const char* key = "fontdir";
211 cJSON* item = cJSON_GetObjectItem(root, key);
212 if (item != nullptr) {
213 AnalyseFontDir(item);
214 }
215 return SUCCESSED;
216 }
217
ParseConfigList(const char * fname)218 int FontConfigJson::ParseConfigList(const char* fname)
219 {
220 if (fname == nullptr) {
221 LOGSO_FUNC_LINE(ERROR) << "ParseConfigList fname is nullptr";
222 return FAILED;
223 }
224 cJSON* root = CheckConfigFile(fname);
225 if (root == nullptr) {
226 LOGSO_FUNC_LINE(ERROR) << "ParseConfigList CheckConfigFile failed";
227 return FAILED;
228 }
229 // "generic", "fallback" - font attribute
230 const char* keys[] = {"generic", "fallback", "fontdir", nullptr};
231 int index = 0;
232 while (true) {
233 if (keys[index] == nullptr) {
234 break;
235 }
236 const char* key = keys[index++];
237 if (!strcmp(key, "fontdir")) {
238 ParseDir(root);
239 } else if (!strcmp(key, "generic")) {
240 ParseGeneric(root, key);
241 } else if (!strcmp(key, "fallback")) {
242 ParseFallback(root, key);
243 }
244 }
245 cJSON_Delete(root);
246 return SUCCESSED;
247 }
248
ParseConfigListPath(const char * fname)249 int FontConfigJson::ParseConfigListPath(const char* fname)
250 {
251 if (fname == nullptr) {
252 LOGSO_FUNC_LINE(ERROR) << "ParseConfigListPath fname is nullptr";
253 return FAILED;
254 }
255 cJSON* root = CheckConfigFile(fname);
256 if (root == nullptr) {
257 LOGSO_FUNC_LINE(ERROR) << "ParseConfigListPath CheckConfigFile failed";
258 return FAILED;
259 }
260 ParseFontMap(root, "font_file_map");
261 cJSON_Delete(root);
262 return SUCCESSED;
263 }
264
ParseAdjustArr(const cJSON * arr,FontGenericInfo & genericInfo)265 int FontConfigJson::ParseAdjustArr(const cJSON* arr, FontGenericInfo &genericInfo)
266 {
267 if (arr == nullptr) {
268 LOGSO_FUNC_LINE(ERROR) << "parse adjust arr failed";
269 return FAILED;
270 }
271 int size = cJSON_GetArraySize(arr);
272 for (int i = 0; i < size; i++) {
273 cJSON* item = cJSON_GetArrayItem(arr, i);
274 if (item == nullptr) {
275 continue;
276 }
277 ParseAdjust(item, genericInfo);
278 }
279 return SUCCESSED;
280 }
281
ParseAliasArr(const cJSON * arr,FontGenericInfo & genericInfo)282 int FontConfigJson::ParseAliasArr(const cJSON* arr, FontGenericInfo &genericInfo)
283 {
284 if (arr == nullptr) {
285 LOGSO_FUNC_LINE(ERROR) << "ParseAliasArr failed";
286 return FAILED;
287 }
288 int size = cJSON_GetArraySize(arr);
289 for (int i = 0; i < size; i++) {
290 cJSON* item = cJSON_GetArrayItem(arr, i);
291 if (item == nullptr) {
292 continue;
293 }
294 ParseAlias(item, genericInfo);
295 }
296 return SUCCESSED;
297 }
298
ParseGeneric(const cJSON * root,const char * key)299 int FontConfigJson::ParseGeneric(const cJSON* root, const char* key)
300 {
301 if (root == nullptr) {
302 LOGSO_FUNC_LINE(ERROR) << "root is nullptr";
303 return FAILED;
304 }
305 cJSON* filters = cJSON_GetObjectItem(root, key);
306 if (filters == nullptr || !cJSON_IsArray(filters)) {
307 LOGSO_FUNC_LINE(ERROR) << "ParseGeneric failed";
308 return FAILED;
309 }
310 int size = cJSON_GetArraySize(filters);
311 for (int i = 0; i < size; i++) {
312 cJSON* item = cJSON_GetArrayItem(filters, i);
313 if (item == nullptr) {
314 continue;
315 }
316 FontGenericInfo genericInfo;
317 cJSON* family = cJSON_GetObjectItem(item, "family");
318 if (family != nullptr && cJSON_IsString(family)) {
319 genericInfo.familyName = std::string(family->valuestring);
320 }
321
322 cJSON* alias = cJSON_GetObjectItem(item, "alias");
323 if (alias != nullptr && cJSON_IsArray(alias)) {
324 ParseAliasArr(alias, genericInfo);
325 }
326
327 cJSON* adjust = cJSON_GetObjectItem(item, "adjust");
328 if (adjust != nullptr && cJSON_IsArray(adjust)) {
329 ParseAdjustArr(adjust, genericInfo);
330 }
331
332 fontPtr->genericSet.push_back(genericInfo);
333 }
334
335 return SUCCESSED;
336 }
337
ParseAlias(const cJSON * root,FontGenericInfo & genericInfo)338 int FontConfigJson::ParseAlias(const cJSON* root, FontGenericInfo &genericInfo)
339 {
340 if (root == nullptr) {
341 LOGSO_FUNC_LINE(ERROR) << "root is nullptr";
342 return FAILED;
343 }
344
345 int size = cJSON_GetArraySize(root);
346 for (int i = 0; i < size; i++) {
347 cJSON* item = cJSON_GetArrayItem(root, i);
348 if (item == nullptr) {
349 continue;
350 }
351 std::string aliasName = std::string(item->string);
352 if (!cJSON_IsNumber(item)) {
353 continue;
354 }
355 int weight = item->valueint;
356 AliasInfo info = {aliasName, weight};
357 genericInfo.aliasSet.emplace_back(std::move(info));
358 }
359
360 return SUCCESSED;
361 }
362
ParseAdjust(const cJSON * root,FontGenericInfo & genericInfo)363 int FontConfigJson::ParseAdjust(const cJSON* root, FontGenericInfo &genericInfo)
364 {
365 if (root == nullptr) {
366 LOGSO_FUNC_LINE(ERROR) << "root is nullptr";
367 return FAILED;
368 }
369 int size = cJSON_GetArraySize(root);
370 const int count = 2; // the adjust item is 2
371 int value[count] = { 0 };
372 for (int i = 0; i < size; i++) {
373 if (i >= count) {
374 break;
375 }
376 cJSON* item = cJSON_GetArrayItem(root, i);
377 if (item == nullptr || !cJSON_IsNumber(item)) {
378 continue;
379 }
380 value[i] = item->valueint;
381 }
382
383 AdjustInfo info = {value[0], value[1]};
384 genericInfo.adjustSet.emplace_back(std::move(info));
385 return SUCCESSED;
386 }
387
ParseFallback(const cJSON * root,const char * key)388 int FontConfigJson::ParseFallback(const cJSON* root, const char* key)
389 {
390 if (root == nullptr) {
391 LOGSO_FUNC_LINE(ERROR) << "root is nullptr";
392 return FAILED;
393 }
394 cJSON* filters = cJSON_GetObjectItem(root, key);
395 if (filters == nullptr || !cJSON_IsArray(filters)) {
396 LOGSO_FUNC_LINE(ERROR) << "cJSON_GetObjectItem failed";
397 return FAILED;
398 }
399 cJSON* forItem = cJSON_GetArrayItem(cJSON_GetArrayItem(filters, 0), 0);
400 int size = cJSON_GetArraySize(forItem);
401 FallbackGroup fallbackGroup;
402 fallbackGroup.groupName = std::string("");
403 for (int i = 0; i < size; i++) {
404 cJSON* item = cJSON_GetArrayItem(forItem, i);
405 if (item == nullptr) {
406 continue;
407 }
408 // refer to FontConfig_OHOS::parseFallbackItem
409 int itemSize = cJSON_GetArraySize(item);
410 for (int j = itemSize - 1; j >= 0; --j) {
411 cJSON* item2 = cJSON_GetArrayItem(item, j);
412 if (item2 == nullptr || item2->valuestring == nullptr || item2->string == nullptr ||
413 strcmp(item2->string, FALLBACK_VARIATIONS_KEY) == 0 ||
414 strcmp(item2->string, FALLBACK_INDEX_KEY) == 0) {
415 continue;
416 }
417 FallbackInfo fallbackInfo;
418 fallbackInfo.familyName = item2->valuestring;
419 fallbackInfo.font = item2->string;
420 fallbackGroup.fallbackInfoSet.emplace_back(std::move(fallbackInfo));
421 break;
422 }
423 }
424 fontPtr->fallbackGroupSet.emplace_back(std::move(fallbackGroup));
425 return SUCCESSED;
426 }
427
ParseFontMap(const cJSON * root,const char * key)428 int FontConfigJson::ParseFontMap(const cJSON* root, const char* key)
429 {
430 if (root == nullptr) {
431 LOGSO_FUNC_LINE(ERROR) << "root is nullptr";
432 return FAILED;
433 }
434 cJSON* filters = cJSON_GetObjectItem(root, key);
435 if (filters == nullptr || !cJSON_IsArray(filters)) {
436 LOGSO_FUNC_LINE(ERROR) << "cJSON_GetObjectItem failed";
437 return FAILED;
438 }
439 int size = cJSON_GetArraySize(filters);
440 for (int i = 0; i < size; i++) {
441 cJSON* item = cJSON_GetArrayItem(filters, i);
442 if (item == nullptr) {
443 continue;
444 }
445 cJSON* item2 = cJSON_GetArrayItem(item, 0);
446 if (item2 == nullptr || item2->valuestring == nullptr || item2->string == nullptr) {
447 continue;
448 }
449 (*fontFileMap)[item2->string] = item2->valuestring;
450 }
451 return SUCCESSED;
452 }
453
DumpAlias(const AliasSet & aliasSet) const454 void FontConfigJson::DumpAlias(const AliasSet &aliasSet) const
455 {
456 if (!aliasSet.empty()) {
457 const std::string space = " ";
458 LOGSO_FUNC_LINE(INFO) << " \"alias\": [";
459 for (auto it : aliasSet) {
460 LOGSO_FUNC_LINE(INFO) << " {";
461 LOGSO_FUNC_LINE(INFO) << space << " \"" <<
462 it.familyName << "\" : " << it.weight;
463 LOGSO_FUNC_LINE(INFO) << " },";
464 }
465 LOGSO_FUNC_LINE(INFO) << " ],";
466 }
467 }
468
DumpAjdust(const AdjustSet & adjustSet) const469 void FontConfigJson::DumpAjdust(const AdjustSet &adjustSet) const
470 {
471 if (!adjustSet.empty()) {
472 LOGSO_FUNC_LINE(INFO) << " \"adjust\": [";
473 const std::string space = " ";
474 for (auto it : adjustSet) {
475 LOGSO_FUNC_LINE(INFO) << " {";
476 LOGSO_FUNC_LINE(INFO) << space << " \"weght\" :" <<
477 it.origValue << " , " << "\"to\" :" << it.newValue;
478 LOGSO_FUNC_LINE(INFO) << " },";
479 }
480 LOGSO_FUNC_LINE(INFO) << " ],";
481 }
482 }
483
DumpGeneric() const484 void FontConfigJson::DumpGeneric() const
485 {
486 LOGSO_FUNC_LINE(INFO) << "generic : [";
487 if (!fontPtr->genericSet.empty()) {
488 for (auto it : fontPtr->genericSet) {
489 LOGSO_FUNC_LINE(INFO) << " \"family\": [\""<< it.familyName << "\"],";
490 DumpAlias(it.aliasSet);
491 DumpAjdust(it.adjustSet);
492 }
493 }
494 LOGSO_FUNC_LINE(INFO) << "]";
495 }
496
DumpForbak() const497 void FontConfigJson::DumpForbak() const
498 {
499 if (!fontPtr->fallbackGroupSet.empty()) {
500 LOGSO_FUNC_LINE(INFO) << "\"fallback\": [";
501 LOGSO_FUNC_LINE(INFO) << "{";
502 for (auto group : fontPtr->fallbackGroupSet) {
503 LOGSO_FUNC_LINE(INFO) << " \"" << group.groupName << "\" : [";
504 if (group.fallbackInfoSet.empty()) continue;
505 const std::string space = " ";
506 for (auto it : group.fallbackInfoSet) {
507 LOGSO_FUNC_LINE(INFO) << " {";
508 LOGSO_FUNC_LINE(INFO) << space << it.font << "\" : \""
509 << it.familyName << "\"";
510 LOGSO_FUNC_LINE(INFO) << " },";
511 }
512 LOGSO_FUNC_LINE(INFO) << " ]";
513 }
514 LOGSO_FUNC_LINE(INFO) << "}";
515 LOGSO_FUNC_LINE(INFO) << "]";
516 }
517 }
518
DumpFontDir() const519 void FontConfigJson::DumpFontDir() const
520 {
521 LOGSO_FUNC_LINE(INFO) << "fontdir : [";
522 if (!fontPtr->fontDirSet.empty()) {
523 for (auto it : fontPtr->fontDirSet) {
524 LOGSO_FUNC_LINE(INFO) << "\""<< it << "\",";
525 }
526 }
527 LOGSO_FUNC_LINE(INFO) << "]";
528 }
529
DumpFontFileMap() const530 void FontConfigJson::DumpFontFileMap() const
531 {
532 for (auto it : (*fontFileMap)) {
533 LOGSO_FUNC_LINE(INFO) << "\"" << it.first << "\": \""
534 << it.second << "\"";
535 }
536 }
537
Dump() const538 void FontConfigJson::Dump() const
539 {
540 if (fontPtr != nullptr) {
541 LOGSO_FUNC_LINE(INFO) << "font config dump fontPtr in";
542 DumpFontDir();
543 DumpGeneric();
544 DumpForbak();
545 LOGSO_FUNC_LINE(INFO) << "font config dump fontPtr out";
546 }
547 if (fontFileMap != nullptr) {
548 LOGSO_FUNC_LINE(INFO) << "font config dump fontFileMap in";
549 DumpFontFileMap();
550 LOGSO_FUNC_LINE(INFO) << "font config dump fontFileMap out";
551 }
552 }
553 } // namespace TextEngine
554 } // namespace Rosen
555 } // namespace OHOS
556