views:

25284

answers:

12

I would like to have an app include a custom font for rendering text, load it, and then use it with standard UIKit elements like UILabel. Is this possible?

I found these links:

but these would require me to render each glyph myself, which is a bit too much like hard work, especially for multi-line text.

I've also found posts that say straight out that it's not possible, but without justification, so I'm looking for a definitive answer.


EDIT - failed -[UIFont fontWithName:size:] experiment

I downloaded Harrowprint.tff (downloaded from here) and added it to my Resources directory and to the project. I then tried this code:

UIFont* font = [UIFont fontWithName:@"Harrowprint" size:20];

which resulted in an exception being thrown. Looking at the TTF file in Finder confirmed that the font name was Harrowprint.


EDIT - there have been a number of replies so far which tell me to read the documentation on X or Y. I've experimented extensively with all of these, and got nowhere. In one case, X turned out to be relevant only on OS X, not on iPhone. Consequently I am setting a bounty for this question, and I will award the bounty to the first person who provides an answer (using only documented APIs) who responds with sufficient information to get this working on the device. Working on the simulator too would be a bonus.


EDIT - it appears that the bounty auto-awards to the answer with the highest nunber of votes. Interesting. No one actually provided an answer that solved the question as asked - the solution that involves coding your own UILabel subclass doesn't support word-wrap, which is an essential feature for me - though I guess I could extend it to do so.

+4  A: 

Yes, you can include custom fonts. Refer to the documentation on UIFont, specifically, the fontWithName:size: method.

1) Make sure you include the font in your resources folder.

2) The "name" of the font is not necessarily the filename.

3) Make sure you have the legal right to use that font. By including it in your app, you're also distributing it, and you need to have the right to do that.

August
Have you actually done this successfull? I can't make it work, and googling finds only other people who have tried it and failed.
Airsource Ltd
Yes. I have. Again, make sure you're using the proper font name.
August
When you say "The "name" of the font is not necessarily the filename" ... how do find the correct name?
Keith Fitzgerald
Yeah, this answer is wrong. You can't do this out of the box.
Jonathan Sterling
Does this simple solution work? Especially for iPhone 3.0 OS?
Raj
+6  A: 

I have done this like this:

Load the font:

- (void)loadFont{
  // Get the path to our custom font and create a data provider.
  NSString *fontPath = [[NSBundle mainBundle] pathForResource:@"mycustomfont" ofType:@"ttf"]; 
  CGDataProviderRef fontDataProvider = CGDataProviderCreateWithFilename([fontPath UTF8String]);

  // Create the font with the data provider, then release the data provider.
  customFont = CGFontCreateWithDataProvider(fontDataProvider);
  CGDataProviderRelease(fontDataProvider); 
}

Now, in your drawRect:, do something like this:

-(void)drawRect:(CGRect)rect{
    [super drawRect:rect];
    // Get the context.
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextClearRect(context, rect);
    // Set the customFont to be the font used to draw.
    CGContextSetFont(context, customFont);

    // Set how the context draws the font, what color, how big.
    CGContextSetTextDrawingMode(context, kCGTextFillStroke);
    CGContextSetFillColorWithColor(context, self.fontColor.CGColor);
    UIColor * strokeColor = [UIColor blackColor];
    CGContextSetStrokeColorWithColor(context, strokeColor.CGColor);
    CGContextSetFontSize(context, 48.0f);

    // Create an array of Glyph's the size of text that will be drawn.
    CGGlyph textToPrint[[self.theText length]];

    // Loop through the entire length of the text.
    for (int i = 0; i < [self.theText length]; ++i) {
        // Store each letter in a Glyph and subtract the MagicNumber to get appropriate value.
        textToPrint[i] = [[self.theText uppercaseString] characterAtIndex:i] + 3 - 32;
    }
    CGAffineTransform textTransform = CGAffineTransformMake(1.0, 0.0, 0.0, -1.0, 0.0, 0.0);
    CGContextSetTextMatrix(context, textTransform);
    CGContextShowGlyphsAtPoint(context, 20, 50, textToPrint, [self.theText length]);
}

Basically you have to do some brute force looping through the text and futzing about with the magic number to find your offset (here, see me using 29) in the font, but it works.

Also, you have to make sure the font is legally embeddable. Most aren't and there are lawyers who specialize in this sort of thing, so be warned.

