CoreSVG.swift (3275B)
1 // 2 // CoreSVG.swift 3 // damus 4 // 5 // Created by Oleg Abalonski on 1/27/23. 6 // Ref: https://gist.github.com/ollieatkinson/eb87a82fcb5500d5561fed8b0900a9f7 7 8 import Darwin 9 import Foundation 10 import UIKit 11 12 @objc 13 class CGSVGDocument: NSObject { } 14 15 var CGSVGDocumentRetain: (@convention(c) (CGSVGDocument?) -> Unmanaged<CGSVGDocument>?) = load("CGSVGDocumentRetain") 16 var CGSVGDocumentRelease: (@convention(c) (CGSVGDocument?) -> Void) = load("CGSVGDocumentRelease") 17 var CGSVGDocumentCreateFromData: (@convention(c) (CFData?, CFDictionary?) -> Unmanaged<CGSVGDocument>?) = load("CGSVGDocumentCreateFromData") 18 var CGContextDrawSVGDocument: (@convention(c) (CGContext?, CGSVGDocument?) -> Void) = load("CGContextDrawSVGDocument") 19 var CGSVGDocumentGetCanvasSize: (@convention(c) (CGSVGDocument?) -> CGSize) = load("CGSVGDocumentGetCanvasSize") 20 21 typealias ImageWithCGSVGDocument = @convention(c) (AnyObject, Selector, CGSVGDocument) -> UIImage 22 var ImageWithCGSVGDocumentSEL: Selector = NSSelectorFromString("_imageWithCGSVGDocument:") 23 24 let CoreSVG = dlopen("/System/Library/PrivateFrameworks/CoreSVG.framework/CoreSVG", RTLD_NOW) 25 26 func load<T>(_ name: String) -> T { 27 unsafeBitCast(dlsym(CoreSVG, name), to: T.self) 28 } 29 30 public class SVG { 31 32 deinit { CGSVGDocumentRelease(document) } 33 34 let document: CGSVGDocument 35 36 public convenience init?(_ value: String) { 37 guard let data = value.data(using: .utf8) else { return nil } 38 self.init(data) 39 } 40 41 public init?(_ data: Data) { 42 guard let document = CGSVGDocumentCreateFromData(data as CFData, nil)?.takeUnretainedValue() else { return nil } 43 guard CGSVGDocumentGetCanvasSize(document) != .zero else { return nil } 44 self.document = document 45 } 46 47 public var size: CGSize { 48 CGSVGDocumentGetCanvasSize(document) 49 } 50 51 public func draw(in context: CGContext) { 52 draw(in: context, size: size) 53 } 54 55 public func draw(in context: CGContext, size target: CGSize) { 56 57 var target = target 58 59 let ratio = ( 60 x: target.width / size.width, 61 y: target.height / size.height 62 ) 63 64 let rect = ( 65 document: CGRect(origin: .zero, size: size), () 66 ) 67 68 let scale: (x: CGFloat, y: CGFloat) 69 70 if target.width <= 0 { 71 scale = (ratio.y, ratio.y) 72 target.width = size.width * scale.x 73 } else if target.height <= 0 { 74 scale = (ratio.x, ratio.x) 75 target.width = size.width * scale.y 76 } else { 77 let min = min(ratio.x, ratio.y) 78 scale = (min, min) 79 target.width = size.width * scale.x 80 target.height = size.height * scale.y 81 } 82 83 let transform = ( 84 scale: CGAffineTransform(scaleX: scale.x, y: scale.y), 85 aspect: CGAffineTransform(translationX: (target.width / scale.x - rect.document.width) / 2, y: (target.height / scale.y - rect.document.height) / 2) 86 ) 87 88 context.translateBy(x: 0, y: target.height) 89 context.scaleBy(x: 1, y: -1) 90 context.concatenate(transform.scale) 91 context.concatenate(transform.aspect) 92 93 CGContextDrawSVGDocument(context, document) 94 } 95 }