mirror of
https://github.com/apache/cordova-plugin-camera.git
synced 2025-01-18 19:22:51 +08:00
fix(ios): preserving EXIF data (#712)
This commit is contained in:
parent
53223c3df2
commit
4608f8ef80
@ -29,6 +29,7 @@
|
||||
#import <ImageIO/CGImageDestination.h>
|
||||
#import <MobileCoreServices/UTCoreTypes.h>
|
||||
#import <objc/message.h>
|
||||
#import <Photos/Photos.h>
|
||||
|
||||
#ifndef __CORDOVA_4_0_0
|
||||
#import <Cordova/NSData+Base64.h>
|
||||
@ -159,7 +160,7 @@ static NSString* toBase64(NSData* data) {
|
||||
if (pictureOptions.sourceType == UIImagePickerControllerSourceTypeCamera) {
|
||||
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted)
|
||||
{
|
||||
if(!granted)
|
||||
if (!granted)
|
||||
{
|
||||
// Denied; show an alert
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
@ -174,11 +175,32 @@ static NSString* toBase64(NSData* data) {
|
||||
[weakSelf.viewController presentViewController:alertController animated:YES completion:nil];
|
||||
});
|
||||
} else {
|
||||
[weakSelf showCameraPicker:command.callbackId withOptions:pictureOptions];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[weakSelf showCameraPicker:command.callbackId withOptions:pictureOptions];
|
||||
});
|
||||
}
|
||||
}];
|
||||
} else {
|
||||
[weakSelf showCameraPicker:command.callbackId withOptions:pictureOptions];
|
||||
[weakSelf options:pictureOptions requestPhotoPermissions:^(BOOL granted) {
|
||||
if (!granted) {
|
||||
// Denied; show an alert
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"] message:NSLocalizedString(@"Access to the camera roll has been prohibited; please enable it in the Settings to continue.", nil) preferredStyle:UIAlertControllerStyleAlert];
|
||||
[alertController addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"OK", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
|
||||
[weakSelf sendNoPermissionResult:command.callbackId];
|
||||
}]];
|
||||
[alertController addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"Settings", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
|
||||
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
|
||||
[weakSelf sendNoPermissionResult:command.callbackId];
|
||||
}]];
|
||||
[weakSelf.viewController presentViewController:alertController animated:YES completion:nil];
|
||||
});
|
||||
} else {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[weakSelf showCameraPicker:command.callbackId withOptions:pictureOptions];
|
||||
});
|
||||
}
|
||||
}];
|
||||
}
|
||||
}];
|
||||
}
|
||||
@ -367,24 +389,51 @@ static NSString* toBase64(NSData* data) {
|
||||
data = UIImageJPEGRepresentation(image, [options.quality floatValue] / 100.0f);
|
||||
}
|
||||
|
||||
if (options.usesGeolocation) {
|
||||
NSDictionary* controllerMetadata = [info objectForKey:@"UIImagePickerControllerMediaMetadata"];
|
||||
if (pickerController.sourceType == UIImagePickerControllerSourceTypeCamera) {
|
||||
if (options.usesGeolocation) {
|
||||
NSDictionary* controllerMetadata = [info objectForKey:@"UIImagePickerControllerMediaMetadata"];
|
||||
if (controllerMetadata) {
|
||||
self.data = data;
|
||||
self.metadata = [[NSMutableDictionary alloc] init];
|
||||
|
||||
NSMutableDictionary* EXIFDictionary = [[controllerMetadata objectForKey:(NSString*)kCGImagePropertyExifDictionary]mutableCopy];
|
||||
if (EXIFDictionary) {
|
||||
[self.metadata setObject:EXIFDictionary forKey:(NSString*)kCGImagePropertyExifDictionary];
|
||||
}
|
||||
|
||||
if (IsAtLeastiOSVersion(@"8.0")) {
|
||||
[[self locationManager] performSelector:NSSelectorFromString(@"requestWhenInUseAuthorization") withObject:nil afterDelay:0];
|
||||
}
|
||||
[[self locationManager] startUpdatingLocation];
|
||||
}
|
||||
data = nil;
|
||||
}
|
||||
} else if (pickerController.sourceType == UIImagePickerControllerSourceTypePhotoLibrary) {
|
||||
PHAsset* asset = [info objectForKey:@"UIImagePickerControllerPHAsset"];
|
||||
NSDictionary* controllerMetadata = [self getImageMetadataFromAsset:asset];
|
||||
|
||||
self.data = data;
|
||||
if (controllerMetadata) {
|
||||
self.data = data;
|
||||
self.metadata = [[NSMutableDictionary alloc] init];
|
||||
|
||||
NSMutableDictionary* EXIFDictionary = [[controllerMetadata objectForKey:(NSString*)kCGImagePropertyExifDictionary]mutableCopy];
|
||||
if (EXIFDictionary) {
|
||||
[self.metadata setObject:EXIFDictionary forKey:(NSString*)kCGImagePropertyExifDictionary];
|
||||
}
|
||||
|
||||
if (IsAtLeastiOSVersion(@"8.0")) {
|
||||
[[self locationManager] performSelector:NSSelectorFromString(@"requestWhenInUseAuthorization") withObject:nil afterDelay:0];
|
||||
NSMutableDictionary* TIFFDictionary = [[controllerMetadata objectForKey:(NSString*)kCGImagePropertyTIFFDictionary
|
||||
]mutableCopy];
|
||||
if (TIFFDictionary) {
|
||||
[self.metadata setObject:TIFFDictionary forKey:(NSString*)kCGImagePropertyTIFFDictionary];
|
||||
}
|
||||
NSMutableDictionary* GPSDictionary = [[controllerMetadata objectForKey:(NSString*)kCGImagePropertyGPSDictionary
|
||||
]mutableCopy];
|
||||
if (GPSDictionary) {
|
||||
[self.metadata setObject:GPSDictionary forKey:(NSString*)kCGImagePropertyGPSDictionary
|
||||
];
|
||||
}
|
||||
[[self locationManager] startUpdatingLocation];
|
||||
}
|
||||
data = nil;
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@ -394,6 +443,78 @@ static NSString* toBase64(NSData* data) {
|
||||
return data;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------
|
||||
-- get the metadata of the image from a PHAsset
|
||||
-------------------------------------------------------------- */
|
||||
- (NSDictionary*)getImageMetadataFromAsset:(PHAsset*)asset {
|
||||
|
||||
if(asset == nil) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
// get photo info from this asset
|
||||
__block NSDictionary *dict = nil;
|
||||
PHImageRequestOptions *imageRequestOptions = [[PHImageRequestOptions alloc] init];
|
||||
imageRequestOptions.synchronous = YES;
|
||||
[[PHImageManager defaultManager]
|
||||
requestImageDataForAsset:asset
|
||||
options:imageRequestOptions
|
||||
resultHandler: ^(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info) {
|
||||
dict = [self convertImageMetadata:imageData]; // as this imageData is in NSData format so we need a method to convert this NSData into NSDictionary
|
||||
}];
|
||||
return dict;
|
||||
}
|
||||
|
||||
-(NSDictionary*)convertImageMetadata:(NSData*)imageData {
|
||||
CGImageSourceRef imageSource = CGImageSourceCreateWithData((__bridge CFDataRef)(imageData), NULL);
|
||||
if (imageSource) {
|
||||
NSDictionary *options = @{(NSString *)kCGImageSourceShouldCache : [NSNumber numberWithBool:NO]};
|
||||
CFDictionaryRef imageProperties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, (__bridge CFDictionaryRef)options);
|
||||
if (imageProperties) {
|
||||
NSDictionary *metadata = (__bridge NSDictionary *)imageProperties;
|
||||
CFRelease(imageProperties);
|
||||
CFRelease(imageSource);
|
||||
NSLog(@"Metadata of selected image%@", metadata);// image metadata after converting NSData into NSDictionary
|
||||
return metadata;
|
||||
}
|
||||
CFRelease(imageSource);
|
||||
}
|
||||
|
||||
NSLog(@"Can't read image metadata");
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)options:(CDVPictureOptions*)options requestPhotoPermissions:(void (^)(BOOL auth))completion
|
||||
{
|
||||
if((unsigned long)options.sourceType == 1){
|
||||
completion(YES);
|
||||
}
|
||||
else{
|
||||
PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus];
|
||||
|
||||
switch (status) {
|
||||
case PHAuthorizationStatusAuthorized:
|
||||
completion(YES);
|
||||
break;
|
||||
case PHAuthorizationStatusNotDetermined: {
|
||||
[PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus authorizationStatus) {
|
||||
if (authorizationStatus == PHAuthorizationStatusAuthorized) {
|
||||
completion(YES);
|
||||
} else {
|
||||
completion(NO);
|
||||
}
|
||||
}];
|
||||
break;
|
||||
}
|
||||
default:
|
||||
completion(NO);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
- (NSString*)tempFilePath:(NSString*)extension
|
||||
{
|
||||
NSString* docsPath = [NSTemporaryDirectory()stringByStandardizingPath];
|
||||
@ -454,17 +575,48 @@ static NSString* toBase64(NSData* data) {
|
||||
image = [self retrieveImage:info options:options];
|
||||
NSData* data = [self processImage:image info:info options:options];
|
||||
if (data) {
|
||||
if (pickerController.sourceType == UIImagePickerControllerSourceTypePhotoLibrary) {
|
||||
NSMutableData *imageDataWithExif = [NSMutableData data];
|
||||
if (self.metadata) {
|
||||
CGImageSourceRef sourceImage = CGImageSourceCreateWithData((__bridge CFDataRef)self.data, NULL);
|
||||
CFStringRef sourceType = CGImageSourceGetType(sourceImage);
|
||||
|
||||
NSString* extension = options.encodingType == EncodingTypePNG? @"png" : @"jpg";
|
||||
NSString* filePath = [self tempFilePath:extension];
|
||||
NSError* err = nil;
|
||||
CGImageDestinationRef destinationImage = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)imageDataWithExif, sourceType, 1, NULL);
|
||||
CGImageDestinationAddImageFromSource(destinationImage, sourceImage, 0, (__bridge CFDictionaryRef)self.metadata);
|
||||
CGImageDestinationFinalize(destinationImage);
|
||||
|
||||
// save file
|
||||
if (![data writeToFile:filePath options:NSAtomicWrite error:&err]) {
|
||||
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsString:[err localizedDescription]];
|
||||
} else {
|
||||
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:[[self urlTransformer:[NSURL fileURLWithPath:filePath]] absoluteString]];
|
||||
CFRelease(sourceImage);
|
||||
CFRelease(destinationImage);
|
||||
} else {
|
||||
imageDataWithExif = [self.data mutableCopy];
|
||||
}
|
||||
|
||||
NSError* err = nil;
|
||||
NSString* extension = self.pickerController.pictureOptions.encodingType == EncodingTypePNG ? @"png":@"jpg";
|
||||
NSString* filePath = [self tempFilePath:extension];
|
||||
|
||||
// save file
|
||||
if (![imageDataWithExif writeToFile:filePath options:NSAtomicWrite error:&err]) {
|
||||
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsString:[err localizedDescription]];
|
||||
}
|
||||
else {
|
||||
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:[[self urlTransformer:[NSURL fileURLWithPath:filePath]] absoluteString]];
|
||||
}
|
||||
|
||||
} else if (pickerController.sourceType != UIImagePickerControllerSourceTypeCamera || !options.usesGeolocation) {
|
||||
// No need to save file if usesGeolocation is true since it will be saved after the location is tracked
|
||||
NSString* extension = options.encodingType == EncodingTypePNG? @"png" : @"jpg";
|
||||
NSString* filePath = [self tempFilePath:extension];
|
||||
NSError* err = nil;
|
||||
|
||||
// save file
|
||||
if (![data writeToFile:filePath options:NSAtomicWrite error:&err]) {
|
||||
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsString:[err localizedDescription]];
|
||||
} else {
|
||||
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:[[self urlTransformer:[NSURL fileURLWithPath:filePath]] absoluteString]];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -649,19 +801,23 @@ static NSString* toBase64(NSData* data) {
|
||||
{
|
||||
CDVPictureOptions* options = self.pickerController.pictureOptions;
|
||||
CDVPluginResult* result = nil;
|
||||
|
||||
NSMutableData *imageDataWithExif = [NSMutableData data];
|
||||
|
||||
if (self.metadata) {
|
||||
NSData* dataCopy = [self.data mutableCopy];
|
||||
CGImageSourceRef sourceImage = CGImageSourceCreateWithData((__bridge CFDataRef)dataCopy, NULL);
|
||||
CFStringRef sourceType = CGImageSourceGetType(sourceImage);
|
||||
|
||||
CGImageDestinationRef destinationImage = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)self.data, sourceType, 1, NULL);
|
||||
CGImageDestinationRef destinationImage = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)imageDataWithExif, sourceType, 1, NULL);
|
||||
CGImageDestinationAddImageFromSource(destinationImage, sourceImage, 0, (__bridge CFDictionaryRef)self.metadata);
|
||||
CGImageDestinationFinalize(destinationImage);
|
||||
|
||||
dataCopy = nil;
|
||||
CFRelease(sourceImage);
|
||||
CFRelease(destinationImage);
|
||||
} else {
|
||||
imageDataWithExif = [self.data mutableCopy];
|
||||
}
|
||||
|
||||
switch (options.destinationType) {
|
||||
@ -695,7 +851,7 @@ static NSString* toBase64(NSData* data) {
|
||||
self.pickerController = nil;
|
||||
self.data = nil;
|
||||
self.metadata = nil;
|
||||
|
||||
imageDataWithExif = nil;
|
||||
if (options.saveToPhotoAlbum) {
|
||||
UIImageWriteToSavedPhotosAlbum([[UIImage alloc] initWithData:self.data], nil, nil, nil);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user