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 "utils/text_log.h"
31
32 namespace OHOS {
33 namespace Rosen {
34 namespace TextEngine {
35 #define SUCCESSED 0
36 #define FAILED 1
37 #define GENERIC_FONT 0
38
39 const char* FONT_DEFAULT_CONFIG = "/system/etc/fontconfig_ohos.json";
40 const char* FONT_FILE_MAP_CONFIG = "/system/etc/font_file_map.json";
41
FontConfig(const char * fname)42 FontConfig::FontConfig(const char* fname)
43 {
44 int err = ParseConfig(fname);
45 if (err != 0) {
46 TEXT_LOGE_LIMIT3_MIN("Failed to parse config, ret %{public}d", err);
47 }
48 }
49
GetFileData(const char * fname,int & size)50 char* FontConfig::GetFileData(const char* fname, int& size)
51 {
52 char realPath[PATH_MAX] = {0};
53 #ifdef _WIN32
54 if (fname == nullptr || _fullpath(realPath, fname, PATH_MAX) == nullptr) {
55 TEXT_LOGE_LIMIT3_MIN("Path or realPath is nullptr");
56 return nullptr;
57 }
58 #else
59 if (fname == nullptr || realpath(fname, realPath) == nullptr) {
60 TEXT_LOGE_LIMIT3_MIN("Path or realPath is nullptr");
61 return nullptr;
62 }
63 #endif
64 std::ifstream file(realPath);
65 if (file.good()) {
66 FILE* fp = fopen(realPath, "r");
67 if (fp == nullptr) {
68 return nullptr;
69 }
70 fseek(fp, 0L, SEEK_END);
71 size = ftell(fp) + 1;
72 rewind(fp);
73 char* data = static_cast<char*>(malloc(size));
74 if (data == nullptr) {
75 fclose(fp);
76 return nullptr;
77 }
78 #ifdef BUILD_NON_SDK_VER
79 if (memset_s(data, size, 0, size) != EOK) {
80 TEXT_LOGE_LIMIT3_MIN("Failed to memset");
81 free(data);
82 data = nullptr;
83 fclose(fp);
84 return nullptr;
85 }
86 #else
87 memset(data, 0, size);
88 #endif
89 (void)fread(data, size, 1, fp);
90 fclose(fp);
91 return data;
92 }
93
94 return nullptr;
95 }
96
CheckConfigFile(const char * fname)97 cJSON* FontConfig::CheckConfigFile(const char* fname)
98 {
99 int size = 0;
100 char* data = GetFileData(fname, size);
101 if (data == nullptr) {
102 TEXT_LOGE_LIMIT3_MIN("Null data");
103 return nullptr;
104 }
105 cJSON_Minify(data);
106 cJSON* res = cJSON_Parse(data);
107 free(data);
108 return res;
109 }
110
ParseFont(const cJSON * root)111 int FontConfig::ParseFont(const cJSON* root)
112 {
113 const char* tag = "font";
114 cJSON* filters = cJSON_GetObjectItem(root, tag);
115 if (filters == nullptr) {
116 TEXT_LOGE("Failed to parse font");
117 return FAILED;
118 }
119 int size = cJSON_GetArraySize(filters);
120 for (int i = 0; i < size;i++) {
121 cJSON* item = cJSON_GetArrayItem(filters, i);
122 if (cJSON_IsString(item)) {
123 fontSet_.emplace_back(rootPath_ + std::string(item->valuestring));
124 }
125 }
126 return SUCCESSED;
127 }
128
ParseConfig(const char * fname)129 int FontConfig::ParseConfig(const char* fname)
130 {
131 if (fname == nullptr) {
132 TEXT_LOGE_LIMIT3_MIN("File name is null");
133 return FAILED;
134 }
135
136 std::string rootPath(fname);
137 size_t idx = rootPath.rfind('/');
138 if (idx == 0 || idx == std::string::npos) {
139 TEXT_LOGE_LIMIT3_MIN("File name is illegal");
140 return FAILED;
141 }
142 rootPath_.assign(rootPath.substr(0, idx) + "/");
143 cJSON* root = CheckConfigFile(fname);
144 if (root == nullptr) {
145 TEXT_LOGE_LIMIT3_MIN("Failed to check config file");
146 return FAILED;
147 }
148 int result = ParseFont(root);
149 cJSON_Delete(root);
150 return result;
151 }
152
Dump() const153 void FontConfig::Dump() const
154 {
155 for (auto it : fontSet_) {
156 TEXT_LOGI("File name: %{public}s", it.c_str());
157 }
158 }
159
GetFontSet() const160 std::vector<std::string> FontConfig::GetFontSet() const
161 {
162 return fontSet_;
163 }
164
165
ParseFile(const char * fname)166 int FontConfigJson::ParseFile(const char* fname)
167 {
168 if (fname == nullptr) {
169 TEXT_LOGD("Null file name");
170 fname = FONT_DEFAULT_CONFIG;
171 }
172
173 TEXT_LOGI("ParseFile fname is: %{public}s", fname);
174 fontPtr = std::make_shared<FontConfigJsonInfo>();
175 indexMap = std::make_shared<std::unordered_map<std::string, size_t>>();
176 fontPtr->fallbackGroupSet.emplace_back();
177 fontPtr->fallbackGroupSet[0].groupName = "";
178 int err = ParseConfigList(fname);
179 // only for compatible with old version
180 fontPtr->genericSet[0].adjustSet = { { 50, 100 }, { 80, 400 }, { 100, 700 }, { 200, 900 } };
181 if (err != 0) {
182 TEXT_LOGE("Failed to ParseFile ParseConfigList");
183 return err;
184 }
185 return SUCCESSED;
186 }
187
ParseFontFileMap(const char * fname)188 int FontConfigJson::ParseFontFileMap(const char* fname)
189 {
190 if (fname == nullptr) {
191 TEXT_LOGD("Null file name");
192 fname = FONT_FILE_MAP_CONFIG;
193 }
194
195 TEXT_LOGI_LIMIT3_MIN("File name: %{public}s", fname);
196 fontFileMap = std::make_shared<FontFileMap>();
197 int err = ParseConfigListPath(fname);
198 if (err != 0) {
199 TEXT_LOGE_LIMIT3_MIN("Failed to parse config path, ret %{public}d", err);
200 return err;
201 }
202 return SUCCESSED;
203 }
204
EmplaceFontJson(const FontJson & fontJson)205 void FontConfigJson::EmplaceFontJson(const FontJson& fontJson)
206 {
207 if (fontPtr == nullptr) {
208 return;
209 }
210
211 if (fontJson.type == GENERIC_FONT) {
212 auto exist = indexMap->find(fontJson.family);
213 if (exist == indexMap->end()) {
214 (*indexMap)[fontJson.family] = fontPtr->genericSet.size();
215 fontPtr->genericSet.emplace_back(FontGenericInfo { fontJson.family });
216 fontPtr->genericSet.back().aliasSet.emplace_back(AliasInfo { fontJson.alias, fontJson.weight });
217 return;
218 }
219 auto& aliasSet = fontPtr->genericSet[exist->second].aliasSet;
220 auto existAlias = std::find_if(aliasSet.begin(), aliasSet.end(),
221 [&fontJson](const AliasInfo& aliasInfo) { return aliasInfo.familyName == fontJson.alias; });
222 if (existAlias == aliasSet.end()) {
223 fontPtr->genericSet[exist->second].aliasSet.emplace_back(AliasInfo { fontJson.alias, fontJson.weight });
224 }
225 return;
226 }
227 fontPtr->fallbackGroupSet[0].fallbackInfoSet.emplace_back(FallbackInfo { fontJson.family, fontJson.lang });
228 }
229
ParseDir(const cJSON * root)230 int FontConfigJson::ParseDir(const cJSON* root)
231 {
232 if (fontPtr == nullptr) {
233 return FAILED;
234 }
235 int size = cJSON_GetArraySize(root);
236 for (int i = 0; i < size; i++) {
237 cJSON* item = cJSON_GetArrayItem(root, i);
238 if (cJSON_IsString(item)) {
239 fontPtr->fontDirSet.emplace_back(std::string(item->valuestring));
240 }
241 }
242 return SUCCESSED;
243 }
244
AnalyseFont(const cJSON * root)245 void FontConfigJson::AnalyseFont(const cJSON* root)
246 {
247 cJSON* item = root->child;
248
249 FontJson fontJson;
250 while (item != nullptr) {
251 if (strcmp(item->string, "type") == 0 && cJSON_IsNumber(item)) {
252 fontJson.type = item->valueint;
253 } else if (strcmp(item->string, "alias") == 0 && cJSON_IsString(item)) {
254 fontJson.alias = item->valuestring;
255 } else if (strcmp(item->string, "family") == 0 && cJSON_IsString(item)) {
256 fontJson.family = item->valuestring;
257 } else if (strcmp(item->string, "weight") == 0 && cJSON_IsNumber(item)) {
258 fontJson.weight = item->valueint;
259 } else if (strcmp(item->string, "lang") == 0 && cJSON_IsString(item)) {
260 fontJson.lang = item->valuestring;
261 }
262 item = item->next;
263 }
264 EmplaceFontJson(fontJson);
265 }
266
ParseFonts(const cJSON * root)267 int FontConfigJson::ParseFonts(const cJSON* root)
268 {
269 if (root == nullptr) {
270 TEXT_LOGE("Failed to parse fonts");
271 return FAILED;
272 }
273 if (cJSON_IsArray(root)) {
274 int fontsSize = cJSON_GetArraySize(root);
275 for (int i = 0; i < fontsSize; i++) {
276 cJSON* item = cJSON_GetArrayItem(root, i);
277 if (cJSON_IsObject(item)) {
278 AnalyseFont(item);
279 }
280 }
281 }
282 return SUCCESSED;
283 }
284
ParseConfigList(const char * fname)285 int FontConfigJson::ParseConfigList(const char* fname)
286 {
287 if (fname == nullptr) {
288 TEXT_LOGE("Null file name");
289 return FAILED;
290 }
291 cJSON* root = CheckConfigFile(fname);
292 if (root == nullptr) {
293 TEXT_LOGE("Illegal file name %{public}s", fname);
294 return FAILED;
295 }
296 // "font_dir", "fonts" - font attribute
297 cJSON* item = root->child;
298 while (item != nullptr) {
299 if (strcmp(item->string, "font_dir") == 0) {
300 ParseDir(item);
301 } else if (strcmp(item->string, "fonts") == 0) {
302 ParseFonts(item);
303 }
304 item = item->next;
305 }
306 cJSON_Delete(root);
307 return SUCCESSED;
308 }
309
ParseConfigListPath(const char * fname)310 int FontConfigJson::ParseConfigListPath(const char* fname)
311 {
312 if (fname == nullptr) {
313 TEXT_LOGE("Null file name");
314 return FAILED;
315 }
316 cJSON* root = CheckConfigFile(fname);
317 if (root == nullptr) {
318 TEXT_LOGE("Illegal file name %{public}s", fname);
319 return FAILED;
320 }
321 ParseFontMap(root, "font_file_map");
322 cJSON_Delete(root);
323 return SUCCESSED;
324 }
325
ParseFontMap(const cJSON * root,const char * key)326 int FontConfigJson::ParseFontMap(const cJSON* root, const char* key)
327 {
328 if (root == nullptr) {
329 TEXT_LOGE("Null root");
330 return FAILED;
331 }
332 cJSON* filters = cJSON_GetObjectItem(root, key);
333 if (filters == nullptr || !cJSON_IsObject(filters)) {
334 TEXT_LOGE("Failed to get object");
335 return FAILED;
336 }
337 cJSON* item = filters->child;
338 while (item != nullptr) {
339 (*fontFileMap)[item->string] = item->valuestring;
340 item = item->next;
341 }
342 return SUCCESSED;
343 }
344
ParseInstallFont(const cJSON * root,std::vector<std::string> & fontPathList)345 int FontConfigJson::ParseInstallFont(const cJSON* root, std::vector<std::string>& fontPathList)
346 {
347 cJSON* rootObj = cJSON_GetObjectItem(root, "fontlist");
348 if (rootObj == nullptr) {
349 TEXT_LOGE("Failed to get json object");
350 return FAILED;
351 }
352 int size = cJSON_GetArraySize(rootObj);
353 if (size <= 0) {
354 TEXT_LOGE("Failed to get json array size");
355 return FAILED;
356 }
357 fontPathList.reserve(size);
358 for (int i = 0; i < size; i++) {
359 cJSON* item = cJSON_GetArrayItem(rootObj, i);
360 if (item == nullptr) {
361 TEXT_LOGE("Failed to get json item");
362 return FAILED;
363 }
364 cJSON* fullPath = cJSON_GetObjectItem(item, "fontfullpath");
365 if (!cJSON_IsString(fullPath)) {
366 TEXT_LOGE("Failed to get fullPath");
367 return FAILED;
368 }
369 fontPathList.emplace_back(std::string(fullPath->valuestring));
370 }
371 return SUCCESSED;
372 }
373
ParseFullName(const cJSON * root,std::vector<std::string> & fullNameList)374 void FontConfigJson::ParseFullName(const cJSON* root, std::vector<std::string>& fullNameList)
375 {
376 cJSON* fullname = cJSON_GetObjectItem(root, "fullname");
377 if (!cJSON_IsArray(fullname)) {
378 TEXT_LOGE("Failed to get key: fullname");
379 return;
380 }
381 int size = cJSON_GetArraySize(fullname);
382 for (int i = 0; i < size; i += 1) {
383 cJSON* item = cJSON_GetArrayItem(fullname, i);
384 if (!cJSON_IsString(item)) {
385 continue;
386 }
387 fullNameList.emplace_back(item->valuestring);
388 }
389 }
390
ParseInstallFont(const cJSON * root,FullNameToPath & fontPathList)391 int FontConfigJson::ParseInstallFont(const cJSON* root, FullNameToPath& fontPathList)
392 {
393 const char* tag = "fontlist";
394 cJSON* rootObj = cJSON_GetObjectItem(root, tag);
395 if (rootObj == nullptr) {
396 TEXT_LOGE("Failed to get json object");
397 return FAILED;
398 }
399 int size = cJSON_GetArraySize(rootObj);
400 if (size <= 0) {
401 TEXT_LOGE("Failed to get json array size");
402 return FAILED;
403 }
404 fontPathList.reserve(size);
405 for (int i = 0; i < size; i++) {
406 cJSON* item = cJSON_GetArrayItem(rootObj, i);
407 if (item == nullptr) {
408 TEXT_LOGE("Failed to get json item");
409 return FAILED;
410 }
411 cJSON* fullPath = cJSON_GetObjectItem(item, "fontfullpath");
412 if (fullPath == nullptr || !cJSON_IsString(fullPath) || fullPath->valuestring == nullptr) {
413 TEXT_LOGE("Failed to get fullPath");
414 return FAILED;
415 }
416 std::vector<std::string> fullNameList;
417 ParseFullName(item, fullNameList);
418 for (size_t i = 0; i < fullNameList.size(); i += 1) {
419 fontPathList.emplace(fullNameList[i], std::make_pair(i, fullPath->valuestring));
420 }
421 }
422 return SUCCESSED;
423 }
424
425 template<typename T>
ParseInstallConfig(const char * fontPath,T & fontPathList)426 int FontConfigJson::ParseInstallConfig(const char* fontPath, T& fontPathList)
427 {
428 if (fontPath == nullptr) {
429 TEXT_LOGE("Null font path");
430 return FAILED;
431 }
432
433 cJSON* root = CheckConfigFile(fontPath);
434 if (root == nullptr) {
435 TEXT_LOGE("Failed to check config file");
436 return FAILED;
437 }
438 if (ParseInstallFont(root, fontPathList) != SUCCESSED) {
439 cJSON_Delete(root);
440 return FAILED;
441 }
442 cJSON_Delete(root);
443 return SUCCESSED;
444 }
445
446 template int FontConfigJson::ParseInstallConfig(const char* fontPath, FullNameToPath& fontPathList);
447 template int FontConfigJson::ParseInstallConfig(const char* fontPath, std::vector<std::string>& fontPathList);
448
DumpAlias(const AliasSet & aliasSet) const449 void FontConfigJson::DumpAlias(const AliasSet& aliasSet) const
450 {
451 if (!aliasSet.empty()) {
452 const char* space = " ";
453 TEXT_LOGI(" \"alias\": [");
454 for (auto it : aliasSet) {
455 TEXT_LOGI(" {");
456 TEXT_LOGI("%{public}s \"%{public}s\" : %{public}d", space, it.familyName.c_str(), it.weight);
457 TEXT_LOGI(" },");
458 }
459 TEXT_LOGI(" ],");
460 }
461 }
462
DumpAjdust(const AdjustSet & adjustSet) const463 void FontConfigJson::DumpAjdust(const AdjustSet& adjustSet) const
464 {
465 if (!adjustSet.empty()) {
466 TEXT_LOGI(" \"adjust\": [");
467 const char* space = " ";
468 for (auto it : adjustSet) {
469 TEXT_LOGI(" {");
470 TEXT_LOGI("%{public}s \"weght\" :%{public}d , \"to\" :%{public}d", space, it.origValue, it.newValue);
471 TEXT_LOGI(" },");
472 }
473 TEXT_LOGI(" ],");
474 }
475 }
476
DumpGeneric() const477 void FontConfigJson::DumpGeneric() const
478 {
479 TEXT_LOGI("Generic : [");
480 if (!fontPtr->genericSet.empty()) {
481 for (auto it : fontPtr->genericSet) {
482 TEXT_LOGI(" \"family\": [\"%{public}s\"],", it.familyName.c_str());
483 DumpAlias(it.aliasSet);
484 DumpAjdust(it.adjustSet);
485 }
486 }
487 TEXT_LOGI("]");
488 }
489
DumpForbak() const490 void FontConfigJson::DumpForbak() const
491 {
492 if (!fontPtr->fallbackGroupSet.empty()) {
493 TEXT_LOGI("\"fallback\": [");
494 TEXT_LOGI("{");
495 for (auto group : fontPtr->fallbackGroupSet) {
496 TEXT_LOGI(" \"%{public}s\" : [", group.groupName.c_str());
497 if (group.fallbackInfoSet.empty())
498 continue;
499 const char* space = " ";
500 for (auto it : group.fallbackInfoSet) {
501 TEXT_LOGI(" {");
502 TEXT_LOGI("%{public}s%{public}s\" : \"%{public}s\"", space, it.font.c_str(), it.familyName.c_str());
503 TEXT_LOGI(" },");
504 }
505 TEXT_LOGI(" ]");
506 }
507 TEXT_LOGI("}");
508 TEXT_LOGI("]");
509 }
510 }
511
DumpFontDir() const512 void FontConfigJson::DumpFontDir() const
513 {
514 TEXT_LOGI("Fontdir : [");
515 if (!fontPtr->fontDirSet.empty()) {
516 for (auto it : fontPtr->fontDirSet) {
517 TEXT_LOGI("\"%{public}s\",", it.c_str());
518 }
519 }
520 TEXT_LOGI("]");
521 }
522
DumpFontFileMap() const523 void FontConfigJson::DumpFontFileMap() const
524 {
525 for (auto it : (*fontFileMap)) {
526 TEXT_LOGI("\"%{public}s\": \"%{public}s\"", it.first.c_str(), it.second.c_str());
527 }
528 }
529
Dump() const530 void FontConfigJson::Dump() const
531 {
532 if (fontPtr != nullptr) {
533 TEXT_LOGI("Font config dump fontPtr in");
534 DumpFontDir();
535 DumpGeneric();
536 DumpForbak();
537 TEXT_LOGI("Font config dump fontPtr out");
538 }
539 if (fontFileMap != nullptr) {
540 TEXT_LOGI("Font config dump fontFileMap in");
541 DumpFontFileMap();
542 TEXT_LOGI("Font config dump fontFileMap out");
543 }
544 }
545 } // namespace TextEngine
546 } // namespace Rosen
547 } // namespace OHOS
548