Bernhard's SharePoint

March 6, 2010

How to transform InfoPath 2007 form data into a Word 2007 document – Part 3

Filed under: SharePoint 2007, InfoPath 2007, C#, Word 2007, Xslt — Tags: , , , , , — Bernhard @ 23:46

Part1 of “How to transform InfoPath 2007 form data into a Word 2007 document” gives an overview on creating a Word 2007 document based on a Word template and InfoPath data. The final transformation is done by using the xslt stylesheet InfoPath2Word.xslt.

The stylesheet expects culture dependend formatted InfoPath data where necessary. Normally date, time and number fields are culture dependend. Part2 shows how to transform InfoPath data into culture formatted InfoPath data. The xslt stylesheet InfoPathFormat.xslt calls the function xdFormatting:formatString($value, $type, $options) to output formatted values.

The formatString function is written in C# and passed to the xslt processor as an xslt extension object. I thought it would be good idea to to post about the formatString function separately, because it is also a key function which might be useful in other contexts. Please refer to Implement InfoPath formatString function for customized form data conversions to get a C# listing and see how to use xslt extensions objects.

The final step

Now since all key modules are explained and available it is time to combine them by packing them into the class MyLib.InfoPath.WordConverter. Below please find the C# listing.

I use the WordConverter inside an InfoPath form. I added a button control to the form. When clicked, the event handler first checks whether a pdf file is attached to the form. It is assumed that a pdf file is a manually signed document. If not, a word document is created and attached.

Example on how to use the WordConverter

public void btnCreateContract_Clicked(object sender, ClickedEventArgs e)
{
    // Get navigators
    XPathNavigator navRoot = this.MainDataSource.CreateNavigator();
    XPathNavigator navContract = navRoot.SelectSingleNode("/ns1:contract/ns1:file", this.NamespaceManager);

    // Create word document only, if no signed contract already attached.
    if (FileAttachment.IsPdf(navContract))
    {
        // Pdf attachment already exists
    }
    else
    {
        // Field is either empty or contains an unsigned document
        string viewPath = "view1.xsl";        // The view1 contains all the field formatting
        string dotxPath = "contract.dotx";    // Word template for contract
        string docxFilename = "contract.docx";    // Filename of the resulting word document

        using (Stream viewStream = this.Template.OpenFileFromPackage(viewPath))
        {
            using (Stream dotxStream = this.Template.OpenFileFromPackage(dotxPath))
            {
                FileAttachment.DeleteNillableAttribute(navContract);
                string wordAttachment = WordConverter.CreateWordDocument(navRoot, viewStream, dotxStream, docxFilename);
                navContract.SetValue(wordAttachment);
            }
        }
    }
}

Conclusion

The intension of this series of 3 posts was to show which steps are necessary to convert InfoPath form data to a rich Word document in cases where an electronical form is not useful, for example where signable paper documents are needed.

To keep things easy the Word document is based on a Word template with an embedded xml structure. The structure, which is actually a xml schema, is used by both the Word template and the InfoPath template. The default InfoPath view contains all the culture dependend formatting information to create the final Word document. In my case, the final document is programmatically attached to the InfoPath form and stored in a SharePoint form library. Now I can query form data, associate workflows and benefit from a convenient document management.

MyLib.InfoPath.WordConverter

using System.IO;
using System.Reflection;
using System.Text;
using System.Xml;
using System.Xml.XPath;
using System.Xml.Xsl;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;

namespace MyLib.InfoPath
{
    public static class WordConverter
    {
        /**************/
        /* Properties */
        /**************/

        public static string InfoPath2WordStyleSheet { get { return GetFullResourceName("InfoPath2Word.xslt"); } }
        public static string InfoPathFormatStyleSheet { get { return GetFullResourceName("InfoPathFormat.xslt"); } }

        /**********************/
        /* CreateWordDocument */
        /**********************/

        /// <summary>
        /// Create a Word 2007 document from a Word template containing customXml tags
        /// </summary>
        /// <param name="infoPathForm">InfoPath form</param>
        /// <param name="viewStream">An InfoPath view (xsl-file), which contains all field formatting templates</param>
        /// <param name="dotxStream">The word template in which the data are to be inserted</param>
        /// <param name="docxFilename">Filename of the resulting word document</param>
        /// <returns>a base64 string which contains the InfoPath attachment encoded word document</returns>
        public static string CreateWordDocument(XPathNavigator infoPathForm, Stream viewStream, Stream dotxStream, string docxFilename)
        {
            // Create a new word document from word template
            using (MemoryStream docxStream = new MemoryStream())
            {
                // Copy template
                CopyStream(dotxStream, docxStream);

                // Open template, convert it to a document and merge document wizh InfoPath form data
                using (WordprocessingDocument docx = WordprocessingDocument.Open(docxStream, true))
                {
                    // Change type from template to document
                    docx.ChangeDocumentType(WordprocessingDocumentType.Document);

                    // Fill template by formatted InfoPath form data
                    FillDocument(docx, FormatInfoPathForm(infoPathForm, viewStream));

                    // Save all
                    docx.Package.Flush();
                }

                // Return base64 encoded string of the word document
                docxStream.Seek(0, SeekOrigin.Begin);
                return FileAttachment.ToBase64String(docxFilename, docxStream);
            }
        }

        /******************/
        /* Private methods */
        /******************/

        private static string GetFullResourceName(string resourceName)
        {
            string [] names = Assembly.GetExecutingAssembly().GetManifestResourceNames();
            foreach (string name in Assembly.GetExecutingAssembly().GetManifestResourceNames())
            {
                if (name.EndsWith(resourceName)) return name;
            }
            throw new FileNotFoundException("Resource file not found", resourceName);
        }
        private static void CopyStream(Stream input, Stream output)
        {
            int bufsize = 1024;
            byte[] buffer = new byte[bufsize];
            int bytes;

            while ((bytes = input.Read(buffer, 0, bufsize)) > 0) output.Write(buffer, 0, bytes);
            output.Seek(0, SeekOrigin.Begin);
        }
        private static void FillDocument(WordprocessingDocument docx, XmlDocument xml)
        {
            // Get document.xml from the docx package
            using (Stream streamDocument = docx.MainDocumentPart.GetStream(FileMode.Open, FileAccess.ReadWrite))
            {
                // Create and load a new transform object
                XslCompiledTransform xslt = NewTransformFromResource(InfoPath2WordStyleSheet);

                // Pass wordTemplate parameter to stylesheet
                XsltArgumentList xArgs = new XsltArgumentList();
                xArgs.AddParam("wordTemplate", "", XmlDocumentFromStream(streamDocument));

                // Transform infopath data to word document
                streamDocument.Seek(0, SeekOrigin.Begin);
                xslt.Transform(xml, xArgs, streamDocument);

                // Save
                docx.MainDocumentPart.Document.Save();
            }
        }
        private static XmlDocument FormatInfoPathForm(XPathNavigator infoPathForm, Stream viewStream)
        {
            using (MemoryStream strFormattedInfoPathForm = new MemoryStream())
            {
                // Create and load transform object
                XslCompiledTransform xslFormatter = NewTransformFromResource(InfoPathFormatStyleSheet, false, false, true);

                // Create transformation argument list
                XsltArgumentList xArgs = new XsltArgumentList();
                MyLib.Xslt.Formatting xo = new MyLib.Xslt.Formatting();
                xArgs.AddExtensionObject(xo.NamespaceUri, xo);
                XmlDocument xView = new XmlDocument();
                xView.Load(viewStream);
                xArgs.AddParam("view", "", xView);

                // Transform InfoPath form to a form with formatted values
                xslFormatter.Transform(infoPathForm, xArgs, strFormattedInfoPathForm);

                // Output formatted form
                strFormattedInfoPathForm.Seek(0, SeekOrigin.Begin);
                XmlDocument result = new XmlDocument();
                result.Load(strFormattedInfoPathForm);
                return result;
            }
        }
        private static XmlDocument XmlDocumentFromStream(Stream inStream)
        {
            XmlDocument xDoc = new XmlDocument();
            xDoc.Load(inStream);
            return xDoc;
        }
        private static XslCompiledTransform NewTransformFromResource(string stylesheetResource)
        {
            return NewTransformFromResource(stylesheetResource, false, null, null);
        }

       
        private static XslCompiledTransform NewTransformFromResource(string stylesheetResource, bool enableDebug)
        {
            return NewTransformFromResource(stylesheetResource, enableDebug, null, null);
        }
        private static XslCompiledTransform NewTransformFromResource(string stylesheetResource, bool enableDebug, bool enableDocumentFunction, bool enableScript)
        {
            return NewTransformFromResource(stylesheetResource, enableDebug, new XsltSettings(enableDocumentFunction, enableScript), null);
        }
        private static XslCompiledTransform NewTransformFromResource(string stylesheetResource, bool enableDebug, XsltSettings settings, XmlResolver resolver)
        {
            using (Stream strStylesheet = Assembly.GetExecutingAssembly().GetManifestResourceStream(stylesheetResource))
            {
                XmlTextReader xsltReader = new XmlTextReader(strStylesheet);
                XslCompiledTransform xslt = new XslCompiledTransform(enableDebug);
                xslt.Load(xsltReader, settings, resolver);
                return xslt;
            }
        }
        private static void Load(XslCompiledTransform xsltTransform, Stream xsltStream)
        {
            // Get xslt stream reader
            XmlTextReader xsltReader = new XmlTextReader(xsltStream);
            xsltTransform.Load(xsltReader);
        }
        private static void Transform(XslCompiledTransform xsltTransform, Stream xmlInStream, Stream xmlOutStream)
        {
            // Get xml stream reader
            XmlTextReader xmlReader = new XmlTextReader(xmlInStream);

            // Create stream writer
            XmlTextWriter xmlWriter = new XmlTextWriter(xmlOutStream, Encoding.UTF8);

            // Transform to new stylesheet
            xsltTransform.Transform(xmlReader, xmlWriter);
        }
    }
}

Advertisement

3 Comments »

  1. where are
    fileattachment
    namespacemanager
    MyLib
    Template
    defined?????

    Comment by bharath — July 5, 2010 @ 13:01

    • “MyLib” is my namespace definition of my C# project. You can use any definition of your choice. Check the “using” statements to find all other definitions.

      Comment by Bernhard — March 20, 2011 @ 20:10

  2. donno when u ll update?

    Comment by bharath — July 5, 2010 @ 13:01


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s

Theme: Silver is the New Black. Blog at WordPress.com.

Follow

Get every new post delivered to your Inbox.