One note about the above example, the InPlaceMetadataWriter WILL NOT WORK unless there's enough padding in the header of your image. If you are creating a new Metadata block, it will not work either. You may try the example above but TrySave will likely return false on any image other than a PNG. In that situation, you need to create a new BitmapFrame so that the Metadata block is not frozen. See the following examples.
The above example is fairly anemic here are some more fleshed out examples. Also, see this page for better resources on data tags: http://msdn.microsoft.com/en-us/library/bb643802.aspx
The following example shows the way to "Un-Freeze" the metadata block. This will cause the image to be re-encoded but that's the price that must be paid to add metadata if there isn't enough room in the header. Follow this pattern for other image types.
// Get the source image stream
using (FileStream imageFileStream =
new FileStream("test.jpg", FileMode.Open))
{
// Load the image in the decoder
JpegBitmapDecoder decoder = new JpegBitmapDecoder(imageFileStream,
BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
// Make a copy of the frame, this will also unlock the metadata
BitmapFrame frameCopy = BitmapFrame.Create(decoder.Frames[0]);
// Now we have a metadata object that is unfrozen
BitmapMetadata copyMetadata = (BitmapMetadata)frameCopy.Metadata;
// Set your metadata here, metadata in the source frame
// will be rewritten to the output frame and any changes
// will overwrite the metadata in the source frame.
copyMetadata.Title = "Test Title";
// Create a new encoder and add the copied frame to it
JpegBitmapEncoder encoder = new JpegBitmapEncoder();
encoder.Frames.Add(frameCopy);
// Save the new file with the new metadata
using (FileStream imageFileOutStream =
new FileStream("testOutput.jpg", FileMode.Create))
{
encoder.Save(imageFileOutStream);
}
}
// If you want to add a brand new metadata block, you must create
// the metadata block first and then pass the metadata to the
// BitmapFrame.Create method so it's written to the frame
// Get the source image stream
using (FileStream imageFileStream =
new FileStream("test.jpg", FileMode.Open))
{
// Create new metadata first, here we are making an IPTC block in a JPG
// NOTE: IPTC tags do not get parsed correctly on Windows 7
BitmapMetadata jpgData = new BitmapMetadata("jpg");
jpgData.SetQuery("/app13/irb/8bimiptc/iptc/object name", "Test Title");
jpgData.SetQuery("/app13/irb/8bimiptc/iptc/keywords", "Test Tag");
jpgData.SetQuery("/app13/irb/8bimiptc/iptc/date created", "20090512");
jpgData.SetQuery("/app13/irb/8bimiptc/iptc/time created", "115300-0800");
jpgData.SetQuery("/app13/irb/8bimiptc/iptc/caption", "Test Comment");
jpgData.SetQuery("/app13/irb/8bimiptc/iptc/by-line", "Test Author");
jpgData.SetQuery("/app13/irb/8bimiptc/iptc/copyright notice", "Copyright 2009");
// Load the image in the decoder
JpegBitmapDecoder decoder = new JpegBitmapDecoder(imageFileStream,
BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
// Make a copy of the frame and also pass in the new metadata
BitmapFrame frameCopy = BitmapFrame.Create(decoder.Frames[0],
null /* thumbnail */,
jpgData /* new metadata */,
decoder.ColorContexts);
// Now we have the image frame that has a fresh IPTC metadata block
// Create a new encoder and add the frame to it
JpegBitmapEncoder encoder = new JpegBitmapEncoder();
encoder.Frames.Add(frameCopy);
// Save the new file with the new metadata
using (FileStream imageFileOutStream =
new FileStream("testOutput2.jpg", FileMode.Create))
{
encoder.Save(imageFileOutStream);
}
}
// Example of TIFF with EXIF
BitmapMetadata tiffData = new BitmapMetadata("tiff");
tiffData.SetQuery("/ifd/{ushort=40091}",
UnicodeEncoding.Unicode.GetBytes("Test Title".ToCharArray()));
tiffData.SetQuery("/ifd/{ushort=40094}",
UnicodeEncoding.Unicode.GetBytes("Test Tag".ToCharArray()));
tiffData.SetQuery("/ifd/exif/{uint=36867}", "2009:05:12 11:53:00");
tiffData.SetQuery("/ifd/{ushort=40092}",
UnicodeEncoding.Unicode.GetBytes("Test Comment".ToCharArray()));
tiffData.SetQuery("/ifd/{ushort=40093}",
UnicodeEncoding.Unicode.GetBytes("Test Author".ToCharArray()));
tiffData.SetQuery("/ifd/{ushort=33432}", "Copyright 2009");
// Example of WDP (Windows Media Photo) with XMP
// NOTE: This does not work (throws COM Exception) on x64
wdpMetadata = new BitmapMetadata("wmphoto");
// With XMP, you need to create the XMP nodes before you use them
// The BitmapMetadata constructor will accept metadata block types as
// well as image types as shown below
wdpMetadata.SetQuery("/ifd/xmp", new BitmapMetadata("xmp"));
wdpMetadata.SetQuery("/ifd/xmp/dc:title", new BitmapMetadata("xmpalt"));
wdpMetadata.SetQuery("/ifd/xmp/exif:UserComment", new BitmapMetadata("xmpalt"));
wdpMetadata.SetQuery("/ifd/xmp/dc:rights", new BitmapMetadata("xmpalt"));
wdpMetadata.SetQuery("/ifd/xmp/dc:creator", new BitmapMetadata("xmpseq"));
wdpMetadata.SetQuery("/ifd/xmp/dc:subject", new BitmapMetadata("xmpbag"));
// XMP Alt has a default value x-default where you can set the value
wdpMetadata.SetQuery("/ifd/xmp/dc:title/x-default", "Test Title");
wdpMetadata.SetQuery("/ifd/xmp/exif:UserComment/x-default", "Test Comment");
wdpMetadata.SetQuery("/ifd/xmp/dc:rights/x-default", "Copyright 2009");
// XMP Seq/XMP Bag are indexed. You can set multiple values using {ulong=<offset>}
wdpMetadata.SetQuery("/ifd/xmp/dc:creator/{ulong=0}", "Test Author 1");
wdpMetadata.SetQuery("/ifd/xmp/dc:creator/{ulong=1}", "Test Author 2");
wdpMetadata.SetQuery("/ifd/xmp/dc:subject/{ulong=0}", "Test Tag");
// This value is at the root of the XMP block using the XMP date format
wdpMetadata.SetQuery("/ifd/xmp/xmp:CreateDate", "2009-05-12T12:05:05");