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 <dirent.h>
19 #include <fstream>
20 #include <libgen.h>
21 #include <sys/stat.h>
22 #include <sys/types.h>
23 #include <unistd.h>
24
25 #ifdef BUILD_NON_SDK_VER
26 #include "securec.h"
27 #endif
28 #include "texgine/utils/exlog.h"
29
30 namespace OHOS {
31 namespace Rosen {
32 namespace TextEngine {
33 #define SUCCESSED 0
34 #define FAILED 1
35
36 const std::string DEFAULT_DIR = "/system/fonts/";
37 const std::string FONT_DEFAULT_CONFIG = "/system/etc/fontconfig.json";
38
FontConfig(const char * fname)39 FontConfig::FontConfig(const char* fname)
40 {
41 int err = ParseConfig(fname);
42 if (err != 0) {
43 LOGSO_FUNC_LINE(ERROR) << "parse config err";
44 }
45 }
46
GetFileData(const char * fname,int & size)47 char* FontConfig::GetFileData(const char* fname, int& size)
48 {
49 #ifdef BUILD_NON_SDK_VER
50 char realPath[PATH_MAX] = {0};
51 if (fname == nullptr || realpath(fname, realPath) == NULL) {
52 LOGSO_FUNC_LINE(ERROR) << "path or realPath is NULL";
53 return nullptr;
54 }
55 #endif
56 std::ifstream file(fname);
57 if (file.good()) {
58 FILE* fp = fopen(fname, "r");
59 if (fp == nullptr) {
60 return nullptr;
61 }
62 fseek(fp, 0L, SEEK_END);
63 size = ftell(fp) + 1;
64 rewind(fp);
65 char* data = static_cast<char*>(malloc(size));
66 if (data == nullptr) {
67 fclose(fp);
68 return nullptr;
69 }
70 #ifdef BUILD_NON_SDK_VER
71 if (memset_s(data, size, 0, size) != EOK) {
72 LOGSO_FUNC_LINE(ERROR) << "memset failed";
73 free(data);
74 data = nullptr;
75 fclose(fp);
76 return nullptr;
77 }
78 #else
79 memset(data, 0, size);
80 #endif
81 (void)fread(data, size, 1, fp);
82 fclose(fp);
83 return data;
84 }
85
86 return nullptr;
87 }
88
CheckConfigFile(const char * fname,Json::Value & root) const89 int FontConfig::CheckConfigFile(const char* fname, Json::Value& root) const
90 {
91 int size = 0;
92 char* data = GetFileData(fname, size);
93 if (data == nullptr) {
94 LOGSO_FUNC_LINE(ERROR) << "data is null";
95 return FAILED;
96 }
97 JSONCPP_STRING errs;
98 Json::CharReaderBuilder charReaderBuilder;
99 std::unique_ptr<Json::CharReader> jsonReader(charReaderBuilder.newCharReader());
100 bool isJson = jsonReader->parse(data, data + size, &root, &errs);
101 free(data);
102 data = nullptr;
103
104 if (!isJson || !errs.empty()) {
105 LOGSO_FUNC_LINE(ERROR) << "not json or errs no empty";
106 return FAILED;
107 }
108 return SUCCESSED;
109 }
110
ParseFont(const Json::Value & root)111 int FontConfig::ParseFont(const Json::Value& root)
112 {
113 for (unsigned int i = 0; i < root.size(); ++i) {
114 if (root[i].isString()) {
115 fontSet_.emplace_back(DEFAULT_DIR + root[i].asString());
116 }
117 }
118 return SUCCESSED;
119 }
120
ParseConfig(const char * fname)121 int FontConfig::ParseConfig(const char* fname)
122 {
123 if (fname == nullptr) {
124 LOGSO_FUNC_LINE(ERROR) << "fname is null";
125 return FAILED;
126 }
127 Json::Value root;
128 int err = CheckConfigFile(fname, root);
129 if (err != 0) {
130 LOGSO_FUNC_LINE(ERROR) << "check config file failed";
131 return err;
132 }
133 const char* tag = "font";
134 if (root.isMember(tag)) {
135 if (root[tag].isArray()) {
136 ParseFont(root[tag]);
137 } else {
138 LOGSO_FUNC_LINE(ERROR) << "not array";
139 return FAILED;
140 }
141 } else {
142 LOGSO_FUNC_LINE(ERROR) << "not member";
143 return FAILED;
144 }
145
146 return SUCCESSED;
147 }
148
Dump() const149 void FontConfig::Dump() const
150 {
151 for (auto it : fontSet_) {
152 LOGSO_FUNC_LINE(INFO) << "fname:" << it;
153 }
154 }
155
GetFontSet() const156 std::vector<std::string> FontConfig::GetFontSet() const
157 {
158 return fontSet_;
159 }
160
ParseFile(const char * fname)161 int FontConfigJson::ParseFile(const char* fname)
162 {
163 if (fname == nullptr) {
164 LOGSO_FUNC_LINE(DEBUG) << "ParseFile fname is nullptr";
165 fname = FONT_DEFAULT_CONFIG.c_str();
166 }
167
168 LOGSO_FUNC_LINE(INFO) << "ParseFile fname is: " << fname;
169 fontPtr = std::make_shared<FontConfigJsonInfo>();
170 int err = ParseConfigList(fname);
171 if (err != 0) {
172 LOGSO_FUNC_LINE(ERROR) << "ParseFile ParseConfigList failed";
173 return err;
174 }
175 return SUCCESSED;
176 }
177
ParseDir(const Json::Value & root)178 int FontConfigJson::ParseDir(const Json::Value &root)
179 {
180 // "fontdir" - directory
181 const char* key = "fontdir";
182 if (root.isMember(key)) {
183 if (root[key].isArray()) {
184 int ret = ParseFontDir(root[key]);
185 if (ret != SUCCESSED) {
186 LOGSO_FUNC_LINE(ERROR) << "ParseConfigList ParseFontDir failed";
187 return ret;
188 }
189 } else {
190 LOGSO_FUNC_LINE(ERROR) << "ParseConfigList root[fontdir] is not array";
191 return FAILED;
192 }
193 }
194 return SUCCESSED;
195 }
196
ParseConfigList(const char * fname)197 int FontConfigJson::ParseConfigList(const char* fname)
198 {
199 if (fname == nullptr) {
200 LOGSO_FUNC_LINE(ERROR) << "ParseConfigList fname is nullptr";
201 return FAILED;
202 }
203 Json::Value root;
204 int ret = CheckConfigFile(fname, root);
205 if (ret != SUCCESSED) {
206 LOGSO_FUNC_LINE(ERROR) << "ParseConfigList CheckConfigFile failed";
207 return ret;
208 }
209 ret = ParseDir(root);
210 if (ret) {
211 return ret;
212 }
213 // "generic", "fallback" - font attribute
214 const char* keys[] = {"generic", "fallback", nullptr};
215 int index = 0;
216 while (true) {
217 if (keys[index] == nullptr) {
218 break;
219 }
220 const char* key = keys[index++];
221 if (!root.isMember(key)) {
222 LOGSO_FUNC_LINE(ERROR) << "ParseConfigList root[" << key << "] is not member";
223 return FAILED;
224 } else if (!root[key].isArray()) {
225 LOGSO_FUNC_LINE(ERROR) << "ParseConfigList root[" << key << "] is not array";
226 return FAILED;
227 }
228 const Json::Value& arr = root[key];
229 for (unsigned int i = 0; i < arr.size(); i++) {
230 ret = ParseFallbakArr(arr, i, key);
231 if (ret != SUCCESSED) {
232 return ret;
233 }
234 }
235 }
236 root.clear();
237 return SUCCESSED;
238 }
239
ParseFallbakArr(const Json::Value & arr,int i,const char * key)240 int FontConfigJson::ParseFallbakArr(const Json::Value& arr, int i, const char* key)
241 {
242 int ret;
243 if (arr[i].isObject()) {
244 if (!strcmp(key, "generic")) {
245 if ((ret = ParseGeneric(arr[i])) != SUCCESSED) {
246 LOGSO_FUNC_LINE(ERROR) << "ParseConfigList ParseGeneric failed";
247 return ret;
248 }
249 } else if (!strcmp(key, "fallback")) {
250 if ((ret = ParseFallback(arr[i])) != SUCCESSED) {
251 LOGSO_FUNC_LINE(ERROR) << "ParseConfigList ParseFallback failed";
252 return ret;
253 }
254 }
255 } else {
256 LOGSO_FUNC_LINE(ERROR) << "ParseConfigList arr is not object :" << i;
257 return FAILED;
258 }
259 return SUCCESSED;
260 }
261
ParseFontDir(const Json::Value & root)262 int FontConfigJson::ParseFontDir(const Json::Value& root)
263 {
264 for (unsigned int i = 0; i < root.size(); i++) {
265 if (root[i].isString()) {
266 const char* dir = root[i].asCString();
267 fontPtr->fontDirSet.emplace_back(std::string(dir));
268 } else {
269 LOGSO_FUNC_LINE(ERROR) << "ParseFontDir root is not string :" << i;
270 return FAILED;
271 }
272 }
273 return SUCCESSED;
274 }
275
CheckGeneric(const Json::Value & root,const char * key)276 int FontConfigJson::CheckGeneric(const Json::Value& root, const char* key)
277 {
278 if (!root.isMember(key)) {
279 LOGSO_FUNC_LINE(ERROR) << "ParseGeneric root is not member";
280 return FAILED;
281 } else if (!root[key].isString()) {
282 LOGSO_FUNC_LINE(ERROR) << "ParseGeneric root is not string";
283 return FAILED;
284 }
285 // "alias" - necessary, the data type should be Array
286 if (!root.isMember("alias")) {
287 LOGSO_FUNC_LINE(ERROR) << "ParseGeneric root has no alias member";
288 return FAILED;
289 }
290 return SUCCESSED;
291 }
292
ParseAliasArr(const Json::Value & arr,const char * key,FontGenericInfo & genericInfo)293 int FontConfigJson::ParseAliasArr(const Json::Value& arr, const char* key,
294 FontGenericInfo &genericInfo)
295 {
296 for (unsigned int j = 0; j < arr.size(); j++) {
297 if (arr[j].isObject()) {
298 if (!strcmp(key, "alias")) {
299 ParseAlias(arr[j], genericInfo);
300 } else if (!strcmp(key, "adjust")) {
301 ParseAdjust(arr[j], genericInfo);
302 } else {
303 LOGSO_FUNC_LINE(ERROR) << "ParseGeneric not support";
304 }
305 } else {
306 LOGSO_FUNC_LINE(ERROR) << "ParseGeneric arr[j:" << j << "] is not object";
307 return FAILED;
308 }
309 }
310 return SUCCESSED;
311 }
312
ParseGeneric(const Json::Value & root)313 int FontConfigJson::ParseGeneric(const Json::Value& root)
314 {
315 // "family" - necessary, the data type should be String
316 const char* key = "family";
317 std::string familyName = root[key].asCString();
318 int ret = CheckGeneric(root, key);
319 if (ret) {
320 return ret;
321 }
322 // "adjust", "variation" - optional
323 std::vector<std::string> tags = {"alias", "adjust"};
324 unsigned int tagsCont = 2; // tags size
325 FontGenericInfo genericInfo;
326 genericInfo.familyName = familyName;
327 for (unsigned int i = 0; i < tags.size(); i++) {
328 key= tags[i].c_str();
329 if (!root.isMember(key)) {
330 continue;
331 }
332 if (root[key].isArray()) {
333 const Json::Value& arr = root[key];
334 ret = ParseAliasArr(arr, key, genericInfo);
335 if (ret) return ret;
336 } else {
337 LOGSO_FUNC_LINE(ERROR) << "ParseGeneric root[key:" << key << "] is not array";
338 return FAILED;
339 }
340 if (root.size() == tagsCont) {
341 break;
342 }
343 }
344 fontPtr->genericSet.push_back(genericInfo);
345 return SUCCESSED;
346 }
347
ParseAlias(const Json::Value & root,FontGenericInfo & genericInfo)348 int FontConfigJson::ParseAlias(const Json::Value& root, FontGenericInfo &genericInfo)
349 {
350 if (root.empty()) {
351 LOGSO_FUNC_LINE(ERROR) << "ParseAlias root is empty";
352 return FAILED;
353 }
354 Json::Value::Members members = root.getMemberNames();
355 const char* key = members[0].c_str();
356 if (!root[key].isInt()) {
357 LOGSO_FUNC_LINE(ERROR) << "ParseAlias root[key:" << key << "] is not int";
358 return FAILED;
359 }
360
361 std::string aliasName = std::string(key);
362 int weight = root[key].asInt();
363 AliasInfo info = {aliasName, weight};
364 genericInfo.aliasSet.emplace_back(std::move(info));
365 return SUCCESSED;
366 }
367
ParseAdjust(const Json::Value & root,FontGenericInfo & genericInfo)368 int FontConfigJson::ParseAdjust(const Json::Value& root, FontGenericInfo &genericInfo)
369 {
370 std::vector<std::string> tags = {"weight", "to"};
371 int values[2]; // value[0] - to save 'weight', value[1] - to save 'to'
372 for (unsigned int i = 0; i < tags.size(); i++) {
373 const char* key = tags[i].c_str();
374 if (!root.isMember(key)) {
375 LOGSO_FUNC_LINE(ERROR) << "ParseAdjust root[key:" << key << "] is not member";
376 return FAILED;
377 } else if (!root[key].isInt()) {
378 LOGSO_FUNC_LINE(ERROR) << "ParseAdjust root[key:" << key << "] is not int";
379 return FAILED;
380 } else {
381 values[i] = root[key].asInt();
382 }
383 }
384 AdjustInfo info = {values[0], values[1]};
385 genericInfo.adjustSet.emplace_back(std::move(info));
386 return SUCCESSED;
387 }
388
ParseFallback(const Json::Value & root)389 int FontConfigJson::ParseFallback(const Json::Value& root)
390 {
391 if (root.empty()) {
392 LOGSO_FUNC_LINE(ERROR) << "ParseFallback root is empty";
393 return FAILED;
394 }
395 Json::Value::Members members = root.getMemberNames();
396 const char* key = members[0].c_str();
397 if (!root[key].isArray()) {
398 LOGSO_FUNC_LINE(ERROR) << "ParseFallback root[key:" << key << "] is not array";
399 return FAILED;
400 }
401 FallbackGroup fallbackGroup;
402 fallbackGroup.groupName = std::string(key);
403 const Json::Value& fallbackArr = root[key];
404 for (unsigned int i = 0; i < fallbackArr.size(); i++) {
405 if (!fallbackArr[i].isObject()) {
406 continue;
407 }
408 FallbackInfo fallbackInfo;
409 ParseFallbackItem(fallbackArr[i], fallbackInfo);
410 fallbackGroup.fallbackInfoSet.emplace_back(std::move(fallbackInfo));
411 }
412 fontPtr->fallbackGroupSet.emplace_back(std::move(fallbackGroup));
413 return SUCCESSED;
414 }
415
ParseFallbackItem(const Json::Value & root,FallbackInfo & fallbackInfo)416 int FontConfigJson::ParseFallbackItem(const Json::Value& root, FallbackInfo &fallbackInfo)
417 {
418 if (root.empty()) {
419 LOGSO_FUNC_LINE(ERROR) << "ParseFallbackItem root is empty";
420 return FAILED;
421 }
422 Json::Value::Members members = root.getMemberNames();
423 const char* key = nullptr;
424 for (unsigned int i = 0; i < members.size(); i++) {
425 if (members[i] != "variations" && members[i] != "index") {
426 key = members[i].c_str();
427 } else continue;
428 }
429 if (key == nullptr) {
430 LOGSO_FUNC_LINE(ERROR) << "ParseFallbackItem key is nullptr";
431 return FAILED;
432 }
433 if (!root[key].isString()) {
434 LOGSO_FUNC_LINE(ERROR) << "ParseFallbackItem root[key:" << key << "] is not string";
435 return FAILED;
436 }
437 std::string lang = std::string(key);
438 std::string familyName = root[key].asCString();
439
440 fallbackInfo.familyName = familyName;
441 fallbackInfo.font = lang;
442 return SUCCESSED;
443 }
444
DumpAlias(const AliasSet & aliasSet) const445 void FontConfigJson::DumpAlias(const AliasSet &aliasSet) const
446 {
447 if (!aliasSet.empty()) {
448 const std::string space = " ";
449 LOGSO_FUNC_LINE(INFO) << " \"alias\": [";
450 for (auto it : aliasSet) {
451 LOGSO_FUNC_LINE(INFO) << " {";
452 LOGSO_FUNC_LINE(INFO) << space << " \"" <<
453 it.familyName << "\" : " << it.weight;
454 LOGSO_FUNC_LINE(INFO) << " },";
455 }
456 LOGSO_FUNC_LINE(INFO) << " ],";
457 }
458 }
459
DumpAjdust(const AdjustSet & adjustSet) const460 void FontConfigJson::DumpAjdust(const AdjustSet &adjustSet) const
461 {
462 if (!adjustSet.empty()) {
463 LOGSO_FUNC_LINE(INFO) << " \"adjust\": [";
464 const std::string space = " ";
465 for (auto it : adjustSet) {
466 LOGSO_FUNC_LINE(INFO) << " {";
467 LOGSO_FUNC_LINE(INFO) << space << " \"weght\" :" <<
468 it.origValue << " , " << "\"to\" :" << it.newValue;
469 LOGSO_FUNC_LINE(INFO) << " },";
470 }
471 LOGSO_FUNC_LINE(INFO) << " ],";
472 }
473 }
474
DumpGeneric() const475 void FontConfigJson::DumpGeneric() const
476 {
477 LOGSO_FUNC_LINE(INFO) << "generic : [";
478 if (!fontPtr->genericSet.empty()) {
479 for (auto it : fontPtr->genericSet) {
480 LOGSO_FUNC_LINE(INFO) << " \"family\": [\""<< it.familyName << "\"],";
481 DumpAlias(it.aliasSet);
482 DumpAjdust(it.adjustSet);
483 }
484 }
485 LOGSO_FUNC_LINE(INFO) << "]";
486 }
487
DumpForbak() const488 void FontConfigJson::DumpForbak() const
489 {
490 if (!fontPtr->fallbackGroupSet.empty()) {
491 LOGSO_FUNC_LINE(INFO) << "\"fallback\": [";
492 LOGSO_FUNC_LINE(INFO) << "{";
493 for (auto group : fontPtr->fallbackGroupSet) {
494 LOGSO_FUNC_LINE(INFO) << " \"" << group.groupName << "\" : [";
495 if (group.fallbackInfoSet.empty()) continue;
496 const std::string space = " ";
497 for (auto it : group.fallbackInfoSet) {
498 LOGSO_FUNC_LINE(INFO) << " {";
499 LOGSO_FUNC_LINE(INFO) << space << it.font << "\" : \""
500 << it.familyName << "\"";
501 LOGSO_FUNC_LINE(INFO) << " },";
502 }
503 LOGSO_FUNC_LINE(INFO) << " ]";
504 }
505 LOGSO_FUNC_LINE(INFO) << "}";
506 LOGSO_FUNC_LINE(INFO) << "]";
507 }
508 }
509
DumpFontDir() const510 void FontConfigJson::DumpFontDir() const
511 {
512 LOGSO_FUNC_LINE(INFO) << "fontdir : [";
513 if (!fontPtr->fontDirSet.empty()) {
514 for (auto it : fontPtr->fontDirSet) {
515 LOGSO_FUNC_LINE(INFO) << "\""<< it << "\",";
516 }
517 }
518 LOGSO_FUNC_LINE(INFO) << "]";
519 }
520
Dump() const521 void FontConfigJson::Dump() const
522 {
523 LOGSO_FUNC_LINE(INFO) << "font config dump in";
524 if (fontPtr == nullptr) {
525 LOGSO_FUNC_LINE(INFO) << "adapter is nullptr";
526 return;
527 }
528
529 DumpFontDir();
530 DumpGeneric();
531 DumpForbak();
532
533 LOGSO_FUNC_LINE(INFO) << "dump out";
534 }
535 } // namespace TextEngine
536 } // namespace Rosen
537 } // namespace OHOS