简体   繁体   English

如何获得NSAttributedString的光学范围?

[英]How can I get the optical bounds of an NSAttributedString?

I need the optical bounds of an attributed string. 我需要属性字符串的光学范围。 I know I can call the .size() method and read its width but this obviously gives me typographic bounds with additional space to the right. 我知道我可以调用.size()方法并读取其宽度,但这显然给了我印刷范围,在右边有额外的空间。

My strings would all be very short and consist only of 1-3 characters, so every string would contain exactly one glyphrun. 我的字符串都非常短,仅包含1-3个字符,因此每个字符串都将恰好包含一个glyphrun。

I found the function CTRunGetImageBounds, and after following the hints in the link from the comment I was able to extract the run and get the bounds, but obviously this does not give me the desired result. 我找到了函数CTRunGetImageBounds,并且按照注释中链接的提示进行操作之后,我能够提取运行并获得界限,但是显然,这并没有给我想要的结果。

The following swift 4 code works in an XCode9 Playground: 以下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)

The code draws the single letters from A - L as single Glyphs in the upper row of the playground's live view. 该代码在游乐场实时视图的上一行中,将A-L中的单个字母绘制为单个字形。 The horizontal position will be advanced after each letter by the letter's width which is retrieved via the getGlyphWidth function. 水平位置将在每个字母之后按字母宽度前进,该宽度通过getGlyphWidth函数获取。

Then it uses the same letters to create attributed strings from it which will then be used to create first a CTLine, extract the (only) CTRun from it and finally measure its width. 然后,它使用相同的字母从中创建属性字符串,然后将其用于首先创建CTLine,从中提取(唯一的)CTRun,最后测量其宽度。 The result is seen in the second line in the live view. 结果在实时视图的第二行中显示。

The first line is the desired result: The width function returns exactly the width of every single letter, resulting in them touching each other. 第一行是期望的结果:width函数精确返回每个字母的宽度,从而使它们彼此接触。

I want the same result with the attributed string version, but here the ImageBounds seem to add an additional padding which I want to avoid. 我希望属性字符串版本具有相同的结果,但是在这里ImageBounds似乎添加了我想避免的其他填充。

How can I measure the exact width from the leftmost to the rightmost pixel of a given text? 如何测量给定文本从最左边到最右边像素的确切宽度?

And is there a less clumsy way to achieve this without having to cast four times (NSAtt.Str->CTLine->CTRun->CGRect->maxX) ? 并且有没有那么笨拙的方法来实现此目标而不必强制转换四次(NSAtt.Str-> CTLine-> CTRun-> CGRect-> maxX)?

Ok, I found the answer myself: 好的,我自己找到了答案:

  1. Using the .width parameter of the CTRunGetImageBounds instead of .maxX brings the right result 使用CTRunGetImageBounds的.width参数而不是.maxX可带来正确的结果

  2. The same function also does exist for the CTLine: CTLineGetImageBounds CTLine也存在相同的功能:CTLineGetImageBounds

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM