I am using Core Text to draw some text. I would like to get the various run bounds, but when I call CTRunGetImageBounds
, the rect that is returned is the correct si
Before you can use CTRunGetImageBounds() or CTLineDraw() you need to set the starting text position with a call to CGContextSetTextPosition() before drawing or computing each line. The reason is a CTLine has no idea where to start drawing (or computing) on its own, it uses the last text position, so you need to give it an XY starting point. To get the origins for a frame's array of lines, call CTFrameGetLineOrigins() with an array of points. Then, before drawing or computing each line - set the origin of that line via CGContextSetTextPosition().
NSArray *lines = (NSArray *)CTFrameGetLines(frame);
CGPoint origins[[lines count]]; // the origins of each line at the baseline
CTFrameGetLineOrigins(frame, CFRangeMake(0, 0), origins);
int i, lineCount;
lineCount = [lines count];
for (i = 0; i < lineCount; i++) {
CTLineRef line = (CTLineRef)[lines objectAtIndex:i];
CGContextSetTextPosition(context, origins[i].x, origins[i].y);
CTLineDraw(line, context);
}
Here is what I came up with. This is completely accurate.
CTFrameRef frame = [self _frameWithRect:rect];
NSArray *lines = (NSArray *)CTFrameGetLines(frame);
CGPoint origins[[lines count]];//the origins of each line at the baseline
CTFrameGetLineOrigins(frame, CFRangeMake(0, 0), origins);
NSUInteger lineIndex = 0;
for (id lineObj in lines) {
CTLineRef line = (CTLineRef)lineObj;
for (id runObj in (NSArray *)CTLineGetGlyphRuns(line)) {
CTRunRef run = (CTRunRef)runObj;
CFRange runRange = CTRunGetStringRange(run);
CGRect runBounds;
CGFloat ascent;//height above the baseline
CGFloat descent;//height below the baseline
runBounds.size.width = CTRunGetTypographicBounds(run, CFRangeMake(0, 0), &ascent, &descent, NULL);
runBounds.size.height = ascent + descent;
CGFloat xOffset = CTLineGetOffsetForStringIndex(line, CTRunGetStringRange(run).location, NULL);
runBounds.origin.x = origins[lineIndex].x + rect.origin.x + xOffset;
runBounds.origin.y = origins[lineIndex].y + rect.origin.y;
runBounds.origin.y -= descent;
//do something with runBounds
}
lineIndex++;
}