【问题标题】:CTFontDrawGlyphs draws at incorrect positionCTFontDrawGlyphs 在不正确的位置绘制
【发布时间】:2017-12-02 21:10:31
【问题描述】:

已编辑:我将原始代码存根替换为独立版本

我正在使用 Quartz2D 将音乐符号绘制到 CGContext。我正在使用 Swift 4 在 Playground 中执行此操作。

Musical Notes 由单个 CGGlyph 的符头和一个线基元组成。字形取自音乐字体“bravura”。 符头字形的 x 位置正好位于其左边缘。符干必须与该原点完美对齐,因此其 x 位置必须是符头的 x 位置 + 符干厚度 / 2。

我有一个函数,它将 CGFloat 作为 x 位置并将字形和词干绘制到 CGContext 中。当 x 的值是像 30.0 这样的舍入值时,这非常有效。但是,当值是像 30.9 这样的小数时,词干会错位:

很明显,线条绘制正确,但字形忽略了 CGFloat 的小数部分。当我将 x 值进一步提高到 31.0 时,两个部分再次同步。

这是简化的代码,可在您的 XCode9 Playground 中使用:

import PlaygroundSupport
import Cocoa

let fontSize = CGFloat(64)

// uncomment the next paragraph if you have added the "Bravura" Font to the ressources folder
/*
let fontURL = Bundle.main.url(forResource: "Bravura", withExtension: "otf")
CTFontManagerRegisterFontsForURL(fontURL! as CFURL, CTFontManagerScope.process, nil)
var font = CTFontCreateWithName("Bravura" as CFString, fontSize, nil)
*/

// comment out the next line if you use Bravura
var font = CTFontCreateWithName("Helvetica" as CFString, fontSize, nil)

func drawAtXPosition(_ posX: CGFloat, inContext context: CGContext) {
    // draw font glyph (notehead)
    var glyph = CTFontGetGlyphWithName(font, "uniE0A3" as CFString)
    var notePosition = CGPoint(x: posX, y: 100)
    CTFontDrawGlyphs(font, &glyph, &notePosition, 1, context)

    // draw line (note stem)
    let stemThickness = CGFloat(fontSize/4*0.12)
    let x: CGFloat = posX + stemThickness / 2
    let y: CGFloat = 100 - (0.168 * fontSize / 4)
    let stemBegin = CGPoint(x:x, y:y)
    let stemEnd = CGPoint(x:x, y: y - (3.5 * fontSize / 4))

    context.setLineWidth(stemThickness)
    context.strokeLineSegments(between: [stemBegin, stemEnd])
}

class MyView: NSView {
    override func draw(_ rect: CGRect) {
        let context = NSGraphicsContext.current!.cgContext
        context.setStrokeColor(CGColor.black)
        context.setFillColor(CGColor.black)

        drawAtXPosition(100, inContext: context)    // this draws as expected

        drawAtXPosition(200.99, inContext: context) // here the glyph is drawn at x=200, while the line is correctly drawn at 200.99
    }
}

var myView = MyView(frame: CGRect(x: 0, y: 0, width: 500, height: 200))
let myViewController = NSViewController()
myViewController.view = myView
PlaygroundPage.current.liveView = myViewController

您可以从...下载音乐字体“Bravura”

https://www.smufl.org/fonts/

...但我重写了代码,使其默认使用 Helvetica,这也适用于演示。

作为我原始代码中的一种解决方法,我在将 x 值传递给绘图函数之前实现了 Foundation 的 .rounded() 方法。这行得通,但我担心在小字体(整个代码的编写是为了让所有内容都围绕字体大小缩放)时,不准确会很明显。

我遇到了 CGContext 的 setShouldSubpixelPositionFonts 和 setAllowsFontSubpixelPositioning 方法,但是将它们设置为 true 并没有任何改变。

那么,有没有一种方法可以像绘制图元一样精确地绘制字体对象(必须有一个)?还是我对将 CGFloats 舍入为 Ints 的担忧是不合理的?

【问题讨论】:

  • 添加完整的自包含代码可能会有所帮助,其他人可以将其复制到他们的 Playground 以重现问题并尝试可能的解决方案。
  • 感谢您的指出。在我的第一个版本中,我希望找到之前遇到过同样问题的人,所以我很快总结了一些行来演示这个问题。如您所见,工作版本实际上需要更多代码,但现在我终于有时间重写我的第一个版本。

标签: swift fonts core-graphics quartz-2d


【解决方案1】:

经过无休止的挖掘,我找到了解决方案:

设置是不够的

context.setShouldSubpixelPositionFonts(true)

你还得设置

context.setShouldSubpixelQuantizeFonts(false)

因为后者似乎覆盖了前者。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-16
    • 1970-01-01
    • 2017-07-01
    • 2023-03-26
    • 2015-04-13
    相关资源
    最近更新 更多