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);
}
}
}
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
donno when u ll update?
Comment by bharath — July 5, 2010 @ 13:01