【问题标题】:Text path created by CTFontCreatePathForGlyph is flippedCTFontCreatePathForGlyph 创建的文本路径被翻转
【发布时间】:2023-08-25 02:58:01
【问题描述】:

我正在尝试实现在CAShapeLayer 内绘制的文本。为此,我需要从文本中创建 UIBezierPath。基于text path solution 应该一切正常,但遗憾的是我找不到问题所在。

CTFontCreatePathForGlyph(runFont, glyph, nil) 创建的路径从反面画了字母。

转换成Swift 4

的代码
      func initializeTextLayer() {
        let fontName: CFString = "Noteworthy-Light" as CFString

        let letters     = CGMutablePath()
        let font        = CTFontCreateWithName(fontName, 50, nil)
        let attrString  = NSAttributedString(string: "TEST", attributes: [kCTFontAttributeName as NSAttributedStringKey : font])
        let line        = CTLineCreateWithAttributedString(attrString)
        let runArray    = CTLineGetGlyphRuns(line)

        for runIndex in 0..<CFArrayGetCount(runArray) {

            let run     : CTRun =  unsafeBitCast(CFArrayGetValueAtIndex(runArray, runIndex), to: CTRun.self)
            let dictRef : CFDictionary = CTRunGetAttributes(run)
            let dict    : NSDictionary = dictRef as NSDictionary
            let runFont = dict[kCTFontAttributeName as String] as! CTFont

            for runGlyphIndex in 0..<CTRunGetGlyphCount(run) {
                let thisGlyphRange  = CFRangeMake(runGlyphIndex, 1)
                var glyph           = CGGlyph()
                var position        = CGPoint.zero
                CTRunGetGlyphs(run, thisGlyphRange, &glyph)
                CTRunGetPositions(run, thisGlyphRange, &position)

                let letter          = CTFontCreatePathForGlyph(runFont, glyph, nil)
                let t               = CGAffineTransform(translationX: position.x, y: position.y)
                if let letter = letter  {
                    letters.addPath(letter, transform: t)
                }
            }
        }
        let path = UIBezierPath()
        path.move(to: CGPoint.zero)
        path.append(UIBezierPath(cgPath: letters))

        let pathLayer               = CAShapeLayer()
        pathLayer.path              = path.cgPath
        self.layer.addSublayer(pathLayer)
    }

任何想法我可以做些什么来镜像它?我找到了 mirror path 的东西,但没有得到任何结果。

【问题讨论】:

  • 几乎可以肯定是因为 iOS 的 y 轴与 Mac-OS 的相反。尝试在渲染前通过 (+1, -1) 保存图形上下文和缩放上下文,然后恢复上下文。
  • 您无法在draw(in rect: CGRect) 之外获取当前图形上下文。我只使用CAShapeLayer's 为此。我错了吗 ?请给我一些启示。
  • 道歉 - 这是一个 CALayer。解决方案仍然有效,但您需要在创建路径时使用CGTransform(无论如何您都在为翻译执行此操作)来反转 y 轴。
  • 我明白你的意思,正在研究解决方案!
  • 干得好 - 你打败了我!您实际上应该将您的答案作为实际答案发布,而不是作为对问题的编辑。这样您就可以 A)将您的答案标记为“正确”,B)也可以对答案进行投票......

标签: swift uibezierpath


【解决方案1】:

正如@Grimxn 所说:我必须进行缩放和翻译。

draw(in rect: CGRect) 内部自定义层的超级视图中添加了以下代码行。

 func updateTextLayerPath() {
    if let pth = popupLayer.textLayerPath {

        UIColor.red.setStroke()

        // glyph path is inverted, so flip vertically
        let flipY = CGAffineTransform(scaleX: 1, y: -1.0)

        // glyph path may be offset on the x coord, and by the height (because it's flipped)
        let translate = CGAffineTransform(translationX: -pth.bounds.origin.x, y: pth.bounds.size.height + pth.bounds.origin.y)

        // apply the transforms
        pth.apply(flipY)
        pth.apply(translate)

        // stroke the path
        pth.stroke()
    }
}

textLayerPath 是自定义CAShapeLayer 中的存储属性,它在超级视图的layoutSubviews 中获取更新。

希望它对所有面临这个问题的人有所帮助。

【讨论】:

    【解决方案2】:

    您还可以为CTFontCreatePathForGlyph 方法提供转换以翻转返回的路径。

    // Mirror the glyph about the x-axis so it not upside down
    var flipVertically = CGAffineTransform(scaleX: 1, y: -1)
    let cgPath = CTFontCreatePathForGlyph(font, glyph, &flipVertically)
    

    【讨论】: