2011-03-06 135 views
3

我正在使用核心文本来绘制一些文本。我想获得各种运行范围,但是当我拨打CTRunGetImageBounds时,返回的矩形尺寸正确,但位置错误。具体来说,线条原点在整个文本的末尾。CTRunGetImageBounds返回不准确的结果

- (void)drawRect:(CGRect)rect 
{ 
    CGContextRef context = UIGraphicsGetCurrentContext(); 
    self.transform = CGAffineTransformMakeScale(1.0, -1.0); 
    CGContextSetTextMatrix(context, CGAffineTransformIdentity); 

    [[UIColor whiteColor] set]; 
    CGContextFillRect(context, self.bounds); 

    NSMutableAttributedString* attrString = [[NSMutableAttributedString alloc] initWithString:@"Blue should be underlined."]; 
    NSRange blueRange = NSMakeRange(0, 4); 
    [attrString beginEditing]; 
    //make all text size 20.0 
    [attrString addAttribute:(NSString *)kCTFontAttributeName value:(id)CTFontCreateWithName((CFStringRef)@"Helvetica", 20.0, NULL) range:NSMakeRange(0, [attrString length])]; 
    //make the text appear in blue 
    [attrString addAttribute:(NSString *)kCTForegroundColorAttributeName value:(id)[[UIColor blueColor] CGColor] range:blueRange]; 
    //next make the text appear with an underline 
    [attrString addAttribute:(NSString *)kCTUnderlineStyleAttributeName value:[NSNumber numberWithInt:1] range:blueRange]; 
    [attrString endEditing]; 

    CGMutablePathRef path = CGPathCreateMutable(); 
    CGRect bounds = CGRectMake(10.0, 10.0, 200.0, 200.0); 
    [[UIColor redColor] set]; 
    CGContextFillRect(context, bounds); 
    CGPathAddRect(path, NULL, bounds); 

    CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attrString); 
    CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), path, NULL); 
    CTFrameDraw(frame, context); 

    for (id lineObj in (NSArray *)CTFrameGetLines(frame)) { 
     CTLineRef line = (CTLineRef)lineObj; 
     for (id runObj in (NSArray *)CTLineGetGlyphRuns(line)) { 
      CTRunRef run = (CTRunRef)runObj; 
      CGRect runBounds = CTRunGetImageBounds(run, context, CFRangeMake(0, 0)); 
      NSLog(@"bounds: %@", NSStringFromCGRect(runBounds)); 

      [[UIColor greenColor] set]; 
      CGContextFillRect(context, runBounds); 
     } 
    } 

    CFRelease(framesetter); 
    CFRelease(frame); 
    [attrString release]; 
} 

产地:

results

回答

10

这里是我想出了。这是完全准确的。

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++; 
} 
+0

最好使用此回答中显示的印刷边界而不是图像边界,因为前者不需要CGContext。另外,getImageBounds函数的速度要慢几个数量级。 – Cocoanetics 2011-06-19 03:11:10

+0

2小时后,我偶然发现了圣杯。谢谢。 – 2012-04-25 08:49:43

+0

上面的代码部分适用于我。返回错误结果的代码如下runBounds.origin.y = originins [lineIndex] .y + rect.origin.y; runBounds.origin.y - = descent;尽管之前的所有代码都会返回宽度,高度,origin.x ... origin.y的错误结果,但任何人都知道为什么?任何帮助赞赏。 – stefanosn 2012-11-06 00:19:12

3

之前,你可以绘制或计算每一行之前使用CTRunGetImageBounds()或CTLineDraw(),你需要设定到CGContextSetTextPosition调用(起始文本位置)。原因在于CTLine不知道从哪里开始绘制(或计算),它使用最后一个文本位置,所以您需要给它一个XY起点。为了获得帧的线阵列的起点,用一个点数组来调用CTFrameGetLineOrigins()。然后,在绘制或计算每一行之前 - 通过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); 
}