OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "ios/chrome/common/string_util.h" | |
6 | |
7 #import <UIKit/UIKit.h> | |
8 | |
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" | |
14 | |
15 namespace { | |
16 typedef BOOL (^ArrayFilterProcedure)(id object, NSUInteger index, BOOL* stop); | |
17 typedef NSString* (^SubstringExtractionProcedure)(NSUInteger); | |
18 } | |
19 | |
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 } | |
30 | |
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 } | |
42 | |
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]]; | |
50 | |
51 // Link part. | |
52 [out_text appendString:[text substringWithRange:link_text_range]]; | |
53 | |
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 } | |
59 | |
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]; | |
64 } | |
65 | |
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; | |
97 } | |
98 | |
99 // TODO(marq): Add unit tests for this function. | |
100 NSString* CleanNSStringForDisplay(NSString* dirty, | |
101 BOOL removeGraphicChars, | |
102 BOOL trim) { | |
103 NSCharacterSet* wspace = [NSCharacterSet whitespaceAndNewlineCharacterSet]; | |
104 NSString* cleanString = dirty; | |
105 if (removeGraphicChars) { | |
106 cleanString = [[cleanString | |
107 componentsSeparatedByCharactersInSet:GraphicCharactersSet()] | |
108 componentsJoinedByString:@" "]; | |
109 } | |
110 base::scoped_nsobject<NSMutableArray> spaceSeparatedCompoments( | |
111 [[cleanString componentsSeparatedByCharactersInSet:wspace] mutableCopy]); | |
112 ArrayFilterProcedure filter = ^(id object, NSUInteger index, BOOL* stop) { | |
113 return [object isEqualToString:@""]; | |
114 }; | |
115 [spaceSeparatedCompoments | |
116 removeObjectsAtIndexes:[spaceSeparatedCompoments | |
117 indexesOfObjectsPassingTest:filter]]; | |
118 cleanString = [spaceSeparatedCompoments componentsJoinedByString:@" "]; | |
119 return trim ? [cleanString stringByTrimmingCharactersInSet:wspace] | |
120 : cleanString; | |
121 } | |
122 | |
123 std::string CleanStringForDisplay(std::string dirty, | |
124 BOOL removeGraphicChars, | |
125 BOOL trim) { | |
126 return base::SysNSStringToUTF8(CleanNSStringForDisplay( | |
127 base::SysUTF8ToNSString(dirty), removeGraphicChars, trim)); | |
128 } | |
129 | |
130 // TODO(marq): Add unit tests for this function. | |
131 NSString* SubstringOfWidth(NSString* string, | |
132 NSDictionary* attributes, | |
133 CGFloat targetWidth, | |
134 BOOL trailing) { | |
135 if (![string length]) | |
136 return nil; | |
137 | |
138 UIFont* font = [attributes objectForKey:NSFontAttributeName]; | |
139 DCHECK(font); | |
140 | |
141 // Function to get the correct substring while insulating against | |
142 // length overrun/underrun. | |
143 base::mac::ScopedBlock<SubstringExtractionProcedure> getSubstring; | |
144 if (trailing) { | |
145 getSubstring.reset([^NSString*(NSUInteger chars) { | |
146 NSUInteger length = [string length]; | |
147 return [string substringFromIndex:length - MIN(length, chars)]; | |
148 } copy]); | |
149 } else { | |
150 getSubstring.reset([^NSString*(NSUInteger chars) { | |
151 return [string substringToIndex:MIN(chars, [string length])]; | |
152 } copy]); | |
153 } | |
154 | |
155 // Guess at the number of characters that will fit, assuming | |
156 // the font's x-height is about 25% wider than an average character (25% | |
157 // value was determined experimentally). | |
158 NSUInteger characters = | |
159 MIN(targetWidth / (font.xHeight * 0.8), [string length]); | |
160 NSInteger increment = 1; | |
161 NSString* substring = getSubstring.get()(characters); | |
162 CGFloat prevWidth = [substring sizeWithAttributes:attributes].width; | |
163 do { | |
164 characters += increment; | |
165 substring = getSubstring.get()(characters); | |
166 CGFloat thisWidth = [substring sizeWithAttributes:attributes].width; | |
167 if (prevWidth > targetWidth) { | |
168 if (thisWidth < targetWidth) | |
169 break; // Shrinking the string, found the right size. | |
170 else | |
171 increment = -1; // Shrink the string | |
172 } else if (prevWidth < targetWidth) { | |
173 if (thisWidth < targetWidth) | |
174 increment = 1; // Grow the string | |
175 else { | |
176 substring = getSubstring.get()(characters - increment); | |
177 break; // Growing the string, found the right size. | |
178 } | |
179 } | |
180 prevWidth = thisWidth; | |
181 } while (characters > 0 && characters < [string length]); | |
182 | |
183 return substring; | |
184 } | |
OLD | NEW |