XMP Metadata

This article provides an overview of XMP metadata and examines how to work with XMP metadata in Graphics Mill, namely:

For general information about metadata, see the Working with Metadata topic.

XMP Overview

The instance of XMP metadata in a file is called an XMP packet, which is represented by the XmpData class in Graphics Mill. An XMP packet consists of XMP metadata properties, and each property has a name and a value. A value in the XMP data model can be one of the following three forms: simple, structure, or array. A property name in an XMP packet shall contain a namespace and be unique within that packet.

The XmpTagNames class provides names of some standard XMP properties, including properties from the Dublin Core, Camera Raw, EXIF, Photoshop, etc. namespaces.

Let us examine the namespaces and forms of property values in more detail.

Note

The code snippet in the writing XMP metadata part of this article illustrates the creation of a namespace and a simple value, an array, a structure, and an alternative array (localized) values.

Namespaces

XML namespaces are used for providing uniquely named elements and attributes in an XML document. Namespaces are an important part in understanding XMP, since all XMP names consist of a namespace and a local name. This means that any XMP property should belong to a namespace. Therefore, before creating a property, make sure that its namespace already exists, or create the namespace. Graphics Mill provides the XmpNamespace class to work with namespaces.

XMP metadata may include properties from one or more of the namespaces. For example, a typical subset might include the following:

  • Dublin Core namespace: dc:title, dc:creator, dc:description, dc:subject, dc:format, dc:rights
  • XMP basic namespace: xmp:CreateDate, xmp:CreatorTool, xmp:ModifyDate, xmp:MetadataDate

Simple Values

A simple value is a Unicode text string. For example, the following standard XMP values are simple values:

<dc:creator>John Wood</dc:creator>
<xmp:CreateDate>2002-08-15T17:10:04-06:00</xmp:CreateDate>

The XmpValueNode class represents simple values in Graphics Mill.

Array Values

An array is a container for zero or more items indexed by their ordinal position, starting from 1. An array item may have any XMP value form, but all items in an array shall have the same form.

XMP provides three variants of arrays:

  • An unordered array shall have no meaning or constraints on the order of items within it. The items in an unordered array may be reordered at any time.
  • In an ordered array the items are ordered by their indices. The items in an ordered array shall not be arbitrarily reordered. The meaning of the order may be defined by data type or by application.
  • In an alternative array the items are ordered and shall not be arbitrarily reordered. The meaning of the order may be defined by data type or by application. If any item is a preferred default, it should be the first item in the array. The first item in the array should be chosen when no other criteria apply.

An unordered array (the dc:subject property):

XML
<dc:subject>
    <rdf:Bag>
        <rdf:li>nature</rdf:li>
        <rdf:li>mountain</rdf:li>
    </rdf:Bag>
</dc:subject>

A special case in alternative arrays are localized nodes. A localized node contains translations of the node value to different languages. Graphics Mill provides two classes to operate on localized nodes: XmpLocalizedTextNode and XmpLocalizedTextItemNode. The XmpLocalizedTextNode represents a localized property and contains one or more different translations, each represented by a XmpLocalizedTextItemNode instance.

An alternative array (the localized dc:description property):

XML
<dc:description>
    <rdf:Alt>
        <rdf:li xml:lang="x-default">Very beautiful mountains.</rdf:li>
        <rdf:li xml:lang="en-US">Very beautiful mountains.</rdf:li>
        <rdf:li xml:lang="de-DE">Sehr schone Berge.</rdf:li>
    </rdf:Alt>
</dc:description>

Structure Values

A structure is a container for zero or more named fields, where the order of the fields is not significant. Fields may be optional or required. Each field in a structure shall have a unique name within that structure and field names need to be XML expanded names. There are no limitations on the field namespace or the value form. The structure values in Graphics Mill are represented by the XmpStructNode class.

A structure (the xmpTPg:MaxPageSize property):

XML
<xmpTPg:MaxPageSize rdf:parseType="Resource">
    <stDim:w>11.0</stDim:w>
    <stDim:unit>inch</stDim:unit>
    <stDim:h>8.5</stDim:h>
</xmpTPg:MaxPageSize>

Reading XMP Metadata

Graphics Mill supports reading XMP information from the JPEG, TIFF, and PDF files. Each file format has a corresponding image reader, a class from the Aurigma.GraphicsMill.Codecs namespace. So, you can use the JpegReader, TiffReader, and PdfReader image readers to extract metadata. The first two classes inherit the ImageReader.Xmp property, while the latter one uses PdfReader.Metadata, which return a string containing XML code.

If you do not want to parse this XML by yourself, you can use the XmpData class described previously.

The following code reads a file and iterates through all its XMP properties:

C#
using (var reader = new JpegReader(@"Images\in.jpg"))
{
    //Check if XMP data are present in the file
    if (reader.Xmp != null)
    {
        //Get an XML code from the reader
        var xmp = new XmpData(reader.Xmp);

        //Go through all simple value nodes and print them along with their tags
        foreach (XmpNode node in xmp.Values)
        {
            if (node.NodeType == XmpNodeType.SimpleProperty)
                Console.WriteLine("{0}: {1}", node.Name, node.ToString());
        }
    }
}

