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