 Chromium Code Reviews
 Chromium Code Reviews Issue 1070223004:
  Stop combining text runs which are connected by 'COMMON' blocks.  (Closed) 
  Base URL: https://chromium.googlesource.com/chromium/src.git@master
    
  
    Issue 1070223004:
  Stop combining text runs which are connected by 'COMMON' blocks.  (Closed) 
  Base URL: https://chromium.googlesource.com/chromium/src.git@master| 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 |