我对 NSLayoutManager 有疑问。我正在显示一些翻译。我设置了字体,然后使用
drawGlyph(forRange:, at:)
绘制自定义下划线(标准下划线不适用于我的用例)。只要布局的文本是当前字体系列中的字符,一切都可以正常工作,但如果这些字符是中文字符,而我的字体系列未涵盖这些字符,则 AppKit 会选择包含它们的字体,但它似乎没有保持相同的字符高度,所以与拉丁字符相比,我在汉字上绘制的下划线高了大约 5 个像素。
我可以使用
defaultLineHeight(for: NSFont)
,但问题是我不知道它使用的是什么字体。例如,如果我将字体设置为 Helvetica
,并且系统将使用 NOTvetica
,因为它包含所需的字符,我怎么知道 NOTvetica
是用于这些字符的,以便我可以通过它到defaultLineHeight(for: NSFont)
?显然,传入 Helvetica
对我没有多大好处,因为这样我得到的字体的测量结果实际上并不是正在使用的字体。
我目前正在计算下划线位置如下:
// Get rect of text being underlined
let height = defaultLineHeight(for: myFont)
var rect = boundingRect(forGlyphRange: glyphRange, in: textContainer)
rect.size = NSSize(width: rect.width, height: height)
// Adjust rect for current font
var origin = rect.origin
origin.y += (rect.height + myFont.descender + 1)
// Create path used to draw the underline
let r = NSRect(origin: origin, size: NSSize(width: rect.width, height: thickness))
let path = NSBezierPath(roundedRect: r, xRadius: 0, yRadius: 0)
path.fill()
显然,根据与正在使用的字体不同的字体来计算在哪里绘制线条会导致下划线放错位置。
这个问题没有由 How to find the default font for East Asian character in MacOS 回答,因为该问题仅提供了有关如何查找默认字体列表的答案,它没有提供确定which字体的方法当前文本范围正在使用。目前可能正在使用的 60 种字体的列表对我来说就像单个未正在使用的字体一样无用。另外,这个问题似乎专门针对寻找仅适用于东亚字符的字体,我需要我的代码能够处理任何可能的字符,无论是印地语、泰语还是其他字符。
您可以在
NSTextStorage.attribute(_:at:effectiveRange:)
回调期间对 NSLayoutManager
的 NSTextStorage
使用 NSLayoutManagerDelegate
。 NSTextStorage
返回回退后正在使用的实际字体,而不是可能不支持字形的原始字体。
例如,这获取用于渲染文本的特定字体,然后可以将其用于调整行片段:
public func layoutManager(
_ layoutManager: NSLayoutManager,
shouldSetLineFragmentRect lineFragmentRect: UnsafeMutablePointer<CGRect>,
lineFragmentUsedRect: UnsafeMutablePointer<CGRect>,
baselineOffset: UnsafeMutablePointer<CGFloat>,
in _: NSTextContainer,
forGlyphRange glyphRange: NSRange
) -> Bool {
let charRange = characterRange(forGlyphRange: glyphRange, actualGlyphRange: nil)
guard let textStorage = layoutManager.textStorage else { return false }
let actualFont = textStorage.attribute(.font, at: charRange.location, effectiveRange: nil)
... do something with the font ...
}
我建议传递 nil 作为 effectiveRange,因为字体可能会在行内改变。