Explorar o código

Merge pull request #2936 from dreampiggy/feature_better_vector_support_pdf

Feature - better support for vector format detection, now PDF rasterized bitmap is built-in
DreamPiggy %!s(int64=6) %!d(string=hai) anos
pai
achega
5c3c40288f

+ 1 - 0
Examples/SDWebImage Demo/MasterViewController.m

@@ -75,6 +75,7 @@
                     @"https://nokiatech.github.io/heif/content/images/ski_jump_1440x960.heic",
                     @"https://nokiatech.github.io/heif/content/image_sequences/starfield_animation.heic",
                     @"https://s2.ax1x.com/2019/11/01/KHYIgJ.gif",
+                    @"https://raw.githubusercontent.com/icons8/flat-color-icons/master/pdf/stack_of_photos.pdf",
                     @"https://nr-platform.s3.amazonaws.com/uploads/platform/published_extension/branding_icon/275/AmazonS3.png",
                     @"http://via.placeholder.com/200x200.jpg",
                     nil];

+ 2 - 0
SDWebImage/Core/NSData+ImageContentType.h

@@ -23,6 +23,8 @@ static const SDImageFormat SDImageFormatTIFF      = 3;
 static const SDImageFormat SDImageFormatWebP      = 4;
 static const SDImageFormat SDImageFormatHEIC      = 5;
 static const SDImageFormat SDImageFormatHEIF      = 6;
+static const SDImageFormat SDImageFormatPDF       = 7;
+static const SDImageFormat SDImageFormatSVG       = 8;
 
 /**
  NSData category about the image content type and UTI.

+ 29 - 0
SDWebImage/Core/NSData+ImageContentType.m

@@ -17,6 +17,7 @@
 
 // Currently Image/IO does not support WebP
 #define kSDUTTypeWebP ((__bridge CFStringRef)@"public.webp")
+#define kSVGTagEnd @"</svg>"
 
 @implementation NSData (ImageContentType)
 
@@ -65,6 +66,24 @@
             }
             break;
         }
+        case 0x25: {
+            if (data.length >= 4) {
+                //%PDF
+                NSString *testString = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(1, 3)] encoding:NSASCIIStringEncoding];
+                if ([testString isEqualToString:@"PDF"]) {
+                    return SDImageFormatPDF;
+                }
+            }
+        }
+        case 0x3C: {
+            if (data.length > 100) {
+                // Check end with SVG tag
+                NSString *testString = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(data.length - 100, 100)] encoding:NSASCIIStringEncoding];
+                if ([testString containsString:kSVGTagEnd]) {
+                    return SDImageFormatSVG;
+                }
+            }
+        }
     }
     return SDImageFormatUndefined;
 }
@@ -93,6 +112,12 @@
         case SDImageFormatHEIF:
             UTType = kSDUTTypeHEIF;
             break;
+        case SDImageFormatPDF:
+            UTType = kUTTypePDF;
+            break;
+        case SDImageFormatSVG:
+            UTType = kUTTypeScalableVectorGraphics;
+            break;
         default:
             // default is kUTTypePNG
             UTType = kUTTypePNG;
@@ -120,6 +145,10 @@
         imageFormat = SDImageFormatHEIC;
     } else if (CFStringCompare(uttype, kSDUTTypeHEIF, 0) == kCFCompareEqualTo) {
         imageFormat = SDImageFormatHEIF;
+    } else if (CFStringCompare(uttype, kUTTypePDF, 0) == kCFCompareEqualTo) {
+        imageFormat = SDImageFormatPDF;
+    } else if (CFStringCompare(uttype, kUTTypeScalableVectorGraphics, 0) == kCFCompareEqualTo) {
+        imageFormat = SDImageFormatSVG;
     } else {
         imageFormat = SDImageFormatUndefined;
     }

+ 29 - 0
SDWebImage/Core/SDAnimatedImage.m

@@ -12,6 +12,7 @@
 #import "SDImageCodersManager.h"
 #import "SDImageFrame.h"
 #import "UIImage+MemoryCacheCost.h"
+#import "UIImage+Metadata.h"
 #import "SDImageAssetManager.h"
 #import "objc/runtime.h"
 
@@ -298,3 +299,31 @@ static CGFloat SDImageScaleFromPath(NSString *string) {
 }
 
 @end
+
+@implementation SDAnimatedImage (Metadata)
+
+- (BOOL)sd_isAnimated {
+    return YES;
+}
+
+- (NSUInteger)sd_imageLoopCount {
+    return self.animatedImageLoopCount;
+}
+
+- (void)setSd_imageLoopCount:(NSUInteger)sd_imageLoopCount {
+    return;
+}
+
+- (SDImageFormat)sd_imageFormat {
+    return self.animatedImageFormat;
+}
+
+- (void)setSd_imageFormat:(SDImageFormat)sd_imageFormat {
+    return;
+}
+
+- (BOOL)sd_isVector {
+    return NO;
+}
+
+@end

+ 2 - 2
SDWebImage/Core/SDAnimatedImageView.m

@@ -470,10 +470,10 @@
 // NSImageView use a subview. We need this subview's layer for actual rendering.
 // Why using this design may because of properties like `imageAlignment` and `imageScaling`, which it's not available for UIImageView.contentMode (it's impossible to align left and keep aspect ratio at the same time)
 - (NSView *)imageView {
-    NSImageView *imageView = imageView = objc_getAssociatedObject(self, NSSelectorFromString(@"_imageView"));
+    NSImageView *imageView = imageView = objc_getAssociatedObject(self, SD_SEL_SPI(imageView));
     if (!imageView) {
         // macOS 10.14
-        imageView = objc_getAssociatedObject(self, NSSelectorFromString(@"_imageSubview"));
+        imageView = objc_getAssociatedObject(self, SD_SEL_SPI(imageSubview));
     }
     return imageView;
 }

+ 4 - 0
SDWebImage/Core/SDImageCoderHelper.m

@@ -575,6 +575,10 @@ static const CGFloat kDestSeemOverlap = 2.0f;   // the numbers of pixels to over
     if (image.sd_isAnimated) {
         return NO;
     }
+    // do not decode vector images
+    if (image.sd_isVector) {
+        return NO;
+    }
     
     return YES;
 }

+ 39 - 11
SDWebImage/Core/SDImageIOAnimatedCoder.m

@@ -14,6 +14,9 @@
 #import "SDAnimatedImageRep.h"
 #import "UIImage+ForceDecode.h"
 
+// Specify DPI for vector format in CGImageSource, like PDF
+static NSString * kSDCGImageSourceRasterizationDPI = @"kCGImageSourceRasterizationDPI";
+
 @interface SDImageIOCoderFrame : NSObject
 
 @property (nonatomic, assign) NSUInteger index; // Frame index (zero based)
@@ -158,9 +161,33 @@
         exifOrientation = kCGImagePropertyOrientationUp;
     }
     
+    CFStringRef uttype = CGImageSourceGetType(source);
+    // Check vector format
+    BOOL isVector = NO;
+    if ([NSData sd_imageFormatFromUTType:uttype] == SDImageFormatPDF) {
+        isVector = YES;
+    }
+    
     CGImageRef imageRef;
-    if (thumbnailSize.width == 0 || thumbnailSize.height == 0 || (pixelWidth <= thumbnailSize.width && pixelHeight <= thumbnailSize.height)) {
-        imageRef = CGImageSourceCreateImageAtIndex(source, index, NULL);
+    if (thumbnailSize.width == 0 || thumbnailSize.height == 0 || pixelWidth == 0 || pixelHeight == 0 || (pixelWidth <= thumbnailSize.width && pixelHeight <= thumbnailSize.height)) {
+        NSDictionary *options;
+        if (isVector) {
+            if (thumbnailSize.width == 0 || thumbnailSize.height == 0) {
+                // Provide the default pixel count for vector images, simply just use the screen size
+#if SD_WATCH
+                thumbnailSize = WKInterfaceDevice.currentDevice.screenBounds.size;
+#elif SD_UIKIT
+                thumbnailSize = UIScreen.mainScreen.bounds.size;
+#elif SD_MAC
+                thumbnailSize = NSScreen.mainScreen.frame.size;
+#endif
+            }
+            CGFloat maxPixelSize = MAX(thumbnailSize.width, thumbnailSize.height);
+            NSUInteger DPIPerPixel = 2;
+            NSUInteger rasterizationDPI = maxPixelSize * DPIPerPixel;
+            options = @{kSDCGImageSourceRasterizationDPI : @(rasterizationDPI)};
+        }
+        imageRef = CGImageSourceCreateImageAtIndex(source, index, (__bridge CFDictionaryRef)options);
     } else {
         NSMutableDictionary *thumbnailOptions = [NSMutableDictionary dictionary];
         thumbnailOptions[(__bridge NSString *)kCGImageSourceCreateThumbnailWithTransform] = @(preserveAspectRatio);
@@ -179,21 +206,22 @@
         thumbnailOptions[(__bridge NSString *)kCGImageSourceThumbnailMaxPixelSize] = @(maxPixelSize);
         thumbnailOptions[(__bridge NSString *)kCGImageSourceCreateThumbnailFromImageIfAbsent] = @(YES);
         imageRef = CGImageSourceCreateThumbnailAtIndex(source, index, (__bridge CFDictionaryRef)thumbnailOptions);
+    }
+    if (!imageRef) {
+        return nil;
+    }
+    
+    if (thumbnailSize.width > 0 && thumbnailSize.height > 0) {
         if (preserveAspectRatio) {
             // kCGImageSourceCreateThumbnailWithTransform will apply EXIF transform as well, we should not apply twice
             exifOrientation = kCGImagePropertyOrientationUp;
         } else {
             // `CGImageSourceCreateThumbnailAtIndex` take only pixel dimension, if not `preserveAspectRatio`, we should manual scale to the target size
-            if (imageRef) {
-                CGImageRef scaledImageRef = [SDImageCoderHelper CGImageCreateScaled:imageRef size:thumbnailSize];
-                CGImageRelease(imageRef);
-                imageRef = scaledImageRef;
-            }
+            CGImageRef scaledImageRef = [SDImageCoderHelper CGImageCreateScaled:imageRef size:thumbnailSize];
+            CGImageRelease(imageRef);
+            imageRef = scaledImageRef;
         }
     }
-    if (!imageRef) {
-        return nil;
-    }
     
 #if SD_UIKIT || SD_WATCH
     UIImageOrientation imageOrientation = [SDImageCoderHelper imageOrientationFromEXIFOrientation:exifOrientation];
@@ -363,7 +391,7 @@
         if (scaleFactor != nil) {
             scale = MAX([scaleFactor doubleValue], 1);
         }
-        image = [SDImageIOAnimatedCoder createFrameAtIndex:0 source:_imageSource scale:scale preserveAspectRatio:_preserveAspectRatio thumbnailSize:_thumbnailSize];
+        image = [self.class createFrameAtIndex:0 source:_imageSource scale:scale preserveAspectRatio:_preserveAspectRatio thumbnailSize:_thumbnailSize];
         if (image) {
             image.sd_imageFormat = self.class.imageFormat;
         }

+ 6 - 0
SDWebImage/Core/SDWebImageDefine.h

@@ -195,6 +195,12 @@ typedef NS_OPTIONS(NSUInteger, SDWebImageOptions) {
      * Note if you use this when using the custom cache serializer, or using the transformer, we will also wait until the output image data written is finished.
      */
     SDWebImageWaitStoreCache = 1 << 22,
+    
+    /**
+     * We usually don't apply transform on vector images, because vector images supports dynamically changing to any size, rasterize to a fixed size will loss details. To modify vector images, you can process the vector data at runtime (such as modifying PDF tag / SVG element).
+     * Use this flag to transform them anyway.
+     */
+    SDWebImageTransformVectorImage = 1 << 23,
 };
 
 

+ 6 - 2
SDWebImage/Core/SDWebImageManager.m

@@ -332,7 +332,9 @@ static id<SDImageLoader> _defaultImageLoader;
     id<SDImageTransformer> transformer = context[SDWebImageContextImageTransformer];
     id<SDWebImageCacheSerializer> cacheSerializer = context[SDWebImageContextCacheSerializer];
     
-    BOOL shouldTransformImage = downloadedImage && (!downloadedImage.sd_isAnimated || (options & SDWebImageTransformAnimatedImage)) && transformer;
+    BOOL shouldTransformImage = downloadedImage && transformer;
+    shouldTransformImage = shouldTransformImage && (!downloadedImage.sd_isAnimated || (options & SDWebImageTransformAnimatedImage));
+    shouldTransformImage = shouldTransformImage && (!downloadedImage.sd_isVector || (options & SDWebImageTransformVectorImage));
     BOOL shouldCacheOriginal = downloadedImage && finished;
     BOOL waitStoreCache = SD_OPTIONS_CONTAINS(options, SDWebImageWaitStoreCache);
     
@@ -380,7 +382,9 @@ static id<SDImageLoader> _defaultImageLoader;
     NSString *key = [self cacheKeyForURL:url context:context];
     id<SDImageTransformer> transformer = context[SDWebImageContextImageTransformer];
     id<SDWebImageCacheSerializer> cacheSerializer = context[SDWebImageContextCacheSerializer];
-    BOOL shouldTransformImage = originalImage && (!originalImage.sd_isAnimated || (options & SDWebImageTransformAnimatedImage)) && transformer;
+    BOOL shouldTransformImage = originalImage && transformer;
+    shouldTransformImage = shouldTransformImage && (!originalImage.sd_isAnimated || (options & SDWebImageTransformAnimatedImage));
+    shouldTransformImage = shouldTransformImage && (!originalImage.sd_isVector || (options & SDWebImageTransformVectorImage));
     BOOL waitStoreCache = SD_OPTIONS_CONTAINS(options, SDWebImageWaitStoreCache);
     // if available, store transformed image to cache
     if (shouldTransformImage) {

+ 9 - 1
SDWebImage/Core/UIImage+Metadata.h

@@ -28,12 +28,20 @@
 
 /**
  * UIKit:
- * Check the `images` array property
+ * Check the `images` array property.
  * AppKit:
  * NSImage currently only support animated via GIF imageRep unlike UIImage. It will check the imageRep's frame count.
  */
 @property (nonatomic, assign, readonly) BOOL sd_isAnimated;
 
+/**
+ * UIKit:
+ * Check the `isSymbolImage` property. Also check the system PDF(iOS 11+) && SVG(iOS 13+) support.
+ * AppKit:
+ * NSImage supports PDF && SVG && EPS imageRep, check the imageRep class.
+ */
+@property (nonatomic, assign, readonly) BOOL sd_isVector;
+
 /**
  * The image format represent the original compressed image data format.
  * If you don't manually specify a format, this information is retrieve from CGImage using `CGImageGetUTType`, which may return nil for non-CG based image. At this time it will return `SDImageFormatUndefined` as default value.

+ 45 - 3
SDWebImage/Core/UIImage+Metadata.m

@@ -8,6 +8,7 @@
 
 #import "UIImage+Metadata.h"
 #import "NSImage+Compatibility.h"
+#import "SDInternalMacros.h"
 #import "objc/runtime.h"
 
 @implementation UIImage (Metadata)
@@ -32,6 +33,32 @@
     return (self.images != nil);
 }
 
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
+- (BOOL)sd_isVector {
+    if (@available(iOS 13.0, tvOS 13.0, watchOS 6.0, *)) {
+        // Xcode 11 supports symbol image, keep Xcode 10 compatible currently
+        SEL SymbolSelector = NSSelectorFromString(@"isSymbolImage");
+        if ([self respondsToSelector:SymbolSelector] && [self performSelector:SymbolSelector]) {
+            return YES;
+        }
+        // SVG
+        SEL SVGSelector = SD_SEL_SPI(CGSVGDocument);
+        if ([self respondsToSelector:SVGSelector] && [self performSelector:SVGSelector]) {
+            return YES;
+        }
+    }
+    if (@available(iOS 11.0, tvOS 11.0, watchOS 4.0, *)) {
+        // PDF
+        SEL PDFSelector = SD_SEL_SPI(CGPDFPage);
+        if ([self respondsToSelector:PDFSelector] && [self performSelector:PDFSelector]) {
+            return YES;
+        }
+    }
+    return NO;
+}
+#pragma clang diagnostic pop
+
 #else
 
 - (NSUInteger)sd_imageLoopCount {
@@ -61,7 +88,7 @@
 }
 
 - (BOOL)sd_isAnimated {
-    BOOL isGIF = NO;
+    BOOL isAnimated = NO;
     NSRect imageRect = NSMakeRect(0, 0, self.size.width, self.size.height);
     NSImageRep *imageRep = [self bestRepresentationForRect:imageRect context:nil hints:nil];
     NSBitmapImageRep *bitmapImageRep;
@@ -70,9 +97,24 @@
     }
     if (bitmapImageRep) {
         NSUInteger frameCount = [[bitmapImageRep valueForProperty:NSImageFrameCount] unsignedIntegerValue];
-        isGIF = frameCount > 1 ? YES : NO;
+        isAnimated = frameCount > 1 ? YES : NO;
+    }
+    return isAnimated;
+}
+
+- (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 isGIF;
+    return NO;
 }
 
 #endif

+ 12 - 0
SDWebImage/Private/SDInternalMacros.h

@@ -21,6 +21,18 @@
 #define SD_OPTIONS_CONTAINS(options, value) (((options) & (value)) == (value))
 #endif
 
+#ifndef SD_CSTRING
+#define SD_CSTRING(str) #str
+#endif
+
+#ifndef SD_NSSTRING
+#define SD_NSSTRING(str) @(SD_CSTRING(str))
+#endif
+
+#ifndef SD_SEL_SPI
+#define SD_SEL_SPI(name) NSSelectorFromString([NSString stringWithFormat:@"_%@", SD_NSSTRING(name)])
+#endif
+
 #ifndef weakify
 #define weakify(...) \
 sd_keywordify \

+ 8 - 0
Tests/SDWebImage Tests.xcodeproj/project.pbxproj

@@ -14,6 +14,9 @@
 		322241802272F808002429DB /* SDUtilsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3222417E2272F808002429DB /* SDUtilsTests.m */; };
 		3226ECBB20754F7700FAFACF /* SDWebImageTestDownloadOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 3226ECBA20754F7700FAFACF /* SDWebImageTestDownloadOperation.m */; };
 		3226ECBC20754F7700FAFACF /* SDWebImageTestDownloadOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 3226ECBA20754F7700FAFACF /* SDWebImageTestDownloadOperation.m */; };
+		3234306223E2BAC800C290C8 /* TestImage.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 3234306123E2BAC800C290C8 /* TestImage.pdf */; };
+		3234306323E2BAC800C290C8 /* TestImage.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 3234306123E2BAC800C290C8 /* TestImage.pdf */; };
+		3234306423E2BAC800C290C8 /* TestImage.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 3234306123E2BAC800C290C8 /* TestImage.pdf */; };
 		323B8E1F20862322008952BE /* SDWebImageTestLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 323B8E1E20862322008952BE /* SDWebImageTestLoader.m */; };
 		323B8E2020862322008952BE /* SDWebImageTestLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 323B8E1E20862322008952BE /* SDWebImageTestLoader.m */; };
 		324047442271956F007C53E1 /* TestEXIF.png in Resources */ = {isa = PBXBuildFile; fileRef = 324047432271956F007C53E1 /* TestEXIF.png */; };
@@ -107,6 +110,7 @@
 		3222417E2272F808002429DB /* SDUtilsTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDUtilsTests.m; sourceTree = "<group>"; };
 		3226ECB920754F7700FAFACF /* SDWebImageTestDownloadOperation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDWebImageTestDownloadOperation.h; sourceTree = "<group>"; };
 		3226ECBA20754F7700FAFACF /* SDWebImageTestDownloadOperation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDWebImageTestDownloadOperation.m; sourceTree = "<group>"; };
+		3234306123E2BAC800C290C8 /* TestImage.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = TestImage.pdf; sourceTree = "<group>"; };
 		323B8E1D20862322008952BE /* SDWebImageTestLoader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDWebImageTestLoader.h; sourceTree = "<group>"; };
 		323B8E1E20862322008952BE /* SDWebImageTestLoader.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDWebImageTestLoader.m; sourceTree = "<group>"; };
 		324047432271956F007C53E1 /* TestEXIF.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = TestEXIF.png; sourceTree = "<group>"; };
@@ -238,6 +242,7 @@
 				433BBBB81D7EF8260086B6E9 /* TestImage.png */,
 				327A418B211D660600495442 /* TestImage.heic */,
 				32905E63211D786E00460FCF /* TestImage.heif */,
+				3234306123E2BAC800C290C8 /* TestImage.pdf */,
 				327054E1206CEFF3006EA328 /* TestImageAnimated.apng */,
 				3297A09E23374D1600814590 /* TestImageAnimated.heic */,
 			);
@@ -443,6 +448,7 @@
 				3299228B2365DC6C00EAFD97 /* TestImage.heic in Resources */,
 				329922872365DC6C00EAFD97 /* TestLoopCount.gif in Resources */,
 				3299228C2365DC6C00EAFD97 /* TestImage.heif in Resources */,
+				3234306423E2BAC800C290C8 /* TestImage.pdf in Resources */,
 				329922892365DC6C00EAFD97 /* TestImageLarge.jpg in Resources */,
 				3299228A2365DC6C00EAFD97 /* TestImage.png in Resources */,
 				329922842365DC6C00EAFD97 /* MonochromeTestImage.jpg in Resources */,
@@ -461,6 +467,7 @@
 				32B99EA3203B31360017FD66 /* TestImage.gif in Resources */,
 				324047452271956F007C53E1 /* TestEXIF.png in Resources */,
 				32B99EA4203B31360017FD66 /* TestImage.jpg in Resources */,
+				3234306323E2BAC800C290C8 /* TestImage.pdf in Resources */,
 				32B99EA6203B31360017FD66 /* TestImage.png in Resources */,
 				3297A0A023374D1700814590 /* TestImageAnimated.heic in Resources */,
 				32B99EA2203B31360017FD66 /* MonochromeTestImage.jpg in Resources */,
@@ -479,6 +486,7 @@
 				5F7F38AD1AE2A77A00B0E330 /* TestImage.jpg in Resources */,
 				32905E64211D786E00460FCF /* TestImage.heif in Resources */,
 				43828A451DA67F9900000E62 /* TestImageLarge.jpg in Resources */,
+				3234306223E2BAC800C290C8 /* TestImage.pdf in Resources */,
 				433BBBB71D7EF8200086B6E9 /* TestImage.gif in Resources */,
 				433BBBB91D7EF8260086B6E9 /* TestImage.png in Resources */,
 				3297A09F23374D1700814590 /* TestImageAnimated.heic in Resources */,

BIN=BIN
Tests/Tests/Images/TestImage.pdf


