When you manage Exif properties, you commonly have two basic tasks to handle : reading
Exif properties (which is quite simple), and writing Exif properties,
which is, in C#, as simple as:
PropertyItem
property = ...;
Image image = ...;
image.SetPropertyItem(property);
In the code above, the Image object may have been created from a file, a
stream, an existing image, or just from scratch...
The PropertyItem may have been retrieved from an image, or... nothing
: the System.Drawing.Imaging.PropertyItem class do not have a public
constructor!
In this post, I am describing a workaround that enable to create new PropertyItem
objects.
The method is, in fact, quite simple. It consists in having a small Jpeg image
with Exif information embedded in you application, and loading PropertyItem
from it when necessary.
First, you have to add your Jpeg file (let's call it "property.jpg") to
your project, and mark it as an embedded resource.
-
In the Solution Explorer, select the project icon,
-
In the Project menu, click the Add an existing item
menu,
-
In the dialog box, choose a valid Jpeg file and click Open; the file does
appear in the Solution Explorer,
-
In the Solution Explorer, select the Jpeg file ,
-
In the Properties view, set the "Build Action" property to "Embedded
Resource".
The second step is to add a reference on the System.Drawing assembly to
the project, if not present, and to add the following lines at the top of the
source file:
using
System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Reflection;
Now when have to load the image and return the first property item it contains :
///
<summary>
/// Creates a new property item.
/// </summary>
/// <returns>A
new instance of the <see
cref="PropertyItem"/> class.</returns>
public static PropertyItem
CreatePropertyImage()
{
// Gets the current assembly,
retrieves the stream for the resource and loads the associated image.
Assembly currentAssembly = Assembly.GetExecutingAssembly();
using(Stream propertyStream =
currentAssembly.GetManifestResourceStream(currentAssembly.GetName().Name +
"." + "property.jpg"))
using(Image resourceImage =
(Image)Image.FromStream(propertyStream))
{
return resourceImage.PropertyItems[0];
}
}
The job is almost complete : we can now optimize this code to load the image
only once and to return a clean object.
private
static Image propertyImage;
private static Image
PropertyImage
{
get
{
if (propertyImage==
null)
propertyImage
= LoadPropertyImage();
return propertyImage;
}
}
private static Image
LoadPropertyImage()
{
// Gets the current assembly,
retrieves the stream for the resource and loads the associated image.
Assembly currentAssembly = Assembly.GetExecutingAssembly();
using(Stream propertyStream =
currentAssembly.GetManifestResourceStream(currentAssembly.GetName().Name +
"." + "property.jpg"))
using(Image resourceImage =
(Image)Image.FromStream(propertyStream))
{
// Creates a lightweight (1x1
pixel) bitmap with a single, empty exif property item.
// This bitmap will remain in memory during the entire
application runtime.
Image propertyImage = new Bitmap(1, 1);
PropertyItem propertyItem = resourceImage.PropertyItems[0];
propertyItem.Id = 0;
propertyItem.Type = 0;
propertyItem.Len = 0;
propertyItem.Value = new byte[0];
propertyImage.SetPropertyItem(propertyItem);
return propertyImage;
}
}
/// <summary>
/// Creates an empty property item.
/// </summary>
/// <returns>A
new, empty instance of the <see
cref="PropertyItem"/> class.</returns>
public static PropertyItem
CreatePropertyItem()
{
return PropertyImage.PropertyItems[0];
}
/// <summary>
/// Creates a new property item having the given
id, type and value.
/// </summary>
/// <param name="id">The
identifier of the property item.</param>
/// <param name="type">The
type of value the property item does contain.</param>
/// <param name="len">The
length of the property value.</param>
/// <param name="value">The
property value.</param>
/// <returns>A
new, initialized, instance of the <see
cref="PropertyItem"/> class.</returns>
public static PropertyItem
CreatePropertyItem(
int id,
short
type,
int len,
byte[]
value)
{
PropertyItem property = CreatePropertyItem();
property.Id = id;
property.Type = type;
property.Len = len;
property.Value = value;
}
. . .
In the next post, we will convert the raw Exif data buffer (the PropertyItem.Value)
into the native type.