/
DTCoreTextGlyphRun.m
326 lines (261 loc) · 6.09 KB
/
DTCoreTextGlyphRun.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
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
//
// DTCoreTextGlyphRun.m
// CoreTextExtensions
//
// Created by Oliver Drobnik on 1/25/11.
// Copyright 2011 Drobnik.com. All rights reserved.
//
#import "DTCoreTextGlyphRun.h"
#import "DTCoreTextLayoutLine.h"
#import "DTTextAttachment.h"
#import "DTCoreTextConstants.h"
#ifndef __IPHONE_4_3
#define __IPHONE_4_3 40300
#endif
#define SYNCHRONIZE_START(obj) dispatch_semaphore_wait(runLock, DISPATCH_TIME_FOREVER);
#define SYNCHRONIZE_END(obj) dispatch_semaphore_signal(runLock);
@interface DTCoreTextGlyphRun ()
@property (nonatomic, assign) CGRect frame;
@property (nonatomic, assign) NSInteger numberOfGlyphs;
@property (nonatomic, unsafe_unretained, readwrite) NSDictionary *attributes;
@property (nonatomic, assign) dispatch_semaphore_t runLock;
@end
@implementation DTCoreTextGlyphRun
{
CTRunRef _run;
CGRect _frame;
CGFloat _offset; // x distance from line origin
CGFloat ascent;
CGFloat descent;
CGFloat leading;
CGFloat width;
NSInteger numberOfGlyphs;
const CGPoint *glyphPositionPoints;
BOOL needToFreeGlyphPositionPoints;
__unsafe_unretained DTCoreTextLayoutLine *_line; // retain cycle, since these objects are retained by the _line
__unsafe_unretained NSDictionary *attributes;
NSArray *stringIndices;
DTTextAttachment *_attachment;
BOOL _hyperlink;
BOOL _didCheckForAttachmentInAttributes;
BOOL _didCheckForHyperlinkInAttributes;
BOOL _didCalculateMetrics;
}
@synthesize runLock;
- (id)initWithRun:(CTRunRef)run layoutLine:(DTCoreTextLayoutLine *)layoutLine offset:(CGFloat)offset
{
self = [super init];
if (self)
{
_run = run;
CFRetain(_run);
_offset = offset;
_line = layoutLine;
runLock = dispatch_semaphore_create(1);
}
return self;
}
- (void)dealloc
{
if (_run)
{
CFRelease(_run);
}
dispatch_release(runLock);
}
- (NSString *)description
{
return [NSString stringWithFormat:@"<%@ glyphs=%d %@>", [self class], [self numberOfGlyphs], NSStringFromCGRect(_frame)];
}
#pragma mark Calculations
- (void)calculateMetrics
{
// calculate metrics
SYNCHRONIZE_START(self)
{
if (!_didCalculateMetrics)
{
width = (CGFloat)CTRunGetTypographicBounds((CTRunRef)_run, CFRangeMake(0, 0), &ascent, &descent, &leading);
_didCalculateMetrics = YES;
}
}
SYNCHRONIZE_END(self)
}
- (CGRect)frameOfGlyphAtIndex:(NSInteger)index
{
if (!_didCalculateMetrics) {
[self calculateMetrics];
}
if (!glyphPositionPoints)
{
// this is a pointer to the points inside the run, thus no retain necessary
glyphPositionPoints = CTRunGetPositionsPtr(_run);
}
if (!glyphPositionPoints || index >= self.numberOfGlyphs)
{
return CGRectNull;
}
CGPoint glyphPosition = glyphPositionPoints[index];
CGRect rect = CGRectMake(_line.baselineOrigin.x + glyphPosition.x, _line.baselineOrigin.y - ascent, _offset + width - glyphPosition.x, ascent + descent);
if (index < self.numberOfGlyphs-1)
{
rect.size.width = glyphPositionPoints[index+1].x - glyphPosition.x;
}
return rect;
}
// TODO: fix indices if the stringRange is modified
- (NSArray *)stringIndices
{
if (!stringIndices)
{
const CFIndex *indices = CTRunGetStringIndicesPtr(_run);
NSInteger count = self.numberOfGlyphs;
NSMutableArray *array = [NSMutableArray arrayWithCapacity:count];
NSInteger i;
for (i = 0; i < count; i++)
{
[array addObject:[NSNumber numberWithInteger:indices[i]]];
}
stringIndices = array;
}
return stringIndices;
}
// bounds of an image encompassing the entire run
- (CGRect)imageBoundsInContext:(CGContextRef)context
{
return CTRunGetImageBounds(_run, context, CFRangeMake(0, 0));
}
// range of the characters from the original string
- (NSRange)stringRange
{
if (!_stringRange.length)
{
CFRange range = CTRunGetStringRange(_run);
_stringRange = NSMakeRange(range.location, range.length);
}
return _stringRange;
}
- (void)drawInContext:(CGContextRef)context
{
if (!_run || !context)
{
return;
}
CGAffineTransform textMatrix = CTRunGetTextMatrix(_run);
if (CGAffineTransformIsIdentity(textMatrix))
{
CTRunDraw(_run, context, CFRangeMake(0, 0));
}
else
{
CGPoint pos = CGContextGetTextPosition(context);
// set tx and ty to current text pos according to docs
textMatrix.tx = pos.x;
textMatrix.ty = pos.y;
CGContextSetTextMatrix(context, textMatrix);
CTRunDraw(_run, context, CFRangeMake(0, 0));
// restore identity
CGContextSetTextMatrix(context, CGAffineTransformIdentity);
}
}
- (void)fixMetricsFromAttachment
{
if (self.attachment)
{
if (!_didCalculateMetrics)
{
[self calculateMetrics];
}
descent = 0;
ascent = self.attachment.displaySize.height;
}
}
#pragma mark Properites
- (NSInteger)numberOfGlyphs
{
if (!numberOfGlyphs)
{
numberOfGlyphs = CTRunGetGlyphCount(_run);
}
return numberOfGlyphs;
}
- (NSDictionary *)attributes
{
if (!attributes)
{
attributes = (__bridge NSDictionary *)CTRunGetAttributes(_run);
}
return attributes;
}
- (DTTextAttachment *)attachment
{
if (!_attachment)
{
if (!_didCheckForAttachmentInAttributes)
{
_attachment = [self.attributes objectForKey:NSAttachmentAttributeName];
_didCheckForAttachmentInAttributes = YES;
}
}
return _attachment;
}
- (BOOL)isHyperlink
{
if (!_hyperlink)
{
if (!_didCheckForHyperlinkInAttributes)
{
_hyperlink = [self.attributes objectForKey:DTLinkAttribute]!=nil;
_didCheckForHyperlinkInAttributes = YES;
}
}
return _hyperlink;
}
- (CGRect)frame
{
if (!_didCalculateMetrics)
{
[self calculateMetrics];
}
return CGRectMake(_line.baselineOrigin.x + _offset, _line.baselineOrigin.y - ascent, width, ascent + descent);
}
- (CGFloat)width
{
if (!_didCalculateMetrics)
{
[self calculateMetrics];
}
return width;
}
- (CGFloat)ascent
{
if (!_didCalculateMetrics)
{
[self calculateMetrics];
}
return ascent;
}
- (CGFloat)descent
{
if (!_didCalculateMetrics)
{
[self calculateMetrics];
}
return descent;
}
- (CGFloat)leading
{
if (!_didCalculateMetrics)
{
[self calculateMetrics];
}
return leading;
}
@synthesize frame = _frame;
@synthesize numberOfGlyphs;
@synthesize attributes;
@synthesize ascent;
@synthesize descent;
@synthesize leading;
@synthesize attachment = _attachment;
@end