damus

nostr ios client
git clone git://jb55.com/damus
Log | Files | Refs | README | LICENSE

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 }