Skip to content

Using try-catch for CoreSVG vector image before rendering on screen, protect some crashes for user #44

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Example/SDWebImageSVGCoder/SDViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ - (void)viewDidLoad
NSURL *svgURL = [NSURL URLWithString:@"https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/w3c.svg"];
NSURL *svgURL2 = [NSURL URLWithString:@"https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/wikimedia.svg"];
NSURL *svgURL3 = [NSURL URLWithString:@"https://simpleicons.org/icons/github.svg"];
// Some SVG will fail, this demo check them and the C++/Objc exception should be catched by SDK
NSURL *badSVGURL = [NSURL URLWithString:@"https://openseauserdata.com/files/b809fe0925eb3629bb1d3edb1fafcc88.svg"];
[SDWebImageManager.sharedManager loadImageWithURL:badSVGURL options:0 progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {

}];

CGSize screenSize = self.view.bounds.size;

Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ If you still worry about the SPI usage, you can use [SDWebImageSVGKitPlugin](htt

There is also another solution: [SVG-Native](https://w3c.github.io/svgwg/specs/svg-native/index.html), a new W3C standard from Adobe, which is a subset of SVG/1.1. Both Apple/Google/Microsoft already join the agreement for this standard, you can try to write your own coder using code from [SVG-native-viewer](https://github.com/adobe/svg-native-viewer/blob/master/svgnative/example/testCocoaCG/SVGNSView.mm) and adopt SVG-native for vector images.

> Warning: Some user report that Apple's CoreSVG has compatible issue for some SVGs (like using non-system Font, gradient), from v1.7.0 we protect some cases, but other exceptions are un-catchable. For these crashes, either use `Render SVG as bitmap image` (see below) or edit your SVG source file to make Apple's CoeSVG compatible.

## Example

To run the example project, clone the repo, and run `pod install` from the Example directory first.
Expand Down
39 changes: 32 additions & 7 deletions SDWebImageSVGCoder/Classes/SDImageSVGCoder.m
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,12 @@ + (SDImageSVGCoder *)sharedCoder {
}

+ (void)initialize {
SDCGSVGDocumentRetain = dlsym(RTLD_DEFAULT, SDBase64DecodedString(@"Q0dTVkdEb2N1bWVudFJldGFpbg==").UTF8String);
SDCGSVGDocumentRelease = dlsym(RTLD_DEFAULT, SDBase64DecodedString(@"Q0dTVkdEb2N1bWVudFJlbGVhc2U=").UTF8String);
SDCGSVGDocumentCreateFromData = dlsym(RTLD_DEFAULT, SDBase64DecodedString(@"Q0dTVkdEb2N1bWVudENyZWF0ZUZyb21EYXRh").UTF8String);
SDCGSVGDocumentWriteToData = dlsym(RTLD_DEFAULT, SDBase64DecodedString(@"Q0dTVkdEb2N1bWVudFdyaXRlVG9EYXRh").UTF8String);
SDCGContextDrawSVGDocument = dlsym(RTLD_DEFAULT, SDBase64DecodedString(@"Q0dDb250ZXh0RHJhd1NWR0RvY3VtZW50").UTF8String);
SDCGSVGDocumentGetCanvasSize = dlsym(RTLD_DEFAULT, SDBase64DecodedString(@"Q0dTVkdEb2N1bWVudEdldENhbnZhc1NpemU=").UTF8String);
SDCGSVGDocumentRetain = (CGSVGDocumentRef (*)(CGSVGDocumentRef))dlsym(RTLD_DEFAULT, SDBase64DecodedString(@"Q0dTVkdEb2N1bWVudFJldGFpbg==").UTF8String);
SDCGSVGDocumentRelease = (void (*)(CGSVGDocumentRef))dlsym(RTLD_DEFAULT, SDBase64DecodedString(@"Q0dTVkdEb2N1bWVudFJlbGVhc2U=").UTF8String);
SDCGSVGDocumentCreateFromData = (CGSVGDocumentRef (*)(CFDataRef data, CFDictionaryRef options))dlsym(RTLD_DEFAULT, SDBase64DecodedString(@"Q0dTVkdEb2N1bWVudENyZWF0ZUZyb21EYXRh").UTF8String);
SDCGSVGDocumentWriteToData = (void (*)(CGSVGDocumentRef document, CFDataRef data, CFDictionaryRef options))dlsym(RTLD_DEFAULT, SDBase64DecodedString(@"Q0dTVkdEb2N1bWVudFdyaXRlVG9EYXRh").UTF8String);
SDCGContextDrawSVGDocument = (void (*)(CGContextRef context, CGSVGDocumentRef document))dlsym(RTLD_DEFAULT, SDBase64DecodedString(@"Q0dDb250ZXh0RHJhd1NWR0RvY3VtZW50").UTF8String);
SDCGSVGDocumentGetCanvasSize = (CGSize (*)(CGSVGDocumentRef document))dlsym(RTLD_DEFAULT, SDBase64DecodedString(@"Q0dTVkdEb2N1bWVudEdldENhbnZhc1NpemU=").UTF8String);
#if SD_UIKIT || SD_WATCH
SDImageWithCGSVGDocumentSEL = NSSelectorFromString(SDBase64DecodedString(@"X2ltYWdlV2l0aENHU1ZHRG9jdW1lbnQ6"));
SDCGSVGDocumentSEL = NSSelectorFromString(SDBase64DecodedString(@"X0NHU1ZHRG9jdW1lbnQ="));
Expand Down Expand Up @@ -152,7 +152,15 @@ - (NSData *)encodedDataWithImage:(UIImage *)image format:(SDImageFormat)format o
return nil;
}

SDCGSVGDocumentWriteToData(document, (__bridge CFDataRef)data, NULL);
@try {
// WARNING! Some CoreSVG exceptions can be catched, but not always
// If you finally crash here (un-catchable), you can only workaround (or hope Apple fix this)
// Do not encode vector UIImage into NSData, query `SDImageCache` for the same key and get back SVG Data
SDCGSVGDocumentWriteToData(document, (__bridge CFMutableDataRef)data, NULL);
} @catch (...) {
// CoreSVG export failed
return nil;
}

return [data copy];
}
Expand All @@ -178,6 +186,23 @@ - (UIImage *)createVectorSVGWithData:(nonnull NSData *)data {
image = ((UIImage *(*)(id,SEL,CGSVGDocumentRef))[UIImage.class methodForSelector:SDImageWithCGSVGDocumentSEL])(UIImage.class, SDImageWithCGSVGDocumentSEL, document);
SDCGSVGDocumentRelease(document);
#endif

// CoreSVG has compatible for some SVG/1.1 format (like Font issue) and may crash when rendering on screen (not here, Core Animation commit time)
// So, we snapshot a 1x1 pixel image and try catch here to check :(

SDGraphicsImageRenderer *renderer = [[SDGraphicsImageRenderer alloc] initWithSize:CGSizeMake(1, 1)];
@try {
__unused UIImage *dummyImage = [renderer imageWithActions:^(CGContextRef _Nonnull context) {
// WARNING! Some CoreSVG exceptions can be catched, but not always
// If you finally crash here (un-catchable), you can only workaround (or hope Apple fix this)
// Change your code to use `SDWebImageContextImageThumbnailPixelSize` context option with enough size to render bitmap SVG instead
[image drawInRect:CGRectMake(0, 0, 1, 1)];
}];
} @catch (...) {
// CoreSVG decode failed
return nil;
}

return image;
}

Expand Down