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