问题
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.
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
回答1:
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.
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
来源:https://stackoverflow.com/questions/15212083/how-to-write-custom-metadata-to-png-images-in-ios