Search code examples
iphonemacoscore-graphicsexifjavax.imageio

Problem setting exif data for an image


I'm using the new ImageIO framework in iOS 4.1. I successfully retrieve the exif metadata using the following:

CFDictionaryRef metadataDict = CMGetAttachment(sampleBuffer, kCGImagePropertyExifDictionary , NULL);

Reading it out, it appears valid. Saving an image out works, but there is never any exif data in the image.

    CGImageDestinationRef myImageDest = CGImageDestinationCreateWithURL((CFURLRef) docurl, kUTTypeJPEG, 1, NULL);

    // Add the image to the destination using previously saved options. 
    CGImageDestinationAddImage(myImageDest, iref, NULL);

    //add back exif
    NSDictionary *props = [NSDictionary dictionaryWithObjectsAndKeys:
                            [NSNumber numberWithFloat:.1], kCGImageDestinationLossyCompressionQuality,
                           metadataDict, kCGImagePropertyExifDictionary, //the exif metadata
                                                        nil];

                          //kCGImagePropertyExifAuxDictionary

    CGImageDestinationSetProperties(myImageDest, (CFDictionaryRef) props);

    // Finalize the image destination. 
    bool status = CGImageDestinationFinalize(myImageDest);

Solution

  • The following blog post is where I got my answer when I had issues with modifying and saving Exif data Caffeinated Cocoa. This works on iOS.

    Here is my test code for writing Exif and GPS data. It pretty much a copy and paste of the code from the above blog. I am using this to write exif data to a captured image.

    NSData *jpeg = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer] ;
    
    CGImageSourceRef  source ;
        source = CGImageSourceCreateWithData((CFDataRef)jpeg, NULL);
    
        //get all the metadata in the image
        NSDictionary *metadata = (NSDictionary *) CGImageSourceCopyPropertiesAtIndex(source,0,NULL);
    
        //make the metadata dictionary mutable so we can add properties to it
        NSMutableDictionary *metadataAsMutable = [[metadata mutableCopy]autorelease];
        [metadata release];
    
        NSMutableDictionary *EXIFDictionary = [[[metadataAsMutable objectForKey:(NSString *)kCGImagePropertyExifDictionary]mutableCopy]autorelease];
        NSMutableDictionary *GPSDictionary = [[[metadataAsMutable objectForKey:(NSString *)kCGImagePropertyGPSDictionary]mutableCopy]autorelease];
        if(!EXIFDictionary) {
            //if the image does not have an EXIF dictionary (not all images do), then create one for us to use
            EXIFDictionary = [NSMutableDictionary dictionary];
        }
        if(!GPSDictionary) {
            GPSDictionary = [NSMutableDictionary dictionary];
        }
    
        //Setup GPS dict
    
    
        [GPSDictionary setValue:[NSNumber numberWithFloat:_lat] forKey:(NSString*)kCGImagePropertyGPSLatitude];
        [GPSDictionary setValue:[NSNumber numberWithFloat:_lon] forKey:(NSString*)kCGImagePropertyGPSLongitude];
        [GPSDictionary setValue:lat_ref forKey:(NSString*)kCGImagePropertyGPSLatitudeRef];
        [GPSDictionary setValue:lon_ref forKey:(NSString*)kCGImagePropertyGPSLongitudeRef];
        [GPSDictionary setValue:[NSNumber numberWithFloat:_alt] forKey:(NSString*)kCGImagePropertyGPSAltitude];
        [GPSDictionary setValue:[NSNumber numberWithShort:alt_ref] forKey:(NSString*)kCGImagePropertyGPSAltitudeRef]; 
        [GPSDictionary setValue:[NSNumber numberWithFloat:_heading] forKey:(NSString*)kCGImagePropertyGPSImgDirection];
        [GPSDictionary setValue:[NSString stringWithFormat:@"%c",_headingRef] forKey:(NSString*)kCGImagePropertyGPSImgDirectionRef];
    
        [EXIFDictionary setValue:xml forKey:(NSString *)kCGImagePropertyExifUserComment];
        //add our modified EXIF data back into the image’s metadata
        [metadataAsMutable setObject:EXIFDictionary forKey:(NSString *)kCGImagePropertyExifDictionary];
        [metadataAsMutable setObject:GPSDictionary forKey:(NSString *)kCGImagePropertyGPSDictionary];
    
        CFStringRef UTI = CGImageSourceGetType(source); //this is the type of image (e.g., public.jpeg)
    
        //this will be the data CGImageDestinationRef will write into
        NSMutableData *dest_data = [NSMutableData data];
    
        CGImageDestinationRef destination = CGImageDestinationCreateWithData((CFMutableDataRef)dest_data,UTI,1,NULL);
    
        if(!destination) {
            NSLog(@"***Could not create image destination ***");
        }
    
        //add the image contained in the image source to the destination, overidding the old metadata with our modified metadata
        CGImageDestinationAddImageFromSource(destination,source,0, (CFDictionaryRef) metadataAsMutable);
    
        //tell the destination to write the image data and metadata into our data object.
        //It will return false if something goes wrong
        BOOL success = NO;
        success = CGImageDestinationFinalize(destination);
    
        if(!success) {
            NSLog(@"***Could not create data from image destination ***");
        }
    
        //now we have the data ready to go, so do whatever you want with it
        //here we just write it to disk at the same path we were passed
        [dest_data writeToFile:file atomically:YES];
    
        //cleanup
    
        CFRelease(destination);
        CFRelease(source);