【问题标题】:How can I get the optical bounds of an NSAttributedString?如何获得 NSAttributedString 的光学边界?
【发布时间】: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


【解决方案1】:

好的,我自己找到了答案:

  1. 使用 CTRunGetImageBounds 的 .width 参数而不是 .maxX 会带来正确的结果

  2. CTLine 也存在相同的函数:CTLineGetImageBounds

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-24
    • 2011-01-05
    • 1970-01-01
    相关资源
    最近更新 更多