Commit 06a2742e authored by ckocagil@chromium.org's avatar ckocagil@chromium.org

RenderTextHarfBuzz: Decide run direction by BiDi embedding level

BUG=382178
NOTRY=true
R=msw

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

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@276379 0039d316-1c4b-4281-b951-d872f2087c98
parent b62dcb86
...@@ -863,12 +863,17 @@ void RenderTextHarfBuzz::ItemizeText() { ...@@ -863,12 +863,17 @@ void RenderTextHarfBuzz::ItemizeText() {
const bool is_text_rtl = GetTextDirection() == base::i18n::RIGHT_TO_LEFT; const bool is_text_rtl = GetTextDirection() == base::i18n::RIGHT_TO_LEFT;
DCHECK_NE(0U, text.length()); DCHECK_NE(0U, text.length());
// If ICU fails to itemize the text, we set |fake_runs| and create a run that // If ICU fails to itemize the text, we create a run that spans the entire
// spans the entire text. This is needed because early returning and leaving // text. This is needed because leaving the runs set empty causes some clients
// the runs set empty causes some clients to crash/misbehave since they expect // to misbehave since they expect non-zero text metrics from a non-empty text.
// non-zero text metrics from a non-empty text.
base::i18n::BiDiLineIterator bidi_iterator; base::i18n::BiDiLineIterator bidi_iterator;
bool fake_runs = !bidi_iterator.Open(text, is_text_rtl, false); if (!bidi_iterator.Open(text, is_text_rtl, false)) {
internal::TextRunHarfBuzz* run = new internal::TextRunHarfBuzz;
run->range = Range(0, text.length());
runs_.push_back(run);
visual_to_logical_ = logical_to_visual_ = std::vector<int32_t>(1, 0);
return;
}
// Temporarily apply composition underlines and selection colors. // Temporarily apply composition underlines and selection colors.
ApplyCompositionAndSelectionStyles(); ApplyCompositionAndSelectionStyles();
...@@ -888,36 +893,34 @@ void RenderTextHarfBuzz::ItemizeText() { ...@@ -888,36 +893,34 @@ void RenderTextHarfBuzz::ItemizeText() {
run->diagonal_strike = style.style(DIAGONAL_STRIKE); run->diagonal_strike = style.style(DIAGONAL_STRIKE);
run->underline = style.style(UNDERLINE); run->underline = style.style(UNDERLINE);
if (fake_runs) { int32 script_item_break = 0;
run_break = text.length(); bidi_iterator.GetLogicalRun(run_break, &script_item_break, &run->level);
} else { // Odd BiDi embedding levels correspond to RTL runs.
int32 script_item_break = 0; run->is_rtl = (run->level % 2) == 1;
bidi_iterator.GetLogicalRun(run_break, &script_item_break, &run->level); // Find the length and script of this script run.
// Find the length and script of this script run. script_item_break = ScriptInterval(text, run_break,
script_item_break = ScriptInterval(text, run_break, script_item_break - run_break, &run->script) + run_break;
script_item_break - run_break, &run->script) + run_break;
// Find the next break and advance the iterators as needed.
// Find the next break and advance the iterators as needed. run_break = std::min(static_cast<size_t>(script_item_break),
run_break = std::min(static_cast<size_t>(script_item_break), TextIndexToLayoutIndex(style.GetRange().end()));
TextIndexToLayoutIndex(style.GetRange().end()));
// Break runs adjacent to character substrings in certain code blocks.
// Break runs adjacent to character substrings in certain code blocks. // This avoids using their fallback fonts for more characters than needed,
// This avoids using their fallback fonts for more characters than needed, // in cases like "\x25B6 Media Title", etc. http://crbug.com/278913
// in cases like "\x25B6 Media Title", etc. http://crbug.com/278913 if (run_break > run->range.start()) {
if (run_break > run->range.start()) { const size_t run_start = run->range.start();
const size_t run_start = run->range.start(); const int32 run_length = static_cast<int32>(run_break - run_start);
const int32 run_length = static_cast<int32>(run_break - run_start); base::i18n::UTF16CharIterator iter(text.c_str() + run_start,
base::i18n::UTF16CharIterator iter(text.c_str() + run_start, run_length);
run_length); const UBlockCode first_block_code = ublock_getCode(iter.get());
const UBlockCode first_block_code = ublock_getCode(iter.get()); const bool first_block_unusual = IsUnusualBlockCode(first_block_code);
const bool first_block_unusual = IsUnusualBlockCode(first_block_code); while (iter.Advance() && iter.array_pos() < run_length) {
while (iter.Advance() && iter.array_pos() < run_length) { const UBlockCode current_block_code = ublock_getCode(iter.get());
const UBlockCode current_block_code = ublock_getCode(iter.get()); if (current_block_code != first_block_code &&
if (current_block_code != first_block_code && (first_block_unusual || IsUnusualBlockCode(current_block_code))) {
(first_block_unusual || IsUnusualBlockCode(current_block_code))) { run_break = run_start + iter.array_pos();
run_break = run_start + iter.array_pos(); break;
break;
}
} }
} }
} }
...@@ -925,12 +928,7 @@ void RenderTextHarfBuzz::ItemizeText() { ...@@ -925,12 +928,7 @@ void RenderTextHarfBuzz::ItemizeText() {
DCHECK(IsValidCodePointIndex(text, run_break)); DCHECK(IsValidCodePointIndex(text, run_break));
style.UpdatePosition(LayoutIndexToTextIndex(run_break)); style.UpdatePosition(LayoutIndexToTextIndex(run_break));
run->range.set_end(run_break); run->range.set_end(run_break);
UBiDiDirection direction = ubidi_getBaseDirection(
text.c_str() + run->range.start(), run->range.length());
if (direction == UBIDI_NEUTRAL)
run->is_rtl = is_text_rtl;
else
run->is_rtl = direction == UBIDI_RTL;
runs_.push_back(run); runs_.push_back(run);
} }
......
...@@ -61,7 +61,7 @@ struct GFX_EXPORT TextRunHarfBuzz { ...@@ -61,7 +61,7 @@ struct GFX_EXPORT TextRunHarfBuzz {
} // namespace internal } // namespace internal
class RenderTextHarfBuzz : public RenderText { class GFX_EXPORT RenderTextHarfBuzz : public RenderText {
public: public:
RenderTextHarfBuzz(); RenderTextHarfBuzz();
virtual ~RenderTextHarfBuzz(); virtual ~RenderTextHarfBuzz();
...@@ -90,6 +90,9 @@ class RenderTextHarfBuzz : public RenderText { ...@@ -90,6 +90,9 @@ class RenderTextHarfBuzz : public RenderText {
virtual void DrawVisualText(Canvas* canvas) OVERRIDE; virtual void DrawVisualText(Canvas* canvas) OVERRIDE;
private: private:
friend class RenderTextTest;
FRIEND_TEST_ALL_PREFIXES(RenderTextTest, HarfBuzz_RunDirection);
// Return the run index that contains the argument; or the length of the // Return the run index that contains the argument; or the length of the
// |runs_| vector if argument exceeds the text length or width. // |runs_| vector if argument exceeds the text length or width.
size_t GetRunContainingCaret(const SelectionModel& caret) const; size_t GetRunContainingCaret(const SelectionModel& caret) const;
......
...@@ -1977,4 +1977,16 @@ TEST_F(RenderTextTest, HarfBuzz_CharToGlyph) { ...@@ -1977,4 +1977,16 @@ TEST_F(RenderTextTest, HarfBuzz_CharToGlyph) {
} }
TEST_F(RenderTextTest, HarfBuzz_RunDirection) {
RenderTextHarfBuzz render_text;
const base::string16 mixed =
WideToUTF16(L"\x05D0\x05D1" L"1234" L"\x05D2\x05D3");
render_text.SetText(mixed);
render_text.EnsureLayout();
ASSERT_EQ(3U, render_text.runs_.size());
EXPECT_TRUE(render_text.runs_[0]->is_rtl);
EXPECT_FALSE(render_text.runs_[1]->is_rtl);
EXPECT_TRUE(render_text.runs_[2]->is_rtl);
}
} // namespace gfx } // namespace gfx
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