【问题标题】:How to write custom metadata to PNG images in iOS如何在 iOS 中将自定义元数据写入 PNG 图像
【发布时间】:2013-02-19 03:53:51
【问题描述】:

我的应用程序应该能够将自定义元数据条目写入 PNG 图像以导出到 UIPasteboard。

通过拼凑有关该主题的各种帖子,我已经能够想出下面给出的课程作为来源。

使用按钮触发 copyPressed 方法,我可以使用 JPG 图像 (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;
    };
}

虽然我能够很好地读取 PNG 元数据,但我似乎无法对其进行写入:

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;
    };
}

但是,文档中没有任何内容表明这应该失败,并且许多 PNG-specific metadata constants 的存在表明它应该成功。

我的应用程序应该使用 PNG 来避免 JPG's lossy compression

为什么我不能在 iOS 的内存中 PNG 图像上设置自定义元数据?

注意:我看过this SO question,但是这里并没有解决问题,就是具体如何将元数据写入PNG图片。

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

【问题讨论】:

    标签: ios objective-c png metadata uipasteboard


    【解决方案1】:

    如果您尝试使用修改后的元数据保存图像

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

    然后在 Finder 中查看它的属性。您将看到kCGImagePropertyPNGDescription 字段已成功设置。

    但是如果你尝试读取这个新文件的元数据,kCGImagePropertyPNGDescription 将会丢失。

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

    经过一些研究,我发现 PNG 不包含元数据。但它可能包含XMP metadata。但是,ImageIO 似乎不适用于 XMP。
    也许您可以尝试使用ImageMagiclibexif

    有用的链接:
    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

    【讨论】:

    • 很高兴知道。显然 iOS 无法使用我的方法从文件中读取 PNG 元数据。如果我确实有办法读取 PNG 元数据,我可能会有一个解决方案。鉴于 iOS 具有像 kCGImagePropertyPNGDescription 这样的常量,我宁愿不使用外部库,而宁愿知道如何仅使用 iOS 库来实际执行此操作。
    • 想象一下,您可以将元数据添加到图像中,您确定其他软件会读取此信息吗?您想添加元数据的原因是什么?
    • 你找到答案了吗?
    猜你喜欢
    • 2014-03-17
    • 2011-07-16
    • 1970-01-01
    • 2012-11-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多