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 "text/font_style.h"
29 #include "utils/text_log.h"
30 
31 #define INSTALL_FONT_CONFIG_FILE "/data/service/el1/public/for-all-app/fonts/install_fontconfig.json"
32 
33 namespace OHOS::Rosen {
34 namespace {
35 constexpr uint32_t WEIGHT_400 = 400;
36 constexpr int SPECIAL_WEIGHT_DIFF = 50;
37 constexpr int WEIGHT_MODULE = 100;
38 }
39 
FontDescriptorCache()40 FontDescriptorCache::FontDescriptorCache() {}
41 
~FontDescriptorCache()42 FontDescriptorCache::~FontDescriptorCache() {}
43 
ClearFontFileCache()44 void FontDescriptorCache::ClearFontFileCache()
45 {
46     allFontDescriptor_.clear();
47     fontFamilyMap_.clear();
48     fullNameMap_.clear();
49     postScriptNameMap_.clear();
50     fontSubfamilyNameMap_.clear();
51     boldCache_.clear();
52     italicCache_.clear();
53     monoSpaceCache_.clear();
54     symbolicCache_.clear();
55     stylishFullNameMap_.clear();
56 }
57 
ParserSystemFonts()58 void FontDescriptorCache::ParserSystemFonts()
59 {
60     for (auto& item : parser_.GetSystemFonts()) {
61         FontDescriptorScatter(item);
62     }
63     Dump();
64 }
65 
ParserStylishFonts()66 void FontDescriptorCache::ParserStylishFonts()
67 {
68     std::vector<TextEngine::FontParser::FontDescriptor> descriptors = parser_.GetVisibilityFonts(TextEngine::ENGLISH);
69     for (const auto& descriptor : descriptors) {
70         FontDescSharedPtr descriptorPtr = std::make_shared<TextEngine::FontParser::FontDescriptor>(descriptor);
71         descriptorPtr->weight = WeightAlignment(descriptorPtr->weight);
72         stylishFullNameMap_[descriptorPtr->fullName].emplace(descriptorPtr);
73     }
74 }
75 
FontDescriptorScatter(FontDescSharedPtr desc)76 void FontDescriptorCache::FontDescriptorScatter(FontDescSharedPtr desc)
77 {
78     auto ret = allFontDescriptor_.emplace(desc);
79     if (!ret.second) {
80         return;
81     }
82 
83     auto handleMapScatter = [desc](auto& map, const auto& key) {
84         map[key].emplace(desc);
85     };
86 
87     handleMapScatter(fontFamilyMap_, desc->fontFamily);
88     handleMapScatter(fullNameMap_, desc->fullName);
89     handleMapScatter(postScriptNameMap_, desc->postScriptName);
90     handleMapScatter(fontSubfamilyNameMap_, desc->fontSubfamily);
91 
92     desc->weight = WeightAlignment(desc->weight);
93     if (static_cast<uint32_t>(desc->weight) > WEIGHT_400) {
94         boldCache_.emplace(desc);
95     }
96 
97     if (desc->italic != 0) {
98         italicCache_.emplace(desc);
99     }
100 
101     if (desc->monoSpace) {
102         monoSpaceCache_.emplace(desc);
103     }
104 
105     if (desc->symbolic) {
106         symbolicCache_.emplace(desc);
107     }
108 }
109 
ParserInstallFontsPathList(std::vector<std::string> & fontPathList)110 bool FontDescriptorCache::ParserInstallFontsPathList(std::vector<std::string>& fontPathList)
111 {
112     std::shared_ptr<Drawing::FontMgr> fontMgr = Drawing::FontMgr::CreateDynamicFontMgr();
113     if (fontMgr == nullptr) {
114         return false;
115     }
116     int ret = fontMgr->ParseInstallFontConfig(INSTALL_FONT_CONFIG_FILE, fontPathList);
117     return ret == Drawing::FontCheckCode::SUCCESSED;
118 }
119 
GetInstallFontList()120 std::unordered_set<std::string> FontDescriptorCache::GetInstallFontList()
121 {
122     std::unordered_set<std::string> fullNameList;
123     std::vector<std::string> fontPathList;
124     if (!ParserInstallFontsPathList(fontPathList)) {
125         TEXT_LOGE("Parser install fonts path list failed");
126         return fullNameList;
127     }
128     for (const auto& path : fontPathList) {
129         std::vector<FontDescSharedPtr> descriptors = parser_.ParserFontDescriptorsFromPath(path);
130         for (const auto& item : descriptors) {
131             fullNameList.emplace(item->fullName);
132         }
133     }
134     return fullNameList;
135 }
136 
GetStylishFontList()137 std::unordered_set<std::string> FontDescriptorCache::GetStylishFontList()
138 {
139     std::unordered_set<std::string> fullNameList;
140     for (const auto& temp : stylishFullNameMap_) {
141         fullNameList.emplace(temp.first);
142     }
143     return fullNameList;
144 }
145 
GetGenericFontList()146 std::unordered_set<std::string> FontDescriptorCache::GetGenericFontList()
147 {
148     std::unordered_set<std::string> fullNameList;
149     for (const auto& temp : allFontDescriptor_) {
150         fullNameList.emplace(temp->fullName);
151     }
152     return fullNameList;
153 }
154 
ProcessSystemFontType(const int32_t & systemFontType,int32_t & fontType)155 bool FontDescriptorCache::ProcessSystemFontType(const int32_t& systemFontType, int32_t& fontType)
156 {
157     if ((static_cast<uint32_t>(systemFontType) & (TextEngine::FontParser::SystemFontType::ALL |
158         TextEngine::FontParser::SystemFontType::GENERIC |
159         TextEngine::FontParser::SystemFontType::STYLISH |
160         TextEngine::FontParser::SystemFontType::INSTALLED)) != systemFontType) {
161         TEXT_LOGE("SystemFontType is invalid, systemFontType: %{public}d", systemFontType);
162         return false;
163     }
164     fontType = systemFontType;
165     if (static_cast<uint32_t>(systemFontType) & TextEngine::FontParser::SystemFontType::ALL) {
166         fontType = TextEngine::FontParser::SystemFontType::GENERIC |
167             TextEngine::FontParser::SystemFontType::STYLISH |
168             TextEngine::FontParser::SystemFontType::INSTALLED;
169     }
170     return true;
171 }
172 
GetSystemFontFullNamesByType(const int32_t & systemFontType,std::unordered_set<std::string> & fontList)173 void FontDescriptorCache::GetSystemFontFullNamesByType(
174     const int32_t &systemFontType, std::unordered_set<std::string> &fontList)
175 {
176     if (systemFontType < 0) {
177         TEXT_LOGE("SystemFontType is an invalid value");
178         return;
179     }
180     int32_t fontType = 0;
181     if (!ProcessSystemFontType(systemFontType, fontType)) {
182         fontList.clear();
183         return;
184     }
185 
186     if (static_cast<uint32_t>(fontType) & TextEngine::FontParser::SystemFontType::GENERIC) {
187         auto fullNameList = GetGenericFontList();
188         fontList.insert(fullNameList.begin(), fullNameList.end());
189     }
190 
191     if (static_cast<uint32_t>(fontType) & TextEngine::FontParser::SystemFontType::STYLISH) {
192         auto fullNameList = GetStylishFontList();
193         fontList.insert(fullNameList.begin(), fullNameList.end());
194     }
195 
196     if (static_cast<uint32_t>(fontType) & TextEngine::FontParser::SystemFontType::INSTALLED) {
197         auto fullNameList = GetInstallFontList();
198         fontList.insert(fullNameList.begin(), fullNameList.end());
199     }
200 }
201 
ParseInstallFontDescSharedPtrByName(const std::string & fullName,FontDescSharedPtr & result)202 bool FontDescriptorCache::ParseInstallFontDescSharedPtrByName(const std::string& fullName, FontDescSharedPtr& result)
203 {
204     std::vector<std::string> fontPathList;
205     if (!ParserInstallFontsPathList(fontPathList)) {
206         TEXT_LOGE("Parser install fonts path list failed");
207         return false;
208     }
209     for (const auto& path : fontPathList) {
210         std::vector<FontDescSharedPtr> descriptors = parser_.ParserFontDescriptorsFromPath(path);
211         for (const auto& item : descriptors) {
212             if (item->fullName == fullName) {
213                 item->weight = WeightAlignment(item->weight);
214                 result = item;
215                 return true;
216             }
217         }
218     }
219     TEXT_LOGE_LIMIT3_MIN("Parser installed fontDescriptor by name failed, fullName: %{public}s", fullName.c_str());
220     return false;
221 }
222 
GetFontDescSharedPtrByFullName(const std::string & fullName,const int32_t & systemFontType,FontDescSharedPtr & result)223 void FontDescriptorCache::GetFontDescSharedPtrByFullName(const std::string& fullName,
224     const int32_t& systemFontType, FontDescSharedPtr& result)
225 {
226     if (fullName.empty()) {
227         TEXT_LOGE("Empty fullName is provided");
228         result = nullptr;
229         return;
230     }
231     int32_t fontType = 0;
232     if (!ProcessSystemFontType(systemFontType, fontType)) {
233         result = nullptr;
234         return;
235     }
236     if (systemFontType < 0) {
237         TEXT_LOGE("SystemFontType is an invalid value");
238         result = nullptr;
239         return;
240     }
241 
242     auto tryFindFontDescriptor = [&fullName, &result](const std::unordered_map<std::string,
243         std::set<FontDescSharedPtr>>& map) -> bool {
244         auto it = map.find(fullName);
245         if (it != map.end()) {
246             result = *(it->second.begin());
247             return true;
248         }
249         return false;
250     };
251     if ((static_cast<uint32_t>(fontType) & TextEngine::FontParser::SystemFontType::GENERIC) &&
252         tryFindFontDescriptor(fullNameMap_)) {
253         return;
254     }
255     if ((static_cast<uint32_t>(fontType) & TextEngine::FontParser::SystemFontType::STYLISH) &&
256         tryFindFontDescriptor(stylishFullNameMap_)) {
257         return;
258     }
259     if ((static_cast<uint32_t>(fontType) & TextEngine::FontParser::SystemFontType::INSTALLED) &&
260         ParseInstallFontDescSharedPtrByName(fullName, result)) {
261         return;
262     }
263     TEXT_LOGD("Failed to get fontDescriptor by fullName: %{public}s", fullName.c_str());
264     result = nullptr;
265 }
266 
Dump()267 void FontDescriptorCache::Dump()
268 {
269     TEXT_LOGD("allFontDescriptor size: %{public}zu, fontFamilyMap size: %{public}zu, fullNameMap size: %{public}zu \
270         postScriptNameMap size: %{public}zu, fontSubfamilyNameMap size: %{public}zu, boldCache size: %{public}zu \
271         italicCache size: %{public}zu, monoSpaceCache size: %{public}zu, symbolicCache size: %{public}zu",
272         allFontDescriptor_.size(), fontFamilyMap_.size(), fullNameMap_.size(), postScriptNameMap_.size(),
273         fontSubfamilyNameMap_.size(), boldCache_.size(), italicCache_.size(), monoSpaceCache_.size(),
274         symbolicCache_.size());
275 }
276 
WeightAlignment(int32_t weight)277 int32_t FontDescriptorCache::WeightAlignment(int32_t weight)
278 {
279     if (weight < Drawing::FontStyle::THIN_WEIGHT) {
280         return Drawing::FontStyle::THIN_WEIGHT;
281     }
282 
283     if (weight > Drawing::FontStyle::EXTRA_BLACK_WEIGHT) {
284         return Drawing::FontStyle::EXTRA_BLACK_WEIGHT;
285     }
286 
287     if ((weight % WEIGHT_MODULE) == 0) {
288         return weight;
289     }
290 
291     static const std::vector<int> weightType = {
292         Drawing::FontStyle::THIN_WEIGHT,
293         Drawing::FontStyle::EXTRA_LIGHT_WEIGHT,
294         Drawing::FontStyle::LIGHT_WEIGHT,
295         Drawing::FontStyle::NORMAL_WEIGHT,
296         Drawing::FontStyle::MEDIUM_WEIGHT,
297         Drawing::FontStyle::SEMI_BOLD_WEIGHT,
298         Drawing::FontStyle::BOLD_WEIGHT,
299         Drawing::FontStyle::EXTRA_BOLD_WEIGHT,
300         Drawing::FontStyle::BLACK_WEIGHT,
301         Drawing::FontStyle::EXTRA_BLACK_WEIGHT
302     };
303     // Obtain weight ranges for non-whole hundred values
304     auto it = std::lower_bound(weightType.begin(), weightType.end(), weight);
305     std::vector<int> targetRange = { *(it - 1), *it };
306 
307     /**
308      * When the font weight is less than NORMAL_WEIGHT, round down as much as possible;
309      * when the font weight exceeds NORMAL_WEIGHT, round up where possible. For example, when weight is 360,
310      * the final font weight is set to 300; when weight is 620, the final font weight is set to 700.
311      */
312     uint32_t minDiff = 0xFFFFFFFF;
313     int resultWeight = 0;
314     for (const auto& item : targetRange) {
315         /**
316          * The maximum weight is EXTRA_BLACK_WEIGHT (1000), when weight and item are at the different
317          * side of NORMAL_WEIGHT, the weight difference between them should be more than 500 (1000/2).
318          */
319         uint32_t weightDiff = 0;
320         constexpr int kWeightDiffThreshold = Drawing::FontStyle::EXTRA_BLACK_WEIGHT / 2;
321         if ((weight == Drawing::FontStyle::NORMAL_WEIGHT && item == Drawing::FontStyle::MEDIUM_WEIGHT) ||
322             (weight == Drawing::FontStyle::MEDIUM_WEIGHT && item == Drawing::FontStyle::NORMAL_WEIGHT)) {
323             weightDiff = static_cast<uint32_t>(SPECIAL_WEIGHT_DIFF);
324         } else if (weight <= Drawing::FontStyle::NORMAL_WEIGHT) {
325             weightDiff = (item <= weight) ? static_cast<uint32_t>(weight - item) :
326                 static_cast<uint32_t>(item - weight + kWeightDiffThreshold);
327         } else if (weight > Drawing::FontStyle::NORMAL_WEIGHT) {
328             weightDiff = (item >= weight) ? static_cast<uint32_t>(item - weight) :
329                 static_cast<uint32_t>(weight - item + kWeightDiffThreshold);
330         }
331 
332         // Retain the font weight with the smallest difference
333         if (weightDiff < minDiff) {
334             minDiff = weightDiff;
335             resultWeight = item;
336         }
337     }
338     return resultWeight;
339 }
340 }