react-native-image-picker / react-native-image-picker

:sunrise_over_mountains: A React Native module that allows you to use native UI to select media from the device library or directly from the camera.
MIT License
8.45k stars 2.08k forks source link

[🐛] Exif/Meta data of selected/taken images not being extracted on iOS #2092

Open jsimmons12 opened 1 year ago

jsimmons12 commented 1 year ago

Description

On iOS, no meta/exif data is being returned on the images selected or taken using the react-native-image-picker library. I have the 'includeExtra' option enabled and it is working correctly on Android, however on iOS it appears as though all of the meta data is being lost.

On images taken with the device, no meta data is captured at all. Using react-native-fs I have saved the images taken using react-native-image-picker directly to the device and copied them to my computer to look at in more detail, which reveals the files themselves have no meta data attached to them.

On images already on the device that have been taken using the device's primary camera, no meta data is being harvested despite it being visible when examining the files themselves.

Appears to be an issue with the process of copying the selected image to the temp directory, at which point the meta data is being lost.

How to repeat issue and example

My code for selecting images/videos:

  selectMedia: async function (type: MediaType) {
    const result = await launchImageLibrary({
      mediaType: type,
      includeExtra: true,
      selectionLimit: 0,
      videoQuality: 'medium'
    });

    return result.assets;
  }

My code for taking images/videos:

  openCamera: async function (type: MediaType, platform: PlatformOSType) {

    let options: CameraOptions = {
      mediaType: type,
      includeExtra: true,
      cameraType: 'back',
      videoQuality: 'medium',
    }

    if (platform === 'ios') {
      options.maxWidth = 4032;
      options.maxHeight = 3024;
    }

    const result = await launchCamera(options)

    if (result.assets) {
      return result.assets;
    }
  }

Solution

Capture the correct meta data and ensure it's not being lost in the file creation process.

Additional Information

mehdinourollah commented 1 year ago

Any update ?!

ArturoTorresMartinez commented 1 year ago

Same here

cristian0791 commented 11 months ago

Any update?

SayisMe commented 11 months ago

Same here too

NomanGul commented 6 months ago

I applied this patch to get full exif data on iOS (including GPS info)

diff --git a/node_modules/react-native-image-picker/ios/ImagePickerManager.mm b/node_modules/react-native-image-picker/ios/ImagePickerManager.mm
index 93e99be..da8d721 100644
--- a/node_modules/react-native-image-picker/ios/ImagePickerManager.mm
+++ b/node_modules/react-native-image-picker/ios/ImagePickerManager.mm
@@ -170,6 +175,15 @@ -(NSMutableDictionary *)mapImageToAsset:(UIImage *)image data:(NSData *)data phA
                                     maxHeight:[self.options[@"maxHeight"] floatValue]];
     }

+    NSMutableDictionary *asset = [[NSMutableDictionary alloc] init];
+
+    NSDictionary *exifData = getExifDataFromImage(data);
+    if (exifData) {
+        asset[@"exif"] = exifData;
+    } else {
+        asset[@"exif"] = @{};
+    }
+
     float quality = [self.options[@"quality"] floatValue];
     if (![image isEqual:newImage] || (quality >= 0 && quality < 1)) {
         if ([fileType isEqualToString:@"jpg"]) {
@@ -179,7 +193,6 @@ -(NSMutableDictionary *)mapImageToAsset:(UIImage *)image data:(NSData *)data phA
         }
     }

-    NSMutableDictionary *asset = [[NSMutableDictionary alloc] init];
     asset[@"type"] = [@"image/" stringByAppendingString:fileType];

     NSString *fileName = [self getImageFileName:fileType];
@@ -213,6 +226,23 @@ -(NSMutableDictionary *)mapImageToAsset:(UIImage *)image data:(NSData *)data phA
     return asset;
 }

+NSDictionary *getExifDataFromImage(NSData *data) {
+    CGImageSourceRef imageSource = CGImageSourceCreateWithData((CFDataRef)data, NULL);
+    if (!imageSource) {
+        return nil;
+    }
+
+    NSDictionary *exifDictionary = (NSDictionary *) CFBridgingRelease(CGImageSourceCopyPropertiesAtIndex(imageSource, 0, NULL));
+    if (!exifDictionary) {
+        CFRelease(imageSource);
+        return nil;
+    }
+
+    CFRelease(imageSource);
+
+    return exifDictionary;
+}
+
 CGImagePropertyOrientation CGImagePropertyOrientationForUIImageOrientation(UIImageOrientation uiOrientation) {
     //code from here: https://developer.apple.com/documentation/imageio/cgimagepropertyorientation?language=objc
     switch (uiOrientation) {
aderiushev commented 5 months ago

@NomanGul it works! thank u so much but it does not return the location for ex. for the just taken photo (camera) exif data for taking & picking is different so when u pick a photo from gallery - it works perfectly well and contains a lot including gps but not that good for the taking option here is an example from what i get

[ { uri: 'file:///var/mobile/Containers/Data/Application/3442C4D2-3FF4-4FDF-9DEF-EBA17174529C/tmp/D5358766-2331-42A0-A2A3-AA57372FF444.jpg',
       fileSize: 208360,
       height: 4032,
       exif: 
        { '{JFIF}': 
           { JFIFVersion: [ 1, 0, 1 ],
             DensityUnit: 0,
             YDensity: 72,
             XDensity: 72 },
          '{Exif}': 
           { PixelXDimension: 4032,
             ColorSpace: 65535,
             PixelYDimension: 3024 },
          '{TIFF}': { Orientation: 6 },
          ProfileName: 'Display P3',
          PixelHeight: 3024,
          ColorModel: 'RGB',
          Depth: 8,
          Orientation: 6,
          PixelWidth: 4032 },
       type: 'image/jpg',
       width: 3024,
       fileName: 'D5358766-2331-42A0-A2A3-AA57372FF444.jpg' } ]
krisnapy commented 4 months ago

I applied this patch to get full exif data on iOS (including GPS info)

diff --git a/node_modules/react-native-image-picker/ios/ImagePickerManager.mm b/node_modules/react-native-image-picker/ios/ImagePickerManager.mm
index 93e99be..da8d721 100644
--- a/node_modules/react-native-image-picker/ios/ImagePickerManager.mm
+++ b/node_modules/react-native-image-picker/ios/ImagePickerManager.mm
@@ -170,6 +175,15 @@ -(NSMutableDictionary *)mapImageToAsset:(UIImage *)image data:(NSData *)data phA
                                     maxHeight:[self.options[@"maxHeight"] floatValue]];
     }

+    NSMutableDictionary *asset = [[NSMutableDictionary alloc] init];
+
+    NSDictionary *exifData = getExifDataFromImage(data);
+    if (exifData) {
+        asset[@"exif"] = exifData;
+    } else {
+        asset[@"exif"] = @{};
+    }
+
     float quality = [self.options[@"quality"] floatValue];
     if (![image isEqual:newImage] || (quality >= 0 && quality < 1)) {
         if ([fileType isEqualToString:@"jpg"]) {
@@ -179,7 +193,6 @@ -(NSMutableDictionary *)mapImageToAsset:(UIImage *)image data:(NSData *)data phA
         }
     }

-    NSMutableDictionary *asset = [[NSMutableDictionary alloc] init];
     asset[@"type"] = [@"image/" stringByAppendingString:fileType];

     NSString *fileName = [self getImageFileName:fileType];
@@ -213,6 +226,23 @@ -(NSMutableDictionary *)mapImageToAsset:(UIImage *)image data:(NSData *)data phA
     return asset;
 }

