Search code examples
c#xmp

Store array in XMP dc:Subject field


I am trying to replicate how Adobe Lightroom is storing the "keywords" within an image (JPEG) structure.

So far i found that the keywords are stored in the metadata in the field

/xmp/dc:Subject

Light room is storing the each tag as xml structure in the format:

<dc:subject>
    <rdf:Bag>
        <rdf:li>Bianca</rdf:li>
        <rdf:li>KEYWORDS -LR</rdf:li>
        <rdf:li>Laura</rdf:li>
        <rdf:li>Lyndel</rdf:li>
        <rdf:li>T-ALL</rdf:li>
    </rdf:Bag>
</dc:subject>

My issues is that when I am writing the keywords with microsoft's metadata query language, the keywords are stored as string, so as a result in the image the keywords are stored in the format:

<dc:subject>Bianca; KEYWORDS -LR; Laura; Lyndel; T-ALL</dc:subject>

So, my question is how to store and array, so the result to be similar to the xml structure above.

Bellow is my code:

// Read the image stream
using (var originalFile = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.None))
{
    // Create the bitmap decoder
    decoder = BitmapDecoder.Create(originalFile, createOptions, BitmapCacheOption.None);

    // The the first frame contains the image metadata
    var bitmapFrame = decoder.Frames[0];

    if (bitmapFrame != null && bitmapFrame.Metadata != null)
    {
        // To be able to modify the metadata, first we need to clone it
        BitmapMetadata metadata = bitmapFrame.Metadata.Clone() as BitmapMetadata;

        // Remove the XP Subject field
        metadata.RemoveQuery("System.Subject");

        // Remove the XP keyword field
        metadata.RemoveQuery("System.Keywords");
        
        // Write Tags (Lightroom keywording)
        string keywords = "K1; K2; K3";
        metadata.SetQuery("/xmp/dc:Subject", keywords);
        
        // Create a bitmap encoder
        var encoder = CreateBitmapEncoder(imageFormat);

        // Create new frame with the updated metadata
        // Keep everything the same except the updated metadata
        encoder.Frames.Add(BitmapFrame.Create(
            bitmapFrame, bitmapFrame.Thumbnail, metadata, bitmapFrame.ColorContexts));

        // Attemp to save the update image
        using (Stream outputFile = File.Open(path + tempSufix, FileMode.Create, FileAccess.ReadWrite))
        {
            encoder.Save(outputFile);
        }
    }
}

Edit:

In this link https://msdn.microsoft.com/en-us/library/ee719963%28v=vs.85%29.aspx it is shown that writing should be in the following query:

/xmp/<xmpbag>dc:subject

But what it <xmpbag>?

Edit2:

It turns out that the write 'location' is as follows:

/xmp/<xmpbag>dc:subject/{ulong=0}

/xmp/<xmpbag>dc:subject/{ulong=1}


Solution

  • // Read the image stream
    using (var originalFile = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.None))
    {
        // Create the bitmap decoder
        decoder = BitmapDecoder.Create(originalFile, createOptions, BitmapCacheOption.None);
    
        // The the first frame contains the image metadata
        var bitmapFrame = decoder.Frames[0];
    
        if (bitmapFrame != null && bitmapFrame.Metadata != null)
        {
            // To be able to modify the metadata, first we need to clone it
            BitmapMetadata metadata = bitmapFrame.Metadata.Clone() as BitmapMetadata;
    
            // Remove the XP Subject field
            metadata.RemoveQuery("System.Subject");
    
            // Remove the XP keyword field
            metadata.RemoveQuery("System.Keywords");
    
            // Write Tags (Lightroom keywording)
            var keywords = "K1; K2; K3";
            var keywordArray = keywords.Split(new[] { "; " }, StringSplitOptions.RemoveEmptyEntries);
    
            // Write in the XMP section
            metadata.SetQuery("/xmp/dc:Subject", new BitmapMetadata("xmpbag"));
            for (var i = 0; i < keywordArray.Length; i++)
            {
                var order = "/{ulong=" + i + "}";
                var keyword = keywordArray[i];
                metadata.SetQuery("/xmp/<xmpbag>dc:Subject" + order, keyword);
            }
    
            // Write in the IPTC section
            metadata.SetQuery("/app13/irb/8bimiptc/iptc/{str=Keywords}", keywordArray);
    
            // Create a bitmap encoder
            var encoder = CreateBitmapEncoder(imageFormat);
    
            // Create new frame with the updated metadata
            // Keep everything the same except the updated metadata
            encoder.Frames.Add(BitmapFrame.Create(
                bitmapFrame, bitmapFrame.Thumbnail, metadata, bitmapFrame.ColorContexts));
    
            // Attemp to save the update image
            using (Stream outputFile = File.Open(path + tempSufix, FileMode.Create, FileAccess.ReadWrite))
            {
                encoder.Save(outputFile);
            }
        }
    }