You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS中CGPath转SVG异常:文本转路径后显示为圆形而非文字

iOS文本转CGPath再转SVG后显示为圆形的问题修复

问题根源分析

你的代码存在两个核心错误,导致SVG渲染异常:

  1. 贝塞尔曲线点处理错误:pathToSVG函数中处理二次/三次曲线时,重复使用同一个点,未正确提取所有控制点和终点。
  2. SVG坐标适配缺失:仅翻转路径Y轴不足以匹配SVG坐标系统,需要通过viewBox修正路径的偏移问题。

修复后的代码实现

1. 修正pathToSVG的曲线点解析逻辑

func pathToSVG(path: CGPath, color: UIColor) -> String {
    let boundingBox = path.boundingBox
    // 添加viewBox参数,确保SVG能正确识别路径的完整坐标范围
    let viewBox = "\(boundingBox.minX) \(boundingBox.minY) \(boundingBox.width) \(boundingBox.height)"
    
    var svgString = "<svg width=\"\(boundingBox.width)\" height=\"\(boundingBox.height)\" viewBox=\"\(viewBox)\" xmlns=\"http://www.w3.org/2000/svg\">\n"
    svgString += "<path d=\""
    
    path.applyWithBlock { elementPointer in
        let element = elementPointer.pointee
        let points = element.points
        
        switch element.type {
        case .moveToPoint:
            svgString += "M \(points.pointee.x) \(points.pointee.y) "
        case .addLineToPoint:
            svgString += "L \(points.pointee.x) \(points.pointee.y) "
        case .addQuadCurveToPoint:
            // 二次曲线:控制点(points[0]) + 终点(points[1])
            let controlPoint = points[0]
            let endPoint = points[1]
            svgString += "Q \(controlPoint.x) \(controlPoint.y) \(endPoint.x) \(endPoint.y) "
        case .addCurveToPoint:
            // 三次曲线:控制点1(points[0]) + 控制点2(points[1]) + 终点(points[2])
            let controlPoint1 = points[0]
            let controlPoint2 = points[1]
            let endPoint = points[2]
            svgString += "C \(controlPoint1.x) \(controlPoint1.y) \(controlPoint2.x) \(controlPoint2.y) \(endPoint.x) \(endPoint.y) "
        case .closeSubpath:
            svgString += "Z "
        @unknown default:
            break
        }
    }
    
    svgString += "\" fill=\"\(color.hexString)\" />\n"
    svgString += "</svg>"
    
    return svgString
}

2. 优化textToPath的同步逻辑

移除不必要的延迟赋值,避免返回路径与全局变量不同步:

func textToPath(text: String, font: UIFont) -> CGPath? {
    let attributedString = NSAttributedString(string: text, attributes: [.font: font])
    let line = CTLineCreateWithAttributedString(attributedString)
    let runArray = CTLineGetGlyphRuns(line) as NSArray
    
    let path = CGMutablePath()
    
    for run in runArray {
        let run = run as! CTRun
        let count = CTRunGetGlyphCount(run)
        
        for index in 0..<count {
            let range = CFRangeMake(index, 1)
            var glyph: CGGlyph = 0
            var position: CGPoint = .zero
            CTRunGetGlyphs(run, range, &glyph)
            CTRunGetPositions(run, range, &position)
            
            if let glyphPath = CTFontCreatePathForGlyph(font, glyph, nil) {
                var transform = CGAffineTransform(translationX: position.x, y: position.y)
                transform = transform.scaledBy(x: 1, y: -1) // 适配SVG Y轴方向
                path.addPath(glyphPath, transform: transform)
            }
        }
    }
    cgPath = path // 直接同步赋值
    return path
}

额外注意事项

  • 确保UIColor的hexString扩展返回标准SVG颜色格式(如#RRGGBB或#RRGGBBAA)。
  • 若仍存在位置偏移,可打印boundingBox的数值,确认viewBox参数是否完整覆盖路径范围。

内容的提问来源于stack exchange,提问作者HeWhoRemains

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 15:55:59