1 /*
2  * Copyright (c) 2024 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_descriptor_cache.h"
17 
18 #include <algorithm>
19 #include <dirent.h>
20 #include <fcntl.h>
21 #include <fstream>
22 #include <sys/stat.h>
23 #include <unicode/brkiter.h>
24 #include <unistd.h>
25 
26 #include "font_config.h"
27 #include "text/common_utils.h"
28 #include "utils/text_log.h"
29 
30 #define INSTALL_FONT_CONFIG_FILE "/data/service/el1/public/for-all-app/fonts/install_fontconfig.json"
31 
32 namespace OHOS::Rosen {
33 namespace {
34 constexpr uint32_t WEIGHT_400 = 400;
35 }
36 
FontDescriptorCache()37 FontDescriptorCache::FontDescriptorCache() {}
38 
~FontDescriptorCache()39 FontDescriptorCache::~FontDescriptorCache() {}
40 
ClearFontFileCache()41 void FontDescriptorCache::ClearFontFileCache()
42 {
43     allFontDescriptor_.clear();
44     fontFamilyMap_.clear();
45     fullNameMap_.clear();
46     postScriptNameMap_.clear();
47     fontSubfamilyNameMap_.clear();
48     boldCache_.clear();
49     italicCache_.clear();
50     monoSpaceCache_.clear();
51     symbolicCache_.clear();
52     stylishFullNameMap_.clear();
53 }
54 
ParserSystemFonts()55 void FontDescriptorCache::ParserSystemFonts()
56 {
57     for (auto& item : parser_.GetSystemFonts()) {
58         FontDescriptorScatter(item);
59     }
60     Dump();
61 }
62 
ParserStylishFonts()63 void FontDescriptorCache::ParserStylishFonts()
64 {
65     icu::Locale locale = icu::Locale::getDefault();
66     std::vector<TextEngine::FontParser::FontDescriptor> descriptors =
67         parser_.GetVisibilityFonts(std::string(locale.getName()));
68     for (const auto& descriptor : descriptors) {
69         FontDescSharedPtr descriptorPtr = std::make_shared<TextEngine::FontParser::FontDescriptor>(descriptor);
70         stylishFullNameMap_[descriptorPtr->fullName].emplace(descriptorPtr);
71     }
72 }
73 
ParserInstallFonts()74 void FontDescriptorCache::ParserInstallFonts()
75 {
76     installPathMap_.clear();
77     std::vector<std::string> fontPathList;
78     std::string fontPath = INSTALL_FONT_CONFIG_FILE;
79 
80     if (!ParseInstalledConfigFile(fontPath, fontPathList)) {
81         TEXT_LOGE("Failed to parse the installed fonts");
82         return;
83     }
84 
85     for (const auto& path : fontPathList) {
86         if (!ProcessInstalledFontPath(path)) {
87             TEXT_LOGE("Failed to process font path, path: %{public}s", path.c_str());
88         }
89     }
90 }
91 
ParseInstalledConfigFile(const std::string& fontPath, std::vector<std::string>& fontPathList)92 bool FontDescriptorCache::ParseInstalledConfigFile(const std::string& fontPath, std::vector<std::string>& fontPathList)
93 {
94     std::shared_ptr<Drawing::FontMgr> fontMgr = Drawing::FontMgr::CreateDynamicFontMgr();
95     std::ifstream configFile(fontPath);
96     if (!configFile.is_open()) {
97         return false;
98     }
99     configFile.close();
100     return (fontMgr->ParseInstallFontConfig(fontPath, fontPathList) == Drawing::FontCheckCode::SUCCESSED);
101 }
102 
ProcessInstalledFontPath(const std::string& path)103 bool FontDescriptorCache::ProcessInstalledFontPath(const std::string& path)
104 {
105     std::shared_ptr<Drawing::FontMgr> fontMgr = Drawing::FontMgr::CreateDefaultFontMgr();
106     int fd = open(path.c_str(), O_RDONLY);
107     if (fd == -1) {
108         return false;
109     }
110     std::vector<Drawing::FontByteArray> fullNameVec;
111     int ret = fontMgr->GetFontFullName(fd, fullNameVec);
112     close(fd);
113     if (ret != Drawing::FontCheckCode::SUCCESSED || fullNameVec.empty()) {
114         return false;
115     }
116     std::vector<std::string> fullNameStringVec;
117     for (const auto& fullName : fullNameVec) {
118         std::string fullNameString;
119         if (Drawing::ConvertToString(fullName.strData.get(), fullName.strLen, fullNameString)) {
120             fullNameStringVec.push_back(fullNameString);
121         } else {
122             fullNameStringVec.clear();
123             return false;
124         }
125     }
126     installPathMap_[path] = fullNameStringVec;
127     return true;
128 }
129 
FontDescriptorScatter(FontDescSharedPtr desc)130 void FontDescriptorCache::FontDescriptorScatter(FontDescSharedPtr desc)
131 {
132     auto ret = allFontDescriptor_.emplace(desc);
133     if (!ret.second) {
134         return;
135     }
136 
137     auto handleMapScatter = [&](auto& map, const auto& key) {
138         map[key].emplace(desc);
139     };
140 
141     handleMapScatter(fontFamilyMap_, desc->fontFamily);
142     handleMapScatter(fullNameMap_, desc->fullName);
143     handleMapScatter(postScriptNameMap_, desc->postScriptName);
144     handleMapScatter(fontSubfamilyNameMap_, desc->fontSubfamily);
145 
146     if (desc->weight > WEIGHT_400) {
147         boldCache_.emplace(desc);
148     }
149 
150     if (desc->italic != 0) {
151         italicCache_.emplace(desc);
152     }
153 
154     if (desc->monoSpace) {
155         monoSpaceCache_.emplace(desc);
156     }
157 
158     if (desc->symbolic) {
159         symbolicCache_.emplace(desc);
160     }
161 }
162 
GetInstallFontList()163 std::unordered_set<std::string> FontDescriptorCache::GetInstallFontList()
164 {
165     ParserInstallFonts();
166     std::unordered_set<std::string> fullNameList;
167     for (const auto& pathAndFonts : installPathMap_) {
168         for (const auto& fullName : pathAndFonts.second) {
169             fullNameList.emplace(fullName);
170         }
171     }
172     return fullNameList;
173 }
174 
GetStylishFontList()175 std::unordered_set<std::string> FontDescriptorCache::GetStylishFontList()
176 {
177     std::unordered_set<std::string> fullNameList;
178     for (const auto& temp : stylishFullNameMap_) {
179         fullNameList.emplace(temp.first);
180     }
181     return fullNameList;
182 }
183 
GetGenericFontList()184 std::unordered_set<std::string> FontDescriptorCache::GetGenericFontList()
185 {
186     std::unordered_set<std::string> fullNameList;
187     for (const auto& temp : allFontDescriptor_) {
188         fullNameList.emplace(temp->fullName);
189     }
190     return fullNameList;
191 }
192 
ProcessSystemFontType(const int32_t& systemFontType, int32_t& fontType)193 bool FontDescriptorCache::ProcessSystemFontType(const int32_t& systemFontType, int32_t& fontType)
194 {
195     if ((systemFontType & (TextEngine::FontParser::SystemFontType::ALL |
196         TextEngine::FontParser::SystemFontType::GENERIC |
197         TextEngine::FontParser::SystemFontType::STYLISH |
198         TextEngine::FontParser::SystemFontType::INSTALLED)) != systemFontType) {
199         TEXT_LOGE("SystemFontType is invalid, systemFontType: %{public}d", systemFontType);
200         return false;
201     }
202     fontType = systemFontType;
203     if (systemFontType & TextEngine::FontParser::SystemFontType::ALL) {
204         fontType = TextEngine::FontParser::SystemFontType::GENERIC |
205             TextEngine::FontParser::SystemFontType::STYLISH |
206             TextEngine::FontParser::SystemFontType::INSTALLED;
207     }
208     return true;
209 }
210 
GetSystemFontFullNamesByType(const int32_t& systemFontType, std::unordered_set<std::string>& fontList)211 void FontDescriptorCache::GetSystemFontFullNamesByType(const int32_t& systemFontType,
212     std::unordered_set<std::string>& fontList)
213 {
214     int32_t fontType;
215     if (!ProcessSystemFontType(systemFontType, fontType)) {
216         fontList.clear();
217         return;
218     }
219 
220     if (fontType & TextEngine::FontParser::SystemFontType::GENERIC) {
221         auto fullNameList = GetGenericFontList();
222         fontList.insert(fullNameList.begin(), fullNameList.end());
223     }
224 
225     if (fontType & TextEngine::FontParser::SystemFontType::STYLISH) {
226         auto fullNameList = GetStylishFontList();
227         fontList.insert(fullNameList.begin(), fullNameList.end());
228     }
229 
230     if (fontType & TextEngine::FontParser::SystemFontType::INSTALLED) {
231         auto fullNameList = GetInstallFontList();
232         fontList.insert(fullNameList.begin(), fullNameList.end());
233     }
234 }
235 
ParseInstallFontDescSharedPtrByName(const std::string& fullName, FontDescSharedPtr& result)236 bool FontDescriptorCache::ParseInstallFontDescSharedPtrByName(const std::string& fullName, FontDescSharedPtr& result)
237 {
238     ParserInstallFonts();
239     std::string path;
240     for (const auto& pathAndFonts : installPathMap_) {
241         for (const auto& font : pathAndFonts.second) {
242             if (font == fullName) {
243                 path = pathAndFonts.first;
244                 break;
245             }
246         }
247         if (!path.empty()) {
248             break;
249         }
250     }
251     // Setting the locale to English is to ensure consistency with the fullName format obtained from Skia.
252     std::string locale = ENGLISH;
253     std::vector<FontDescSharedPtr> descriptors;
254     if (parser_.ParserFontDescriptorFromPath(path, descriptors, locale)) {
255         for (auto& item : descriptors) {
256             if (item->fullName == fullName) {
257                 result = item;
258                 return true;
259             }
260         }
261     }
262     TEXT_LOGE_LIMIT3_MIN("Failed to parser fontDescriptor from path, path: %{public}s", path.c_str());
263     return false;
264 }
265 
GetFontDescSharedPtrByFullName(const std::string& fullName, const int32_t& systemFontType, FontDescSharedPtr& result)266 void FontDescriptorCache::GetFontDescSharedPtrByFullName(const std::string& fullName,
267     const int32_t& systemFontType, FontDescSharedPtr& result)
268 {
269     if (fullName.empty()) {
270         TEXT_LOGE("Empty fullName is provided");
271         result = nullptr;
272         return;
273     }
274     int32_t fontType;
275     if (!ProcessSystemFontType(systemFontType, fontType)) {
276         result = nullptr;
277         return;
278     }
279     auto tryFindFontDescriptor = [&fullName, &result](const std::unordered_map<std::string,
280         std::set<FontDescSharedPtr>>& map) -> bool {
281         auto it = map.find(fullName);
282         if (it != map.end()) {
283             result = *(it->second.begin());
284             return true;
285         }
286         return false;
287     };
288     if ((fontType & TextEngine::FontParser::SystemFontType::GENERIC) && tryFindFontDescriptor(fullNameMap_)) {
289         return;
290     }
291     if ((fontType & TextEngine::FontParser::SystemFontType::STYLISH) && tryFindFontDescriptor(stylishFullNameMap_)) {
292         return;
293     }
294     if ((fontType & TextEngine::FontParser::SystemFontType::INSTALLED) &&
295         ParseInstallFontDescSharedPtrByName(fullName, result)) {
296         return;
297     }
298     TEXT_LOGD("Failed to get fontDescriptor by fullName: %{public}s", fullName.c_str());
299     result = nullptr;
300 }
301 
HandleMapIntersection(std::set<FontDescSharedPtr>& finishRet, const std::string& name, std::unordered_map<std::string, std::set<FontDescSharedPtr>>& map)302 bool FontDescriptorCache::HandleMapIntersection(std::set<FontDescSharedPtr>& finishRet, const std::string& name,
303     std::unordered_map<std::string, std::set<FontDescSharedPtr>>& map)
304 {
305     if (name.empty()) {
306         return true;
307     }
308     auto iter = map.find(name);
309     if (iter == map.end()) {
310         return false;
311     }
312     if (finishRet.empty()) {
313         finishRet = iter->second;
314     } else {
315         std::set<FontDescSharedPtr> temp;
316         std::set_intersection(iter->second.begin(), iter->second.end(), finishRet.begin(), finishRet.end(),
317             std::insert_iterator<std::set<FontDescSharedPtr>>(temp, temp.begin()));
318         if (temp.empty()) {
319             return false;
320         }
321         finishRet = std::move(temp);
322     }
323     return true;
324 }
325 
FilterBoldCache(int weight, std::set<FontDescSharedPtr>& finishRet)326 bool FontDescriptorCache::FilterBoldCache(int weight, std::set<FontDescSharedPtr>& finishRet)
327 {
328     if (weight < 0) {
329         return false;
330     }
331 
332     if (weight == 0) {
333         return true;
334     }
335 
336     std::set<FontDescSharedPtr> temp;
337     std::set<FontDescSharedPtr>::iterator begin;
338     std::set<FontDescSharedPtr>::iterator end;
339     if (!finishRet.empty()) {
340         begin = finishRet.begin();
341         end = finishRet.end();
342     } else if (weight > WEIGHT_400) {
343         begin = boldCache_.begin();
344         end = boldCache_.end();
345     } else {
346         begin = allFontDescriptor_.begin();
347         end = allFontDescriptor_.end();
348     }
349     std::for_each(begin, end, [&](FontDescSharedPtr item) {
350         if (item->weight == weight) {
351             temp.emplace(item);
352         }
353     });
354 
355     if (temp.empty()) {
356         TEXT_LOGD("Failed to match weight");
357         return false;
358     }
359     finishRet = std::move(temp);
360     return true;
361 }
362 
FilterWidthCache(int width, std::set<FontDescSharedPtr>& finishRet)363 bool FontDescriptorCache::FilterWidthCache(int width, std::set<FontDescSharedPtr>& finishRet)
364 {
365     if (width < 0) {
366         return false;
367     }
368 
369     if (width == 0) {
370         return true;
371     }
372 
373     std::set<FontDescSharedPtr> temp;
374     std::set<FontDescSharedPtr>::iterator begin;
375     std::set<FontDescSharedPtr>::iterator end;
376     if (!finishRet.empty()) {
377         begin = finishRet.begin();
378         end = finishRet.end();
379     } else {
380         begin = allFontDescriptor_.begin();
381         end = allFontDescriptor_.end();
382     }
383     std::for_each(begin, end, [&](FontDescSharedPtr item) {
384         if (item->width == width) {
385             temp.emplace(item);
386         }
387     });
388 
389     if (temp.empty()) {
390         TEXT_LOGD("Failed to match width");
391         return false;
392     }
393     finishRet = std::move(temp);
394     return true;
395 }
396 
FilterItalicCache(int italic, std::set<FontDescSharedPtr>& finishRet)397 bool FontDescriptorCache::FilterItalicCache(int italic, std::set<FontDescSharedPtr>& finishRet)
398 {
399     if (italic == 0) {
400         return true;
401     }
402     std::set<FontDescSharedPtr> temp;
403     if (!finishRet.empty()) {
404         std::for_each(finishRet.begin(), finishRet.end(), [&](FontDescSharedPtr item) {
405             if (item->italic != 0) {
406                 temp.emplace(item);
407             }
408         });
409     } else {
410         temp = italicCache_;
411     }
412     if (temp.empty()) {
413         TEXT_LOGD("Failed to match italic");
414         return false;
415     }
416     finishRet = std::move(temp);
417     return true;
418 }
419 
FilterMonoSpaceCache(bool monoSpace, std::set<FontDescSharedPtr>& finishRet)420 bool FontDescriptorCache::FilterMonoSpaceCache(bool monoSpace, std::set<FontDescSharedPtr>& finishRet)
421 {
422     if (!monoSpace) {
423         return true;
424     }
425 
426     std::set<FontDescSharedPtr> temp;
427     if (!finishRet.empty()) {
428         std::for_each(finishRet.begin(), finishRet.end(), [&](FontDescSharedPtr item) {
429             if (item->monoSpace) {
430                 temp.emplace(item);
431             }
432         });
433     } else {
434         temp = monoSpaceCache_;
435     }
436     if (temp.empty()) {
437         TEXT_LOGD("Failed to match monoSpace");
438         return false;
439     }
440     finishRet = std::move(temp);
441     return true;
442 }
443 
FilterSymbolicCache(bool symbolic, std::set<FontDescSharedPtr>& finishRet)444 bool FontDescriptorCache::FilterSymbolicCache(bool symbolic, std::set<FontDescSharedPtr>& finishRet)
445 {
446     if (!symbolic) {
447         return true;
448     }
449     std::set<FontDescSharedPtr> temp;
450     if (!finishRet.empty()) {
451         std::for_each(finishRet.begin(), finishRet.end(), [&](FontDescSharedPtr item) {
452             if (item->symbolic) {
453                 temp.emplace(item);
454             }
455         });
456     } else {
457         temp = symbolicCache_;
458     }
459     if (temp.empty()) {
460         TEXT_LOGD("Failed to match symbolic");
461         return false;
462     }
463     finishRet = std::move(temp);
464     return true;
465 }
466 
IsDefault(FontDescSharedPtr desc)467 bool FontDescriptorCache::IsDefault(FontDescSharedPtr desc)
468 {
469     if (desc->fontFamily.empty() && desc->fullName.empty() && desc->postScriptName.empty()
470         && desc->fontSubfamily.empty() && desc->weight == 0 && desc->width == 0 && desc->italic == 0
471         && !desc->monoSpace && !desc->symbolic) {
472         return true;
473     }
474     return false;
475 }
476 
MatchFromFontDescriptor(FontDescSharedPtr desc, std::set<FontDescSharedPtr>& result)477 void FontDescriptorCache::MatchFromFontDescriptor(FontDescSharedPtr desc, std::set<FontDescSharedPtr>& result)
478 {
479     if (desc == nullptr) {
480         TEXT_LOGE("desc is nullptr");
481         return;
482     }
483 
484     if (IsDefault(desc)) {
485         result = std::set<FontDescSharedPtr>(allFontDescriptor_.begin(), allFontDescriptor_.end());
486         return;
487     }
488 
489     std::set<FontDescSharedPtr> finishRet;
490     TEXT_INFO_CHECK(HandleMapIntersection(finishRet, desc->fontFamily, fontFamilyMap_), return,
491         "Failed to match fontFamily");
492     TEXT_INFO_CHECK(HandleMapIntersection(finishRet, desc->fullName, fullNameMap_), return, "Failed to match fullName");
493     TEXT_INFO_CHECK(HandleMapIntersection(finishRet, desc->postScriptName, postScriptNameMap_), return,
494         "Failed to match postScriptName");
495     TEXT_INFO_CHECK(HandleMapIntersection(finishRet, desc->fontSubfamily, fontSubfamilyNameMap_), return,
496         "Failed to match fontSubfamily");
497 
498     TEXT_CHECK(FilterBoldCache(desc->weight, finishRet), return);
499     TEXT_CHECK(FilterWidthCache(desc->width, finishRet), return);
500     TEXT_CHECK(FilterItalicCache(desc->italic, finishRet), return);
501     TEXT_CHECK(FilterMonoSpaceCache(desc->monoSpace, finishRet), return);
502     TEXT_CHECK(FilterSymbolicCache(desc->symbolic, finishRet), return);
503     result = std::move(finishRet);
504 }
505 
Dump()506 void FontDescriptorCache::Dump()
507 {
508     TEXT_LOGD("allFontDescriptor size: %{public}zu, fontFamilyMap size: %{public}zu, fullNameMap size: %{public}zu \
509         postScriptNameMap size: %{public}zu, fontSubfamilyNameMap size: %{public}zu, boldCache size: %{public}zu \
510         italicCache size: %{public}zu, monoSpaceCache size: %{public}zu, symbolicCache size: %{public}zu",
511         allFontDescriptor_.size(), fontFamilyMap_.size(), fullNameMap_.size(), postScriptNameMap_.size(),
512         fontSubfamilyNameMap_.size(), boldCache_.size(), italicCache_.size(), monoSpaceCache_.size(),
513         symbolicCache_.size());
514 }
515 }