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.
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.
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.
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:
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.
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 (the dc:subject
property):
<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):
<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>
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):
<xmpTPg:MaxPageSize rdf:parseType="Resource"> <stDim:w>11.0</stDim:w> <stDim:unit>inch</stDim:unit> <stDim:h>8.5</stDim:h> </xmpTPg:MaxPageSize>
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:
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:
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]); } } }
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:
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); }