• 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 "span_to_html.h"
17 
18 #include <sys/stat.h>
19 #include <unistd.h>
20 
21 #include "base/image/image_packer.h"
22 #include "core/text/html_utils.h"
23 
24 namespace OHOS::Ace {
25 const constexpr char* CONVERT_PNG_FORMAT = "image/png";
26 const constexpr char* DEFAULT_SUFFIX = ".png";
27 const mode_t CHOWN_RW_UG = 0660;
28 const constexpr size_t COLOR_MIN_LENGHT = 3;
29 const constexpr char* TEMP_HTML_CONVERT_DATA_ROOT_PATH = "data/storage/el2/base/temp/htmlconvert";
OHOS_ACE_ConvertHmtlToSpanString(std::vector<uint8_t> & span,std::string & html)30 extern "C" ACE_FORCE_EXPORT int OHOS_ACE_ConvertHmtlToSpanString(std::vector<uint8_t>& span, std::string& html)
31 {
32     SpanToHtml convert;
33     html = convert.ToHtml(span);
34     return 0;
35 }
36 
37 using namespace OHOS::Ace::NG;
FontStyleToHtml(const std::optional<Ace::FontStyle> & value)38 std::string SpanToHtml::FontStyleToHtml(const std::optional<Ace::FontStyle>& value)
39 {
40     return ToHtmlStyleFormat(
41         "font-style", value.value_or(Ace::FontStyle::NORMAL) == Ace::FontStyle::NORMAL ? "normal" : "italic");
42 }
43 
FontSizeToHtml(const std::optional<Dimension> & value)44 std::string SpanToHtml::FontSizeToHtml(const std::optional<Dimension>& value)
45 {
46     return ToHtmlStyleFormat("font-size", DimensionToString(value.value_or(TEXT_DEFAULT_FONT_SIZE)));
47 }
48 
FontWeightToHtml(const std::optional<FontWeight> & value)49 std::string SpanToHtml::FontWeightToHtml(const std::optional<FontWeight>& value)
50 {
51     static const LinearEnumMapNode<FontWeight, std::string> table[] = {
52         { FontWeight::W100, "100" },
53         { FontWeight::W200, "200" },
54         { FontWeight::W300, "300" },
55         { FontWeight::W400, "400" },
56         { FontWeight::W500, "500" },
57         { FontWeight::W600, "600" },
58         { FontWeight::W700, "700" },
59         { FontWeight::W800, "800" },
60         { FontWeight::W900, "900" },
61         { FontWeight::BOLD, "bold" },
62         { FontWeight::NORMAL, "normal" },
63         { FontWeight::BOLDER, "bolder" },
64         { FontWeight::LIGHTER, "lighter" },
65         { FontWeight::MEDIUM, "medium" },
66         { FontWeight::REGULAR, "regular" },
67     };
68 
69     auto index = BinarySearchFindIndex(table, ArraySize(table), value.value_or(FontWeight::NORMAL));
70     return ToHtmlStyleFormat("font-weight", index < 0 ? "normal" : table[index].value);
71 }
72 
StrokeWidthToHtml(const std::optional<Dimension> & value)73 std::string SpanToHtml::StrokeWidthToHtml(const std::optional<Dimension>& value)
74 {
75     return ToHtmlStyleFormat("stroke-width", DimensionToString(value.value_or(TEXT_DEFAULT_STROKE_WIDTH)));
76 }
77 
StrokeColorToHtml(const std::optional<Color> & value)78 std::string SpanToHtml::StrokeColorToHtml(const std::optional<Color>& value)
79 {
80     auto color = value.value_or(Color::BLACK).ColorToString();
81     ToHtmlColor(color);
82     return ToHtmlStyleFormat("stroke-color", color);
83 }
84 
ToHtmlColor(std::string & color)85 void SpanToHtml::ToHtmlColor(std::string& color)
86 {
87     if (color.length() < COLOR_MIN_LENGHT) {
88         return;
89     }
90     // argb -> rgda
91     char second = color[1];
92     char third = color[2];
93     // earse 2 character after # and apped at end
94     color.erase(1, 2);
95     color.push_back(second);
96     color.push_back(third);
97 }
98 
ColorToHtml(const std::optional<Color> & value)99 std::string SpanToHtml::ColorToHtml(const std::optional<Color>& value)
100 {
101     auto color = value.value_or(Color::BLACK).ColorToString();
102     ToHtmlColor(color);
103     return ToHtmlStyleFormat("color", color);
104 }
105 
BackgroundColorToHtml(const std::optional<TextBackgroundStyle> & value)106 std::string SpanToHtml::BackgroundColorToHtml(const std::optional<TextBackgroundStyle>& value)
107 {
108     if (!value.has_value()) {
109         return "";
110     }
111 
112     const auto& backgroundStyle = value.value();
113     if (!backgroundStyle.backgroundColor.has_value()) {
114         return "";
115     }
116 
117     auto color = backgroundStyle.backgroundColor.value().ColorToString();
118     ToHtmlColor(color);
119     return ToHtmlStyleFormat("background-color", color);
120 }
121 
FontFamilyToHtml(const std::optional<std::vector<std::string>> & value)122 std::string SpanToHtml::FontFamilyToHtml(const std::optional<std::vector<std::string>>& value)
123 {
124     return ToHtmlStyleFormat("font-family", GetFontFamilyInJson(value));
125 }
126 
FontSuperscriptToHtml(const std::optional<SuperscriptStyle> & value)127 std::string SpanToHtml::FontSuperscriptToHtml(const std::optional<SuperscriptStyle>& value)
128 {
129     static const LinearEnumMapNode<SuperscriptStyle, std::string> table[] = {
130         { SuperscriptStyle::SUPERSCRIPT, "superscript" },
131         { SuperscriptStyle::SUBSCRIPT, "subscript" },
132         { SuperscriptStyle::NORMAL, "normal" },
133     };
134 
135     auto index = BinarySearchFindIndex(table, ArraySize(table), value.value_or(SuperscriptStyle::NORMAL));
136     return ToHtmlStyleFormat("font-superscript", index < 0 ? "normal" : table[index].value);
137 }
138 
TextDecorationToHtml(const std::vector<TextDecoration> & decorations)139 std::string SpanToHtml::TextDecorationToHtml(const std::vector<TextDecoration>& decorations)
140 {
141     static const LinearEnumMapNode<TextDecoration, std::string> decorationTable[] = {
142         { TextDecoration::NONE, "none" },
143         { TextDecoration::UNDERLINE, "underline" },
144         { TextDecoration::OVERLINE, "overline" },
145         { TextDecoration::LINE_THROUGH, "line-through" },
146         { TextDecoration::INHERIT, "inherit" },
147     };
148 
149     std::string style;
150     for (TextDecoration decoration : decorations) {
151         auto index = BinarySearchFindIndex(decorationTable, ArraySize(decorationTable), decoration);
152         if (index < 0) {
153             continue;
154         }
155         style += decorationTable[index].value + " ";
156     }
157     style.pop_back();
158 
159     return ToHtmlStyleFormat("text-decoration-line", style);
160 }
161 
TextDecorationStyleToHtml(TextDecorationStyle decorationStyle)162 std::string SpanToHtml::TextDecorationStyleToHtml(TextDecorationStyle decorationStyle)
163 {
164     static const LinearEnumMapNode<TextDecorationStyle, std::string> table[] = {
165         { TextDecorationStyle::SOLID, "solid" },
166         { TextDecorationStyle::DOUBLE, "double" },
167         { TextDecorationStyle::DOTTED, "dotted" },
168         { TextDecorationStyle::DASHED, "dashed" },
169         { TextDecorationStyle::WAVY, "wavy" },
170     };
171 
172     auto index = BinarySearchFindIndex(table, ArraySize(table), decorationStyle);
173     if (index < 0) {
174         return "";
175     }
176 
177     return ToHtmlStyleFormat("text-decoration-style", table[index].value);
178 }
179 
DimensionToString(const Dimension & dimension)180 std::string SpanToHtml::DimensionToString(const Dimension& dimension)
181 {
182     return StringUtils::DoubleToString(dimension.ConvertToVp()).append("px");
183 }
184 
DimensionToStringWithoutUnit(const Dimension & dimension)185 std::string SpanToHtml::DimensionToStringWithoutUnit(const Dimension& dimension)
186 {
187     return StringUtils::DoubleToString(dimension.ConvertToVp());
188 }
189 
ToHtml(const std::string & key,const std::optional<Dimension> & dimension)190 std::string SpanToHtml::ToHtml(const std::string& key, const std::optional<Dimension>& dimension)
191 {
192     if (!dimension) {
193         return "";
194     }
195     auto& value = *dimension;
196     if (!value.IsValid()) {
197         return "";
198     }
199     return ToHtmlStyleFormat(key, DimensionToString(value));
200 }
201 
DecorationToHtml(const NG::FontStyle & fontStyle)202 std::string SpanToHtml::DecorationToHtml(const NG::FontStyle& fontStyle)
203 {
204     auto types = fontStyle.GetTextDecoration().value_or(
205         std::vector<TextDecoration>({TextDecoration::NONE}));
206     if (!V2::IsValidTextDecorations(types)) {
207         return "";
208     }
209     std::string html;
210     auto color = fontStyle.GetTextDecorationColor();
211     if (color) {
212         auto htmlColor = color->ColorToString();
213         ToHtmlColor(htmlColor);
214         html += ToHtmlStyleFormat("text-decoration-color", htmlColor);
215     }
216     html += TextDecorationToHtml(types);
217     auto style = fontStyle.GetTextDecorationStyle();
218     if (style) {
219         html += TextDecorationStyleToHtml(*style);
220     }
221 
222     return html;
223 }
224 
ToHtml(const std::optional<std::vector<Shadow>> & shadows)225 std::string SpanToHtml::ToHtml(const std::optional<std::vector<Shadow>>& shadows)
226 {
227     if (!shadows.has_value()) {
228         return "";
229     }
230 
231     if (shadows.value().empty()) {
232         return "";
233     }
234 
235     std::string style;
236     for (const auto& shadow : shadows.value()) {
237         if (!shadow.IsValid()) {
238             continue;
239         }
240 
241         auto htmlColor = shadow.GetColor().ColorToString();
242         ToHtmlColor(htmlColor);
243 
244         style += Dimension(shadow.GetOffset().GetX()).ToString() + " " +
245                  Dimension(shadow.GetOffset().GetY()).ToString() + " " + Dimension(shadow.GetBlurRadius()).ToString() +
246                  " " + htmlColor + ",";
247     }
248     style.pop_back();
249 
250     return ToHtmlStyleFormat("text-shadow", style);
251 }
252 
ToHtml(const std::string & key,const std::optional<CalcDimension> & dimesion)253 std::string SpanToHtml::ToHtml(const std::string& key, const std::optional<CalcDimension>& dimesion)
254 {
255     if (!dimesion) {
256         return "";
257     }
258 
259     return ToHtmlStyleFormat(key, DimensionToString(*dimesion));
260 }
261 
ToHtml(const std::string & key,bool syncLoad)262 std::string SpanToHtml::ToHtml(const std::string& key, bool syncLoad)
263 {
264     return ToHtmlStyleFormat(key, V2::ConvertBoolToString(syncLoad));
265 }
266 
ToHtmlImgSizeAttribute(const std::string & key,const std::optional<CalcDimension> & dimesion)267 std::string SpanToHtml::ToHtmlImgSizeAttribute(const std::string& key, const std::optional<CalcDimension>& dimesion)
268 {
269     if (!dimesion) {
270         return "";
271     }
272 
273     return ToHtmlAttributeFormat(key, DimensionToStringWithoutUnit(*dimesion));
274 }
275 
ToHtml(const std::optional<ImageSpanSize> & size)276 std::string SpanToHtml::ToHtml(const std::optional<ImageSpanSize>& size)
277 {
278     if (!size) {
279         return "";
280     }
281 
282     std::string style = ToHtmlImgSizeAttribute("width", size->width);
283     style += ToHtmlImgSizeAttribute("height", size->height);
284     return style;
285 }
286 
ToHtml(const std::optional<VerticalAlign> & verticalAlign)287 std::string SpanToHtml::ToHtml(const std::optional<VerticalAlign>& verticalAlign)
288 {
289     if (!verticalAlign) {
290         return "";
291     }
292 
293     static const LinearEnumMapNode<VerticalAlign, std::string> table[] = {
294         { VerticalAlign::TOP, "top" },
295         { VerticalAlign::CENTER, "center" },
296         { VerticalAlign::BOTTOM, "bottom" },
297         { VerticalAlign::BASELINE, "baseline" },
298         { VerticalAlign::NONE, "" },
299     };
300     auto iter = BinarySearchFindIndex(table, ArraySize(table), *verticalAlign);
301     if (iter < 0) {
302         return "";
303     }
304 
305     return ToHtmlStyleFormat("vertical-align", table[iter].value);
306 }
307 
ToHtml(const std::optional<ImageFit> & objectFit)308 std::string SpanToHtml::ToHtml(const std::optional<ImageFit>& objectFit)
309 {
310     if (!objectFit) {
311         return "";
312     }
313 
314     static const LinearEnumMapNode<ImageFit, std::string> table[] = {
315         { ImageFit::FILL, "fill" },
316         { ImageFit::CONTAIN, "contain" },
317         { ImageFit::COVER, "cover" },
318         { ImageFit::FITWIDTH, "none" },
319         { ImageFit::FITHEIGHT, "none" },
320         { ImageFit::NONE, "none" },
321         { ImageFit::SCALE_DOWN, "scale-down" },
322     };
323 
324     auto index = BinarySearchFindIndex(table, ArraySize(table), *objectFit);
325     if (index < 0) {
326         return "";
327     }
328 
329     return ToHtmlStyleFormat("object-fit", table[index].value);
330 }
331 
ToHtml(const std::string & key,const std::optional<OHOS::Ace::NG::MarginProperty> & prop)332 std::string SpanToHtml::ToHtml(const std::string& key, const std::optional<OHOS::Ace::NG::MarginProperty>& prop)
333 {
334     if (!prop) {
335         return "";
336     }
337 
338     if (prop->top == prop->right && prop->right == prop->bottom && prop->bottom == prop->left) {
339         if (!prop->top) {
340             return "";
341         }
342         return ToHtmlStyleFormat(key, DimensionToString(prop->top->GetDimension()));
343     }
344 
345     auto padding = prop->top.has_value() ? DimensionToString(prop->top->GetDimension()) : "0";
346     padding += " " + (prop->right.has_value() ? DimensionToString(prop->right->GetDimension()) : "0");
347     padding += " " + (prop->bottom.has_value() ? DimensionToString(prop->bottom->GetDimension()) : "0");
348     padding += " " + (prop->left.has_value() ? DimensionToString(prop->left->GetDimension()) : "0");
349 
350     return ToHtmlStyleFormat(key, padding);
351 }
352 
ToHtml(const std::optional<OHOS::Ace::NG::BorderRadiusProperty> & borderRadius)353 std::string SpanToHtml::ToHtml(const std::optional<OHOS::Ace::NG::BorderRadiusProperty>& borderRadius)
354 {
355     if (!borderRadius) {
356         return "";
357     }
358 
359     std::string radius;
360     if (borderRadius->radiusTopLeft) {
361         radius += ToHtmlStyleFormat("border-top-left-radius", DimensionToString(*borderRadius->radiusTopLeft));
362     }
363     if (borderRadius->radiusTopRight) {
364         radius += ToHtmlStyleFormat("border-top-right-radius", DimensionToString(*borderRadius->radiusTopRight));
365     }
366     if (borderRadius->radiusBottomRight) {
367         radius += ToHtmlStyleFormat("border-bottom-right-radius", DimensionToString(*borderRadius->radiusBottomRight));
368     }
369     if (borderRadius->radiusBottomLeft) {
370         radius += ToHtmlStyleFormat("border-bottom-left-radius", DimensionToString(*borderRadius->radiusBottomLeft));
371     }
372 
373     return radius;
374 }
375 
CreateDirectory(const std::string & path)376 bool SpanToHtml::CreateDirectory(const std::string& path)
377 {
378     if (access(path.c_str(), F_OK) == 0) {
379         return true;
380     }
381 
382     std::string::size_type index = 0;
383     do {
384         std::string subPath;
385         index = path.find('/', index + 1);
386         if (index == std::string::npos) {
387             subPath = path;
388         } else {
389             subPath = path.substr(0, index);
390         }
391 
392         if (access(subPath.c_str(), F_OK) != 0) {
393             if (mkdir(subPath.c_str(), (S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)) != 0) {
394                 return false;
395             }
396         }
397     } while (index != std::string::npos);
398 
399     if (access(path.c_str(), F_OK) == 0) {
400         return true;
401     }
402 
403     return false;
404 }
405 
WriteLocalFile(RefPtr<PixelMap> pixelMap,std::string & filePath,std::string & fileUri)406 int SpanToHtml::WriteLocalFile(RefPtr<PixelMap> pixelMap, std::string& filePath, std::string& fileUri)
407 {
408     std::string fileName;
409     auto pos = filePath.rfind("/");
410     if (pos == std::string::npos) {
411         fileName = filePath;
412     } else {
413         fileName = filePath.substr(pos + 1);
414     }
415 
416     if (fileName.empty()) {
417         int64_t now =
418             std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch())
419                 .count();
420         fileName = std::to_string(now) + DEFAULT_SUFFIX;
421     }
422 
423     CreateDirectory(TEMP_HTML_CONVERT_DATA_ROOT_PATH);
424     std::string localPath = TEMP_HTML_CONVERT_DATA_ROOT_PATH + std::string("/") + fileName;
425     RefPtr<ImagePacker> imagePacker = ImagePacker::Create();
426     if (imagePacker == nullptr) {
427         return -1;
428     }
429     PackOption option;
430     option.format = CONVERT_PNG_FORMAT;
431     imagePacker->StartPacking(localPath, option);
432     imagePacker->AddImage(*pixelMap);
433     int64_t packedSize = 0;
434     if (imagePacker->FinalizePacking(packedSize)) {
435         return -1;
436     }
437 
438     if (chmod(localPath.c_str(), CHOWN_RW_UG) != 0) {
439     }
440 
441     fileUri = "file:///" + localPath;
442     return 0;
443 }
444 
ImageToHtml(RefPtr<NG::SpanItem> item)445 std::string SpanToHtml::ImageToHtml(RefPtr<NG::SpanItem> item)
446 {
447     auto image = AceType::DynamicCast<ImageSpanItem>(item);
448     if (image == nullptr) {
449         return "";
450     }
451 
452     auto options = image->options;
453     if (!options.image && !options.imagePixelMap) {
454         return "";
455     }
456 
457     std::string urlName;
458     auto pixelMap = options.imagePixelMap.value_or(nullptr);
459     if (pixelMap) {
460         int ret = WriteLocalFile(pixelMap, *options.image, urlName);
461         LOGI("img write ret: %{public}d height: %{public}d, width: %{public}d, size:%{public}d", ret,
462             pixelMap->GetHeight(), pixelMap->GetWidth(), pixelMap->GetByteCount());
463     } else if (options.image.has_value()) {
464         urlName = options.image.value();
465     }
466     std::string imgHtml = "<img src=\"" + urlName + "\" ";
467     imgHtml += ToHtml(options.imageAttribute->size);
468     if (options.imageAttribute) {
469         imgHtml += " style=\"";
470         imgHtml += ToHtml(options.imageAttribute->verticalAlign);
471         imgHtml += ToHtml(options.imageAttribute->objectFit);
472         imgHtml += ToHtml("margin", options.imageAttribute->marginProp);
473         imgHtml += ToHtml(options.imageAttribute->borderRadius);
474         imgHtml += ToHtml("padding", options.imageAttribute->paddingProp);
475         if (!pixelMap) {
476             imgHtml += ToHtml("sync-load", options.imageAttribute->syncLoad);
477         }
478         imgHtml += "\"";
479     }
480 
481     imgHtml += ">";
482     return imgHtml;
483 }
484 
NormalStyleToHtml(const NG::FontStyle & fontStyle,const OHOS::Ace::NG::TextLineStyle & textLineStyle)485 std::string SpanToHtml::NormalStyleToHtml(
486     const NG::FontStyle& fontStyle, const OHOS::Ace::NG::TextLineStyle& textLineStyle)
487 {
488     std::string style = FontSizeToHtml(fontStyle.GetFontSize());
489     style += FontStyleToHtml(fontStyle.GetItalicFontStyle());
490     style += FontWeightToHtml(fontStyle.GetFontWeight());
491     style += ColorToHtml(fontStyle.GetTextColor());
492     style += FontFamilyToHtml(fontStyle.GetFontFamily());
493     style += StrokeWidthToHtml(fontStyle.GetStrokeWidth());
494     style += StrokeColorToHtml(fontStyle.GetStrokeColor());
495     style += FontSuperscriptToHtml(fontStyle.GetSuperscript());
496     style += DecorationToHtml(fontStyle);
497     style += ToHtml("vertical-align", textLineStyle.GetBaselineOffset());
498     style += ToHtml("line-height", textLineStyle.GetLineHeight());
499     style += ToHtml("letter-spacing", fontStyle.GetLetterSpacing());
500     style += ToHtml(fontStyle.GetTextShadow());
501     if (style.empty()) {
502         return "";
503     }
504     return "style=\"" + style + "\"";
505 }
506 
NormalStyleToHtml(const RefPtr<NG::SpanItem> & item)507 std::string SpanToHtml::NormalStyleToHtml(const RefPtr<NG::SpanItem>& item)
508 {
509     if (!item || !item->fontStyle || !item->textLineStyle) {
510         return "";
511     }
512 
513     const auto& fontStyle = *item->fontStyle;
514     const auto& textLineStyle = *item->textLineStyle;
515 
516     std::string style = FontSizeToHtml(fontStyle.GetFontSize());
517     style += FontStyleToHtml(fontStyle.GetItalicFontStyle());
518     style += FontWeightToHtml(fontStyle.GetFontWeight());
519     style += ColorToHtml(fontStyle.GetTextColor());
520     style += FontFamilyToHtml(fontStyle.GetFontFamily());
521     style += StrokeWidthToHtml(fontStyle.GetStrokeWidth());
522     style += StrokeColorToHtml(fontStyle.GetStrokeColor());
523     style += FontSuperscriptToHtml(fontStyle.GetSuperscript());
524     style += DecorationToHtml(fontStyle);
525     style += ToHtml("vertical-align", textLineStyle.GetBaselineOffset());
526     style += ToHtml("line-height", textLineStyle.GetLineHeight());
527     style += ToHtml("letter-spacing", fontStyle.GetLetterSpacing());
528     style += ToHtml(fontStyle.GetTextShadow());
529 
530     // Add backgroundStyle parsing
531     if (item->backgroundStyle) {
532         style += BackgroundColorToHtml(item->backgroundStyle);
533     }
534 
535     if (style.empty()) {
536         return "";
537     }
538     return "style=\"" + style + "\"";
539 }
540 
ToHtml(const std::optional<OHOS::Ace::TextAlign> & object)541 std::string SpanToHtml::ToHtml(const std::optional<OHOS::Ace::TextAlign>& object)
542 {
543     if (!object.has_value()) {
544         return "";
545     }
546 
547     static const LinearEnumMapNode<TextAlign, std::string> table[] = {
548         { TextAlign::START, "start" },
549         { TextAlign::CENTER, "center" },
550         { TextAlign::END, "end" },
551         { TextAlign::JUSTIFY, "justify" },
552     };
553     auto index = BinarySearchFindIndex(table, ArraySize(table), *object);
554     if (index < 0) {
555         return "";
556     }
557 
558     return ToHtmlStyleFormat("text-align", table[index].value);
559 }
560 
ToHtml(const std::optional<OHOS::Ace::TextVerticalAlign> & object)561 std::string SpanToHtml::ToHtml(const std::optional<OHOS::Ace::TextVerticalAlign>& object)
562 {
563     if (!object.has_value()) {
564         return "";
565     }
566 
567     static const LinearEnumMapNode<TextVerticalAlign, std::string> table[] = {
568         { TextVerticalAlign::BASELINE, "baseline" },
569         { TextVerticalAlign::BOTTOM, "bottom" },
570         { TextVerticalAlign::CENTER, "middle" },
571         { TextVerticalAlign::TOP, "top" },
572     };
573     auto index = BinarySearchFindIndex(table, ArraySize(table), *object);
574     if (index < 0) {
575         return "";
576     }
577 
578     return ToHtmlStyleFormat("vertical-align", table[index].value);
579 }
580 
ToHtml(const std::optional<OHOS::Ace::WordBreak> & object)581 std::string SpanToHtml::ToHtml(const std::optional<OHOS::Ace::WordBreak>& object)
582 {
583     if (!object.has_value()) {
584         return "";
585     }
586 
587     // no keep_all
588     static const LinearEnumMapNode<WordBreak, std::string> table[] = {
589         { WordBreak::NORMAL, "normal" },
590         { WordBreak::BREAK_ALL, "break_all" },
591         { WordBreak::BREAK_WORD, "break_word" },
592     };
593     auto index = BinarySearchFindIndex(table, ArraySize(table), *object);
594     if (index < 0) {
595         return "";
596     }
597 
598     return ToHtmlStyleFormat("word-break", table[index].value);
599 }
600 
ToHtml(const std::optional<OHOS::Ace::TextOverflow> & object)601 std::string SpanToHtml::ToHtml(const std::optional<OHOS::Ace::TextOverflow>& object)
602 {
603     if (!object.has_value()) {
604         return "";
605     }
606 
607     static const LinearEnumMapNode<TextOverflow, std::string> table[] = {
608         { TextOverflow::CLIP, "clip" },
609         { TextOverflow::ELLIPSIS, "ellipsis" },
610         { TextOverflow::MARQUEE, "marquee" },
611     };
612     auto index = BinarySearchFindIndex(table, ArraySize(table), *object);
613     if (index < 0) {
614         return "";
615     }
616 
617     return ToHtmlStyleFormat("text-overflow", table[index].value);
618 }
619 
LeadingMarginToHtml(const OHOS::Ace::NG::TextLineStyle & style)620 std::string SpanToHtml::LeadingMarginToHtml(const OHOS::Ace::NG::TextLineStyle& style)
621 {
622     auto object = style.GetLeadingMargin();
623     if (!object) {
624         return "";
625     }
626 
627     if (!object.has_value()) {
628         return "";
629     }
630 
631     return "";
632 }
633 
ParagraphStyleToHtml(const OHOS::Ace::NG::TextLineStyle & textLineStyle)634 std::string SpanToHtml::ParagraphStyleToHtml(const OHOS::Ace::NG::TextLineStyle& textLineStyle)
635 {
636     auto details = ToHtml(textLineStyle.GetTextAlign());
637     details += ToHtml(textLineStyle.GetTextVerticalAlign());
638     details += ToHtml("text-indent", textLineStyle.GetTextIndent());
639     details += ToHtml(textLineStyle.GetWordBreak());
640     details += ToHtml(textLineStyle.GetTextOverflow());
641     if (details.empty()) {
642         return "";
643     }
644     return "style=\"" + details + "\"";
645 }
646 
ToHtml(const SpanString & spanString)647 std::string SpanToHtml::ToHtml(const SpanString& spanString)
648 {
649     auto items = spanString.GetSpanItems();
650     bool newLine = true;
651     size_t paragrapStart = 0;
652     std::string out = "<div >";
653     for (const auto& item : items) {
654         auto paragraphStyle = ParagraphStyleToHtml(*item->textLineStyle);
655         if (newLine && !paragraphStyle.empty()) {
656             out += "<p " + paragraphStyle + ">";
657             newLine = false;
658         }
659         std::string str = "";
660         if (item->spanItemType == SpanItemType::NORMAL) {
661             if (paragrapStart == 0) {
662                 paragrapStart = str.length();
663             }
664             if (item->backgroundStyle) {
665                 str += "<span " + NormalStyleToHtml(item) + ">";
666             } else {
667                 str += "<span " + NormalStyleToHtml(*item->fontStyle, *item->textLineStyle) + ">";
668             }
669             auto content = UtfUtils::Str16DebugToStr8(item->GetSpanContent());
670             auto wContent = StringUtils::ToWstring(content);
671             if (wContent.back() == L'\n') {
672                 if (newLine) {
673                     str.insert(paragrapStart, "<p>");
674                     paragrapStart = 0;
675                 }
676                 content.pop_back();
677                 str += WrapWithAnchorIfNeeded(item, content) + "</span>";
678                 str += "</p>";
679                 newLine = true;
680             } else {
681                 str += WrapWithAnchorIfNeeded(item, content) + "</span>";
682             }
683         } else if (item->spanItemType == SpanItemType::IMAGE) {
684             str += ImageToHtml(item);
685         }
686         out += str;
687     }
688 
689     if (!newLine) {
690         out += "</p>";
691     }
692 
693     out += "</div>";
694     return out;
695 }
696 
ToHtml(std::vector<uint8_t> & values)697 std::string SpanToHtml::ToHtml(std::vector<uint8_t>& values)
698 {
699     auto spanString = SpanString::DecodeTlv(values);
700     return ToHtml(*spanString);
701 }
702 
HandleSingleSpanItemHtml(const RefPtr<NG::SpanItem> & item,std::string & out,size_t & paragraphStart,bool & newLine)703 void SpanToHtml::HandleSingleSpanItemHtml(const RefPtr<NG::SpanItem>& item, std::string& out,
704     size_t& paragraphStart, bool& newLine)
705 {
706     auto paragraphStyle = ParagraphStyleToHtml(*item->textLineStyle);
707     if (newLine && !paragraphStyle.empty()) {
708         out += "<p " + paragraphStyle + ">";
709         newLine = false;
710     }
711     std::string str = "";
712     if (item->spanItemType == SpanItemType::NORMAL) {
713         if (paragraphStart == 0) {
714             paragraphStart = str.length();
715         }
716         if (item->backgroundStyle) {
717             str += "<span " + NormalStyleToHtml(item) + ">";
718         } else {
719             str += "<span " + NormalStyleToHtml(*item->fontStyle, *item->textLineStyle) + ">";
720         }
721         auto content = UtfUtils::Str16DebugToStr8(item->GetSpanContent());
722         auto wContent = StringUtils::ToWstring(content);
723         if (wContent.back() == L'\n') {
724             if (newLine) {
725                 str.insert(paragraphStart, "<p>");
726                 paragraphStart = 0;
727             }
728             content.pop_back();
729             str += WrapWithAnchorIfNeeded(item, content) + "</span>";
730             str += "</p>";
731             newLine = true;
732         } else {
733             str += WrapWithAnchorIfNeeded(item, content) + "</span>";
734         }
735     } else if (item->spanItemType == SpanItemType::IMAGE) {
736         str += ImageToHtml(item);
737     }
738     out += str;
739 }
740 
ToHtml(const std::list<RefPtr<SpanItem>> & spanItems)741 std::string SpanToHtml::ToHtml(const std::list<RefPtr<SpanItem>>& spanItems)
742 {
743     bool newLine = true;
744     size_t paragraphStart = 0;
745     std::string out = "<div >";
746     for (const auto& item : spanItems) {
747         HandleSingleSpanItemHtml(item, out, paragraphStart, newLine);
748     }
749     if (!newLine) {
750         out += "</p>";
751     }
752     out += "</div>";
753     return out;
754 }
755 
ToHtmlForNormalType(const NG::FontStyle & fontStyle,const NG::TextLineStyle & textLineStyle,const std::u16string & contentStr)756 std::string SpanToHtml::ToHtmlForNormalType(const NG::FontStyle& fontStyle,
757     const NG::TextLineStyle& textLineStyle, const std::u16string& contentStr)
758 {
759     bool newLine = true;
760     size_t paragraphStart = 0;
761     std::string out = "<div >";
762     auto paragraphStyle = ParagraphStyleToHtml(textLineStyle);
763     if (newLine && !paragraphStyle.empty()) {
764         out += "<p " + paragraphStyle + ">";
765         newLine = false;
766     }
767     if (paragraphStart == 0) {
768         paragraphStart = out.length();
769     }
770     out += "<span " + NormalStyleToHtml(fontStyle, textLineStyle) + ">";
771     auto content = UtfUtils::Str16DebugToStr8(contentStr);
772     auto wContent = StringUtils::ToWstring(content);
773     if (wContent.back() == L'\n') {
774         if (newLine) {
775             out.insert(paragraphStart, "<p>");
776             paragraphStart = 0;
777         }
778         content.pop_back();
779         out += content + "</span>";
780         out += "</p>";
781         newLine = true;
782     } else {
783         out += content + "</span>";
784     }
785     if (!newLine) {
786         out += "</p>";
787     }
788     out += "</div>";
789     return out;
790 }
791 
ToHtml(const SpanString * str)792 std::string HtmlUtils::ToHtml(const SpanString* str)
793 {
794     const std::string html = SpanToHtml::ToHtml(*str);
795     return html;
796 }
797 
ToHtml(const std::list<RefPtr<NG::SpanItem>> & spanItems)798 std::string HtmlUtils::ToHtml(const std::list<RefPtr<NG::SpanItem>>& spanItems)
799 {
800     const std::string html = SpanToHtml::ToHtml(spanItems);
801     return html;
802 }
803 
ToHtmlForNormalType(const NG::FontStyle & fontStyle,const NG::TextLineStyle & textLineStyle,const std::u16string & contentStr)804 std::string HtmlUtils::ToHtmlForNormalType(const NG::FontStyle& fontStyle,
805     const NG::TextLineStyle& textLineStyle, const std::u16string& contentStr)
806 {
807     const std::string html = SpanToHtml::ToHtmlForNormalType(fontStyle, textLineStyle, contentStr);
808     return html;
809 }
WrapWithAnchorIfNeeded(const RefPtr<NG::SpanItem> & item,const std::string & content)810 std::string SpanToHtml::WrapWithAnchorIfNeeded(const RefPtr<NG::SpanItem>& item, const std::string& content)
811 {
812     if (item->urlAddress) {
813         std::string href = UtfUtils::Str16DebugToStr8(item->urlAddress.value_or(std::u16string(u"")));
814         return "<a href=\"" + href + "\">" + content + "</a>";
815     }
816     return content;
817 }
818 } // namespace OHOS::Ace