Index: third_party/WebKit/Source/platform/fonts/shaping/ShapingLineBreaker.cpp |
diff --git a/third_party/WebKit/Source/platform/fonts/shaping/ShapingLineBreaker.cpp b/third_party/WebKit/Source/platform/fonts/shaping/ShapingLineBreaker.cpp |
index 5206620abd964b923089b5c2b7b48b99b87f3495..dafe8f19e9a07cc0bdc11a84600fe359ea0e06c8 100644 |
--- a/third_party/WebKit/Source/platform/fonts/shaping/ShapingLineBreaker.cpp |
+++ b/third_party/WebKit/Source/platform/fonts/shaping/ShapingLineBreaker.cpp |
@@ -12,16 +12,15 @@ |
namespace blink { |
-ShapingLineBreaker::ShapingLineBreaker(const HarfBuzzShaper* shaper, |
- const Font* font, |
- const ShapeResult* result, |
- const AtomicString locale, |
- LineBreakType break_type) |
+ShapingLineBreaker::ShapingLineBreaker( |
+ const HarfBuzzShaper* shaper, |
+ const Font* font, |
+ const ShapeResult* result, |
+ const LazyLineBreakIterator* break_iterator) |
: shaper_(shaper), |
font_(font), |
result_(result), |
- locale_(locale), |
- break_type_(break_type) { |
+ break_iterator_(break_iterator) { |
text_ = String(shaper->GetText(), shaper->TextLength()); |
} |
@@ -51,6 +50,24 @@ unsigned NextSafeToBreakBefore(const UChar* text, |
return offset; |
} |
+// ShapingLineBreaker computes using visual positions. This function flips |
+// logical advance to visual, or vice versa. |
+LayoutUnit FlipRtl(LayoutUnit value, TextDirection direction) { |
+ return direction != TextDirection::kRtl ? value : -value; |
+} |
+ |
+// Snaps a visual position to the line start direction. |
+LayoutUnit SnapStart(float value, TextDirection direction) { |
+ return direction != TextDirection::kRtl ? LayoutUnit::FromFloatFloor(value) |
+ : LayoutUnit::FromFloatCeil(value); |
+} |
+ |
+// Snaps a visual position to the line end direction. |
+LayoutUnit SnapEnd(float value, TextDirection direction) { |
+ return direction != TextDirection::kRtl ? LayoutUnit::FromFloatCeil(value) |
+ : LayoutUnit::FromFloatFloor(value); |
+} |
+ |
} // namespace |
// Shapes a line of text by finding a valid and appropriate break opportunity |
@@ -86,9 +103,35 @@ PassRefPtr<ShapeResult> ShapingLineBreaker::ShapeLine( |
unsigned start, |
LayoutUnit available_space, |
unsigned* break_offset) { |
+ DCHECK_GE(available_space, LayoutUnit(0)); |
+ unsigned range_start = result_->StartIndexForResult(); |
+ unsigned range_end = result_->EndIndexForResult(); |
+ DCHECK_GE(start, range_start); |
+ DCHECK_LT(start, range_end); |
+ |
// The start position in the original shape results. |
- LayoutUnit start_position = result_->SnappedStartPositionForOffset(start); |
+ float start_position_float = result_->PositionForOffset(start - range_start); |
TextDirection direction = result_->Direction(); |
+ LayoutUnit start_position = SnapStart(start_position_float, direction); |
+ |
+ // Find a candidate break opportunity by identifying the last offset before |
+ // exceeding the available space and the determine the closest valid break |
+ // preceding the candidate. |
+ LayoutUnit end_position = SnapEnd(start_position_float, direction) + |
+ FlipRtl(available_space, direction); |
+ DCHECK_GE(FlipRtl(end_position - start_position, direction), LayoutUnit(0)); |
+ unsigned candidate_break = |
+ result_->OffsetForPosition(end_position, false) + range_start; |
+ // candidate_break should be >= start, but rounding errors can chime in when |
+ // comparing floats. See ShapeLineZeroAvailableWidth on Linux/Mac. |
+ candidate_break = std::max(candidate_break, start); |
+ unsigned break_opportunity = |
+ break_iterator_->PreviousBreakOpportunity(candidate_break, start); |
+ if (break_opportunity <= start) { |
+ break_opportunity = break_iterator_->NextBreakOpportunity( |
+ std::max(candidate_break, start + 1)); |
+ } |
+ DCHECK_GT(break_opportunity, start); |
// If the start offset is not at a safe-to-break boundary the content between |
// the start and the next safe-to-break boundary needs to be reshaped and the |
@@ -96,25 +139,19 @@ PassRefPtr<ShapeResult> ShapingLineBreaker::ShapeLine( |
RefPtr<ShapeResult> line_start_result; |
unsigned first_safe = |
NextSafeToBreakBefore(shaper_->GetText(), shaper_->TextLength(), start); |
- if (first_safe != start) { |
+ DCHECK_GE(first_safe, start); |
+ // Reshape takes place only when first_safe is before the break opportunity. |
+ // Otherwise reshape will be part of line_end_result. |
+ if (first_safe != start && first_safe < break_opportunity) { |
LayoutUnit original_width = |
- result_->SnappedEndPositionForOffset(first_safe) - start_position; |
+ FlipRtl(SnapEnd(result_->PositionForOffset(first_safe - range_start), |
+ direction) - |
+ start_position, |
+ direction); |
line_start_result = shaper_->Shape(font_, direction, start, first_safe); |
available_space += line_start_result->SnappedWidth() - original_width; |
} |
- // Find a candidate break opportunity by identifying the last offset before |
- // exceeding the available space and the determine the closest valid break |
- // preceding the candidate. |
- LazyLineBreakIterator break_iterator(text_, locale_, break_type_); |
- LayoutUnit end_position = start_position + available_space; |
- unsigned candidate_break = result_->OffsetForPosition(end_position, false); |
- unsigned break_opportunity = |
- break_iterator.PreviousBreakOpportunity(candidate_break, start); |
- if (break_opportunity <= start) { |
- break_opportunity = break_iterator.NextBreakOpportunity(candidate_break); |
- } |
- |
RefPtr<ShapeResult> line_end_result; |
unsigned last_safe = break_opportunity; |
while (break_opportunity > start) { |
@@ -125,17 +162,26 @@ PassRefPtr<ShapeResult> ShapingLineBreaker::ShapeLine( |
unsigned previous_safe = std::max( |
PreviousSafeToBreakAfter(shaper_->GetText(), start, break_opportunity), |
start); |
+ DCHECK_LE(previous_safe, break_opportunity); |
if (previous_safe != break_opportunity) { |
- LayoutUnit safe_position = |
- result_->SnappedStartPositionForOffset(previous_safe); |
- while (break_opportunity > previous_safe && previous_safe > start) { |
+ LayoutUnit safe_position = SnapStart( |
+ result_->PositionForOffset(previous_safe - range_start), direction); |
+ while (break_opportunity > previous_safe && previous_safe >= start) { |
line_end_result = |
- shaper_->Shape(font_, direction, previous_safe, break_opportunity); |
- if (safe_position + line_end_result->SnappedWidth() <= end_position) |
+ shaper_->Shape(font_, direction, previous_safe, |
+ std::min(break_opportunity, range_end)); |
+ if (line_end_result->SnappedWidth() <= |
+ FlipRtl(end_position - safe_position, direction)) |
break; |
+ // Doesn't fit after the reshape. Try previous break opportunity, or |
+ // overflow if there were none. |
+ unsigned previous_break_opportunity = |
+ break_iterator_->PreviousBreakOpportunity(break_opportunity - 1, |
+ start); |
+ if (previous_break_opportunity <= start) |
+ break; |
+ break_opportunity = previous_break_opportunity; |
line_end_result = nullptr; |
- break_opportunity = break_iterator.PreviousBreakOpportunity( |
- break_opportunity - 1, start); |
} |
} |
@@ -146,7 +192,7 @@ PassRefPtr<ShapeResult> ShapingLineBreaker::ShapeLine( |
// No suitable break opportunity, not exceeding the available space, |
// found. Choose the next valid one even though it will overflow. |
- break_opportunity = break_iterator.NextBreakOpportunity(candidate_break); |
+ break_opportunity = break_iterator_->NextBreakOpportunity(candidate_break); |
} |
// Create shape results for the line by copying from the re-shaped result (if |
@@ -160,6 +206,10 @@ PassRefPtr<ShapeResult> ShapingLineBreaker::ShapeLine( |
if (line_end_result) |
line_end_result->CopyRange(last_safe, max_length, line_result.Get()); |
+ DCHECK_GT(break_opportunity, start); |
+ DCHECK_EQ(std::min(break_opportunity, range_end) - start, |
+ line_result->NumCharacters()); |
+ |
*break_offset = break_opportunity; |
return line_result.Release(); |
} |