简体   繁体   English

使用 Swift 获取字体字形指标

[英]Get font glyph metrics with Swift

In order to positioning / scaling font glyphs in a custom UIView I need to know some glyph metrics, such as: - ascent (height of the glyph from the base line, such as the part of "g" that stands over the base line) - descent (depth of the glyph from the base line, such as the part of "g" that stands under the base line) - width - kerning - italic correction (the part of the glyph that exceeds its width in italic)为了在自定义 UIView 中定位/缩放字体字形,我需要知道一些字形指标,例如: - ascent(字形距基线的高度,例如位于基线上方的“g”部分) - 下降(字形从基线开始的深度,例如位于基线下方的“g”部分) - 宽度 - 字距调整 - 斜体校正(字形中超出其斜体宽度的部分)

I tried subclassing NSLayoutManager and read those information from drawGlyphs :我尝试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
            }
        }
    }
}

but glyphRect has the same exact width/height for every glyph, so it carries the max (height+depth) vertical space and max width for the whole font, which isn't what I need (I need those information for every glyph: I is taller than i and j has depth while E hasn't).但是glyphRect对于每个字形都具有相同的确切宽度/高度,因此它带有整个字体的最大(高度+深度)垂直空间和最大宽度,这不是我需要的(我需要每个字形的这些信息:i高, j有深度,而E没有)。

Is it possible to collect this information via TextKit?是否可以通过 TextKit 收集这些信息? Are the other font metrics (kerning, italic correction) available?其他字体指标(字距调整、斜体校正)是否可用?

Thank you for your help, Luca.谢谢你的帮助,卢卡。

Typically you'd do this with Core Text, not NSLayoutManager, at least for width, ascent and decent.通常你会使用 Core Text,而不是 NSLayoutManager,至少在宽度、上升和体面方面是这样。 I'll discuss the others below.我将在下面讨论其他的。

Consider an attributed string:考虑一个属性字符串:

let string = NSAttributedString(string: "squids")

From there we want to break it into "glyph runs."从那里我们想把它分解成“字形运行”。 A glyph run is a sequence of glyphs with all the same attributes.字形运行是具有所有相同属性的字形序列。 (In this case there's only one.) To do that, first make a CTLine, an then ask for the CTRun objects: (在这种情况下只有一个。)为此,首先创建一个 CTLine,然后请求 CTRun 对象:

let line = CTLineCreateWithAttributedString(string)
let glyphRuns = CTLineGetGlyphRuns(line) as! [CTRun]

Each run will have a font, which is what we need to look up the metrics, and a collection of glyphs.每次运行都会有一个字体,这是我们查找指标所需的字体,以及一组字形。 Here's a sketch of the calling code:这是调用代码的草图:

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) }
}

Of course CTRun doesn't have such a nice interface, so we need to build it as an extension:当然 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
    }
}

Keep in mind that these are metrics .请记住,这些是指标 They're not the actual bounding box of the drawn glyph.它们不是绘制字形的实际边界框。 Some fonts draw outside their box (Zapfino is famous for it).一些 fonts 在他们的盒子外面画(Zapfino 以它而闻名)。 If you want the actual image box, then you need CTRunGetImageBounds instead.如果你想要实际的图像框,那么你需要CTRunGetImageBounds代替。 There is also CTFontGetOpticalBoundsForGlyphs which will give you boxes more useful for lining things up correctly (since glyphs often look better if lined up in ways that do not precisely match how they're drawn).还有CTFontGetOpticalBoundsForGlyphs可以为您提供更有用的框,以便正确排列事物(因为如果以与绘制方式不完全匹配的方式排列,字形通常看起来更好)。

I assume you're familiar with all this, but for completeness, remember that many things that don't have a "descender" per se still have a descent.我假设你对这一切都很熟悉,但为了完整起见,请记住许多本身没有“下降器”的东西仍然有下降器。 For example, in Helvetica, the "s" descends slightly below the baseline (also "d" and many other glyphs with a curved base).例如,在 Helvetica 中,“s”略低于基线(还有“d”和许多其他具有弯曲底部的字形)。

To the other metrics you note, some of these aren't glyph metrics.对于您注意到的其他指标,其中一些不是字形指标。 For example, a single glyph doesn't have its own kerning metric.例如,单个字形没有自己的字距调整度量。 Kerning is something that's applied to pairs of glyphs.字距调整适用于成对的字形。

Similarly, I don't really feel italic correction applies here.同样,我真的不觉得斜体校正适用于此。 In the vast majority of cases, you don't "italic" a font on Cocoa platforms.在绝大多数情况下,您不会在 Cocoa 平台上“斜体”字体。 You choose the italic variant of a font, but that's a completely different font.您选择字体的斜体变体,但这是完全不同的字体。 So you don't apply spacing correction to "italicized Helvetica."因此,您不要对“斜体 Helvetica”应用间距校正。 You just substitute Helvetica-Oblique, which has its own widths, etc. (There's no Helvetica-Italic in the Cocoa world, but there is a HelveticaNeue-Italic.) I don't know of anywhere in the Cocoa layout system where a "italic correction" is applied.您只需替换 Helvetica-Oblique,它有自己的宽度等。(Cocoa 世界中没有 Helvetica-Italic,但有 HelveticaNeue-Italic。)我不知道 Z71B5EE5A967359A57491Z16B856F3F0 布局系统中的任何地方斜体校正”被应用。 There are some places it would be probably nice, but I can't think of anywhere it actually happens.有些地方可能会很好,但我想不出它实际发生的任何地方。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM