1 /* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.text.cts; 18 19 import static org.hamcrest.MatcherAssert.assertThat; 20 import org.hamcrest.Description; 21 import org.hamcrest.BaseMatcher; 22 23 import android.graphics.Typeface; 24 import android.test.AndroidTestCase; 25 import android.text.Html; 26 import android.text.Layout; 27 import android.text.Spannable; 28 import android.text.SpannableString; 29 import android.text.Spanned; 30 import android.text.Html.ImageGetter; 31 import android.text.Html.TagHandler; 32 import android.text.style.AlignmentSpan; 33 import android.text.style.BackgroundColorSpan; 34 import android.text.style.BulletSpan; 35 import android.text.style.ForegroundColorSpan; 36 import android.text.style.QuoteSpan; 37 import android.text.style.StrikethroughSpan; 38 import android.text.style.StyleSpan; 39 import android.text.style.SubscriptSpan; 40 import android.text.style.SuperscriptSpan; 41 import android.text.style.TypefaceSpan; 42 import android.text.style.URLSpan; 43 import android.text.style.UnderlineSpan; 44 45 public class HtmlTest extends AndroidTestCase { 46 private final static int SPAN_EXCLUSIVE_INCLUSIVE = Spannable.SPAN_EXCLUSIVE_INCLUSIVE; 47 testSingleTagOnWhileString()48 public void testSingleTagOnWhileString() { 49 final String source = "<b>hello</b>"; 50 51 Spanned spanned = Html.fromHtml(source); 52 assertSingleTagOnWhileString(spanned); 53 spanned = Html.fromHtml(source, null, null); 54 assertSingleTagOnWhileString(spanned); 55 } 56 assertSingleTagOnWhileString(Spanned spanned)57 private void assertSingleTagOnWhileString(Spanned spanned) { 58 final int expectStart = 0; 59 final int expectEnd = 5; 60 final int expectLen = 1; 61 final int start = -1; 62 final int end = 100; 63 64 Object[] spans = spanned.getSpans(start, end, Object.class); 65 assertEquals(expectLen, spans.length); 66 assertEquals(expectStart, spanned.getSpanStart(spans[0])); 67 assertEquals(expectEnd, spanned.getSpanEnd(spans[0])); 68 } 69 testBadHtml()70 public void testBadHtml() { 71 final String source = "Hello <b>b<i>bi</b>i</i>"; 72 73 Spanned spanned = Html.fromHtml(source); 74 assertBadHtml(spanned); 75 spanned = Html.fromHtml(source, null, null); 76 assertBadHtml(spanned); 77 } 78 assertBadHtml(Spanned spanned)79 private void assertBadHtml(Spanned spanned) { 80 final int start = 0; 81 final int end = 100; 82 final int spansLen = 3; 83 84 Object[] spans = spanned.getSpans(start, end, Object.class); 85 assertEquals(spansLen, spans.length); 86 } 87 testSymbols()88 public void testSymbols() { 89 final String source = "© > <"; 90 final String expected = "\u00a9 > <"; 91 92 String spanned = Html.fromHtml(source).toString(); 93 assertEquals(expected, spanned); 94 spanned = Html.fromHtml(source, null, null).toString(); 95 assertEquals(expected, spanned); 96 } 97 testColor()98 public void testColor() throws Exception { 99 final Class<ForegroundColorSpan> type = ForegroundColorSpan.class; 100 101 Spanned s = Html.fromHtml("<font color=\"#00FF00\">something</font>"); 102 ForegroundColorSpan[] colors = s.getSpans(0, s.length(), type); 103 assertEquals(0xFF00FF00, colors[0].getForegroundColor()); 104 105 s = Html.fromHtml("<font color=\"navy\">NAVY</font>"); 106 colors = s.getSpans(0, s.length(), type); 107 assertEquals(0xFF000080, colors[0].getForegroundColor()); 108 109 s = Html.fromHtml("<font color=\"gibberish\">something</font>"); 110 colors = s.getSpans(0, s.length(), type); 111 assertEquals(0, colors.length); 112 113 // By default use the color values from android.graphics.Color instead of HTML/CSS 114 s = Html.fromHtml("<font color=\"green\">GREEN</font>"); 115 colors = s.getSpans(0, s.length(), type); 116 assertEquals(0xFF00FF00, colors[0].getForegroundColor()); 117 118 s = Html.fromHtml("<font color=\"gray\">GRAY</font>"); 119 colors = s.getSpans(0, s.length(), type); 120 assertEquals(0xFF888888, colors[0].getForegroundColor()); 121 122 s = Html.fromHtml("<font color=\"grey\">GREY</font>"); 123 colors = s.getSpans(0, s.length(), type); 124 assertEquals(0xFF888888, colors[0].getForegroundColor()); 125 126 s = Html.fromHtml("<font color=\"lightgray\">LIGHTGRAY</font>"); 127 colors = s.getSpans(0, s.length(), type); 128 assertEquals(0xFFCCCCCC, colors[0].getForegroundColor()); 129 130 s = Html.fromHtml("<font color=\"lightgrey\">LIGHTGREY</font>"); 131 colors = s.getSpans(0, s.length(), type); 132 assertEquals(0xFFCCCCCC, colors[0].getForegroundColor()); 133 134 s = Html.fromHtml("<font color=\"darkgray\">DARKGRAY</font>"); 135 colors = s.getSpans(0, s.length(), type); 136 assertEquals(0xFF444444, colors[0].getForegroundColor()); 137 138 s = Html.fromHtml("<font color=\"darkgrey\">DARKGREY</font>"); 139 colors = s.getSpans(0, s.length(), type); 140 assertEquals(0xFF444444, colors[0].getForegroundColor()); 141 } 142 testUseCssColor()143 public void testUseCssColor() throws Exception { 144 final Class<ForegroundColorSpan> type = ForegroundColorSpan.class; 145 final int flags = Html.FROM_HTML_OPTION_USE_CSS_COLORS; 146 147 Spanned s = Html.fromHtml("<font color=\"green\">GREEN</font>", flags); 148 ForegroundColorSpan[] colors = s.getSpans(0, s.length(), type); 149 assertEquals(0xFF008000, colors[0].getForegroundColor()); 150 151 s = Html.fromHtml("<font color=\"gray\">GRAY</font>", flags); 152 colors = s.getSpans(0, s.length(), type); 153 assertEquals(0xFF808080, colors[0].getForegroundColor()); 154 155 s = Html.fromHtml("<font color=\"grey\">GREY</font>", flags); 156 colors = s.getSpans(0, s.length(), type); 157 assertEquals(0xFF808080, colors[0].getForegroundColor()); 158 159 s = Html.fromHtml("<font color=\"lightgray\">LIGHTGRAY</font>", flags); 160 colors = s.getSpans(0, s.length(), type); 161 assertEquals(0xFFD3D3D3, colors[0].getForegroundColor()); 162 163 s = Html.fromHtml("<font color=\"lightgrey\">LIGHTGREY</font>", flags); 164 colors = s.getSpans(0, s.length(), type); 165 assertEquals(0xFFD3D3D3, colors[0].getForegroundColor()); 166 167 s = Html.fromHtml("<font color=\"darkgray\">DARKGRAY</font>", flags); 168 colors = s.getSpans(0, s.length(), type); 169 assertEquals(0xFFA9A9A9, colors[0].getForegroundColor()); 170 171 s = Html.fromHtml("<font color=\"darkgrey\">DARKGREY</font>", flags); 172 colors = s.getSpans(0, s.length(), type); 173 assertEquals(0xFFA9A9A9, colors[0].getForegroundColor()); 174 } 175 testStylesFromHtml()176 public void testStylesFromHtml() { 177 Spanned s = Html.fromHtml("<span style=\"color:#FF0000; background-color:#00FF00; " 178 + "text-decoration:line-through;\">style</span>"); 179 180 ForegroundColorSpan[] foreground = s.getSpans(0, s.length(), ForegroundColorSpan.class); 181 assertEquals(1, foreground.length); 182 assertEquals(0xFFFF0000, foreground[0].getForegroundColor()); 183 184 BackgroundColorSpan[] background = s.getSpans(0, s.length(), BackgroundColorSpan.class); 185 assertEquals(1, background.length); 186 assertEquals(0xFF00FF00, background[0].getBackgroundColor()); 187 188 StrikethroughSpan[] strike = s.getSpans(0, s.length(), StrikethroughSpan.class); 189 assertEquals(1, strike.length); 190 } 191 testParagraphs()192 public void testParagraphs() throws Exception { 193 SpannableString s = new SpannableString("Hello world"); 194 assertThat(Html.toHtml(s), matchesIgnoringTrailingWhitespace( 195 "<p dir=\"ltr\">Hello world</p>")); 196 197 s = new SpannableString("Hello world\nor something"); 198 assertThat(Html.toHtml(s), matchesIgnoringTrailingWhitespace( 199 "<p dir=\"ltr\">Hello world<br>\nor something</p>")); 200 assertThat(Html.toHtml(s, Html.TO_HTML_PARAGRAPH_LINES_INDIVIDUAL), 201 matchesIgnoringTrailingWhitespace( 202 "<p dir=\"ltr\" style=\"margin-top:0; margin-bottom:0;\">Hello world</p>\n" 203 + "<p dir=\"ltr\" style=\"margin-top:0; margin-bottom:0;\">or something</p>")); 204 205 s = new SpannableString("Hello world\n\nor something"); 206 assertThat(Html.toHtml(s), matchesIgnoringTrailingWhitespace( 207 "<p dir=\"ltr\">Hello world</p>\n<p dir=\"ltr\">or something</p>")); 208 209 s = new SpannableString("Hello world\n\n\nor something"); 210 assertThat(Html.toHtml(s), matchesIgnoringTrailingWhitespace( 211 "<p dir=\"ltr\">Hello world<br></p>\n<p dir=\"ltr\">or something</p>")); 212 assertThat(Html.toHtml(s, Html.TO_HTML_PARAGRAPH_LINES_INDIVIDUAL), 213 matchesIgnoringTrailingWhitespace( 214 "<p dir=\"ltr\" style=\"margin-top:0; margin-bottom:0;\">Hello world</p>\n" 215 + "<br>\n" 216 + "<br>\n" 217 + "<p dir=\"ltr\" style=\"margin-top:0; margin-bottom:0;\">or something</p>")); 218 } 219 testParagraphStyles()220 public void testParagraphStyles() throws Exception { 221 SpannableString s = new SpannableString("Hello world"); 222 s.setSpan(new AlignmentSpan.Standard(Layout.Alignment.ALIGN_CENTER), 223 0, s.length(), Spanned.SPAN_PARAGRAPH); 224 assertThat(Html.toHtml(s, Html.TO_HTML_PARAGRAPH_LINES_INDIVIDUAL), 225 matchesIgnoringTrailingWhitespace( 226 "<p dir=\"ltr\" style=\"margin-top:0; margin-bottom:0; text-align:center;\">" 227 + "Hello world</p>")); 228 229 // Set another AlignmentSpan of a different alignment. Only the last one should be encoded. 230 s.setSpan(new AlignmentSpan.Standard(Layout.Alignment.ALIGN_OPPOSITE), 231 0, s.length(), Spanned.SPAN_PARAGRAPH); 232 assertThat(Html.toHtml(s, Html.TO_HTML_PARAGRAPH_LINES_INDIVIDUAL), 233 matchesIgnoringTrailingWhitespace( 234 "<p dir=\"ltr\" style=\"margin-top:0; margin-bottom:0; text-align:end;\">" 235 + "Hello world</p>")); 236 237 // Set another AlignmentSpan without SPAN_PARAGRAPH flag. 238 // This will be ignored and the previous alignment should be encoded. 239 s.setSpan(new AlignmentSpan.Standard(Layout.Alignment.ALIGN_NORMAL), 240 0, s.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 241 assertThat(Html.toHtml(s, Html.TO_HTML_PARAGRAPH_LINES_INDIVIDUAL), 242 matchesIgnoringTrailingWhitespace( 243 "<p dir=\"ltr\" style=\"margin-top:0; margin-bottom:0; text-align:end;\">" 244 + "Hello world</p>")); 245 } 246 testBulletSpan()247 public void testBulletSpan() throws Exception { 248 SpannableString s = new SpannableString("Bullet1\nBullet2\nNormal paragraph"); 249 s.setSpan(new BulletSpan(), 0, 8, Spanned.SPAN_PARAGRAPH); 250 s.setSpan(new BulletSpan(), 8, 16, Spanned.SPAN_PARAGRAPH); 251 assertThat(Html.toHtml(s, Html.TO_HTML_PARAGRAPH_LINES_INDIVIDUAL), 252 matchesIgnoringTrailingWhitespace( 253 "<ul style=\"margin-top:0; margin-bottom:0;\">\n" 254 + "<li dir=\"ltr\">Bullet1</li>\n" 255 + "<li dir=\"ltr\">Bullet2</li>\n" 256 + "</ul>\n" 257 + "<p dir=\"ltr\" style=\"margin-top:0; margin-bottom:0;\">Normal paragraph</p>")); 258 } 259 testBlockquote()260 public void testBlockquote() throws Exception { 261 final int start = 0; 262 263 SpannableString s = new SpannableString("Hello world"); 264 int end = s.length(); 265 s.setSpan(new QuoteSpan(), start, end, Spannable.SPAN_PARAGRAPH); 266 assertThat(Html.toHtml(s), matchesIgnoringTrailingWhitespace( 267 "<blockquote><p dir=\"ltr\">Hello world</p>\n</blockquote>")); 268 269 s = new SpannableString("Hello\n\nworld"); 270 end = 7; 271 s.setSpan(new QuoteSpan(), start, end, Spannable.SPAN_PARAGRAPH); 272 assertThat(Html.toHtml(s), matchesIgnoringTrailingWhitespace( 273 "<blockquote><p dir=\"ltr\">Hello</p>\n</blockquote>\n<p dir=\"ltr\">world</p>")); 274 } 275 testEntities()276 public void testEntities() throws Exception { 277 SpannableString s = new SpannableString("Hello <&> world"); 278 assertThat(Html.toHtml(s), matchesIgnoringTrailingWhitespace( 279 "<p dir=\"ltr\">Hello <&> world</p>")); 280 281 s = new SpannableString("Hello \u03D5 world"); 282 assertThat(Html.toHtml(s), matchesIgnoringTrailingWhitespace( 283 "<p dir=\"ltr\">Hello ϕ world</p>")); 284 285 s = new SpannableString("Hello world"); 286 assertThat(Html.toHtml(s), matchesIgnoringTrailingWhitespace( 287 "<p dir=\"ltr\">Hello world</p>")); 288 } 289 testMarkup()290 public void testMarkup() throws Exception { 291 final int start = 6; 292 293 SpannableString s = new SpannableString("Hello bold world"); 294 int end = s.length() - start; 295 s.setSpan(new StyleSpan(Typeface.BOLD), start, end, SPAN_EXCLUSIVE_INCLUSIVE); 296 assertThat(Html.toHtml(s), matchesIgnoringTrailingWhitespace( 297 "<p dir=\"ltr\">Hello <b>bold</b> world</p>")); 298 299 s = new SpannableString("Hello italic world"); 300 end = s.length() - start; 301 s.setSpan(new StyleSpan(Typeface.ITALIC), start, end, SPAN_EXCLUSIVE_INCLUSIVE); 302 assertThat(Html.toHtml(s), matchesIgnoringTrailingWhitespace( 303 "<p dir=\"ltr\">Hello <i>italic</i> world</p>")); 304 305 s = new SpannableString("Hello monospace world"); 306 end = s.length() - start; 307 s.setSpan(new TypefaceSpan("monospace"), start, end, SPAN_EXCLUSIVE_INCLUSIVE); 308 assertThat(Html.toHtml(s), matchesIgnoringTrailingWhitespace( 309 "<p dir=\"ltr\">Hello <tt>monospace</tt> world</p>")); 310 311 s = new SpannableString("Hello superscript world"); 312 end = s.length() - start; 313 s.setSpan(new SuperscriptSpan(), start, end, SPAN_EXCLUSIVE_INCLUSIVE); 314 assertThat(Html.toHtml(s), matchesIgnoringTrailingWhitespace( 315 "<p dir=\"ltr\">Hello <sup>superscript</sup> world</p>")); 316 317 s = new SpannableString("Hello subscript world"); 318 end = s.length() - start; 319 s.setSpan(new SubscriptSpan(), start, end, SPAN_EXCLUSIVE_INCLUSIVE); 320 assertThat(Html.toHtml(s), matchesIgnoringTrailingWhitespace( 321 "<p dir=\"ltr\">Hello <sub>subscript</sub> world</p>")); 322 323 s = new SpannableString("Hello underline world"); 324 end = s.length() - start; 325 s.setSpan(new UnderlineSpan(), start, end, SPAN_EXCLUSIVE_INCLUSIVE); 326 assertThat(Html.toHtml(s), matchesIgnoringTrailingWhitespace( 327 "<p dir=\"ltr\">Hello <u>underline</u> world</p>")); 328 329 s = new SpannableString("Hello struck world"); 330 end = s.length() - start; 331 s.setSpan(new StrikethroughSpan(), start, end, SPAN_EXCLUSIVE_INCLUSIVE); 332 assertThat(Html.toHtml(s), matchesIgnoringTrailingWhitespace("<p dir=\"ltr\">Hello " 333 + "<span style=\"text-decoration:line-through;\">struck</span> world</p>")); 334 335 s = new SpannableString("Hello linky world"); 336 end = s.length() - start; 337 s.setSpan(new URLSpan("http://www.google.com"), start, end, SPAN_EXCLUSIVE_INCLUSIVE); 338 assertThat(Html.toHtml(s), matchesIgnoringTrailingWhitespace("<p dir=\"ltr\">Hello " 339 + "<a href=\"http://www.google.com\">linky</a> world</p>")); 340 341 s = new SpannableString("Hello foreground world"); 342 end = s.length() - start; 343 s.setSpan(new ForegroundColorSpan(0x00FF00), start, end, SPAN_EXCLUSIVE_INCLUSIVE); 344 assertThat(Html.toHtml(s), matchesIgnoringTrailingWhitespace("<p dir=\"ltr\">Hello " 345 + "<span style=\"color:#00FF00;\">foreground</span> world</p>")); 346 347 s = new SpannableString("Hello background world"); 348 end = s.length() - start; 349 s.setSpan(new BackgroundColorSpan(0x00FF00), start, end, SPAN_EXCLUSIVE_INCLUSIVE); 350 assertThat(Html.toHtml(s), matchesIgnoringTrailingWhitespace("<p dir=\"ltr\">Hello " 351 + "<span style=\"background-color:#00FF00;\">background</span> world</p>")); 352 } 353 testMarkupFromHtml()354 public void testMarkupFromHtml() throws Exception { 355 final int expectedStart = 6; 356 final int expectedEnd = expectedStart + 6; 357 358 String tags[] = {"del", "s", "strike"}; 359 for (String tag : tags) { 360 String source = String.format("Hello <%s>struck</%s> world", tag, tag); 361 Spanned spanned = Html.fromHtml(source); 362 Object[] spans = spanned.getSpans(0, spanned.length(), Object.class); 363 assertEquals(1, spans.length); 364 assertEquals(StrikethroughSpan.class, spans[0].getClass()); 365 assertEquals(expectedStart, spanned.getSpanStart(spans[0])); 366 assertEquals(expectedEnd, spanned.getSpanEnd(spans[0])); 367 } 368 } 369 370 /** 371 * Tests if text alignments encoded as CSS TEXT-ALIGN property are correctly converted into 372 * {@link AlignmentSpan}s. Note that the span will also cover the first newline character after 373 * the text. 374 */ testTextAlignCssFromHtml()375 public void testTextAlignCssFromHtml() throws Exception { 376 String tags[] = {"p", "h1", "h2", "h3", "h4", "h5", "h6", "div", "blockquote"}; 377 378 for (String tag : tags) { 379 String source = String.format("<%s style=\"text-align:start\">TEXT</%s>" 380 + "<%s style=\"text-align:center\">TEXT</%s>" 381 + "<%s style=\"text-align:end\">TEXT</%s>", 382 tag, tag, tag, tag, tag, tag); 383 Spanned spanned = Html.fromHtml(source); 384 AlignmentSpan[] spans = spanned.getSpans(0, spanned.length(), AlignmentSpan.class); 385 assertEquals(3, spans.length); 386 387 assertEquals(Layout.Alignment.ALIGN_NORMAL, spans[0].getAlignment()); 388 assertEquals(0, spanned.getSpanStart(spans[0])); 389 assertEquals(5, spanned.getSpanEnd(spans[0])); 390 391 assertEquals(Layout.Alignment.ALIGN_CENTER, spans[1].getAlignment()); 392 assertEquals(6, spanned.getSpanStart(spans[1])); 393 assertEquals(11, spanned.getSpanEnd(spans[1])); 394 395 assertEquals(Layout.Alignment.ALIGN_OPPOSITE, spans[2].getAlignment()); 396 assertEquals(12, spanned.getSpanStart(spans[2])); 397 assertEquals(17, spanned.getSpanEnd(spans[2])); 398 399 // Other valid TEXT-ALIGN property encodings 400 source = String.format("<%s style=\'text-align:center\''>TEXT</%s>" 401 + "<%s style=\"text-align:center;\">TEXT</%s>" 402 + "<%s style=\"text-align : center ; \">TEXT</%s>", 403 tag, tag, tag, tag, tag, tag); 404 spanned = Html.fromHtml(source); 405 spans = spanned.getSpans(0, spanned.length(), AlignmentSpan.class); 406 assertEquals(3, spans.length); 407 408 // Invalid TEXT-ALIGN property encodings 409 source = String.format("<%s style=\"text-align:centre\">TEXT</%s>" 410 + "<%s style=\"text-alignment:center\">TEXT</%s>" 411 + "<%s style=\"align:center\">TEXT</%s>" 412 + "<%s style=\"text-align:gibberish\">TEXT</%s>", 413 tag, tag, tag, tag, tag, tag, tag, tag); 414 spanned = Html.fromHtml(source); 415 spans = spanned.getSpans(0, spanned.length(), AlignmentSpan.class); 416 assertEquals(0, spans.length); 417 } 418 } 419 testBlockLevelElementsFromHtml()420 public void testBlockLevelElementsFromHtml() throws Exception { 421 String source = "<blockquote>BLOCKQUOTE</blockquote>" 422 + "<div>DIV</div>" 423 + "<p>P</p>" 424 + "<h1>HEADING</h1>"; 425 426 int flags = Html.FROM_HTML_SEPARATOR_LINE_BREAK_BLOCKQUOTE 427 | Html.FROM_HTML_SEPARATOR_LINE_BREAK_DIV; 428 assertEquals("BLOCKQUOTE\nDIV\n\nP\n\nHEADING\n\n", 429 Html.fromHtml(source, flags, null, null).toString()); 430 431 flags = Html.FROM_HTML_SEPARATOR_LINE_BREAK_DIV 432 | Html.FROM_HTML_SEPARATOR_LINE_BREAK_PARAGRAPH; 433 assertEquals("BLOCKQUOTE\n\nDIV\nP\n\nHEADING\n\n", 434 Html.fromHtml(source, flags, null, null).toString()); 435 436 flags = Html.FROM_HTML_SEPARATOR_LINE_BREAK_PARAGRAPH 437 | Html.FROM_HTML_SEPARATOR_LINE_BREAK_HEADING; 438 assertEquals("BLOCKQUOTE\n\nDIV\n\nP\nHEADING\n", 439 Html.fromHtml(source, flags, null, null).toString()); 440 } 441 testListFromHtml()442 public void testListFromHtml() throws Exception { 443 String source = "CITRUS FRUITS:<ul><li>LEMON</li><li>LIME</li><li>ORANGE</li></ul>"; 444 assertEquals("CITRUS FRUITS:\n\nLEMON\n\nLIME\n\nORANGE\n\n", 445 Html.fromHtml(source).toString()); 446 447 int flags = Html.FROM_HTML_SEPARATOR_LINE_BREAK_LIST; 448 // The <li> still has to be separated by two newline characters 449 assertEquals("CITRUS FRUITS:\n\nLEMON\n\nLIME\n\nORANGE\n\n", 450 Html.fromHtml(source, flags, null, null).toString()); 451 452 flags = Html.FROM_HTML_SEPARATOR_LINE_BREAK_LIST_ITEM; 453 assertEquals("CITRUS FRUITS:\n\nLEMON\nLIME\nORANGE\n\n", 454 Html.fromHtml(source, flags, null, null).toString()); 455 456 flags = Html.FROM_HTML_SEPARATOR_LINE_BREAK_LIST 457 | Html.FROM_HTML_SEPARATOR_LINE_BREAK_LIST_ITEM; 458 assertEquals("CITRUS FRUITS:\nLEMON\nLIME\nORANGE\n", 459 Html.fromHtml(source, flags, null, null).toString()); 460 } 461 testParagraphFromHtml()462 public void testParagraphFromHtml() throws Exception { 463 final int flags = Html.FROM_HTML_SEPARATOR_LINE_BREAK_PARAGRAPH; 464 465 String source = "<p>Line 1</p><p>Line 2</p>"; 466 assertEquals("Line 1\nLine 2\n", 467 Html.fromHtml(source, flags).toString()); 468 assertEquals("Line 1\n\nLine 2\n\n", 469 Html.fromHtml(source).toString()); 470 471 source = "<br>Line 1<br>Line 2<br>"; 472 assertEquals("\nLine 1\nLine 2\n", 473 Html.fromHtml(source, flags).toString()); 474 assertEquals("\nLine 1\nLine 2\n", 475 Html.fromHtml(source).toString()); 476 477 source = "<br><p>Line 1</p><br><p>Line 2</p><br>"; 478 assertEquals("\nLine 1\n\nLine 2\n\n", 479 Html.fromHtml(source, flags).toString()); 480 assertEquals("\n\nLine 1\n\n\nLine 2\n\n\n", 481 Html.fromHtml(source).toString()); 482 483 source = "<p>Line 1<br>Line 2</p><p>Line 3</p>"; 484 assertEquals("Line 1\nLine 2\nLine 3\n", 485 Html.fromHtml(source, flags).toString()); 486 assertEquals("Line 1\nLine 2\n\nLine 3\n\n", 487 Html.fromHtml(source).toString()); 488 } 489 testHeadingFromHtml()490 public void testHeadingFromHtml() throws Exception { 491 final int flags = Html.FROM_HTML_SEPARATOR_LINE_BREAK_HEADING; 492 493 String source = "<h1>Heading 1</h1><h1>Heading 2</h1>"; 494 assertEquals("Heading 1\nHeading 2\n", 495 Html.fromHtml(source, flags).toString()); 496 assertEquals("Heading 1\n\nHeading 2\n\n", 497 Html.fromHtml(source).toString()); 498 499 source = "<br><h1>Heading 1</h1><br><h1>Heading 2</h1><br>"; 500 assertEquals("\nHeading 1\n\nHeading 2\n\n", 501 Html.fromHtml(source, flags).toString()); 502 assertEquals("\n\nHeading 1\n\n\nHeading 2\n\n\n", 503 Html.fromHtml(source).toString()); 504 505 source = "<h1>Heading 1<br>Heading 2</h1><h1>Heading 3</h1>"; 506 assertEquals("Heading 1\nHeading 2\nHeading 3\n", 507 Html.fromHtml(source, flags).toString()); 508 assertEquals("Heading 1\nHeading 2\n\nHeading 3\n\n", 509 Html.fromHtml(source).toString()); 510 } 511 testImg()512 public void testImg() throws Exception { 513 Spanned s = Html.fromHtml("yes<img src=\"http://example.com/foo.gif\">no"); 514 assertThat(Html.toHtml(s), matchesIgnoringTrailingWhitespace( 515 "<p dir=\"ltr\">yes<img src=\"http://example.com/foo.gif\">no</p>")); 516 } 517 testUtf8()518 public void testUtf8() throws Exception { 519 Spanned s = Html.fromHtml("<p>\u0124\u00eb\u0142\u0142o, world!</p>"); 520 assertThat(Html.toHtml(s), matchesIgnoringTrailingWhitespace( 521 "<p dir=\"ltr\">Ĥëłło, world!</p>")); 522 } 523 testSurrogates()524 public void testSurrogates() throws Exception { 525 Spanned s = Html.fromHtml("\ud83d\udc31"); 526 assertThat(Html.toHtml(s), matchesIgnoringTrailingWhitespace( 527 "<p dir=\"ltr\">🐱</p>")); 528 } 529 testBadSurrogates()530 public void testBadSurrogates() throws Exception { 531 Spanned s = Html.fromHtml("\udc31\ud83d"); 532 assertThat(Html.toHtml(s), matchesIgnoringTrailingWhitespace("<p dir=\"ltr\"></p>")); 533 } 534 matchesIgnoringTrailingWhitespace( String expected)535 private static StringIgnoringTrailingWhitespaceMatcher matchesIgnoringTrailingWhitespace( 536 String expected) { 537 return new StringIgnoringTrailingWhitespaceMatcher(expected); 538 } 539 540 private static final class StringIgnoringTrailingWhitespaceMatcher extends 541 BaseMatcher<String> { 542 private final String mStrippedString; 543 StringIgnoringTrailingWhitespaceMatcher(String string)544 public StringIgnoringTrailingWhitespaceMatcher(String string) { 545 mStrippedString = stripTrailingWhitespace(string); 546 } 547 548 @Override matches(Object item)549 public boolean matches(Object item) { 550 final String string = (String) item; 551 return mStrippedString.equals(stripTrailingWhitespace(string)); 552 } 553 stripTrailingWhitespace(String text)554 private String stripTrailingWhitespace(String text) { 555 return text.replaceFirst("\\s+$", ""); 556 } 557 558 @Override describeTo(Description description)559 public void describeTo(Description description) { 560 description.appendText("is equal to ") 561 .appendText(mStrippedString) 562 .appendText(" ignoring tailing whitespaces"); 563 } 564 } 565 } 566