John Dyer

Technology and web development in curly bracket languages {Javascript, C#, ActionScript}

Quick C# Vista Photo Tag Reader

by John Dyer 2. September 2006 07:20

I've been reading that Vista adds it's tagging data directly to the file (here and here). I tagged some JPEGs and then opened them up in a text file to see what the XMP data would look like. Then I decided to write some quick and dirty code to read the meta data Windows Vista adds to photos. The code opens the file and reads it line-by-line until it reaches the XMP section. Then it pulls out the Title, Subject, Comments, Rating, and Tags that Vista Photo Gallery adds.

public class VistaMetaExtractor
{
    public static VistaMetaInfo GetMetaInfo(string filename)
    {
        VistaMetaInfo metaInfo = null;
        // Find XMP data in file (it might be faster to read the enter file into memory for files under 10MB)
        string xmpData = FindStringInFile(filename, "<xmp:xmpmeta", "</xmp:xmpmeta>");
        if (xmpData != string.Empty)
        {
            // change namespace definitions (i.e. xmlns:prefix##="http://www.w3.org/2000/xmlns/" )
            xmpData = System.Text.RegularExpressions.Regex.Replace(xmpData, @"xmlns:prefix(?:(\d{1,3}))=""http://www.w3.org/2000/xmlns/""", @"xmlns:prefix$1=""http://randomurl.org""");
            XmlDocument xmlDocument = new XmlDocument();
            xmlDocument.LoadXml(@"<?xml version=""1.0""?>" + xmpData);
            // add namespaces
            XmlNamespaceManager nsMan = new XmlNamespaceManager(xmlDocument.NameTable);
            nsMan.AddNamespace("xmp", "http://ns.adobe.com/xap/1.0/");
            nsMan.AddNamespace("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#");
            nsMan.AddNamespace("MicrosoftPhoto", "http://ns.microsoft.com/photo/1.0");
            nsMan.AddNamespace("dc", "http://purl.org/dc/elements/1.1/");
            nsMan.AddNamespace("tiff", "http://ns.adobe.com/tiff/1.0/");
            nsMan.AddNamespace("exif", "http://ns.adobe.com/exif/1.0/");
            // 2. fill in details from XMP data
            metaInfo = new VistaMetaInfo();
            metaInfo.FileInfo = new FileInfo(filename);
            // TAGS
            XmlNodeList tagNodes = xmlDocument.SelectNodes("//rdf:RDF/rdf:Description/dc:subject/rdf:Bag/rdf:li", nsMan);
            metaInfo.Tags = new string[tagNodes.Count];
            for (int i = 0; i < tagNodes.Count; i++)
            {
                metaInfo.Tags[ i ] = tagNodes[ i ].InnerText;
            }
            // TITLE
            XmlNodeList titleNodes = xmlDocument.SelectNodes("//rdf:RDF/rdf:Description/dc:title/rdf:Alt/rdf:li", nsMan);
            metaInfo.Title = (titleNodes.Count > 0) ? titleNodes[0].InnerText : "";
            // SUBJECT
            XmlNodeList subjectNodes = xmlDocument.SelectNodes("//rdf:RDF/rdf:Description/dc:description/rdf:Alt/rdf:li", nsMan);
            metaInfo.Subject = (subjectNodes.Count > 0) ? subjectNodes[0].InnerText : "";
            // COMMENTS
            XmlNodeList commentNodes = xmlDocument.SelectNodes("//rdf:RDF/rdf:Description/exif:UserComment/rdf:Alt/rdf:li", nsMan);
            metaInfo.Comments = (commentNodes.Count > 0) ? commentNodes[0].InnerText : "";
            // RATING
            XmlNodeList vistaRating = xmlDocument.SelectNodes("//rdf:RDF/rdf:Description/MicrosoftPhoto:Rating", nsMan);
            metaInfo.VistaRating = (vistaRating.Count > 0) ? Convert.ToInt32(vistaRating[0].InnerText) : 0;
            // STARS
            XmlNodeList ratingNodes = xmlDocument.SelectNodes("//rdf:RDF/rdf:Description/xmp:Rating", nsMan);
            metaInfo.Rating = (ratingNodes.Count > 0) ? Convert.ToInt32(ratingNodes[0].InnerText) : 0;
        }
        return metaInfo;
    }
    private static string FindStringInFile(string filename, string startString, string endString)
    {
        string output = string.Empty;
        bool inString = false;
        bool done = false;
        StreamReader sr = new StreamReader(filename);
        while (sr.Peek() >= 0 && !done)
        {
            string line = sr.ReadLine();
            if (inString)
            {
                // check for final
                int endIndex = line.IndexOf(endString);
                if (endIndex > -1)
                {
                    output += line.Substring(0, endIndex + endString.Length);
                    done = true;
                }
                else
                {
                    // keep appending if not at the end
                    output += line;
                }
            }
            else
            {
                // check for start
                int startIndex = line.IndexOf(startString);
                if (startIndex > -1)
                {
                    output += line.Substring(startIndex);
                    inString = true;
                }
            }
        }
        sr.Close();
        return output;
    }
}
public class VistaMetaInfo
{
    private FileInfo _fileInfo;
    private int _rating;
    private int _vistaRating;
    private string _title;
    private string _comments;
    private string _subject;
    private string[] _tags;
    public FileInfo FileInfo
    {
        get { return _fileInfo; }
        set { _fileInfo = value; }
    }
    
    public int Rating
    {
        get { return _rating; }
        set { _rating = value; }
    }
    public int VistaRating
    {
        get { return _vistaRating; }
        set { _vistaRating = value; }
    }
    public string Title
    {
        get { return _title; }
        set { _title = value; }
    }
    public string Subject
    {
        get { return _subject; }
        set { _subject = value; }
    }
    public string Comments
    {
        get { return _comments; }
        set { _comments = value; }
    }
    public string[] Tags
    {
        get { return _tags; }
        set { _tags = value; }
    }
}

It works pretty well and seems fast enough to make a simple gallery application..

Web Statistics