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