How do you overwrite image metadata?

2020-02-28 06:03发布

问题:

I can't seem to get image metadata to be written to the image correctly if the key/val is already present in the original image metadata with CGImageDestination. It works just fine if it they key/val is not present in the original metadata.

It's almost as though image metadata properties in the original image take precedence over modifications. Is this some kind of byzantine formatting issue I am not aware of, where I need to populate the key/val in some unusual way, a bug, or? Anyone else seen this?

Code and output below, for both cases where it works properly (if the value is not already set) and fails to write (if the value is already set to something else).

Any help appreciated greatly appreciated.

Here is where/how I create the image NSData:

// convert the existing asset to nsdata to overwrite itself
ALAssetRepresentation* rep = [asset defaultRepresentation];
Byte* buffer               = (Byte*)malloc(rep.size);
NSUInteger buffered        = [rep getBytes:buffer fromOffset:0.0 length:rep.size error:nil];
NSData* imageData          = [NSData dataWithBytesNoCopy:buffer length:buffered freeWhenDone:YES];

// write the metadata directly into the nsdata of the image itself
NSData* newImage = [self writeMetadataIntoImageData:imageData metadata:newMetadata];

Here is the actual modification of the metadata:

- (NSData*)writeMetadataIntoImageData:(NSData*)imageData metadata:(NSMutableDictionary*)metadataAsMutable
{
    // create an imagesourceref
    CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef) imageData, NULL);

    // read and log pre write metadata
    NSDictionary* metadata = (NSDictionary *) CFBridgingRelease(CGImageSourceCopyPropertiesAtIndex(source,0,NULL));
    NSLog(@"Before:\n------------------------------%@\n------------------------------", metadata);

    // set the new metadata keys here
    NSMutableDictionary* iptc = [metadataAsMutable[(NSString*)kCGImagePropertyIPTCDictionary] mutableCopy];
    if (!iptc)
    {
        iptc = [NSMutableDictionary dictionaryWithCapacity:1];
    }
    iptc[(NSString*)kCGImagePropertyIPTCCaptionAbstract] = @"Hardcoded Caption";
    metadataAsMutable[(NSString*)kCGImagePropertyIPTCDictionary] = iptc;

    // log the new metadata as we want it written
    NSLog(@"Parameter:\n------------------------------%@\n------------------------------", metadataAsMutable);

    // this is the type of image (e.g., public.jpeg)
    CFStringRef UTI = CGImageSourceGetType(source);

    // create a new data object and write the new image into it
    NSMutableData *dest_data = [NSMutableData data];
    CGImageDestinationRef destination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)dest_data,UTI,1,NULL);
    if(!destination)
    {
        NSLog(@"Error: Could not create image destination");
    }
    // add the image contained in the image source to the destination, overidding the old metadata with our modified metadata
    CGImageDestinationAddImageFromSource(destination,source,0, (__bridge CFDictionaryRef) metadataAsMutable);

    BOOL success = NO;
    success = CGImageDestinationFinalize(destination);
    if(!success)
    {
        NSLog(@"Error: Could not create data from image destination");
    }

    // read and log post write metadata
    CGImageSourceRef  source2;
    source2 = CGImageSourceCreateWithData((__bridge CFDataRef) dest_data, NULL);
    NSDictionary *metadata2 = (NSDictionary *) CFBridgingRelease(CGImageSourceCopyPropertiesAtIndex(source2,0,NULL));
    NSLog(@"After:\n------------------------------%@\n------------------------------", metadata2);

    // cleanup
    CFRelease(destination);

    // return the new data
    return dest_data;
}

Here are the NSLogs for when the image has an existing value for the key:

Before:
------------------------------{
    <...snip...>
    "{IPTC}" =     {
        "Caption/Abstract" = Blurry;
        DateCreated = 20130923;
        DigitalCreationDate = 20130923;
        DigitalCreationTime = 173815;
        Keywords =         (
            fake
        );
        SupplementalCategory =         (
            fake
        );
        TimeCreated = 173815;
    };
    <...snip...>
}
------------------------------
Parameter:
------------------------------{
    <...snip...>
    "{IPTC}" =     {
        "Caption/Abstract" = "Hardcoded Caption";
        DateCreated = 20130923;
        DigitalCreationDate = 20130923;
        DigitalCreationTime = 173815;
        Keywords =         (
            fake
        );
        SupplementalCategory =         (
            fake
        );
        TimeCreated = 173815;
    };
    <...snip...>
}
------------------------------
After:
------------------------------{
    <...snip...>
    "{IPTC}" =     {
        "Caption/Abstract" = Blurry;
        DateCreated = 20130923;
        DigitalCreationDate = 20130923;
        DigitalCreationTime = 173815;
        Keywords =         (
            fake
        );
        SupplementalCategory =         (
            fake
        );
        TimeCreated = 173815;
    };
    <...snip...>
}
------------------------------

Here are the NSLogs for when the image has no value for the key:

Before:
------------------------------{
    <...snip...>
    "{IPTC}" =     {
        DateCreated = 20130925;
        DigitalCreationDate = 20130925;
        DigitalCreationTime = 192856;
        Keywords =         (
            fake
        );
        SupplementalCategory =         (
            fake
        );
        TimeCreated = 192856;
    };
    <...snip...>
}
------------------------------
Parameter:
------------------------------{
    <...snip...>
    "{IPTC}" =     {
        "Caption/Abstract" = "Hardcoded Caption";
        DateCreated = 20130925;
        DigitalCreationDate = 20130925;
        DigitalCreationTime = 192856;
        Keywords =         (
            fake
        );
        SupplementalCategory =         (
            fake
        );
        TimeCreated = 192856;
    };
    <...snip...>
}
------------------------------
After:
------------------------------{
    <...snip...>
    "{IPTC}" =     {
        "Caption/Abstract" = "Hardcoded Caption";
        DateCreated = 20130925;
        DigitalCreationDate = 20130925;
        DigitalCreationTime = 192856;
        Keywords =         (
            fake
        );
        SupplementalCategory =         (
            fake
        );
        TimeCreated = 192856;
    };
    <...snip...>
}
------------------------------

回答1:

According to the IPTC documentation the description field is bound to the TIFF and EXIF address. changing the value in the TIFF it updates also the IPTC entry! thank you user2452250 for the tip.