1 /*
2 * Copyright (c) 2024 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 "html_to_span.h"
17 #include <sstream>
18
19 #include "core/text/html_utils.h"
20
21 namespace OHOS::Ace {
22 constexpr int ONE_PARAM = 1;
23 constexpr int TWO_PARAM = 2;
24 constexpr int THREE_PARAM = 3;
25 constexpr int FOUR_PARAM = 4;
26
27 constexpr int TOP_PARAM = 0;
28 constexpr int RIGHT_PARAM = 1;
29 constexpr int BOTTOM_PARAM = 2;
30 constexpr int LEFT_PARAM = 3;
31 constexpr int FIRST_PARAM = 0;
32 constexpr int SECOND_PARAM = 1;
33 constexpr int THIRD_PARAM = 2;
34 constexpr int FOURTH_PARAM = 3;
35
36 constexpr int MAX_STYLE_FORMAT_NUMBER = 3;
37
ToLowerCase(std::string & str)38 void ToLowerCase(std::string& str)
39 {
40 for (auto& c : str) {
41 c = tolower(c);
42 }
43 }
44
ParseFontFamily(const std::string & fontFamily)45 std::vector<std::string> ParseFontFamily(const std::string& fontFamily)
46 {
47 std::vector<std::string> fonts;
48 std::stringstream ss(fontFamily);
49 std::string token;
50 while (std::getline(ss, token, ',')) {
51 std::string font = std::string(token.begin(), token.end());
52 font.erase(std::remove_if(font.begin(), font.end(), isspace), font.end());
53
54 if (!font.empty()) {
55 fonts.push_back(font);
56 }
57 }
58
59 return fonts;
60 }
61
StringToVerticalAlign(const std::string & align)62 VerticalAlign StringToVerticalAlign(const std::string& align)
63 {
64 if (align == "bottom") {
65 return VerticalAlign::BOTTOM;
66 }
67 if (align == "middle") {
68 return VerticalAlign::CENTER;
69 }
70 if (align == "top") {
71 return VerticalAlign::TOP;
72 }
73 return VerticalAlign::NONE;
74 }
75
StringToFontStyle(const std::string & fontStyle)76 FontStyle StringToFontStyle(const std::string& fontStyle)
77 {
78 return fontStyle == "italic" ? FontStyle::ITALIC : FontStyle::NORMAL;
79 }
80
StringToSuperscriptStyle(const std::string & superscriptStyle)81 SuperscriptStyle StringToSuperscriptStyle(const std::string& superscriptStyle)
82 {
83 if (superscriptStyle == "superscript") {
84 return SuperscriptStyle::SUPERSCRIPT;
85 } else if (superscriptStyle == "subscript") {
86 return SuperscriptStyle::SUBSCRIPT;
87 } else if (superscriptStyle == "normal") {
88 return SuperscriptStyle::NORMAL;
89 }
90 return SuperscriptStyle::NONE;
91 }
92
StringToTextDecorationStyle(const std::string & textDecorationStyle)93 TextDecorationStyle StringToTextDecorationStyle(const std::string& textDecorationStyle)
94 {
95 std::string value = StringUtils::TrimStr(textDecorationStyle);
96 if (value == "dashed") {
97 return TextDecorationStyle::DASHED;
98 }
99 if (value == "dotted") {
100 return TextDecorationStyle::DOTTED;
101 }
102 if (value == "double") {
103 return TextDecorationStyle::DOUBLE;
104 }
105 if (value == "solid") {
106 return TextDecorationStyle::SOLID;
107 }
108 if (value == "wavy") {
109 return TextDecorationStyle::WAVY;
110 }
111
112 return TextDecorationStyle::SOLID;
113 }
114
StringToTextDecoration(const std::string & textDecoration)115 std::vector<TextDecoration> StringToTextDecoration(const std::string& textDecoration)
116 {
117 std::istringstream ss(textDecoration);
118 std::string tmp;
119 std::vector<std::string> decorations;
120 while (std::getline(ss, tmp, ' ')) {
121 decorations.emplace_back(tmp);
122 }
123 std::vector<TextDecoration> result;
124 for (const auto &its : decorations) {
125 std::string value = StringUtils::TrimStr(its);
126 TextDecoration decoration;
127 if (value == "inherit") {
128 decoration = TextDecoration::INHERIT;
129 }
130 if (value == "line-through") {
131 decoration = TextDecoration::LINE_THROUGH;
132 }
133 if (value == "overline") {
134 decoration = TextDecoration::OVERLINE;
135 }
136 if (value == "underline") {
137 decoration = TextDecoration::UNDERLINE;
138 }
139 result.push_back(decoration);
140 }
141 return result;
142 }
143
ConvertStrToFit(const std::string & fit)144 ImageFit ConvertStrToFit(const std::string& fit)
145 {
146 if (fit == "fill") {
147 return ImageFit::FILL;
148 }
149 if (fit == "contain") {
150 return ImageFit::CONTAIN;
151 }
152 if (fit == "cover") {
153 return ImageFit::COVER;
154 }
155 if (fit == "scaledown") {
156 return ImageFit::SCALE_DOWN;
157 }
158 if (fit == "none") {
159 return ImageFit::NONE;
160 }
161
162 return ImageFit::CONTAIN;
163 }
164
ParseStyleAttr(const std::string & style)165 HtmlToSpan::Styles HtmlToSpan::ParseStyleAttr(const std::string& style)
166 {
167 Styles styles;
168 if (style.find(':') == std::string::npos) {
169 return styles;
170 }
171 std::regex pattern(R"(\s*([^:]+):([^;]+);?)");
172 std::smatch match;
173 std::string::const_iterator searchStart(style.begin());
174
175 while (std::regex_search(searchStart, style.end(), match, pattern)) {
176 if (match.size() < MAX_STYLE_FORMAT_NUMBER) {
177 continue;
178 }
179 std::string key = std::regex_replace(match[1].str(), std::regex(R"(\s+)"), "");
180 std::string value = std::regex_replace(match[2].str(), std::regex(R"(\s+)"), " ");
181 ToLowerCase(key);
182 styles.emplace_back(key, value);
183 searchStart = match[0].second;
184 }
185
186 return styles;
187 }
188
189 template<class T>
Get(StyleValue * styleValue) const190 T* HtmlToSpan::Get(StyleValue* styleValue) const
191 {
192 auto v = std::get_if<T>(styleValue);
193 if (v == nullptr) {
194 return nullptr;
195 }
196 return static_cast<T*>(v);
197 }
198
199 // for example str = 0.00px
FromString(const std::string & str)200 Dimension HtmlToSpan::FromString(const std::string& str)
201 {
202 static const int32_t PERCENT_UNIT = 100;
203 static const std::unordered_map<std::string, DimensionUnit> uMap {
204 { "px", DimensionUnit::PX },
205 { "vp", DimensionUnit::VP },
206 { "fp", DimensionUnit::FP },
207 { "%", DimensionUnit::PERCENT },
208 { "lpx", DimensionUnit::LPX },
209 { "auto", DimensionUnit::AUTO },
210 { "rem", DimensionUnit::INVALID },
211 { "em", DimensionUnit::INVALID },
212 };
213
214 double value = 0.0;
215 DimensionUnit unit = DimensionUnit::VP;
216 if (str.empty()) {
217 LOGE("UITree |ERROR| empty string");
218 return Dimension(NG::TEXT_DEFAULT_FONT_SIZE);
219 }
220
221 for (int32_t i = static_cast<int32_t>(str.length()) - 1; i >= 0; --i) {
222 if (str[i] >= '0' && str[i] <= '9') {
223 auto startIndex = i + 1;
224 value = StringUtils::StringToDouble(str.substr(0, startIndex));
225 startIndex = std::clamp(startIndex, 0, static_cast<int32_t>(str.length()));
226 auto subStr = str.substr(startIndex);
227 if (subStr == "pt") {
228 value = static_cast<int>(value * PT_TO_PX + ROUND_TO_INT);
229 break;
230 }
231 auto iter = uMap.find(subStr);
232 if (iter != uMap.end()) {
233 unit = iter->second;
234 }
235 value = unit == DimensionUnit::PERCENT ? value / PERCENT_UNIT : value;
236 break;
237 }
238 }
239 if (unit == DimensionUnit::PX) {
240 return Dimension(value, DimensionUnit::VP);
241 } else if (unit == DimensionUnit::INVALID) {
242 return Dimension(NG::TEXT_DEFAULT_FONT_SIZE);
243 }
244
245 return Dimension(value, unit);
246 }
247
InitFont(const std::string & key,const std::string & value,const std::string & index,StyleValues & values)248 void HtmlToSpan::InitFont(
249 const std::string& key, const std::string& value, const std::string& index, StyleValues& values)
250 {
251 auto [ret, styleValue] = GetStyleValue<Font>(index, values);
252 if (!ret) {
253 return;
254 }
255
256 Font* font = Get<Font>(styleValue);
257 if (font == nullptr) {
258 return;
259 }
260
261 if (key == "color") {
262 font->fontColor = ToSpanColor(value);
263 } else if (key == "font-size") {
264 font->fontSize = FromString(value);
265 } else if (key == "font-weight") {
266 font->fontWeight = StringUtils::StringToFontWeight(value);
267 } else if (key == "font-style") {
268 font->fontStyle = StringToFontStyle(value);
269 } else if (key == "font-family") {
270 font->fontFamilies = ParseFontFamily(value);
271 } else if (key == "font-variant") { // not support
272 } else if (key == "stroke-width") {
273 font->strokeWidth = FromString(value);
274 } else if (key == "stroke-color") {
275 font->strokeColor = ToSpanColor(value);
276 } else if (key == "font-superscript") {
277 font->superscript = StringToSuperscriptStyle(value);
278 }
279 }
280
IsFontAttr(const std::string & key)281 bool HtmlToSpan::IsFontAttr(const std::string& key)
282 {
283 if (key == "font-size" || key == "font-weight" || key == "font-style" || key == "font-family" ||
284 key == "color" || key == "stroke-width" || key == "stroke-color" || key == "font-superscript") {
285 return true;
286 }
287 return false;
288 }
289
IsBackgroundColorAttr(const std::string & key) const290 bool HtmlToSpan::IsBackgroundColorAttr(const std::string& key) const
291 {
292 return key == "background-color";
293 }
294
IsForegroundColorAttr(const std::string & key) const295 bool HtmlToSpan::IsForegroundColorAttr(const std::string& key) const
296 {
297 return key == "foreground-color";
298 }
299
InitBackgroundColor(const std::string & key,const std::string & value,const std::string & index,StyleValues & values)300 void HtmlToSpan::InitBackgroundColor(
301 const std::string& key, const std::string& value, const std::string& index, StyleValues& values)
302 {
303 auto [ret, styleValue] = GetStyleValue<TextBackgroundStyle>(index, values);
304 if (!ret) {
305 return;
306 }
307
308 TextBackgroundStyle* style = Get<TextBackgroundStyle>(styleValue);
309 if (style == nullptr) {
310 return;
311 }
312
313 if (key == "background-color") {
314 style->backgroundColor = ToSpanColor(value);
315 }
316 }
317
InitForegroundColor(const std::string & key,const std::string & value,const std::string & index,StyleValues & values)318 void HtmlToSpan::InitForegroundColor(
319 const std::string& key, const std::string& value, const std::string& index, StyleValues& values)
320 {
321 auto [ret, styleValue] = GetStyleValue<Font>(index, values);
322 if (!ret) {
323 return;
324 }
325
326 Font* font = Get<Font>(styleValue);
327 if (font == nullptr) {
328 return;
329 }
330
331 if (key == "foreground-color") {
332 font->fontColor = ToSpanColor(value);
333 }
334 }
335
InitParagraph(const std::string & key,const std::string & value,const std::string & index,StyleValues & values)336 void HtmlToSpan::InitParagraph(
337 const std::string& key, const std::string& value, const std::string& index, StyleValues& values)
338 {
339 auto [ret, styleValue] = GetStyleValue<SpanParagraphStyle>(index, values);
340 if (!ret) {
341 return;
342 }
343
344 SpanParagraphStyle* style = Get<SpanParagraphStyle>(styleValue);
345 if (style == nullptr) {
346 return;
347 }
348
349 if (key == "text-align") {
350 style->align = StringToTextAlign(value);
351 } else if (key == "vertical-align") {
352 style->textVerticalAlign = StringToTextVerticalAlign(value);
353 } else if (key == "word-break") {
354 style->wordBreak = StringToWordBreak(value);
355 } else if (key == "text-overflow") {
356 style->textOverflow = StringToTextOverflow(value);
357 } else if (IsTextIndentAttr(key)) {
358 style->textIndent = FromString(value);
359 } else {
360 }
361 }
362
IsParagraphAttr(const std::string & key)363 bool HtmlToSpan::IsParagraphAttr(const std::string& key)
364 {
365 if (key == "text-align" || key == "word-break" || key == "text-overflow" || key == "text-indent") {
366 return true;
367 }
368 return false;
369 }
370
IsDecorationLine(const std::string & key)371 bool HtmlToSpan::IsDecorationLine(const std::string& key)
372 {
373 if (key == "none" || key == "underline" || key == "overline" || key == "line-through" || key == "blink" ||
374 key == "inherit") {
375 return true;
376 }
377 return false;
378 }
379
IsDecorationStyle(const std::string & key)380 bool HtmlToSpan::IsDecorationStyle(const std::string& key)
381 {
382 if (key == "solid" || key == "double" || key == "dotted" || key == "dashed" || key == "wavy" || key == "inherit") {
383 return true;
384 }
385 return false;
386 }
387
InitDecoration(const std::string & key,const std::string & value,const std::string & index,StyleValues & values)388 void HtmlToSpan::InitDecoration(
389 const std::string& key, const std::string& value, const std::string& index, StyleValues& values)
390 {
391 auto [ret, styleValue] = GetStyleValue<DecorationSpanParam>(index, values);
392 if (!ret) {
393 return;
394 }
395 DecorationSpanParam* decoration = Get<DecorationSpanParam>(styleValue);
396 if (decoration == nullptr) {
397 return;
398 }
399
400 if (key == "text-decoration-line") {
401 decoration->decorationType = StringToTextDecoration(value);
402 } else if (key == "text-decoration-style") {
403 decoration->decorationSytle = StringToTextDecorationStyle(value);
404 } else if (key == "text-decoration-color") {
405 decoration->color = ToSpanColor(value);
406 } else if (key == "text-decoration-thickness") { // not supported: html has unit while lineThicknessScale is float
407 } else if (key == "text-decoration") {
408 std::istringstream ss1(value);
409 std::string word;
410 std::vector<std::string> words;
411 while (ss1 >> word) {
412 words.push_back(word);
413 if (IsDecorationLine(word)) {
414 decoration->decorationType = StringToTextDecoration(word);
415 } else if (IsDecorationStyle(word)) {
416 decoration->decorationSytle = StringToTextDecorationStyle(word);
417 } else {
418 decoration->color = ToSpanColor(word);
419 }
420 }
421 }
422 }
423
IsDecorationAttr(const std::string & key)424 bool HtmlToSpan::IsDecorationAttr(const std::string& key)
425 {
426 return key.compare(0, strlen("text-decoration"), "text-decoration") == 0;
427 }
428
429 template<class T>
InitDimension(const std::string & key,const std::string & value,const std::string & index,StyleValues & values)430 void HtmlToSpan::InitDimension(
431 const std::string& key, const std::string& value, const std::string& index, StyleValues& values)
432 {
433 if (value.compare(0, strlen("normal"), "normal") == 0) {
434 return;
435 }
436 auto [ret, styleValue] = GetStyleValue<T>(index, values);
437 if (!ret) {
438 return;
439 }
440 T* obj = Get<T>(styleValue);
441 if (obj == nullptr) {
442 return;
443 }
444 obj->dimension = FromString(value);
445 }
446
InitLineHeight(const std::string & key,const std::string & value,StyleValues & values)447 void HtmlToSpan::InitLineHeight(const std::string& key, const std::string& value, StyleValues& values)
448 {
449 auto [unit, size] = GetUnitAndSize(value);
450 if (!unit.empty()) {
451 InitDimension<LineHeightSpanSparam>(key, value, "line-height", values);
452 return;
453 }
454
455 auto it = values.find("font");
456 if (it == values.end()) {
457 return;
458 }
459 Font* font = Get<Font>(&it->second);
460 if (font != nullptr) {
461 size = size * font->fontSize->Value();
462 InitDimension<LineHeightSpanSparam>(key, std::to_string(size) + unit, "line-height", values);
463 }
464 }
465
IsLetterSpacingAttr(const std::string & key)466 bool HtmlToSpan::IsLetterSpacingAttr(const std::string& key)
467 {
468 return key.compare(0, strlen("letter-spacing"), "letter-spacing") == 0;
469 }
470
ToSpanColor(const std::string & value)471 Color HtmlToSpan::ToSpanColor(const std::string& value)
472 {
473 std::smatch matches;
474 std::string color = value;
475 std::string tmp = value;
476 tmp.erase(std::remove(tmp.begin(), tmp.end(), ' '), tmp.end());
477 auto regStr = "#[0-9A-Fa-f]{7,8}";
478 constexpr auto tmpLeastLength = 3;
479 if (std::regex_match(tmp, matches, std::regex(regStr)) && tmp.length() >= tmpLeastLength) {
480 auto rgb = tmp.substr(1);
481 // remove last 2 character rgba -> argb
482 rgb.erase(rgb.length() - 2, 2);
483 auto alpha = tmp.substr(tmp.length() - 2);
484 color = "#" + alpha + rgb;
485 }
486
487 return Color::FromString(color);
488 }
489
InitTextShadow(const std::string & key,const std::string & value,const std::string & index,StyleValues & values)490 void HtmlToSpan::InitTextShadow(
491 const std::string& key, const std::string& value, const std::string& index, StyleValues& values)
492 {
493 auto [ret, styleValue] = GetStyleValue<std::vector<Shadow>>(index, values);
494 if (!ret) {
495 return;
496 }
497 std::vector<Shadow>* shadow = Get<std::vector<Shadow>>(styleValue);
498 if (shadow == nullptr) {
499 return;
500 }
501 std::istringstream ss(value);
502 std::string tmp;
503 std::vector<std::vector<std::string>> shadows;
504 while (std::getline(ss, tmp, ',')) {
505 std::istringstream iss(tmp);
506 std::string word;
507 std::vector<std::string> words;
508 while (iss >> word) {
509 words.emplace_back(word);
510 }
511 if (words.size() > FOUR_PARAM || words.size() < TWO_PARAM) {
512 return;
513 }
514 shadows.emplace_back(words);
515 }
516 for (const auto &its : shadows) {
517 std::vector<std::string> attribute(FOUR_PARAM);
518 uint8_t num = 0;
519 for (const auto &it : its) {
520 if (IsLength(it)) {
521 attribute[num] = it;
522 num++;
523 continue;
524 }
525 attribute[FOURTH_PARAM] = it;
526 }
527 Shadow textShadow;
528 InitShadow(textShadow, attribute);
529 shadow->emplace_back(std::move(textShadow));
530 }
531 }
532
InitShadow(Shadow & textShadow,std::vector<std::string> & attribute)533 void HtmlToSpan::InitShadow(Shadow &textShadow, std::vector<std::string> &attribute)
534 {
535 if (!attribute[FIRST_PARAM].empty()) {
536 textShadow.SetOffsetX(FromString(attribute[FIRST_PARAM]).Value());
537 }
538 if (!attribute[SECOND_PARAM].empty()) {
539 textShadow.SetOffsetY(FromString(attribute[SECOND_PARAM]).Value());
540 }
541 if (!attribute[THIRD_PARAM].empty()) {
542 textShadow.SetBlurRadius(FromString(attribute[THIRD_PARAM]).Value());
543 }
544 if (!attribute[FOURTH_PARAM].empty()) {
545 textShadow.SetColor(ToSpanColor(attribute[FOURTH_PARAM]));
546 }
547 }
548
IsLength(const std::string & str)549 bool HtmlToSpan::IsLength(const std::string& str)
550 {
551 return !str.empty() &&
552 (std::all_of(str.begin(), str.end(), ::isdigit) || str.find("px") != std::string::npos);
553 }
554
IsTextShadowAttr(const std::string & key)555 bool HtmlToSpan::IsTextShadowAttr(const std::string& key)
556 {
557 return key.compare(0, strlen("text-shadow"), "text-shadow") == 0;
558 }
559
IsTextIndentAttr(const std::string & key)560 bool HtmlToSpan::IsTextIndentAttr(const std::string& key)
561 {
562 return key.compare(0, strlen("text-indent"), "text-indent") == 0;
563 }
564
IsLineHeightAttr(const std::string & key)565 bool HtmlToSpan::IsLineHeightAttr(const std::string& key)
566 {
567 return key.compare(0, strlen("line-height"), "line-height") == 0;
568 }
569
IsPaddingAttr(const std::string & key)570 bool HtmlToSpan::IsPaddingAttr(const std::string& key)
571 {
572 if (key == "padding" || key == "padding-top" || key == "padding-right" || key == "padding-bottom" ||
573 key == "padding-left") {
574 return true;
575 }
576 return false;
577 }
578
IsMarginAttr(const std::string & key)579 bool HtmlToSpan::IsMarginAttr(const std::string& key)
580 {
581 if (key == "margin" || key == "margin-top" || key == "margin-right" || key == "margin-bottom" ||
582 key == "margin-left") {
583 return true;
584 }
585 return false;
586 }
587
IsBorderAttr(const std::string & key)588 bool HtmlToSpan::IsBorderAttr(const std::string& key)
589 {
590 if (key == "border-radius" || key == "border-top-left-radius" || key == "border-top-right-radius" ||
591 key == "border-bottom-right-radius" || key == "border-bottom-left-radius") {
592 return true;
593 }
594 return false;
595 }
596
SetPaddingOption(const std::string & key,const std::string & value,ImageSpanOptions & options)597 void HtmlToSpan::SetPaddingOption(const std::string& key, const std::string& value, ImageSpanOptions& options)
598 {
599 if (!options.imageAttribute->paddingProp) {
600 options.imageAttribute->paddingProp = std::make_optional<NG::PaddingProperty>();
601 }
602 auto& paddings = options.imageAttribute->paddingProp;
603 if (key == "padding") {
604 std::istringstream ss(value);
605 std::string word;
606 std::vector<std::string> words;
607 while (ss >> word) {
608 words.push_back(word);
609 }
610
611 size_t size = words.size();
612 if (size == ONE_PARAM) {
613 paddings->top = NG::CalcLength(FromString(words[TOP_PARAM]));
614 paddings->right = NG::CalcLength(FromString(words[TOP_PARAM]));
615 paddings->bottom = NG::CalcLength(FromString(words[TOP_PARAM]));
616 paddings->left = NG::CalcLength(FromString(words[TOP_PARAM]));
617 } else if (size == TWO_PARAM) {
618 paddings->top = NG::CalcLength(FromString(words[TOP_PARAM]));
619 paddings->right = NG::CalcLength(FromString(words[RIGHT_PARAM]));
620 paddings->bottom = NG::CalcLength(FromString(words[TOP_PARAM]));
621 paddings->left = NG::CalcLength(FromString(words[RIGHT_PARAM]));
622 } else if (size == THREE_PARAM) {
623 paddings->top = NG::CalcLength(FromString(words[TOP_PARAM]));
624 paddings->right = NG::CalcLength(FromString(words[RIGHT_PARAM]));
625 paddings->bottom = NG::CalcLength(FromString(words[BOTTOM_PARAM]));
626 paddings->left = NG::CalcLength(FromString(words[RIGHT_PARAM]));
627 } else if (size == FOUR_PARAM) {
628 paddings->top = NG::CalcLength(FromString(words[TOP_PARAM]));
629 paddings->right = NG::CalcLength(FromString(words[RIGHT_PARAM]));
630 paddings->bottom = NG::CalcLength(FromString(words[BOTTOM_PARAM]));
631 paddings->left = NG::CalcLength(FromString(words[LEFT_PARAM]));
632 }
633 } else if (key == "padding-top") {
634 paddings->top = NG::CalcLength(FromString(value));
635 } else if (key == "padding-right") {
636 paddings->right = NG::CalcLength(FromString(value));
637 } else if (key == "padding-bottom") {
638 paddings->bottom = NG::CalcLength(FromString(value));
639 } else if (key == "padding-left") {
640 paddings->left = NG::CalcLength(FromString(value));
641 }
642 }
SetMarginOption(const std::string & key,const std::string & value,ImageSpanOptions & options)643 void HtmlToSpan::SetMarginOption(const std::string& key, const std::string& value, ImageSpanOptions& options)
644 {
645 if (!options.imageAttribute->marginProp) {
646 options.imageAttribute->marginProp = std::make_optional<NG::MarginProperty>();
647 }
648 auto& marginProp = options.imageAttribute->marginProp;
649 if (key == "margin") {
650 std::istringstream ss(value);
651 std::string word;
652 std::vector<std::string> words;
653 while (ss >> word) {
654 words.push_back(word);
655 }
656
657 size_t size = words.size();
658 if (size == ONE_PARAM) {
659 marginProp->top = NG::CalcLength(FromString(words[TOP_PARAM]));
660 marginProp->right = NG::CalcLength(FromString(words[TOP_PARAM]));
661 marginProp->bottom = NG::CalcLength(FromString(words[TOP_PARAM]));
662 marginProp->left = NG::CalcLength(FromString(words[TOP_PARAM]));
663 } else if (size == TWO_PARAM) {
664 marginProp->top = NG::CalcLength(FromString(words[TOP_PARAM]));
665 marginProp->right = NG::CalcLength(FromString(words[RIGHT_PARAM]));
666 marginProp->bottom = NG::CalcLength(FromString(words[TOP_PARAM]));
667 marginProp->left = NG::CalcLength(FromString(words[RIGHT_PARAM]));
668 } else if (size == THREE_PARAM) {
669 marginProp->top = NG::CalcLength(FromString(words[TOP_PARAM]));
670 marginProp->right = NG::CalcLength(FromString(words[RIGHT_PARAM]));
671 marginProp->bottom = NG::CalcLength(FromString(words[BOTTOM_PARAM]));
672 marginProp->left = NG::CalcLength(FromString(words[RIGHT_PARAM]));
673 } else if (size == FOUR_PARAM) {
674 marginProp->top = NG::CalcLength(FromString(words[TOP_PARAM]));
675 marginProp->right = NG::CalcLength(FromString(words[RIGHT_PARAM]));
676 marginProp->bottom = NG::CalcLength(FromString(words[BOTTOM_PARAM]));
677 marginProp->left = NG::CalcLength(FromString(words[LEFT_PARAM]));
678 }
679 } else if (key == "margin-top") {
680 marginProp->top = NG::CalcLength(FromString(value));
681 } else if (key == "margin-right") {
682 marginProp->right = NG::CalcLength(FromString(value));
683 } else if (key == "margin-bottom") {
684 marginProp->bottom = NG::CalcLength(FromString(value));
685 } else if (key == "margin-left") {
686 marginProp->left = NG::CalcLength(FromString(value));
687 }
688 }
SetBorderOption(const std::string & key,const std::string & value,ImageSpanOptions & options)689 void HtmlToSpan::SetBorderOption(const std::string& key, const std::string& value, ImageSpanOptions& options)
690 {
691 if (!options.imageAttribute->borderRadius) {
692 options.imageAttribute->borderRadius = std::make_optional<NG::BorderRadiusProperty>();
693 options.imageAttribute->borderRadius->multiValued = true;
694 }
695 auto& borderRadius = options.imageAttribute->borderRadius;
696 if (key == "border-radius") {
697 std::istringstream ss(value);
698 std::string word;
699 std::vector<std::string> words;
700 while (ss >> word) {
701 words.push_back(word);
702 }
703 size_t size = words.size();
704 if (size == ONE_PARAM) {
705 borderRadius->radiusTopLeft = FromString(words[TOP_PARAM]);
706 borderRadius->radiusTopRight = FromString(words[TOP_PARAM]);
707 borderRadius->radiusBottomRight = FromString(words[TOP_PARAM]);
708 borderRadius->radiusBottomLeft = FromString(words[TOP_PARAM]);
709 } else if (size == TWO_PARAM) {
710 borderRadius->radiusTopLeft = FromString(words[TOP_PARAM]);
711 borderRadius->radiusTopRight = FromString(words[RIGHT_PARAM]);
712 borderRadius->radiusBottomRight = FromString(words[TOP_PARAM]);
713 borderRadius->radiusBottomLeft = FromString(words[RIGHT_PARAM]);
714 } else if (size == THREE_PARAM) {
715 borderRadius->radiusTopLeft = FromString(words[TOP_PARAM]);
716 borderRadius->radiusTopRight = FromString(words[RIGHT_PARAM]);
717 borderRadius->radiusBottomRight = FromString(words[BOTTOM_PARAM]);
718 borderRadius->radiusBottomLeft = FromString(words[RIGHT_PARAM]);
719 } else if (size == FOUR_PARAM) {
720 borderRadius->radiusTopLeft = FromString(words[TOP_PARAM]);
721 borderRadius->radiusTopRight = FromString(words[RIGHT_PARAM]);
722 borderRadius->radiusBottomRight = FromString(words[BOTTOM_PARAM]);
723 borderRadius->radiusBottomLeft = FromString(words[LEFT_PARAM]);
724 }
725 } else if (key == "border-top-left-radius") {
726 borderRadius->radiusTopLeft = FromString(value);
727 } else if (key == "border-top-right-radius") {
728 borderRadius->radiusTopRight = FromString(value);
729 } else if (key == "border-bottom-right-radius") {
730 borderRadius->radiusBottomRight = FromString(value);
731 } else if (key == "border-bottom-left-radius") {
732 borderRadius->radiusBottomLeft = FromString(value);
733 }
734 }
HandleImgSpanOption(const Styles & styleMap,ImageSpanOptions & options)735 void HtmlToSpan::HandleImgSpanOption(const Styles& styleMap, ImageSpanOptions& options)
736 {
737 for (const auto& [key, value] : styleMap) {
738 auto trimVal = StringUtils::TrimStr(value);
739 if (IsPaddingAttr(key)) {
740 SetPaddingOption(key, trimVal, options);
741 } else if (IsMarginAttr(key)) {
742 SetMarginOption(key, trimVal, options);
743 } else if (IsBorderAttr(key)) {
744 SetBorderOption(key, trimVal, options);
745 } else if (key == "object-fit") {
746 options.imageAttribute->objectFit = ConvertStrToFit(trimVal);
747 } else if (key == "vertical-align") {
748 options.imageAttribute->verticalAlign = StringToVerticalAlign(trimVal);
749 } else if (key == "width" || key == "height") {
750 HandleImageSize(key, trimVal, options);
751 } else if (key == "sync-load") {
752 options.imageAttribute->syncLoad = V2::ConvertStringToBool(trimVal);
753 }
754 }
755 }
HandleImagePixelMap(const std::string & src,ImageSpanOptions & option)756 void HtmlToSpan::HandleImagePixelMap(const std::string& src, ImageSpanOptions& option)
757 {
758 if (src.empty()) {
759 return;
760 }
761 NG::LoadNotifier loadNotifier(nullptr, nullptr, nullptr);
762 RefPtr<NG::ImageLoadingContext> ctx =
763 AceType::MakeRefPtr<NG::ImageLoadingContext>(ImageSourceInfo(src), std::move(loadNotifier), true);
764 CHECK_NULL_VOID(ctx);
765 ctx->LoadImageData();
766 ctx->MakeCanvasImageIfNeed(ctx->GetImageSize(), true, ImageFit::NONE);
767 auto image = ctx->MoveCanvasImage();
768 if (image != nullptr) {
769 auto pixelMap = image->GetPixelMap();
770 if (pixelMap) {
771 option.imagePixelMap = pixelMap;
772 }
773 }
774 if (option.imagePixelMap.has_value() && option.imagePixelMap.value() != nullptr) {
775 auto pixel = option.imagePixelMap.value();
776 LOGI("img height: %{public}d, width: %{public}d, size:%{public}d", pixel->GetHeight(),
777 pixel->GetWidth(), pixel->GetByteCount());
778 } else {
779 option.image = src;
780 }
781 }
782
HandleImageSize(const std::string & key,const std::string & value,ImageSpanOptions & options)783 void HtmlToSpan::HandleImageSize(const std::string& key, const std::string& value, ImageSpanOptions& options)
784 {
785 if (!options.imageAttribute->size) {
786 options.imageAttribute->size = std::make_optional<ImageSpanSize>();
787 }
788 if (key == "width") {
789 options.imageAttribute->size->width = FromString(value);
790 } else {
791 options.imageAttribute->size->height = FromString(value);
792 }
793 }
794
MakeImageSpanOptions(const std::string & key,const std::string & value,ImageSpanOptions & options)795 void HtmlToSpan::MakeImageSpanOptions(const std::string& key, const std::string& value, ImageSpanOptions& options)
796 {
797 if (key == "src") {
798 options.image = value;
799 HandleImagePixelMap(value, options);
800 } else if (key == "style") {
801 Styles styleMap = ParseStyleAttr(value);
802 HandleImgSpanOption(styleMap, options);
803 } else if (key == "width" || key == "height") {
804 HandleImageSize(key, value, options);
805 }
806 }
807
StringToTextAlign(const std::string & value)808 TextAlign HtmlToSpan::StringToTextAlign(const std::string& value)
809 {
810 if (value == "left") {
811 return TextAlign::LEFT;
812 }
813 if (value == "right") {
814 return TextAlign::RIGHT;
815 }
816 if (value == "center") {
817 return TextAlign::CENTER;
818 }
819 if (value == "justify") {
820 return TextAlign::JUSTIFY;
821 }
822 return TextAlign::LEFT;
823 }
824
StringToTextVerticalAlign(const std::string & value)825 TextVerticalAlign HtmlToSpan::StringToTextVerticalAlign(const std::string& value)
826 {
827 if (value == "baseline") {
828 return TextVerticalAlign::BASELINE;
829 }
830 if (value == "bottom") {
831 return TextVerticalAlign::BOTTOM;
832 }
833 if (value == "middle") {
834 return TextVerticalAlign::CENTER;
835 }
836 if (value == "top") {
837 return TextVerticalAlign::TOP;
838 }
839 return TextVerticalAlign::BASELINE;
840 }
841
StringToWordBreak(const std::string & value)842 WordBreak HtmlToSpan::StringToWordBreak(const std::string& value)
843 {
844 if (value == "normal") {
845 return WordBreak::NORMAL;
846 }
847 if (value == "break-all") {
848 return WordBreak::BREAK_ALL;
849 }
850 if (value == "keep-all") {
851 return WordBreak::BREAK_WORD;
852 }
853 return WordBreak::NORMAL;
854 }
855
StringToTextOverflow(const std::string & value)856 TextOverflow HtmlToSpan::StringToTextOverflow(const std::string& value)
857 {
858 if (value == "clip") {
859 return TextOverflow::CLIP;
860 }
861 if (value == "ellipsis") {
862 return TextOverflow::ELLIPSIS;
863 }
864 return TextOverflow::NONE;
865 }
866
ToDefalutSpan(xmlNodePtr node,size_t len,size_t & pos,std::vector<SpanInfo> & spanInfos)867 void HtmlToSpan::ToDefalutSpan(xmlNodePtr node, size_t len, size_t& pos, std::vector<SpanInfo>& spanInfos)
868 {
869 if (len == 0) {
870 return;
871 }
872
873 SpanInfo info;
874 info.type = HtmlType::DEFAULT;
875 info.start = pos;
876 info.end = pos + len;
877 spanInfos.emplace_back(std::move(info));
878 }
879
880 template<class T>
GetStyleValue(const std::string & key,std::map<std::string,StyleValue> & values)881 std::pair<bool, HtmlToSpan::StyleValue*> HtmlToSpan::GetStyleValue(
882 const std::string& key, std::map<std::string, StyleValue>& values)
883 {
884 auto it = values.find(key);
885 if (it == values.end()) {
886 StyleValue value = T();
887 it = values.emplace(key, value).first;
888 }
889
890 if (it == values.end()) {
891 return std::make_pair(false, nullptr);
892 }
893
894 return std::make_pair(true, &it->second);
895 }
896
ToParagraphSpan(xmlNodePtr node,size_t len,size_t & pos,std::vector<SpanInfo> & spanInfos)897 void HtmlToSpan::ToParagraphSpan(xmlNodePtr node, size_t len, size_t& pos, std::vector<SpanInfo>& spanInfos)
898 {
899 SpanInfo info;
900 info.type = HtmlType::PARAGRAPH;
901 info.start = pos;
902 info.end = pos + len;
903 xmlAttrPtr curNode = node->properties;
904 if (curNode == nullptr) {
905 SpanParagraphStyle style;
906 info.values.emplace_back(style);
907 } else {
908 for (; curNode; curNode = curNode->next) {
909 auto styles = ToTextSpanStyle(curNode);
910 for (auto [key, value] : styles) {
911 info.values.emplace_back(value);
912 }
913 }
914 }
915
916 spanInfos.emplace_back(std::move(info));
917 }
918
GetUnitAndSize(const std::string & str)919 std::pair<std::string, double> HtmlToSpan::GetUnitAndSize(const std::string& str)
920 {
921 double value = 0.0;
922 for (int32_t i = static_cast<int32_t>(str.length() - 1); i >= 0; --i) {
923 if (str[i] >= '0' && str[i] <= '9') {
924 value = StringUtils::StringToDouble(str.substr(0, i + 1));
925 auto subStr = str.substr(i + 1);
926 return { subStr, value };
927 }
928 }
929 return { "", value };
930 }
931
ToTextSpanStyle(xmlAttrPtr curNode)932 std::map<std::string, HtmlToSpan::StyleValue> HtmlToSpan::ToTextSpanStyle(xmlAttrPtr curNode)
933 {
934 auto attrContent = xmlGetProp(curNode->parent, curNode->name);
935 if (attrContent == nullptr) {
936 return {};
937 }
938 std::string strStyle(reinterpret_cast<const char*>(attrContent));
939 xmlFree(attrContent);
940 Styles styleMap = ParseStyleAttr(strStyle);
941 std::map<std::string, StyleValue> styleValues;
942 for (auto& [key, value] : styleMap) {
943 auto trimVal = StringUtils::TrimStr(value);
944 if (IsFontAttr(key)) {
945 InitFont(key, trimVal, "font", styleValues);
946 } else if (IsForegroundColorAttr(key)) {
947 InitForegroundColor(key, trimVal, "font", styleValues);
948 } else if (IsDecorationAttr(key)) {
949 InitDecoration(key, trimVal, "decoration", styleValues);
950 } else if (IsLetterSpacingAttr(key)) {
951 InitDimension<LetterSpacingSpanParam>(key, trimVal, "letterSpacing", styleValues);
952 } else if (IsTextShadowAttr(key)) {
953 InitTextShadow(key, trimVal, "shadow", styleValues);
954 } else if (IsLineHeightAttr(key)) {
955 InitLineHeight(key, trimVal, styleValues);
956 } else if (IsParagraphAttr(key)) {
957 InitParagraph(key, trimVal, "paragrap", styleValues);
958 } else if (IsBackgroundColorAttr(key)) {
959 InitBackgroundColor(key, trimVal, "backgroundColor", styleValues);
960 }
961 }
962
963 return styleValues;
964 }
965
AddStyleSpan(const std::string & element,SpanInfo & info)966 void HtmlToSpan::AddStyleSpan(const std::string& element, SpanInfo& info)
967 {
968 std::map<std::string, StyleValue> styles;
969 if (element == "strong" || element == "b") {
970 InitFont("font-weight", "bold", "font", styles);
971 } else if (element == "sup") {
972 InitFont("font-superscript", "superscript", "font", styles);
973 } else if (element == "sub") {
974 InitFont("font-superscript", "subscript", "font", styles);
975 } else if (element == "del" || element == "s") {
976 InitDecoration("text-decoration-line", "line-through", "decoration", styles);
977 } else if (element == "u") {
978 InitDecoration("text-decoration-line", "underline", "decoration", styles);
979 } else if (element == "i" || element == "em") {
980 InitFont("font-style", "italic", "font", styles);
981 }
982
983 for (auto [key, value] : styles) {
984 info.values.emplace_back(value);
985 }
986 }
987
ToTextSpan(const std::string & element,xmlNodePtr node,size_t len,size_t & pos,std::vector<SpanInfo> & spanInfos)988 void HtmlToSpan::ToTextSpan(
989 const std::string& element, xmlNodePtr node, size_t len, size_t& pos, std::vector<SpanInfo>& spanInfos)
990 {
991 SpanInfo info;
992 info.type = HtmlType::TEXT;
993 info.start = pos;
994 info.end = pos + len;
995 xmlAttrPtr curNode = node->properties;
996 for (; curNode; curNode = curNode->next) {
997 auto styles = ToTextSpanStyle(curNode);
998 for (auto [key, value] : styles) {
999 info.values.emplace_back(value);
1000 }
1001 }
1002 if (!element.empty()) {
1003 AddStyleSpan(element, info);
1004 }
1005 if (info.values.empty()) {
1006 return;
1007 }
1008 spanInfos.emplace_back(std::move(info));
1009 }
1010
ToAnchorSpan(xmlNodePtr node,size_t len,size_t & pos,std::vector<SpanInfo> & spanInfos)1011 void HtmlToSpan::ToAnchorSpan(xmlNodePtr node, size_t len, size_t& pos, std::vector<SpanInfo>& spanInfos)
1012 {
1013 SpanInfo info;
1014 info.type = HtmlType::ANCHOR;
1015 info.start = pos;
1016 info.end = pos + len;
1017 xmlAttrPtr curNode = node->properties;
1018 for (; curNode; curNode = curNode->next) {
1019 std::string attrName = reinterpret_cast<const char*>(curNode->name);
1020 if (attrName == "href") {
1021 auto attrContent = xmlGetProp(curNode->parent, curNode->name);
1022 if (attrContent != nullptr) {
1023 std::string hrefValue = reinterpret_cast<const char*>(attrContent);
1024 xmlFree(attrContent);
1025 info.values.emplace_back(hrefValue);
1026 }
1027 }
1028 if (attrName == "style") {
1029 auto styles = ToTextSpanStyle(curNode);
1030 for (auto [key, value] : styles) {
1031 info.values.emplace_back(value);
1032 }
1033 }
1034 }
1035 spanInfos.emplace_back(std::move(info));
1036 }
1037
ToImageOptions(const std::map<std::string,std::string> & styles,ImageSpanOptions & option)1038 void HtmlToSpan::ToImageOptions(const std::map<std::string, std::string>& styles, ImageSpanOptions& option)
1039 {
1040 option.imageAttribute = std::make_optional<ImageSpanAttribute>();
1041 for (auto& [key, value] : styles) {
1042 MakeImageSpanOptions(key, value, option);
1043 }
1044 }
1045
ToImage(xmlNodePtr node,size_t len,size_t & pos,std::vector<SpanInfo> & spanInfos,bool isProcessImageOptions)1046 void HtmlToSpan::ToImage(xmlNodePtr node, size_t len, size_t& pos, std::vector<SpanInfo>& spanInfos,
1047 bool isProcessImageOptions)
1048 {
1049 std::map<std::string, std::string> styleMap;
1050 xmlAttrPtr curNode = node->properties;
1051 for (; curNode; curNode = curNode->next) {
1052 auto attrContent = xmlGetProp(curNode->parent, curNode->name);
1053 if (attrContent != nullptr) {
1054 styleMap[reinterpret_cast<const char*>(curNode->name)] = reinterpret_cast<const char*>(attrContent);
1055 xmlFree(attrContent);
1056 }
1057 }
1058
1059 ImageSpanOptions option;
1060 if (isProcessImageOptions) {
1061 ToImageOptions(styleMap, option);
1062 }
1063
1064 SpanInfo info;
1065 info.type = HtmlType::IMAGE;
1066 info.start = pos;
1067 info.end = pos + len;
1068 info.values.emplace_back(std::move(option));
1069 spanInfos.emplace_back(std::move(info));
1070 }
1071
ToSpan(xmlNodePtr curNode,size_t & pos,std::string & allContent,std::vector<SpanInfo> & spanInfos,bool isNeedLoadPixelMap)1072 void HtmlToSpan::ToSpan(
1073 xmlNodePtr curNode, size_t& pos, std::string& allContent, std::vector<SpanInfo>& spanInfos,
1074 bool isNeedLoadPixelMap)
1075 {
1076 size_t curNodeLen = 0;
1077 if (curNode->content) {
1078 std::string curNodeContent = reinterpret_cast<const char*>(curNode->content);
1079 allContent += curNodeContent;
1080 curNodeLen = StringUtils::ToWstring(curNodeContent).length();
1081 }
1082
1083 std::string htmlTag = reinterpret_cast<const char*>(curNode->name);
1084 size_t childPos = pos + curNodeLen;
1085 ParseHtmlToSpanInfo(curNode->children, childPos, allContent, spanInfos);
1086 if (curNode->type == XML_ELEMENT_NODE) {
1087 if (htmlTag == "p") {
1088 if (curNode->parent == nullptr || curNode->parent->type != XML_ELEMENT_NODE ||
1089 xmlStrcmp(curNode->parent->name, (const xmlChar*)"span") != 0) {
1090 // The <p> contained in <span> is discarded. It is not considered as a standard writing method.
1091 allContent += "\n";
1092 childPos++;
1093 ToParagraphSpan(curNode, childPos - pos, pos, spanInfos);
1094 }
1095 } else if (htmlTag == "img") {
1096 childPos++;
1097 ToImage(curNode, childPos - pos, pos, spanInfos, isNeedLoadPixelMap);
1098 } else if (htmlTag == "a") {
1099 ToAnchorSpan(curNode, childPos - pos, pos, spanInfos);
1100 } else if (htmlTag == "br") {
1101 allContent += "\n";
1102 childPos++;
1103 } else {
1104 ToTextSpan(htmlTag, curNode, childPos - pos, pos, spanInfos);
1105 }
1106 }
1107 pos = childPos;
1108 }
1109
ParseHtmlToSpanInfo(xmlNodePtr node,size_t & pos,std::string & allContent,std::vector<SpanInfo> & spanInfos,bool isNeedLoadPixelMap)1110 void HtmlToSpan::ParseHtmlToSpanInfo(
1111 xmlNodePtr node, size_t& pos, std::string& allContent, std::vector<SpanInfo>& spanInfos, bool isNeedLoadPixelMap)
1112 {
1113 xmlNodePtr curNode = nullptr;
1114 for (curNode = node; curNode; curNode = curNode->next) {
1115 if (curNode->type == XML_ELEMENT_NODE || curNode->type == XML_TEXT_NODE) {
1116 ToSpan(curNode, pos, allContent, spanInfos, isNeedLoadPixelMap);
1117 }
1118 }
1119 }
1120
PrintSpanInfos(const std::vector<SpanInfo> & spanInfos)1121 void HtmlToSpan::PrintSpanInfos(const std::vector<SpanInfo>& spanInfos)
1122 {
1123 for (auto& info : spanInfos) {
1124 LOGI("span type %{public}d start:%{public}zu end:%{public}zu, style size:%{public}zu",
1125 static_cast<int>(info.type), info.start, info.end, info.values.size());
1126 }
1127 }
1128
AfterProcSpanInfos(std::vector<SpanInfo> & spanInfos)1129 void HtmlToSpan::AfterProcSpanInfos(std::vector<SpanInfo>& spanInfos)
1130 {
1131 std::vector<std::pair<size_t, size_t>> paragraphPos;
1132 for (auto& info : spanInfos) {
1133 if (info.type == HtmlType::PARAGRAPH) {
1134 paragraphPos.push_back({ info.start, info.end });
1135 }
1136 }
1137
1138 for (auto& pos : paragraphPos) {
1139 for (auto& info : spanInfos) {
1140 if (info.type != HtmlType::PARAGRAPH && info.type != HtmlType::IMAGE && pos.second == info.end + 1) {
1141 info.end += 1;
1142 break;
1143 }
1144 }
1145 }
1146 }
1147
CreateSpan(size_t index,const SpanInfo & info,StyleValue & value)1148 RefPtr<SpanBase> HtmlToSpan::CreateSpan(size_t index, const SpanInfo& info, StyleValue& value)
1149 {
1150 if (index == static_cast<uint32_t>(StyleIndex::STYLE_FONT)) {
1151 return MakeSpan<Font, FontSpan>(info, value);
1152 }
1153
1154 if (index == static_cast<uint32_t>(StyleIndex::STYLE_DECORATION)) {
1155 return MakeDecorationSpan(info, value);
1156 }
1157
1158 if (index == static_cast<uint32_t>(StyleIndex::STYLE_BASELINE)) {
1159 return MakeDimensionSpan<BaseLineSpanParam, BaselineOffsetSpan>(info, value);
1160 }
1161
1162 if (index == static_cast<uint32_t>(StyleIndex::STYLE_LETTERSPACE)) {
1163 return MakeDimensionSpan<LetterSpacingSpanParam, LetterSpacingSpan>(info, value);
1164 }
1165
1166 if (index == static_cast<uint32_t>(StyleIndex::STYLE_LINEHEIGHT)) {
1167 return MakeDimensionSpan<LineHeightSpanSparam, LineHeightSpan>(info, value);
1168 }
1169
1170 if (index == static_cast<uint32_t>(StyleIndex::STYLE_SHADOWS)) {
1171 return MakeSpan<std::vector<Shadow>, TextShadowSpan>(info, value);
1172 }
1173
1174 if (index == static_cast<uint32_t>(StyleIndex::STYLE_PARAGRAPH)) {
1175 return MakeSpan<SpanParagraphStyle, ParagraphStyleSpan>(info, value);
1176 }
1177
1178 if (index == static_cast<uint32_t>(StyleIndex::STYLE_BACKGROUND_COLOR)) {
1179 return MakeSpan<TextBackgroundStyle, BackgroundColorSpan>(info, value);
1180 }
1181
1182 if (index == static_cast<uint32_t>(StyleIndex::STYLE_URL)) {
1183 return MakeSpan<std::string, UrlSpan>(info, value);
1184 }
1185
1186 return nullptr;
1187 }
1188
1189 template<class T, class P>
MakeSpan(const SpanInfo & info,StyleValue & value)1190 RefPtr<SpanBase> HtmlToSpan::MakeSpan(const SpanInfo& info, StyleValue& value)
1191 {
1192 auto style = Get<T>(&value);
1193 if (style != nullptr) {
1194 return AceType::MakeRefPtr<P>(*style, info.start, info.end);
1195 }
1196
1197 return nullptr;
1198 }
1199
1200 template<class T, class P>
MakeDimensionSpan(const SpanInfo & info,StyleValue & value)1201 RefPtr<SpanBase> HtmlToSpan::MakeDimensionSpan(const SpanInfo& info, StyleValue& value)
1202 {
1203 auto style = Get<T>(&value);
1204 if (style != nullptr) {
1205 return AceType::MakeRefPtr<P>(style->dimension, info.start, info.end);
1206 }
1207
1208 return nullptr;
1209 }
1210
MakeDecorationSpan(const SpanInfo & info,StyleValue & value)1211 RefPtr<SpanBase> HtmlToSpan::MakeDecorationSpan(const SpanInfo& info, StyleValue& value)
1212 {
1213 auto style = Get<DecorationSpanParam>(&value);
1214 std::optional<TextDecorationOptions> options = TextDecorationOptions();
1215 if (style != nullptr) {
1216 // Enable multi-decoration line support by default
1217 options->enableMultiType = true;
1218 return AceType::MakeRefPtr<DecorationSpan>(
1219 std::vector<TextDecoration>({style->decorationType}), style->color,
1220 style->decorationSytle, 1.0f, options, info.start, info.end);
1221 }
1222
1223 return nullptr;
1224 }
1225
AddSpans(const SpanInfo & info,RefPtr<MutableSpanString> mutableSpan)1226 void HtmlToSpan::AddSpans(const SpanInfo& info, RefPtr<MutableSpanString> mutableSpan)
1227 {
1228 for (auto value : info.values) {
1229 size_t index = value.index();
1230 RefPtr<SpanBase> span;
1231 if (index >= 0 && index < static_cast<size_t>(StyleIndex::STYLE_MAX)) {
1232 span = CreateSpan(index, info, value);
1233 }
1234 if (span != nullptr) {
1235 mutableSpan->AddSpan(span, true, true, false);
1236 }
1237 }
1238 }
1239
AddImageSpans(const SpanInfo & info,RefPtr<MutableSpanString> mutableSpan)1240 void HtmlToSpan::AddImageSpans(const SpanInfo& info, RefPtr<MutableSpanString> mutableSpan)
1241 {
1242 for (auto value : info.values) {
1243 auto style = Get<ImageSpanOptions>(&value);
1244 if (style == nullptr) {
1245 continue;
1246 }
1247 auto span = AceType::MakeRefPtr<MutableSpanString>(*style);
1248 mutableSpan->InsertSpanString(info.start, span);
1249 }
1250 }
1251
GenerateSpans(const std::string & allContent,const std::vector<SpanInfo> & spanInfos)1252 RefPtr<MutableSpanString> HtmlToSpan::GenerateSpans(
1253 const std::string& allContent, const std::vector<SpanInfo>& spanInfos)
1254 {
1255 auto mutableSpan = AceType::MakeRefPtr<MutableSpanString>(UtfUtils::Str8DebugToStr16(allContent));
1256 if (spanInfos.empty()) {
1257 return mutableSpan;
1258 }
1259 for (int32_t i = 0; i < static_cast<int32_t>(spanInfos.size()); ++i) {
1260 auto info = spanInfos[i];
1261 if (info.type == HtmlType::IMAGE) {
1262 AddImageSpans(info, mutableSpan);
1263 }
1264 }
1265 for (int32_t i = static_cast<int32_t>(spanInfos.size()) - 1; i >= 0; --i) {
1266 auto info = spanInfos[i];
1267 if (info.type == HtmlType::PARAGRAPH) {
1268 AddSpans(info, mutableSpan);
1269 } else if (info.type != HtmlType::IMAGE) {
1270 AddSpans(info, mutableSpan);
1271 }
1272 }
1273 return mutableSpan;
1274 }
1275
ToSpanString(const std::string & html,const bool isNeedLoadPixelMap)1276 RefPtr<MutableSpanString> HtmlToSpan::ToSpanString(const std::string& html, const bool isNeedLoadPixelMap)
1277 {
1278 htmlDocPtr doc = htmlReadMemory(html.c_str(), html.length(), nullptr, "UTF-8", 0);
1279 if (doc == nullptr) {
1280 return nullptr;
1281 }
1282
1283 auto docSharedPtr = std::shared_ptr<xmlDoc>(doc, [](htmlDocPtr doc) { xmlFreeDoc(doc); });
1284 if (docSharedPtr == nullptr) {
1285 return nullptr;
1286 }
1287
1288 xmlNode* root = xmlDocGetRootElement(docSharedPtr.get());
1289 if (root == nullptr) {
1290 return nullptr;
1291 }
1292
1293 size_t pos = 0;
1294 std::string content;
1295 std::vector<SpanInfo> spanInfos;
1296 ParseHtmlToSpanInfo(root, pos, content, spanInfos, isNeedLoadPixelMap);
1297 AfterProcSpanInfos(spanInfos);
1298 PrintSpanInfos(spanInfos);
1299 return GenerateSpans(content, spanInfos);
1300 }
1301
FromHtml(const std::string & html)1302 RefPtr<MutableSpanString> HtmlUtils::FromHtml(const std::string& html)
1303 {
1304 HtmlToSpan hts;
1305 auto styledString = hts.ToSpanString(html);
1306 return styledString;
1307 }
1308 } // namespace OHOS::Ace
1309