[ios] Drawing unicode string in CGContext

There’s one big, big defect in CGContextShowText function. It only works for ASCII characters. There’s actually another way to draw in CGContext using NSString drawInRect: method, but it doesn’t work in my case because I need to calculate all the transform coordinates manually.

So after some googling I finally found an alternative using CTLine in CoreText framework. After tweaking the code here and there, finally it worked! To make things easy I’ll just paste the code here. Note that I use ARC feature on this code.

CTFontRef font = CTFontCreateWithName((__bridge CFStringRef)_fontName, _fontSize, &CGAffineTransformIdentity);
CFStringRef keys[] = { kCTFontAttributeName, kCTForegroundColorAttributeName };
CFTypeRef values[] = { font, _fillColor.CGColor };
CFDictionaryRef attributes = CFDictionaryCreate(kCFAllocatorDefault, (const void**)&keys, (const void**)&values, sizeof(keys[0]), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFDictionaryRef attributes = CFDictionaryCreate(kCFAllocatorDefault, (const void**)&keys, (const void**)&values, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFAttributedStringRef attrString = CFAttributedStringCreate(kCFAllocatorDefault, (__bridge CFStringRef)text, attributes);

CTLineRef line = CTLineCreateWithAttributedString(attrString);
CTLineDraw(line, context);

Edit: The deleted line causes crash in Release Build, so I had to change it

There’s good and bad things about CTLineDraw. The good thing is it respects most of CGContext states that work for CGContextShowText. Some CGContext functions that still work on CTLineDraw are CGContextSetTextPosition, CGContextSetTextMatrix, CGContextSetTextDrawingMode, CGContextSetStrokeColorWithColor, and CGContextSetLineWidth. So we can safely set the text position and text stroke.

Now the one function that doesn’t work is CGContextSetFillColorWithColor. The text color must be passed via the values array in above code. (see the _fillColor.CGColor part) The second thing that doesn’t work is more tricky. After calling CTLineDraw and you call CGContextGetTextPosition, the value doesn’t change from the value before you call CTLineDraw, while for CGContextShowText the cursor will move to the end of the string. In order to simulate the same situation with CGContextShowText, we can set the text position manually by adding the text width to the previous position.

float textWidth = CTLineGetTypographicBounds(line, NULL, NULL, NULL);
CGContextSetTextPosition(context, prevPosition.x + textWidth, prevPosition.y);

Of course it works for single line text. I didn’t try on multiline text because I handled multiline text by drawing it line by line.

So that’s how I handle the problem. If you know an easier solution to do the same thing, please do tell me.

This entry was posted in Programming and tagged , , , , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s