You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by er...@apache.org on 2022/03/22 07:24:04 UTC
[cordova-plugin-camera] branch master updated: fix(ios): preserving EXIF data (#712)
This is an automated email from the ASF dual-hosted git repository.
erisu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cordova-plugin-camera.git
The following commit(s) were added to refs/heads/master by this push:
new 4608f8e fix(ios): preserving EXIF data (#712)
4608f8e is described below
commit 4608f8ef8027a5c7130a3479c2d37a5e391c70e8
Author: Scott Murphy <sc...@alwaysvip.com>
AuthorDate: Tue Mar 22 00:23:56 2022 -0700
fix(ios): preserving EXIF data (#712)
---
src/ios/CDVCamera.m | 198 ++++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 177 insertions(+), 21 deletions(-)
diff --git a/src/ios/CDVCamera.m b/src/ios/CDVCamera.m
index 2f2738b..6793da4 100644
--- a/src/ios/CDVCamera.m
+++ b/src/ios/CDVCamera.m
@@ -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);
+
+ CGImageDestinationRef destinationImage = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)imageDataWithExif, sourceType, 1, NULL);
+ CGImageDestinationAddImageFromSource(destinationImage, sourceImage, 0, (__bridge CFDictionaryRef)self.metadata);
+ CGImageDestinationFinalize(destinationImage);
+
+ CFRelease(sourceImage);
+ CFRelease(destinationImage);
+ } else {
+ imageDataWithExif = [self.data mutableCopy];
+ }
- NSString* extension = options.encodingType == EncodingTypePNG? @"png" : @"jpg";
- NSString* filePath = [self tempFilePath:extension];
- NSError* err = nil;
+ NSError* err = nil;
+ NSString* extension = self.pickerController.pictureOptions.encodingType == EncodingTypePNG ? @"png":@"jpg";
+ NSString* filePath = [self tempFilePath:extension];
- // 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]];
+ // 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);
}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cordova.apache.org
For additional commands, e-mail: commits-help@cordova.apache.org