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_parser.h"
17 
18 #include <codecvt>
19 #include <dirent.h>
20 #include <map>
21 #include <list>
22 #include <algorithm>
23 #include <iostream>
24 #include <iomanip>
25 #include <securec.h>
26 #ifdef BUILD_NON_SDK_VER
27 #include <iconv.h>
28 #endif
29 
30 #include "font_config.h"
31 #include "texgine/utils/exlog.h"
32 
33 namespace OHOS {
34 namespace Rosen {
35 namespace TextEngine {
36 #define SUCCESSED 0
37 #define FAILED 1
38 
39 #define FONT_CONFIG_FILE  "/system/fonts/visibility_list.json"
40 #define FONT_CONFIG_PROD_FILE "/sys_prod/fonts/visibility_list.json"
41 #define SYSTEM_FONT_PATH "/system/fonts/"
42 #define SYS_PROD_FONT_PATH "/sys_prod/fonts/"
43 
44 #define HALF(a) ((a) / 2)
45 
FontParser()46 FontParser::FontParser()
47 {
48     data_ = nullptr;
49     length_ = 0;
50     fontSet_.clear();
51     FontConfig fontConfig(FONT_CONFIG_FILE);
52     auto fonts = fontConfig.GetFontSet();
53     fontSet_.insert(fontSet_.end(), fonts.begin(), fonts.end());
54     FontConfig fontProdConfig(FONT_CONFIG_PROD_FILE);
55     auto prodFonts = fontProdConfig.GetFontSet();
56     fontSet_.insert(fontSet_.end(), prodFonts.begin(), prodFonts.end());
57 }
58 
ProcessCmapTable(const struct CmapTables * cmapTable,FontParser::FontDescriptor & fontDescriptor)59 void FontParser::ProcessCmapTable(const struct CmapTables* cmapTable, FontParser::FontDescriptor& fontDescriptor)
60 {
61     for (auto i = 0; i < cmapTable->numTables.Get(); ++i) {
62         const auto& record = cmapTable->encodingRecords[i];
63         FontParser::PlatformId platformId = static_cast<FontParser::PlatformId>(record.platformID.Get());
64         FontParser::EncodingIdWin encodingId = static_cast<FontParser::EncodingIdWin>(record.encodingID.Get());
65         if (platformId == FontParser::PlatformId::WINDOWS && encodingId == FontParser::EncodingIdWin::SYMBOL) {
66             fontDescriptor.symbolic = true;
67             break;
68         }
69     }
70 }
71 
GetStringFromNameId(FontParser::NameId nameId,unsigned int languageId,const std::string & nameString,FontParser::FontDescriptor & fontDescriptor)72 void FontParser::GetStringFromNameId(FontParser::NameId nameId, unsigned int languageId, const std::string& nameString,
73     FontParser::FontDescriptor& fontDescriptor)
74 {
75     switch (nameId) {
76         case FontParser::NameId::FONT_FAMILY: {
77             SetNameString(fontDescriptor, fontDescriptor.fontFamily, fontDescriptor.fontFamilyLid,
78                 languageId, nameString);
79             break;
80         }
81         case FontParser::NameId::FONT_SUBFAMILY: {
82             SetNameString(fontDescriptor, fontDescriptor.fontSubfamily, fontDescriptor.fontSubfamilyLid,
83                 languageId, nameString);
84             break;
85         }
86         case FontParser::NameId::FULL_NAME: {
87             if (!fontDescriptor.requestedFullname.empty() &&
88                 fontDescriptor.fullName == fontDescriptor.requestedFullname) {
89                 break;
90             }
91 
92             SetNameString(fontDescriptor, fontDescriptor.fullName, fontDescriptor.fullNameLid,
93                 languageId, nameString);
94             break;
95         }
96         case FontParser::NameId::POSTSCRIPT_NAME: {
97             SetNameString(fontDescriptor, fontDescriptor.postScriptName, fontDescriptor.postScriptNameLid,
98                 languageId, nameString);
99             break;
100         }
101         default: {
102             break;
103         }
104     }
105 }
106 
SetNameString(FontParser::FontDescriptor & fontDescriptor,std::string & field,unsigned int & fieldLid,unsigned int languageId,const std::string & nameString)107 void FontParser::SetNameString(FontParser::FontDescriptor& fontDescriptor, std::string& field, unsigned int& fieldLid,
108     unsigned int languageId, const std::string& nameString)
109 {
110     bool willSet = field.empty();
111     if (!willSet) {
112         willSet = languageId == fontDescriptor.requestedLid ||
113                   (fieldLid != fontDescriptor.requestedLid && languageId == LANGUAGE_DEFAULT);
114     }
115 
116     if (willSet) {
117         fieldLid = languageId;
118         field = nameString;
119     }
120 }
121 
ProcessNameTable(const struct NameTable * nameTable,FontParser::FontDescriptor & fontDescriptor) const122 int FontParser::ProcessNameTable(const struct NameTable* nameTable, FontParser::FontDescriptor& fontDescriptor) const
123 {
124     auto count = nameTable->count.Get();
125     auto storageOffset = nameTable->storageOffset.Get();
126     auto stringStorage = data_ + storageOffset;
127     for (int i = 0; i < count; ++i) {
128         if (nameTable->nameRecord[i].stringOffset.Get() == 0 && nameTable->nameRecord[i].length.Get() == 0) {
129             continue;
130         }
131         FontParser::NameId nameId = static_cast<FontParser::NameId>(nameTable->nameRecord[i].nameId.Get());
132         // Parsing fields with NameId greater than 7 is not currently supported.
133         if (nameId > FontParser::NameId::TRADEMARK) {
134             continue;
135         }
136         unsigned int languageId = static_cast<unsigned int>(nameTable->nameRecord[i].languageId.Get());
137         FontParser::PlatformId platformId =
138             static_cast<FontParser::PlatformId>(nameTable->nameRecord[i].platformId.Get());
139         auto len = nameTable->nameRecord[i].length.Get();
140         auto stringOffset = nameTable->nameRecord[i].stringOffset.Get();
141         const char* data = stringStorage + stringOffset;
142         if (platformId == FontParser::PlatformId::MACINTOSH) {
143 #ifdef BUILD_NON_SDK_VER
144             std::string nameString = ConvertToString(std::string(data, len), "GB2312", "UTF-8");
145 #else
146             std::string nameString(data, len);
147 #endif
148             GetStringFromNameId(nameId, languageId, nameString, fontDescriptor);
149         } else if (platformId == FontParser::PlatformId::WINDOWS) {
150 #ifdef BUILD_NON_SDK_VER
151             std::string nameString = ConvertToString(std::string(data, len), "UTF-16BE", "UTF-8");
152 #else
153             std::string nameString(data, len);
154 #endif
155             GetStringFromNameId(nameId, languageId, nameString, fontDescriptor);
156         }
157     }
158 
159     return SUCCESSED;
160 }
161 
ProcessPostTable(const struct PostTable * postTable,FontParser::FontDescriptor & fontDescriptor)162 void FontParser::ProcessPostTable(const struct PostTable* postTable, FontParser::FontDescriptor& fontDescriptor)
163 {
164     if (postTable->italicAngle.Get() != 0) {
165         fontDescriptor.italic = 1; // means support italics
166     } else {
167         fontDescriptor.italic = 0;
168     }
169     if (postTable->isFixedPitch.Get() == 1) {
170         fontDescriptor.monoSpace = true;
171     }
172 }
173 
ParseCmapTable(std::shared_ptr<Drawing::Typeface> typeface,FontParser::FontDescriptor & fontDescriptor)174 int FontParser::ParseCmapTable(std::shared_ptr<Drawing::Typeface> typeface, FontParser::FontDescriptor& fontDescriptor)
175 {
176     auto tag = HB_TAG('c', 'm', 'a', 'p');
177     auto size = typeface->GetTableSize(tag);
178     if (size <= 0) {
179         LOGSO_FUNC_LINE(ERROR) << "haven't cmap";
180         return FAILED;
181     }
182     std::unique_ptr<char[]> tableData = nullptr;
183     tableData = std::make_unique<char[]>(size);
184     auto retTableData = typeface->GetTableData(tag, 0, size, tableData.get());
185     if (size != retTableData) {
186         LOGSO_FUNC_LINE(ERROR) <<"get table data failed size: " << size << ", ret: " << retTableData;
187         return FAILED;
188     }
189     hb_blob_t* hblob = nullptr;
190     hblob = hb_blob_create(
191         reinterpret_cast<const char*>(tableData.get()), size, HB_MEMORY_MODE_WRITABLE, tableData.get(), nullptr);
192     if (hblob == nullptr) {
193         LOGSO_FUNC_LINE(ERROR) << "hblob is nullptr";
194         return FAILED;
195     }
196     data_ = hb_blob_get_data(hblob, nullptr);
197     length_ = hb_blob_get_length(hblob);
198     auto parseCmap = std::make_shared<CmapTableParser>(data_, length_);
199     auto cmapTable = parseCmap->Parse(data_, length_);
200     ProcessCmapTable(cmapTable, fontDescriptor);
201     hb_blob_destroy(hblob);
202     return SUCCESSED;
203 }
204 
ParseNameTable(std::shared_ptr<Drawing::Typeface> typeface,FontParser::FontDescriptor & fontDescriptor)205 int FontParser::ParseNameTable(std::shared_ptr<Drawing::Typeface> typeface, FontParser::FontDescriptor& fontDescriptor)
206 {
207     auto tag = HB_TAG('n', 'a', 'm', 'e');
208     auto size = typeface->GetTableSize(tag);
209     if (size <= 0) {
210         LOGSO_FUNC_LINE(ERROR) << "haven't name";
211         return FAILED;
212     }
213     std::unique_ptr<char[]> tableData = nullptr;
214     tableData = std::make_unique<char[]>(size);
215     auto retTableData = typeface->GetTableData(tag, 0, size, tableData.get());
216     if (size != retTableData) {
217         LOGSO_FUNC_LINE(ERROR) <<"get table data failed size: " << size << ", ret: " << retTableData;
218         return FAILED;
219     }
220     hb_blob_t* hblob = nullptr;
221     hblob = hb_blob_create(
222         reinterpret_cast<const char*>(tableData.get()), size, HB_MEMORY_MODE_WRITABLE, tableData.get(), nullptr);
223     if (hblob == nullptr) {
224         LOGSO_FUNC_LINE(ERROR) << "hblob is nullptr";
225         return FAILED;
226     }
227     data_ = hb_blob_get_data(hblob, nullptr);
228     length_ = hb_blob_get_length(hblob);
229     auto parseName = std::make_shared<NameTableParser>(data_, length_);
230     auto nameTable = parseName->Parse(data_, length_);
231     int ret = ProcessNameTable(nameTable, fontDescriptor);
232     if (ret != SUCCESSED) {
233         LOGSO_FUNC_LINE(ERROR) << "process name table failed";
234         hb_blob_destroy(hblob);
235         return FAILED;
236     }
237     hb_blob_destroy(hblob);
238     return SUCCESSED;
239 }
240 
ParsePostTable(std::shared_ptr<Drawing::Typeface> typeface,FontParser::FontDescriptor & fontDescriptor)241 int FontParser::ParsePostTable(std::shared_ptr<Drawing::Typeface> typeface, FontParser::FontDescriptor& fontDescriptor)
242 {
243     auto tag = HB_TAG('p', 'o', 's', 't');
244     auto size = typeface->GetTableSize(tag);
245     if (size <= 0) {
246         LOGSO_FUNC_LINE(ERROR) << "haven't post";
247         return FAILED;
248     }
249     std::unique_ptr<char[]> tableData = nullptr;
250     tableData = std::make_unique<char[]>(size);
251     auto retTableData = typeface->GetTableData(tag, 0, size, tableData.get());
252     if (size != retTableData) {
253         LOGSO_FUNC_LINE(ERROR) <<"get table data failed size: " << size << ", ret: " << retTableData;
254         return FAILED;
255     }
256     hb_blob_t* hblob = nullptr;
257     hblob = hb_blob_create(
258         reinterpret_cast<const char*>(tableData.get()), size, HB_MEMORY_MODE_WRITABLE, tableData.get(), nullptr);
259     if (hblob == nullptr) {
260         LOGSO_FUNC_LINE(ERROR) << "hblob is nullptr";
261         return FAILED;
262     }
263     data_ = hb_blob_get_data(hblob, nullptr);
264     length_ = hb_blob_get_length(hblob);
265     auto parsePost = std::make_shared<PostTableParser>(data_, length_);
266     auto postTable = parsePost->Parse(data_, length_);
267     ProcessPostTable(postTable, fontDescriptor);
268     hb_blob_destroy(hblob);
269     return SUCCESSED;
270 }
271 
ParseTable(std::shared_ptr<Drawing::Typeface> typeface,FontParser::FontDescriptor & fontDescriptor)272 int FontParser::ParseTable(std::shared_ptr<Drawing::Typeface> typeface, FontParser::FontDescriptor& fontDescriptor)
273 {
274     if (ParseCmapTable(typeface, fontDescriptor) != SUCCESSED) {
275         LOGSO_FUNC_LINE(ERROR) << "parse cmap failed";
276         return FAILED;
277     }
278     if (ParseNameTable(typeface, fontDescriptor) != SUCCESSED) {
279         LOGSO_FUNC_LINE(ERROR) << "parse name failed";
280         return FAILED;
281     }
282     if (ParsePostTable(typeface, fontDescriptor) != SUCCESSED) {
283         LOGSO_FUNC_LINE(ERROR) << "parse post failed";
284         return FAILED;
285     }
286 
287     return SUCCESSED;
288 }
289 
SetFontDescriptor(const unsigned int languageId)290 int FontParser::SetFontDescriptor(const unsigned int languageId)
291 {
292     visibilityFonts_.clear();
293     std::list<std::string> fontSetCache;
294     for (unsigned int i = 0; i < fontSet_.size(); ++i) {
295         FontParser::FontDescriptor fontDescriptor;
296         fontDescriptor.requestedLid = languageId;
297         fontDescriptor.path = fontSet_[i];
298         const char* path = fontSet_[i].c_str();
299         auto typeface = Drawing::Typeface::MakeFromFile(path);
300         if (typeface == nullptr) {
301             LOGSO_FUNC_LINE(ERROR) << "typeface is nullptr, can not parse: " << fontDescriptor.path;
302             continue;
303         }
304         auto fontStyle = typeface->GetFontStyle();
305         fontDescriptor.weight = fontStyle.GetWeight();
306         fontDescriptor.width = fontStyle.GetWidth();
307         if (ParseTable(typeface, fontDescriptor) !=  SUCCESSED) {
308             LOGSO_FUNC_LINE(ERROR) << "parse table failed";
309             return FAILED;
310         }
311         size_t idx = fontSet_[i].rfind('/');
312         std::string fontName = fontSet_[i].substr(idx + 1, fontSet_[i].size() - idx - 1);
313         if (std::find(fontSetCache.begin(), fontSetCache.end(), fontName) == fontSetCache.end()) {
314             fontSetCache.push_back(fontName);
315             visibilityFonts_.emplace_back(fontDescriptor);
316         }
317     }
318 
319     return SUCCESSED;
320 }
321 
322 #ifdef BUILD_NON_SDK_VER
ConvertToString(const std::string & src,const std::string & srcType,const std::string & targetType)323 std::string FontParser::ConvertToString(const std::string& src, const std::string& srcType,
324     const std::string& targetType)
325 {
326     std::string utf8Str;
327     iconv_t conv = iconv_open(targetType.c_str(), srcType.c_str());
328     if (conv == (iconv_t)-1) {
329         return utf8Str;
330     }
331     char* inBuf = const_cast<char*>(src.c_str());
332     size_t inBytesLeft = src.length();
333     size_t outBytesLeft = inBytesLeft * 2;
334     char* outBuf = new char[outBytesLeft];
335     char* outBufStart = outBuf;
336     size_t res = iconv(conv, &inBuf, &inBytesLeft, &outBuf, &outBytesLeft);
337     if (res != (size_t)-1) {
338         utf8Str.assign(outBufStart, outBuf - outBufStart);
339     }
340     delete[] outBufStart;
341     iconv_close(conv);
342     return utf8Str;
343 }
344 #endif
345 
GetVisibilityFonts(const std::string & locale)346 std::vector<FontParser::FontDescriptor> FontParser::GetVisibilityFonts(const std::string &locale)
347 {
348     if (SetFontDescriptor(GetLanguageId(locale)) != SUCCESSED) {
349         LOGSO_FUNC_LINE(ERROR) << "set visibility font descriptor failed";
350     }
351 
352     return visibilityFonts_;
353 }
354 
355 class SystemFont {
356 public:
SystemFont(const char * fPath=SYSTEM_FONT_PATH)357     explicit SystemFont(const char* fPath = SYSTEM_FONT_PATH)
358     {
359         ParseConfig(fPath);
360     }
361 
362     ~SystemFont() = default;
363 
GetSystemFontSet() const364     std::shared_ptr<std::vector<std::string>> GetSystemFontSet() const
365     {
366         return systemFontSet_;
367     }
368 
369 private:
ParseConfig(const char * fPath)370     void ParseConfig(const char* fPath)
371     {
372         if (fPath == nullptr) {
373             return;
374         }
375         systemFontSet_ = std::make_shared<std::vector<std::string>>();
376         DIR *dir = opendir(fPath);
377         if (dir == nullptr) {
378             return;
379         }
380         struct dirent *entry;
381         while ((entry = readdir(dir)) != nullptr) {
382             if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
383                 continue;
384             }
385             std::string tmp = entry->d_name;
386             systemFontSet_->push_back(SYSTEM_FONT_PATH + tmp);
387         }
388         closedir(dir);
389     }
390 
391     std::shared_ptr<std::vector<std::string>> systemFontSet_;
392 };
393 
GetSystemFonts(const std::string locale)394 std::vector<std::shared_ptr<FontParser::FontDescriptor>> FontParser::GetSystemFonts(const std::string locale)
395 {
396     std::vector<std::shared_ptr<Drawing::Typeface>> typefaces = Drawing::Typeface::GetSystemFonts();
397     return CreateFontDescriptors(typefaces, locale);
398 }
399 
ParserFontDescriptorsFromPath(const std::string & path,const std::string & locale)400 std::vector<std::shared_ptr<FontParser::FontDescriptor>> FontParser::ParserFontDescriptorsFromPath(
401     const std::string& path, const std::string& locale)
402 {
403     std::vector<std::shared_ptr<Drawing::Typeface>> typefaces;
404     int index = 0;
405     std::shared_ptr<Drawing::Typeface> typeface = nullptr;
406     while ((typeface = Drawing::Typeface::MakeFromFile(path.c_str(), index)) != nullptr) {
407         typefaces.push_back(typeface);
408         index++;
409     }
410     return CreateFontDescriptors(typefaces, locale);
411 }
412 
CreateFontDescriptors(const std::vector<std::shared_ptr<Drawing::Typeface>> & typefaces,const std::string & locale)413 std::vector<std::shared_ptr<FontParser::FontDescriptor>> FontParser::CreateFontDescriptors(
414     const std::vector<std::shared_ptr<Drawing::Typeface>>& typefaces, const std::string& locale)
415 {
416     if (typefaces.empty()) {
417         return {};
418     }
419 
420     std::vector<std::shared_ptr<FontDescriptor>> descriptors;
421     descriptors.reserve(typefaces.size());
422     unsigned int languageId = static_cast<unsigned int>(GetLanguageId(locale));
423     for (auto& item : typefaces) {
424         FontDescriptor desc;
425         desc.requestedLid = languageId;
426         desc.path = item->GetFontPath();
427         auto fontStyle = item->GetFontStyle();
428         desc.weight = fontStyle.GetWeight();
429         desc.width = fontStyle.GetWidth();
430         if (ParseTable(item, desc) != SUCCESSED) {
431             continue;
432         }
433         descriptors.emplace_back(std::make_shared<FontDescriptor>(desc));
434     }
435     return descriptors;
436 }
437 
ParseFontDescriptor(const std::string & fontName,const unsigned int languageId)438 std::unique_ptr<FontParser::FontDescriptor> FontParser::ParseFontDescriptor(const std::string& fontName,
439     const unsigned int languageId)
440 {
441     FontConfigJson fontConfigJson;
442     fontConfigJson.ParseFontFileMap();
443     std::shared_ptr<FontFileMap> fontFileMap = fontConfigJson.GetFontFileMap();
444     if (fontFileMap == nullptr || (*fontFileMap).empty()) {
445         LOGSO_FUNC_LINE(ERROR) << "fontFileMap is nullptr";
446         return nullptr;
447     }
448     if ((*fontFileMap).find(fontName) == (*fontFileMap).end()) {
449         LOGSO_FUNC_LINE(ERROR) << "full name not found";
450         return nullptr;
451     }
452     std::string path = SYSTEM_FONT_PATH + (*fontFileMap)[fontName];
453     auto typeface = Drawing::Typeface::MakeFromFile(path.c_str());
454     if (typeface == nullptr) {
455         path = SYS_PROD_FONT_PATH + (*fontFileMap)[fontName];
456         typeface = Drawing::Typeface::MakeFromFile(path.c_str());
457         if (typeface == nullptr) {
458             LOGSO_FUNC_LINE(ERROR) << "typeface is nullptr, can not parse: " << path.c_str();
459             return nullptr;
460         }
461     }
462 
463     FontParser::FontDescriptor fontDescriptor;
464     fontDescriptor.requestedLid = languageId;
465     fontDescriptor.path = path;
466 
467     fontDescriptor.requestedFullname = fontName;
468     auto fontStyle = typeface->GetFontStyle();
469     fontDescriptor.weight = fontStyle.GetWeight();
470     fontDescriptor.width = fontStyle.GetWidth();
471 
472     if (ParseTable(typeface, fontDescriptor) !=  SUCCESSED) {
473         LOGSO_FUNC_LINE(ERROR) << "parse table failed";
474         return nullptr;
475     }
476     if (fontDescriptor.fullName == fontName) {
477         return std::make_unique<FontDescriptor>(fontDescriptor);
478     }
479     return nullptr;
480 }
481 
GetVisibilityFontByName(const std::string & fontName,const std::string locale)482 std::unique_ptr<FontParser::FontDescriptor> FontParser::GetVisibilityFontByName(const std::string& fontName,
483     const std::string locale)
484 {
485     return ParseFontDescriptor(fontName, GetLanguageId(locale));
486 }
487 } // namespace TextEngine
488 } // namespace Rosen
489 } // namespace OHOS
490