Use CoreGraphics to decode PDF instead of ImageIO to solve iOS 16's issue
We no longer use ImageIO to decode PDF, seems they don't maintain that kSDCGImageSourceRasterizationDPI Copy the code from SDWebImagePDFCoder, need to update
This commit is contained in:
parent
8a26bb3f3d
commit
f4706453d6
|
@ -18,8 +18,6 @@
|
|||
#import <ImageIO/ImageIO.h>
|
||||
#import <CoreServices/CoreServices.h>
|
||||
|
||||
// Specify DPI for vector format in CGImageSource, like PDF
|
||||
static NSString * kSDCGImageSourceRasterizationDPI = @"kCGImageSourceRasterizationDPI";
|
||||
// Specify File Size for lossy format encoding, like JPEG
|
||||
static NSString * kSDCGImageDestinationRequestedFileSize = @"kCGImageDestinationRequestedFileSize";
|
||||
|
||||
|
@ -568,6 +566,12 @@ static CGImageRef __nullable SDCGImageCreateCopy(CGImageRef cg_nullable image) {
|
|||
return nil;
|
||||
}
|
||||
NSMutableDictionary *properties = [NSMutableDictionary dictionary];
|
||||
#if SD_UIKIT || SD_WATCH
|
||||
CGImagePropertyOrientation exifOrientation = [SDImageCoderHelper exifOrientationFromImageOrientation:image.imageOrientation];
|
||||
#else
|
||||
CGImagePropertyOrientation exifOrientation = kCGImagePropertyOrientationUp;
|
||||
#endif
|
||||
properties[(__bridge NSString *)kCGImagePropertyOrientation] = @(exifOrientation);
|
||||
// Encoding Options
|
||||
double compressionQuality = 1;
|
||||
if (options[SDImageCoderEncodeCompressionQuality]) {
|
||||
|
|
|
@ -10,13 +10,12 @@
|
|||
#import "SDImageCoderHelper.h"
|
||||
#import "NSImage+Compatibility.h"
|
||||
#import "UIImage+Metadata.h"
|
||||
#import "SDImageGraphics.h"
|
||||
#import "SDImageIOAnimatedCoderInternal.h"
|
||||
|
||||
#import <ImageIO/ImageIO.h>
|
||||
#import <CoreServices/CoreServices.h>
|
||||
|
||||
// Specify DPI for vector format in CGImageSource, like PDF
|
||||
static NSString * kSDCGImageSourceRasterizationDPI = @"kCGImageSourceRasterizationDPI";
|
||||
// Specify File Size for lossy format encoding, like JPEG
|
||||
static NSString * kSDCGImageDestinationRequestedFileSize = @"kCGImageDestinationRequestedFileSize";
|
||||
|
||||
|
@ -57,29 +56,65 @@ static NSString * kSDCGImageDestinationRequestedFileSize = @"kCGImageDestination
|
|||
return coder;
|
||||
}
|
||||
|
||||
#pragma mark - Utils
|
||||
+ (CGRect)boxRectFromPDFFData:(nonnull NSData *)data {
|
||||
#pragma mark - Bitmap PDF representation
|
||||
- (UIImage *)createBitmapPDFWithData:(nonnull NSData *)data pageNumber:(NSUInteger)pageNumber targetSize:(CGSize)targetSize preserveAspectRatio:(BOOL)preserveAspectRatio {
|
||||
NSParameterAssert(data);
|
||||
UIImage *image;
|
||||
|
||||
CGDataProviderRef provider = CGDataProviderCreateWithCFData((__bridge CFDataRef)data);
|
||||
if (!provider) {
|
||||
return CGRectZero;
|
||||
return nil;
|
||||
}
|
||||
CGPDFDocumentRef document = CGPDFDocumentCreateWithProvider(provider);
|
||||
CGDataProviderRelease(provider);
|
||||
if (!document) {
|
||||
return CGRectZero;
|
||||
return nil;
|
||||
}
|
||||
|
||||
// `CGPDFDocumentGetPage` page number is 1-indexed.
|
||||
CGPDFPageRef page = CGPDFDocumentGetPage(document, 1);
|
||||
CGPDFPageRef page = CGPDFDocumentGetPage(document, pageNumber + 1);
|
||||
if (!page) {
|
||||
CGPDFDocumentRelease(document);
|
||||
return CGRectZero;
|
||||
return nil;
|
||||
}
|
||||
|
||||
CGRect boxRect = CGPDFPageGetBoxRect(page, kCGPDFMediaBox);
|
||||
CGPDFBox box = kCGPDFMediaBox;
|
||||
CGRect rect = CGPDFPageGetBoxRect(page, box);
|
||||
CGRect targetRect = rect;
|
||||
if (!CGSizeEqualToSize(targetSize, CGSizeZero)) {
|
||||
targetRect = CGRectMake(0, 0, targetSize.width, targetSize.height);
|
||||
}
|
||||
|
||||
CGFloat xRatio = targetRect.size.width / rect.size.width;
|
||||
CGFloat yRatio = targetRect.size.height / rect.size.height;
|
||||
CGFloat xScale = preserveAspectRatio ? MIN(xRatio, yRatio) : xRatio;
|
||||
CGFloat yScale = preserveAspectRatio ? MIN(xRatio, yRatio) : yRatio;
|
||||
|
||||
// `CGPDFPageGetDrawingTransform` will only scale down, but not scale up, so we need calculate the actual scale again
|
||||
CGRect drawRect = CGRectMake( 0, 0, targetRect.size.width / xScale, targetRect.size.height / yScale);
|
||||
CGAffineTransform scaleTransform = CGAffineTransformMakeScale(xScale, yScale);
|
||||
CGAffineTransform transform = CGPDFPageGetDrawingTransform(page, box, drawRect, 0, preserveAspectRatio);
|
||||
|
||||
SDGraphicsBeginImageContextWithOptions(targetRect.size, NO, 0);
|
||||
CGContextRef context = SDGraphicsGetCurrentContext();
|
||||
|
||||
#if SD_UIKIT || SD_WATCH
|
||||
// Core Graphics coordinate system use the bottom-left, UIKit use the flipped one
|
||||
CGContextTranslateCTM(context, 0, targetRect.size.height);
|
||||
CGContextScaleCTM(context, 1, -1);
|
||||
#endif
|
||||
|
||||
CGContextConcatCTM(context, scaleTransform);
|
||||
CGContextConcatCTM(context, transform);
|
||||
|
||||
CGContextDrawPDFPage(context, page);
|
||||
|
||||
image = SDGraphicsGetImageFromCurrentImageContext();
|
||||
SDGraphicsEndImageContext();
|
||||
|
||||
CGPDFDocumentRelease(document);
|
||||
|
||||
return boxRect;
|
||||
return image;
|
||||
}
|
||||
|
||||
#pragma mark - Decode
|
||||
|
@ -113,6 +148,31 @@ static NSString * kSDCGImageDestinationRequestedFileSize = @"kCGImageDestination
|
|||
preserveAspectRatio = preserveAspectRatioValue.boolValue;
|
||||
}
|
||||
|
||||
// Check vector format
|
||||
if ([NSData sd_imageFormatForImageData:data] == SDImageFormatPDF) {
|
||||
// History before iOS 16, ImageIO can decode PDF with rasterization size, but can't ever :(
|
||||
// So, use CoreGraphics to decode PDF (copy code from SDWebImagePDFCoder, may do refactor in the future)
|
||||
UIImage *image;
|
||||
NSUInteger pageNumber = 0; // Still use first page, may added options is user want
|
||||
#if SD_MAC
|
||||
// If don't use thumbnail, prefers the built-in generation of vector image
|
||||
// macOS's `NSImage` supports PDF built-in rendering
|
||||
if (thumbnailSize.width == 0 || thumbnailSize.height == 0) {
|
||||
NSPDFImageRep *imageRep = [[NSPDFImageRep alloc] initWithData:data];
|
||||
if (imageRep) {
|
||||
imageRep.currentPage = pageNumber;
|
||||
image = [[NSImage alloc] initWithSize:imageRep.size];
|
||||
[image addRepresentation:imageRep];
|
||||
image.sd_imageFormat = SDImageFormatPDF;
|
||||
return image;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
image = [self createBitmapPDFWithData:data pageNumber:pageNumber targetSize:thumbnailSize preserveAspectRatio:preserveAspectRatio];
|
||||
image.sd_imageFormat = SDImageFormatPDF;
|
||||
return image;
|
||||
}
|
||||
|
||||
BOOL lazyDecode = YES; // Defaults YES for static image coder
|
||||
NSNumber *lazyDecodeValue = options[SDImageCoderDecodeUseLazyDecoding];
|
||||
if (lazyDecodeValue != nil) {
|
||||
|
@ -150,35 +210,9 @@ static NSString * kSDCGImageDestinationRequestedFileSize = @"kCGImageDestination
|
|||
|
||||
CFStringRef uttype = CGImageSourceGetType(source);
|
||||
SDImageFormat imageFormat = [NSData sd_imageFormatFromUTType:uttype];
|
||||
// Check vector format
|
||||
NSDictionary *decodingOptions = nil;
|
||||
if (imageFormat == SDImageFormatPDF) {
|
||||
// Use 72 DPI (1:1 inch to pixel) by default, matching Apple's PDFKit behavior
|
||||
NSUInteger rasterizationDPI = 72;
|
||||
CGFloat maxPixelSize = MAX(thumbnailSize.width, thumbnailSize.height);
|
||||
if (maxPixelSize > 0) {
|
||||
// Calculate DPI based on PDF box and pixel size
|
||||
CGRect boxRect = [self.class boxRectFromPDFFData:data];
|
||||
CGFloat maxBoxSize = MAX(boxRect.size.width, boxRect.size.height);
|
||||
if (maxBoxSize > 0) {
|
||||
rasterizationDPI = rasterizationDPI * (maxPixelSize / maxBoxSize);
|
||||
}
|
||||
}
|
||||
decodingOptions = @{
|
||||
// This option will cause ImageIO return the pixel size from `CGImageSourceCopyProperties`
|
||||
// If not provided, it always return 0 size
|
||||
kSDCGImageSourceRasterizationDPI : @(rasterizationDPI),
|
||||
};
|
||||
// Already calculated DPI, avoid re-calculation based on thumbnail information
|
||||
preserveAspectRatio = YES;
|
||||
thumbnailSize = CGSizeZero;
|
||||
}
|
||||
|
||||
UIImage *image = [SDImageIOAnimatedCoder createFrameAtIndex:0 source:source scale:scale preserveAspectRatio:preserveAspectRatio thumbnailSize:thumbnailSize lazyDecode:lazyDecode options:decodingOptions];
|
||||
UIImage *image = [SDImageIOAnimatedCoder createFrameAtIndex:0 source:source scale:scale preserveAspectRatio:preserveAspectRatio thumbnailSize:thumbnailSize lazyDecode:lazyDecode options:nil];
|
||||
CFRelease(source);
|
||||
if (!image) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
image.sd_imageFormat = imageFormat;
|
||||
return image;
|
||||
|
|
|
@ -11,12 +11,6 @@
|
|||
#import "UIColor+SDHexString.h"
|
||||
#import <SDWebImageWebPCoder/SDWebImageWebPCoder.h>
|
||||
|
||||
@interface SDImageIOCoder ()
|
||||
|
||||
+ (CGRect)boxRectFromPDFFData:(nonnull NSData *)data;
|
||||
|
||||
@end
|
||||
|
||||
@interface SDWebImageDecoderTests : SDTestCase
|
||||
|
||||
@end
|
||||
|
@ -484,7 +478,7 @@ withLocalImageURL:(NSURL *)imageUrl
|
|||
expect(pixelHeight).beGreaterThan(0);
|
||||
// check vector format should use 72 DPI
|
||||
if (isVector) {
|
||||
CGRect boxRect = [SDImageIOCoder boxRectFromPDFFData:inputImageData];
|
||||
CGRect boxRect = [self boxRectFromPDFData:inputImageData];
|
||||
expect(boxRect.size.width).beGreaterThan(0);
|
||||
expect(boxRect.size.height).beGreaterThan(0);
|
||||
// Since 72 DPI is 1:1 from inch size to pixel size
|
||||
|
@ -564,4 +558,29 @@ withLocalImageURL:(NSURL *)imageUrl
|
|||
return thumbnailImages;
|
||||
}
|
||||
|
||||
#pragma mark - Utils
|
||||
- (CGRect)boxRectFromPDFData:(nonnull NSData *)data {
|
||||
CGDataProviderRef provider = CGDataProviderCreateWithCFData((__bridge CFDataRef)data);
|
||||
if (!provider) {
|
||||
return CGRectZero;
|
||||
}
|
||||
CGPDFDocumentRef document = CGPDFDocumentCreateWithProvider(provider);
|
||||
CGDataProviderRelease(provider);
|
||||
if (!document) {
|
||||
return CGRectZero;
|
||||
}
|
||||
|
||||
// `CGPDFDocumentGetPage` page number is 1-indexed.
|
||||
CGPDFPageRef page = CGPDFDocumentGetPage(document, 1);
|
||||
if (!page) {
|
||||
CGPDFDocumentRelease(document);
|
||||
return CGRectZero;
|
||||
}
|
||||
|
||||
CGRect boxRect = CGPDFPageGetBoxRect(page, kCGPDFMediaBox);
|
||||
CGPDFDocumentRelease(document);
|
||||
|
||||
return boxRect;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
Loading…
Reference in New Issue