The following code is useful if you want to get only specific properties:

C#
using (var reader = new JpegReader(@"Images\in.jpg"))
{
    //Check if XMP data are present in the file
    if (reader.Xmp != null)
    {
        //Get an XML code from the reader
        var xmp = new XmpData(reader.Xmp);

        //Print the value of the xmp:CreatorTool tag if it exists
        if (xmp.Contains(XmpTagNames.XmpCreatorTool))
        {
            Console.WriteLine("This image was created using {0}",
                xmp[XmpTagNames.XmpCreatorTool]);
        }
    }
}

Writing XMP Metadata

Graphics Mill supports writing XMP information to JPEG, TIFF, and PDF files. To write XMP metadata you should use the Xmp property of image writers JpegWriter.Xmp and TiffWriter.Xmp or the Metadata property of PdfWriter.Metadata.

The Xmp and Metadata properties of the writers are quite the same as ones of the readers discussed previously. They accept a string containing an XML code as a value. You can construct this string by yourself or use the XmpData class.

The XmpData class provides the AddNode(XmpNode) and Remove(Object) methods to operate with tree nodes. The first one allows adding a node to the current tree. The second method removes a node from the tree. After the tree is designed, call the XmpData.Save() method. The Save() method returns a string suitable for the Xmp and Metadata properties.

The following code adds the simple (dc:creator), array (dc:subject), structure (xmpTPg:MaxPageSize), and localized (dc:description) values to the XMP metadata of an image and saves the resulting file:

C#
using (var jpegReader = new JpegReader(@"Images\in.jpg"))
using (var jpegWriter = new JpegWriter(@"Images\out.jpg"))
{
    XmpData xmp = null;

    //Check if XMP data are present in the file and...
    if (jpegReader.Xmp != null)
    {
        //...get an XML code from the reader
        xmp = new XmpData(jpegReader.Xmp);
    }
    else
    {
        //...or create an empty structure
        xmp = new XmpData();
    }

    //Remove the dc:Creator tag if it is already exists
    if (xmp.Contains(XmpTagNames.DCCreator))
    {
        xmp.Remove(XmpTagNames.DCCreator);
    }

    //Add the dc:Creator tag (simple value)
    var node = new XmpValueNode(
        XmpNodeType.SimpleProperty, "John Wood", XmpTagNames.DCCreator);
    xmp.AddNode(node);

    //Remove the dc:Description tag if it is already exists
    if (xmp.Contains(XmpTagNames.DCDescription))
    {
        xmp.Remove(XmpTagNames.DCDescription);
    }

    //Add the dc:Description tag (localized simple value)
    var localizedNode = new XmpLocalizedTextNode(XmpTagNames.DCDescription);
    localizedNode.AddNode(new XmpLocalizedTextItemNode("en-US", "Very beautiful mountains."));
    localizedNode.AddNode(new XmpLocalizedTextItemNode("de-DE", "Sehr schone Berge."));
    xmp.AddNode(localizedNode);

    //Remove the dc:Subject tag if it is already exists
    if (xmp.Contains(XmpTagNames.DCSubject))
    {
        xmp.Remove(XmpTagNames.DCSubject);
    }

    //Add the dc:Subject tag (array value)
    var arrayNode = new XmpArrayNode(XmpNodeType.UnorderedArray, "dc:subject");
    arrayNode.Add(new XmpValueNode(XmpNodeType.SimpleProperty, "nature", null));
    arrayNode.Add(new XmpValueNode(XmpNodeType.SimpleProperty, "mountain", null));
    arrayNode.Add(new XmpValueNode(XmpNodeType.SimpleProperty, "strange", null));
    xmp.AddNode(arrayNode);

    //Remove the xmpTPg:MaxPageSize tag if it is already exists
    if (xmp.Contains(XmpTagNames.XmpTPgMaxPageSize))
    {
        xmp.Remove(XmpTagNames.XmpTPgMaxPageSize);
    }

    //Add the xmpTPg:MaxPageSize tag (structure value)
    //Add the stDim: namespace
    var stDim = XmpNamespace.AddNamespace("http://ns.adobe.com/xap/1.0/sType/Dimensions#", "stDim:");

    var structNode = new XmpStructNode(XmpTagNames.XmpTPgMaxPageSize);
    structNode.Add("stDim:h", new XmpValueNode(XmpNodeType.SimpleProperty, "8.5", "stDim:h"));
    structNode.Add("stDim:w", new XmpValueNode(XmpNodeType.SimpleProperty, "11.0", "stDim:w"));
    structNode.Add("stDim:unit", new XmpValueNode(XmpNodeType.SimpleProperty, "inch", "stDim:unit"));
    xmp.AddNode(structNode);

    //Save modified metadata with the file
    jpegWriter.Xmp = xmp.Save();
    Pipeline.Run(jpegReader + jpegWriter);
}

See Also

Reference

Manual

Other