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