Question

I need to draw text into a CGContext and want to use CATextLayer to set the text. Can anyone give me some steps as to how it'll be done. I've written some code for it, but don't know the correctness of it (since I'm new to iPhone Development).

Some basic questions : 1. How to obtain position and size of text for setting the CATextLayer frame 2. The text is somehow coming out inverted which I've fixed using CGAffineTransform, but its a hacky fix and I want to know the actual reason why the text is coming out inverted. 3. I also need to set the text border width and color for which I'm using NSAttributedString, but setting the text size (increasing or decreasing) does not reflect on the screen.

EDIT 1 :

I've updated the code as per your recommendations, but the text is not resizing even after using CTFontRef. Also, on the iPhone the text is getting truncated a little bit from the top. Any idea what's being set incorrectly? When I checked the values I'm getting from getTypographicBounds, they're all 0 (origin.x, origin.y, width and height).

The getTypographicBounds method :

width = CTLineGetTypographicBounds(m_line, &ascent, &descent, &leading);

// Return text bounds
return CGRectMake(0, 0, width, ascent + descent);

The modified code :

CATextLayer*    text      = [[CATextLayer alloc] init];
CGColorSpaceRef rgbColor  = CGColorSpaceCreateDeviceRGB();
CGColorRef rgb            = CGColorCreate(rgbColor, m_fill_color);
text.foregroundColor      = rgb;
text.frame                = CGRectMake([self getTypographicBounds].origin.x, [self getTypographicBounds].origin.y, [self getTypographicBounds].size.width + 2*m_padding, [self getTypographicBounds].size.height + 2*m_padding);
text.wrapped              = YES;

    NSMutableDictionary *stringAttributes = [NSMutableDictionary dictionary];

    CTFontRef aCFFont = CTFontCreateWithName ((CFStringRef)m_font_name, 30.0, NULL);

    // Set a negative width so we get both stroke and fill to show
    [stringAttributes setObject: (NSObject*)aCFFont forKey: (id)kCTFontNameAttribute];
    [stringAttributes setObject: [NSNumber numberWithFloat: -3 ] forKey: (id)kCTStrokeWidthAttributeName];
    [stringAttributes setObject: [UIColor whiteColor] forKey: (id)kCTStrokeColorAttributeName];

    NSAttributedString *stringToDraw = [[NSAttributedString alloc] initWithString:m_text
                                                                       attributes:stringAttributes];

    text.string = (NSString*)stringToDraw;
    [text drawInContext:ctx];

EDIT 2 : The wiki example uses CTLine, I'm using CATextLayer. Reason :: CATextLayer lets me modify text during runtime to show Chinese, Japanese, Hindi, Russian and all languages with different scripts. Also, CATextLayer seems to render with more clarity than CoreText.

The issue I'm facing right now is that the text being displayed by [text drawInContext:ctx] line is truncating from the top. I checked the frame and text font size. Font size is 16 px and frame height is 17.768, so I don't know why it's truncating.

// Ensure CTLine is up-to-date
if (m_is_dirty)
    [self recomputeLine];

// Get text metrics
CGFloat width;
CGFloat ascent;
CGFloat descent;
CGFloat leading;

width = CTLineGetTypographicBounds(m_line, &ascent, &descent, &leading);

// Position text so that top of text aligns with top of layer
CGContextSetTextMatrix(ctx, CGAffineTransformIdentity );

// Setup text drawing style
CGContextSetRGBFillColor   (ctx, m_fill_color  [0], m_fill_color  [1], m_fill_color  [2], 1.0);
CGContextSetRGBStrokeColor (ctx, m_stroke_color[0], m_stroke_color[1], m_stroke_color[2], 1.0);
CGContextSetFont           (ctx, m_font        );
CGContextSetFontSize       (ctx, m_font_size   );
CGContextSetLineWidth      (ctx, m_stroke_width);

CATextLayer*    text      = [[CATextLayer alloc] init];
CGColorSpaceRef rgbColor  = CGColorSpaceCreateDeviceRGB();
CGColorRef rgb            = CGColorCreate(rgbColor, m_fill_color);
text.foregroundColor      = rgb;
text.fontSize = m_font_size;
text.font = m_font;
text.frame                = CGRectMake([self getTypographicBounds].origin.x, [self getTypographicBounds].origin.y, [self getTypographicBounds].size.width, [self getTypographicBounds].size.height);
text.wrapped              = YES;

NSMutableDictionary *stringAttributes = [NSMutableDictionary dictionary];


// Set a negative width so we get both stroke and fill to show
[stringAttributes setObject: [UIFont fontWithName:m_font_name size:m_font_size] forKey: (id)kCTFontNameAttribute];
[stringAttributes setObject: [NSNumber numberWithFloat: -3 ] forKey: (id)kCTStrokeWidthAttributeName];
[stringAttributes setObject: [UIColor whiteColor] forKey: (id)kCTStrokeColorAttributeName];

NSAttributedString *stringToDraw = [[NSAttributedString alloc] initWithString:m_text
                                                                       attributes:stringAttributes];

text.string = (NSString*)stringToDraw;        
[text drawInContext:ctx];

Thanks in advance

Nick

Was it helpful?

Solution

It is because of the flipped coordinate space used for CGContexts. For a full explanation (with diagrams) see here.

EDIT: To control the position translate your context before drawing the text:

CGContextTranslateCTM(context, x, y);

EDIT 2:

Looks like you need to use a CTFontRef, not a UIFont. For an example, see here (see post by Gordon Apple Wed Jun 23 10:40:23 2010).

EDIT 3:

Unless you specifically need to use NSAttributedString, I would recommend using the much simpler NSString drawInRect:withFont:lineBreakMode:alignment: method. It creates a CATextLayer for you, but is much simpler to use:

    UIFont *font = [UIFont fontWithName:m_font_name size:30.f];
    [m_text drawInRect:text.frame  withFont:font lineBreakMode:UILineBreakModeWordWrap alignment:UITextAlignmentLeft];

EDIT 4:

Try setting your dictionary like this:

    CTFontRef ctFont = CTFontCreateWithName((__bridge CFStringRef)fontName, pointSize,NULL);
    CGColorRef color = textColor.CGColor;

    NSDictionary *attributesDict = [NSDictionary dictionaryWithObjectsAndKeys:
                                    (__bridge id)ctFont, (id)kCTFontAttributeName,
                                    color, (id)kCTForegroundColorAttributeName,nil];
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top