导读:本期聚焦于孙悟空创作的《如何在macOS中使用Core Text的CTFontFeature启用连字、旧式数字、分数与小型大写字母等字体特性》,敬请观看详情。字体排版中那些精致的效果,比如英文单词里的连字、变化高度呈现的旧式数字、优雅的分数形式以及小型大写字母,在macOS平台上都可以通过Core Text框架的CTFontFeature体系来精确控制。本文围绕CTFontFeature相关API展开,先梳理字体特性的底层原理与OpenType特性标签的对应关系,再通过完整的Objective-C与Swift代码示例,演示如何查询字体支持的特性、如何启用连字与旧式数字等常用排版特性,并对比手动设置run features与使用UIFontDescriptor两种方案的差异,最后总结在实际项目中应用这些特性时的注意事项与常见踩坑点,帮助开发者在原生应用中实现专业级的文字排版效果。

在macOS原生开发中,想要实现专业级的文字排版,仅仅调用CTFontCreateWithName创建一个字体是远远不够的。高质量的西文排印往往依赖字体内置的OpenType特性,例如常见的连字(ligature)、旧式数字(oldstyle figures)、分数(fractions)以及小型大写字母(small caps)。Core Text提供了完整的CTFontFeature体系,让开发者可以细粒度地控制这些特性。本文将从原理到实践,详细讲解如何用CTFontFeature开启这些排版特性。

如何在macOS中使用Core Text的CTFontFeature启用连字、旧式数字、分数与小型大写字母等字体特性

一、理解字体特性的底层原理

在深入API之前,需要先弄清楚什么是字体特性。现代字体文件(如OpenType字体)内部包含一张GSUB表(Glyph Substitution Table,字形替换表),它定义了在不同条件下某些字形序列应该被替换为其他字形。例如输入fi两个字符时,启用连字特性后渲染引擎会自动替换为一个整体字形,避免字母i上的点与f的弧线发生视觉冲突。

每个特性都有一个四字符的标签(tag),例如连字相关的标签是liga和rlig,旧式数字是onum,分数是frac,小型大写字母是smcp。Core Text通过CTFontFeature相关API把这些抽象的特性暴露给开发者。需要注意的是,并非所有字体都支持全部特性,只有字体本身实现了对应的GSUB规则,启用特性才会产生效果。举例来说,系统中著名的San Francisco字体对连字和分数支持良好,但小型大写字母的支持则取决于具体的字重和版本。

Core Text在macOS 10.15之后还提供了更现代的CTFontManagerCreateFontDescriptorFromData配合CTFontCopyFeatures的查询方式,可以枚举字体支持的全部特性及其可选值,这在需要动态适配字体的场景中非常实用。

二、查询字体支持的特性

在启用特性之前,先查询字体支持哪些特性是一个好习惯。Core Text提供了CTFontCopyFeatures函数,它返回一个包含字典的数组,每个字典描述一个特性组。下面是Objective-C的实现示例:

CTFontRef font = CTFontCreateWithName(CFSTR("Helvetica"), 14.0, NULL);
CFArrayRef features = CTFontCopyFeatures(font);
if (features != NULL) {
    for (CFIndex i = 0; i < CFArrayGetCount(features); i++) {
        CFDictionaryRef feature = CFArrayGetValueAtIndex(features, i);
        // 每个字典包含 kCTFontFeatureTypeIdentifierKey 等键
        CFShow(feature);
    }
    CFRelease(features);
}
CFRelease(font);

返回的字典中,kCTFontFeatureTypeIdentifierKey标识特性类型,kCTFontFeatureTypeNameKey是人类可读的名称,而kCTFontFeatureTypeSelectorsKey下面则是可选值列表。旧式数字对应的类型标识是kLetterCaseType附近的数值组合,实际开发中更推荐直接使用常量名,例如kNumberCaseTypekLowerCaseNumbersSelector,这些常量定义在CoreText的头文件中,语义清晰且不易出错。

Swift开发者可以通过桥接调用同一套C API,也可以借助UIFontDescriptor的字符串属性来查询。不过要注意macOS上的NSFontDescriptor在解析特性字典时行为与iOS略有差异,跨平台项目中要做兼容测试。

三、启用连字与旧式数字的完整示例

启用特性的核心思路是:构建特性字典数组,把它设置到字体描述符(CTFontDescriptorRef)的kCTFontFeaturesAttribute属性上,再用这个描述符创建新字体。下面以Objective-C演示同时开启标准连字与旧式数字:

