1 /*
2  * Copyright (c) 2021-2022 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 "core/components/theme/theme_utils.h"
17 
18 #include <regex>
19 
20 #include "core/components/theme/theme_constants.h"
21 #include "core/components/theme/theme_constants_defines.h"
22 
23 namespace OHOS::Ace {
24 namespace {
25 
26 constexpr uint32_t THEME_ID_MIN_SIZE = 5; // Input should contain "@id"
27 constexpr uint32_t THEME_ID_MATCH_SIZE = 2;
28 const std::regex THEME_ID_REGEX(R"(^\"@id([0-9]+)\"$)", std::regex::icase); // regex for "@id001"
29 constexpr uint32_t THEME_ATTR_MIN_SIZE = 7;
30 const std::regex THEME_ATTR_REGEX(R"(\?theme:([a-zA-Z0-9_]+))"); // regex for "?theme:attr_color_emphasis"
31 constexpr uint32_t CUSTOM_STYLE_STRING_MAX_SIZE = 128;
32 constexpr uint32_t OHOS_ID_MIN_SIZE = 7; // Input should contain "@ohos_id"
33 constexpr uint32_t SYSTEM_RES_ID_START = 0x7000000;
34 const std::regex OHOS_ID_REGEX(R"(^@ohos_id_([0-9]+)$)", std::regex::icase); // regex for "@ohos_id_001"
35 // regex for "@sys.type.xxx", xxx represents system resource id.
36 const std::regex SYS_TYPE_RES_ID_REGEX(R"(^@sys.(\w+).([0-9]+)$)", std::regex::icase);
37 constexpr uint32_t SYS_TYPE_RES_ID_MIN_SIZE = 7;
38 // regex for "@app.type.xxx", xxx represents application name.
39 const std::regex APP_TYPE_RES_NAME_REGEX(R"(^@app.(\w+).(\w+)$)", std::regex::icase);
40 constexpr uint32_t APP_TYPE_RES_NAME_MIN_SIZE = 7;
41 constexpr uint32_t TYPE_RESOURCE_MATCH_SIZE = 3;
42 // regex for "@sys.media.xxx", xxx represents system resource id.
43 const std::regex SYS_MEDIA_RES_ID_REGEX(R"(^@sys\.media\.([0-9]+)$)", std::regex::icase);
44 // regex for "@app.media.xxx", xxx represents resource name.
45 const std::regex APP_MEDIA_RES_NAME_REGEX(R"(^@app\.media\.(\w+)$)", std::regex::icase);
46 constexpr uint32_t MEDIA_RESOURCE_MATCH_SIZE = 2;
47 
48 const std::set<uint32_t> FONT_WEIGHT_STYLE_ID = {
49     THEME_BUTTON_TEXT_FONTWEIGHT,
50     THEME_DIALOG_TITLE_TEXT_FONTWEIGHT,
51     THEME_TOAST_TEXT_TEXT_FONTWEIGHT,
52     THEME_TEXTFIELD_FONT_WEIGHT,
53     THEME_SEARCH_FONT_WEIGHT
54 };
55 
56 } // namespace
57 
ParseThemeIdReference(const std::string & str,const RefPtr<ThemeConstants> & themeConstants)58 IdParseResult ThemeUtils::ParseThemeIdReference(const std::string& str, const RefPtr<ThemeConstants>& themeConstants)
59 {
60     std::smatch matches;
61     IdParseResult result { .parseSuccess = false, .isIdRef = false, .id = 0, .refAttr = "" };
62     if (str.size() > THEME_ID_MIN_SIZE && std::regex_match(str, matches, THEME_ID_REGEX) &&
63         matches.size() == THEME_ID_MATCH_SIZE) {
64         // Platform style id is no more than 32 bit.
65         result.id = static_cast<uint32_t>(std::stoul(matches[1].str()));
66         result.parseSuccess = true;
67         result.isIdRef = true;
68         return result;
69     }
70     if (str.size() > THEME_ATTR_MIN_SIZE && std::regex_match(str, matches, THEME_ATTR_REGEX) &&
71         matches.size() == THEME_ID_MATCH_SIZE) {
72         result.refAttr = matches[1].str();
73         result.parseSuccess = true;
74         result.isIdRef = false;
75         return result;
76     }
77     if (str.size() > OHOS_ID_MIN_SIZE && std::regex_match(str, matches, OHOS_ID_REGEX) &&
78         matches.size() == THEME_ID_MATCH_SIZE) {
79         // Platform style id is no more than 32 bit.
80         result.id = static_cast<uint32_t>(std::stoul(matches[1].str())) + SYSTEM_RES_ID_START;
81         result.parseSuccess = true;
82         result.isIdRef = true;
83         return result;
84     }
85     if (str.size() > SYS_TYPE_RES_ID_MIN_SIZE && std::regex_match(str, matches, SYS_TYPE_RES_ID_REGEX) &&
86         matches.size() == TYPE_RESOURCE_MATCH_SIZE) {
87         result.id = static_cast<uint32_t>(std::stoul(matches[2].str())) + SYSTEM_RES_ID_START;
88         result.parseSuccess = true;
89         result.isIdRef = true;
90         return result;
91     }
92     if (str.size() > APP_TYPE_RES_NAME_MIN_SIZE && std::regex_match(str, matches, APP_TYPE_RES_NAME_REGEX) &&
93         matches.size() == TYPE_RESOURCE_MATCH_SIZE) {
94         uint32_t resId = 0;
95         if (themeConstants && themeConstants->GetResourceIdByName(matches[2].str(), matches[1].str(), resId)) {
96             result.id = resId;
97             result.parseSuccess = true;
98             result.isIdRef = true;
99             return result;
100         }
101     }
102     // Not reference format, ignore.
103     return result;
104 }
105 
ParseStyleValue(uint32_t styleId,const ResValueWrapper & model,const std::string & value)106 ResValueWrapper ThemeUtils::ParseStyleValue(
107     uint32_t styleId, const ResValueWrapper& model, const std::string& value)
108 {
109     ResValueWrapper resultValue = { .type = model.type, .isPublic = model.isPublic };
110     if (FONT_WEIGHT_STYLE_ID.count(styleId) > 0) {
111         resultValue.value = static_cast<int32_t>(StringUtils::StringToFontWeight(value));
112         return resultValue;
113     }
114     switch (model.type) {
115         case ThemeConstantsType::COLOR:
116             resultValue.value = Color::FromString(value, COLOR_ALPHA_MASK);
117             break;
118         case ThemeConstantsType::DIMENSION:
119             resultValue.value = StringUtils::StringToDimension(value);
120             break;
121         case ThemeConstantsType::INT:
122             resultValue.value = StringUtils::StringToInt(value);
123             break;
124         case ThemeConstantsType::DOUBLE:
125             resultValue.value = StringUtils::StringToDouble(value);
126             break;
127         case ThemeConstantsType::STRING:
128             if (value.size() < CUSTOM_STYLE_STRING_MAX_SIZE) {
129                 resultValue.value = value;
130             } else {
131                 resultValue.type = ThemeConstantsType::ERROR;
132             }
133             break;
134         default:
135             resultValue.type = ThemeConstantsType::ERROR;
136             break;
137     }
138     return resultValue;
139 }
140 
ProcessImageSource(const std::string & imageSrc,const RefPtr<ThemeConstants> & themeConstants)141 std::string ThemeUtils::ProcessImageSource(const std::string& imageSrc, const RefPtr<ThemeConstants>& themeConstants)
142 {
143     std::smatch matches;
144     uint32_t resId = 0;
145     std::string resName;
146     if (std::regex_match(imageSrc, matches, APP_MEDIA_RES_NAME_REGEX) && matches.size() == MEDIA_RESOURCE_MATCH_SIZE) {
147         resName = matches[1].str();
148     }
149     if (std::regex_match(imageSrc, matches, SYS_MEDIA_RES_ID_REGEX) && matches.size() == MEDIA_RESOURCE_MATCH_SIZE) {
150         resId = static_cast<uint32_t>(std::stoul(matches[1].str())) + SYSTEM_RES_ID_START;
151     }
152     // not a image from global global resource manager subsystem, no need process.
153     if (resId == 0 && resName.empty()) {
154         return imageSrc;
155     }
156 
157     if (!themeConstants) {
158         return "";
159     }
160     if (resId == 0 && !themeConstants->GetResourceIdByName(resName, "media", resId)) {
161         TAG_LOGW(AceLogTag::ACE_THEME, "get image id failed, name: %{public}s", resName.c_str());
162         return "";
163     }
164 
165     std::string imagePath = themeConstants->GetString(resId);
166     auto seperatorPos = imagePath.rfind('.');
167     if (seperatorPos == std::string::npos) {
168         return "";
169     }
170     // image format suffix, such as ".png",".svg" and so on.
171     std::string imageSuffix = imagePath.substr(seperatorPos);
172 
173     // resource name or resource id of image in global resource manager subsystem is the same in dark or light mode.
174     // image will not be reloaded if name is not changed when switching between dark and light modes.
175     std::string colorMode;
176     switch (SystemProperties::GetColorMode()) {
177         case ColorMode::LIGHT:
178             colorMode = "light";
179             break;
180         case ColorMode::DARK:
181             colorMode = "dark";
182             break;
183         default:
184             colorMode = "undefined";
185             break;
186     }
187     std::string result = "resource://" + colorMode + "/" + std::to_string(resId) + imageSuffix;
188     return result;
189 }
190 
191 } // namespace OHOS::Ace