15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// This file implements utility functions for eliding and formatting UI text.
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Note that several of the functions declared in text_elider.h are implemented
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// in this file using helper classes in an unnamed namespace.
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)#include "ui/gfx/text_elider.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <string>
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <vector>
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/files/file_path.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/i18n/break_iterator.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/i18n/char_iterator.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/i18n/rtl.h"
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/memory/scoped_ptr.h"
202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/strings/string_split.h"
217d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)#include "base/strings/string_util.h"
22c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "base/strings/sys_string_conversions.h"
23868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/utf_string_conversions.h"
24ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch#include "third_party/icu/source/common/unicode/rbbi.h"
25ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch#include "third_party/icu/source/common/unicode/uloc.h"
263551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)#include "ui/gfx/font_list.h"
27116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#include "ui/gfx/render_text.h"
283551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)#include "ui/gfx/text_utils.h"
295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)using base::ASCIIToUTF16;
315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)using base::UTF8ToUTF16;
325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)using base::WideToUTF16;
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)namespace gfx {
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)namespace {
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
38116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#if defined(OS_ANDROID) || defined(OS_IOS)
39116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch// The returned string will have at least one character besides the ellipsis
40116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch// on either side of '@'; if that's impossible, a single ellipsis is returned.
41116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch// If possible, only the username is elided. Otherwise, the domain is elided
42116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch// in the middle, splitting available width equally with the elided username.
43116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch// If the username is short enough that it doesn't need half the available
44116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch// width, the elided domain will occupy that extra width.
45a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)base::string16 ElideEmail(const base::string16& email,
46a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                          const FontList& font_list,
47a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                          float available_pixel_width) {
484e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  if (GetStringWidthF(email, font_list) <= available_pixel_width)
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return email;
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Split the email into its local-part (username) and domain-part. The email
52116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  // spec allows for @ symbols in the username under some special requirements,
53116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  // but not in the domain part, so splitting at the last @ symbol is safe.
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const size_t split_index = email.find_last_of('@');
55a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  DCHECK_NE(split_index, base::string16::npos);
56a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  base::string16 username = email.substr(0, split_index);
57a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  base::string16 domain = email.substr(split_index + 1);
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!username.empty());
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!domain.empty());
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Subtract the @ symbol from the available width as it is mandatory.
62a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  const base::string16 kAtSignUTF16 = ASCIIToUTF16("@");
634e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  available_pixel_width -= GetStringWidthF(kAtSignUTF16, font_list);
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Check whether eliding the domain is necessary: if eliding the username
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // is sufficient, the domain will not be elided.
674e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  const float full_username_width = GetStringWidthF(username, font_list);
684e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  const float available_domain_width =
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      available_pixel_width -
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      std::min(full_username_width,
714e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)               GetStringWidthF(username.substr(0, 1) + kEllipsisUTF16,
724e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                               font_list));
734e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  if (GetStringWidthF(domain, font_list) > available_domain_width) {
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Elide the domain so that it only takes half of the available width.
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Should the username not need all the width available in its half, the
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // domain will occupy the leftover width.
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // If |desired_domain_width| is greater than |available_domain_width|: the
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // minimal username elision allowed by the specifications will not fit; thus
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // |desired_domain_width| must be <= |available_domain_width| at all cost.
804e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    const float desired_domain_width =
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        std::min(available_domain_width,
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 std::max(available_pixel_width - full_username_width,
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          available_pixel_width / 2));
8446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    domain = ElideText(domain, font_list, desired_domain_width, ELIDE_MIDDLE);
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Failing to elide the domain such that at least one character remains
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // (other than the ellipsis itself) remains: return a single ellipsis.
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (domain.length() <= 1U)
88a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      return base::string16(kEllipsisUTF16);
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Fit the username in the remaining width (at this point the elided username
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // is guaranteed to fit with at least one character remaining given all the
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // precautions taken earlier).
944e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  available_pixel_width -= GetStringWidthF(domain, font_list);
9546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  username = ElideText(username, font_list, available_pixel_width, ELIDE_TAIL);
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return username + kAtSignUTF16 + domain;
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
98116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#endif
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)}  // namespace
10146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)
10246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)// U+2026 in utf8
10346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)const char kEllipsis[] = "\xE2\x80\xA6";
10446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)const base::char16 kEllipsisUTF16[] = { 0x2026, 0 };
10546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)const base::char16 kForwardSlash = '/';
10646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)
10746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)StringSlicer::StringSlicer(const base::string16& text,
10846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)                           const base::string16& ellipsis,
10946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)                           bool elide_in_middle,
11046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)                           bool elide_at_beginning)
11146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    : text_(text),
11246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)      ellipsis_(ellipsis),
11346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)      elide_in_middle_(elide_in_middle),
11446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)      elide_at_beginning_(elide_at_beginning) {
11546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)}
11646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)
11746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)base::string16 StringSlicer::CutString(size_t length, bool insert_ellipsis) {
11846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  const base::string16 ellipsis_text = insert_ellipsis ? ellipsis_
11946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)                                                       : base::string16();
12046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)
12146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  if (elide_at_beginning_)
12246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    return ellipsis_text +
12346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)           text_.substr(FindValidBoundaryBefore(text_.length() - length));
12446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)
12546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  if (!elide_in_middle_)
12646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    return text_.substr(0, FindValidBoundaryBefore(length)) + ellipsis_text;
12746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)
12846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  // We put the extra character, if any, before the cut.
12946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  const size_t half_length = length / 2;
13046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  const size_t prefix_length = FindValidBoundaryBefore(length - half_length);
13146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  const size_t suffix_start_guess = text_.length() - half_length;
13246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  const size_t suffix_start = FindValidBoundaryAfter(suffix_start_guess);
13346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  const size_t suffix_length =
13446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)      half_length - (suffix_start_guess - suffix_start);
13546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  return text_.substr(0, prefix_length) + ellipsis_text +
13646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)         text_.substr(suffix_start, suffix_length);
13746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)}
13846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)
13946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)size_t StringSlicer::FindValidBoundaryBefore(size_t index) const {
14046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  DCHECK_LE(index, text_.length());
14146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  if (index != text_.length())
14246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    U16_SET_CP_START(text_.data(), 0, index);
14346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  return index;
14446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)}
14546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)
14646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)size_t StringSlicer::FindValidBoundaryAfter(size_t index) const {
14746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  DCHECK_LE(index, text_.length());
14846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  if (index != text_.length())
14946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    U16_SET_CP_LIMIT(text_.data(), 0, index, text_.length());
15046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  return index;
15146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)}
15246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)
153a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)base::string16 ElideFilename(const base::FilePath& filename,
154a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                             const FontList& font_list,
155a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                             float available_pixel_width) {
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(OS_WIN)
157a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  base::string16 filename_utf16 = filename.value();
158a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  base::string16 extension = filename.Extension();
159a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  base::string16 rootname = filename.BaseName().RemoveExtension().value();
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#elif defined(OS_POSIX)
161a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  base::string16 filename_utf16 = WideToUTF16(base::SysNativeMBToWide(
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      filename.value()));
163a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  base::string16 extension = WideToUTF16(base::SysNativeMBToWide(
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      filename.Extension()));
165a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  base::string16 rootname = WideToUTF16(base::SysNativeMBToWide(
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      filename.BaseName().RemoveExtension().value()));
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1694e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  const float full_width = GetStringWidthF(filename_utf16, font_list);
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (full_width <= available_pixel_width)
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return base::i18n::GetDisplayStringInLTRDirectionality(filename_utf16);
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (rootname.empty() || extension.empty()) {
17446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    const base::string16 elided_name =
17546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)        ElideText(filename_utf16, font_list, available_pixel_width, ELIDE_TAIL);
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return base::i18n::GetDisplayStringInLTRDirectionality(elided_name);
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1794e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  const float ext_width = GetStringWidthF(extension, font_list);
1804e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  const float root_width = GetStringWidthF(rootname, font_list);
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We may have trimmed the path.
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (root_width + ext_width <= available_pixel_width) {
184a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    const base::string16 elided_name = rootname + extension;
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return base::i18n::GetDisplayStringInLTRDirectionality(elided_name);
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (ext_width >= available_pixel_width) {
189a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    const base::string16 elided_name = ElideText(
19046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)        rootname + extension, font_list, available_pixel_width, ELIDE_MIDDLE);
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return base::i18n::GetDisplayStringInLTRDirectionality(elided_name);
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1944e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  float available_root_width = available_pixel_width - ext_width;
195a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  base::string16 elided_name =
19646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)      ElideText(rootname, font_list, available_root_width, ELIDE_TAIL);
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  elided_name += extension;
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return base::i18n::GetDisplayStringInLTRDirectionality(elided_name);
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
201a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)base::string16 ElideText(const base::string16& text,
202a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                         const FontList& font_list,
203a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                         float available_pixel_width,
20446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)                         ElideBehavior behavior) {
205116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#if !defined(OS_ANDROID) && !defined(OS_IOS)
206116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  DCHECK_NE(behavior, FADE_TAIL);
207116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
208116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  render_text->SetCursorEnabled(false);
209116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  // Do not bother accurately sizing strings over 5000 characters here, for
210116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  // performance purposes. This matches the behavior of Canvas::SizeStringFloat.
211116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  render_text->set_truncate_length(5000);
212116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  render_text->SetFontList(font_list);
213116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  available_pixel_width = std::ceil(available_pixel_width);
214116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  render_text->SetDisplayRect(gfx::Rect(gfx::Size(available_pixel_width, 1)));
215116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  render_text->SetElideBehavior(behavior);
216116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  render_text->SetText(text);
217116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  return render_text->layout_text();
218116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#else
21946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  DCHECK_NE(behavior, FADE_TAIL);
220116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  if (text.empty() || behavior == FADE_TAIL || behavior == NO_ELIDE ||
221116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      GetStringWidthF(text, font_list) <= available_pixel_width) {
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return text;
223116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  }
22446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  if (behavior == ELIDE_EMAIL)
22546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    return ElideEmail(text, font_list, available_pixel_width);
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
22746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  const bool elide_in_middle = (behavior == ELIDE_MIDDLE);
22846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  const bool elide_at_beginning = (behavior == ELIDE_HEAD);
22946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  const bool insert_ellipsis = (behavior != TRUNCATE);
230a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  const base::string16 ellipsis = base::string16(kEllipsisUTF16);
2315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  StringSlicer slicer(text, ellipsis, elide_in_middle, elide_at_beginning);
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2333551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  if (insert_ellipsis &&
2344e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      GetStringWidthF(ellipsis, font_list) > available_pixel_width)
235a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    return base::string16();
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Use binary search to compute the elided text.
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t lo = 0;
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t hi = text.length() - 1;
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t guess;
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (guess = (lo + hi) / 2; lo <= hi; guess = (lo + hi) / 2) {
2425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // We check the width of the whole desired string at once to ensure we
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // handle kerning/ligatures/etc. correctly.
2445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // TODO(skanuj) : Handle directionality of ellipsis based on adjacent
2455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // characters.  See crbug.com/327963.
246a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    const base::string16 cut = slicer.CutString(guess, insert_ellipsis);
2475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const float guess_width = GetStringWidthF(cut, font_list);
2485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (guess_width == available_pixel_width)
2495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      break;
2505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (guess_width > available_pixel_width) {
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      hi = guess - 1;
252116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      // Move back on the loop terminating condition when the guess is too wide.
2535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      if (hi < lo)
2545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        lo = hi;
2555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    } else {
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      lo = guess + 1;
2575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return slicer.CutString(guess, insert_ellipsis);
261116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#endif
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
26446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)bool ElideString(const base::string16& input,
26546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)                 int max_len,
266a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                 base::string16* output) {
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_GE(max_len, 0);
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (static_cast<int>(input.length()) <= max_len) {
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    output->assign(input);
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (max_len) {
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case 0:
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      output->clear();
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case 1:
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      output->assign(input.substr(0, 1));
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case 2:
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      output->assign(input.substr(0, 2));
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case 3:
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      output->assign(input.substr(0, 1) + ASCIIToUTF16(".") +
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     input.substr(input.length() - 1));
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case 4:
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      output->assign(input.substr(0, 1) + ASCIIToUTF16("..") +
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     input.substr(input.length() - 1));
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default: {
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      int rstr_len = (max_len - 3) / 2;
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      int lstr_len = rstr_len + ((max_len - 3) % 2);
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      output->assign(input.substr(0, lstr_len) + ASCIIToUTF16("...") +
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     input.substr(input.length() - rstr_len));
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Internal class used to track progress of a rectangular string elide
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// operation.  Exists so the top-level ElideRectangleString() function
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// can be broken into smaller methods sharing this state.
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class RectangleString {
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  RectangleString(size_t max_rows, size_t max_cols,
311a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                  bool strict, base::string16 *output)
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      : max_rows_(max_rows),
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        max_cols_(max_cols),
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        current_row_(0),
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        current_col_(0),
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        strict_(strict),
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        suppressed_(false),
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        output_(output) {}
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Perform deferred initializations following creation.  Must be called
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // before any input can be added via AddString().
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void Init() { output_->clear(); }
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Add an input string, reformatting to fit the desired dimensions.
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // AddString() may be called multiple times to concatenate together
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // multiple strings into the region (the current caller doesn't do
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // this, however).
328a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  void AddString(const base::string16& input);
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Perform any deferred output processing.  Must be called after the
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // last AddString() call has occurred.
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool Finalize();
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private:
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Add a line to the rectangular region at the current position,
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // either by itself or by breaking it into words.
337a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  void AddLine(const base::string16& line);
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Add a word to the rectangular region at the current position,
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // either by itself or by breaking it into characters.
341a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  void AddWord(const base::string16& word);
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Add text to the output string if the rectangular boundaries
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // have not been exceeded, advancing the current position.
345a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  void Append(const base::string16& string);
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Set the current position to the beginning of the next line.  If
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // |output| is true, add a newline to the output string if the rectangular
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // boundaries have not been exceeded.  If |output| is false, we assume
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // some other mechanism will (likely) do similar breaking after the fact.
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void NewLine(bool output);
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Maximum number of rows allowed in the output string.
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t max_rows_;
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Maximum number of characters allowed in the output string.
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t max_cols_;
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Current row position, always incremented and may exceed max_rows_
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // when the input can not fit in the region.  We stop appending to
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // the output string, however, when this condition occurs.  In the
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // future, we may want to expose this value to allow the caller to
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // determine how many rows would actually be required to hold the
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // formatted string.
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t current_row_;
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Current character position, should never exceed max_cols_.
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t current_col_;
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // True when we do whitespace to newline conversions ourselves.
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool strict_;
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // True when some of the input has been truncated.
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool suppressed_;
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // String onto which the output is accumulated.
377a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  base::string16* output_;
3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(RectangleString);
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
382a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)void RectangleString::AddString(const base::string16& input) {
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::i18n::BreakIterator lines(input,
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  base::i18n::BreakIterator::BREAK_NEWLINE);
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (lines.Init()) {
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    while (lines.Advance())
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      AddLine(lines.GetString());
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NOTREACHED() << "BreakIterator (lines) init failed";
3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool RectangleString::Finalize() {
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (suppressed_) {
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    output_->append(ASCIIToUTF16("..."));
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return true;
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return false;
3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
401a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)void RectangleString::AddLine(const base::string16& line) {
4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (line.length() < max_cols_) {
4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Append(line);
4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    base::i18n::BreakIterator words(line,
4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    base::i18n::BreakIterator::BREAK_SPACE);
4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (words.Init()) {
4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      while (words.Advance())
4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        AddWord(words.GetString());
4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NOTREACHED() << "BreakIterator (words) init failed";
4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Account for naturally-occuring newlines.
4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ++current_row_;
4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  current_col_ = 0;
4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
419a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)void RectangleString::AddWord(const base::string16& word) {
4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (word.length() < max_cols_) {
4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Word can be made to fit, no need to fragment it.
4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (current_col_ + word.length() >= max_cols_)
4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NewLine(strict_);
4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Append(word);
4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Word is so big that it must be fragmented.
4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int array_start = 0;
4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int char_start = 0;
4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    base::i18n::UTF16CharIterator chars(&word);
4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    while (!chars.end()) {
4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // When boundary is hit, add as much as will fit on this line.
4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (current_col_ + (chars.char_pos() - char_start) >= max_cols_) {
4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        Append(word.substr(array_start, chars.array_pos() - array_start));
4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        NewLine(true);
4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        array_start = chars.array_pos();
4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        char_start = chars.char_pos();
4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
4385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      chars.Advance();
4395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
4405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Add the last remaining fragment, if any.
4415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (array_start != chars.array_pos())
4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      Append(word.substr(array_start, chars.array_pos() - array_start));
4435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
446a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)void RectangleString::Append(const base::string16& string) {
4475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (current_row_ < max_rows_)
4485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    output_->append(string);
4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else
4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    suppressed_ = true;
4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  current_col_ += string.length();
4525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void RectangleString::NewLine(bool output) {
4555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (current_row_ < max_rows_) {
4565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (output)
4575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      output_->append(ASCIIToUTF16("\n"));
4585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
4595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    suppressed_ = true;
4605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ++current_row_;
4625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  current_col_ = 0;
4635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Internal class used to track progress of a rectangular text elide
4665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// operation.  Exists so the top-level ElideRectangleText() function
4675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// can be broken into smaller methods sharing this state.
4685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class RectangleText {
4695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
4704e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  RectangleText(const FontList& font_list,
4714e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                float available_pixel_width,
4725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                int available_pixel_height,
47358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                WordWrapBehavior wrap_behavior,
474a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                std::vector<base::string16>* lines)
4753551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      : font_list_(font_list),
4763551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)        line_height_(font_list.GetHeight()),
4775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        available_pixel_width_(available_pixel_width),
4785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        available_pixel_height_(available_pixel_height),
4795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        wrap_behavior_(wrap_behavior),
4805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        current_width_(0),
4815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        current_height_(0),
4825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        last_line_ended_in_lf_(false),
4835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        lines_(lines),
4842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        insufficient_width_(false),
4852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        insufficient_height_(false) {}
4865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Perform deferred initializions following creation.  Must be called
4885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // before any input can be added via AddString().
4895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void Init() { lines_->clear(); }
4905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Add an input string, reformatting to fit the desired dimensions.
4925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // AddString() may be called multiple times to concatenate together
4935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // multiple strings into the region (the current caller doesn't do
4945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // this, however).
495a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  void AddString(const base::string16& input);
4965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Perform any deferred output processing.  Must be called after the last
4982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // AddString() call has occured. Returns a combination of
4992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // |ReformattingResultFlags| indicating whether the given width or height was
5002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // insufficient, leading to elision or truncation.
5012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int Finalize();
5025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private:
5045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Add a line to the rectangular region at the current position,
5055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // either by itself or by breaking it into words.
506a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  void AddLine(const base::string16& line);
5075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Wrap the specified word across multiple lines.
509a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  int WrapWord(const base::string16& word);
5105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Add a long word - wrapping, eliding or truncating per the wrap behavior.
512a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  int AddWordOverflow(const base::string16& word);
5135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Add a word to the rectangluar region at the current position.
515a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  int AddWord(const base::string16& word);
5165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Append the specified |text| to the current output line, incrementing the
5185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // running width by the specified amount. This is an optimization over
5195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // |AddToCurrentLine()| when |text_width| is already known.
520a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  void AddToCurrentLineWithWidth(const base::string16& text, float text_width);
5215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Append the specified |text| to the current output line.
523a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  void AddToCurrentLine(const base::string16& text);
5245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Set the current position to the beginning of the next line.
5265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool NewLine();
5275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5283551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  // The font list used for measuring text width.
5294e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  const FontList& font_list_;
5305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The height of each line of text.
5325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int line_height_;
5335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The number of pixels of available width in the rectangle.
5354e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  const float available_pixel_width_;
5365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The number of pixels of available height in the rectangle.
5385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int available_pixel_height_;
5395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The wrap behavior for words that are too long to fit on a single line.
54158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  const WordWrapBehavior wrap_behavior_;
5425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The current running width.
5444e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  float current_width_;
5455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The current running height.
5475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int current_height_;
5485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The current line of text.
550a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  base::string16 current_line_;
5515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Indicates whether the last line ended with \n.
5535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool last_line_ended_in_lf_;
5545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The output vector of lines.
556a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  std::vector<base::string16>* lines_;
5575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Indicates whether a word was so long that it had to be truncated or elided
5592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // to fit the available width.
5602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bool insufficient_width_;
5612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Indicates whether there were too many lines for the available height.
5632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bool insufficient_height_;
5645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(RectangleText);
5665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
5675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
568a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)void RectangleText::AddString(const base::string16& input) {
5695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::i18n::BreakIterator lines(input,
5705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  base::i18n::BreakIterator::BREAK_NEWLINE);
5715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (lines.Init()) {
5722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    while (!insufficient_height_ && lines.Advance()) {
573a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      base::string16 line = lines.GetString();
5745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // The BREAK_NEWLINE iterator will keep the trailing newline character,
5755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // except in the case of the last line, which may not have one.  Remove
5765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // the newline character, if it exists.
5775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      last_line_ended_in_lf_ = !line.empty() && line[line.length() - 1] == '\n';
5785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (last_line_ended_in_lf_)
5795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        line.resize(line.length() - 1);
5805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      AddLine(line);
5815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
5825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
5835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NOTREACHED() << "BreakIterator (lines) init failed";
5845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)int RectangleText::Finalize() {
5885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Remove trailing whitespace from the last line or remove the last line
5895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // completely, if it's just whitespace.
5902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!insufficient_height_ && !lines_->empty()) {
591a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    base::TrimWhitespace(lines_->back(), base::TRIM_TRAILING, &lines_->back());
5925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (lines_->back().empty() && !last_line_ended_in_lf_)
5935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      lines_->pop_back();
5945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (last_line_ended_in_lf_)
596a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    lines_->push_back(base::string16());
59758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  return (insufficient_width_ ? INSUFFICIENT_SPACE_HORIZONTAL : 0) |
59858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)         (insufficient_height_ ? INSUFFICIENT_SPACE_VERTICAL : 0);
5995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
601a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)void RectangleText::AddLine(const base::string16& line) {
6024e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  const float line_width = GetStringWidthF(line, font_list_);
6035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (line_width <= available_pixel_width_) {
6045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    AddToCurrentLineWithWidth(line, line_width);
6055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
6065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Iterate over positions that are valid to break the line at. In general,
6075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // these are word boundaries but after any punctuation following the word.
6085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    base::i18n::BreakIterator words(line,
6095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    base::i18n::BreakIterator::BREAK_LINE);
6105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (words.Init()) {
6115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      while (words.Advance()) {
6125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        const bool truncate = !current_line_.empty();
613a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        const base::string16& word = words.GetString();
6145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        const int lines_added = AddWord(word);
6155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (lines_added) {
6165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          if (truncate) {
6175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            // Trim trailing whitespace from the line that was added.
6185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            const int line = lines_->size() - lines_added;
619a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            base::TrimWhitespace(lines_->at(line), base::TRIM_TRAILING,
620a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                                 &lines_->at(line));
6215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          }
622a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          if (base::ContainsOnlyChars(word, base::kWhitespaceUTF16)) {
6235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            // Skip the first space if the previous line was carried over.
6245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            current_width_ = 0;
6255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            current_line_.clear();
6265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          }
6275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
6285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
6295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
6305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NOTREACHED() << "BreakIterator (words) init failed";
6315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
6325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Account for naturally-occuring newlines.
6345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  NewLine();
6355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
637a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)int RectangleText::WrapWord(const base::string16& word) {
6385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Word is so wide that it must be fragmented.
639a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  base::string16 text = word;
6405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int lines_added = 0;
6415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool first_fragment = true;
6422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  while (!insufficient_height_ && !text.empty()) {
643a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    base::string16 fragment =
64446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)        ElideText(text, font_list_, available_pixel_width_, TRUNCATE);
645c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    // At least one character has to be added at every line, even if the
646c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    // available space is too small.
64746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    if (fragment.empty())
648c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      fragment = text.substr(0, 1);
6495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!first_fragment && NewLine())
6505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      lines_added++;
6515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    AddToCurrentLine(fragment);
6525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    text = text.substr(fragment.length());
6535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    first_fragment = false;
6545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return lines_added;
6565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
658a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)int RectangleText::AddWordOverflow(const base::string16& word) {
6595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int lines_added = 0;
6605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Unless this is the very first word, put it on a new line.
6625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!current_line_.empty()) {
6635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!NewLine())
6645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return 0;
6655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    lines_added++;
6665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
66858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  if (wrap_behavior_ == IGNORE_LONG_WORDS) {
6695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    current_line_ = word;
6705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    current_width_ = available_pixel_width_;
67158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  } else if (wrap_behavior_ == WRAP_LONG_WORDS) {
6725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    lines_added += WrapWord(word);
6735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
67458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    const ElideBehavior elide_behavior =
67546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)        (wrap_behavior_ == ELIDE_LONG_WORDS ? ELIDE_TAIL : TRUNCATE);
676a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    const base::string16 elided_word =
67758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        ElideText(word, font_list_, available_pixel_width_, elide_behavior);
6785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    AddToCurrentLine(elided_word);
6792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    insufficient_width_ = true;
6805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return lines_added;
6835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
685a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)int RectangleText::AddWord(const base::string16& word) {
6865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int lines_added = 0;
687a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  base::string16 trimmed;
688a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  base::TrimWhitespace(word, base::TRIM_TRAILING, &trimmed);
6894e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  const float trimmed_width = GetStringWidthF(trimmed, font_list_);
6905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (trimmed_width <= available_pixel_width_) {
6915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Word can be made to fit, no need to fragment it.
6925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ((current_width_ + trimmed_width > available_pixel_width_) && NewLine())
6935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      lines_added++;
6945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Append the non-trimmed word, in case more words are added after.
6955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    AddToCurrentLine(word);
6965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
69758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    lines_added = AddWordOverflow(wrap_behavior_ == IGNORE_LONG_WORDS ?
6985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  trimmed : word);
6995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return lines_added;
7015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
703a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)void RectangleText::AddToCurrentLine(const base::string16& text) {
7044e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  AddToCurrentLineWithWidth(text, GetStringWidthF(text, font_list_));
7055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
707a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)void RectangleText::AddToCurrentLineWithWidth(const base::string16& text,
7084e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                                              float text_width) {
7095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (current_height_ >= available_pixel_height_) {
7102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    insufficient_height_ = true;
7115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
7125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  current_line_.append(text);
7145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  current_width_ += text_width;
7155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool RectangleText::NewLine() {
7185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool line_added = false;
7195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (current_height_ < available_pixel_height_) {
7205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    lines_->push_back(current_line_);
7215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    current_line_.clear();
7225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    line_added = true;
7235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
7242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    insufficient_height_ = true;
7255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  current_height_ += line_height_;
7275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  current_width_ = 0;
7285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return line_added;
7295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
7325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
733a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)bool ElideRectangleString(const base::string16& input, size_t max_rows,
734a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                          size_t max_cols, bool strict,
735a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                          base::string16* output) {
7365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  RectangleString rect(max_rows, max_cols, strict, output);
7375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  rect.Init();
7385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  rect.AddString(input);
7395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return rect.Finalize();
7405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
742a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)int ElideRectangleText(const base::string16& input,
7434e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                       const FontList& font_list,
7444e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                       float available_pixel_width,
7453551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)                       int available_pixel_height,
7463551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)                       WordWrapBehavior wrap_behavior,
747a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                       std::vector<base::string16>* lines) {
7483551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  RectangleText rect(font_list,
7495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     available_pixel_width,
7505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     available_pixel_height,
7515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     wrap_behavior,
7525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     lines);
7535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  rect.Init();
7545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  rect.AddString(input);
7555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return rect.Finalize();
7565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7585f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)base::string16 TruncateString(const base::string16& string,
7595f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                              size_t length,
7605f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                              BreakType break_type) {
7615f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  DCHECK(break_type == CHARACTER_BREAK || break_type == WORD_BREAK);
7625f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
7635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (string.size() <= length)
7645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // String fits, return it.
7655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return string;
7665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (length == 0)
7685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // No room for the elide string, return an empty string.
769a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    return base::string16();
7705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t max = length - 1;
7725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Added to the end of strings that are too big.
774a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  static const base::char16 kElideString[] = { 0x2026, 0 };
7755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (max == 0)
7775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Just enough room for the elide string.
7785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return kElideString;
7795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7805f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  int32_t index = static_cast<int32_t>(max);
7815f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  if (break_type == WORD_BREAK) {
7825f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    // Use a line iterator to find the first boundary.
7835f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    UErrorCode status = U_ZERO_ERROR;
7845f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    scoped_ptr<icu::BreakIterator> bi(
7855f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        icu::RuleBasedBreakIterator::createLineInstance(
7865f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            icu::Locale::getDefault(), status));
7875f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if (U_FAILURE(status))
7885f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      return string.substr(0, max) + kElideString;
7895f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    bi->setText(string.c_str());
7905f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    index = bi->preceding(index);
7915f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if (index == icu::BreakIterator::DONE || index == 0) {
7925f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      // We either found no valid line break at all, or one right at the
7935f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      // beginning of the string. Go back to the end; we'll have to break in the
7945f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      // middle of a word.
7955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      index = static_cast<int32_t>(max);
7965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
7975f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  }
7985f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
7995f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // Use a character iterator to find the previous non-whitespace character.
8005f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  icu::StringCharacterIterator char_iterator(string.c_str());
8015f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  char_iterator.setIndex(index);
8025f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  while (char_iterator.hasPrevious()) {
8035f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    char_iterator.previous();
8045f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if (!(u_isspace(char_iterator.current()) ||
8055f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          u_charType(char_iterator.current()) == U_CONTROL_CHAR ||
8065f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          u_charType(char_iterator.current()) == U_NON_SPACING_MARK)) {
8075f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      // Not a whitespace character. Advance the iterator so that we
8085f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      // include the current character in the truncated string.
8095f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      char_iterator.next();
8105f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      break;
8115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
8125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
8135f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  if (char_iterator.hasPrevious()) {
8145f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    // Found a valid break point.
8155f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    index = char_iterator.getIndex();
8165f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  } else {
8175f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    // String has leading whitespace, return the elide string.
8185f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    return kElideString;
8195f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  }
8205f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
8215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return string.substr(0, index) + kElideString;
8225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
8235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
82458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}  // namespace gfx
825