How to use UIFontDescriptor when CTFeatureTypeIdentifier is not available

378 views Asked by At

I use the following snippet to get the font features:

let font = UIFont.systemFont(ofSize: 16)
let features: NSArray = CTFontCopyFeatures(font)!
print("properties = \(features)")

So If I see this:

{
CTFeatureTypeExclusive = 1;
CTFeatureTypeIdentifier = 22;
CTFeatureTypeName = "Text Spacing";
CTFeatureTypeSelectors =         (
                {
        CTFeatureSelectorDefault = 1;
        CTFeatureSelectorIdentifier = 7;
        CTFeatureSelectorName = "No Change";
    },
                {
        CTFeatureSelectorIdentifier = 8;
        CTFeatureSelectorName = "No Kerning";
    }
);

I can translate that into Swift as:

 let fontDescriptorFeatureSettings = [
            [ UIFontDescriptor.FeatureKey.featureIdentifier : 22,
              UIFontDescriptor.FeatureKey.typeIdentifier : 8], ]

This successfully disables kerning for me.

In the same snippet, I see:

{
CTFeatureOpenTypeTag = cv05;
CTFeatureSampleText = I;
CTFeatureTooltipText = "\U00a9 2015-2022 Apple Inc. All rights reserved.";
CTFeatureTypeExclusive = 1;
CTFeatureTypeName = "Seriffed Capital I";
CTFeatureTypeSelectors =         (
                {
        CTFeatureOpenTypeValue = 0;
        CTFeatureSelectorDefault = 1;
        CTFeatureSelectorName = Off;
    },
                {
        CTFeatureOpenTypeValue = 1;
        CTFeatureSelectorName = On;
    }
);

How do I translate that into Swift to get a Seriffed 'I'?

The issue for me is the absence of:

CTFeatureTypeIdentifier

1

There are 1 answers

5
matt On BEST ANSWER

First of all, your kerning disablement code is wrong, in two ways:

  • In the very confusing UIFontDescriptor.FeatureKey namespace, .featureIdentifier and .typeIdentifier are deprecated, replaced respectively by .type and .selector. (I filed a bug on those old names years ago, and it looks like Apple heard me and changed them. Hooray!)

  • For the values corresponding to those keys, don't use raw numbers if you don't have to; that's illegible and fragile. These things often have constant names.

So when you say

let fontDescriptorFeatureSettings = [
    [ 
        UIFontDescriptor.FeatureKey.featureIdentifier: 22,
        UIFontDescriptor.FeatureKey.typeIdentifier: 8,
    ],
]

You should be saying

let fontDescriptorFeatureSettings = [
    [ 
        UIFontDescriptor.FeatureKey.type: kTextSpacingType,
        UIFontDescriptor.FeatureKey.selector: 8,
    ],
]
// I couldn't find a name for the `8` here

Okay, so. To get a seriffed I, specify the kStylisticAltSixOnSelector as your kStylisticAlternativesType.

let fontDescriptorFeatureSettings = [
    [ 
        UIFontDescriptor.FeatureKey.type: kStylisticAlternativesType,
        UIFontDescriptor.FeatureKey.selector: kStylisticAltSixOnSelector,
    ],
]

Now, you might say, "Okay, fine, but I'm also getting some other changes I didn't necessarily want, such as the curvy ell and the slashed zero."

That is true, but at the high level at which you are operating with UIFontDescriptor, there's nothing you can do about that. What you have found in the dictionary are individual glyph variants. The only way you can access those is by drawing your text directly with CoreText, and I don't think there's any chance you're going to want to get down to that level of text drawing. What you want to do is just pop this text into a UILabel or similar, and be done with it; in that case, turning on the alt six variant is the best you can do.