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(), 17);
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>("123456");
214
215 std::vector<uint8_t> buffer;
216 imageSpan->EncodeTlv(buffer);
217
218 SpanToHtml convert;
219 auto u8ToHtml = convert.ToHtml(buffer);
220 EXPECT_NE(u8ToHtml.empty(), true);
221
222 auto out = convert.ToHtml(*imageSpan);
223 EXPECT_NE(out.empty(), true);
224 EXPECT_EQ(out, u8ToHtml);
225
226 HtmlToSpan toSpan;
227 auto dstSpan = toSpan.ToSpanString(out);
228 EXPECT_NE(dstSpan, nullptr);
229
230 auto dstHtml = convert.ToHtml(*dstSpan);
231 EXPECT_EQ(out, dstHtml);
232
233 // image is invalid,to html discart image the first char is not black space
234 EXPECT_EQ(dstSpan->GetString(), "123456");
235 auto spans = dstSpan->GetSpans(0, 6);
236 EXPECT_EQ(spans.size(), 1);
237 }
238
239 HWTEST_F(HtmlConvertTestNg, HtmlConvert003, TestSize.Level1)
240 {
241 const std::string fontHtml = "<!DOCTYPE html>"
242 "<html>"
243 "<body>"
244 "<p>我是正常的</p>"
245 "<p style=\"COLOR:red;\">我是红色的</p>"
246 "<p style=\"font-family: 'Times New Roman', serif; font-size: 14px; font-weight: "
247 "normal; color: red; color: blue;\">我是蓝色的<strong style=\"color:blue; "
248 "font-size:100px;\">这段文字很重要!</strong><del>蓝色</del></p>"
249 "<p style=\"font-size:50px;\">我是50的</p>"
250 "</body>"
251 "</html>";
252 HtmlToSpan toSpan;
253 auto dstSpan = toSpan.ToSpanString(fontHtml);
254 EXPECT_NE(dstSpan, nullptr);
255
256 SpanToHtml convert;
257 auto dstHtml = convert.ToHtml(*dstSpan);
258 HtmlToSpan toSpan1;
259 auto dstSpan1 = toSpan1.ToSpanString(dstHtml);
260 EXPECT_EQ(IsSpanItemSame(dstSpan->GetSpanItems(), dstSpan1->GetSpanItems()), true);
261 auto secondHtml = convert.ToHtml(*dstSpan1);
262 EXPECT_EQ(secondHtml, dstHtml);
263 }
264
265 HWTEST_F(HtmlConvertTestNg, SpanString004, TestSize.Level1)
266 {
267 auto spanString = AceType::MakeRefPtr<SpanString>("01234中文56789");
268 std::vector<uint8_t> buff;
269 spanString->EncodeTlv(buff);
270 EXPECT_EQ(buff.size() > 0, true);
271 SpanToHtml toHtml;
272 auto htmlFromU8 = toHtml.ToHtml(buff);
273 auto htmlFromSpan = toHtml.ToHtml(*spanString);
274 EXPECT_EQ(htmlFromU8, htmlFromSpan);
275
276 HtmlToSpan toSpan;
277 auto spanFromHtml = toSpan.ToSpanString(htmlFromU8);
278 EXPECT_EQ(IsSpanItemSame(spanFromHtml->GetSpanItems(), spanString->GetSpanItems()), true);
279
280 SpanToHtml toHtml1;
281 auto hmtlString = toHtml1.ToHtml(*spanFromHtml);
282 EXPECT_EQ(hmtlString, htmlFromSpan);
283 }
284
285 HWTEST_F(HtmlConvertTestNg, HtmlConvert005, TestSize.Level1)
286 {
287 auto spanString = AceType::MakeRefPtr<SpanString>("0123456789");
288 spanString->AddSpan(AceType::MakeRefPtr<FontSpan>(testFont1, 0, 3));
289 spanString->AddSpan(AceType::MakeRefPtr<FontSpan>(testFont2, 3, 5));
290 spanString->AddSpan(AceType::MakeRefPtr<FontSpan>(testEmptyFont, 5, 8));
291 spanString->AddSpan(AceType::MakeRefPtr<LetterSpacingSpan>(Dimension(5), 5, 8));
292 spanString->AddSpan(AceType::MakeRefPtr<DecorationSpan>(
293 TextDecoration::LINE_THROUGH, Color::BLUE, TextDecorationStyle::WAVY, 0, 1));
294
295 SpanToHtml convert;
296 auto out = convert.ToHtml(*spanString);
297 HtmlToSpan toSpan;
298 auto dstSpan = toSpan.ToSpanString(out);
299 EXPECT_EQ(IsSpanItemSame(dstSpan->GetSpanItems(), spanString->GetSpanItems()), true);
300 }
301 HWTEST_F(HtmlConvertTestNg, HtmlConvert006, TestSize.Level1)
302 {
303 const std::string multiHtml = "<html>"
304 "<body>"
305 "<p style=\"color:red;\">dddd当地经的123456</p>"
306 "</body>"
307 "</html>";
308 HtmlToSpan toSpan;
309 auto dstSpan = toSpan.ToSpanString(multiHtml);
310 std::list<RefPtr<NG::SpanItem>> spans = dstSpan->GetSpanItems();
311 EXPECT_EQ(spans.size(), 1);
312 auto it = spans.begin();
313 EXPECT_EQ((*it)->fontStyle->GetTextColor().value(), OHOS::Ace::Color::RED);
314 }
315
316 HWTEST_F(HtmlConvertTestNg, HtmlConvert007, TestSize.Level1)
317 {
318 const std::string multiHtml = "<html>"
319 "<body>"
320 "<p style=\"font-size:50px\">dddd当地经的123456</p>"
321 "</body>"
322 "</html>";
323 HtmlToSpan toSpan;
324 auto dstSpan = toSpan.ToSpanString(multiHtml);
325 std::list<RefPtr<NG::SpanItem>> spans = dstSpan->GetSpanItems();
326 EXPECT_EQ(spans.size(), 1);
327 auto it = spans.begin();
328 EXPECT_EQ((*it)->fontStyle->GetFontSize().value(), Dimension(50, DimensionUnit::VP));
329 }
330
331 HWTEST_F(HtmlConvertTestNg, HtmlConvert008, TestSize.Level1)
332 {
333 auto spanString = AceType::MakeRefPtr<SpanString>("段落标题\n正文第一段开始");
334 SpanParagraphStyle spanParagraphStyle;
335 spanParagraphStyle.align = TextAlign::CENTER;
336 // default max lines 4
337 spanParagraphStyle.maxLines = 4;
338 spanParagraphStyle.wordBreak = WordBreak::BREAK_ALL;
339 spanParagraphStyle.textOverflow = TextOverflow::ELLIPSIS;
340 // defalut textIndent 23
341 spanParagraphStyle.textIndent = Dimension(23.0_vp);
342 spanParagraphStyle.leadingMargin = LeadingMargin();
343 // default width 25.0 height 26.0
344 spanParagraphStyle.leadingMargin->size = LeadingMarginSize(Dimension(25.0_vp), Dimension(26.0));
345 auto paragraphStyle = AceType::MakeRefPtr<ParagraphStyleSpan>(spanParagraphStyle, 0, 5);
346 spanString->AddSpan(paragraphStyle);
347
348 SpanToHtml convert;
349 auto out = convert.ToHtml(*spanString);
350 std::string result =
351 "<div ><p style=\"text-align: center;text-indent: 23.00px;word-break: break_all;text-overflow: ellipsis;\">"
352 "<span style=\"font-size: 16.00px;font-style: normal;font-weight: normal;color: #000000FF;font-family: "
353 "HarmonyOS Sans;\">段落标题</span></p><span style=\"font-size: 16.00px;font-style: normal;font-weight: "
354 "normal;color: #000000FF;font-family: HarmonyOS Sans;\">正文第一段开始</span></div>";
355 EXPECT_EQ(out, result);
356 }
357
358 HWTEST_F(HtmlConvertTestNg, HtmlConvert009, TestSize.Level1)
359 {
360 auto spanString = AceType::MakeRefPtr<SpanString>("向上到顶适中向下到底");
361 spanString->AddSpan(AceType::MakeRefPtr<BaselineOffsetSpan>(Dimension(20.0_vp), 0, 4));
362 spanString->AddSpan(AceType::MakeRefPtr<BaselineOffsetSpan>(Dimension(10.0_vp), 4, 6));
363
364 SpanToHtml convert;
365 auto out = convert.ToHtml(*spanString);
366 std::string result =
367 "<div ><span style=\"font-size: 16.00px;font-style: normal;font-weight: normal;color: #000000FF;font-family: "
368 "HarmonyOS Sans;vertical-align: 20.00px;\">向上到顶</span><span style=\"font-size: 16.00px;font-style: "
369 "normal;font-weight: normal;color: #000000FF;font-family: HarmonyOS Sans;vertical-align: "
370 "10.00px;\">适中</span><span style=\"font-size: 16.00px;font-style: normal;font-weight: normal;color: "
371 "#000000FF;font-family: HarmonyOS Sans;\">向下到底</span></div>";
372 EXPECT_EQ(out, result);
373 }
374
375 HWTEST_F(HtmlConvertTestNg, HtmlConvert010, TestSize.Level1)
376 {
377 const std::string multiHtml =
378 "<html>"
379 "<body>"
380 "<p style=\"font-size:50px\"><span style=\"font-size:100px\">100fontsize</span>dddd当地经的123456<span "
381 "style=\"font-size:30px\">30fontsize</span>1232132</p>"
382 "</body>"
383 "</html>";
384 HtmlToSpan toSpan;
385 auto dstSpan = toSpan.ToSpanString(multiHtml);
386 std::list<RefPtr<NG::SpanItem>> spans = dstSpan->GetSpanItems();
387 EXPECT_EQ(spans.size(), 4);
388
389 SpanToHtml convert;
390 auto dstHtml = convert.ToHtml(*dstSpan);
391 HtmlToSpan toSpan1;
392 auto dstSpan1 = toSpan1.ToSpanString(dstHtml);
393 EXPECT_EQ(IsSpanItemSame(dstSpan->GetSpanItems(), dstSpan1->GetSpanItems()), true);
394 auto secondHtml = convert.ToHtml(*dstSpan1);
395 EXPECT_EQ(secondHtml, dstHtml);
396 }
397
398 HWTEST_F(HtmlConvertTestNg, HtmlConvert011, TestSize.Level1)
399 {
400 const std::string multiHtml =
401 "<html>"
402 "<head>"
403 "</head>"
404 "<body>"
405 "<p style=\"font-size:50px;text-shadow: 0 0 3px red, green 0 0;\">"
406 "<span style=\"font-size:100px\">100fontsize</span>dddd当地经的123456<span "
407 "style=\"font-size:30px\">30fontsize</span>1232132</p>"
408 "</body>"
409 "</html>";
410 HtmlToSpan toSpan;
411 auto dstSpan = toSpan.ToSpanString(multiHtml);
412 std::list<RefPtr<NG::SpanItem>> spans = dstSpan->GetSpanItems();
413 EXPECT_EQ(spans.size(), 4);
414 auto it = spans.begin();
415 EXPECT_EQ((*it)->fontStyle->GetTextShadow().value()[0].GetColor(), OHOS::Ace::Color::RED);
416 EXPECT_EQ((*it)->fontStyle->GetTextShadow().value()[1].GetColor(), OHOS::Ace::Color::GREEN);
417 }
418 } // namespace OHOS::Ace::NG