-
Notifications
You must be signed in to change notification settings - Fork 41
/
NS(Attributed)String+Geometrics.m
164 lines (134 loc) · 5.65 KB
/
NS(Attributed)String+Geometrics.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
#import "NS(Attributed)String+Geometrics.h"
/*
To use this file in a non-ARC target, #define NO_ARC 1 in a prefix
file, or for smart ARC-or-not detection, include our super whiz bang
SSY_ARC_OR_NO_ARC.h in your project and #import "SSY_ARC_OR_NO_ARC.h"
in your prefix file.
*/
NSInteger gNSStringGeometricsTypesetterBehavior = NSTypesetterLatestBehavior ;
@implementation NSAttributedString (Geometrics)
#pragma mark * Measure Attributed String
- (NSSize)sizeForWidth:(CGFloat)width
height:(CGFloat)height {
NSSize answer = NSZeroSize ;
if ([self length] > 0) {
// Checking for empty string is necessary since Layout Manager will give the nominal
// height of one line if length is 0. Our API specifies 0.0 for an empty string.
NSSize size = NSMakeSize(width, height) ;
NSTextContainer *textContainer = [[NSTextContainer alloc] initWithContainerSize:size] ;
NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:self] ;
NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init] ;
[layoutManager addTextContainer:textContainer] ;
[textStorage addLayoutManager:layoutManager] ;
[layoutManager setHyphenationFactor:0.0] ;
if (gNSStringGeometricsTypesetterBehavior != NSTypesetterLatestBehavior) {
[layoutManager setTypesetterBehavior:gNSStringGeometricsTypesetterBehavior] ;
}
// NSLayoutManager is lazy, so we need the following kludge to force layout:
[layoutManager glyphRangeForTextContainer:textContainer] ;
answer = [layoutManager usedRectForTextContainer:textContainer].size ;
#if !__has_feature(objc_arc)
[textStorage release] ;
[textContainer release] ;
#endif
// Adjust if there is extra height for the cursor
NSSize extraLineSize = [layoutManager extraLineFragmentRect].size ;
if (extraLineSize.height > 0) {
answer.height -= extraLineSize.height ;
}
#if !__has_feature(objc_arc)
[layoutManager release] ;
#endif
// In case we changed it above, set typesetterBehavior back
// to the default value.
gNSStringGeometricsTypesetterBehavior = NSTypesetterLatestBehavior ;
}
return answer ;
}
- (CGFloat)heightForWidth:(CGFloat)width {
return [self sizeForWidth:width
height:FLT_MAX].height ;
}
- (CGFloat)widthForHeight:(CGFloat)height {
return [self sizeForWidth:FLT_MAX
height:height].width ;
}
- (NSAttributedString*)attributedStringTruncatedToWidth:(CGFloat)width
height:(CGFloat)height {
NSAttributedString* answer = self ;
if ([self length] > 2) {
if (([self widthForHeight:height] > width) || ([self heightForWidth:width] > height)) {
NSMutableAttributedString* attributedString = [[self mutableCopy] autorelease] ;
[attributedString replaceCharactersInRange:NSMakeRange([attributedString length] - 1, 1)
withString:@"…"] ;
while (
(([attributedString widthForHeight:height] > width) || ([attributedString heightForWidth:width] > height))
&&
([attributedString length] > 1)
) {
[attributedString replaceCharactersInRange:NSMakeRange([attributedString length] - 2, 2)
withString:@"…"] ;
}
answer = [[attributedString copy] autorelease] ;
}
}
return answer ;
}
@end
@implementation NSString (Geometrics)
#pragma mark * Given String with Attributes
- (NSSize)sizeForWidth:(CGFloat)width
height:(CGFloat)height
attributes:(NSDictionary*)attributes {
NSSize answer ;
NSAttributedString *astr = [[NSAttributedString alloc] initWithString:self
attributes:attributes] ;
answer = [astr sizeForWidth:width
height:height] ;
#if !__has_feature(objc_arc)
[astr release] ;
#endif
return answer ;
}
- (CGFloat)heightForWidth:(CGFloat)width
attributes:(NSDictionary*)attributes {
return [self sizeForWidth:width
height:FLT_MAX
attributes:attributes].height ;
}
- (CGFloat)widthForHeight:(CGFloat)height
attributes:(NSDictionary*)attributes {
return [self sizeForWidth:FLT_MAX
height:height
attributes:attributes].width ;
}
#pragma mark * Given String with Font
- (NSSize)sizeForWidth:(CGFloat)width
height:(CGFloat)height
font:(NSFont*)font {
NSSize answer = NSZeroSize ;
if (font == nil) {
NSLog(@"[%@ %@]: Internal Error 561-3810: Nil font", [self class], NSStringFromSelector(_cmd)) ;
}
else {
NSDictionary* attributes = [NSDictionary dictionaryWithObjectsAndKeys:
font, NSFontAttributeName, nil] ;
answer = [self sizeForWidth:width
height:height
attributes:attributes] ;
}
return answer ;
}
- (CGFloat)heightForWidth:(CGFloat)width
font:(NSFont*)font {
return [self sizeForWidth:width
height:FLT_MAX
font:font].height ;
}
- (CGFloat)widthForHeight:(CGFloat)height
font:(NSFont*)font {
return [self sizeForWidth:FLT_MAX
height:height
font:font].width ;
}
@end