• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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