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