簡體   English   中英

如何獲得NSAttributedString的光學范圍?

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

我需要屬性字符串的光學范圍。 我知道我可以調用.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)?

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

  1. 使用CTRunGetImageBounds的.width參數而不是.maxX可帶來正確的結果

  2. CTLine也存在相同的功能:CTLineGetImageBounds

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM