Search code examples
objective-cmacosexifnsimageiptc

Writing image metadata (EXIF/TIFF/IPTC) to image file in OS X


I am creating a photo editing app, and so far I've managed to read the metadata from image files successfully (after getting an answer to this question: Reading Camera data from EXIF while opening NSImage on OS X).

source = CGImageSourceCreateWithURL((__bridge CFURLRef)url, NULL);
NSDictionary *props = (__bridge_transfer NSDictionary *) CGImageSourceCopyPropertiesAtIndex(source, 0, NULL);

This copies all the metadata of the image file to a dictionary, and it works faily well. However, I couldn't find out how to write this metadata back to a newly created NSImage (or to an image file). Here is how I save my file (where img is an NSImage instance without metadata and self.cachedMetadata is the dictionary read from the initial image):

NSBitmapImageRep *rep = [NSBitmapImageRep imageRepWithData:[img TIFFRepresentation]];
[rep setProperty:NSImageEXIFData withValue:self.cachedMetadata];
NSData *data;
if([[fileName lowercaseString] rangeOfString:@".png"].location != NSNotFound){
    data = [rep representationUsingType:NSPNGFileType properties:nil];
}else if([[fileName lowercaseString] rangeOfString:@".tif"].location != NSNotFound){
    data = [rep representationUsingType:NSTIFFFileType properties:nil];
}else{ //assume jpeg
    data = [rep representationUsingType:NSJPEGFileType properties:@{NSImageCompressionFactor: [NSNumber numberWithFloat:1], NSImageEXIFData: self.cachedMetadata}];
}

[data writeToFile:fileName atomically:YES];

How can I write the metadata? I used to write just EXIF for JPEG (the dictionary was EXIF-only previously) successfully but because EXIF lacked some of the fields that the initial images had (IPTC and TIFF tags) I needed to change my reading method. Now I have all the data, but I don't know how to write it to the newly-created image file.

Thanks, Can.


Solution

  • Found an answer from another StackOverflow question: How do you overwrite image metadata?:

    (code taken from that question itself and modified for my needs, which contains the answer to my question)

    //assuming I've already loaded the image source and read the meta
    CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef) data, NULL);
    CFStringRef imageType = CGImageSourceGetType(source);
    
    //new empty data to write the final image data to
    NSMutableData *resultData = [NSMutableData data];
    CGImageDestinationRef imgDest = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)(resultData), imageType, 1, NULL);
    
    //copy image data
    CGImageDestinationAddImageFromSource(imgDest, source, 0, (__bridge CFDictionaryRef)(window.cachedMetadata));
    BOOL success = CGImageDestinationFinalize(imgDest);
    

    This worked perfectly for writing back all the metadata including EXIF, TIFF, and IPTC.