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