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