Genericrich
Genericrich - this is certainly useful information, but it is basically a duplicate of BNicholson's post in my first link. It also requires rendering every Glyph - which is exactly what I don't want to do.
Airsource Ltd
What I get for answering in a hurry!I've found it works pretty well. YMMV.
Genericrich
I'm sure it does work well, but I (really really) don't want to write my own layout engine to handle everything UILabel and UITextView currently do for me - particularly word wrapping, positioning, and editing.
Airsource Ltd
AFAICS, this only works with uppercase characters, hence the 'uppercaseString' call.
Martin Cote
I'm downvoting since the "magic number" trick is a pure hack that doesn't really work. The real solution is to get the cmap table from the TTF file.
Martin Cote
A: 

Look up ATSApplicationFontsPath

A simple plist entry that allows you to include the font file(s) in your app resources folder and they "just work" in your app.

matt
Have you tried this on device? I had no joy, and googling suggests that this plist entry isn't supported on iPhone, even though it's documented for iPhone OS.
Airsource Ltd
Sorry, I am only using it in an OS X app at the moment.
matt
+1  A: 

It's not out yet, but the next version of cocos2d (2d game framework) will support variable length bitmap fonts as character maps.

http://code.google.com/p/cocos2d-iphone/issues/detail?id=317

The author doesn't have a nailed down release date for this version, but I did see a posting that indicated it would be in the next month or two.

John
+37  A: 

I created a simple module that extends UILabel and handles loading .ttf files. I released it opensource under the Apache license and put it on github here: git://github.com/zynga/FontLabel.git

The important files are FontLabel.h and FontLabel.m.

It uses some of the code from Genericrich's answer above.

Browse the source here: http://github.com/zynga/FontLabel/tree/master

commanda
I've tried using your code but it crashes quite often depending on the font. For example, try using the African or Tiki fonts from here http://www.fontspace.com/category/tiki.
4thSpace
This library works great.I need help with the vertical spacing though. Can't figure out how to do it.So I have this message.numberOfLines = 3; How do I control the vertical spacing between line 1, and line 2 and line 3?Thank you,Tee
teepusink
If this method is followed, we have to implement the drawing logic. What if we want to display the font directly on a UILabel instance instead of a custom sub-class of UILabel?
Raj
+12  A: 

The only way I've been able to successfully load custom UIFonts is via the private GraphicsServices framework.

The following will load all the .ttf fonts in the application's main bundle:

BOOL GSFontAddFromFile(const char * path);
NSUInteger loadFonts()
{
 NSUInteger newFontCount = 0;
 for (NSString *fontFile in [[NSBundle mainBundle] pathsForResourcesOfType:@"ttf" inDirectory:nil])
  newFontCount += GSFontAddFromFile([fontFile UTF8String]);
 return newFontCount;
}

Once fonts are loaded, they can be used just like the Apple-provided fonts:

NSLog(@"Available Font Families: %@", [UIFont familyNames]);
[label setFont:[UIFont fontWithName:@"Consolas" size:20.0f]];

GraphicsServices can even be loaded at runtime in case the API disappears in the future:

#import <dlfcn.h>
NSUInteger loadFonts()
{
 NSUInteger newFontCount = 0;
 NSBundle *frameworkBundle = [NSBundle bundleWithIdentifier:@"com.apple.GraphicsServices"];
 const char *frameworkPath = [[frameworkBundle executablePath] UTF8String];
 if (frameworkPath) {
  void *graphicsServices = dlopen(frameworkPath, RTLD_NOLOAD | RTLD_LAZY);
  if (graphicsServices) {
   BOOL (*GSFontAddFromFile)(const char *) = dlsym(graphicsServices, "GSFontAddFromFile");
   if (GSFontAddFromFile)
    for (NSString *fontFile in [[NSBundle mainBundle] pathsForResourcesOfType:@"ttf" inDirectory:nil])
     newFontCount += GSFontAddFromFile([fontFile UTF8String]);
  }
 }
 return newFontCount;
}
rpetrich
note to those wondering, this does work, but you'll need to call loadFonts right before using the font -- they don't seem to stay loaded throughout the application
pixel
In OS 3.0 you definitely need to use the second second example, don't forget the import. Worked pretty well for me.
Jasconius
Another note -- this is a private framework, and dynamically loads it... both of which will likely stop the acceptance of your app into the AppStore.
pixel
Yup, the answer clearly states it's private.
rpetrich
What is the conclusion? What alternatives to this method?
Raj
It's very VERY private. If you can deal with drawing the text yourself, you can use `CGFontCreateWithDataProvider`. `UIAppFonts` can be used on 3.2+
rpetrich
A: 

