Roll src/third_party/WebKit d9c6159:8139f33 (svn 201974:201975)
[chromium-blink-merge.git] / ios / chrome / common / string_util.mm
blobd8fca0e71a6266a9389f853dc85c7f97beb9509e
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ios/chrome/common/string_util.h"
7 #import <UIKit/UIKit.h>
9 #include "base/logging.h"
10 #include "base/mac/scoped_block.h"
11 #include "base/mac/scoped_nsobject.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/strings/sys_string_conversions.h"
15 namespace {
16 typedef BOOL (^ArrayFilterProcedure)(id object, NSUInteger index, BOOL* stop);
17 typedef NSString* (^SubstringExtractionProcedure)(NSUInteger);
20 NSString* ParseStringWithLink(NSString* text, NSRange* out_link_range) {
21   // Find the range within |text| and create a substring without the link tags.
22   NSRange begin_range = [text rangeOfString:@"BEGIN_LINK[ \t]*"
23                                     options:NSRegularExpressionSearch];
24   NSRange link_text_range = NSMakeRange(NSNotFound, 0);
25   if (begin_range.length == 0) {
26     if (out_link_range)
27       *out_link_range = link_text_range;
28     return text;
29   }
31   NSUInteger after_begin_link = NSMaxRange(begin_range);
32   NSRange range_to_search_for_end_link =
33       NSMakeRange(after_begin_link, text.length - after_begin_link);
34   NSRange end_range = [text rangeOfString:@"[ \t]*END_LINK"
35                                   options:NSRegularExpressionSearch
36                                     range:range_to_search_for_end_link];
37   if (end_range.length == 0) {
38     if (out_link_range)
39       *out_link_range = link_text_range;
40     return text;
41   }
43   link_text_range.location = after_begin_link;
44   link_text_range.length = end_range.location - link_text_range.location;
45   base::scoped_nsobject<NSMutableString> out_text(
46       [[NSMutableString alloc] init]);
47   // First part - before the link.
48   if (begin_range.location > 0)
49     [out_text appendString:[text substringToIndex:begin_range.location]];
51   // Link part.
52   [out_text appendString:[text substringWithRange:link_text_range]];
54   // Last part - after the link.
55   NSUInteger after_end_link = NSMaxRange(end_range);
56   if (after_end_link < [text length]) {
57     [out_text appendString:[text substringFromIndex:after_end_link]];
58   }
60   link_text_range.location = begin_range.location;
61   if (out_link_range)
62     *out_link_range = link_text_range;
63   return [NSString stringWithString:out_text];
66 // Ranges of unicode codepage containing drawing characters.
67 // 2190—21FF Arrows
68 // 2200—22FF Mathematical Operators
69 // 2300—23FF Miscellaneous Technical
70 // 2400—243F Control Pictures
71 // 2440—245F Optical Character Recognition
72 // 2460—24FF Enclosed Alphanumerics
73 // 2500—257F Box Drawing
74 // 2580—259F Block Elements
75 // 25A0—25FF Geometric Shapes
76 // 2600—26FF Miscellaneous Symbols
77 // 2700—27BF Dingbats
78 // 27C0—27EF Miscellaneous Mathematical Symbols-A
79 // 27F0—27FF Supplemental Arrows-A
80 // 2900—297F Supplemental Arrows-B
81 // 2980—29FF Miscellaneous Mathematical Symbols-B
82 // 2A00—2AFF Supplemental Mathematical Operators
83 // 2B00—2BFF Miscellaneous Symbols and Arrows
84 // The section 2800—28FF Braille Patterns must be preserved.
85 // The list of characters that must be deleted from the selection.
86 NSCharacterSet* GraphicCharactersSet() {
87   static NSMutableCharacterSet* graphicalCharsSet;
88   static dispatch_once_t dispatch_once_token;
89   dispatch_once(&dispatch_once_token, ^{
90     graphicalCharsSet = [[NSMutableCharacterSet alloc] init];
91     NSRange graphicalCharsFirstRange = NSMakeRange(0x2190, 0x2800 - 0x2190);
92     NSRange graphicalCharsSecondRange = NSMakeRange(0x2900, 0x2c00 - 0x2900);
93     [graphicalCharsSet addCharactersInRange:graphicalCharsFirstRange];
94     [graphicalCharsSet addCharactersInRange:graphicalCharsSecondRange];
95   });
96   return graphicalCharsSet;
99 NSString* CleanNSStringForDisplay(NSString* dirty, BOOL removeGraphicChars) {
100   NSCharacterSet* wspace = [NSCharacterSet whitespaceAndNewlineCharacterSet];
101   NSString* cleanString = dirty;
102   if (removeGraphicChars) {
103     cleanString = [[cleanString
104         componentsSeparatedByCharactersInSet:GraphicCharactersSet()]
105         componentsJoinedByString:@" "];
106   }
107   base::scoped_nsobject<NSMutableArray> spaceSeparatedCompoments(
108       [[cleanString componentsSeparatedByCharactersInSet:wspace] mutableCopy]);
109   ArrayFilterProcedure filter = ^(id object, NSUInteger index, BOOL* stop) {
110     return [object isEqualToString:@""];
111   };
112   [spaceSeparatedCompoments
113       removeObjectsAtIndexes:[spaceSeparatedCompoments
114                                  indexesOfObjectsPassingTest:filter]];
115   cleanString = [spaceSeparatedCompoments componentsJoinedByString:@" "];
116   return cleanString;
119 std::string CleanStringForDisplay(std::string dirty, BOOL removeGraphicChars) {
120   return base::SysNSStringToUTF8(CleanNSStringForDisplay(
121       base::SysUTF8ToNSString(dirty), removeGraphicChars));
124 NSString* SubstringOfWidth(NSString* string,
125                            NSDictionary* attributes,
126                            CGFloat targetWidth,
127                            BOOL trailing) {
128   if (![string length])
129     return nil;
131   UIFont* font = [attributes objectForKey:NSFontAttributeName];
132   DCHECK(font);
134   // Function to get the correct substring while insulating against
135   // length overrun/underrun.
136   base::mac::ScopedBlock<SubstringExtractionProcedure> getSubstring;
137   if (trailing) {
138     getSubstring.reset([^NSString*(NSUInteger chars) {
139       NSUInteger length = [string length];
140       return [string substringFromIndex:length - MIN(length, chars)];
141     } copy]);
142   } else {
143     getSubstring.reset([^NSString*(NSUInteger chars) {
144       return [string substringToIndex:MIN(chars, [string length])];
145     } copy]);
146   }
148   // Guess at the number of characters that will fit, assuming
149   // the font's x-height is about 25% wider than an average character (25%
150   // value was determined experimentally).
151   NSUInteger characters =
152       MIN(targetWidth / (font.xHeight * 0.8), [string length]);
153   NSInteger increment = 1;
154   NSString* substring = getSubstring.get()(characters);
155   CGFloat prevWidth = [substring sizeWithAttributes:attributes].width;
156   do {
157     characters += increment;
158     substring = getSubstring.get()(characters);
159     CGFloat thisWidth = [substring sizeWithAttributes:attributes].width;
160     if (prevWidth > targetWidth) {
161       if (thisWidth <= targetWidth)
162         break;  // Shrinking the string, found the right size.
163       else
164         increment = -1;  // Shrink the string
165     } else if (prevWidth < targetWidth) {
166       if (thisWidth < targetWidth)
167         increment = 1;  // Grow the string
168       else {
169         substring = getSubstring.get()(characters - increment);
170         break;  // Growing the string, found the right size.
171       }
172     }
173     prevWidth = thisWidth;
174   } while (characters > 0 && characters < [string length]);
176   return substring;