UILabel’s font property accepts UIFonts – but strange enough there’s no way to get a custom loaded CGFont (from a ttf or otf file) converted into such an UIFont. You’re stuck with the iPhone’s pre-installed fonts – at least when you have to support iOS 3.0 devices.
After googling a bit and searching Stackoverflow I found the solutions presented there not ideal or great, but too heavy weight.
So I inherited UILabel with a very lean custom class UILabelWithCGFont and overloaded it’s drawTextInRect: method like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | -(void)drawTextInRect:(CGRect)rect { MRLogD(@"(%f,%f) (%f,%f)", rect.origin.x, rect.origin.y, rect.size.width, rect.size.height); if ( _CGFont == NULL ) { [super drawTextInRect:rect]; return; } NSAssert(_mapping != NULL, @"Mapping function pointer not set."); // prepare the target graphics context. const CGContextRef ctx = UIGraphicsGetCurrentContext(); CGContextSaveGState(ctx); { // prepare the glyphs array to draw const NSString *txt = self.text; const size_t glyphCount = txt.length; CGGlyph glyphs[glyphCount]; { // turn the string txt into glyphs (indices into the font): // give non-allocating unicode character retrieval a try: const UniChar *raw_unichars = CFStringGetCharactersPtr( (CFStringRef)txt ); const UniChar *unichars = raw_unichars == NULL ? malloc( glyphCount * sizeof(UniChar) ) : raw_unichars; NSAssert(unichars != NULL, @"unichars not allocated"); if ( raw_unichars == NULL ) CFStringGetCharacters( (CFStringRef)txt, CFRangeMake(0, txt.length), (UniChar *)unichars ); for ( int i = glyphCount - 1; i >= 0; i-- ) glyphs[i] = _mapping(unichars[i]); if ( raw_unichars == NULL ) free( (void *)unichars ); } CGContextSetFont(ctx, _CGFont); CGContextSetFontSize(ctx, self.font.pointSize); CGContextSetTextMatrix( ctx, CGAffineTransformMake(1.0, 0.0, 0.0, -1.0, 0.0, 0.0) ); // first print 'invisible' to measure size: CGContextSetTextDrawingMode(ctx, kCGTextInvisible); const CGPoint pre = CGContextGetTextPosition(ctx); CGContextShowGlyphs(ctx, glyphs, glyphCount); const CGPoint post = CGContextGetTextPosition(ctx); // restore text position CGContextSetTextPosition(ctx, pre.x, pre.y); // centered horizontal + vertical: NSAssert( (int)rect.origin.x == 0, @"origin.x not zero" ); NSAssert( (int)rect.origin.y == 0, @"origin.y not zero" ); NSAssert(self.baselineAdjustment == UIBaselineAdjustmentAlignCenters, @"vertical alignment not 'center'"); NSAssert(self.textAlignment == UITextAlignmentCenter, @"horizontal alignment not 'center'"); const CGPoint p = CGPointMake( ( rect.size.width - (post.x - pre.x) ) / 2, (rect.size.height + self.font.pointSize + pre.y) / 2 ); // finally render it to the graphics context: CGContextSetTextDrawingMode(ctx, kCGTextFill); CGContextSetFillColorWithColor(ctx, self.textColor.CGColor); CGContextShowGlyphsAtPoint(ctx, p.x, p.y, glyphs, glyphCount); } CGContextRestoreGState(ctx); } |
Usage: Just turn the UILabel instances in Interface Builder into UILabelWithCGFont and implement the UIViewController::viewDidLoad method like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | CGGlyph unicode2glyphDeutscheDruckschrift(UniChar c) { if ( '0' < = c && c <= '9' ) return c + (16 - '0'); if ( 'A' <= c && c <= 'Z' ) return c + (32 - 'A'); if ( 'a' <= c && c <= 'z' ) return c + (58 - 'a'); return 0; } -(void)viewDidLoad { [super viewDidLoad]; ... [fontLabel setFontFromFile:@"DeutscheDruckschrift" ofType:@"ttf" mapping:unicode2glyphDeutscheDruckschrift]; ... } |
See this github gist for the complete implementation.
The mapping from Unicode character codes to glyph indices (inside the font description) currently is done via a C mapping function you have to provide a function pointer for. A later implementation could map the unicode character code to the glyph name and leverage CGFontGetGlyphWithGlyphName and render the custom mapping function obsolete.
Comments 3
Great post, amazed, no one has thanked you 🙂
do you know any way for general string -> glyph conversion?
Posted 02 Jun 2011 at 1:04 am ¶Thanks a lot!
http://github.com/zynga/FontLabel does a complete char -> glyph.
Posted 02 Jun 2011 at 12:07 pm ¶this is great!
Posted 03 Jun 2011 at 5:26 pm ¶thanks
Trackbacks & Pingbacks 1
[…] Well, if you want to draw a text in a label with a custom font, like CGFontRef, it is not quite simple. I have googled a little and found a solution for you which implies you to inherit UILabel class and rewrite drawTextInRect method for this. All the necessary information for you is here. […]
Post a Comment