Monday 9 March 2009

Nested Mail Merge Regions using Aspose.Words

I've been looking into various solutions for merging Word documents server-side, without resorting to Microsoft Word Automation as it is apparently the devil.

After trialing a handful of solutions on the web, I've concluded that Aspose.Words has some killer features, such as doc and docx support, and PDF file export. Furthermore, unlike competing products, I won't have to sell a kidney to pay for it. Perfect!

That said, once getting into the nuts and bolts of things I discovered one shortfall that is thoroughly discussed in their forums: No support for nested mail merge regions. A region is something Aspose created to allow a portion of a Word document to grow dynamically based on a set of data, e.g. a Table.

Compared to what Microsoft Word can do, a region is pretty powerful stuff. However, not powerful enough for what I need as our app has lots of hierarchical data. For example, imagine a food order that has multiple delivery times:

10.30am
Bagels
Fruit Juice

12.30pm
Sandwiches
Tea & Coffee

If I wanted to define this in a Word template, my regions would look like this:

{TableStart:DeliveryTimes}
{TableStart:Items}
{TableEnd:Items}
{TableEnd:DeliveryTimes}

Now, for anyone who's tried, Aspose.Words will simply throw an exception: Nested mail merge regions are not yet supported.

I figured that since Aspose.Words provides a full Document Object Model for a document, I could write my own class. A bit of looping here and there... easy!

Hardly. I can now see why Aspose has been holding out on such a function. It actually turned out to be a right nightmare to write the class. That said, I got there in the end so I thought I'd share it with the rest of you so that you too don't need to toil over this for days on end.

Introducing AdvancedMailMerge

You may need to add a reference to your copy of Aspose.Words in the WordsExtensions project in order for it to compile.

In terms of how to set up your Word document regions, I've used the same syntax as Aspose.Words. Create merge fields called TableStart:TableName and TableEnd:TableName.

To perform the merge, it's pretty straightforward:

  1. Load your Word file into an Aspose.Words Document object.
  2. Create an instance of the AdvancedMailMerge object.
  3. Pass the Document into the Load method of AdvancedMailMerge. The Load method will validate the regions in the document.
  4. Check the Errors property to ensure there are no structural errors in the document.
  5. Call AddRegionData to add as many data sources as you require. There are special overloads of this function for defining child/parent relationships so that your data is filtered as per your data relationships.
  6. Call the Merge function. This will do it's magic and merge the nested regions with your data sources.
  7. You can then call the Save method of the Document object, outputting a PDF or whatever you like.

User data filtering

There is also a groovy filtering feature that allows users to filter region data by adding syntax to a TableStart merge field. If you add the \b switch to a field, followed by a standard ADO select query, the data will be filtered in the document, e.g.:

{MERGEFIELD TableStart:Items \b Title = 'Bagel' }

Switch support

Since I can't use the standard Aspose.Words MailMerge class, I'm left to support Mail Merge switches myself (e.g. \* Upper to make the result uppercase etc). I've created a MergeFieldSwitch class to handle this. However there are too many switches for me to support. I've added the basics, however they haven't really been tested. Be prepared for bugs if you want to use these.

Please Contribute

I have a lot of testing still to do on this, and I know there will be some issues. If you find any problems, please post them on the CodePlex site and I will look into it.

I'll post my own bug fixes to CodePlex as I come across them.

Cheers,
Anthony.

Sunday 8 March 2009

Obfuscating .NET code on the cheap

I recently needed to obfuscate a .NET component I wrote, and never having done this before I started looking into the various options available. Being a small side project I really didn't want to spend big bucks.

The obvious first choice was to try out the Dotfuscator Community Edition that ships with Visual Studio as it's free. I obfuscated my component, and then disassembled it using Red Gate's Reflector.

It turned out that all it did was obfuscate my namespaces & method names, however it didn't touch the control flow of the methods, meaning that all of my logic source code was still clearly visible.

These features are available in their professional edition which is way out my budget.

Googling around, I stumbled across Eazfuscator.NET, a free .NET obfuscator written by Oleksiy Gapotchenko. Installing the app was a breeze, it even integrated with the Visual Studio tools menu.

It has some very powerful features, including control flow obfuscation, assembly merging, and re-signing strongly named assemblies. For a free app this is very impressive, as you would pay thousands for this elsewhere.

Upon running my obfuscated component through Reflector, my source code had been completely obfuscated, making me a very happy man.

So far I'm wrapped in what this app can do, and I'll keep you posted as I discover more.