【发布时间】:2018-11-14 04:58:32
【问题描述】:
我需要属性字符串的光学边界。我知道我可以调用 .size() 方法并读取它的宽度,但这显然为我提供了右侧额外空间的印刷边界。
我的字符串都很短,只包含 1-3 个字符,所以每个字符串都只包含一个字形。
我找到了函数 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)?
【问题讨论】:
标签: swift nsattributedstring bounds