Font Editing

Problematic: For some iOS application that I worked on, I had to use specific fonts and I encountered 2 issues doing so:

- The vertical offset can be too high or too low and the text is misaligned

- For a specific case I have to replace a specific glyph with another

 I looked for different mac software to edit fonts and achieve this and I did not find one that was able to edit and save the font in it's original format for a reasonable price. 

During my researches I found Apple Font Tool Suite a command line tool that can do that by extracting the font infos into an XML file. Then it's possible to edit these properties into the XML file and reintegrate this back into the font.

A font contains different tables representing glyphs, metrics, character mapping, ligature, kerning... The list of all tables is available here.

The command that is used to extract/fuse infos is called ftxdumperfuser, here is the parameters that we will use:

     - '-t' (--table) Followed by the four character tag for the table to dump or fuse

     - '-A d' to dump the table into an XML file

     - '-A f' to fuse the XML data into the font

1. Replace glyph into a font

I'm working with the font Futura.ttc which contains 4 variants:

     - Condensed ExtraBold

     - Condensed Medium

     - Medium Italic

     - Medium

In this case you will got an XML output for each variants.

What I will do here, is replace the lowercase glyphs with the uppercase glyphs. (We have an app that share the same code base for different clients, and one of our clients asked us for a version with the Futura font that use uppercase everywhere in the app). 

To achieve this we will replace the glyph data and update the metric (the new glyph can take more or less space).

font-lowercase.png
font-uppercase-glyph.png
font-uppercase-metrics.png

 These 3 screenshots are showing: 

     - The original font in lowercase

     - The modified font just be replacing lowercase glyphs with uppercase glyphs

     - The modified font after adjusting the metrics

 

 

There is 2 tables to access to get these infos:

     - 'glyf'  The glyph outline table

     - 'hmtx' The horizontal metric table

 
// to extract from the font
ftxdumperfuser -t glyf -p -G -A d Futura.ttc
ftxdumperfuser -t hmtx -p -G -A d Futura.ttc

I made a copy of the font and renamed it to FuturaUppercase.ttc and got this after the extract:

FuturaUppercase.png

You can find the glyph data and replace them this the data you want, here is an example with the 'A' uppercase and lowercase: 

 
 

And the do the same with the metric values: 

 

Once these replacements are done, you can fuse this back into the font: 

// to fuse to the font
ftxdumperfuser -t hmtx -A f Futura.ttc
ftxdumperfuser -t glyf -A f Futura.ttc

2. Change vertical offset

If the font is displayed too high or too low, you wan want to change the vertical offset. To achieve this, you will extract the 'hhea' table and the property to edit is the  'ascender value'.

// to extract the font
ftxdumperfuser -t hhea -A d Futura.ttc

// to fuse the font
ftxdumperfuser -t hhea -A f Futura.ttc

Try different ascender values till you got the baseline you expect.  I had to almost divide mine by 2 to get the right alignement.

3. Rename the font

If you want to be able to use the font you edited and the original font in the same iOS project you need to rename the font with a different name. There is also a table for that. 

The table 'name' includes the human readable names.


FuturaUppercase-Medium

// to extract from the font
ftxdumperfuser -t name -A d Futura.ttc

// to fuse to the font
ftxdumperfuser -t name -A f Futura.ttc

iOS use the postscript name to load a font, so that the one you want to edit: 

External Link

http://stackoverflow.com/questions/7535498/uibutton-custom-font-vertical-alignment

 

UIAppearance proxy with custom view

Problematic:  I created a custom iOS component to display scores with an icon associated to what it represent. Now I have the need to customize the appearance of this component throughout my application.

My component KWIllustratedLabel is a UILabel subclass that just add an icon on the left and a gradient background with a border, as show in the following.

The KWIllustratedLabel can be used to display two different kind of scores into the application:

  •  Points which represent the user experience, are in yellow with a lightning bolt.
  • Credits  that the user win in the game and can be spent, represented in green with a money bag.

There is also some convenient methods and properties to keep these labels consistent across the application. Here is an extract from the header file.

 

@interface KWIllustratedLabel : UILabel

@property (nonatomic, assign) KWIllustratedLabelType type;
@property (nonatomic, assign) KWIllustratedLabelShadowType shadowType;
@property (nonatomic, assign) KWIllustratedLabelResizingMode resizingMode;
@property (nonatomic, assign) KWIllustratedLabelSize size;
@property (nonatomic, assign) CGFloat minWidth;
@property (nonatomic, assign) CGFloat maxWidth;

- (void)setTextFromScore:(NSNumber *)score;
- (void)setTextFromScoreWithPlusSign:(NSNumber *)score;

@end

