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