ImageIO iPhone doesn't handle IPTCDictionary on write

Originator:cgodefroy
Number:rdar://8204225 Date Originated:2010/07/18
Status:Closed Resolved:2011-07 iOS 5
Product:iOS Product Version:4.0.1
Classification:Serious Bug Reproducible:Always
 
18-Jul-2010 10:21 AM Cyril Godefroy:
Summary:
Trying to edit and rewrite the IPTCDictionary with imageIO on the iPhone SDK 4.0.1 deletes the dictionary from the image. Seems only happening on jpeg files.

Steps to Reproduce:
---------------

Open a public.jpeg image with ImageIO

Read the ExifDictionary
read the GPSDictionary
Read the IPTCDictionary

Edit the info from these dictionaries

Rewrite the dictionaries to the file


Expected Results:
--------------

The end file should contain IPTC info, for example Keywords

Actual Results:
-----------
Preview on the Mac doesn't display any IPTC info, nor keywords


Notes:
------
I did try by putting a file with IPTC info in the shared document folder, editing it and saving it back: the destinationFile had no more IPTC Keywords.

Another developer had the same kind of issues, and reported on Stack Overflow:
http://stackoverflow.com/questions/3003036/

Here is the code, I also provide the full app.


- (void)saveMetaData{	
	CGImageSourceRef source = CGImageSourceCreateWithURL((CFURLRef)[NSURL fileURLWithPath:[self filePath]], nil);
	
	//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];
    [metadata release];
	
	NSMutableDictionary *EXIFDictionary = [[[metadataAsMutable objectForKey:(NSString *)kCGImagePropertyExifDictionary]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];
    }
	
	//we need to format the date so it conforms to the EXIF spec and can be read by other apps
	
    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc]init];
    
	//the date format for EXIF dates as from http://www.abmt.unibas.ch/dokumente/ExIF.pdf
    [dateFormatter setDateFormat:@"yyyy:MM:dd HH:mm:ss"]; 
    
	//use the date formatter to get a string from the date we were passed in the EXIF format
	NSString *EXIFFormattedCreatedDate = [dateFormatter stringFromDate:[self creationDate]]; 
    
	[dateFormatter release];
    
    [EXIFDictionary setObject:EXIFFormattedCreatedDate forKey:(NSString *)kCGImagePropertyExifDateTimeDigitized];
	
	NSMutableDictionary *GPSDictionary = [[[metadataAsMutable objectForKey:(NSString *)kCGImagePropertyGPSDictionary ]mutableCopy]autorelease];
	if(!GPSDictionary){

		GPSDictionary = [NSMutableDictionary dictionary];
	}
	[GPSDictionary setObject:self.longitude forKey:(NSString *)kCGImagePropertyGPSLongitude];
	[GPSDictionary setObject:self.latitude forKey:(NSString *)kCGImagePropertyGPSLatitude];
	
	
	NSMutableDictionary *IPTCDictionary = [[[metadataAsMutable objectForKey:(NSString *)kCGImagePropertyIPTCDictionary ]mutableCopy]autorelease];
	if(!IPTCDictionary){
		IPTCDictionary = [NSMutableDictionary dictionary];
	}
	if(self.name)
		[IPTCDictionary setObject:self.name forKey:(NSString *)kCGImagePropertyIPTCHeadline];
	if(self.tags)
		[IPTCDictionary setObject:self.tags forKey:(NSString *)kCGImagePropertyIPTCKeywords];
	
										  
	//add our modified dictionaries data back into the image’s metadata
    [metadataAsMutable setObject:EXIFDictionary forKey:(NSString *)kCGImagePropertyExifDictionary];
	[metadataAsMutable setObject:IPTCDictionary forKey:(NSString *)kCGImagePropertyIPTCDictionary];
    [metadataAsMutable setObject:GPSDictionary  forKey:(NSString *)kCGImagePropertyGPSDictionary];
	
    
    CFStringRef UTI = CGImageSourceGetType(source);
	//this will be the data CGImageDestinationRef will write into
    NSMutableData *data = [NSMutableData data];
    
    CGImageDestinationRef destination = CGImageDestinationCreateWithData((CFMutableDataRef)data,UTI,1,NULL);
    
    if(!destination)
    {
        NSLog(@"***Could not create image destination ***");
        return ;
    }
    
    //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 ***");
        return ;
    }
    
    //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
    [data writeToURL:[NSURL fileURLWithPath:[self filePath]] atomically:YES];
    
    //cleanup
    CFRelease(destination);
    CFRelease(source);
}


18-Jul-2010 10:25 AM Cyril Godefroy:
'Archive-1.zip' was successfully uploaded



-----------------
OpenRadar Note: Am I wrong in believing I should be able to write my iptc dict with this code? I have to admit, I'm an imageIO noob.

Comments

Don't make a copy

I had the same thing happen to me. Don't make a mutable copy for yourself. Edit the returned object directly (it's already mutable). Then you'll keep your changes as long as it's a metadata tag that's already supported. If you try to add one that's not supported (it's not documented which don't exist on iOS (see http://www.openradar.me/8569366)) it'll silently get thrown away.


Please note: Reports posted here will not necessarily be seen by Apple. All problems should be submitted at bugreport.apple.com before they are posted here. Please only post information for Radars that you have filed yourself, and please do not include Apple confidential information in your posts. Thank you!