Exif Properties in .Net - Part 2 : creating new properties

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:

Code Copy
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:

Code Copy
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 :

Code Copy
/// <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.

Code Copy HideScrollFull
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.

posted on Sunday, March 27, 2005 8:04 PM

Feedback

# re: Exif Properties in .Net - Part 2 : creating new properties 4/19/2005 2:11 PM djonexx

Or, you can retrieve the protected constructor using reflection, invoke it and then use the PropertyItem:

private PropertyItem CreatePropertyItem()
{
System.Reflection.ConstructorInfo ci = typeof(PropertyItem).GetConstructor(
BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public , null, new Type[] { }, null);
return (PropertyItem)ci.Invoke(null);
}

# re: Exif Properties in .Net - Part 2 : creating new properties 4/21/2005 3:50 PM Skup

Yes it is actually possible, but il will break if implementation changes.

It's always better not to rely on private members.

Skup

# re: Exif Properties in .Net - Part 2 : creating new properties 5/6/2005 12:35 AM Rushi Desai

If I load a JPEG image using:
Image img = Image.FromFile("my.jpg");

Then add your propertyitem
img.SetPropertyItem(
CreatePropertyItem());

And save it:
img.Save("saved.jpg");

Will this achieve lossless saving? Or will it re-encode the JPEG?

# re: Exif Properties in .Net - Part 2 : creating new properties 5/6/2005 11:00 AM Buz

Yes, the image is not recompressed and only Exif properties are modified.

See the article below for more information concerning operations you can do without recompression :

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdicpp/GDIPlus/usingGDIPlus/usingimageencodersanddecoders/transformingajpegimagewithoutlossofinformation.asp

# Getting the caption of an image programatically 8/21/2006 4:22 AM David Mohundro

# re: Exif Properties in .Net - Part 2 : creating new properties 11/15/2006 2:54 PM Shinta

@ djonexx:
or simple:
PropertyItem pi=(PropertyItem)Activator.CreateInstance(typeof(PropertyItem), true);

# re: Exif Properties in .Net - Part 2 : creating new properties 11/2/2009 3:29 PM Borkenschorf

Nice code that could come in handy. Since IMHO most parts of the .net framework are waterproofed, the constructor of the PropertyItem class is protected for some reasons. Described in msdn here:
http://msdn.microsoft.com/en-us/library/system.drawing.imaging.propertyitem.aspx.

I did not test Shintas or djonexx snippets (although learned something new). One can assume that the blog authors solution works and that its the way they demand in msdn.

By the way, I cannot imagine a usecase for creating new PropertyItems when using ExifTags, since they are usually made by cameras. Unless you develop own hardware stuff, whats the purpose?

I am asking because I'm hacking an MetaDataEditor-Control, you can edit and view jpegs and it's ExifTags. Nevertheless, maybe anyone has an idea if the adding of ExifTags should be allowed? I'm not informed about IPTC metadata in tifs or jpegs, but with e.g. XNView they are editable in contrary to Exif. Does the PropertyItem concept support IPTC?

Title
 
Name
 
Url
Comments   
Protected by Clearscreen.SharpHIPEnter the code you see: