使用Swift获取字体标志符号度量

使用Swift获取字体标志符号度量,swift,glyph,textkit,fontmetrics,Swift,Glyph,Textkit,Fontmetrics,为了在自定义UIView中定位/缩放字体图示符,我需要了解一些图示符指标,例如: -上升(标志符号距基线的高度,例如“g”位于基线上方的部分) -下降(轮廓距基线的深度,例如位于基线下方的“g”部分) -宽度 -紧缺 -斜体校正(字形中超出斜体宽度的部分) 我尝试子类化NSLayoutManager,并从drawGlyphs中读取这些信息: override func drawGlyphs(forGlyphRange glyphsToShow: NSRange, at origin: CGPoi

为了在自定义UIView中定位/缩放字体图示符,我需要了解一些图示符指标,例如: -上升(标志符号距基线的高度,例如“g”位于基线上方的部分) -下降(轮廓距基线的深度,例如位于基线下方的“g”部分) -宽度 -紧缺 -斜体校正(字形中超出斜体宽度的部分)

我尝试子类化
NSLayoutManager
,并从
drawGlyphs
中读取这些信息:

override func drawGlyphs(forGlyphRange glyphsToShow: NSRange, at origin: CGPoint) {
    enumerateLineFragments(forGlyphRange: glyphsToShow) {
        (rect, usedRect, textContainer, glyphRange, stop) in
        for i in glyphsToShow.location ..< NSMaxRange(glyphsToShow) {

            if let textContainer = self.textContainer(forGlyphAt: glyphsToShow.location, effectiveRange: nil) {
                var glyphRect = self.boundingRect(forGlyphRange: NSMakeRange(i, 1), in:textContainer)
                glyphRect.origin.x += origin.x;
                glyphRect.origin.y += origin.y;

                /// NOW I HAVE AT LEAST THE BOUNDING BOX
            }
        }
    }
}
覆盖func绘图图示符(forglyprange图示符stoshow:NSRange,原点:CGPoint){
枚举线条片段(forglyprange:glyphsToShow){
(rect、usedRect、textContainer、glyphRange、stop)在
对于glyphsToShow.location中的i..
但是
glyphRect
对于每个字形具有相同的精确宽度/高度,因此它携带了整个字体的最大(高度+深度)垂直空间和最大宽度,这不是我需要的(我需要每个字形的这些信息:II高,j有深度,而E没有)

是否可以通过TextKit收集此信息?其他字体指标(字距、斜体校正)是否可用

谢谢你的帮助,
Luca.

通常,您会使用核心文本,而不是NSLayoutManager,至少在宽度、上升和体面方面。我将在下面讨论其他问题

考虑一个属性化字符串:

let string = NSAttributedString(string: "squids")
从这里我们想把它分成“字形运行”。字形运行是一系列具有相同属性的字形。(在本例中只有一个。)为此,首先创建CTLine,然后请求CTRun对象:

let line = CTLineCreateWithAttributedString(string)
let glyphRuns = CTLineGetGlyphRuns(line) as! [CTRun]
每次运行都将有一个字体,这是我们查找度量所需的字体,以及一组字形。下面是调用代码的草图:

for run in glyphRuns {
    let font = run.font!
    let glyphs = run.glyphs()
    let boundingRects = run.boundingRects(for: glyphs, in: font)
    for pair in zip(glyphs, boundingRects) { print(pair) }
}
当然CTRun没有这么好的界面,所以我们需要将其构建为一个扩展:

extension CTRun {
    var font: CTFont? {
        let attributes = CTRunGetAttributes(self) as! [CFString: Any]
        guard let font = attributes[kCTFontAttributeName] else { return nil }
        return (font as! CTFont)
    }

    func glyphs(in range: Range<Int> = 0..<0) -> [CGGlyph] {
        let count = range.isEmpty ? CTRunGetGlyphCount(self) : range.count
        var glyphs = Array(repeating: CGGlyph(), count: count)
        CTRunGetGlyphs(self, CFRangeMake(range.startIndex, range.count), &glyphs)
        return glyphs
    }

    func boundingRects(for glyphs: [CGGlyph], in font: CTFont) -> [CGRect] {
        var boundingRects = Array(repeating: CGRect(), count: glyphs.count)
        CTFontGetBoundingRectsForGlyphs(font, .default, glyphs, &boundingRects, glyphs.count)
        return boundingRects
    }
}
extensionCtrun{
var字体:CTFont{
让attributes=CtrunGetAttribute(self)作为![CFString:Any]
guard let font=attributes[kCTFontAttributeName]else{return nil}
返回(字体为!CTFont)
}
func图示符(范围:范围=0..[CGGlyph]{
let count=range.isEmpty?CTRunGetGlyphCount(self):range.count
var glyphs=数组(重复:cglyph(),计数:计数)
CTRunGetGlyphs(self、CFRangeMake(range.startIndex、range.count)和glyphs)
返回图示符
}
func boundingRects(用于字形:[cglyph],字体:CTFont)->[CGRect]{
var boundingRects=数组(重复:CGRect(),计数:glyphs.count)
ctFontGetBoundingRects格式文字(字体、.default、glyphs、&boundingRects、glyphs.count)
返回边界矩形
}
}
请记住,这些都是度量。它们不是绘制图示符的实际边界框。一些字体在其框外绘制(Zapfino以其而闻名)。如果您想要实际的图像框,则需要使用
CTRunGetImageBounds
。还有
CTFontGetOpticalBoundsForGlyphs
,这将使框在正确排列时更有用(因为如果以与绘制方式不完全匹配的方式排列,字形通常看起来更好)

我假设您对所有这些都很熟悉,但为了完整起见,请记住,许多本身没有“下降者”的东西仍然有下降。例如,在Helvetica中,“s”略微下降到基线以下(还有“d”和许多其他具有弯曲底面的轮廓)

对于您注意到的其他度量,其中一些度量不是glyph度量。例如,单个glyph没有自己的紧排度量。紧排是应用于成对glyph的度量


类似地,我并不认为斜体更正适用于这里。在绝大多数情况下,你不会在Cocoa平台上使用“斜体”字体。你会选择斜体变体字体,但这是一种完全不同的字体。因此,你不会对“斜体Helvetica”应用间距更正你只需要替换Helvetica斜体,它有自己的宽度,等等(可可世界中没有Helvetica斜体,但有HelveticaNeue斜体)。我不知道可可版式系统中的任何地方有“斜体校正”应用。有些地方可能会很好,但我想不出它实际发生的地方。

非常感谢Rob的深入回答!我对文本布局引擎特别是数学公式布局很感兴趣,所以我正在尽力理解TeX是如何布局数学的。这是我为什么会感到恶心的主要原因king还支持斜体更正。我将尽快尝试您的解决方案。与此同时,我发现许多实现使用存储在文件(如TeX
.tfm
文件)中的某种预计算字体度量。因此,另一种方法可能是编写解析这些文件所需的Swift代码。再次感谢您的帮助swer!TeX在这个问题上是非常先进和专业的。核心文本更注重文本的布局(即单词,供人类阅读作为标签和句子)。如果你对数学布局感兴趣,我肯定会继续深入研究TeX是如何做事情的,而将其翻译成核心文本可能是一个非常有趣和富有成效的项目。TeX已经做了大量的工作。我知道这是一项具有挑战性的任务(至少)我不希望能够在Swift中移植整个TeX数学布局引擎。不过,我将此任务作为一种实现