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