Как записать пользовательские метаданные в изображения PNG в iOS

Мое приложение должно иметь возможность записывать пользовательские записи метаданных в изображения 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, предполагают, что это должно быть выполнено успешно.

Мое приложение должно использовать PNG, чтобы избежать сжатия JPG с потерями< /а>.

Почему я не могу установить пользовательские метаданные для изображения PNG в памяти в iOS?

Примечание. Я видел этот вопрос SO, но он здесь не решается проблема, заключающаяся в том, как конкретно записывать метаданные в изображения 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

person Rich Apodaca    schedule 04.03.2013    source источник


Ответы (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. Однако похоже, что ImageIO не работает с XMP.
Возможно, вы можете попробовать использовать ImageMagic или libexif.

Полезные ссылки:
Спецификация PNG
Чтение/запись изображения XMP на iPhone/Objective-c
Поддерживает ли PNG поля метаданных, такие как автор, модель камеры и т. д.?
Содержит ли PNG данные EXIF, такие как JPG?
libexif.sourceforge.net

person Sergey Kuryanov    schedule 16.03.2013
comment
Хорошо знать. Очевидно, iOS не может читать метаданные PNG из файлов, используя мой метод. Если бы у меня был способ читать метаданные PNG, у меня могло бы быть решение. Учитывая, что в iOS есть такие константы, как kCGImagePropertyPNGDescription, я бы предпочел не использовать внешние библиотеки и предпочел бы знать, как на самом деле сделать это только с библиотеками iOS. - person Rich Apodaca; 21.03.2013
comment
Представьте, что вы можете добавлять метаданные к изображению, вы уверены, что другие программы прочитают эту информацию? По какой причине вы хотите добавить метаданные? - person Sergey Kuryanov; 21.03.2013
comment
вы нашли ответ? - person Crashalot; 09.08.2014