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 #include <cstddef>
16 #include <cstdint>
17 #include <iostream>
18 #include <ostream>
19 #include <vector>
20 
21 #include "test/mock/core/common/mock_theme_manager.h"
22 #include "test/mock/core/pipeline/mock_pipeline_context.h"
23 #include "test/mock/core/render/mock_paragraph.h"
24 
25 #include "base/geometry/dimension.h"
26 #include "base/geometry/size.h"
27 #include "base/memory/ace_type.h"
28 #include "base/memory/referenced.h"
29 #include "core/components/common/properties/color.h"
30 #include "core/components/common/properties/text_style.h"
31 #include "core/components/text/text_theme.h"
32 #include "html_to_span.h"
33 #include "span_to_html.h"
34 #include "core/components_ng/pattern/text/span/mutable_span_string.h"
35 #include "core/components_ng/pattern/text/span/span_object.h"
36 #include "core/components_ng/pattern/text/span/span_string.h"
37 #include "core/components_ng/pattern/text/span_node.h"
38 #include "core/components_ng/pattern/text/text_pattern.h"
39 #include "core/components_ng/pattern/text/text_styles.h"
40 #include "core/components_ng/property/measure_property.h"
41 
42 #undef private
43 #undef protected
44 
45 using namespace testing;
46 using namespace testing::ext;
47 namespace OHOS::Ace::NG {
48 class HtmlConvertTestNg : public testing::Test {
49 public:
50     static void SetUpTestSuite();
51     static void TearDownTestSuite();
52     void SetUp() override;
53     void TearDown() override;
54     bool IsSpanItemSame(std::list<RefPtr<NG::SpanItem>> src, std::list<RefPtr<NG::SpanItem>> other);
55     SpanParagraphStyle GetDefaultParagraphStyle();
56     ImageSpanOptions GetImageOption(const std::string& src);
57 };
58 
SetUpTestSuite()59 void HtmlConvertTestNg::SetUpTestSuite()
60 {
61     MockPipelineContext::SetUp();
62 }
63 
TearDownTestSuite()64 void HtmlConvertTestNg::TearDownTestSuite()
65 {
66     MockPipelineContext::TearDown();
67 }
68 
SetUp()69 void HtmlConvertTestNg::SetUp() {}
70 
TearDown()71 void HtmlConvertTestNg::TearDown() {}
72 
73 std::string test_str[] = { "hello", "world", "this", "find", "gank", "pink", "that", "when", "how", "cpp" };
74 Font testFont1 { OHOS::Ace::FontWeight::BOLD, Dimension(29.0, DimensionUnit::PX), OHOS::Ace::FontStyle::ITALIC,
75     std::vector<std::string>(test_str, test_str + 10), OHOS::Ace::Color::BLUE };
76 Font testFont2 { OHOS::Ace::FontWeight::LIGHTER, Dimension(19.0, DimensionUnit::PX), OHOS::Ace::FontStyle::ITALIC,
77     std::vector<std::string>(test_str, test_str + 10), OHOS::Ace::Color::GRAY };
78 Font testEmptyFont {};
GetImageOption(const std::string & src)79 ImageSpanOptions HtmlConvertTestNg::GetImageOption(const std::string& src)
80 {
81     ImageSpanSize size { .width = 50.0_vp, .height = 50.0_vp };
82     BorderRadiusProperty borderRadius;
83     borderRadius.SetRadius(2.0_vp);
84     MarginProperty margins;
85     // margins len 10.0
86     margins.SetEdges(CalcLength(10.0));
87     PaddingProperty paddings;
88     // paddings len 5.0
89     paddings.SetEdges(CalcLength(5.0));
90     ImageSpanAttribute attr { .size = size,
91         .paddingProp = paddings,
92         .marginProp = margins,
93         .borderRadius = borderRadius,
94         .objectFit = ImageFit::COVER,
95         .verticalAlign = VerticalAlign::BOTTOM };
96     ImageSpanOptions option { .image = src, .imageAttribute = attr };
97     return option;
98 }
99 
GetDefaultParagraphStyle()100 SpanParagraphStyle HtmlConvertTestNg::GetDefaultParagraphStyle()
101 {
102     SpanParagraphStyle spanParagraphStyle;
103     spanParagraphStyle.align = TextAlign::END;
104     // default max lines 4
105     spanParagraphStyle.maxLines = 4;
106     spanParagraphStyle.wordBreak = WordBreak::BREAK_ALL;
107     spanParagraphStyle.textOverflow = TextOverflow::ELLIPSIS;
108     // defalut textIndent 23
109     spanParagraphStyle.textIndent = Dimension(23.0_vp);
110     spanParagraphStyle.leadingMargin = LeadingMargin();
111     // default width 25.0 height 26.0
112     spanParagraphStyle.leadingMargin->size = LeadingMarginSize(Dimension(25.0_vp), Dimension(26.0));
113     return spanParagraphStyle;
114 }
115 
IsSpanItemSame(std::list<RefPtr<NG::SpanItem>> src,std::list<RefPtr<NG::SpanItem>> other)116 bool HtmlConvertTestNg::IsSpanItemSame(std::list<RefPtr<NG::SpanItem>> src, std::list<RefPtr<NG::SpanItem>> other)
117 {
118     if (src.size() != other.size()) {
119         return false;
120     }
121 
122     while (src.size() != 0) {
123         auto it = src.front();
124         auto otherIt = other.front();
125         if (it->interval.first != otherIt->interval.first || it->interval.second != otherIt->interval.second ||
126             it->content != otherIt->content) {
127             return false;
128         }
129         src.pop_front();
130         other.pop_front();
131     }
132     return true;
133 }
134 
135 HWTEST_F(HtmlConvertTestNg, HtmlConvert000, TestSize.Level1)
136 {
137     auto imageOption = GetImageOption("src/icon-1.png");
138     auto mutableStr = AceType::MakeRefPtr<MutableSpanString>(imageOption);
139     auto spanString3 = AceType::MakeRefPtr<SpanString>("012345678\n9");
140     spanString3->AddSpan(AceType::MakeRefPtr<FontSpan>(testFont1, 0, 3));
141     spanString3->AddSpan(AceType::MakeRefPtr<FontSpan>(testFont2, 3, 5));
142     spanString3->AddSpan(AceType::MakeRefPtr<FontSpan>(testEmptyFont, 5, 8));
143 
144     spanString3->AddSpan(AceType::MakeRefPtr<BaselineOffsetSpan>(Dimension(4), 0, 2));
145     spanString3->AddSpan(AceType::MakeRefPtr<LetterSpacingSpan>(Dimension(5), 5, 8));
146     spanString3->AddSpan(AceType::MakeRefPtr<DecorationSpan>(
147         TextDecoration::LINE_THROUGH, Color::BLUE, TextDecorationStyle::WAVY, 0, 1));
148 
149     auto spanParagraphStyle = GetDefaultParagraphStyle();
150     auto paraSpan = AceType::MakeRefPtr<ParagraphStyleSpan>(spanParagraphStyle, 2, 5);
151 
152     spanString3->AddSpan(paraSpan);
153     Shadow textShadow;
154     textShadow.SetBlurRadius(0.0);
155     textShadow.SetColor(Color::BLUE);
156     textShadow.SetOffsetX(5.0);
157     textShadow.SetOffsetY(5.0);
158 
159     Shadow textShadow1;
160     textShadow1.SetBlurRadius(1.0);
161     textShadow1.SetColor(Color::BLUE);
162     textShadow1.SetOffsetX(10.0);
163     textShadow1.SetOffsetY(10.0);
164 
165     vector<Shadow> textShadows { textShadow, textShadow1 };
166     spanString3->AddSpan(AceType::MakeRefPtr<TextShadowSpan>(textShadows, 3, 6));
167     mutableStr->InsertSpanString(1, spanString3);
168 
169     auto spanString2 = AceType::MakeRefPtr<SpanString>("测试一下中文,\n看看是什么情况");
170     spanString2->AddSpan(AceType::MakeRefPtr<FontSpan>(testFont1, 0, 5));
171     spanString2->AddSpan(AceType::MakeRefPtr<FontSpan>(testFont2, 6, 10));
172     spanString2->AddSpan(AceType::MakeRefPtr<LetterSpacingSpan>(Dimension(10), 12, 14));
173 
174     mutableStr->InsertSpanString(5, spanString2);
175     SpanToHtml convert;
176     auto out = convert.ToHtml(*mutableStr);
177     HtmlToSpan toSpan;
178     auto dstSpan = toSpan.ToSpanString(out);
179     EXPECT_NE(dstSpan, nullptr);
180     auto items = dstSpan->GetSpanItems();
181     EXPECT_EQ(items.size(), 16);
182 }
183 
184 HWTEST_F(HtmlConvertTestNg, HtmlConvert001, TestSize.Level1)
185 {
186     auto spanString = AceType::MakeRefPtr<SpanString>("0123456789");
187     spanString->AddSpan(AceType::MakeRefPtr<FontSpan>(testFont1, 0, 3));
188     spanString->AddSpan(AceType::MakeRefPtr<FontSpan>(testFont2, 3, 5));
189     spanString->AddSpan(AceType::MakeRefPtr<FontSpan>(testEmptyFont, 5, 8));
190 
191     std::vector<uint8_t> buffer;
192     spanString->EncodeTlv(buffer);
193     SpanToHtml convert;
194     auto u8ToHtml = convert.ToHtml(buffer);
195     EXPECT_NE(u8ToHtml.empty(), true);
196 
197     auto out = convert.ToHtml(*spanString);
198     EXPECT_NE(out.empty(), true);
199     EXPECT_EQ(out, u8ToHtml);
200 
201     HtmlToSpan toSpan;
202     auto dstSpan = toSpan.ToSpanString(out);
203     EXPECT_NE(dstSpan, nullptr);
204     auto items = dstSpan->GetSpanItems();
205     EXPECT_EQ(items.size(), 4);
206 
207     EXPECT_EQ(items.size(), spanString->GetSpanItems().size());
208 }
209 
210 HWTEST_F(HtmlConvertTestNg, HtmlConvert002, TestSize.Level1)
211 {
212     auto imageOption = GetImageOption("src/icon-1.png");
213     auto imageSpan = AceType::MakeRefPtr<MutableSpanString>(imageOption);
214     auto mutableStr2 = AceType::MakeRefPtr<MutableSpanString>("123456");
215     imageSpan->AppendSpanString(mutableStr2);
216 
217     std::vector<uint8_t> buffer;
218     imageSpan->EncodeTlv(buffer);
219 
220     SpanToHtml convert;
221     auto u8ToHtml = convert.ToHtml(buffer);
222     EXPECT_NE(u8ToHtml.empty(), true);
223 
224     auto out = convert.ToHtml(*imageSpan);
225     EXPECT_NE(out.empty(), true);
226     EXPECT_EQ(out, u8ToHtml);
227 
228     HtmlToSpan toSpan;
229     auto dstSpan = toSpan.ToSpanString(out);
230     EXPECT_NE(dstSpan, nullptr);
231 
232     auto dstHtml = convert.ToHtml(*dstSpan);
233     EXPECT_EQ(out, dstHtml);
234 
235     // image is invalid,to html discart image the first char is not black space
236     EXPECT_EQ(dstSpan->GetString(), "123456");
237     auto spans = dstSpan->GetSpans(0, 6);
238     EXPECT_EQ(spans.size(), 1);
239 }
240 
241 HWTEST_F(HtmlConvertTestNg, HtmlConvert003, TestSize.Level1)
242 {
243     const std::string fontHtml = "<!DOCTYPE html>"
244                                  "<html>"
245                                  "<body>"
246                                  "<p>我是正常的</p>"
247                                  "<p style=\"COLOR:red;\">我是红色的</p>"
248                                  "<p style=\"font-family: 'Times New Roman', serif; font-size: 14px; font-weight: "
249                                  "normal; color: red; color: blue;\">我是蓝色的<strong style=\"color:blue; "
250                                  "font-size:100px;\">这段文字很重要!</strong><del>蓝色</del></p>"
251                                  "<p style=\"font-size:50px;\">我是50的</p>"
252                                  "</body>"
253                                  "</html>";
254     HtmlToSpan toSpan;
255     auto dstSpan = toSpan.ToSpanString(fontHtml);
256     EXPECT_NE(dstSpan, nullptr);
257 
258     SpanToHtml convert;
259     auto dstHtml = convert.ToHtml(*dstSpan);
260     HtmlToSpan toSpan1;
261     auto dstSpan1 = toSpan1.ToSpanString(dstHtml);
262     EXPECT_EQ(IsSpanItemSame(dstSpan->GetSpanItems(), dstSpan1->GetSpanItems()), true);
263     auto secondHtml = convert.ToHtml(*dstSpan1);
264     EXPECT_EQ(secondHtml, dstHtml);
265 }
266 
267 HWTEST_F(HtmlConvertTestNg, SpanString004, TestSize.Level1)
268 {
269     auto spanString = AceType::MakeRefPtr<SpanString>("01234中文56789");
270     std::vector<uint8_t> buff;
271     spanString->EncodeTlv(buff);
272     EXPECT_EQ(buff.size() > 0, true);
273     SpanToHtml toHtml;
274     auto htmlFromU8 = toHtml.ToHtml(buff);
275     auto htmlFromSpan = toHtml.ToHtml(*spanString);
276     EXPECT_EQ(htmlFromU8, htmlFromSpan);
277 
278     HtmlToSpan toSpan;
279     auto spanFromHtml = toSpan.ToSpanString(htmlFromU8);
280     EXPECT_EQ(IsSpanItemSame(spanFromHtml->GetSpanItems(), spanString->GetSpanItems()), true);
281 
282     SpanToHtml toHtml1;
283     auto hmtlString = toHtml1.ToHtml(*spanFromHtml);
284     EXPECT_EQ(hmtlString, htmlFromSpan);
285 }
286 
287 HWTEST_F(HtmlConvertTestNg, HtmlConvert005, TestSize.Level1)
288 {
289     auto spanString = AceType::MakeRefPtr<SpanString>("0123456789");
290     spanString->AddSpan(AceType::MakeRefPtr<FontSpan>(testFont1, 0, 3));
291     spanString->AddSpan(AceType::MakeRefPtr<FontSpan>(testFont2, 3, 5));
292     spanString->AddSpan(AceType::MakeRefPtr<FontSpan>(testEmptyFont, 5, 8));
293     spanString->AddSpan(AceType::MakeRefPtr<LetterSpacingSpan>(Dimension(5), 5, 8));
294     spanString->AddSpan(AceType::MakeRefPtr<DecorationSpan>(
295         TextDecoration::LINE_THROUGH, Color::BLUE, TextDecorationStyle::WAVY, 0, 1));
296 
297     SpanToHtml convert;
298     auto out = convert.ToHtml(*spanString);
299     HtmlToSpan toSpan;
300     auto dstSpan = toSpan.ToSpanString(out);
301     EXPECT_EQ(IsSpanItemSame(dstSpan->GetSpanItems(), spanString->GetSpanItems()), true);
302 }
303 HWTEST_F(HtmlConvertTestNg, HtmlConvert006, TestSize.Level1)
304 {
305     const std::string multiHtml = "<html>"
306                                   "<body>"
307                                   "<p style=\"color:red;\">dddd当地经的123456</p>"
308                                   "</body>"
309                                   "</html>";
310     HtmlToSpan toSpan;
311     auto dstSpan = toSpan.ToSpanString(multiHtml);
312     std::list<RefPtr<NG::SpanItem>> spans = dstSpan->GetSpanItems();
313     EXPECT_EQ(spans.size(), 1);
314     auto it = spans.begin();
315     EXPECT_EQ((*it)->fontStyle->GetTextColor().value(), OHOS::Ace::Color::RED);
316 }
317 
318 HWTEST_F(HtmlConvertTestNg, HtmlConvert007, TestSize.Level1)
319 {
320     const std::string multiHtml = "<html>"
321                                   "<body>"
322                                   "<p style=\"font-size:50px\">dddd当地经的123456</p>"
323                                   "</body>"
324                                   "</html>";
325     HtmlToSpan toSpan;
326     auto dstSpan = toSpan.ToSpanString(multiHtml);
327     std::list<RefPtr<NG::SpanItem>> spans = dstSpan->GetSpanItems();
328     EXPECT_EQ(spans.size(), 1);
329     auto it = spans.begin();
330     EXPECT_EQ((*it)->fontStyle->GetFontSize().value(), Dimension(50, DimensionUnit::VP));
331 }
332 
333 HWTEST_F(HtmlConvertTestNg, HtmlConvert008, TestSize.Level1)
334 {
335     auto spanString = AceType::MakeRefPtr<SpanString>("段落标题\n正文第一段开始");
336     SpanParagraphStyle spanParagraphStyle;
337     spanParagraphStyle.align = TextAlign::CENTER;
338     // default max lines 4
339     spanParagraphStyle.maxLines = 4;
340     spanParagraphStyle.wordBreak = WordBreak::BREAK_ALL;
341     spanParagraphStyle.textOverflow = TextOverflow::ELLIPSIS;
342     // defalut textIndent 23
343     spanParagraphStyle.textIndent = Dimension(23.0_vp);
344     spanParagraphStyle.leadingMargin = LeadingMargin();
345     // default width 25.0 height 26.0
346     spanParagraphStyle.leadingMargin->size = LeadingMarginSize(Dimension(25.0_vp), Dimension(26.0));
347     auto paragraphStyle = AceType::MakeRefPtr<ParagraphStyleSpan>(spanParagraphStyle, 0, 5);
348     spanString->AddSpan(paragraphStyle);
349 
350     SpanToHtml convert;
351     auto out = convert.ToHtml(*spanString);
352     std::string result =
353         "<div ><p style=\"text-align: center;text-indent: 23.00px;word-break: break_all;text-overflow: ellipsis;\">"
354         "<span style=\"font-size: 16.00px;font-style: normal;font-weight: normal;color: #000000FF;font-family: "
355         "HarmonyOS Sans;\">段落标题</span></p><span style=\"font-size: 16.00px;font-style: normal;font-weight: "
356         "normal;color: #000000FF;font-family: HarmonyOS Sans;\">正文第一段开始</span></div>";
357     EXPECT_EQ(out, result);
358 }
359 
360 HWTEST_F(HtmlConvertTestNg, HtmlConvert009, TestSize.Level1)
361 {
362     auto spanString = AceType::MakeRefPtr<SpanString>("向上到顶适中向下到底");
363     spanString->AddSpan(AceType::MakeRefPtr<BaselineOffsetSpan>(Dimension(20.0_vp), 0, 4));
364     spanString->AddSpan(AceType::MakeRefPtr<BaselineOffsetSpan>(Dimension(10.0_vp), 4, 6));
365 
366     SpanToHtml convert;
367     auto out = convert.ToHtml(*spanString);
368     std::string result =
369         "<div ><span style=\"font-size: 16.00px;font-style: normal;font-weight: normal;color: #000000FF;font-family: "
370         "HarmonyOS Sans;vertical-align: 20.00px;\">向上到顶</span><span style=\"font-size: 16.00px;font-style: "
371         "normal;font-weight: normal;color: #000000FF;font-family: HarmonyOS Sans;vertical-align: "
372         "10.00px;\">适中</span><span style=\"font-size: 16.00px;font-style: normal;font-weight: normal;color: "
373         "#000000FF;font-family: HarmonyOS Sans;\">向下到底</span></div>";
374     EXPECT_EQ(out, result);
375 }
376 
377 HWTEST_F(HtmlConvertTestNg, HtmlConvert010, TestSize.Level1)
378 {
379     const std::string multiHtml =
380         "<html>"
381         "<body>"
382         "<p style=\"font-size:50px\"><span style=\"font-size:100px\">100fontsize</span>dddd当地经的123456<span "
383         "style=\"font-size:30px\">30fontsize</span>1232132</p>"
384         "</body>"
385         "</html>";
386     HtmlToSpan toSpan;
387     auto dstSpan = toSpan.ToSpanString(multiHtml);
388     std::list<RefPtr<NG::SpanItem>> spans = dstSpan->GetSpanItems();
389     EXPECT_EQ(spans.size(), 4);
390 
391     SpanToHtml convert;
392     auto dstHtml = convert.ToHtml(*dstSpan);
393     HtmlToSpan toSpan1;
394     auto dstSpan1 = toSpan1.ToSpanString(dstHtml);
395     EXPECT_EQ(IsSpanItemSame(dstSpan->GetSpanItems(), dstSpan1->GetSpanItems()), true);
396     auto secondHtml = convert.ToHtml(*dstSpan1);
397     EXPECT_EQ(secondHtml, dstHtml);
398 }
399 
400 HWTEST_F(HtmlConvertTestNg, HtmlConvert011, TestSize.Level1)
401 {
402     const std::string multiHtml =
403         "<html>"
404         "<head>"
405         "</head>"
406         "<body>"
407         "<p style=\"font-size:50px;text-shadow: 0 0 3px red, green 0 0;\">"
408         "<span style=\"font-size:100px\">100fontsize</span>dddd当地经的123456<span "
409         "style=\"font-size:30px\">30fontsize</span>1232132</p>"
410         "</body>"
411         "</html>";
412     HtmlToSpan toSpan;
413     auto dstSpan = toSpan.ToSpanString(multiHtml);
414     std::list<RefPtr<NG::SpanItem>> spans = dstSpan->GetSpanItems();
415     EXPECT_EQ(spans.size(), 4);
416     auto it = spans.begin();
417     EXPECT_EQ((*it)->fontStyle->GetTextShadow().value()[0].GetColor(), OHOS::Ace::Color::RED);
418     EXPECT_EQ((*it)->fontStyle->GetTextShadow().value()[1].GetColor(), OHOS::Ace::Color::GREEN);
419 }
420 } // namespace OHOS::Ace::NG