Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "ui/gfx/render_text.h" | 5 #include "ui/gfx/render_text.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 | 8 |
| 9 #include "base/format_macros.h" | 9 #include "base/format_macros.h" |
| 10 #include "base/i18n/break_iterator.h" | 10 #include "base/i18n/break_iterator.h" |
| (...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 83 render_text->MoveCursor(CHARACTER_BREAK, direction, false); | 83 render_text->MoveCursor(CHARACTER_BREAK, direction, false); |
| 84 } | 84 } |
| 85 // Check that cursoring is clamped at the line edge. | 85 // Check that cursoring is clamped at the line edge. |
| 86 EXPECT_EQ(expected.back(), render_text->selection_model()); | 86 EXPECT_EQ(expected.back(), render_text->selection_model()); |
| 87 // Check that it is the line edge. | 87 // Check that it is the line edge. |
| 88 render_text->MoveCursor(LINE_BREAK, direction, false); | 88 render_text->MoveCursor(LINE_BREAK, direction, false); |
| 89 EXPECT_EQ(expected.back(), render_text->selection_model()); | 89 EXPECT_EQ(expected.back(), render_text->selection_model()); |
| 90 } | 90 } |
| 91 #endif // !defined(OS_MACOSX) | 91 #endif // !defined(OS_MACOSX) |
| 92 | 92 |
| 93 // Test utility for Multiline_Newline test case. Empty |expected_range| means | |
| 94 // the blank line which has no segments. Otherwise |segments| should contain | |
| 95 // exactly one line segment whose range equals to |expected_range|. | |
| 96 void VerifyLineSegments(const Range& expected_range, | |
| 97 const std::vector<internal::LineSegment>& segments) { | |
| 98 EXPECT_EQ(expected_range.is_empty() ? 0ul : 1ul, segments.size()); | |
| 99 if (!expected_range.is_empty()) | |
| 100 EXPECT_EQ(expected_range, segments[0].char_range); | |
| 101 } | |
| 102 | |
| 103 // The class which records the drawing operations so that the test case can | 93 // The class which records the drawing operations so that the test case can |
| 104 // verify where exactly the glyphs are drawn. | 94 // verify where exactly the glyphs are drawn. |
| 105 class TestSkiaTextRenderer : public internal::SkiaTextRenderer { | 95 class TestSkiaTextRenderer : public internal::SkiaTextRenderer { |
| 106 public: | 96 public: |
| 107 struct TextLog { | 97 struct TextLog { |
| 108 TextLog() : glyph_count(0u) {} | 98 TextLog() : glyph_count(0u) {} |
| 109 PointF origin; | 99 PointF origin; |
| 110 size_t glyph_count; | 100 size_t glyph_count; |
| 111 }; | 101 }; |
| 112 | 102 |
| (...skipping 2008 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2121 const wchar_t* const text; | 2111 const wchar_t* const text; |
| 2122 const Range first_line_char_range; | 2112 const Range first_line_char_range; |
| 2123 const Range second_line_char_range; | 2113 const Range second_line_char_range; |
| 2124 bool is_ltr; | 2114 bool is_ltr; |
| 2125 } kTestStrings[] = { | 2115 } kTestStrings[] = { |
| 2126 { L"abc defg hijkl", Range(0, 9), Range(9, 14), true }, | 2116 { L"abc defg hijkl", Range(0, 9), Range(9, 14), true }, |
| 2127 { L"qwertyzxcvbn", Range(0, 10), Range(10, 12), true }, | 2117 { L"qwertyzxcvbn", Range(0, 10), Range(10, 12), true }, |
| 2128 { L"\x0627\x0644\x0644\x063A\x0629 " | 2118 { L"\x0627\x0644\x0644\x063A\x0629 " |
| 2129 L"\x0627\x0644\x0639\x0631\x0628\x064A\x0629", | 2119 L"\x0627\x0644\x0639\x0631\x0628\x064A\x0629", |
| 2130 Range(0, 6), Range(6, 13), false }, | 2120 Range(0, 6), Range(6, 13), false }, |
| 2131 { L"\x062A\x0641\x0627\x062D\x05EA\x05E4\x05D5\x05D6\x05D9" | 2121 { L"\x062A\x0641\x0627\x062D \x05EA\x05E4\x05D5\x05D6\x05D9" |
| 2132 L"\x05DA\x05DB\x05DD", Range(0, 4), Range(4, 12), false } | 2122 L"\x05DA\x05DB\x05DD", Range(0, 5), Range(5, 13), false } |
| 2133 }; | 2123 }; |
| 2134 | 2124 |
| 2135 RenderTextHarfBuzz render_text; | 2125 RenderTextHarfBuzz render_text; |
| 2136 // Specify the fixed width for characters to suppress the possible variations | 2126 // Specify the fixed width for characters to suppress the possible variations |
| 2137 // of linebreak results. | 2127 // of linebreak results. |
| 2138 render_text.set_glyph_width_for_test(5); | 2128 render_text.set_glyph_width_for_test(5); |
| 2139 render_text.SetDisplayRect(Rect(50, 1000)); | 2129 render_text.SetDisplayRect(Rect(50, 1000)); |
| 2140 render_text.SetMultiline(true); | 2130 render_text.SetMultiline(true); |
| 2141 render_text.SetWordWrapBehavior(WRAP_LONG_WORDS); | 2131 render_text.SetWordWrapBehavior(WRAP_LONG_WORDS); |
| 2142 render_text.SetHorizontalAlignment(ALIGN_TO_HEAD); | 2132 render_text.SetHorizontalAlignment(ALIGN_TO_HEAD); |
| (...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2222 for (size_t i = 0; i < arraysize(kTestStrings); ++i) { | 2212 for (size_t i = 0; i < arraysize(kTestStrings); ++i) { |
| 2223 SCOPED_TRACE(base::StringPrintf("kTestStrings[%" PRIuS "]", i)); | 2213 SCOPED_TRACE(base::StringPrintf("kTestStrings[%" PRIuS "]", i)); |
| 2224 render_text.SetText(WideToUTF16(kTestStrings[i].text)); | 2214 render_text.SetText(WideToUTF16(kTestStrings[i].text)); |
| 2225 render_text.Draw(&canvas); | 2215 render_text.Draw(&canvas); |
| 2226 EXPECT_EQ(kTestStrings[i].lines_count, render_text.lines_.size()); | 2216 EXPECT_EQ(kTestStrings[i].lines_count, render_text.lines_.size()); |
| 2227 if (kTestStrings[i].lines_count != render_text.lines_.size()) | 2217 if (kTestStrings[i].lines_count != render_text.lines_.size()) |
| 2228 continue; | 2218 continue; |
| 2229 | 2219 |
| 2230 for (size_t j = 0; j < kTestStrings[i].lines_count; ++j) { | 2220 for (size_t j = 0; j < kTestStrings[i].lines_count; ++j) { |
| 2231 SCOPED_TRACE(base::StringPrintf("Line %" PRIuS "", j)); | 2221 SCOPED_TRACE(base::StringPrintf("Line %" PRIuS "", j)); |
| 2232 VerifyLineSegments(kTestStrings[i].line_char_ranges[j], | 2222 // There might be multiple segments in one line. Merge all the segments |
|
msw
2015/05/27 17:38:00
Are there actually multiple segments in any of the
msw
2015/05/27 17:38:00
nit: "Merge all the segments ranges in the same li
xdai1
2015/06/01 16:51:16
Yes, for test case "a \n b", there are four runs h
xdai1
2015/06/01 16:51:16
Done.
| |
| 2233 render_text.lines_[j].segments); | 2223 // range. |
| 2224 size_t segment_size = render_text.lines()[j].segments.size(); | |
|
msw
2015/05/27 17:38:00
nit: const
xdai1
2015/06/01 16:51:16
Done.
| |
| 2225 Range line_range; | |
| 2226 if (segment_size > 0) | |
| 2227 line_range = Range( | |
| 2228 render_text.lines()[j].segments[0].char_range.start(), | |
| 2229 render_text.lines()[j].segments[segment_size - 1].char_range.end()); | |
|
msw
2015/05/27 17:38:00
Would we need to union all the ranges for RTL? Pro
xdai1
2015/06/01 16:51:16
I don't think so. This test function doesn't test
| |
| 2230 EXPECT_EQ(kTestStrings[i].line_char_ranges[j], line_range); | |
| 2234 } | 2231 } |
| 2235 } | 2232 } |
| 2236 } | 2233 } |
| 2237 | 2234 |
| 2238 // Make sure that multiline mode ignores elide behavior. | 2235 // Make sure that multiline mode ignores elide behavior. |
| 2239 TEST_F(RenderTextTest, Multiline_IgnoreElide) { | 2236 TEST_F(RenderTextTest, Multiline_IgnoreElide) { |
| 2240 const wchar_t kTestString[] = | 2237 const wchar_t kTestString[] = |
| 2241 L"very very very long string xxxxxxxxxxxxxxxxxxxxxxxxxx"; | 2238 L"very very very long string xxxxxxxxxxxxxxxxxxxxxxxxxx"; |
| 2242 const wchar_t kEllipsis[] = L"\x2026"; | 2239 const wchar_t kEllipsis[] = L"\x2026"; |
| 2243 | 2240 |
| (...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2358 for (size_t j = 0; j < render_text.lines().size(); ++j) { | 2355 for (size_t j = 0; j < render_text.lines().size(); ++j) { |
| 2359 SCOPED_TRACE(base::StringPrintf("%" PRIuS "-th line", j)); | 2356 SCOPED_TRACE(base::StringPrintf("%" PRIuS "-th line", j)); |
| 2360 EXPECT_EQ(kTestScenarios[i].char_ranges[j], | 2357 EXPECT_EQ(kTestScenarios[i].char_ranges[j], |
| 2361 render_text.lines()[j].segments[0].char_range); | 2358 render_text.lines()[j].segments[0].char_range); |
| 2362 EXPECT_EQ(kTestScenarios[i].char_ranges[j].length() * kGlyphSize, | 2359 EXPECT_EQ(kTestScenarios[i].char_ranges[j].length() * kGlyphSize, |
| 2363 render_text.lines()[j].size.width()); | 2360 render_text.lines()[j].size.width()); |
| 2364 } | 2361 } |
| 2365 } | 2362 } |
| 2366 } | 2363 } |
| 2367 | 2364 |
| 2365 TEST_F(RenderTextTest, Multiline_LineBreakerBehavior) { | |
| 2366 const int kGlyphSize = 5; | |
| 2367 const struct { | |
| 2368 const wchar_t* const text; | |
| 2369 const WordWrapBehavior behavior; | |
| 2370 const size_t num_lines; | |
|
msw
2015/05/27 17:38:00
nit: Why not just assume 3 in the code; we assume
xdai1
2015/06/01 16:51:16
Done.
| |
| 2371 const Range char_ranges[3]; | |
| 2372 } kTestScenarios[] = { | |
| 2373 { L"a single run", IGNORE_LONG_WORDS, 3u, | |
| 2374 {Range(0, 2), Range(2, 9), Range(9, 12) } }, | |
| 2375 // 3 words: "That's ", ""good". ", "aaa" and 7 runs: "That", "'", "s ", | |
| 2376 // """, "good", "". ", "aaa". They all mixed together. | |
| 2377 { L"That's \"good\". aaa", IGNORE_LONG_WORDS, 3u, | |
| 2378 {Range(0, 7), Range(7, 15), Range(15, 18) } }, | |
| 2379 // Test "\"" should be put into a new line correctly. | |
| 2380 { L"a \"good\" one.", IGNORE_LONG_WORDS, 3u, | |
| 2381 {Range(0, 2), Range(2, 9), Range(9, 13) } }, | |
| 2382 // Test for full-width space. | |
| 2383 { L"That's\x3000good.\x3000yyy", IGNORE_LONG_WORDS, 3u, | |
| 2384 {Range(0, 7), Range(7, 13), Range(13, 16) } }, | |
| 2385 { L"a single run", TRUNCATE_LONG_WORDS, 3u, | |
| 2386 {Range(0, 2), Range(2, 6), Range(9, 12) } }, | |
| 2387 { L"That's \"good\". aaa", TRUNCATE_LONG_WORDS, 3u, | |
| 2388 {Range(0, 4), Range(7, 11), Range(15, 18) } }, | |
| 2389 { L"That's good. aaa", TRUNCATE_LONG_WORDS, 3u, | |
| 2390 {Range(0, 4), Range(7, 11), Range(13, 16) } }, | |
| 2391 { L"a \"good\" one.", TRUNCATE_LONG_WORDS, 3u, | |
| 2392 {Range(0, 2), Range(2, 6), Range(9, 13) } }, | |
| 2393 { L"asingleword", WRAP_LONG_WORDS, 3u, | |
| 2394 {Range(0, 4), Range(4, 8), Range(8, 11) } }, | |
| 2395 { L"That's good", WRAP_LONG_WORDS, 3u, | |
| 2396 {Range(0, 4), Range(4, 7), Range(7, 11) } }, | |
| 2397 { L"That's \"g\".", WRAP_LONG_WORDS, 3u, | |
| 2398 {Range(0, 4), Range(4, 7), Range(7, 11) } }, | |
| 2399 }; | |
| 2400 | |
| 2401 RenderTextHarfBuzz render_text; | |
| 2402 render_text.SetMultiline(true); | |
| 2403 render_text.set_glyph_width_for_test(kGlyphSize); | |
| 2404 render_text.SetDisplayRect(Rect(0, 0, kGlyphSize * 4, 0)); | |
| 2405 | |
| 2406 Canvas canvas; | |
| 2407 | |
| 2408 for (size_t i = 0; i < arraysize(kTestScenarios); ++i) { | |
| 2409 SCOPED_TRACE(base::StringPrintf("kTestStrings[%" PRIuS "]", i)); | |
| 2410 render_text.SetText(WideToUTF16(kTestScenarios[i].text)); | |
| 2411 render_text.SetWordWrapBehavior(kTestScenarios[i].behavior); | |
| 2412 render_text.Draw(&canvas); | |
| 2413 | |
| 2414 ASSERT_EQ(kTestScenarios[i].num_lines, render_text.lines().size()); | |
| 2415 for (size_t j = 0; j < render_text.lines().size(); ++j) { | |
| 2416 SCOPED_TRACE(base::StringPrintf("%" PRIuS "-th line", j)); | |
| 2417 // Merges all the segments ranges in the same line. | |
|
msw
2015/05/27 17:38:00
nit: "Merge"
xdai1
2015/06/01 16:51:16
Done.
| |
| 2418 size_t segment_size = render_text.lines()[j].segments.size(); | |
| 2419 Range line_range; | |
| 2420 if (segment_size > 0) | |
| 2421 line_range = Range( | |
| 2422 render_text.lines()[j].segments[0].char_range.start(), | |
| 2423 render_text.lines()[j].segments[segment_size - 1].char_range.end()); | |
| 2424 EXPECT_EQ(kTestScenarios[i].char_ranges[j], line_range); | |
| 2425 EXPECT_EQ(kTestScenarios[i].char_ranges[j].length() * kGlyphSize, | |
| 2426 render_text.lines()[j].size.width()); | |
| 2427 } | |
| 2428 } | |
| 2429 } | |
| 2430 | |
| 2368 TEST_F(RenderTextTest, NewlineWithoutMultilineFlag) { | 2431 TEST_F(RenderTextTest, NewlineWithoutMultilineFlag) { |
| 2369 const wchar_t* kTestStrings[] = { | 2432 const wchar_t* kTestStrings[] = { |
| 2370 L"abc\ndef", L"a \n b ", L"ab\n", L"a\n\nb", L"\nab", L"\n", | 2433 L"abc\ndef", L"a \n b ", L"ab\n", L"a\n\nb", L"\nab", L"\n", |
| 2371 }; | 2434 }; |
| 2372 | 2435 |
| 2373 RenderTextHarfBuzz render_text; | 2436 RenderTextHarfBuzz render_text; |
| 2374 render_text.SetDisplayRect(Rect(200, 1000)); | 2437 render_text.SetDisplayRect(Rect(200, 1000)); |
| 2375 Canvas canvas; | 2438 Canvas canvas; |
| 2376 | 2439 |
| 2377 for (size_t i = 0; i < arraysize(kTestStrings); ++i) { | 2440 for (size_t i = 0; i < arraysize(kTestStrings); ++i) { |
| (...skipping 225 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2603 render_text.EnsureLayout(); | 2666 render_text.EnsureLayout(); |
| 2604 internal::TextRunList* run_list = render_text.GetRunList(); | 2667 internal::TextRunList* run_list = render_text.GetRunList(); |
| 2605 ASSERT_EQ(3U, run_list->size()); | 2668 ASSERT_EQ(3U, run_list->size()); |
| 2606 EXPECT_EQ(Range(0, 1), run_list->runs()[0]->range); | 2669 EXPECT_EQ(Range(0, 1), run_list->runs()[0]->range); |
| 2607 EXPECT_EQ(Range(1, 2), run_list->runs()[1]->range); | 2670 EXPECT_EQ(Range(1, 2), run_list->runs()[1]->range); |
| 2608 EXPECT_EQ(Range(2, 3), run_list->runs()[2]->range); | 2671 EXPECT_EQ(Range(2, 3), run_list->runs()[2]->range); |
| 2609 | 2672 |
| 2610 render_text.SetText(WideToUTF16(L"x \x25B6 y")); | 2673 render_text.SetText(WideToUTF16(L"x \x25B6 y")); |
| 2611 render_text.EnsureLayout(); | 2674 render_text.EnsureLayout(); |
| 2612 run_list = render_text.GetRunList(); | 2675 run_list = render_text.GetRunList(); |
| 2613 ASSERT_EQ(3U, run_list->size()); | 2676 ASSERT_EQ(4U, run_list->size()); |
| 2614 EXPECT_EQ(Range(0, 2), run_list->runs()[0]->range); | 2677 EXPECT_EQ(Range(0, 2), run_list->runs()[0]->range); |
| 2615 EXPECT_EQ(Range(2, 3), run_list->runs()[1]->range); | 2678 EXPECT_EQ(Range(2, 3), run_list->runs()[1]->range); |
| 2616 EXPECT_EQ(Range(3, 5), run_list->runs()[2]->range); | 2679 EXPECT_EQ(Range(3, 4), run_list->runs()[2]->range); |
| 2680 EXPECT_EQ(Range(4, 5), run_list->runs()[3]->range); | |
| 2617 } | 2681 } |
| 2618 | 2682 |
| 2619 TEST_F(RenderTextTest, HarfBuzz_BreakRunsByEmoji) { | 2683 TEST_F(RenderTextTest, HarfBuzz_BreakRunsByEmoji) { |
| 2620 RenderTextHarfBuzz render_text; | 2684 RenderTextHarfBuzz render_text; |
| 2621 | 2685 |
| 2622 // \xF0\x9F\x98\x81 (U+1F601) is smile icon emoji. \xE2\x9C\xA8 (U+2728) is | 2686 // \xF0\x9F\x98\x81 (U+1F601) is smile icon emoji. \xE2\x9C\xA8 (U+2728) is |
| 2623 // a sparkle icon. Both can be drawn with color emoji fonts, so runs should be | 2687 // a sparkle icon. Both can be drawn with color emoji fonts, so runs should be |
| 2624 // separated. See crbug.com/448909 | 2688 // separated. See crbug.com/448909 |
| 2625 render_text.SetText(UTF8ToUTF16("x\xF0\x9F\x98\x81y\xE2\x9C\xA8")); | 2689 render_text.SetText(UTF8ToUTF16("x\xF0\x9F\x98\x81y\xE2\x9C\xA8")); |
| 2626 render_text.EnsureLayout(); | 2690 render_text.EnsureLayout(); |
| (...skipping 314 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2941 string_size.set_width(string_size.width() / 2); | 3005 string_size.set_width(string_size.width() / 2); |
| 2942 render_text.SetDisplayRect(gfx::Rect(string_size)); | 3006 render_text.SetDisplayRect(gfx::Rect(string_size)); |
| 2943 render_text.EnsureLayout(); | 3007 render_text.EnsureLayout(); |
| 2944 CFIndex glyph_count = CTLineGetGlyphCount(render_text.line_); | 3008 CFIndex glyph_count = CTLineGetGlyphCount(render_text.line_); |
| 2945 EXPECT_GT(text.size(), static_cast<size_t>(glyph_count)); | 3009 EXPECT_GT(text.size(), static_cast<size_t>(glyph_count)); |
| 2946 EXPECT_NE(0, glyph_count); | 3010 EXPECT_NE(0, glyph_count); |
| 2947 } | 3011 } |
| 2948 #endif | 3012 #endif |
| 2949 | 3013 |
| 2950 } // namespace gfx | 3014 } // namespace gfx |
| OLD | NEW |