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/memory/scoped_ptr.h" | 11 #include "base/memory/scoped_ptr.h" |
11 #include "base/strings/string_util.h" | 12 #include "base/strings/string_util.h" |
12 #include "base/strings/stringprintf.h" | 13 #include "base/strings/stringprintf.h" |
13 #include "base/strings/utf_string_conversions.h" | 14 #include "base/strings/utf_string_conversions.h" |
14 #include "testing/gtest/include/gtest/gtest.h" | 15 #include "testing/gtest/include/gtest/gtest.h" |
15 #include "ui/gfx/break_list.h" | 16 #include "ui/gfx/break_list.h" |
16 #include "ui/gfx/canvas.h" | 17 #include "ui/gfx/canvas.h" |
17 #include "ui/gfx/render_text_harfbuzz.h" | 18 #include "ui/gfx/render_text_harfbuzz.h" |
18 | 19 |
19 #if defined(OS_WIN) | 20 #if defined(OS_WIN) |
(...skipping 2027 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2047 | 2048 |
2048 render_text->SetText(WideToUTF16(L"x \x25B6 y")); | 2049 render_text->SetText(WideToUTF16(L"x \x25B6 y")); |
2049 render_text->EnsureLayout(); | 2050 render_text->EnsureLayout(); |
2050 ASSERT_EQ(3U, render_text->runs_.size()); | 2051 ASSERT_EQ(3U, render_text->runs_.size()); |
2051 EXPECT_EQ(Range(0, 2), render_text->runs_[0]->range); | 2052 EXPECT_EQ(Range(0, 2), render_text->runs_[0]->range); |
2052 EXPECT_EQ(Range(2, 3), render_text->runs_[1]->range); | 2053 EXPECT_EQ(Range(2, 3), render_text->runs_[1]->range); |
2053 EXPECT_EQ(Range(3, 5), render_text->runs_[2]->range); | 2054 EXPECT_EQ(Range(3, 5), render_text->runs_[2]->range); |
2054 } | 2055 } |
2055 #endif // defined(OS_WIN) | 2056 #endif // defined(OS_WIN) |
2056 | 2057 |
2057 TEST_F(RenderTextTest, HarfBuzz_CharToGlyph) { | 2058 // Test TextRunHarfBuzz's cluster finding logic. |
| 2059 TEST_F(RenderTextTest, HarfBuzz_Clusters) { |
2058 struct { | 2060 struct { |
2059 uint32 glyph_to_char[4]; | 2061 uint32 glyph_to_char[4]; |
2060 size_t char_to_glyph_expected[4]; | 2062 Range chars[4]; |
2061 Range char_range_to_glyph_range_expected[4]; | 2063 Range glyphs[4]; |
2062 bool is_rtl; | 2064 bool is_rtl; |
2063 } cases[] = { | 2065 } cases[] = { |
2064 { // From string "A B C D" to glyphs "a b c d". | 2066 { // From string "A B C D" to glyphs "a b c d". |
2065 { 0, 1, 2, 3 }, | 2067 { 0, 1, 2, 3 }, |
2066 { 0, 1, 2, 3 }, | 2068 { Range(0, 1), Range(1, 2), Range(2, 3), Range(3, 4) }, |
2067 { Range(0, 1), Range(1, 2), Range(2, 3), Range(3, 4) }, | 2069 { Range(0, 1), Range(1, 2), Range(2, 3), Range(3, 4) }, |
2068 false | 2070 false |
2069 }, | 2071 }, |
2070 { // From string "A B C D" to glyphs "d b c a". | 2072 { // From string "A B C D" to glyphs "d c b a". |
2071 { 3, 2, 1, 0 }, | 2073 { 3, 2, 1, 0 }, |
2072 { 3, 2, 1, 0 }, | 2074 { Range(0, 1), Range(1, 2), Range(2, 3), Range(3, 4) }, |
2073 { Range(3, 4), Range(2, 3), Range(1, 2), Range(0, 1) }, | 2075 { Range(3, 4), Range(2, 3), Range(1, 2), Range(0, 1) }, |
2074 true | 2076 true |
2075 }, | 2077 }, |
2076 { // From string "A B C D" to glyphs "ab c c d". | 2078 { // From string "A B C D" to glyphs "ab c c d". |
2077 { 0, 2, 2, 3 }, | 2079 { 0, 2, 2, 3 }, |
2078 { 0, 0, 1, 3 }, | 2080 { Range(0, 2), Range(0, 2), Range(2, 3), Range(3, 4) }, |
2079 { Range(0, 1), Range(0, 1), Range(1, 3), Range(3, 4) }, | 2081 { Range(0, 1), Range(0, 1), Range(1, 3), Range(3, 4) }, |
2080 false | 2082 false |
2081 }, | 2083 }, |
2082 { // From string "A B C D" to glyphs "d c c ba". | 2084 { // From string "A B C D" to glyphs "d c c ba". |
2083 { 3, 2, 2, 0 }, | 2085 { 3, 2, 2, 0 }, |
2084 { 3, 3, 1, 0 }, | 2086 { Range(0, 2), Range(0, 2), Range(2, 3), Range(3, 4) }, |
2085 { Range(3, 4), Range(3, 4), Range(1, 3), Range(0, 1) }, | 2087 { Range(3, 4), Range(3, 4), Range(1, 3), Range(0, 1) }, |
2086 true | 2088 true |
2087 }, | 2089 }, |
2088 }; | 2090 }; |
2089 | 2091 |
2090 internal::TextRunHarfBuzz run; | 2092 internal::TextRunHarfBuzz run; |
2091 run.range = Range(0, 4); | 2093 run.range = Range(0, 4); |
2092 run.glyph_count = 4; | 2094 run.glyph_count = 4; |
2093 run.glyph_to_char.reset(new uint32[4]); | 2095 run.glyph_to_char.resize(4); |
2094 | 2096 |
2095 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) { | 2097 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) { |
2096 std::copy(cases[i].glyph_to_char, cases[i].glyph_to_char + 4, | 2098 std::copy(cases[i].glyph_to_char, cases[i].glyph_to_char + 4, |
2097 run.glyph_to_char.get()); | 2099 run.glyph_to_char.begin()); |
2098 run.is_rtl = cases[i].is_rtl; | 2100 run.is_rtl = cases[i].is_rtl; |
| 2101 |
2099 for (size_t j = 0; j < 4; ++j) { | 2102 for (size_t j = 0; j < 4; ++j) { |
2100 SCOPED_TRACE(base::StringPrintf("Case %" PRIuS ", char %" PRIuS, i, j)); | 2103 SCOPED_TRACE(base::StringPrintf("Case %" PRIuS ", char %" PRIuS, i, j)); |
2101 EXPECT_EQ(cases[i].char_to_glyph_expected[j], run.CharToGlyph(j)); | 2104 Range chars; |
2102 EXPECT_EQ(cases[i].char_range_to_glyph_range_expected[j], | 2105 Range glyphs; |
2103 run.CharRangeToGlyphRange(Range(j, j + 1))); | 2106 run.GetClusterAt(j, &chars, &glyphs); |
| 2107 EXPECT_EQ(cases[i].chars[j], chars); |
| 2108 EXPECT_EQ(cases[i].glyphs[j], glyphs); |
| 2109 EXPECT_EQ(cases[i].glyphs[j], run.CharRangeToGlyphRange(chars)); |
2104 } | 2110 } |
2105 } | 2111 } |
2106 } | 2112 } |
| 2113 |
| 2114 // Ensure that graphemes with multiple code points do not get split. |
| 2115 TEST_F(RenderTextTest, HarfBuzz_SubglyphGraphemeCases) { |
| 2116 const wchar_t* cases[] = { |
| 2117 // "A" with a combining umlaut, followed by a "B". |
| 2118 L"A\x0308" L"B", |
| 2119 // Devanagari biconsonantal conjunct "ki", followed by an "a". |
| 2120 L"\x0915\x093f\x0905", |
| 2121 // Thai consonant and vowel pair "cho chan" + "sara am", followed by Thai |
| 2122 // digit 0. |
| 2123 L"\x0e08\x0e33\x0E50", |
| 2124 }; |
| 2125 |
| 2126 RenderTextHarfBuzz render_text; |
| 2127 |
| 2128 for (size_t i = 0; i < arraysize(cases); ++i) { |
| 2129 SCOPED_TRACE(base::StringPrintf("Case %" PRIuS, i)); |
| 2130 |
| 2131 base::string16 text = WideToUTF16(cases[i]); |
| 2132 render_text.SetText(text); |
| 2133 render_text.EnsureLayout(); |
| 2134 ASSERT_EQ(1U, render_text.runs_.size()); |
| 2135 internal::TextRunHarfBuzz* run = render_text.runs_[0]; |
| 2136 |
| 2137 base::i18n::BreakIterator* iter = render_text.grapheme_iterator_.get(); |
| 2138 Range first_grapheme_bounds = run->GetGraphemeBounds(iter, 0); |
| 2139 EXPECT_EQ(first_grapheme_bounds, run->GetGraphemeBounds(iter, 1)); |
| 2140 Range second_grapheme_bounds = run->GetGraphemeBounds(iter, 2); |
| 2141 EXPECT_EQ(first_grapheme_bounds.end(), second_grapheme_bounds.start()); |
| 2142 } |
| 2143 } |
| 2144 |
| 2145 // Test the partition of a multi-grapheme cluster into grapheme ranges. |
| 2146 TEST_F(RenderTextTest, HarfBuzz_SubglyphGraphemePartition) { |
| 2147 struct { |
| 2148 uint32 glyph_to_char[2]; |
| 2149 Range bounds[4]; |
| 2150 bool is_rtl; |
| 2151 } cases[] = { |
| 2152 { // From string "A B C D" to glyphs "a bcd". |
| 2153 { 0, 1 }, |
| 2154 { Range(0, 10), Range(10, 13), Range(13, 17), Range(17, 20) }, |
| 2155 false |
| 2156 }, |
| 2157 { // From string "A B C D" to glyphs "ab cd". |
| 2158 { 0, 2 }, |
| 2159 { Range(0, 5), Range(5, 10), Range(10, 15), Range(15, 20) }, |
| 2160 false |
| 2161 }, |
| 2162 { // From string "A B C D" to glyphs "dcb a". |
| 2163 { 1, 0 }, |
| 2164 { Range(10, 20), Range(7, 10), Range(3, 7), Range(0, 3) }, |
| 2165 true |
| 2166 }, |
| 2167 { // From string "A B C D" to glyphs "dc ba". |
| 2168 { 2, 0 }, |
| 2169 { Range(15, 20), Range(10, 15), Range(5, 10), Range(0, 5) }, |
| 2170 true |
| 2171 }, |
| 2172 }; |
| 2173 |
| 2174 internal::TextRunHarfBuzz run; |
| 2175 run.range = Range(0, 4); |
| 2176 run.glyph_count = 2; |
| 2177 run.glyph_to_char.resize(2); |
| 2178 run.positions.reset(new SkPoint[4]); |
| 2179 run.width = 20; |
| 2180 |
| 2181 const base::string16 kString = ASCIIToUTF16("abcd"); |
| 2182 scoped_ptr<base::i18n::BreakIterator> iter(new base::i18n::BreakIterator( |
| 2183 kString, base::i18n::BreakIterator::BREAK_CHARACTER)); |
| 2184 ASSERT_TRUE(iter->Init()); |
| 2185 |
| 2186 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) { |
| 2187 std::copy(cases[i].glyph_to_char, cases[i].glyph_to_char + 2, |
| 2188 run.glyph_to_char.begin()); |
| 2189 run.is_rtl = cases[i].is_rtl; |
| 2190 for (int j = 0; j < 2; ++j) |
| 2191 run.positions[j].set(j * 10, 0); |
| 2192 |
| 2193 for (size_t j = 0; j < 4; ++j) { |
| 2194 SCOPED_TRACE(base::StringPrintf("Case %" PRIuS ", char %" PRIuS, i, j)); |
| 2195 EXPECT_EQ(cases[i].bounds[j], run.GetGraphemeBounds(iter.get(), j)); |
| 2196 } |
| 2197 } |
| 2198 } |
2107 | 2199 |
2108 TEST_F(RenderTextTest, HarfBuzz_RunDirection) { | 2200 TEST_F(RenderTextTest, HarfBuzz_RunDirection) { |
2109 RenderTextHarfBuzz render_text; | 2201 RenderTextHarfBuzz render_text; |
2110 const base::string16 mixed = | 2202 const base::string16 mixed = |
2111 WideToUTF16(L"\x05D0\x05D1" L"1234" L"\x05D2\x05D3"); | 2203 WideToUTF16(L"\x05D0\x05D1" L"1234" L"\x05D2\x05D3"); |
2112 render_text.SetText(mixed); | 2204 render_text.SetText(mixed); |
2113 render_text.EnsureLayout(); | 2205 render_text.EnsureLayout(); |
2114 ASSERT_EQ(3U, render_text.runs_.size()); | 2206 ASSERT_EQ(3U, render_text.runs_.size()); |
2115 EXPECT_TRUE(render_text.runs_[0]->is_rtl); | 2207 EXPECT_TRUE(render_text.runs_[0]->is_rtl); |
2116 EXPECT_FALSE(render_text.runs_[1]->is_rtl); | 2208 EXPECT_FALSE(render_text.runs_[1]->is_rtl); |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2164 for (size_t i = 0; i < arraysize(kTestStrings); ++i) { | 2256 for (size_t i = 0; i < arraysize(kTestStrings); ++i) { |
2165 render_text->SetText(WideToUTF16(kTestStrings[i])); | 2257 render_text->SetText(WideToUTF16(kTestStrings[i])); |
2166 render_text->EnsureLayout(); | 2258 render_text->EnsureLayout(); |
2167 | 2259 |
2168 for (size_t j = 0; j < render_text->text().length(); ++j) | 2260 for (size_t j = 0; j < render_text->text().length(); ++j) |
2169 EXPECT_FALSE(render_text->GetGlyphBounds(j).is_empty()); | 2261 EXPECT_FALSE(render_text->GetGlyphBounds(j).is_empty()); |
2170 } | 2262 } |
2171 } | 2263 } |
2172 | 2264 |
2173 } // namespace gfx | 2265 } // namespace gfx |
OLD | NEW |