- (CTFontRef)fontWithFeatures {
    // 定义要启用的特性:连字 + 旧式数字
    NSArray *features = @[
        @{ // 启用标准连字 (liga)
          (id)kCTFontFeatureTypeIdentifierKey : @(kLigaturesType),
          (id)kCTFontFeatureSelectorIdentifierKey : @(kCommonLigaturesOnSelector)
        },
        @{ // 启用旧式数字 (onum)
          (id)kCTFontFeatureTypeIdentifierKey : @(kNumberCaseType),
          (id)kCTFontFeatureSelectorIdentifierKey : @(kLowerCaseNumbersSelector)
        }
    ];
    NSDictionary *attributes = @{
        (id)kCTFontNameAttribute : (id)CFSTR("Times New Roman"),
        (id)kCTFontFeaturesAttribute : features
    };
    CTFontDescriptorRef descriptor =
        CTFontDescriptorCreateWithAttributes((CFDictionaryRef)attributes);
    return CTFontCreateWithFontDescriptor(descriptor, 18.0, NULL);
}

创建好的字体可以直接用于NSAttributedString的kCTFontAttributeName属性,绘制时Core Text会自动应用这些特性。验证效果很简单:输入包含fi、fl的英文单词观察是否变成单一字形,输入数字12345观察数字高度是否变成高低错落的形式。

分数与小型大写字母的开启方式完全相同,只需替换类型与选择器常量。分数使用kFractionsType配合kVerticalFractionSelector(对角分数,即斜杠形式)或kHorizontalFractionSelector(堆叠分数),小型大写字母使用kLetterCaseType配合kSmallCapsSelector。示例中改为斜分数的写法如下:

@{ // 启用对角分数 (frac)
  (id)kCTFontFeatureTypeIdentifierKey : @(kFractionsType),
  (id)kCTFontFeatureSelectorIdentifierKey : @(kVerticalFractionSelector)
},
@{ // 启用小型大写字母 (smcp)
  (id)kCTFontFeatureTypeIdentifierKey : @(kLetterCaseType),
  (id)kCTFontFeatureSelectorIdentifierKey : @(kSmallCapsSelector)
}

四、Swift实现与替代方案对比

在Swift中,除了直接调用C API,还可以使用NSFontDescriptor的特性字典,代码更为简洁:

let baseFont = NSFont(name: "Times New Roman", size: 18) ?? NSFont.systemFont(ofSize: 18)
let features: [[NSFontDescriptor.FeatureKey: Int]] = [
    [.type: kLigaturesType, .selector: kCommonLigaturesOnSelector],
    [.type: kNumberCaseType, .selector: kLowerCaseNumbersSelector],
    [.type: kLetterCaseType, .selector: kSmallCapsSelector]
]
let descriptor = baseFont.fontDescriptor.addingAttributes([
    .featureSettings: features
])
let styledFont = NSFont(descriptor: descriptor, size: 18)

两种方案的差异值得对比。基于CTFontDescriptor的方式更底层,适合需要精确控制Core Text绘制管线的场景,例如自定义文本布局引擎或跨iOS与macOS共享代码。基于NSFontDescriptor的方式则与AppKit的控件体系结合更好,直接把styledFont赋值给NSTextField等控件的font属性即可生效。

还有一种思路是在AttributedString中通过kCTFontFeatureSettingsAttribute按文本段落设置特性,粒度更细,适合同一行文字中混用比例数字与旧式数字的排版需求。三种方式底层最终都会转化为Core Text的run features,只是入口层级不同,开发者可以根据项目架构灵活选择。

五、实践中的注意事项

第一个常见问题是特性不生效。原因几乎都是字体不支持该特性,例如Helvetica本身没有真正的旧式数字字形,启用后渲染结果不会有任何变化。开发者应该在运行时用CTFontCopyFeatures做检测,或者引导用户选择支持完备的字体,如Times New Roman、Georgia、Hoefler Text等。

第二个问题是选择器冲突。某些特性组是互斥的,例如开启了小型大写字母又尝试开启标题大写,后设置的选择器可能覆盖前者。构建特性数组时要保证每个特性类型只出现一次,需要的选项以最后一次为准,必要时拆分为多个字体分别应用于不同的文本范围。

最后,在性能敏感的场景(如表格大量刷新)中,应缓存带有特性的CTFontRef,而不是每次绘制都重新构建字体描述符。字体描述符的创建涉及字典拷贝与字体匹配,频繁调用会带来可测量的开销。掌握了这些要点之后,配合Core Text的排版能力,就可以在macOS应用中呈现出媲美专业排版软件的文字效果了。

CTFontFeatureCore Text字体特性macOS字体排版修改时间:2026-09-01 12:44:38

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。