You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用CoreText加载与AppKit的NSFont效果一致的字体?

用CoreText加载与AppKit一致字体的问题

我尝试使用CTFontCreateWithFontDescriptor()加载字体,但其表现与NSFont()加载的字体存在差异,目标是用CoreText加载出与AppKit完全一致的字体。

NSTextField表现差异

将两种字体用于NSTextField时,文本偏移和渲染效果不匹配:

let familyName = "Helvetica"
let fontSize: CGFloat = 30
let string = "Hello, World! gÖ,|#"

// 用AppKit加载字体
let font1 = NSFont(name: familyName, size: fontSize)!

// 用CoreText API加载字体
let font2 = CTFontCreateWithFontDescriptor(CTFontDescriptorCreateWithAttributes([
    kCTFontFamilyNameAttribute: familyName,
    kCTFontSizeAttribute: fontSize,
] as [CFString : Any] as CFDictionary), 0, nil) as NSFont

// 设置文本框
let field1 = NSTextField()
let field2 = NSTextField()
field1.setAsLabel()
field2.setAsLabel()
field1.font = font1
field2.font = font2
field1.stringValue = string
field2.stringValue = string

NSTextField结果预览

NSAttributedString边界框差异

将两种字体用于NSAttributedString时,文本的边界框高度不同:

let bounds1 = NSAttributedString(string: string, attributes: [
    .font: font1
]).boundingRect(with: .infinity)

let bounds2 = NSAttributedString(string: string, attributes: [
    .font: font2
]).boundingRect(with: .infinity)

print(bounds1) // 输出: (0.0, -7.0, 252.3486328125, 37.0)
print(bounds2) // 输出: (0.0, -7.0, 252.3486328125, 31.0)

调查过程

我对比了字体、字体属性、特性、功能设置甚至二进制字体表,未发现差异,推测是内部因素改变了表现。
我发现若为NSAttributedString的边界框获取方法添加.usesDeviceMetrics选项,两者返回结果一致,但帧也完全不同:

let bounds1 = NSAttributedString(string: "Hello, World! gÖ,|#", attributes: [.font: font1]).boundingRect(with: .infinity, options: [.usesDeviceMetrics])
let bounds2 = NSAttributedString(string: "Hello, World! gÖ,|#", attributes: [.font: font2]).boundingRect(with: .infinity, options: [.usesDeviceMetrics])

print(bounds1) // 输出: (2.3583984375, -6.6357421875, 249.9609375, 33.837890625)
print(bounds2) // 输出: (2.3583984375, -6.6357421875, 249.9609375, 33.837890625)

问题

如何用CoreText加载出与NSFont效果一致的字体?
次要问题:为何NSTextField会将其帧原点设为-2.0?


附:代码中使用的扩展

extension CGSize {
    public static var infinity: Self {
        .init(width: CGFloat.infinity, height: CGFloat.infinity)
    }
}

extension NSTextField {
    func setAsLabel() {
        translatesAutoresizingMaskIntoConstraints = false
        isBezeled = false
        isBordered = false
        isEditable = false
        isSelectable = false
        drawsBackground = false
    }
}

内容的提问来源于stack exchange,提问作者Marcel Kiss

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 15:22:46