• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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