How to write custom metadata to PNG images in iOS

My application should be able to write custom metadata entries to PNG images for export to the UIPasteboard.

By piecing together various posts on the subject, I’ve been able to come up with the class given below as source.

  • Using Dictation - iOS 6 - DidStart?
  • Having problems allowing interaction in UIView animation
  • Substring from NSString
  • How to cancel a download request when cancel button pressed in swift
  • Couldn't load a xcode project because it is already opened from another project or workspace
  • Handling class initialization failure in Swift extension
  • Triggering the copyPressed method with a button, I’m able to set custom metadata with JPG images (EXIF):

    Image[6101:907] found jpg exif dictionary
    Image[6101:907] checking image metadata on clipboard
    Image[6101:907] {
        ColorModel = RGB;
        Depth = 8;
        Orientation = 1;
        PixelHeight = 224;
        PixelWidth = 240;
        "{Exif}" =     {
            ColorSpace = 1;
            PixelXDimension = 240;
            PixelYDimension = 224;
            UserComment = "Here is a comment";
        };
        "{JFIF}" =     {
            DensityUnit = 0;
            JFIFVersion =         (
                1,
                1
            );
            XDensity = 1;
            YDensity = 1;
        };
        "{TIFF}" =     {
            Orientation = 1;
        };
    }
    

    Although I’m able to read the PNG metadata just fine, I can’t seem to write to it:

    Image[6116:907] found png property dictionary
    Image[6116:907] checking image metadata on clipboard
    Image[6116:907] {
        ColorModel = RGB;
        Depth = 8;
        PixelHeight = 224;
        PixelWidth = 240;
        "{PNG}" =     {
            InterlaceType = 0;
        };
    }
    

    However, nothing in the documentation suggests this should fail and the presence of many PNG-specific metadata constants suggests it should succeed.

    My application should use PNG to avoid JPG’s lossy compression.

    Why can I not set custom metadata on an in-memory PNG image in iOS?

    Note: I’ve seen this SO question, but it doesn’t address the problem here, which is how to write metadata to PNG images specifically.

    IMViewController.m

    #import "IMViewController.h"
    #import <ImageIO/ImageIO.h>
    
    @interface IMViewController ()
    
    @end
    
    @implementation IMViewController
    
    - (IBAction)copyPressed:(id)sender
    {
    //    [self copyJPG];
        [self copyPNG];
    }
    
    -(void)copyPNG
    {
        NSData *pngData = UIImagePNGRepresentation([UIImage imageNamed:@"wow.png"]);
        CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)pngData, NULL);
        NSDictionary *metadata = (__bridge NSDictionary *) CGImageSourceCopyPropertiesAtIndex(source, 0, NULL);
        NSMutableDictionary *mutableMetadata = [metadata mutableCopy];
        NSMutableDictionary *dict = [[mutableMetadata objectForKey:(NSString *) kCGImagePropertyPNGDictionary] mutableCopy];
    
        if (dict) {
            NSLog(@"found png property dictionary");
        } else {
            NSLog(@"creating png property dictionary");
            dict = [NSMutableDictionary dictionary];
        }
    
        // set values on the root dictionary
        [mutableMetadata setObject:@"Name of Software" forKey:(NSString *)kCGImagePropertyPNGDescription];
        [mutableMetadata setObject:dict forKey:(NSString *)kCGImagePropertyPNGDictionary];
    
        // set values on the internal dictionary
        [dict setObject:@"works" forKey:(NSString *)kCGImagePropertyPNGDescription];
    
        CFStringRef UTI = CGImageSourceGetType(source);
        NSMutableData *data = [NSMutableData data];
        CGImageDestinationRef destination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef) data, UTI, 1, NULL);
    
        if (!destination) {
            NSLog(@">>> Could not create image destination <<<");
    
            return;
        }
    
        CGImageDestinationAddImageFromSource(destination, source, 0, (__bridge CFDictionaryRef) mutableMetadata);
    
        BOOL success = CGImageDestinationFinalize(destination);
    
        if (!success) {
            NSLog(@">>> Error Writing Data <<<");
        }
    
        UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
    
        [pasteboard setData:data forPasteboardType:@"public.png"];
        [self showPNGMetadata];
    }
    
    -(void)copyJPG
    {
        NSData *jpgData = UIImageJPEGRepresentation([UIImage imageNamed:@"wow.jpg"], 1);
        CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef) jpgData, NULL);
        NSDictionary *metadata = (__bridge NSDictionary *) CGImageSourceCopyPropertiesAtIndex(source, 0, NULL);
        NSMutableDictionary *mutableMetadata = [metadata mutableCopy];
        NSMutableDictionary *exif = [[mutableMetadata objectForKey:(NSString *)kCGImagePropertyExifDictionary] mutableCopy];
    
        if (exif) {
            NSLog(@"found jpg exif dictionary");
        } else {
            NSLog(@"creating jpg exif dictionary");
        }
    
        // set values on the exif dictionary
        [exif setObject:@"Here is a comment" forKey:(NSString *)kCGImagePropertyExifUserComment];
        [mutableMetadata setObject:exif forKey:(NSString *)kCGImagePropertyExifDictionary];
    
        CFStringRef UTI = CGImageSourceGetType(source);
        NSMutableData *data = [NSMutableData data];
        CGImageDestinationRef destination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef) data, UTI, 1, NULL);
    
        if(!destination) {
            NSLog(@">>> Could not create image destination <<<");
    
            return;
        }
    
        CGImageDestinationAddImageFromSource(destination,source, 0, (__bridge CFDictionaryRef) mutableMetadata);
    
        BOOL success = CGImageDestinationFinalize(destination);
    
        if (!success) {
            NSLog(@">>> Could not create data from image destination <<<");
        }
    
        UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
    
        [pasteboard setData:data forPasteboardType:@"public.jpeg"];
        [self showJPGMetadata];
    }
    
    -(void)showJPGMetadata
    {
        NSLog(@"checking image metadata on clipboard");
    
        UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
        NSData *data = [pasteboard dataForPasteboardType:@"public.jpeg"];
    
        CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL);
        NSDictionary *metadata = (__bridge NSDictionary *) CGImageSourceCopyPropertiesAtIndex(source,0,NULL);
    
        NSLog(@"%@", metadata);
    }
    
    -(void)showPNGMetadata
    {
        NSLog(@"checking image metadata on clipboard");
    
        UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
        NSData *data = [pasteboard dataForPasteboardType:@"public.png"];
    
        CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL);
        NSDictionary *metadata = (__bridge NSDictionary *) CGImageSourceCopyPropertiesAtIndex(source,0,NULL);
    
        NSLog(@"%@", metadata);
    }
    
    @end
    

    Solutions Collect From Internet About “How to write custom metadata to PNG images in iOS”

    If you will try to save your image with modified metadata

    [data writeToFile:[NSTemporaryDirectory() stringByAppendingPathComponent:@"test.png"]   
           atomically:YES];
    

    And than view it properties in Finder. You will see that kCGImagePropertyPNGDescription field was setted up successfully.

    enter image description here

    But if you will try read metadata of this new file, kCGImagePropertyPNGDescription will be lost.

    ColorModel = RGB;
    Depth = 8;
    PixelHeight = 1136;
    PixelWidth = 640;
    "{PNG}" =     {
        InterlaceType = 0;
    };
    

    After some research I found that PNG doesn’t contain metadata. But it may contain XMP metadata. However seems like ImageIO didn’t work with XMP.
    Maybe you can try to use ImageMagic or libexif.

    Useful links:
    PNG Specification
    Reading/Writing image XMP on iPhone / Objective-c
    Does PNG support metadata fields like Author, Camera Model, etc?
    Does PNG contain EXIF data like JPG?
    libexif.sourceforge.net