Index: ui/gfx/text_elider.cc |
diff --git a/ui/gfx/text_elider.cc b/ui/gfx/text_elider.cc |
index c58a96d9748b4c357e29a07ef01a97cd6f1a3ac9..5b9469614f22f8080a5dee020af64a4058264669 100644 |
--- a/ui/gfx/text_elider.cc |
+++ b/ui/gfx/text_elider.cc |
@@ -39,6 +39,68 @@ |
namespace { |
+// Helper class to split + elide text, while respecting UTF16 surrogate pairs. |
+class StringSlicer { |
+ public: |
+ StringSlicer(const base::string16& text, |
+ const base::string16& ellipsis, |
+ bool elide_in_middle) |
+ : text_(text), |
+ ellipsis_(ellipsis), |
+ elide_in_middle_(elide_in_middle) { |
+ } |
+ |
+ // Cuts |text_| to be |length| characters long. If |elide_in_middle_| is true, |
+ // the middle of the string is removed to leave equal-length pieces from the |
+ // beginning and end of the string; otherwise, the end of the string is |
+ // removed and only the beginning remains. If |insert_ellipsis| is true, |
+ // then an ellipsis character will be inserted at the cut point. |
+ base::string16 CutString(size_t length, bool insert_ellipsis) { |
+ const base::string16 ellipsis_text = insert_ellipsis ? ellipsis_ |
+ : base::string16(); |
+ |
+ if (!elide_in_middle_) |
+ return text_.substr(0, FindValidBoundaryBefore(length)) + ellipsis_text; |
+ |
+ // We put the extra character, if any, before the cut. |
+ const size_t half_length = length / 2; |
+ const size_t prefix_length = FindValidBoundaryBefore(length - half_length); |
+ const size_t suffix_start_guess = text_.length() - half_length; |
+ const size_t suffix_start = FindValidBoundaryAfter(suffix_start_guess); |
+ const size_t suffix_length = |
+ half_length - (suffix_start_guess - suffix_start); |
+ return text_.substr(0, prefix_length) + ellipsis_text + |
+ text_.substr(suffix_start, suffix_length); |
+ } |
+ |
+ private: |
+ // Returns a valid cut boundary at or before |index|. |
+ size_t FindValidBoundaryBefore(size_t index) const { |
+ DCHECK_LE(index, text_.length()); |
+ if (index != text_.length()) |
+ U16_SET_CP_START(text_.data(), 0, index); |
+ return index; |
+ } |
+ |
+ // Returns a valid cut boundary at or after |index|. |
+ size_t FindValidBoundaryAfter(size_t index) const { |
+ DCHECK_LE(index, text_.length()); |
+ if (index != text_.length()) |
+ U16_SET_CP_LIMIT(text_.data(), 0, index, text_.length()); |
+ return index; |
+ } |
+ |
+ // The text to be sliced. |
+ const base::string16& text_; |
+ |
+ // Ellipsis string to use. |
+ const base::string16& ellipsis_; |
+ |
+ // If true, the middle of the string will be elided. |
+ bool elide_in_middle_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(StringSlicer); |
+}; |
// Build a path from the first |num_components| elements in |path_elements|. |
// Prepends |path_prefix|, appends |filename|, inserts ellipsis if appropriate. |
@@ -87,46 +149,6 @@ |
} |
} // namespace |
- |
-StringSlicer::StringSlicer(const base::string16& text, |
- const base::string16& ellipsis, |
- bool elide_in_middle) |
- : text_(text), |
- ellipsis_(ellipsis), |
- elide_in_middle_(elide_in_middle) { |
-} |
- |
-base::string16 StringSlicer::CutString(size_t length, bool insert_ellipsis) { |
- const base::string16 ellipsis_text = insert_ellipsis ? ellipsis_ |
- : base::string16(); |
- |
- if (!elide_in_middle_) |
- return text_.substr(0, FindValidBoundaryBefore(length)) + ellipsis_text; |
- |
- // We put the extra character, if any, before the cut. |
- const size_t half_length = length / 2; |
- const size_t prefix_length = FindValidBoundaryBefore(length - half_length); |
- const size_t suffix_start_guess = text_.length() - half_length; |
- const size_t suffix_start = FindValidBoundaryAfter(suffix_start_guess); |
- const size_t suffix_length = |
- half_length - (suffix_start_guess - suffix_start); |
- return text_.substr(0, prefix_length) + ellipsis_text + |
- text_.substr(suffix_start, suffix_length); |
-} |
- |
-size_t StringSlicer::FindValidBoundaryBefore(size_t index) const { |
- DCHECK_LE(index, text_.length()); |
- if (index != text_.length()) |
- U16_SET_CP_START(text_.data(), 0, index); |
- return index; |
-} |
- |
-size_t StringSlicer::FindValidBoundaryAfter(size_t index) const { |
- DCHECK_LE(index, text_.length()); |
- if (index != text_.length()) |
- U16_SET_CP_LIMIT(text_.data(), 0, index, text_.length()); |
- return index; |
-} |
base::string16 ElideEmail(const base::string16& email, |
const FontList& font_list, |
@@ -455,8 +477,7 @@ |
// (eliding way too much from a ridiculous string is probably still |
// ridiculous), but we should check other widths for bogus values as well. |
if (current_text_pixel_width <= 0 && !text.empty()) { |
- const base::string16 cut = |
- slicer.CutString(text.length() / 2, insert_ellipsis); |
+ const base::string16 cut = slicer.CutString(text.length() / 2, false); |
return ElideText(cut, font_list, available_pixel_width, elide_behavior); |
} |
@@ -472,23 +493,20 @@ |
size_t hi = text.length() - 1; |
size_t guess; |
for (guess = (lo + hi) / 2; lo <= hi; guess = (lo + hi) / 2) { |
- // We check the width of the whole desired string at once to ensure we |
+ // We check the length of the whole desired string at once to ensure we |
// handle kerning/ligatures/etc. correctly. |
- // TODO(skanuj) : Handle directionality of ellipsis based on adjacent |
- // characters. See crbug.com/327963. |
const base::string16 cut = slicer.CutString(guess, insert_ellipsis); |
- const float guess_width = GetStringWidthF(cut, font_list); |
- if (guess_width == available_pixel_width) |
- break; |
- if (guess_width > available_pixel_width) { |
+ const float guess_length = GetStringWidthF(cut, font_list); |
+ // Check again that we didn't hit a Pango width overflow. If so, cut the |
+ // current string in half and start over. |
+ if (guess_length <= 0) { |
+ return ElideText(slicer.CutString(guess / 2, false), |
+ font_list, available_pixel_width, elide_behavior); |
+ } |
+ if (guess_length > available_pixel_width) |
hi = guess - 1; |
- // Move back if we are on loop terminating condition, and guess is wider |
- // than available. |
- if (hi < lo) |
- lo = hi; |
- } else { |
+ else |
lo = guess + 1; |
- } |
} |
return slicer.CutString(guess, insert_ellipsis); |