pocketsvg/PocketSVG

How to convert UIBezierpath to SVG?

firattamur opened this issue · 8 comments

Hello,

I am trying to create a coloring app. I can read svg image and fill their backgrounds. I want to save my UIBezier paths as svg after coloring process is done. I saw the function SVGStringFromCGPaths path I could not figure out how to set AttributeSet of svg? A short explanation or a small examples would be great.

Thanks,

You'll need to explain how the "coloring process" works.

If your paths stay as SVGBezierPath instances you can just use self.SVGRepresentation.

Hello,

Thank you for your response first of all. Here is my code for coloring process. I am creating CAShapeLayers from SVGBezierPaths then fill inside of them. I want to save result of coloring process as SVG. Is there any way to convert colored CAShapeLayers to SVG?

import UIKit
import PocketSVG


class ViewController: UIViewController {

private var paths: [UIBezierPath]!
private var shapeLayerIndices = [CAShapeLayer:Int]()
private var touchedshapedLayerIndices = [CAShapeLayer:Int]()
private var shapeLayers = [CAShapeLayer]()
private var affineTransform: CGAffineTransform!

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view.
    
    let url = Bundle.main.url(forResource: "simple", withExtension: "svg")!
    
    paths = SVGBezierPath.pathsFromSVG(at: url)
    let tigerLayer = CALayer()
    
    for (index, path) in paths.enumerated() {
    
        let shapeLayer = CAShapeLayer()
        shapeLayer.path = path.cgPath
        
        shapeLayer.strokeColor = UIColor.black.cgColor
        shapeLayer.fillColor = UIColor.white.cgColor

        shapeLayerIndices[shapeLayer] = index
        tigerLayer.addSublayer(shapeLayer)
        
    }
    
    let scale = CGAffineTransform(scaleX: 1, y: 1)
    let transform = CGAffineTransform(translationX: 100, y: 100)
    affineTransform = scale.concatenating(transform)
    tigerLayer.setAffineTransform(affineTransform)
    view.layer.addSublayer(tigerLayer)
                
}

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
   
    let touch = touches.first
    let point = touch!.location(in: self.view).applying(self.affineTransform.inverted())
        
    guard let sublayers = self.view.layer.sublayers?[0].sublayers else { return }

    self.touchedshapedLayerIndices = [CAShapeLayer:Int]()
    
    for (_, sublayer) in sublayers.enumerated() {
        
        if let sublayer = sublayer as? CAShapeLayer, let path = sublayer.path {
            
            if path.contains(point) {
                touchedshapedLayerIndices[sublayer] = self.shapeLayerIndices[sublayer]
            }
        }
        
    }
    
    let topShapeLayer = touchedshapedLayerIndices.max { a, b in a.value < b.value }
    
    guard topShapeLayer != nil else {
        return
    }
    
    let sublayer = topShapeLayer!.key
    let index    = topShapeLayer!.value
    
    sublayer.removeFromSuperlayer()
    sublayer.fillColor = UIColor.red.cgColor
    self.view.layer.sublayers?[0].insertSublayer(sublayer, at: UInt32(index))
    
}

}

You'll have to iterate over the path array(and change its type to [SVGBezierPath]), then for each of the paths, use pathBySettingSVGAttributes: with the svg attributes you want to change. It'll return a new path reflecting the changes.

something like

let attributeSet = SVGMutableAttributeSet()
let paths = shapeLayers.map { layer in 
    let path = paths[shapeLayerIndices[layer]]
    let newPath = path.pathBySettingSVGAttributes(["fill": layer.backgroundColor])
    attributeSet.set(attributes: path.svgAttributes, forPath: newPath.CGPath)
    return newPath.CGPath
}

let svgString = SVGStringFromCGPaths(updatedPaths, attributeSet)

I haven't actually compiled the above, but the general idea should work.

Thanks a lot! I will try it and share the results.

Hello,

I have tried your code. I got the error in below. I tried to give color parameter as UIColor and CgColor but neither of them worked.

Screen Shot 2021-10-13 at 7 43 07 PM
Screen Shot 2021-10-13 at 7 42 50 PM

Thanks,

I had the same problem, and I modified the code myself to keep it from crashing

for(NSString *key in pathAttrs) {
      if(![pathAttrs[key] isKindOfClass:[NSString class]]) { // Color
           [svg appendFormat:@" %@=\"%@\"", key, hexTriplet((__bridge CGColorRef)pathAttrs[key]).string()];
              if (CFGetTypeID((__bridge CFTypeRef)(pathAttrs[key])) == CGColorGetTypeID()) {
                    float const alpha = CGColorGetAlpha((__bridge CGColorRef)pathAttrs[key]);
                    if(alpha < 1.0)
                        [svg appendFormat:@" %@-opacity=\"%.2g\"", key, alpha];
                }
      } else
           [svg appendFormat:@" %@=\"%@\"", key, pathAttrs[key]];
}

@CodeForRabbit It'd be great if you could make a PR with that

fixed via #214