• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <cstdint>
20 #include <dirent.h>
21 #include <fcntl.h>
22 #include <fstream>
23 #include <sys/stat.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 "text/font_mgr.h"
30 #include "utils/text_log.h"
31 
32 #define INSTALL_FONT_CONFIG_FILE "/data/service/el1/public/for-all-app/fonts/install_fontconfig.json"
33 
34 namespace OHOS::Rosen {
35 namespace {
36 constexpr uint32_t WEIGHT_400 = 400;
37 constexpr int SPECIAL_WEIGHT_DIFF = 50;
38 constexpr int WEIGHT_MODULE = 100;
39 }
40 
FontDescriptorCache()41 FontDescriptorCache::FontDescriptorCache() {}
42 
~FontDescriptorCache()43 FontDescriptorCache::~FontDescriptorCache() {}
44 
ClearFontFileCache()45 void FontDescriptorCache::ClearFontFileCache()
46 {
47     allFontDescriptor_.clear();
48     fontFamilyMap_.clear();
49     fullNameMap_.clear();
50     postScriptNameMap_.clear();
51     fontSubfamilyNameMap_.clear();
52     boldCache_.clear();
53     italicCache_.clear();
54     monoSpaceCache_.clear();
55     symbolicCache_.clear();
56     stylishFullNameMap_.clear();
57     dynamicFullNameMap_.clear();
58 }
59 
ParserSystemFonts()60 void FontDescriptorCache::ParserSystemFonts()
61 {
62     // System fonts have already been parsed
63     if (!fullNameMap_.empty()) {
64         return;
65     }
66     for (auto& item : parser_.GetSystemFonts()) {
67         FontDescriptorScatter(item);
68     }
69     Dump();
70 }
71 
ParserStylishFonts()72 void FontDescriptorCache::ParserStylishFonts()
73 {
74     // Stylish fonts have already been parsed
75     if (!stylishFullNameMap_.empty()) {
76         return;
77     }
78     std::vector<TextEngine::FontParser::FontDescriptor> descriptors = parser_.GetVisibilityFonts(TextEngine::ENGLISH);
79     for (const auto& descriptor : descriptors) {
80         FontDescSharedPtr descriptorPtr = std::make_shared<TextEngine::FontParser::FontDescriptor>(descriptor);
81         descriptorPtr->weight = WeightAlignment(descriptorPtr->weight);
82         stylishFullNameMap_[descriptorPtr->fullName].emplace(descriptorPtr);
83     }
84 }
85 
ParserFontsByFontType(int32_t fontType)86 void FontDescriptorCache::ParserFontsByFontType(int32_t fontType)
87 {
88     if (static_cast<uint32_t>(fontType) & TextEngine::FontParser::SystemFontType::GENERIC) {
89         ParserSystemFonts();
90     }
91     if (static_cast<uint32_t>(fontType) & TextEngine::FontParser::SystemFontType::STYLISH) {
92         ParserStylishFonts();
93     }
94 }
95 
FontDescriptorScatter(FontDescSharedPtr desc)96 void FontDescriptorCache::FontDescriptorScatter(FontDescSharedPtr desc)
97 {
98     auto ret = allFontDescriptor_.emplace(desc);
99     if (!ret.second) {
100         return;
101     }
102 
103     auto handleMapScatter = [desc](auto& map, const auto& key) {
104         map[key].emplace(desc);
105     };
106 
107     handleMapScatter(fontFamilyMap_, desc->fontFamily);
108     handleMapScatter(fullNameMap_, desc->fullName);
109     handleMapScatter(postScriptNameMap_, desc->postScriptName);
110     handleMapScatter(fontSubfamilyNameMap_, desc->fontSubfamily);
111 
112     desc->weight = WeightAlignment(desc->weight);
113     if (static_cast<uint32_t>(desc->weight) > WEIGHT_400) {
114         boldCache_.emplace(desc);
115     }
116 
117     if (desc->italic != 0) {
118         italicCache_.emplace(desc);
119     }
120 
121     if (desc->monoSpace) {
122         monoSpaceCache_.emplace(desc);
123     }
124 
125     if (desc->symbolic) {
126         symbolicCache_.emplace(desc);
127     }
128 }
129 
ParserInstallFontsPathList(std::vector<std::string> & fontPathList)130 bool FontDescriptorCache::ParserInstallFontsPathList(std::vector<std::string>& fontPathList)
131 {
132     std::shared_ptr<Drawing::FontMgr> fontMgr = Drawing::FontMgr::CreateDynamicFontMgr();
133     if (fontMgr == nullptr) {
134         return false;
135     }
136     int ret = fontMgr->ParseInstallFontConfig(INSTALL_FONT_CONFIG_FILE, fontPathList);
137     return ret == Drawing::FontCheckCode::SUCCESSED;
138 }
139 
GetInstallFontList()140 std::unordered_set<std::string> FontDescriptorCache::GetInstallFontList()
141 {
142     std::unordered_set<std::string> fullNameList;
143     std::vector<std::string> fontPathList;
144     if (!ParserInstallFontsPathList(fontPathList)) {
145         TEXT_LOGE("Failed to parser install fonts path list");
146         return fullNameList;
147     }
148     for (const auto& path : fontPathList) {
149         std::vector<FontDescSharedPtr> descriptors = parser_.ParserFontDescriptorsFromPath(path);
150         for (const auto& item : descriptors) {
151             fullNameList.emplace(item->fullName);
152         }
153     }
154     return fullNameList;
155 }
156 
GetStylishFontList()157 std::unordered_set<std::string> FontDescriptorCache::GetStylishFontList()
158 {
159     std::unordered_set<std::string> fullNameList;
160     for (const auto& temp : stylishFullNameMap_) {
161         fullNameList.emplace(temp.first);
162     }
163     return fullNameList;
164 }
165 
GetGenericFontList()166 std::unordered_set<std::string> FontDescriptorCache::GetGenericFontList()
167 {
168     std::unordered_set<std::string> fullNameList;
169     for (const auto& temp : allFontDescriptor_) {
170         fullNameList.emplace(temp->fullName);
171     }
172     return fullNameList;
173 }
174 
GetDynamicFontList()175 std::unordered_set<std::string> FontDescriptorCache::GetDynamicFontList()
176 {
177     std::unordered_set<std::string> fullNameList;
178     for (const auto& temp : dynamicFullNameMap_) {
179         fullNameList.emplace(temp.first);
180     }
181     return fullNameList;
182 }
183 
ProcessSystemFontType(int32_t systemFontType,int32_t & fontType)184 bool FontDescriptorCache::ProcessSystemFontType(int32_t systemFontType, int32_t& fontType)
185 {
186     if ((static_cast<uint32_t>(systemFontType) & (TextEngine::FontParser::SystemFontType::ALL |
187         TextEngine::FontParser::SystemFontType::GENERIC |
188         TextEngine::FontParser::SystemFontType::STYLISH |
189         TextEngine::FontParser::SystemFontType::INSTALLED |
190         TextEngine::FontParser::SystemFontType::CUSTOMIZED)) != systemFontType) {
191         TEXT_LOGE("Invalid system font type %{public}d", systemFontType);
192         return false;
193     }
194     fontType = systemFontType;
195     if (static_cast<uint32_t>(systemFontType) & TextEngine::FontParser::SystemFontType::ALL) {
196         fontType = TextEngine::FontParser::SystemFontType::GENERIC |
197             TextEngine::FontParser::SystemFontType::STYLISH |
198             TextEngine::FontParser::SystemFontType::INSTALLED |
199             TextEngine::FontParser::SystemFontType::CUSTOMIZED;
200     }
201     return true;
202 }
203 
GetSystemFontFullNamesByType(int32_t systemFontType,std::unordered_set<std::string> & fontList)204 void FontDescriptorCache::GetSystemFontFullNamesByType(
205     int32_t systemFontType, std::unordered_set<std::string> &fontList)
206 {
207     if (systemFontType < 0) {
208         TEXT_LOGE("Invalid system font type %{public}d", systemFontType);
209         return;
210     }
211     int32_t fontType = 0;
212     if (!ProcessSystemFontType(systemFontType, fontType)) {
213         fontList.clear();
214         return;
215     }
216 
217     ParserFontsByFontType(fontType);
218 
219     uint32_t fontCategory = static_cast<uint32_t>(fontType);
220     if (fontCategory & TextEngine::FontParser::SystemFontType::GENERIC) {
221         auto fullNameList = GetGenericFontList();
222         fontList.insert(fullNameList.begin(), fullNameList.end());
223     }
224 
225     if (fontCategory & TextEngine::FontParser::SystemFontType::STYLISH) {
226         auto fullNameList = GetStylishFontList();
227         fontList.insert(fullNameList.begin(), fullNameList.end());
228     }
229 
230     if (fontCategory & TextEngine::FontParser::SystemFontType::INSTALLED) {
231         auto fullNameList = GetInstallFontList();
232         fontList.insert(fullNameList.begin(), fullNameList.end());
233     }
234 
235     if (fontCategory & TextEngine::FontParser::SystemFontType::CUSTOMIZED) {
236         auto fullNameList = GetDynamicFontList();
237         fontList.insert(fullNameList.begin(), fullNameList.end());
238     }
239 }
240 
ParseInstallFontDescSharedPtrByName(const std::string & fullName,FontDescSharedPtr & result)241 bool FontDescriptorCache::ParseInstallFontDescSharedPtrByName(const std::string& fullName, FontDescSharedPtr& result)
242 {
243     std::vector<std::string> fontPathList;
244     if (!ParserInstallFontsPathList(fontPathList)) {
245         TEXT_LOGE("Failed to parser install fonts path list");
246         return false;
247     }
248     for (const auto& path : fontPathList) {
249         std::vector<FontDescSharedPtr> descriptors = parser_.ParserFontDescriptorsFromPath(path);
250         for (const auto& item : descriptors) {
251             if (item->fullName == fullName) {
252                 item->weight = WeightAlignment(item->weight);
253                 result = item;
254                 return true;
255             }
256         }
257     }
258     TEXT_LOGE_LIMIT3_MIN("Failed to parser installed font descriptor by full name: %{public}s", fullName.c_str());
259     return false;
260 }
261 
GetFontTypeFromParams(const std::string & fullName,int32_t systemFontType,int32_t & fontType)262 bool FontDescriptorCache::GetFontTypeFromParams(const std::string& fullName,
263     int32_t systemFontType, int32_t& fontType)
264 {
265     if (fullName.empty()) {
266         TEXT_LOGE("Empty full name");
267         return false;
268     }
269     if (!ProcessSystemFontType(systemFontType, fontType)) {
270         return false;
271     }
272     if (systemFontType < 0) {
273         TEXT_LOGE("Invalid system font type %{public}d", systemFontType);
274         return false;
275     }
276     return true;
277 }
278 
GetFontDescSharedPtrByFullName(const std::string & fullName,int32_t systemFontType,FontDescSharedPtr & result)279 void FontDescriptorCache::GetFontDescSharedPtrByFullName(const std::string& fullName,
280     int32_t systemFontType, FontDescSharedPtr& result)
281 {
282     int32_t fontType = 0;
283     if (!GetFontTypeFromParams(fullName, systemFontType, fontType)) {
284         return;
285     }
286 
287     ParserFontsByFontType(fontType);
288 
289     auto tryFindFontDescriptor = [&fullName, &result](const std::unordered_map<std::string,
290         std::set<FontDescSharedPtr>>& map) -> bool {
291         auto it = map.find(fullName);
292         if (it != map.end()) {
293             result = *(it->second.begin());
294             return true;
295         }
296         return false;
297     };
298 
299     uint32_t  fontCategory = static_cast<uint32_t>(fontType);
300     if ((fontCategory & TextEngine::FontParser::SystemFontType::GENERIC) &&
301         tryFindFontDescriptor(fullNameMap_)) {
302         return;
303     }
304     if ((fontCategory & TextEngine::FontParser::SystemFontType::STYLISH) &&
305         tryFindFontDescriptor(stylishFullNameMap_)) {
306         return;
307     }
308     if ((fontCategory & TextEngine::FontParser::SystemFontType::INSTALLED) &&
309         ParseInstallFontDescSharedPtrByName(fullName, result)) {
310         return;
311     }
312     if ((fontCategory & TextEngine::FontParser::SystemFontType::CUSTOMIZED)) {
313         auto it = dynamicFullNameMap_.find(fullName);
314         if (it != dynamicFullNameMap_.end()) {
315             result = dynamicFullNameMap_[fullName];
316             return;
317         }
318     }
319     TEXT_LOGD("Failed to get fontDescriptor by full name: %{public}s", fullName.c_str());
320     result = nullptr;
321 }
322 
CacheDynamicTypeface(std::shared_ptr<Drawing::Typeface> typeface,const std::string & familyName)323 void FontDescriptorCache::CacheDynamicTypeface(std::shared_ptr<Drawing::Typeface> typeface,
324     const std::string &familyName)
325 {
326     std::vector<std::shared_ptr<TextEngine::FontParser::FontDescriptor>> fontDescArr =
327         parser_.CreateFontDescriptors({typeface});
328     for (auto fontDesc : fontDescArr) {
329         fontDesc->fontFamily = familyName;
330         dynamicFullNameMap_[fontDesc->fontFamily] = fontDesc;
331     }
332 }
333 
DeleteDynamicTypefaceFromCache(const std::string & familyName)334 void FontDescriptorCache::DeleteDynamicTypefaceFromCache(const std::string &familyName)
335 {
336     dynamicFullNameMap_.erase(familyName);
337 }
338 
HandleMapIntersection(std::set<FontDescSharedPtr> & finishRet,const std::string & name,std::unordered_map<std::string,std::set<FontDescSharedPtr>> & map)339 bool FontDescriptorCache::HandleMapIntersection(std::set<FontDescSharedPtr>& finishRet, const std::string& name,
340     std::unordered_map<std::string, std::set<FontDescSharedPtr>>& map)
341 {
342     if (name.empty()) {
343         return true;
344     }
345     auto iter = map.find(name);
346     if (iter == map.end()) {
347         return false;
348     }
349     if (finishRet.empty()) {
350         finishRet = iter->second;
351     } else {
352         std::set<FontDescSharedPtr> temp;
353         std::set_intersection(iter->second.begin(), iter->second.end(), finishRet.begin(), finishRet.end(),
354             std::insert_iterator<std::set<FontDescSharedPtr>>(temp, temp.begin()));
355         if (temp.empty()) {
356             return false;
357         }
358         finishRet = std::move(temp);
359     }
360     return true;
361 }
362 
FilterBoldCache(int weight,std::set<FontDescSharedPtr> & finishRet)363 bool FontDescriptorCache::FilterBoldCache(int weight, std::set<FontDescSharedPtr>& finishRet)
364 {
365     if (weight < 0) {
366         return false;
367     }
368 
369     if (weight == 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 if (static_cast<uint32_t>(weight) > WEIGHT_400) {
380         begin = boldCache_.begin();
381         end = boldCache_.end();
382     } else {
383         begin = allFontDescriptor_.begin();
384         end = allFontDescriptor_.end();
385     }
386     std::for_each(begin, end, [&](FontDescSharedPtr item) {
387         if (item->weight == weight) {
388             temp.emplace(item);
389         }
390     });
391 
392     if (temp.empty()) {
393         TEXT_LOGD("Failed to match weight");
394         return false;
395     }
396     finishRet = std::move(temp);
397     return true;
398 }
399 
FilterWidthCache(int width,std::set<FontDescSharedPtr> & finishRet)400 bool FontDescriptorCache::FilterWidthCache(int width, std::set<FontDescSharedPtr>& finishRet)
401 {
402     if (width < 0) {
403         return false;
404     }
405 
406     if (width == 0) {
407         return true;
408     }
409 
410     std::set<FontDescSharedPtr> temp;
411     std::set<FontDescSharedPtr>::iterator begin;
412     std::set<FontDescSharedPtr>::iterator end;
413     if (!finishRet.empty()) {
414         begin = finishRet.begin();
415         end = finishRet.end();
416     } else {
417         begin = allFontDescriptor_.begin();
418         end = allFontDescriptor_.end();
419     }
420     std::for_each(begin, end, [&](FontDescSharedPtr item) {
421         if (item->width == width) {
422             temp.emplace(item);
423         }
424     });
425 
426     if (temp.empty()) {
427         TEXT_LOGD("Failed to match width");
428         return false;
429     }
430     finishRet = std::move(temp);
431     return true;
432 }
433 
FilterItalicCache(int italic,std::set<FontDescSharedPtr> & finishRet)434 bool FontDescriptorCache::FilterItalicCache(int italic, std::set<FontDescSharedPtr>& finishRet)
435 {
436     if (italic == 0) {
437         return true;
438     }
439     std::set<FontDescSharedPtr> temp;
440     if (!finishRet.empty()) {
441         std::for_each(finishRet.begin(), finishRet.end(), [&](FontDescSharedPtr item) {
442             if (item->italic != 0) {
443                 temp.emplace(item);
444             }
445         });
446     } else {
447         temp = italicCache_;
448     }
449     if (temp.empty()) {
450         TEXT_LOGD("Failed to match italic");
451         return false;
452     }
453     finishRet = std::move(temp);
454     return true;
455 }
456 
FilterMonoSpaceCache(bool monoSpace,std::set<FontDescSharedPtr> & finishRet)457 bool FontDescriptorCache::FilterMonoSpaceCache(bool monoSpace, std::set<FontDescSharedPtr>& finishRet)
458 {
459     if (!monoSpace) {
460         return true;
461     }
462 
463     std::set<FontDescSharedPtr> temp;
464     if (!finishRet.empty()) {
465         std::for_each(finishRet.begin(), finishRet.end(), [&](FontDescSharedPtr item) {
466             if (item->monoSpace) {
467                 temp.emplace(item);
468             }
469         });
470     } else {
471         temp = monoSpaceCache_;
472     }
473     if (temp.empty()) {
474         TEXT_LOGD("Failed to match monoSpace");
475         return false;
476     }
477     finishRet = std::move(temp);
478     return true;
479 }
480 
FilterSymbolicCache(bool symbolic,std::set<FontDescSharedPtr> & finishRet)481 bool FontDescriptorCache::FilterSymbolicCache(bool symbolic, std::set<FontDescSharedPtr>& finishRet)
482 {
483     if (!symbolic) {
484         return true;
485     }
486     std::set<FontDescSharedPtr> temp;
487     if (!finishRet.empty()) {
488         std::for_each(finishRet.begin(), finishRet.end(), [&](FontDescSharedPtr item) {
489             if (item->symbolic) {
490                 temp.emplace(item);
491             }
492         });
493     } else {
494         temp = symbolicCache_;
495     }
496     if (temp.empty()) {
497         TEXT_LOGD("Failed to match symbolic");
498         return false;
499     }
500     finishRet = std::move(temp);
501     return true;
502 }
503 
IsDefault(FontDescSharedPtr desc)504 bool FontDescriptorCache::IsDefault(FontDescSharedPtr desc)
505 {
506     if (desc->fontFamily.empty() && desc->fullName.empty() && desc->postScriptName.empty()
507         && desc->fontSubfamily.empty() && desc->weight == 0 && desc->width == 0 && desc->italic == 0
508         && !desc->monoSpace && !desc->symbolic) {
509         return true;
510     }
511     return false;
512 }
513 
MatchFromFontDescriptor(FontDescSharedPtr desc,std::set<FontDescSharedPtr> & result)514 void FontDescriptorCache::MatchFromFontDescriptor(FontDescSharedPtr desc, std::set<FontDescSharedPtr>& result)
515 {
516     if (desc == nullptr) {
517         TEXT_LOGE("Null desc");
518         return;
519     }
520     ParserSystemFonts();
521     if (IsDefault(desc)) {
522         result = std::set<FontDescSharedPtr>(allFontDescriptor_.begin(), allFontDescriptor_.end());
523         return;
524     }
525     desc->weight = (desc->weight > 0) ? WeightAlignment(desc->weight) : desc->weight;
526     std::set<FontDescSharedPtr> finishRet;
527     TEXT_INFO_CHECK(HandleMapIntersection(finishRet, desc->fontFamily, fontFamilyMap_), return,
528         "Failed to match font family");
529     TEXT_INFO_CHECK(HandleMapIntersection(finishRet, desc->fullName, fullNameMap_), return, "Failed to match fullName");
530     TEXT_INFO_CHECK(HandleMapIntersection(finishRet, desc->postScriptName, postScriptNameMap_), return,
531         "Failed to match post script name");
532     TEXT_INFO_CHECK(HandleMapIntersection(finishRet, desc->fontSubfamily, fontSubfamilyNameMap_), return,
533         "Failed to match font subfamily");
534 
535     TEXT_CHECK(FilterBoldCache(desc->weight, finishRet), return);
536     TEXT_CHECK(FilterWidthCache(desc->width, finishRet), return);
537     TEXT_CHECK(FilterItalicCache(desc->italic, finishRet), return);
538     TEXT_CHECK(FilterMonoSpaceCache(desc->monoSpace, finishRet), return);
539     TEXT_CHECK(FilterSymbolicCache(desc->symbolic, finishRet), return);
540     result = std::move(finishRet);
541 }
542 
Dump()543 void FontDescriptorCache::Dump()
544 {
545     TEXT_LOGD("allFontDescriptor size: %{public}zu, fontFamilyMap size: %{public}zu, fullNameMap size: %{public}zu \
546         postScriptNameMap size: %{public}zu, fontSubfamilyNameMap size: %{public}zu, boldCache size: %{public}zu \
547         italicCache size: %{public}zu, monoSpaceCache size: %{public}zu, symbolicCache size: %{public}zu",
548         allFontDescriptor_.size(), fontFamilyMap_.size(), fullNameMap_.size(), postScriptNameMap_.size(),
549         fontSubfamilyNameMap_.size(), boldCache_.size(), italicCache_.size(), monoSpaceCache_.size(),
550         symbolicCache_.size());
551 }
552 
WeightAlignment(int32_t weight)553 int32_t FontDescriptorCache::WeightAlignment(int32_t weight)
554 {
555     if (weight < Drawing::FontStyle::THIN_WEIGHT) {
556         return Drawing::FontStyle::THIN_WEIGHT;
557     }
558 
559     if (weight > Drawing::FontStyle::EXTRA_BLACK_WEIGHT) {
560         return Drawing::FontStyle::EXTRA_BLACK_WEIGHT;
561     }
562 
563     if ((weight % WEIGHT_MODULE) == 0) {
564         return weight;
565     }
566 
567     static const std::vector<int> weightType = {
568         Drawing::FontStyle::THIN_WEIGHT,
569         Drawing::FontStyle::EXTRA_LIGHT_WEIGHT,
570         Drawing::FontStyle::LIGHT_WEIGHT,
571         Drawing::FontStyle::NORMAL_WEIGHT,
572         Drawing::FontStyle::MEDIUM_WEIGHT,
573         Drawing::FontStyle::SEMI_BOLD_WEIGHT,
574         Drawing::FontStyle::BOLD_WEIGHT,
575         Drawing::FontStyle::EXTRA_BOLD_WEIGHT,
576         Drawing::FontStyle::BLACK_WEIGHT,
577         Drawing::FontStyle::EXTRA_BLACK_WEIGHT
578     };
579     // Obtain weight ranges for non-whole hundred values
580     auto it = std::lower_bound(weightType.begin(), weightType.end(), weight);
581     std::vector<int> targetRange = { *(it - 1), *it };
582 
583     /**
584      * When the font weight is less than NORMAL_WEIGHT, round down as much as possible;
585      * when the font weight exceeds NORMAL_WEIGHT, round up where possible. For example, when weight is 360,
586      * the final font weight is set to 300; when weight is 620, the final font weight is set to 700.
587      */
588     uint32_t minDiff = 0xFFFFFFFF;
589     int resultWeight = 0;
590     for (const auto& item : targetRange) {
591         /**
592          * The maximum weight is EXTRA_BLACK_WEIGHT (1000), when weight and item are at the different
593          * side of NORMAL_WEIGHT, the weight difference between them should be more than 500 (1000/2).
594          */
595         uint32_t weightDiff = 0;
596         constexpr int kWeightDiffThreshold = Drawing::FontStyle::EXTRA_BLACK_WEIGHT / 2;
597         if ((weight == Drawing::FontStyle::NORMAL_WEIGHT && item == Drawing::FontStyle::MEDIUM_WEIGHT) ||
598             (weight == Drawing::FontStyle::MEDIUM_WEIGHT && item == Drawing::FontStyle::NORMAL_WEIGHT)) {
599             weightDiff = static_cast<uint32_t>(SPECIAL_WEIGHT_DIFF);
600         } else if (weight <= Drawing::FontStyle::NORMAL_WEIGHT) {
601             weightDiff = (item <= weight) ? static_cast<uint32_t>(weight - item) :
602                 static_cast<uint32_t>(item - weight + kWeightDiffThreshold);
603         } else if (weight > Drawing::FontStyle::NORMAL_WEIGHT) {
604             weightDiff = (item >= weight) ? static_cast<uint32_t>(item - weight) :
605                 static_cast<uint32_t>(weight - item + kWeightDiffThreshold);
606         }
607 
608         // Retain the font weight with the smallest difference
609         if (weightDiff < minDiff) {
610             minDiff = weightDiff;
611             resultWeight = item;
612         }
613     }
614     return resultWeight;
615 }
616 }