+NSDictionary *getExifDataFromImage(NSData *data) {
+    CGImageSourceRef imageSource = CGImageSourceCreateWithData((CFDataRef)data, NULL);
+    if (!imageSource) {
+        return nil;
+    }
+
+    NSDictionary *exifDictionary = (NSDictionary *) CFBridgingRelease(CGImageSourceCopyPropertiesAtIndex(imageSource, 0, NULL));
+    if (!exifDictionary) {
+        CFRelease(imageSource);
+        return nil;
+    }
+
+    CFRelease(imageSource);
+
+    return exifDictionary;
+}
+
 CGImagePropertyOrientation CGImagePropertyOrientationForUIImageOrientation(UIImageOrientation uiOrientation) {
     //code from here: https://developer.apple.com/documentation/imageio/cgimagepropertyorientation?language=objc
     switch (uiOrientation) {

@NomanGul thank you it's working, do you have a solution for Android? I have tried to extract the latitude and longitude and it returns '0/1,0/1,0/1'.

Abdel-Kader commented 4 months ago

I applied this patch to get full exif data on iOS (including GPS info)

diff --git a/node_modules/react-native-image-picker/ios/ImagePickerManager.mm b/node_modules/react-native-image-picker/ios/ImagePickerManager.mm
index 93e99be..da8d721 100644
--- a/node_modules/react-native-image-picker/ios/ImagePickerManager.mm
+++ b/node_modules/react-native-image-picker/ios/ImagePickerManager.mm
@@ -170,6 +175,15 @@ -(NSMutableDictionary *)mapImageToAsset:(UIImage *)image data:(NSData *)data phA
                                     maxHeight:[self.options[@"maxHeight"] floatValue]];
     }

+    NSMutableDictionary *asset = [[NSMutableDictionary alloc] init];
+
+    NSDictionary *exifData = getExifDataFromImage(data);
+    if (exifData) {
+        asset[@"exif"] = exifData;
+    } else {
+        asset[@"exif"] = @{};
+    }
+
     float quality = [self.options[@"quality"] floatValue];
     if (![image isEqual:newImage] || (quality >= 0 && quality < 1)) {
         if ([fileType isEqualToString:@"jpg"]) {
@@ -179,7 +193,6 @@ -(NSMutableDictionary *)mapImageToAsset:(UIImage *)image data:(NSData *)data phA
         }
     }

-    NSMutableDictionary *asset = [[NSMutableDictionary alloc] init];
     asset[@"type"] = [@"image/" stringByAppendingString:fileType];

     NSString *fileName = [self getImageFileName:fileType];
@@ -213,6 +226,23 @@ -(NSMutableDictionary *)mapImageToAsset:(UIImage *)image data:(NSData *)data phA
     return asset;
 }

+NSDictionary *getExifDataFromImage(NSData *data) {
+    CGImageSourceRef imageSource = CGImageSourceCreateWithData((CFDataRef)data, NULL);
+    if (!imageSource) {
+        return nil;
+    }
+
+    NSDictionary *exifDictionary = (NSDictionary *) CFBridgingRelease(CGImageSourceCopyPropertiesAtIndex(imageSource, 0, NULL));
+    if (!exifDictionary) {
+        CFRelease(imageSource);
+        return nil;
+    }
+
+    CFRelease(imageSource);
+
+    return exifDictionary;
+}
+
 CGImagePropertyOrientation CGImagePropertyOrientationForUIImageOrientation(UIImageOrientation uiOrientation) {
     //code from here: https://developer.apple.com/documentation/imageio/cgimagepropertyorientation?language=objc
     switch (uiOrientation) {

Please how to apply this patch ? Do I need to install another version ? or just copy paste the changes ?

pep108 commented 4 months ago

Please how to apply this patch ? Do I need to install another version ? or just copy paste the changes ?

add these 2 packages to your devDependencies "patch-package": "^8.0.0", "postinstall-postinstall": "^2.1.0"

then add this to your scripts: "postinstall": "patch-package"

then go ahead and run "npm run postinstall" to apply the patch.

pep108 commented 4 months ago

I just created this gist to patch both iOS and Android https://gist.github.com/pep108/8b0bdf156d823c117be655a010a337aa

krisnapy commented 4 months ago

I just created this gist to patch both iOS and Android https://gist.github.com/pep108/8b0bdf156d823c117be655a010a337aa

Does it work for Android, it looks like it only change the Uri?

krisnapy commented 4 months ago

I tried on Android but it not work yet.

https://github.com/react-native-image-picker/react-native-image-picker/issues/2301

pep108 commented 4 months ago

@krisnapy It works for me on Android. I am only looking at the timestamp when the image was created. It appears that the appSpecificUri is a reference to the temporary image and the uri points to the original file with metadata.

mo-nathan commented 2 months ago

How does the above fix compare with the fix in the PR #2165?