One of the little tasks I have is maintaining the Thistle Society web site and that includes adding photos to the gallery pages. I hold information about each picture in an XML file including the original file name, caption and date taken. Up until now I have been entering all this information manually in the XML but I wanted to try to automatically extract the dates from the photos so I wrote a little application to make it easy to load and edit the XML file.
Having read up on image file formats in Wikipedia and other places I know that all the information I need is will be stored in the image files as image metadata including the Date Created. However, I tried parsing the file details myself – a long and complex job as there are several different versions in different image file formats. So, when I found that GDI+ on Windows handles most of the messy bits I was somewhat relieved.
Starting with a new addition to the Kajabity Tools which provides all the file open/save/close functionality (it will be in the next release, coming soon) it is quite easy to build the application and display all the image details in a ListView (in Details mode). Handling the Item Activated event I open a dialog to view and edit the original image file and stored details.
I added a PictureBox control to load and display the image and a small ListView control (Details mode) to display all of the metadata (PropertyItems). Small problem when I initially tried this – the image hadn’t loaded so I got an exception that the object didn’t exist. This was easily resolved by handling the PictureBox LoadCompleted event and filling the property list then – the columns are the property Id, property type, property length and property value.
void PictureBoxLoadCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e) { try { // The PictureBox control is pictureBox - get all the PropertyItems. PropertyItem[] propItems = pictureBox.Image.PropertyItems; foreach ( PropertyItem propItem in propItems ) { ListViewItem item = new ListViewItem( "0x" + propItem.Id.ToString( "x" ) ); item.SubItems.Add( new ListViewItem.ListViewSubItem( item, propItem.Type.ToString() ) ); item.SubItems.Add( new ListViewItem.ListViewSubItem( item, propItem.Len.ToString() ) ); // Code here to extract the value of the property item values... // The ListView control is listImageProperties. listImageProperties.Items.Add( item ); } } catch( Exception ex ) { MessageBox.Show( this, ex.Message, "Error loading image metadata" ); } }
The property value is a byte array whose contents depend on the property id and must be interpreted according to the type and length. GDI+ uses a fixed set of standardised property codes – so once you know the one(s) you need, it’s easy to fetch them. The property I need has Id 0x0132 (PropertyTagDateTime) which has a data type of 2 (PropertyTagTypeASCII – a text string) and a length of 20.
The sample code below shows how to fetch the creation date property and convert it to a DateTime.
try { // GDI+ provides standardised definitions! // http://msdn.microsoft.com/en-us/library/ms534415(VS.85).aspx PropertyItem propItem = pictureBox.Image.GetPropertyItem( 0x132 ); if( propItem != null ) { System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding(); string text = encoding.GetString( propItem.Value, 0, propItem.Len - 1 ); CultureInfo provider = CultureInfo.InvariantCulture; DateTime dateCreated = DateTime.ParseExact( text, "yyyy:MM:d H:m:s", provider ); Debug.WriteLine( "converted " + text + " to " + dateCreated.ToString() ); } else { Debug.WriteLine( "No Property Found" ); } } catch( Exception ex ) { Debug.WriteLine( "Error: " + ex.Message ); }
First, the code shows how to retrieve the value of the property with a special twist – the strings appear to end with a zero byte (like a C style string) so you need to exclude the last byte.
Next, I needed to convert the text to a DateTime. The strings appear to all be formatted using colons – for example “2008:09:27 22:15:35”. I used the DateTime ParseExact method to convert it to a DateTime. Job done.
Of course there was one final problem I had to resolve – many of the pictures were taken on cameras where the date hadn’t been set – so I’m back to manually editing them. Still, it’s a lot easier with a DateTimePicker control.
Hi,
I’ve just implemented a tool to edit the “Date Taken” property (the name in the properties dialog, details tab when right click an image file). According to all documentation, it is the “PropertyTagDateTime” but when i change this property value – it doesnt affect the “Date taken”. If I read this property – I am reading what Ii wrote. It just doesnt appear in the properties dialog or on Win7 column “Date Taken”.
Any idea??
Thanks,
Alon
All I can suggest is to display all of the properties to see if another one is being read as the Date Taken value. Also, verify that the format of the value you are setting matches the expected one for the value.
I’ll have another look when I get more time.
Yes, I did that – none of the PropertyItems show now the “Date Taken”…
I also wrote what I read and still doesn’t change the “Date Taken” but I do read again what I just wrote… Weird!
Alon
I’ve found it… is PropertyTagExifDTOrig (0x9003).
From MSDN:
“Date and time when the original image data was generated.
For a digital camera, the date and time when the picture was taken.”
Alon