Tuesday, December 11, 2007

Accessing JPEG Exif Information in .NET

Syopsis: How to JPEG access date/time information from .NET code.
If you've ever looked at the properties of one of your digital photos, you've noticed a ton of information in the Details tab such as the date the picture was taken, what kind of camera was used, and many other camera details such as aperture, exposure, etc.
This information is supplied for you by the camera and stored in the picture when it is taken.  Thankfully, all cameras use the same format when recording this metadata in their resulting JPEG or TIFF images.  The name of this format is Exif.
Exif is an abbreviation for Exchangeable Image File Format.  This standard basically spells out the properties that can be stored with an image.  There are a lot of properties available.  I've listed a few below and you can read more on it here and here.
Friendly Tag Name Tag Value (Hexadecimal) Sample Value Data Type Detail
Manufacturer 010f Canon ASCII String
Model 0110 Canon DIGITAL IXUS ASCII String
Date and Time 9003 2003:08:11 16:14:32 ASCII String YYYY:MM:DD HH:MM:SS
Flash 9209 0 Unsigned Short 0 indicates that the flash did not fire.
Aperture Value 9202 262144/65536 Unsigned Rational Indicates the amount of light the camera lets in.
Firmware Version 0007 Firmware Version 1.0 ASCII String

Retrieving the Date/Time

What we're concerned about here is the date and time the picture was taken.  In .NET we can easily tap into that data by using PropertyIdList and the PropertyItems properties of an instance of the System.Drawing.Image class.  PropertyIdList gives us a listing of the Exif tags available in integer format.  And PropertyItems gives us access to those values in a byte array.  One collection for both of these would have been nice, but I can't really complain.
So, the first step is to open our JPEG image and get an Image instance to work with:
Image image = Image.FromFile(fileName);
Next, we'll loop through all the tag ID's and find the date/time tag that we're looking for (0x0132):
int tagIndex = -1;
for (int i = 0; i < image.PropertyIdList.Length; i++)
{
    if (image.PropertyIdList[i] == Int32.Parse("132", NumberStyles.HexNumber))
    {
        tagIndex = i;
        break;
    }
}
The next step is to lookup the particular value and translate that to ASCII text that we can work with.
PropertyItem item = image.PropertyItems[tagIndex];
dateString = ASCIIEncoding.ASCII.GetString(item.Value);
Finally, let's go ahead and stuff that date back into a real DateTime structure for easy encapsulation.
string[] dParts = dateString.Split(new string[] { ":", " " }, StringSplitOptions.RemoveEmptyEntries);
int year = Convert.ToInt32(dParts[0].Trim());
int month = Convert.ToInt32(dParts[1].Trim());
int day = Convert.ToInt32(dParts[2].Trim());
int hour = Convert.ToInt32(dParts[3].Trim());
int minute = Convert.ToInt32(dParts[4].Trim());
int second = Convert.ToInt32(dParts[5].Trim());

date = new DateTime(year, month, day, hour, minute, second);
Now that we have our pieces, we'll go ahead and create a nice, clean method that we can use on any given image object:
private static Nullable<DateTime> GetImageDate(Image image)
{
    int tagIndex = -1;

    Nullable<DateTime> date = null;
    // find the index of the id we're looking for
    // Note: the Exif specification at
http://exif.org specifies 0x9003 for
    // the date/time the image was taken
    for (int i = 0; i < image.PropertyIdList.Length; i++)
    {
        if (image.PropertyIdList[i] == Int32.Parse("9003", NumberStyles.HexNumber))
        {
            tagIndex = i;
            break;
        }
    }

    // return if the tag is not found
    if (tagIndex < 0) return null;

    // parse the date string which is in the format: yyyy:mm:dd hh:mm:ss
    string dateString = null;

    try
    {
        PropertyItem item = image.PropertyItems[tagIndex];
        dateString = ASCIIEncoding.ASCII.GetString(item.Value);

        string[] dParts = dateString.Split(new string[] { ":", " " }, StringSplitOptions.RemoveEmptyEntries);
        int year = Convert.ToInt32(dParts[0].Trim());
        int month = Convert.ToInt32(dParts[1].Trim());
        int day = Convert.ToInt32(dParts[2].Trim());
        int hour = Convert.ToInt32(dParts[3].Trim());
        int minute = Convert.ToInt32(dParts[4].Trim());
        int second = Convert.ToInt32(dParts[5].Trim());

        date = new DateTime(year, month, day, hour, minute, second);
    }
    catch { }
    // in case of an exception, we'll just ignore it and not return a date

    return date;
}
Now you can drop the above code in any application and have some fun.

Monday, September 24, 2007

SuperBranding

If you’ve ever modified a WSS/MOSS master page only to be frustrated that some pages never change their look you'll want to look into a solution to this that Ted Pattison posted on CodePlex.  The reason for this disparity is that SharePoint has two master pages: Default.master and Application.master.  Default.master is what you'll be editing from within the SharePoint Designer and is something that can be customized from the site definition.  Conversely, anything that lives in the _layouts directory are called application pages and cannot be customized for each site.  These pages are accessible by any SharePoint site and are usually used for settings pages like /_layouts/settings.aspx.  These application pages all point to a master page that is different than the customizable master page and is called application.master.  The way Ted's solution works is to intercept the request using an HttpModule and determine if the page is pointing to application.master.  If so, it swaps out the reference to default.master instead.  I would expect that the next version/service pack should take care of this little nuisance.

Saturday, September 1, 2007

KY Day of .NET

Yesterday we had a day-long code camp in Louisville.  It was a great success with about 70 people attending.  We covered topics from WCF, to SQL 2008, to LINQ.  I had the privilege of covering WCF.  It was a good preparation for me since I'll be doing the September DevCares on WCF on the 28th.  A summary of all the sessions can be found here: kydayof.net.

Friday, August 31, 2007

Sample SharePoint Master Pages

To the person new to SharePoint (MOSS and WSS), the UI out of the box is pretty darn cool looking.  It is very functional and is polished.  However, if you've seen it on more than one site then you're probably already sick of it.  When I got in to styling SharePoint, I wanted to just do something simple.  What I needed then was a set of samples, complete with images and stylesheets and a master page.  I finally found something that I can use and implement in about 10 minutes with SharePoint Designer.  On the Microsoft Office site, you'll find a set of 4 master pages with several color schemes each.

Thursday, August 30, 2007

Hosting WCF in WAS

WAS (Windows Activation Service) is a technology developed primarily for web service hosting that gives you many of the benefits of hosting a web service in IIS, without actually using IIS.  It is a stand-alone service that is meant to be "activated" when requests come in.  These requests could be HTTP or some other protocol such as TCP or Named Pipes, which IIS doesn't support.  Since the next generation of distributed computing technology (WCF) is protocol-agnostic, these two technologies seem to be made for one another.  Gee what a coincidence.

If you're curious how to host a WCF service in WAS, I found a great article here.  This is something to keep an eye on because it's geared up for WCF hosting.  This is already available in Vista and it's clearly something you'll want in your bag of tricks when Server 2008 is released.

My First Post

So what in the world would I need a blog for?  I can tell you.  I can't remember all this stuff and I need a place to write it down.  So what better place than a blog, where the entire world can see it.  I hope you find it interesting.  Now, hopefully people will get off my case for not having a blog.
By the way, I'm using Windows Live Writer for my blog tool.  Very cool.