In this post I’ll explain how I can add a Custom Property to a JPEG Image.
Why this? In my case I’ve to associate to the picture some metadata about a specific item of a production plant before to upload the image inside a common server side repository: the original solution is to temporally maintain a local database to relate metadata with images; instead of this, using this way, each image is atomic and self-consistent.
In the code below, you can see how write and then read my Custom Property using a c# console application: between the lines some useful comments.
using System;
usingSystem.Drawing;
usingSystem.Drawing.Imaging;
usingSystem.Globalization;
usingSystem.Reflection;
usingSystem.Text;
namespaceAddJPGMetadata
{
class Program
{
static void Main(string[] args)
{
const string SOURCEIMAGE = @"c:\users\s.russo\desktop\IMG_3761.jpg";
const string DESTINATIONIMAGE = @"c:\users\s.russo\desktop\IMG_3761_.jpg";
//*******************************************************
//Setting Image Custom Property
//*******************************************************
//Max content lenght
const int MAXCONTENTLEN = 53284;
//This is a fake json to be added as custom property to my JPG Files
//I used a json for my porpouse: you can use any information to be added as propery
//Using json is a nice and clean way to dinamically store several metadata inside the same property
const string FAKEJSON = "{\"Fist Name\":\"Mario\",\"Last Name\":\"Bianchi\",\"Gender\":\"M\",\"Age\":\"34\"}";
//This is the Propery ID
//You can choose any ID
//Be carefull this id is not used yet having a look at this list
//https://msdn.microsoft.com/en-us/library/system.drawing.imaging.propertyitem.id(v=vs.110).aspx
const int PROPID = 0xEFFE;
//This is the original image
//Custom property will be added to this image
Image img = Image.FromFile(SOURCEIMAGE);
//Creating custom property
//I have to user Activator.CreateInstance because the ProperyItem object does not have a constructor
//Thanks to stackoverflow for the solution
PropertyItem pi = (PropertyItem)Activator.CreateInstance(typeof(PropertyItem), BindingFlags.Instance | BindingFlags.NonPublic, null, new object[0], CultureInfo.InvariantCulture);
//My property ID
pi.Id = PROPID;
//Property Type
//https://msdn.microsoft.com/en-us/library/system.drawing.imaging.propertyitem.type(v=vs.110).aspx
// 1 = Specifies that Value is an array of bytes.
pi.Type = 1;
//Convert my JSON string to byte array
//Be carefull: if the byte array lenght is greather than 53284
//GDI+ will throw an exception
//53284 is an empiric value
pi.Value = UTF8Encoding.ASCII.GetBytes(FAKEJSON);
pi.Len = pi.Value.Length;
if (pi.Len > MAXCONTENTLEN)
throw new ArgumentException($"Content is too long. Max lenght is {MAXCONTENTLEN}. Your actual lenght is {pi.Len}");
//Setting property
img.SetPropertyItem(pi);
//Saving Image with another name
img.Save(DESTINATIONIMAGE, ImageFormat.Jpeg);
//*******************************************************
//Reading Image Custom Property
//*******************************************************
//Loading image
Image readImg = Image.FromFile(DESTINATIONIMAGE);
//Reading image property
var p = readImg.GetPropertyItem(PROPID);
//Writing property content into the console window
Console.WriteLine(UTF8Encoding.ASCII.GetString(p.Value));
Console.ReadLine();
}
}
}