1 /*
2 * Copyright (c) 2021 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 <regex>
17 #include <unordered_map>
18
19 #include "core/components/common/properties/text_style.h"
20
21 namespace OHOS::Ace {
22 namespace {
23
24 constexpr int32_t FONT_FEATURE_MAX_SIZE = 2;
25 constexpr int32_t FONT_FEATURE_KEY_LENGTH = 6;
26 constexpr int32_t FONT_FEATURE_PARENTHESES_LENGTH = 2; // length of ()
27 const char FONT_FEATURE_NONE[] = "none";
28 const char FONT_FEATURE_NORMAL[] = "normal";
29 const char FONT_FEATURE_ON[] = "on";
30 const char FONT_FEATURE_OFF[] = "off";
31
32 } // namespace
33
ParseFontVariantCaps(const std::string & fontVariant,std::unordered_map<std::string,int32_t> & fontFeatures)34 bool ParseFontVariantCaps(const std::string& fontVariant, std::unordered_map<std::string, int32_t>& fontFeatures)
35 {
36 if (fontVariant == FONT_FEATURE_NORMAL || fontVariant.empty()) {
37 return true;
38 }
39
40 const std::unordered_map<std::string, void (*)(std::unordered_map<std::string, int32_t>&)> operators = {
41 { "small-caps",
42 [](std::unordered_map<std::string, int32_t>& fontFeatures) { fontFeatures.try_emplace("smcp", 1); } },
43 { "all-small-caps",
44 [](std::unordered_map<std::string, int32_t>& fontFeatures) {
45 fontFeatures.try_emplace("c2sc", 1);
46 fontFeatures.try_emplace("smcp", 1);
47 } },
48 { "petite-caps",
49 [](std::unordered_map<std::string, int32_t>& fontFeatures) { fontFeatures.try_emplace("pcap", 1); } },
50 { "all-petite-caps",
51 [](std::unordered_map<std::string, int32_t>& fontFeatures) {
52 fontFeatures.try_emplace("c2pc", 1);
53 fontFeatures.try_emplace("pcap", 1);
54 } },
55 { "unicase",
56 [](std::unordered_map<std::string, int32_t>& fontFeatures) { fontFeatures.try_emplace("unic", 1); } },
57 { "titling-caps",
58 [](std::unordered_map<std::string, int32_t>& fontFeatures) { fontFeatures.try_emplace("titl", 1); } },
59 };
60 auto iter = operators.find(fontVariant);
61 if (iter != operators.end()) {
62 iter->second(fontFeatures);
63 return true;
64 }
65 return false;
66 }
67
ParseFontVariantNumeric(const std::string & fontVariant,std::unordered_map<std::string,int32_t> & fontFeatures)68 bool ParseFontVariantNumeric(const std::string& fontVariant, std::unordered_map<std::string, int32_t>& fontFeatures)
69 {
70 if (fontVariant == FONT_FEATURE_NORMAL || fontVariant.empty()) {
71 return true;
72 }
73
74 const std::unordered_map<std::string, void (*)(std::unordered_map<std::string, int32_t>&)> operators = {
75 { "ordinal",
76 [](std::unordered_map<std::string, int32_t>& fontFeatures) { fontFeatures.try_emplace("ordn", 1); } },
77 { "slashed-zero",
78 [](std::unordered_map<std::string, int32_t>& fontFeatures) { fontFeatures.try_emplace("zero", 1); } },
79 { "lining-nums",
80 [](std::unordered_map<std::string, int32_t>& fontFeatures) { fontFeatures.try_emplace("lnum", 1); } },
81 { "oldstyle-nums",
82 [](std::unordered_map<std::string, int32_t>& fontFeatures) { fontFeatures.try_emplace("onum", 1); } },
83 { "proportional-nums",
84 [](std::unordered_map<std::string, int32_t>& fontFeatures) { fontFeatures.try_emplace("pnum", 1); } },
85 { "tabular-nums",
86 [](std::unordered_map<std::string, int32_t>& fontFeatures) { fontFeatures.try_emplace("tnum", 1); } },
87 { "diagonal-fractions",
88 [](std::unordered_map<std::string, int32_t>& fontFeatures) { fontFeatures.try_emplace("frac", 1); } },
89 { "stacked-fractions",
90 [](std::unordered_map<std::string, int32_t>& fontFeatures) { fontFeatures.try_emplace("afrc", 1); } },
91 };
92 auto iter = operators.find(fontVariant);
93 if (iter != operators.end()) {
94 iter->second(fontFeatures);
95 return true;
96 }
97 return false;
98 }
99
ParseFontVariantAlternates(const std::string & fontVariant,std::unordered_map<std::string,int32_t> & fontFeatures)100 bool ParseFontVariantAlternates(const std::string& fontVariant, std::unordered_map<std::string, int32_t>& fontFeatures)
101 {
102 // format of font-variant-alternates is key(value) | normal | historical-forms.
103 if (fontVariant == FONT_FEATURE_NORMAL || fontVariant.empty()) {
104 return true;
105 }
106 if (fontVariant != "historical-forms" && !regex_match(fontVariant, std::regex(".+(.+)"))) {
107 return false;
108 }
109
110 std::string key = fontVariant;
111 int32_t value = 1;
112 auto valueIndex = fontVariant.find_first_of("(");
113 if (valueIndex != std::string::npos && fontVariant.size() > valueIndex) {
114 key = fontVariant.substr(0, valueIndex);
115 value = StringUtils::StringToInt(
116 fontVariant.substr(valueIndex + 1, fontVariant.size() - key.size() - FONT_FEATURE_PARENTHESES_LENGTH));
117 }
118 const std::unordered_map<std::string,
119 void (*)(const std::string&, int32_t, std::unordered_map<std::string, int32_t>&)>
120 operators = {
121 { "historical-forms",
122 [](const std::string& key, int32_t value, std::unordered_map<std::string, int32_t>& fontFeatures) {
123 fontFeatures.try_emplace("hist", 1);
124 } },
125 { "stylistic",
126 [](const std::string& key, int32_t value, std::unordered_map<std::string, int32_t>& fontFeatures) {
127 fontFeatures.try_emplace("salt", value);
128 } },
129 { "styleset",
130 [](const std::string& key, int32_t value, std::unordered_map<std::string, int32_t>& fontFeatures) {
131 // Convert value to ssxx, like 1 to ss01.
132 std::string temp = "0" + std::to_string(value);
133 fontFeatures.try_emplace("ss" + temp.substr(temp.size() - 2, 2), 1);
134 } },
135 { "character-variant",
136 [](const std::string& key, int32_t value, std::unordered_map<std::string, int32_t>& fontFeatures) {
137 // Convert value to cvxx, like 1 to cv01.
138 std::string temp = "0" + std::to_string(value);
139 fontFeatures.try_emplace("cv" + temp.substr(temp.size() - 2, 2), 1);
140 } },
141 { "swash",
142 [](const std::string& key, int32_t value, std::unordered_map<std::string, int32_t>& fontFeatures) {
143 fontFeatures.try_emplace("swsh", value);
144 fontFeatures.try_emplace("cswh", value);
145 } },
146 { "ornaments",
147 [](const std::string& key, int32_t value, std::unordered_map<std::string, int32_t>& fontFeatures) {
148 fontFeatures.try_emplace("ornm", value);
149 } },
150 { "annotation",
151 [](const std::string& key, int32_t value, std::unordered_map<std::string, int32_t>& fontFeatures) {
152 fontFeatures.try_emplace("nalt", value);
153 } },
154 };
155 auto iter = operators.find(key);
156 if (iter != operators.end()) {
157 iter->second(key, value, fontFeatures);
158 return true;
159 }
160 return false;
161 }
162
ParseFontVariantLigatures(const std::string & fontVariant,std::unordered_map<std::string,int32_t> & fontFeatures)163 bool ParseFontVariantLigatures(const std::string& fontVariant, std::unordered_map<std::string, int32_t>& fontFeatures)
164 {
165 if (fontVariant == FONT_FEATURE_NORMAL || fontVariant.empty()) {
166 return true;
167 }
168
169 const std::unordered_map<std::string, void (*)(std::unordered_map<std::string, int32_t>&)> operators = {
170 { "none",
171 [](std::unordered_map<std::string, int32_t>& fontFeatures) {
172 fontFeatures.try_emplace("liga ", 0);
173 fontFeatures.try_emplace("clig ", 0);
174 fontFeatures.try_emplace("dlig ", 0);
175 fontFeatures.try_emplace("hlig ", 0);
176 fontFeatures.try_emplace("calt ", 0);
177 } },
178 { "common-ligatures",
179 [](std::unordered_map<std::string, int32_t>& fontFeatures) {
180 fontFeatures.try_emplace("liga ", 1);
181 fontFeatures.try_emplace("clig ", 1);
182 } },
183 { "no-common-ligatures",
184 [](std::unordered_map<std::string, int32_t>& fontFeatures) {
185 fontFeatures.try_emplace("liga", 0);
186 fontFeatures.try_emplace("clig", 0);
187 } },
188 { "discretionary-ligatures",
189 [](std::unordered_map<std::string, int32_t>& fontFeatures) { fontFeatures.try_emplace("dlig", 1); } },
190 { "no-discretionary-ligatures",
191 [](std::unordered_map<std::string, int32_t>& fontFeatures) { fontFeatures.try_emplace("dlig", 0); } },
192 { "historical-ligatures",
193 [](std::unordered_map<std::string, int32_t>& fontFeatures) { fontFeatures.try_emplace("hlig", 1); } },
194 { "no-historical-ligatures",
195 [](std::unordered_map<std::string, int32_t>& fontFeatures) { fontFeatures.try_emplace("hlig", 0); } },
196 { "contextual",
197 [](std::unordered_map<std::string, int32_t>& fontFeatures) { fontFeatures.try_emplace("calt", 1); } },
198 { "no-contextual",
199 [](std::unordered_map<std::string, int32_t>& fontFeatures) { fontFeatures.try_emplace("calt", 0); } },
200 };
201 auto iter = operators.find(fontVariant);
202 if (iter != operators.end()) {
203 iter->second(fontFeatures);
204 return true;
205 }
206 return false;
207 }
208
ParseFontVariantEastAsian(const std::string & fontVariant,std::unordered_map<std::string,int32_t> & fontFeatures)209 bool ParseFontVariantEastAsian(const std::string& fontVariant, std::unordered_map<std::string, int32_t>& fontFeatures)
210 {
211 if (fontVariant == FONT_FEATURE_NORMAL || fontVariant.empty()) {
212 return true;
213 }
214
215 const std::unordered_map<std::string, void (*)(std::unordered_map<std::string, int32_t>&)> operators = {
216 { "ruby", [](std::unordered_map<std::string, int32_t>& fontFeatures) { fontFeatures.try_emplace("ruby", 1); } },
217 { "jis78",
218 [](std::unordered_map<std::string, int32_t>& fontFeatures) { fontFeatures.try_emplace("jp78", 1); } },
219 { "jis83",
220 [](std::unordered_map<std::string, int32_t>& fontFeatures) { fontFeatures.try_emplace("jp83", 1); } },
221 { "jis90",
222 [](std::unordered_map<std::string, int32_t>& fontFeatures) { fontFeatures.try_emplace("jp90", 1); } },
223 { "jis04",
224 [](std::unordered_map<std::string, int32_t>& fontFeatures) { fontFeatures.try_emplace("jp04", 1); } },
225 { "simplified",
226 [](std::unordered_map<std::string, int32_t>& fontFeatures) { fontFeatures.try_emplace("smpl", 1); } },
227 { "traditional",
228 [](std::unordered_map<std::string, int32_t>& fontFeatures) { fontFeatures.try_emplace("trad", 1); } },
229 { "proportional-width",
230 [](std::unordered_map<std::string, int32_t>& fontFeatures) { fontFeatures.try_emplace("pwid", 1); } },
231 { "full-width",
232 [](std::unordered_map<std::string, int32_t>& fontFeatures) { fontFeatures.try_emplace("fwid", 1); } },
233 };
234 auto iter = operators.find(fontVariant);
235 if (iter != operators.end()) {
236 iter->second(fontFeatures);
237 return true;
238 }
239 return false;
240 }
241
ParseFontVariant(const std::string & fontVariant,std::unordered_map<std::string,int32_t> & fontFeatures)242 void ParseFontVariant(const std::string& fontVariant, std::unordered_map<std::string, int32_t>& fontFeatures)
243 {
244 if (fontVariant.empty()) {
245 return;
246 }
247 auto tempFontVariant = fontVariant;
248 StringUtils::TrimStrLeadingAndTrailing(tempFontVariant);
249 if (ParseFontVariantCaps(tempFontVariant, fontFeatures)) {
250 return;
251 }
252 if (ParseFontVariantNumeric(tempFontVariant, fontFeatures)) {
253 return;
254 }
255 if (ParseFontVariantAlternates(tempFontVariant, fontFeatures)) {
256 return;
257 }
258 if (ParseFontVariantLigatures(tempFontVariant, fontFeatures)) {
259 return;
260 }
261 if (ParseFontVariantEastAsian(tempFontVariant, fontFeatures)) {
262 return;
263 }
264 }
265
266 /*
267 * Format of font-feature-settings:
268 * normal | none | <string>
269 * number of <string> can be single or multiple, and separated by space ' '
270 */
ParseFontVariants(const std::string & fontVariants)271 std::unordered_map<std::string, int32_t> ParseFontVariants(const std::string& fontVariants)
272 {
273 std::unordered_map<std::string, int32_t> fontFeatures;
274 if (fontVariants == FONT_FEATURE_NORMAL || fontVariants.empty()) {
275 return fontFeatures;
276 }
277
278 if (fontVariants == FONT_FEATURE_NONE) {
279 fontFeatures.try_emplace("liga ", 0);
280 fontFeatures.try_emplace("clig ", 0);
281 fontFeatures.try_emplace("dlig ", 0);
282 fontFeatures.try_emplace("hlig ", 0);
283 fontFeatures.try_emplace("calt ", 0);
284 return fontFeatures;
285 }
286
287 std::stringstream stream(fontVariants);
288 std::string fontVariant;
289 while (getline(stream, fontVariant, ' ')) {
290 ParseFontVariant(fontVariant, fontFeatures);
291 }
292 return fontFeatures;
293 }
294
ParseFontFeatureSetting(const std::string & fontFeatureSetting,std::unordered_map<std::string,int32_t> & fontFeatures)295 void ParseFontFeatureSetting(
296 const std::string& fontFeatureSetting, std::unordered_map<std::string, int32_t>& fontFeatures)
297 {
298 if (fontFeatureSetting.empty()) {
299 LOGW("ParseFontFeatureSetting fontFeatureSetting is empty");
300 return;
301 }
302
303 auto temp = fontFeatureSetting;
304 StringUtils::TrimStrLeadingAndTrailing(temp);
305 if (temp.empty()) {
306 LOGW("ParseFontFeatureSetting fontFeatureSetting is empty");
307 return;
308 }
309
310 std::vector<std::string> value;
311 StringUtils::StringSplitter(temp, ' ', value);
312 if (value.empty() || value.size() > FONT_FEATURE_MAX_SIZE || value[0].size() != FONT_FEATURE_KEY_LENGTH) {
313 LOGW("ParseFontFeatureSetting param is invalid");
314 return;
315 }
316
317 switch (value.size()) {
318 case 1:
319 fontFeatures.try_emplace(value[0], 1);
320 break;
321 case 2:
322 fontFeatures.try_emplace(value[0],
323 value[1] == FONT_FEATURE_ON ? 1
324 : (value[1] == FONT_FEATURE_OFF ? 0 : StringUtils::StringToInt(value[1])));
325 break;
326 default:
327 LOGW("ParseFontFeatureSetting format of font-feature-settings is invalid");
328 break;
329 }
330 }
331
332 /*
333 * Format of font-feature-settings:
334 * normal | <feature-tag-value>, where <feature-tag-value> = <string> [ <integer> | on | off ], like: "liga" 0
335 * number of <feature-tag-value> can be single or multiple, and separated by comma ','
336 */
ParseFontFeatureSettings(const std::string & fontFeatureSettings)337 std::unordered_map<std::string, int32_t> ParseFontFeatureSettings(const std::string& fontFeatureSettings)
338 {
339 std::unordered_map<std::string, int32_t> fontFeatures;
340 if (fontFeatureSettings == FONT_FEATURE_NORMAL || fontFeatureSettings.empty()) {
341 return fontFeatures;
342 }
343
344 std::stringstream stream(fontFeatureSettings);
345 std::string fontFeatureSetting;
346 while (getline(stream, fontFeatureSetting, ',')) {
347 ParseFontFeatureSetting(fontFeatureSetting, fontFeatures);
348 }
349 return fontFeatures;
350 }
351
352 } // namespace OHOS::Ace