• 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("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 #ifdef BUILD_NON_SDK_VER
53     char realPath[PATH_MAX] = {0};
54     if (fname == nullptr || realpath(fname, realPath) == NULL) {
55         TEXT_LOGE("Invalid parameter");
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             TEXT_LOGE("Failed to memset");
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 }
91 
CheckConfigFile(const char * fname) const92 cJSON* FontConfig::CheckConfigFile(const char* fname) const
93 {
94     int size = 0;
95     char* data = GetFileData(fname, size);
96     if (data == nullptr) {
97         TEXT_LOGE("Null data");
98         return nullptr;
99     }
100     cJSON_Minify(data);
101     cJSON* res = cJSON_Parse(data);
102     free(data);
103     return res;
104 }
105 
ParseFont(const cJSON * root)106 int FontConfig::ParseFont(const cJSON* root)
107 {
108     const char* tag = "font";
109     cJSON* filters = cJSON_GetObjectItem(root, tag);
110     if (filters == nullptr) {
111         TEXT_LOGE("Failed to parse font");
112         return FAILED;
113     }
114     int size = cJSON_GetArraySize(filters);
115     for (int i = 0; i < size;i++) {
116         cJSON* item = cJSON_GetArrayItem(filters, i);
117         if (cJSON_IsString(item)) {
118             fontSet_.emplace_back(rootPath_ + std::string(item->valuestring));
119         }
120     }
121     return SUCCESSED;
122 }
123 
ParseConfig(const char * fname)124 int FontConfig::ParseConfig(const char* fname)
125 {
126     if (fname == nullptr) {
127         TEXT_LOGE("File name is null");
128         return FAILED;
129     }
130 
131     std::string rootPath(fname);
132     size_t idx = rootPath.rfind('/');
133     if (idx == 0 || idx == std::string::npos) {
134         TEXT_LOGE("File name is illegal");
135         return FAILED;
136     }
137     rootPath_.assign(rootPath.substr(0, idx) + "/");
138     cJSON* root = CheckConfigFile(fname);
139     if (root == nullptr) {
140         TEXT_LOGE("Failed to check config file");
141         return FAILED;
142     }
143     int result = ParseFont(root);
144     cJSON_Delete(root);
145     return result;
146 }
147 
Dump() const148 void FontConfig::Dump() const
149 {
150     for (auto it : fontSet_) {
151         TEXT_LOGI("File name: %{public}s", it.c_str());
152     }
153 }
154 
GetFontSet() const155 std::vector<std::string> FontConfig::GetFontSet() const
156 {
157     return fontSet_;
158 }
159 
160 
ParseFile(const char * fname)161 int FontConfigJson::ParseFile(const char* fname)
162 {
163     if (fname == nullptr) {
164         TEXT_LOGD("Null file name");
165         fname = FONT_DEFAULT_CONFIG;
166     }
167 
168     TEXT_LOGI("ParseFile fname is: %{public}s", fname);
169     fontPtr = std::make_shared<FontConfigJsonInfo>();
170     indexMap = std::make_shared<std::unordered_map<std::string, size_t>>();
171     fontPtr->fallbackGroupSet.emplace_back();
172     fontPtr->fallbackGroupSet[0].groupName = "";
173     int err = ParseConfigList(fname);
174     // only for compatible with old version
175     fontPtr->genericSet[0].adjustSet = { { 50, 100 }, { 80, 400 }, { 100, 700 }, { 200, 900 } };
176     if (err != 0) {
177         TEXT_LOGE("Failed to ParseFile ParseConfigList");
178         return err;
179     }
180     return SUCCESSED;
181 }
182 
ParseFontFileMap(const char * fname)183 int FontConfigJson::ParseFontFileMap(const char* fname)
184 {
185     if (fname == nullptr) {
186         TEXT_LOGD("Null file name");
187         fname = FONT_FILE_MAP_CONFIG;
188     }
189 
190     TEXT_LOGI("File name: %{public}s", fname);
191     fontFileMap = std::make_shared<FontFileMap>();
192     int err = ParseConfigListPath(fname);
193     if (err != 0) {
194         TEXT_LOGE("Failed to parse config path, ret %{public}d", err);
195         return err;
196     }
197     return SUCCESSED;
198 }
199 
EmplaceFontJson(const FontJson & fontJson)200 void FontConfigJson::EmplaceFontJson(const FontJson& fontJson)
201 {
202     if (fontPtr == nullptr) {
203         return;
204     }
205 
206     if (fontJson.type == GENERIC_FONT) {
207         auto exist = indexMap->find(fontJson.family);
208         if (exist == indexMap->end()) {
209             (*indexMap)[fontJson.family] = fontPtr->genericSet.size();
210             fontPtr->genericSet.emplace_back(FontGenericInfo { fontJson.family });
211             fontPtr->genericSet.back().aliasSet.emplace_back(AliasInfo { fontJson.alias, fontJson.weight });
212             return;
213         }
214         auto& aliasSet = fontPtr->genericSet[exist->second].aliasSet;
215         auto existAlias = std::find_if(aliasSet.begin(), aliasSet.end(),
216             [&fontJson](const AliasInfo& aliasInfo) { return aliasInfo.familyName == fontJson.alias; });
217         if (existAlias == aliasSet.end()) {
218             fontPtr->genericSet[exist->second].aliasSet.emplace_back(AliasInfo { fontJson.alias, fontJson.weight });
219         }
220         return;
221     }
222     fontPtr->fallbackGroupSet[0].fallbackInfoSet.emplace_back(FallbackInfo { fontJson.family, fontJson.lang });
223 }
224 
ParseDir(const cJSON * root)225 int FontConfigJson::ParseDir(const cJSON* root)
226 {
227     if (fontPtr == nullptr) {
228         return FAILED;
229     }
230     int size = cJSON_GetArraySize(root);
231     for (int i = 0; i < size; i++) {
232         cJSON* item = cJSON_GetArrayItem(root, i);
233         if (cJSON_IsString(item)) {
234             fontPtr->fontDirSet.emplace_back(std::string(item->valuestring));
235         }
236     }
237     return SUCCESSED;
238 }
239 
AnalyseFont(const cJSON * root)240 void FontConfigJson::AnalyseFont(const cJSON* root)
241 {
242     cJSON* item = root->child;
243 
244     FontJson fontJson;
245     while (item != nullptr) {
246         if (strcmp(item->string, "type") == 0 && cJSON_IsNumber(item)) {
247             fontJson.type = item->valueint;
248         } else if (strcmp(item->string, "alias") == 0 && cJSON_IsString(item)) {
249             fontJson.alias = item->valuestring;
250         } else if (strcmp(item->string, "family") == 0 && cJSON_IsString(item)) {
251             fontJson.family = item->valuestring;
252         } else if (strcmp(item->string, "weight") == 0 && cJSON_IsNumber(item)) {
253             fontJson.weight = item->valueint;
254         } else if (strcmp(item->string, "lang") == 0  && cJSON_IsString(item)) {
255             fontJson.lang = item->valuestring;
256         }
257         item = item->next;
258     }
259     EmplaceFontJson(fontJson);
260 }
261 
ParseFonts(const cJSON * root)262 int FontConfigJson::ParseFonts(const cJSON* root)
263 {
264     if (root == nullptr) {
265         TEXT_LOGE("Failed to parse fonts");
266         return FAILED;
267     }
268     if (cJSON_IsArray(root)) {
269         int fontsSize = cJSON_GetArraySize(root);
270         for (int i = 0; i < fontsSize; i++) {
271             cJSON* item = cJSON_GetArrayItem(root, i);
272             if (cJSON_IsObject(item)) {
273                 AnalyseFont(item);
274             }
275         }
276     }
277     return SUCCESSED;
278 }
279 
ParseConfigList(const char * fname)280 int FontConfigJson::ParseConfigList(const char* fname)
281 {
282     if (fname == nullptr) {
283         TEXT_LOGE("Null file name");
284         return FAILED;
285     }
286     cJSON* root = CheckConfigFile(fname);
287     if (root == nullptr) {
288         TEXT_LOGE("Illegal file name %{public}s", fname);
289         return FAILED;
290     }
291     // "font_dir", "fonts" - font attribute
292     cJSON* item = root->child;
293     while (item != nullptr) {
294         if (strcmp(item->string, "font_dir") == 0) {
295             ParseDir(item);
296         } else if (strcmp(item->string, "fonts") == 0) {
297             ParseFonts(item);
298         }
299         item = item->next;
300     }
301     cJSON_Delete(root);
302     return SUCCESSED;
303 }
304 
ParseConfigListPath(const char * fname)305 int FontConfigJson::ParseConfigListPath(const char* fname)
306 {
307     if (fname == nullptr) {
308         TEXT_LOGE("Null file name");
309         return FAILED;
310     }
311     cJSON* root = CheckConfigFile(fname);
312     if (root == nullptr) {
313         TEXT_LOGE("Illegal file name %{public}s", fname);
314         return FAILED;
315     }
316     ParseFontMap(root, "font_file_map");
317     cJSON_Delete(root);
318     return SUCCESSED;
319 }
320 
ParseFontMap(const cJSON * root,const char * key)321 int FontConfigJson::ParseFontMap(const cJSON* root, const char* key)
322 {
323     if (root == nullptr) {
324         TEXT_LOGE("Null root");
325         return FAILED;
326     }
327     cJSON* filters = cJSON_GetObjectItem(root, key);
328     if (filters == nullptr || !cJSON_IsObject(filters)) {
329         TEXT_LOGE("Failed to get object");
330         return FAILED;
331     }
332     cJSON* item = filters->child;
333     while (item != nullptr) {
334         (*fontFileMap)[item->string] = item->valuestring;
335         item = item->next;
336     }
337     return SUCCESSED;
338 }
339 
ParseInstallFont(const cJSON * root,std::vector<std::string> & fontPathList)340 int FontConfigJson::ParseInstallFont(const cJSON* root, std::vector<std::string>& fontPathList)
341 {
342     const char* tag = "fontlist";
343     cJSON* rootObj = cJSON_GetObjectItem(root, tag);
344     if (rootObj == nullptr) {
345         TEXT_LOGE("Failed to get json object");
346         return FAILED;
347     }
348     int size = cJSON_GetArraySize(rootObj);
349     if (size <= 0) {
350         TEXT_LOGE("Failed to get json array size");
351         return FAILED;
352     }
353     fontPathList.reserve(size);
354     for (int i = 0; i < size; i++) {
355         cJSON* item = cJSON_GetArrayItem(rootObj, i);
356         if (item == nullptr) {
357             TEXT_LOGE("Failed to get json item");
358             return FAILED;
359         }
360         cJSON* fullPath = cJSON_GetObjectItem(item, "fontfullpath");
361         if (fullPath == nullptr || !cJSON_IsString(fullPath) || fullPath->valuestring == nullptr) {
362             TEXT_LOGE("Failed to get fullPath");
363             return FAILED;
364         }
365         fontPathList.emplace_back(std::string(fullPath->valuestring));
366     }
367     return SUCCESSED;
368 }
369 
ParseInstallConfig(const char * fontPath,std::vector<std::string> & fontPathList)370 int FontConfigJson::ParseInstallConfig(const char* fontPath, std::vector<std::string>& fontPathList)
371 {
372     if (fontPath == nullptr) {
373         TEXT_LOGE("Null font path");
374         return FAILED;
375     }
376 
377     cJSON* root = CheckConfigFile(fontPath);
378     if (root == nullptr) {
379         TEXT_LOGE("Failed to check config file");
380         return FAILED;
381     }
382     if (ParseInstallFont(root, fontPathList) != SUCCESSED) {
383         cJSON_Delete(root);
384         return FAILED;
385     }
386     cJSON_Delete(root);
387     return SUCCESSED;
388 }
389 
DumpAlias(const AliasSet & aliasSet) const390 void FontConfigJson::DumpAlias(const AliasSet& aliasSet) const
391 {
392     if (!aliasSet.empty()) {
393         const char* space = "    ";
394         TEXT_LOGI("  \"alias\": [");
395         for (auto it : aliasSet) {
396             TEXT_LOGI("  {");
397             TEXT_LOGI("%{public}s  \"%{public}s\" : %{public}d", space, it.familyName.c_str(), it.weight);
398             TEXT_LOGI("   },");
399         }
400         TEXT_LOGI("  ],");
401     }
402 }
403 
DumpAjdust(const AdjustSet & adjustSet) const404 void FontConfigJson::DumpAjdust(const AdjustSet& adjustSet) const
405 {
406     if (!adjustSet.empty()) {
407         TEXT_LOGI("  \"adjust\": [");
408         const char* space = "    ";
409         for (auto it : adjustSet) {
410             TEXT_LOGI("   {");
411             TEXT_LOGI("%{public}s  \"weght\" :%{public}d , \"to\" :%{public}d", space, it.origValue, it.newValue);
412             TEXT_LOGI("   },");
413         }
414         TEXT_LOGI("  ],");
415     }
416 }
417 
DumpGeneric() const418 void FontConfigJson::DumpGeneric() const
419 {
420     TEXT_LOGI("Generic : [");
421     if (!fontPtr->genericSet.empty()) {
422         for (auto it : fontPtr->genericSet) {
423             TEXT_LOGI("  \"family\": [\"%{public}s\"],", it.familyName.c_str());
424             DumpAlias(it.aliasSet);
425             DumpAjdust(it.adjustSet);
426         }
427     }
428     TEXT_LOGI("]");
429 }
430 
DumpForbak() const431 void FontConfigJson::DumpForbak() const
432 {
433     if (!fontPtr->fallbackGroupSet.empty()) {
434         TEXT_LOGI("\"fallback\": [");
435         TEXT_LOGI("{");
436         for (auto group : fontPtr->fallbackGroupSet) {
437             TEXT_LOGI(" \"%{public}s\" : [", group.groupName.c_str());
438             if (group.fallbackInfoSet.empty())
439                 continue;
440             const char* space = "    ";
441             for (auto it : group.fallbackInfoSet) {
442                 TEXT_LOGI("  {");
443                 TEXT_LOGI("%{public}s%{public}s\" : \"%{public}s\"", space, it.font.c_str(), it.familyName.c_str());
444                 TEXT_LOGI("   },");
445             }
446             TEXT_LOGI(" ]");
447         }
448         TEXT_LOGI("}");
449         TEXT_LOGI("]");
450     }
451 }
452 
DumpFontDir() const453 void FontConfigJson::DumpFontDir() const
454 {
455     TEXT_LOGI("Fontdir : [");
456     if (!fontPtr->fontDirSet.empty()) {
457         for (auto it : fontPtr->fontDirSet) {
458             TEXT_LOGI("\"%{public}s\",", it.c_str());
459         }
460     }
461     TEXT_LOGI("]");
462 }
463 
DumpFontFileMap() const464 void FontConfigJson::DumpFontFileMap() const
465 {
466     for (auto it : (*fontFileMap)) {
467         TEXT_LOGI("\"%{public}s\": \"%{public}s\"", it.first.c_str(), it.second.c_str());
468     }
469 }
470 
Dump() const471 void FontConfigJson::Dump() const
472 {
473     if (fontPtr != nullptr) {
474         TEXT_LOGI("Font config dump fontPtr in");
475         DumpFontDir();
476         DumpGeneric();
477         DumpForbak();
478         TEXT_LOGI("Font config dump fontPtr out");
479     }
480     if (fontFileMap != nullptr) {
481         TEXT_LOGI("Font config dump fontFileMap in");
482         DumpFontFileMap();
483         TEXT_LOGI("Font config dump fontFileMap out");
484     }
485 }
486 } // namespace TextEngine
487 } // namespace Rosen
488 } // namespace OHOS
489