Commit 2772f497 authored by mukai's avatar mukai Committed by Commit bot

Add multiline support to RenderTextHarfBuzz.

The implementation of LineBreaker is mostly a straightforward
porting of RenderTextWin's implementation.

BUG=248597
R=ckocagil@chromium.org, msw@chromium.org
TEST=manually, customized ash_shell

Review URL: https://codereview.chromium.org/882643005

Cr-Commit-Position: refs/heads/master@{#315010}
parent b045fc23
......@@ -580,6 +580,7 @@ class GFX_EXPORT RenderText {
FRIEND_TEST_ALL_PREFIXES(RenderTextTest, Multiline_NormalWidth);
FRIEND_TEST_ALL_PREFIXES(RenderTextTest, Multiline_SufficientWidth);
FRIEND_TEST_ALL_PREFIXES(RenderTextTest, Multiline_Newline);
FRIEND_TEST_ALL_PREFIXES(RenderTextTest, NewlineWithoutMultilineFlag);
FRIEND_TEST_ALL_PREFIXES(RenderTextTest, GlyphBounds);
FRIEND_TEST_ALL_PREFIXES(RenderTextTest, HarfBuzz_GlyphBounds);
FRIEND_TEST_ALL_PREFIXES(RenderTextTest,
......
This diff is collapsed.
......@@ -113,6 +113,7 @@ class GFX_EXPORT RenderTextHarfBuzz : public RenderText {
private:
friend class RenderTextTest;
FRIEND_TEST_ALL_PREFIXES(RenderTextTest, Multiline_NormalWidth);
FRIEND_TEST_ALL_PREFIXES(RenderTextTest, HarfBuzz_RunDirection);
FRIEND_TEST_ALL_PREFIXES(RenderTextTest, HarfBuzz_BreakRunsByUnicodeBlocks);
FRIEND_TEST_ALL_PREFIXES(RenderTextTest, HarfBuzz_BreakRunsByEmoji);
......@@ -121,6 +122,13 @@ class GFX_EXPORT RenderTextHarfBuzz : public RenderText {
FRIEND_TEST_ALL_PREFIXES(RenderTextTest, HarfBuzz_NonExistentFont);
FRIEND_TEST_ALL_PREFIXES(RenderTextTest, HarfBuzz_UniscribeFallback);
// Specify the width of a glyph for test. The width of glyphs is very
// platform-dependent and environment-dependent. Otherwise multiline test
// will become really flaky.
void set_glyph_width_for_test(uint8 test_width) {
glyph_width_for_test_ = test_width;
}
// Return the run index that contains the argument; or the length of the
// |runs_| vector if argument exceeds the text length or width.
size_t GetRunContainingCaret(const SelectionModel& caret) const;
......@@ -168,6 +176,12 @@ class GFX_EXPORT RenderTextHarfBuzz : public RenderText {
// be NULL in case of an error.
scoped_ptr<base::i18n::BreakIterator> grapheme_iterator_;
// The total size of the layouted text.
SizeF total_size_;
// Fixed width of glyphs. This should only be set in test environments.
uint8 glyph_width_for_test_;
DISALLOW_COPY_AND_ASSIGN(RenderTextHarfBuzz);
};
......
......@@ -80,6 +80,16 @@ void RunMoveCursorLeftRightTest(RenderText* render_text,
}
#endif // !defined(OS_MACOSX)
// Test utility for Multiline_Newline test case. Empty |expected_range| means
// the blank line which has no segments. Otherwise |segments| should contain
// exactly one line segment whose range equals to |expected_range|.
void VerifyLineSegments(const Range& expected_range,
const std::vector<internal::LineSegment>& segments) {
EXPECT_EQ(expected_range.is_empty() ? 0ul : 1ul, segments.size());
if (!expected_range.is_empty())
EXPECT_EQ(expected_range, segments[0].char_range);
}
} // namespace
class RenderTextTest : public testing::Test {
......@@ -1863,121 +1873,129 @@ TEST_F(RenderTextTest, SelectionKeepsLigatures) {
}
}
// TODO(ckocagil): Enable for RenderTextHarfBuzz after implementing multiline.
// Ensure strings wrap onto multiple lines for a small available width.
TEST_F(RenderTextTest, DISABLED_Multiline_MinWidth) {
TEST_F(RenderTextTest, Multiline_MinWidth) {
const wchar_t* kTestStrings[] = { kWeak, kLtr, kLtrRtl, kLtrRtlLtr, kRtl,
kRtlLtr, kRtlLtrRtl };
scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
render_text->SetDisplayRect(Rect(1, 1000));
render_text->SetMultiline(true);
RenderTextHarfBuzz render_text;
render_text.SetDisplayRect(Rect(1, 1000));
render_text.SetMultiline(true);
Canvas canvas;
for (size_t i = 0; i < arraysize(kTestStrings); ++i) {
SCOPED_TRACE(base::StringPrintf("kTestStrings[%" PRIuS "]", i));
render_text->SetText(WideToUTF16(kTestStrings[i]));
render_text->Draw(&canvas);
EXPECT_GT(render_text->lines_.size(), 1U);
render_text.SetText(WideToUTF16(kTestStrings[i]));
render_text.Draw(&canvas);
EXPECT_GT(render_text.lines_.size(), 1U);
}
}
// TODO(ckocagil): Enable for RenderTextHarfBuzz after implementing multiline.
// Ensure strings wrap onto multiple lines for a normal available width.
TEST_F(RenderTextTest, DISABLED_Multiline_NormalWidth) {
TEST_F(RenderTextTest, Multiline_NormalWidth) {
const struct {
const wchar_t* const text;
const Range first_line_char_range;
const Range second_line_char_range;
} kTestStrings[] = {
{ L"abc defg hijkl", Range(0, 9), Range(9, 14) },
{ L"qwertyzxcvbn", Range(0, 8), Range(8, 12) },
{ L"\x062A\x0641\x0627\x062D\x05EA\x05E4\x05D5\x05D6\x05D9\x05DD",
Range(4, 10), Range(0, 4) }
{ L"qwertyzxcvbn", Range(0, 10), Range(10, 12) },
{ L"\x062A\x0641\x0627\x062D\x05EA\x05E4\x05D5\x05D6\x05D9"
L"\x05DA\x05DB\x05DD", Range(4, 12), Range(0, 4) }
};
scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
render_text->SetDisplayRect(Rect(50, 1000));
render_text->SetMultiline(true);
RenderTextHarfBuzz render_text;
// Specify the fixed width for characters to suppress the possible variations
// of linebreak results.
render_text.set_glyph_width_for_test(5);
render_text.SetDisplayRect(Rect(50, 1000));
render_text.SetMultiline(true);
Canvas canvas;
for (size_t i = 0; i < arraysize(kTestStrings); ++i) {
SCOPED_TRACE(base::StringPrintf("kTestStrings[%" PRIuS "]", i));
render_text->SetText(WideToUTF16(kTestStrings[i].text));
render_text->Draw(&canvas);
ASSERT_EQ(2U, render_text->lines_.size());
ASSERT_EQ(1U, render_text->lines_[0].segments.size());
render_text.SetText(WideToUTF16(kTestStrings[i].text));
render_text.Draw(&canvas);
ASSERT_EQ(2U, render_text.lines_.size());
ASSERT_EQ(1U, render_text.lines_[0].segments.size());
EXPECT_EQ(kTestStrings[i].first_line_char_range,
render_text->lines_[0].segments[0].char_range);
ASSERT_EQ(1U, render_text->lines_[1].segments.size());
render_text.lines_[0].segments[0].char_range);
ASSERT_EQ(1U, render_text.lines_[1].segments.size());
EXPECT_EQ(kTestStrings[i].second_line_char_range,
render_text->lines_[1].segments[0].char_range);
render_text.lines_[1].segments[0].char_range);
}
}
// TODO(ckocagil): Enable for RenderTextHarfBuzz after implementing multiline.
// Ensure strings don't wrap onto multiple lines for a sufficient available
// width.
TEST_F(RenderTextTest, DISABLED_Multiline_SufficientWidth) {
TEST_F(RenderTextTest, Multiline_SufficientWidth) {
const wchar_t* kTestStrings[] = { L"", L" ", L".", L" . ", L"abc", L"a b c",
L"\x62E\x628\x632", L"\x62E \x628 \x632" };
scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
render_text->SetDisplayRect(Rect(30, 1000));
render_text->SetMultiline(true);
RenderTextHarfBuzz render_text;
render_text.SetDisplayRect(Rect(1000, 1000));
render_text.SetMultiline(true);
Canvas canvas;
for (size_t i = 0; i < arraysize(kTestStrings); ++i) {
SCOPED_TRACE(base::StringPrintf("kTestStrings[%" PRIuS "]", i));
render_text->SetText(WideToUTF16(kTestStrings[i]));
render_text->Draw(&canvas);
EXPECT_EQ(1U, render_text->lines_.size());
render_text.SetText(WideToUTF16(kTestStrings[i]));
render_text.Draw(&canvas);
EXPECT_EQ(1U, render_text.lines_.size());
}
}
// TODO(ckocagil): Enable for RenderTextHarfBuzz after implementing multiline.
TEST_F(RenderTextTest, DISABLED_Multiline_Newline) {
TEST_F(RenderTextTest, Multiline_Newline) {
const struct {
const wchar_t* const text;
const size_t lines_count;
// Ranges of the characters on each line preceding the newline.
const Range first_line_char_range;
const Range second_line_char_range;
const Range line_char_ranges[3];
} kTestStrings[] = {
{ L"abc\ndef", Range(0, 3), Range(4, 7) },
{ L"a \n b ", Range(0, 2), Range(3, 6) },
{ L"\n" , Range::InvalidRange(), Range::InvalidRange() }
{L"abc\ndef", 2ul, { Range(0, 3), Range(4, 7), Range::InvalidRange() } },
{L"a \n b ", 2ul, { Range(0, 2), Range(3, 6), Range::InvalidRange() } },
{L"ab\n", 2ul, { Range(0, 2), Range(), Range::InvalidRange() } },
{L"a\n\nb", 3ul, { Range(0, 1), Range(), Range(3, 4) } },
{L"\nab", 2ul, { Range(), Range(1, 3), Range::InvalidRange() } },
{L"\n", 2ul, { Range(), Range(), Range::InvalidRange() } },
};
scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
render_text->SetDisplayRect(Rect(200, 1000));
render_text->SetMultiline(true);
RenderTextHarfBuzz render_text;
render_text.SetDisplayRect(Rect(200, 1000));
render_text.SetMultiline(true);
Canvas canvas;
for (size_t i = 0; i < arraysize(kTestStrings); ++i) {
SCOPED_TRACE(base::StringPrintf("kTestStrings[%" PRIuS "]", i));
render_text->SetText(WideToUTF16(kTestStrings[i].text));
render_text->Draw(&canvas);
render_text.SetText(WideToUTF16(kTestStrings[i].text));
render_text.Draw(&canvas);
EXPECT_EQ(kTestStrings[i].lines_count, render_text.lines_.size());
if (kTestStrings[i].lines_count != render_text.lines_.size())
continue;
for (size_t j = 0; j < kTestStrings[i].lines_count; ++j) {
SCOPED_TRACE(base::StringPrintf("Line %" PRIuS "", j));
VerifyLineSegments(kTestStrings[i].line_char_ranges[j],
render_text.lines_[j].segments);
}
}
}
TEST_F(RenderTextTest, NewlineWithoutMultilineFlag) {
const wchar_t* kTestStrings[] = {
L"abc\ndef", L"a \n b ", L"ab\n", L"a\n\nb", L"\nab", L"\n",
};
RenderTextHarfBuzz render_text;
render_text.SetDisplayRect(Rect(200, 1000));
Canvas canvas;
for (size_t i = 0; i < arraysize(kTestStrings); ++i) {
SCOPED_TRACE(base::StringPrintf("kTestStrings[%" PRIuS "]", i));
render_text.SetText(WideToUTF16(kTestStrings[i]));
render_text.Draw(&canvas);
ASSERT_EQ(2U, render_text->lines_.size());
const Range first_expected_range = kTestStrings[i].first_line_char_range;
ASSERT_EQ(first_expected_range.IsValid() ? 2U : 1U,
render_text->lines_[0].segments.size());
if (first_expected_range.IsValid())
EXPECT_EQ(first_expected_range,
render_text->lines_[0].segments[0].char_range);
const internal::LineSegment& newline_segment =
render_text->lines_[0].segments[first_expected_range.IsValid() ? 1 : 0];
ASSERT_EQ(1U, newline_segment.char_range.length());
EXPECT_EQ(L'\n', kTestStrings[i].text[newline_segment.char_range.start()]);
const Range second_expected_range = kTestStrings[i].second_line_char_range;
ASSERT_EQ(second_expected_range.IsValid() ? 1U : 0U,
render_text->lines_[1].segments.size());
if (second_expected_range.IsValid())
EXPECT_EQ(second_expected_range,
render_text->lines_[1].segments[0].char_range);
EXPECT_EQ(1U, render_text.lines_.size());
}
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment