7
7
8
8
#import " SDImagePDFCoder.h"
9
9
#import " SDWebImagePDFCoderDefine.h"
10
+ #import " objc/runtime.h"
10
11
11
12
#define SD_FOUR_CC (c1,c2,c3,c4 ) ((uint32_t )(((c4) << 24 ) | ((c3) << 16 ) | ((c2) << 8 ) | (c1)))
12
13
@@ -22,6 +23,92 @@ + (instancetype)_imageWithCGPDFPage:(CGPDFPageRef)page scale:(double)scale orien
22
23
@end
23
24
#endif
24
25
26
+ #if SD_MAC
27
+ static void *kNSGraphicsContextScaleFactorKey ;
28
+
29
+ static CGContextRef SDCGContextCreateBitmapContext (CGSize size, BOOL opaque, CGFloat scale) {
30
+ if (scale == 0 ) {
31
+ // Match `UIGraphicsBeginImageContextWithOptions`, reset to the scale factor of the device’s main screen if scale is 0.
32
+ scale = [NSScreen mainScreen ].backingScaleFactor ;
33
+ }
34
+ size_t width = ceil (size.width * scale);
35
+ size_t height = ceil (size.height * scale);
36
+ if (width < 1 || height < 1 ) return NULL ;
37
+
38
+ // pre-multiplied BGRA for non-opaque, BGRX for opaque, 8-bits per component, as Apple's doc
39
+ CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB ();
40
+ CGImageAlphaInfo alphaInfo = kCGBitmapByteOrder32Host | (opaque ? kCGImageAlphaNoneSkipFirst : kCGImageAlphaPremultipliedFirst );
41
+ CGContextRef context = CGBitmapContextCreate (NULL , width, height, 8 , 0 , space, kCGBitmapByteOrderDefault | alphaInfo);
42
+ CGColorSpaceRelease (space);
43
+ if (!context) {
44
+ return NULL ;
45
+ }
46
+ CGContextScaleCTM (context, scale, scale);
47
+
48
+ return context;
49
+ }
50
+ #endif
51
+
52
+ static void SDGraphicsBeginImageContextWithOptions (CGSize size, BOOL opaque, CGFloat scale) {
53
+ #if SD_UIKIT || SD_WATCH
54
+ UIGraphicsBeginImageContextWithOptions (size, opaque, scale);
55
+ #else
56
+ CGContextRef context = SDCGContextCreateBitmapContext (size, opaque, scale);
57
+ if (!context) {
58
+ return ;
59
+ }
60
+ NSGraphicsContext *graphicsContext = [NSGraphicsContext graphicsContextWithCGContext: context flipped: NO ];
61
+ objc_setAssociatedObject (graphicsContext, &kNSGraphicsContextScaleFactorKey , @(scale), OBJC_ASSOCIATION_RETAIN );
62
+ CGContextRelease (context);
63
+ [NSGraphicsContext saveGraphicsState ];
64
+ NSGraphicsContext .currentContext = graphicsContext;
65
+ #endif
66
+ }
67
+
68
+ static CGContextRef SDGraphicsGetCurrentContext (void ) {
69
+ #if SD_UIKIT || SD_WATCH
70
+ return UIGraphicsGetCurrentContext ();
71
+ #else
72
+ return NSGraphicsContext .currentContext .CGContext ;
73
+ #endif
74
+ }
75
+
76
+ static void SDGraphicsEndImageContext (void ) {
77
+ #if SD_UIKIT || SD_WATCH
78
+ UIGraphicsEndImageContext ();
79
+ #else
80
+ [NSGraphicsContext restoreGraphicsState ];
81
+ #endif
82
+ }
83
+
84
+ static UIImage * SDGraphicsGetImageFromCurrentImageContext (void ) {
85
+ #if SD_UIKIT || SD_WATCH
86
+ return UIGraphicsGetImageFromCurrentImageContext ();
87
+ #else
88
+ NSGraphicsContext *context = NSGraphicsContext .currentContext ;
89
+ CGContextRef contextRef = context.CGContext ;
90
+ if (!contextRef) {
91
+ return nil ;
92
+ }
93
+ CGImageRef imageRef = CGBitmapContextCreateImage (contextRef);
94
+ if (!imageRef) {
95
+ return nil ;
96
+ }
97
+ CGFloat scale = 0 ;
98
+ NSNumber *scaleFactor = objc_getAssociatedObject (context, &kNSGraphicsContextScaleFactorKey );
99
+ if ([scaleFactor isKindOfClass: [NSNumber class ]]) {
100
+ scale = scaleFactor.doubleValue ;
101
+ }
102
+ if (!scale) {
103
+ // reset to the scale factor of the device’s main screen if scale is 0.
104
+ scale = [NSScreen mainScreen ].backingScaleFactor ;
105
+ }
106
+ NSImage *image = [[NSImage alloc ] initWithCGImage: imageRef scale: scale orientation: kCGImagePropertyOrientationUp ];
107
+ CGImageRelease (imageRef);
108
+ return image;
109
+ #endif
110
+ }
111
+
25
112
@implementation SDImagePDFCoder
26
113
27
114
+ (SDImagePDFCoder *)sharedCoder {
@@ -43,13 +130,17 @@ - (UIImage *)decodedImageWithData:(NSData *)data options:(SDImageCoderOptions *)
43
130
}
44
131
45
132
NSUInteger pageNumber = 0 ;
133
+ BOOL preferredBitmap = NO ;
46
134
CGSize imageSize = CGSizeZero;
47
135
BOOL preserveAspectRatio = YES ;
48
136
// Parse args
49
137
SDWebImageContext *context = options[SDImageCoderWebImageContext];
50
138
if (context[SDWebImageContextPDFPageNumber]) {
51
139
pageNumber = [context[SDWebImageContextPDFPageNumber] unsignedIntegerValue ];
52
140
}
141
+ if (context[SDWebImageContextPDFPerferredBitmap]) {
142
+ preferredBitmap = [context[SDWebImageContextPDFPerferredBitmap] boolValue ];
143
+ }
53
144
if (context[SDWebImageContextPDFImageSize]) {
54
145
NSValue *sizeValue = context[SDWebImageContextPDFImageSize];
55
146
#if SD_UIKIT
@@ -62,7 +153,14 @@ - (UIImage *)decodedImageWithData:(NSData *)data options:(SDImageCoderOptions *)
62
153
preserveAspectRatio = [context[SDWebImageContextPDFImagePreserveAspectRatio] boolValue ];
63
154
}
64
155
65
- UIImage *image = [self sd_createPDFImageWithData: data pageNumber: pageNumber targetSize: imageSize preserveAspectRatio: preserveAspectRatio];
156
+ UIImage *image;
157
+ if (!preferredBitmap && [self .class supportsVectorPDFImage ]) {
158
+ image = [self createVectorPDFWithData: data pageNumber: pageNumber];
159
+ } else {
160
+ image = [self createBitmapPDFWithData: data pageNumber: pageNumber targetSize: imageSize preserveAspectRatio: preserveAspectRatio];
161
+ }
162
+
163
+ image.sd_imageFormat = SDImageFormatPDF;
66
164
67
165
return image;
68
166
}
@@ -76,8 +174,8 @@ - (NSData *)encodedDataWithImage:(UIImage *)image format:(SDImageFormat)format o
76
174
return nil ;
77
175
}
78
176
79
- // Using Core Graphics to draw PDF but not PDFKit(iOS 11+/macOS 10.4+) to keep old firmware compatible
80
- - (UIImage *)sd_createPDFImageWithData : (nonnull NSData *)data pageNumber : (NSUInteger )pageNumber targetSize : (CGSize) targetSize preserveAspectRatio : ( BOOL ) preserveAspectRatio {
177
+ # pragma mark - Vector PDF representation
178
+ - (UIImage *)createVectorPDFWithData : (nonnull NSData *)data pageNumber : (NSUInteger )pageNumber {
81
179
NSParameterAssert (data);
82
180
UIImage *image;
83
181
@@ -87,12 +185,10 @@ - (UIImage *)sd_createPDFImageWithData:(nonnull NSData *)data pageNumber:(NSUInt
87
185
if (!imageRep) {
88
186
return nil ;
89
187
}
90
- imageRep.currentPage = pageNumber
188
+ imageRep.currentPage = pageNumber;
91
189
image = [[NSImage alloc ] initWithSize: imageRep.size];
92
190
[image addRepresentation: imageRep];
93
-
94
191
#else
95
-
96
192
CGDataProviderRef provider = CGDataProviderCreateWithCFData ((__bridge CFDataRef)data);
97
193
if (!provider) {
98
194
return nil ;
@@ -110,11 +206,33 @@ - (UIImage *)sd_createPDFImageWithData:(nonnull NSData *)data pageNumber:(NSUInt
110
206
return nil ;
111
207
}
112
208
113
- // Check if we can use built-in PDF image support, instead of draw bitmap
114
- if ([[self class ] supportsBuiltInPDFImage ]) {
115
- UIImage *image = [UIImage _imageWithCGPDFPage: page];
209
+ image = [UIImage _imageWithCGPDFPage: page];
210
+ CGPDFDocumentRelease (document);
211
+ #endif
212
+
213
+ return image;
214
+ }
215
+
216
+ #pragma mark - Bitmap PDF representation
217
+ - (UIImage *)createBitmapPDFWithData : (nonnull NSData *)data pageNumber : (NSUInteger )pageNumber targetSize : (CGSize)targetSize preserveAspectRatio : (BOOL )preserveAspectRatio {
218
+ NSParameterAssert (data);
219
+ UIImage *image;
220
+
221
+ CGDataProviderRef provider = CGDataProviderCreateWithCFData ((__bridge CFDataRef)data);
222
+ if (!provider) {
223
+ return nil ;
224
+ }
225
+ CGPDFDocumentRef document = CGPDFDocumentCreateWithProvider (provider);
226
+ CGDataProviderRelease (provider);
227
+ if (!document) {
228
+ return nil ;
229
+ }
230
+
231
+ // `CGPDFDocumentGetPage` page number is 1-indexed.
232
+ CGPDFPageRef page = CGPDFDocumentGetPage (document, pageNumber + 1 );
233
+ if (!page) {
116
234
CGPDFDocumentRelease (document);
117
- return image ;
235
+ return nil ;
118
236
}
119
237
120
238
CGPDFBox box = kCGPDFCropBox ;
@@ -134,31 +252,33 @@ - (UIImage *)sd_createPDFImageWithData:(nonnull NSData *)data pageNumber:(NSUInt
134
252
CGAffineTransform scaleTransform = CGAffineTransformMakeScale (xScale, yScale);
135
253
CGAffineTransform transform = CGPDFPageGetDrawingTransform (page, box, drawRect, 0 , preserveAspectRatio);
136
254
137
- UIGraphicsBeginImageContextWithOptions (targetRect.size , NO , 0 );
138
- CGContextRef context = UIGraphicsGetCurrentContext ();
255
+ SDGraphicsBeginImageContextWithOptions (targetRect.size , NO , 0 );
256
+ CGContextRef context = SDGraphicsGetCurrentContext ();
139
257
140
- // Core Graphics coordinate system use the bottom-left, iOS use the flipped one
258
+ #if SD_UIKIT
259
+ // Core Graphics coordinate system use the bottom-left, UIkit use the flipped one
141
260
CGContextTranslateCTM (context, 0 , targetRect.size .height );
142
261
CGContextScaleCTM (context, 1 , -1 );
262
+ #endif
143
263
144
264
CGContextConcatCTM (context, scaleTransform);
145
265
CGContextConcatCTM (context, transform);
146
266
147
267
CGContextDrawPDFPage (context, page);
148
268
149
- image = UIGraphicsGetImageFromCurrentImageContext ();
150
- UIGraphicsEndImageContext ();
269
+ image = SDGraphicsGetImageFromCurrentImageContext ();
270
+ SDGraphicsEndImageContext ();
151
271
152
272
CGPDFDocumentRelease (document);
153
- #endif
154
-
155
- image.sd_imageFormat = SDImageFormatPDF;
156
273
157
274
return image;
158
275
}
159
276
160
- #if SD_UIKIT
161
- + (BOOL )supportsBuiltInPDFImage {
277
+ + (BOOL )supportsVectorPDFImage {
278
+ #if SD_MAC
279
+ // macOS's `NSImage` supports PDF built-in rendering
280
+ return YES ;
281
+ #else
162
282
static dispatch_once_t onceToken;
163
283
static BOOL supports;
164
284
dispatch_once (&onceToken, ^{
@@ -170,8 +290,8 @@ + (BOOL)supportsBuiltInPDFImage {
170
290
}
171
291
});
172
292
return supports;
173
- }
174
293
#endif
294
+ }
175
295
176
296
+ (BOOL )isPDFFormatForData : (NSData *)data {
177
297
if (!data) {
0 commit comments