Skip to content

Commit

Permalink
Using try-catch for CoreSVG vector image before rendering on screen, …
Browse files Browse the repository at this point in the history
…protect some crash for user

However, this can only solve some of crashes, some of crashes like Danger Pointer are un-catchable
  • Loading branch information
dreampiggy committed May 5, 2023
1 parent afbe025 commit 7ff561a
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 7 deletions.
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
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

0 comments on commit 7ff561a

Please sign in to comment.