为了在自定义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
}
}
}
}
但是glyphRect
的每个字形都具有相同的确切宽度/高度,因此它带有整个字体的最大(高度+深度)垂直空间和最大宽度,这不是我需要的(我需要每个字形的那些信息: I 比 i 高,而 j 具有深度,而 E 还没有)。
是否可以通过TextKit收集此信息?其他字体指标(字距调整,斜体校正)可用吗?
感谢您的帮助, 卢卡。
答案 0 :(得分:1)
通常,至少在宽度,上升和体面方面,您将使用Core Text而非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
}
}
请记住,这些是指标。它们不是绘制字形的实际边界框。一些字体在其框外绘制(Zapfino以此闻名)。如果需要实际的图像框,则需要CTRunGetImageBounds
。还有CTFontGetOpticalBoundsForGlyphs
可以为您提供使框更正确对齐的有用的框(因为字形如果以与绘制方式不完全匹配的方式排列,通常看起来会更好)。
我认为您已经熟悉了所有这些内容,但是为了完整起见,请记住,许多本身没有“下降符”的事物仍然有下降的趋势。例如,在Helvetica中,“ s”下降到基线以下(也包括“ d”和许多其他具有弯曲基数的字形)。
对于您注意到的其他指标,其中一些不是字形指标。例如,单个字形没有其自己的字距调整量度。字距调整是应用于字形对的。
类似地,我并不觉得这里的斜体校正适用。在绝大多数情况下,您不会在Cocoa平台上“斜体”字体。您选择字体的斜体变体,但这是完全不同的字体。因此,您不要对“斜体Helvetica”应用间距校正。您可以用具有自己宽度等的Helvetica-Oblique代替。(可可世界中没有Helvetica-Italic,但是有HelveticaNeue-Italic。)我不知道可可布局系统中的任何地方,“斜体校正”。在某些地方可能会很不错,但是我无法想到它实际发生的任何地方。