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