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