Everything is drawn with vectors and can be resized, but that's not the subject here. What we want is to offer more flexibility for the pictograms, so we are providing an alternative artwork for each label type:

  • Points we can have a star instead of the lightning bolt
  • Credits can hide our company logo on the money bag

To achieve this I added a Boolean named useAlternativeIcon that will enable the usage of the new icon depending on the type of the label.

1. Customize properties with UIAppearence

To add support UIAppearance with our custom component we need to:

  1. Subclass UIView (conforms to the UIAppearance protocol)
  2. Expose style attributes as properties and tag them with the UI_APPEARANCE_SELECTOR macro

My class is a UILabel subclass so it also inherit from UIView, by adding the macro on this new attribute we get UIApparence working for free!

@property (nonatomic, strong) NSNumber *useAlternativeIcon UI_APPEARANCE_SELECTOR;

And in the appearance configuration we can use:

KWIllustratedLabel *illustratedLabelAppearance = [KWIllustratedLabel appearance];
illustratedLabelAppearance.useAlternativeIcon = @YES;

But what if we want to use the alternative artwork only for a specific type of illustrated label. Let's say for the Points but not for the Credits for example. It could be useful to be able to specify a parameter that will define when to apply the appearance.

2. Customize parametrized properties (i.e. use alternative artwork only for a specific type)

The documentation is really brief for this, it just says "To participate in the appearance proxy API, tag your appearance property accessor methods in your header with UI_APPEARANCE_SELECTOR."

Appearance property accessor methods must be of the form:

- (PropertyType)propertyForAxis1:(IntegerType)axis1 axis2:(IntegerType)axis2 axisN:(IntegerType)axisN;
- (void)setProperty:(PropertyType)property forAxis1:(IntegerType)axis1 axis2:(IntegerType)axis2 axisN:(IntegerType)axisN;

I followed this and created the methods as you can see below. But then the documentation does not specify how to implement them and when these methods are called by the framework. After some research I found that the setter is called just before your view is added to the window and layout it's content.

/**
 * Defines the label icon appearance for a specific `KWIllustratedLabelType`.
 * @param illustratedLabelType The type of illustrated label that we want to set the alternative representation for.
 */
- (void)setUseAlternativeIcon:(NSNumber *)useAlternativeIcon forLabelType:(KWIllustratedLabelType)illustratedLabelType UI_APPEARANCE_SELECTOR;

/**
 * Returns a boolean that indicates if the label icon appearance is specific for a given `KWIllustratedLabelType`.
 * @param illustratedLabelType The type of illustrated label that we want to get the alternative representation from.
 * @return A number representing a boolean indicating if the label use the alternative representation or not.
 */
- (NSNumber *)useAlternativeIconForLabelType:(KWIllustratedLabelType)illustratedLabelType UI_APPEARANCE_SELECTOR;

You can just save that value in an NSDictionary for example, so you have the value when you draw your views or if the getter gets called later. The internal implementation is really straight forward. Here is the implementation for the getter/setter with the axis and an internal getter for the property that takes care about the setting provided by the proxy.

- (void)setUseAlternativeIcon:(NSNumber *)alternativeIcon forLabelType:(KWIllustratedLabelType)illustratedLabelType {
    if (!self.appearanceStorage) {
        self.appearanceStorage = [NSMutableDictionary dictionary];
    }
    // Set the appearance for the type inside our appearence storage
    NSString *storageKey = [NSString stringWithFormat:@"icon-%u", illustratedLabelType];
    [self.appearanceStorage setValue:alternativeIcon forKey:storageKey];
}

- (NSNumber *)useAlternativeIconForLabelType:(KWIllustratedLabelType)illustratedLabelType {
    // Return the appearance for the type from our appearence storage
    NSString *storageKey = [NSString stringWithFormat:@"icon-%u", illustratedLabelType];
    return [self.appearanceStorage valueForKey:storageKey];
}

// Private accessor that takes care of proxy value if set
- (NSNumber *)useAlternativeIconUsingAppearance {
    // Use the class value if set, else use the proxy value
    if (useAlternativeIcon) {
        return useAlternativeIcon;
    } else {
        return [self useAlternativeIconForLabelType:self.type];
    }
}

So now we can choose for which type of badge we want to use this setting and it's almost as simple as using the setter.

KWIllustratedLabel *illustratedLabelAppearance = [KWIllustratedLabel appearance];
[illustratedLabelAppearance setUseAlternativeIcon:@YES forLabelType:KWIllustratedLabelTypePoints];

UIAppearance is really powerfull and convinient to customize an application appearance. If you are writing custom components, you should be thinking about the compatibility with this framework.

External Links

http://petersteinberger.com/blog/2013/uiappearance-for-custom-views

http://logicalthought.co/blog/2012/10/8/uiappearance-and-custom-views