Maybe the author forgot to give the font a Mac FOND name?

  1. Open the font in FontForge then go to Element>Font Info
  2. There is a "Mac" Option where you can set the FOND name.
  3. Under File>Export Font you can create a new ttf

You could also give the "Apple" option in the export dialog a try.

DISCLAIMER: I'm not a IPhone developer!

ByteNirvana
+2  A: 

I have been trying out the various suggestions on this page on Iphone OS 3.1.2 and these are my conclusions:

Simply using [UIFont fontWithName:size:] with the fonts in the Resources directory will not work, even if the FOND name is set using FontForge.

[UIFont fontWithName:size:] will work if the fonts are loaded first using GSFontAddFromFile. But GSFontAddFromFile is not part of Iphone OS 3.1.2 so it has to be dynamically loaded as described by rpetrich.

Jacob Wallström
@JacobSo have you found out any solution?
Raj
Yes, the last paragraph in my reply above describes the solution. Load the fonts using GSFontAddFromFile using rpetrich's method.
Jacob Wallström
A: 

Here is another variable-length font system for iPhone: http://ambiguiti.es/2009/05/easyglyph-an-iphone-font-system/

Alexei
+25  A: 

iPhone 3.2 supports it, although it's iPad only
From the What's New in iPhone OS 3.2 doc:

Custom Font Support
Applications that want to use custom fonts can now include those fonts in their application bundle and register those fonts with the system by including the UIAppFonts key in their Info.plist file. The value of this key is an array of strings identifying the font files in the application’s bundle. When the system sees the key, it loads the specified fonts and makes them available to the application.

Once the fonts have been set in the Info.plist, you can use your custom fonts as any other font in IB or programatically.

There is an ongoing thread on Apple Developer Forums:
https://devforums.apple.com/thread/37824 (login required)

EDIT: Obviously, iOS 4 supports this as well.

Sam V
Good source of information. But how to implement it in older OS / for iPhone.
Raj
FontLabel (1st answer) is what I use for older OS.
Sam V
Here's a step by step tutorial for iOS4: http://blog.beefyapps.com/2010/06/custom-fonts-in-ios-4/
paul_sns
A: 

I tried the dynamic things, and it works like a charm. Even on device.

Blockquote Another note -- this is a private framework, and dynamically loads it... both of which will likely stop the acceptance of your app into the AppStore. – pixel Dec 1 '09 at 22:05

extern void * dlsym(void * __handle, const char * __symbol) is really a private Methode?

Second question: Is there someone who posted on AppleDStore? And have return about this?

Seems like the use will be available with 0S4 since you can already doing this with 3.2 (Ipad) with the .plist methode (i don't remember wich plist-name but it tried and work fine, but i'm workin on pod :S)

loïc
Nice... but not much point if you can't submit it!
ing0
Indeed. Apple are now using an automated tool to scan for private APIs. Have been since late last year,
Airsource Ltd
+4  A: 

There is a simple way to use custom fonts in iOS 4.

  1. Add your font file (for example, Chalkduster.ttf) to Resources folder of the project in XCode.
  2. Open info.plist and add a new key called UIAppFonts. The type of this key should be array.
  3. Add your custom font name to this array including extension ("Chalkduster.ttf").
  4. Now you can use [UIFont fontWithName:@"Chalkduster" size:16] in your application.

Unfortunately, IB doesn't allow to initialize labels with custom fonts. See this question to solve this problem. My favorite solution is to use custom UILabel subclass:

@implementation CustomFontLabel

- (id)initWithCoder:(NSCoder *)decoder
{
    if (self = [super initWithCoder: decoder])
    {
        [self setFont: [UIFont fontWithName: @"Chalkduster" size: self.font.pointSize]];
    }
    return self;
}

@end
alexey
This seems to be supported back to 3.2, not just 4.0+
Jesse Rusak