SDWebImage / SDWebImageSVGCoder

A SVG coder plugin for SDWebImage, using Apple's built-in framework
MIT License
103 stars 34 forks source link

Decoded SVG is not a vector on macOS #53

Closed ladvoc closed 3 months ago

ladvoc commented 3 months ago

After decoding an SVG to an NSImage on macOS 14.4.1, the sd_isVector property returns false:

func testDecodeSVG() throws {
    let url = Bundle(for: AppTests.self).url(forResource: "flag", withExtension: "svg")!
    let svgData = try! Data(contentsOf: url)

    let image = SDImageSVGCoder.shared.decodedImage(with: svgData, options: [:])!
    XCTAssertTrue(image.sd_isVector) // false
    print(image.representations)
}

However, printing the NSImage's representations reveals it does contain an NSSVGImageRep:

[_NSSVGImageRep 0x600002e7caf0 Size={1235, 650} ColorSpace=Generic RGB colorspace BPS=0 Pixels=0x0 Alpha=NO AppearanceName=(null)]
dreampiggy commented 3 months ago

It's open sourced. Just check and debug to find out the reason: https://github.com/SDWebImage/SDWebImage/blob/a936e64ab1447952bb22c1404b2b253b4415f453/SDWebImage/Core/UIImage%2BMetadata.m#L145

Maybe the representation is not what you see here

dreampiggy commented 3 months ago

Or maybe the class name changed. You can debug each value

dreampiggy commented 3 months ago

@ladvoc Any news ? Or you can provide a reproducable demo for me to investigate

ladvoc commented 3 months ago

See this repository for a simple reproducible demo.

The issue appears to be related to the implementation of sd_isVector. For reference, I have included its implementation from UIImage+Metadata.m below:

- (BOOL)sd_isVector {
    NSRect imageRect = NSMakeRect(0, 0, self.size.width, self.size.height);
    NSImageRep *imageRep = [self bestRepresentationForRect:imageRect context:nil hints:nil];

    if ([imageRep isKindOfClass:[NSPDFImageRep class]]) {
        return YES;
    }
    if ([imageRep isKindOfClass:[NSEPSImageRep class]]) {
        return YES;
    }
    if ([NSStringFromClass(imageRep.class) hasSuffix:@"NSSVGImageRep"]) {
        return YES;
    }
    return NO;
}

In the case of an SVG-backed NSImage, it expects bestRepresentationForRect to return an instance of NSSVGImageRep (with or without the underscore prefix). However, when run on my system, the debugger reveals this method actually returns an NSImageRepGeometryProxy which wraps the underlying _NSSVGImageRep. Consequently, the third condition isn't hit, and the method returns false.

dreampiggy commented 3 months ago

Don't rely on that sd_isVector maybe. Our SDK internal code seems does nearly don't rely on this API.

Or, a better way to workaround is to do not touch that bestRepresentationForRect, but instead, we use our own solution to find out the actual represent on macOS AppKit.

Another question, I don't have macOS 14.4.1, isn't that NSImageRepGeometryProxy should be a NSProxy ? If so, we can change this NSStringFromClass(imageRep.class), to another API like isKindOfClass

dreampiggy commented 3 months ago

You can use lldb debugger to print NSImageRepGeometryProxy's class.superClass or check NSImageRepGeometryProxy.isProxy https://developer.apple.com/documentation/objectivec/1418956-nsobject/1418528-isproxy?language=objc#

ladvoc commented 3 months ago

Yes, NSImageRepGeometryProxy is a proxy; [imageRep isProxy] is true, and [imageRep isKindOfClass: NSClassFromString(@"_NSSVGImageRep")] is also true.

dreampiggy commented 3 months ago

Check if this https://github.com/SDWebImage/SDWebImage/pull/3716 solve your issue ?

You can use the branch or commit dependency of SDWebImage in your App to test, and I may release another patch version after you test finished.

ladvoc commented 3 months ago

Yep, this fixes the issue! See my updated sample app in which I have pinned the SDWebImage dependency to your branch.

dreampiggy commented 3 months ago

I've released the SDWebImage v5.19.3 version which contains (only) this fix.