核心文本:获取文本边界的最简单准确方法?
Core Text: Simplest accurate means of getting text bounds?
问题:
使用 Core Text 获取给定文本行边界的最简单准确方法是什么?
问题:
我尝试了多种技术,但得到的结果不一致。
上下文:
我一直在努力弄清楚如何找到目前单行文本的实际范围。
Core Text 文档在框架、行和 运行 级别例程 return 方面充其量是不完整的,指出它们 return "actual" 边界,当他们实际上不同意(至少在框架级别)时,并且不 return 特定字符串的实际边界,但看起来 return 比字体大小预测的大小更大。
然而,当我获得单个字形的边界时,我确实看到了不同字符之间的差异,这向我表明这是唯一准确的方法(遍历所有字形,找到它们的有效联合)。
我已经在 playground 文件中编写了一些测试,我是否遗漏了一些明显的东西?我的最终目标(部分/大部分工作)是拥有一个自定义 CALayer,它接受一个属性字符串,并将其缩放以填充整个边界。这与自动布局配合得很好,因为我可以根据需要创建一个随布局缩放的标签。
下面是我的 playground 片段,请注意右栏中生成的值。
// Playground - noun: a place where people can play
import UIKit
import CoreText
import QuartzCore
print("start")
let f = UIFont.systemFontOfSize(12)
let t = NSAttributedString(string: "A", attributes: [NSFontAttributeName:f])
let line = CTLineCreateWithAttributedString(t)
var ascent:CGFloat = 0, descent:CGFloat = 0, leading:CGFloat = 0
CTLineGetTypographicBounds(line, &ascent, &descent, &leading)
ascent
descent
ascent + descent
leading
let boundingSize = CGSizeMake(CGFloat.max, CGFloat.max)
let fs = CTFramesetterCreateWithAttributedString(t)
let frameSize = CTFramesetterSuggestFrameSizeWithConstraints(fs, CFRangeMake(0, t.length),
nil, boundingSize, nil)
frameSize
let lineWidth = frameSize.width
let lineHeight = frameSize.height
// for each run
let runs = CTLineGetGlyphRuns(line) as [AnyObject] as! [CTRun]
for (runIndex, run) in runs.enumerate() {
// get font
let attributes = CTRunGetAttributes(run) as NSDictionary as! [String:AnyObject]
let runFont:CTFont = attributes[kCTFontAttributeName as String] as! CTFont
// get glyphs for current run
let glyphCount = CTRunGetGlyphCount(run)
let range = CFRangeMake(0, glyphCount)
var glyphs = Array<CGGlyph>(count: glyphCount, repeatedValue: CGGlyph())
var positions = [CGPoint](count: glyphCount, repeatedValue: CGPointZero)
CTRunGetGlyphs(run, range, &glyphs)
CTRunGetPositions(run, range, &positions)
let runWidth = CTRunGetTypographicBounds(run, range, &ascent, &descent, &leading)
runWidth
ascent
descent
leading
// for each glyph in run
for (glyphInRun, var glyph) in glyphs.enumerate() {
let glyphFrame = withUnsafePointer(&glyph) { pointer -> CGRect in
return CTFontGetBoundingRectsForGlyphs(runFont, .Default, pointer, nil, 1) }
glyphFrame.size
}
}
print("done")
谢谢!
您正在使用 CTRunGetTypographicBounds
。 Typographic bounds 是指用于排版的边界,因此 ascent、descent 和 advance 是基于字体规格,而不是 运行.
中字形的实际外观
听起来你想要 CTRunGetImageBounds
,其中 returns 一个 CGRect
“紧紧包围 运行 字形的路径”。请注意,您可以为上下文参数传递 nil
(在我的测试中)。
问题:
使用 Core Text 获取给定文本行边界的最简单准确方法是什么?
问题:
我尝试了多种技术,但得到的结果不一致。
上下文:
我一直在努力弄清楚如何找到目前单行文本的实际范围。
Core Text 文档在框架、行和 运行 级别例程 return 方面充其量是不完整的,指出它们 return "actual" 边界,当他们实际上不同意(至少在框架级别)时,并且不 return 特定字符串的实际边界,但看起来 return 比字体大小预测的大小更大。
然而,当我获得单个字形的边界时,我确实看到了不同字符之间的差异,这向我表明这是唯一准确的方法(遍历所有字形,找到它们的有效联合)。
我已经在 playground 文件中编写了一些测试,我是否遗漏了一些明显的东西?我的最终目标(部分/大部分工作)是拥有一个自定义 CALayer,它接受一个属性字符串,并将其缩放以填充整个边界。这与自动布局配合得很好,因为我可以根据需要创建一个随布局缩放的标签。
下面是我的 playground 片段,请注意右栏中生成的值。
// Playground - noun: a place where people can play
import UIKit
import CoreText
import QuartzCore
print("start")
let f = UIFont.systemFontOfSize(12)
let t = NSAttributedString(string: "A", attributes: [NSFontAttributeName:f])
let line = CTLineCreateWithAttributedString(t)
var ascent:CGFloat = 0, descent:CGFloat = 0, leading:CGFloat = 0
CTLineGetTypographicBounds(line, &ascent, &descent, &leading)
ascent
descent
ascent + descent
leading
let boundingSize = CGSizeMake(CGFloat.max, CGFloat.max)
let fs = CTFramesetterCreateWithAttributedString(t)
let frameSize = CTFramesetterSuggestFrameSizeWithConstraints(fs, CFRangeMake(0, t.length),
nil, boundingSize, nil)
frameSize
let lineWidth = frameSize.width
let lineHeight = frameSize.height
// for each run
let runs = CTLineGetGlyphRuns(line) as [AnyObject] as! [CTRun]
for (runIndex, run) in runs.enumerate() {
// get font
let attributes = CTRunGetAttributes(run) as NSDictionary as! [String:AnyObject]
let runFont:CTFont = attributes[kCTFontAttributeName as String] as! CTFont
// get glyphs for current run
let glyphCount = CTRunGetGlyphCount(run)
let range = CFRangeMake(0, glyphCount)
var glyphs = Array<CGGlyph>(count: glyphCount, repeatedValue: CGGlyph())
var positions = [CGPoint](count: glyphCount, repeatedValue: CGPointZero)
CTRunGetGlyphs(run, range, &glyphs)
CTRunGetPositions(run, range, &positions)
let runWidth = CTRunGetTypographicBounds(run, range, &ascent, &descent, &leading)
runWidth
ascent
descent
leading
// for each glyph in run
for (glyphInRun, var glyph) in glyphs.enumerate() {
let glyphFrame = withUnsafePointer(&glyph) { pointer -> CGRect in
return CTFontGetBoundingRectsForGlyphs(runFont, .Default, pointer, nil, 1) }
glyphFrame.size
}
}
print("done")
谢谢!
您正在使用 CTRunGetTypographicBounds
。 Typographic bounds 是指用于排版的边界,因此 ascent、descent 和 advance 是基于字体规格,而不是 运行.
听起来你想要 CTRunGetImageBounds
,其中 returns 一个 CGRect
“紧紧包围 运行 字形的路径”。请注意,您可以为上下文参数传递 nil
(在我的测试中)。