Generating documents with user editable templates

Chris Henry

OCC's product development team is currently working on new web-based components to extend our existing ContrOCC product to provide specialist corporate finance functionality tailored to local authority requirements. Each new component is being written from the ground up, and we are taking the opportunity to review our technology choices even where we have some prior experience.

A particular area of interest is document generation. Although our products are based on core financial, contract management, and case management features, each of these gives rise to documents which are either posted to care users and providers, or uploaded to portals where they can be accessed online.

Our requirements

The letterheads, fonts, explanatory text, and choice of data fields need to be configurable for different local authority customers, and our preferred method is to allow upload of an MS Word mail merge template which we then combine with the relevant data. The resulting Word document can either be stored directly, or converted to PDF if preferred.

Unfortunately, MS Word automation itself is not a suitable technology for web-based products (to give just one reason, it might open a popup dialog on the server). We have some prior experience with Aspose.Words, which we have used before for complex merges including table data, although we had to code this by hand through Aspose's (excellent) C# object model, and it was one of our candidates for the new module alongside other free and commercial libraries.

We developed prototype document generation functionality integrated with data sources from the working code base of our new component. We reviewed our requirements and quickly concluded that although we only required PDF output, we still wanted to stick with Word-based templates.

Two commercial competitors quickly emerged: Aspose.Words and GemBox.Document. We found to our pleasant surprise that both now support MS Word's table mail merge, meaning we can generate a reasonably complex document using mail merge functionality alone. In terms of our prototype itself there was nothing to separate the two products.

The outcome

We ultimately decided to stick with Aspose based partly on efficiency of licensing across multiple products, and partly on the confidence that we have from using it across many years without a hiccup.

Aspose logo

The final implementation - once we'd hidden Aspose behind an interface and converted from our business objects to Aspose mail merge objects - took only two C# classes totalling three screens of neatly formatted code. A couple of snippets are shown here to give a flavour of the work:

private static IEnumerable GetRegionDataSources(object model)
{
    return from p in model.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)
        where p.CanRead
            && typeof(IEnumerable).IsAssignableFrom(p.PropertyType)
            && (p.PropertyType != typeof(string))
        let regionCollection = p.GetValue(model) as IEnumerable
        select new MailMergeObjectDataSource(p.Name, regionCollection);
}

public void Write(object model, Stream stream)
{
    _templateStream.Position = 0;
    var document = new Aspose.Words.Document(_templateStream);
    var dataSource = new MailMergeObjectDataSource(model);

    document.MailMerge.Execute(dataSource);
    foreach (var regionDataSource in dataSource.RegionDataSources)
    {
        document.MailMerge.ExecuteWithRegions(regionDataSource);
    }

    document.Save(stream, Aspose.Words.SaveFormat.Pdf);
    stream.Position = 0;
}

Here is Aspose.Words in action generating a bill in ContrOCC's web-based billing module:

ContrOCC Billing with PDF bill