2011-03-06 90 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); 
}