I have some EXIF stored in a UIImage that I store using NSFileManager writeToFile:atomically:
.
Now, when I try to read back the EXIF metadata imageProperties
here shows the metadata I expect:
NSData *data = [NSData dataWithContentsOfFile:filePath];
source = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL);
imageProperties = CGImageSourceCopyPropertiesAtIndex(source, 0, NULL);
However, if I read the data as a UIImage first, the data is gone:
UIImage *writtenImage = [UIImage imageWithContentsOfFile:filePath];
CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)UIImagePNGRepresentation(writtenImage), NULL);
CFDictionaryRef imageProperties = CGImageSourceCopyPropertiesAtIndex(source, 0, NULL);
Additionally, even on converting the data variable above to an image, it fails:
NSData *data = [NSData dataWithContentsOfFile:filePath];
UIImage *writtenImage = [UIImage imageWithData:data];
CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)UIImagePNGRepresentation(writtenImage), NULL);
CFDictionaryRef imageProperties = CGImageSourceCopyPropertiesAtIndex(source, 0, NULL);
Can EXIF metadata only be ready from the NSData?
EXIF is a header for certain image files (containers like png
, jpg
, tif
, etc..). When the files are unpacked, the image is a completely different thing - an array of data representing pixels, where EXIF header is no longer relevant. Thus, when you have an NSData
object of an image container it's a completely different beast compared to the image data incapsulated in a UIImage
object (EXIF header is just not present there).
You can still deduce certain metadata and fill it yourself, but this has nothing to do with UIImage
, in this case you just work with the containers (files) directly:
typedef NS_CLOSED_ENUM(uint8_t, TDWEXIFOrientationValue) {
TDWEXIFOrientationValueTopLeft = 1,
TDWEXIFOrientationValueTopRight,
TDWEXIFOrientationValueBottomRight,
TDWEXIFOrientationValueBottomLeft,
TDWEXIFOrientationValueLeftTop,
TDWEXIFOrientationValueRightTop,
TDWEXIFOrientationValueRightBottom,
TDWEXIFOrientationValueLeftBotton,
};
- (void)writeEXIFHeader:(CGImageSourceRef)image {
/* get the file type */
CFStringRef UTI = CGImageSourceGetType(image);
if (!UTI) {
/* Handle Error Retrieving File Type Accordingly */
return;
}
NSMutableData *imageData = [NSMutableData new];
/* create an image destination for saving the file */
CGImageDestinationRef destination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)imageData, UTI, 1, NULL);
if (!destination) {
/* Handle Error Creating CGImageDestinationRef Accordingly */
return;
}
const void *keys[] = {
kCGImageDestinationDateTime,
kCGImageDestinationOrientation,
kCGImageDestinationMergeMetadata
};
TDWEXIFOrientationValue orientationValue = TDWEXIFOrientationValueTopLeft;
const void *values[] = {
CFDateCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent()), // Sets current date and time for the picture
CFNumberCreate(kCFAllocatorDefault, kCFNumberCharType, &orientationValue), // Default orientation
kCFBooleanTrue // merge exif metadata instead of overwriting it
};
/* create an CFDictionaryRef with the modified metadata */
CFDictionaryRef exifData = CFDictionaryCreate(kCFAllocatorDefault, keys, values, sizeof(keys) / sizeof(keys[0]), NULL, NULL);
/* add an image to the destination */
CFErrorRef errorRef = nil;
if (!CGImageDestinationCopyImageSource(destination, image, exifData, &errorRef)) {
CFStringRef errorDescription = CFErrorCopyDescription(errorRef);
/* Handle Error Creating Writing EXIF data Accordingly */
CFRelease(errorDescription);
}
}