我需要属性字符串的光学范围。我知道我可以调用.size()方法并读取其宽度,但这显然给了我印刷范围,并在右边增加了空间。
我的字符串都非常短,仅包含1-3个字符,因此每个字符串都将恰好包含一个glyphrun。
我找到了函数CTRunGetImageBounds,并按照注释中链接的提示进行操作之后,我能够提取行程并获得界限,但是显然,这并没有给我想要的结果。
以下Swift 4代码可在XCode9 Playground中使用:
import Cocoa
import PlaygroundSupport
public func getGlyphWidth(glyph: CGGlyph, font: CTFont) -> CGFloat {
var glyph = glyph
var bBox = CGRect()
CTFontGetBoundingRectsForGlyphs(font, .default, &glyph, &bBox, 1)
return bBox.width
}
class MyView: NSView {
init(inFrame: CGRect) {
super.init(frame: inFrame)
}
required init?(coder decoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func draw(_ rect: CGRect) {
// setup context properties
let context: CGContext = NSGraphicsContext.current!.cgContext
context.setStrokeColor(CGColor.black)
context.setTextDrawingMode(.fill)
// prepare variables and constants
let alphabet = ["A","B","C","D","E","F","G","H","I","J","K","L"]
let font = CTFontCreateWithName("Helvetica" as CFString, 48, nil)
var glyphX: CGFloat = 10
// draw alphabet as single glyphs
for letter in alphabet {
var glyph = CTFontGetGlyphWithName(font, letter as CFString)
var glyphPosition = CGPoint(x: glyphX, y: 80)
CTFontDrawGlyphs(font, &glyph, &glyphPosition, 1, context)
glyphX+=getGlyphWidth(glyph: glyph, font: font)
}
let textStringAttributes: [NSAttributedStringKey : Any] = [
NSAttributedStringKey.font : font,
]
glyphX = 10
// draw alphabet as attributed strings
for letter in alphabet {
let textPosition = NSPoint(x: glyphX, y: 20)
let text = NSAttributedString(string: letter, attributes: textStringAttributes)
let line = CTLineCreateWithAttributedString(text)
let runs = CTLineGetGlyphRuns(line) as! [CTRun]
let width = (CTRunGetImageBounds(runs[0], nil, CFRange(location: 0,length: 0))).maxX
text.draw(at: textPosition)
glyphX += width
}
}
}
var frameRect = CGRect(x: 0, y: 0, width: 400, height: 150)
PlaygroundPage.current.liveView = MyView(inFrame: frameRect)
该代码在游乐场实时视图的上一行中将A-L中的单个字母绘制为单个字形。在每个字母之后,水平位置将前进一个字母宽度,该宽度通过getGlyphWidth函数获取。
然后,它使用相同的字母从中创建属性字符串,然后将其用于首先创建CTLine,从中提取(仅)CTRun,最后测量其宽度。结果显示在实时视图的第二行。
第一行是期望的结果:width函数精确返回每个字母的宽度,从而使它们彼此接触。
我希望属性字符串版本具有相同的结果,但是在这里ImageBounds似乎添加了我想避免的其他填充。
如何测量给定文本从最左边到最右边像素的确切宽度?
是否有一种不太笨拙的方法来实现这一目标而不必强制转换四次(NSAtt.Str-> CTLine-> CTRun-> CGRect-> maxX)?
答案 0 :(得分:0)
好的,我自己找到了答案:
使用CTRunGetImageBounds的.width参数代替.maxX会带来正确的结果
CTLine也存在相同的功能:CTLineGetImageBounds