-
-
Save ollieatkinson/eb87a82fcb5500d5561fed8b0900a9f7 to your computer and use it in GitHub Desktop.
import Darwin | |
import Foundation | |
import UIKit | |
// https://github.com/xybp888/iOS-SDKs/blob/master/iPhoneOS17.1.sdk/System/Library/PrivateFrameworks/CoreSVG.framework/CoreSVG.tbd | |
// https://developer.limneos.net/index.php?ios=17.1&framework=UIKitCore.framework&header=UIImage.h | |
@objc | |
class CGSVGDocument: NSObject { } | |
var CGSVGDocumentRetain: (@convention(c) (CGSVGDocument?) -> Unmanaged<CGSVGDocument>?) = load("CGSVGDocumentRetain") | |
var CGSVGDocumentRelease: (@convention(c) (CGSVGDocument?) -> Void) = load("CGSVGDocumentRelease") | |
var CGSVGDocumentCreateFromData: (@convention(c) (CFData?, CFDictionary?) -> Unmanaged<CGSVGDocument>?) = load("CGSVGDocumentCreateFromData") | |
var CGContextDrawSVGDocument: (@convention(c) (CGContext?, CGSVGDocument?) -> Void) = load("CGContextDrawSVGDocument") | |
var CGSVGDocumentGetCanvasSize: (@convention(c) (CGSVGDocument?) -> CGSize) = load("CGSVGDocumentGetCanvasSize") | |
typealias ImageWithCGSVGDocument = @convention(c) (AnyObject, Selector, CGSVGDocument) -> UIImage | |
var ImageWithCGSVGDocumentSEL: Selector = NSSelectorFromString("_imageWithCGSVGDocument:") | |
let CoreSVG = dlopen("/System/Library/PrivateFrameworks/CoreSVG.framework/CoreSVG", RTLD_NOW) | |
func load<T>(_ name: String) -> T { | |
unsafeBitCast(dlsym(CoreSVG, name), to: T.self) | |
} | |
public class SVG { | |
deinit { CGSVGDocumentRelease(document) } | |
let document: CGSVGDocument | |
public convenience init?(_ value: String) { | |
guard let data = value.data(using: .utf8) else { return nil } | |
self.init(data) | |
} | |
public init?(_ data: Data) { | |
guard let document = CGSVGDocumentCreateFromData(data as CFData, nil)?.takeUnretainedValue() else { return nil } | |
guard CGSVGDocumentGetCanvasSize(document) != .zero else { return nil } | |
self.document = document | |
} | |
public var size: CGSize { | |
CGSVGDocumentGetCanvasSize(document) | |
} | |
public func image() -> UIImage? { | |
let ImageWithCGSVGDocument = unsafeBitCast(UIImage.self.method(for: ImageWithCGSVGDocumentSEL), to: ImageWithCGSVGDocument.self) | |
let image = ImageWithCGSVGDocument(UIImage.self, ImageWithCGSVGDocumentSEL, document) | |
return image | |
} | |
public func draw(in context: CGContext) { | |
draw(in: context, size: size) | |
} | |
public func draw(in context: CGContext, size target: CGSize) { | |
var target = target | |
let ratio = ( | |
x: target.width / size.width, | |
y: target.height / size.height | |
) | |
let rect = ( | |
document: CGRect(origin: .zero, size: size), () | |
) | |
let scale: (x: CGFloat, y: CGFloat) | |
if target.width <= 0 { | |
scale = (ratio.y, ratio.y) | |
target.width = size.width * scale.x | |
} else if target.height <= 0 { | |
scale = (ratio.x, ratio.x) | |
target.width = size.width * scale.y | |
} else { | |
let min = min(ratio.x, ratio.y) | |
scale = (min, min) | |
target.width = size.width * scale.x | |
target.height = size.height * scale.y | |
} | |
let transform = ( | |
scale: CGAffineTransform(scaleX: scale.x, y: scale.y), | |
aspect: CGAffineTransform(translationX: (target.width / scale.x - rect.document.width) / 2, y: (target.height / scale.y - rect.document.height) / 2) | |
) | |
context.translateBy(x: 0, y: target.height) | |
context.scaleBy(x: 1, y: -1) | |
context.concatenate(transform.scale) | |
context.concatenate(transform.aspect) | |
CGContextDrawSVGDocument(context, document) | |
} | |
} | |
ollieatkinson
commented
May 10, 2021
•
Hi there,
I am trying to use this but I get the following error:
Entity: line 1: parser error : Start tag expected, '<' not found
\211PNG
^
CoreSVG has logged an error. Set environment variabe "CORESVG_VERBOSE" to learn more.
Entity: line 1: parser error : Start tag expected, '<' not found
\211PNG
^
Do you have an idea what this means? I basically just passed Data
I got from an URLSession
inside of SVG
s initializer.
An additional question is it possible to use this to (rasterize) and show SVG images in a SwiftUI Image
? Because that is what I am eventually trying to achieve
Hi there, I am trying to use this but I get the following error:
Entity: line 1: parser error : Start tag expected, '<' not found \211PNG ^ CoreSVG has logged an error. Set environment variabe "CORESVG_VERBOSE" to learn more. Entity: line 1: parser error : Start tag expected, '<' not found \211PNG ^
Do you have an idea what this means? I basically just passed
Data
I got from anURLSession
inside ofSVG
s initializer.An additional question is it possible to use this to (rasterize) and show SVG images in a SwiftUI
Image
? Because that is what I am eventually trying to achieve
You can try to use UIImage
's init(data:) method and then convert it to SwiftUI image like here. UIImage should support SVG natively:
You use image objects to represent image data of all kinds...
yeah, that did not work for vector data though when I tried. Not sure why, but Apple's SVG support seems really limited and therefore a pain.
Loading SVG seems to work only when loading from the bundle.
@misteu When using this in SwiftUI I used UI/NSViewRepresentable
and drew the SVG to the graphic context:
struct SVGView: UIViewRepresentable {
private final class View: UIView {
var svg: SVG
init(_ svg: SVG) {
self.svg = svg
super.init(frame: .init(origin: .zero, size: svg.size))
isOpaque = false
}
@available(*, unavailable) required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
overrid func draw(_ rect: CGRect) {
guard let context = UIGraphicsGetCurrentContext() else { return }
svg.draw(in: context, size: rect.size)
}
}
private let svg: SVG
init(_ svg: SVG) { self.svg = svg }
public func makeUIView(context: Context) -> UIView {
View(svg)
}
public func updateUIView(_ view: UIView, context: Context) {
view.setNeedsDisplay()
}
}
For those wanting to use this inside of production app, I've not been blocked with using this since the initial version in 2021, you just need to obfuscate the symbols
// private var CGSVGDocumentRetain: (@convention(c) (CGSVGDocument?) -> Unmanaged<CGSVGDocument>?) = load("CGSVGDocumentRetain")
private var CGSVGDocumentRetain: (@convention(c) (CGSVGDocument?) -> Unmanaged<CGSVGDocument>?) = load("==gbpFGdlJFduVWb1N2bEdkVTd0Q".deobfuscated)
// private var CGSVGDocumentRelease: (@convention(c) (CGSVGDocument?) -> Void) = load("CGSVGDocumentRelease")
private var CGSVGDocumentRelease: (@convention(c) (CGSVGDocument?) -> Void) = load("=U2chVGblJFduVWb1N2bEdkVTd0Q".deobfuscated)
// private var CGSVGDocumentCreateFromData: (@convention(c) (CFData?, CFDictionary?) -> Unmanaged<CGSVGDocument>?) = load("CGSVGDocumentCreateFromData")
private var CGSVGDocumentCreateFromData: (@convention(c) (CFData?, CFDictionary?) -> Unmanaged<CGSVGDocument>?) = load("hRXYE12byZUZ0FWZyNEduVWb1N2bEdkVTd0Q".deobfuscated)
// private var CGSVGDocumentWriteToData: (@convention(c) (CGSVGDocument?, CFData?, CFDictionary?) -> Void) = load("CGSVGDocumentWriteToData")
private var CGSVGDocumentWriteToData: (@convention(c) (CGSVGDocument?, CFData?, CFDictionary?) -> Void) = load("hRXYE9GVlRXaydFduVWb1N2bEdkVTd0Q".deobfuscated)
// private var CGContextDrawSVGDocument: (@convention(c) (CGContext?, CGSVGDocument?) -> Void) = load("CGContextDrawSVGDocument")
private var CGContextDrawSVGDocument: (@convention(c) (CGContext?, CGSVGDocument?) -> Void) = load("05WZtV3YvR0RWN1dhJHR0hXZ052bDd0Q".deobfuscated)
// private var CGSVGDocumentGetCanvasSize: (@convention(c) (CGSVGDocument?) -> CGSize) = load("CGSVGDocumentGetCanvasSize")
private var CGSVGDocumentGetCanvasSize: (@convention(c) (CGSVGDocument?) -> CGSize) = load("=UmepN1chZnbhNEdldEduVWb1N2bEdkVTd0Q".deobfuscated)
#if canImport(UIKit)
// private typealias ImageWithCGSVGDocument = @convention(c) (AnyObject, Selector, CGSVGDocument) -> UIImage
private typealias ImageWithCGSVGDocument = @convention(c) (AnyObject, Selector, CGSVGDocument) -> UIImage
#endif
// private var ImageWithCGSVGDocumentSEL: Selector = NSSelectorFromString("_imageWithCGSVGDocument:")
private var ImageWithCGSVGDocumentSEL: Selector = NSSelectorFromString("6Qnbl1Wdj9GRHZ1UHNEa0l2VldWYtl2X".deobfuscated)
// private let CoreSVG = dlopen("/System/Library/PrivateFrameworks/CoreSVG.framework/CoreSVG", RTLD_NOW)
private let CoreSVG = dlopen("=ckVTVmcvN0LrJ3b3VWbhJnZuckVTVmcvN0LztmcvdXZtFmcGVGdhZXayB1L5JXYyJWaM9SblR3c5N1L".deobfuscated, RTLD_NOW)
extension String {
fileprivate var deobfuscated: String { Data(base64Encoded: String(reversed()))!.string }
}
extension Data {
fileprivate var string: String { String(decoding: self, as: UTF8.self) }
}
Can be used this class in UIKit? I'm trying to render a SVG downloaded from a remote server but I get this message:
Entity: line 1: parser error : Start tag expected, '<' not found
\377\330\377\340
^
This is my implementation:
let data = try Data(contentsOf: url)
if let svg = SVG(data) {
let image = svg.image()
let render = UIGraphicsImageRenderer(size: svg.size)
let image2 = render.image { context in
svg.draw(in: context.cgContext)
}
DispatchQueue.main.async {
self.image = image2
}
}
The app crashes inside of the draw() method, at this line:
CGContextDrawSVGDocument(context, document)
UPDATE: I found that the problem is with the image that I'm trying to download. Is there any problem with encoding types?
My original image is .utf8 (the one that cannot be assigned) and a one that worked for me (downloaded from internet) is isoLatin1.
Can be used this class in UIKit?
Yes.
This is my implementation:
I would recommend something like this:
final class SVGView: UIView {
var svg: SVG
init(_ svg: SVG) {
self.svg = svg
super.init(frame: .init(origin: .zero, size: svg.size))
isOpaque = false
}
@available(*, unavailable) required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func draw(_ rect: CGRect) {
guard let context = UIGraphicsGetCurrentContext() else { return }
svg.draw(in: context, size: rect.size)
}
}
UPDATE: I found that the problem is with the image that I'm trying to download. Is there any problem with encoding types?
My original image is .utf8 (the one that cannot be assigned) and a one that worked for me (downloaded from internet) is isoLatin1.
That is strange - are you able to share the SVG which fails to decode?
This is the image @ollieatkinson :