Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does CoreText support Small Caps?

Tags:

ios

core-text

Does CoreText have any facility for selecting a SmallCaps variant of a font, or for synthesizing small caps if the font doesn't have that feature? I can't find anything in the CoreText documentation that talks about small caps, though there are facilities for dealing with font variations/features. Has anyone done anything similar to this?

like image 532
Lily Ballard Avatar asked Jan 26 '11 21:01

Lily Ballard


2 Answers

As no one here has provided a Swift 4 sample, I'm just going to include playground code to display some small caps text in a UILabel:

//: Playground - noun: a place where people can play    
import UIKit
import CoreGraphics

let pointSize : CGFloat = 24
let fontDescriptor = UIFont(name: "HoeflerText-Regular", size: pointSize)!.fontDescriptor



let fractionFontDesc = fontDescriptor.addingAttributes(
    [
        UIFontDescriptor.AttributeName.featureSettings: [
            [
                UIFontDescriptor.FeatureKey.featureIdentifier: kLetterCaseType,
                UIFontDescriptor.FeatureKey.typeIdentifier: kSmallCapsSelector
            ]
        ]
    ] )

let label = UILabel(frame: CGRect(x: 0, y: 0, width: 500, height: 100))

label.font = UIFont(descriptor: fractionFontDesc, size:pointSize)
label.text = "Montpelier, Vermont" 
like image 133
Glenn Howes Avatar answered Oct 12 '22 04:10

Glenn Howes


I decided to answer here to provide a more complete solution to anyone trying to solve this issue, as the info here is incomplete.

This solution uses the iOS 7 UIFontDescriptor as I am now dropping support for iOS 6.

As Anthony Mattox pointed out, the system font values (which are listed as 3 and 3 but should be noted to actually be kLetterCaseType and kSmallCapsSelector, you should not refer to an enum by its number), will not work for custom fonts. I am not sure whether this is the case for all custom fonts or just some, but I found this to be the case with mine.

When digging into the declaration of both of these enum values, you can actually see that they are deprecated anyway and presumably only work for the few system fonts that support small caps. After logging the available attributes for my custom font as outlined by Anthony, I found the 2 correct attributes to use for custom fonts. They are kLowerCaseType and kLowerCaseSmallCapsSelector. I believe that this combination is the only other option so for any font you attempt to use, it will be one or the other.

I wrote some category methods to encapsulate this functionality for both cases:

- (UIFont *) smallCapSystemFont
{
    UIFontDescriptor *descriptor = [self fontDescriptor];
    NSArray *array = @[@{UIFontFeatureTypeIdentifierKey : @(kLetterCaseType),
                         UIFontFeatureSelectorIdentifierKey : @(kSmallCapsSelector)}];
    descriptor = [descriptor fontDescriptorByAddingAttributes:@{UIFontDescriptorFeatureSettingsAttribute : array}];
    return [UIFont fontWithDescriptor:descriptor size:0];
}

- (UIFont *) smallCapCustomFont
{
    UIFontDescriptor *descriptor = [self fontDescriptor];
    NSArray *array = @[@{UIFontFeatureTypeIdentifierKey : @(kLowerCaseType),
                         UIFontFeatureSelectorIdentifierKey : @(kLowerCaseSmallCapsSelector)}];
    descriptor = [descriptor fontDescriptorByAddingAttributes:@{UIFontDescriptorFeatureSettingsAttribute : array}];
    return [UIFont fontWithDescriptor:descriptor size:0];
}

You use these by creating a font with the correct name and size and then calling one of these methods on it which will return a small cap version of that font. You will need to figure out the correct method to use for whatever small caps font you decide to use.

There is probably a clever way to figure out which one to use programmatically at runtime by checking for available types (even by just analyzing the results of that font properties array), but I have not bothered to do so as I am only using a few different fonts and a manual check is suitable for me.

Edit:

One thing I noticed is that numbers are handled separately. If you want numbers to be small capped too (which actually seems to be called "Old-style numbers" in the case of most fonts that support it), you will need that explicitly as an attribute.

Looks like it is the same for both supporting system fonts and custom fonts, unlike letters.

You would just add this dictionary to each of the arrays above:

@{UIFontFeatureTypeIdentifierKey : @(kNumberCaseType),
                         UIFontFeatureSelectorIdentifierKey : @(kLowerCaseNumbersSelector)}

Once again, for this to work the font itself actually needs to support this attribute.

like image 26
Dima Avatar answered Oct 12 '22 02:10

Dima