+ 28 - 5
Tests/Tests/SDImageCoderTests.m

@@ -156,22 +156,34 @@
         withLocalImageURL:heicURL
          supportsEncoding:supportsEncoding
            encodingFormat:SDImageFormatHEIC
-          isAnimatedImage:isAnimatedImage];
+          isAnimatedImage:isAnimatedImage
+            isVectorImage:NO];
     }
 }
 
+- (void)test17ThatPDFWorks {
+    NSURL *pdfURL = [[NSBundle bundleForClass:[self class]] URLForResource:@"TestImage" withExtension:@"pdf"];
+    [self verifyCoder:[SDImageIOCoder sharedCoder]
+    withLocalImageURL:pdfURL
+     supportsEncoding:NO
+       encodingFormat:SDImageFormatUndefined
+      isAnimatedImage:NO
+        isVectorImage:YES];
+}
+
 - (void)verifyCoder:(id<SDImageCoder>)coder
 withLocalImageURL:(NSURL *)imageUrl
  supportsEncoding:(BOOL)supportsEncoding
   isAnimatedImage:(BOOL)isAnimated {
-    [self verifyCoder:coder withLocalImageURL:imageUrl supportsEncoding:supportsEncoding encodingFormat:SDImageFormatUndefined isAnimatedImage:isAnimated];
+    [self verifyCoder:coder withLocalImageURL:imageUrl supportsEncoding:supportsEncoding encodingFormat:SDImageFormatUndefined isAnimatedImage:isAnimated isVectorImage:NO];
 }
 
 - (void)verifyCoder:(id<SDImageCoder>)coder
   withLocalImageURL:(NSURL *)imageUrl
    supportsEncoding:(BOOL)supportsEncoding
      encodingFormat:(SDImageFormat)encodingFormat
-    isAnimatedImage:(BOOL)isAnimated {
+    isAnimatedImage:(BOOL)isAnimated
+      isVectorImage:(BOOL)isVector {
     NSData *inputImageData = [NSData dataWithContentsOfURL:imageUrl];
     expect(inputImageData).toNot.beNil();
     SDImageFormat inputImageFormat = [NSData sd_imageFormatForImageData:inputImageData];
@@ -204,7 +216,18 @@ withLocalImageURL:(NSURL *)imageUrl
     CGFloat pixelHeight = inputImage.size.height;
     expect(pixelWidth).beGreaterThan(0);
     expect(pixelHeight).beGreaterThan(0);
-    // check thumnail with scratch
+    // check vector format supports thumbnail with screen size
+    if (isVector) {
+#if SD_UIKIT
+        CGFloat maxScreenSize = MAX(UIScreen.mainScreen.bounds.size.width, UIScreen.mainScreen.bounds.size.height);
+#else
+        CGFloat maxScreenSize = MAX(NSScreen.mainScreen.frame.size.width, NSScreen.mainScreen.frame.size.height);
+#endif
+        expect(pixelWidth).equal(maxScreenSize);
+        expect(pixelHeight).equal(maxScreenSize);
+    }
+    
+    // check thumbnail with scratch
     CGFloat thumbnailWidth = 50;
     CGFloat thumbnailHeight = 50;
     UIImage *thumbImage = [coder decodedImageWithData:inputImageData options:@{
@@ -213,7 +236,7 @@ withLocalImageURL:(NSURL *)imageUrl
     }];
     expect(thumbImage).toNot.beNil();
     expect(thumbImage.size).equal(CGSizeMake(thumbnailWidth, thumbnailHeight));
-    // check thumnail with aspect ratio limit
+    // check thumbnail with aspect ratio limit
     thumbImage = [coder decodedImageWithData:inputImageData options:@{
         SDImageCoderDecodeThumbnailPixelSize : @(CGSizeMake(thumbnailWidth, thumbnailHeight)),
         SDImageCoderDecodePreserveAspectRatio : @(YES)