Words and more words and words... on Word.

Generally I talk about vsto deployment here, this is mainly due to the simple limitation that what I work on and know best is deployment.  In my average day when I am "doing my thing" to improve the product I generally am working on stuff around how files get from one place to another and once the files are lined up does the piping fit so that everything works as we want it to.  Today though, I'm going to write some words on Word, or more specifically just some of my thoughts and experiences in customizing word.

One of the things that I do when testing is create tools.  A significant chunk my coding time is usually around automation but fairly frequently I have to look at scenarios that are simply outside of the realm of automation.  Often in dealing with these scenarios I have to do several steps, so it is natural that given enough repetition, I'd rather have a tool that stream lines information.  Usually I create Winform Applications when I'm creating tools.  Generally I am working in a situation where I can't assume a working VSTO install so it makes sense to not build my tools around it too much.

There are however cases when I stray from the path of deployment (and even VSTO) and have to look at other things.  In one project that I'm working on, I've had to do a lot of cracking into binary storage files.  The amount of tooling around this is pretty vast but usually there is a pretty big gaping hole I come across frequently, and that is taking the raw byte hex and "color parsing" it to help visually break down the file.  In the current project I'm working, the file uses a fairly standard patterning that is fairly easy to program.  So I hacked together a tool to do this (did I say I was going to talk about Word?  I really should get back on track....).

One of the limitations of winforms is that working with Text takes a lot more work to add coloring, formatting and dynamic font changing.  Additionally it's fairly complicated to annotate text without touching it in a way that is detrimental an irreversible.  Frankly I don't want to have to worry about any of those details.  So if I have to do something visually with Text (something more than simple spacing/line breaking format) I create a Word add-in or a customized Word doc (aha! I wasn't off track, just taking the circuitous route). 

So In this project I was working on, I created a fairly simple word add-in that allowed me to do some quick parsing/coloring of selected text.  I think I spent about 45 minutes to an hour writing it (though I re-used another 30 or 40 minutes of previous work I wrote around parsing hex text).  This tool has already saved me about 7 hours (I've had it for about a week now).  Here's a (slightly doctored image) example of this tool in action:

 Example of Tag Notate in Action

I doctored up the image a little bit to because I don't want to reveal specific information about the project, but the basic gist is there (the text coloring is valid for example).  With a little more knowledge and time, this tool could be expanded into a generic pattern matching program.  Additionally there are some very cool features that I'm not even scratching the surface on (reviewer comments for example, if you don't know what they are you should spend some time with Office 2007 in the review pane).

So what does the code look like? I warn you now, this is the ugliest raw code I write.  No sane human should have to read it but....here it is:

[Code]

 public static void ColorTagRange(Word.Range inputRange)
{
    System.Object missing = global::System.Type.Missing;

    /*
     * We're going to Color the Range with the following Pattern:
     * Tag ID (4 Hex characters) Color 1
     * Tag Size (8 Hex characters) Color 2
     * Tag Value (2*TagSize value Hex characters)no color change
     */
    string IDSizeValue = "";
    int TagStart = 0;
    int TagSize = 0;
    int tagIDcount = 0;
    int tagSizecount = 0;
    int tagValuecount = 0;

    Word.Range TagIDRange = null;
    Word.Range TagSizeRange = null;

    Word.WdColor IDColor = Word.WdColor.wdColorBrightGreen;
    Word.WdColor SizeColor = Word.WdColor.wdColorAqua;


    string inputText = inputRange.Text;
    char[] inputCharArray = inputText.ToCharArray();
    
    //Set the Tag stat to "ID" 
    TagState currentState = TagState.TagID;

    //offset from Range start.
    int offset = 0;

    Word.Document Doc = Globals.ThisAddIn.Application.ActiveDocument;

    while(offset < inputCharArray.Length)
    {               
        switch(currentState)
        {
            case TagState.TagID:
                if (isHexChar(inputCharArray[offset]))
                {
                    TagIDRange = Doc.Range(ref missing, ref missing);
                    TagIDRange.Start = inputRange.Start + offset;
                    TagIDRange.End = inputRange.Start + offset +1;
                    TagIDRange.Shading.BackgroundPatternColor = IDColor;
                    tagIDcount++;
                }
                if (tagIDcount == 4)
                {

                    currentState = TagState.TagSize;
                }
                offset++;
            break;
            case TagState.TagSize:
                if (isHexChar(inputCharArray[offset]))
                {
                    IDSizeValue = IDSizeValue + inputCharArray[offset].ToString();

                    TagSizeRange = Doc.Range(ref missing, ref missing);
                    TagSizeRange.Start = inputRange.Start + offset;
                    TagSizeRange.End = inputRange.Start + offset + 1;
                    TagSizeRange.Shading.BackgroundPatternColor = SizeColor;
                    tagSizecount++;
                }
            
                if (tagSizecount == 8)
                {
                    try{
                        TagSize = Convert.ToInt32(HEXStringEndianChanger(IDSizeValue), 16)*2; 
                    }
                    catch
                    {
                        System.Windows.Forms.MessageBox.Show("Invalid Size Conversion @: " 
              + TagSizeRange.Start.ToString());
                    }
                        
                    currentState = TagState.TagValue;
                }
                offset++;
            
            break;
            case TagState.TagValue:
                if (tagValuecount == TagSize)
                {
                tagIDcount = 0;
                tagSizecount = 0;
                tagValuecount = 0;
                IDSizeValue = "";
                TagSize = 0;
                currentState = TagState.TagID;
                }
                else
                {
                    if (isHexChar(inputCharArray[offset]))
                        tagValuecount++;
                    offset++;
                }
            break;
        }
    }           
}

[EndCode]

So yeah.... sorry about the ugly code, I did say I wrote it in less than an hour.

Anyway, I'm going to wrap this up with a quick summary on my thoughts of this:

Word is an awesome platform for doing relatively complex tasks with simple easy to write code.  Even if you're not a developer who creates Super Cool Customized Office Projects for a living, you might still want to spend a little time getting to know some of the basic stuff.  Being able to write these simple little tools may just make your life a little easier. 

Thanks for reading.

Kris