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