We’ve just upgraded from v5 to v11 and its not working the same as it used to.
Hi Dale,
Hi Dale,
database.
public void MailMergeInsertHtml()
{
Document doc = new Document(MyDir + "MailMerge.InsertHtml.doc");
// Add a handler for the MergeField event.
doc.MailMerge.FieldMergingCallback = new HandleMergeFieldInsertHtml();
// Load some Html from file.
StreamReader sr = File.OpenText(MyDir + "MailMerge.HtmlData.html");
string htmltext = sr.ReadToEnd();
sr.Close();
// Execute mail merge.
doc.MailMerge.Execute(new string[] { "htmlField1" }, new string[] { htmltext });
// Save resulting document with a new name.
doc.Save(MyDir + "MailMerge.InsertHtml Out.doc");
}
private class HandleMergeFieldInsertHtml : IFieldMergingCallback
{
///
/// This is called when merge field is actually merged with data in the document.
///
void IFieldMergingCallback.FieldMerging(FieldMergingArgs e)
{
// All merge fields that expect HTML data should be marked with some prefix, e.g. 'html'.
if (e.DocumentFieldName.StartsWith("html"))
{
// Insert the text for this merge field as HTML data, using DocumentBuilder.
DocumentBuilder builder = new DocumentBuilder(e.Document);
builder.MoveToMergeField(e.DocumentFieldName);
builder.InsertHtml((string)e.FieldValue);
// The HTML text itself should not be inserted.
// We have already inserted it as an HTML.
e.Text = "";
}
}
void IFieldMergingCallback.ImageFieldMerging(ImageFieldMergingArgs e)
{
// Do nothing.
}
}
Thanks Awais, I must be missing something, because your code looks to be doing exactly the same thing as mine, which is corrupting the formatting. Any ideas?
If I was to guess what the issue is, my guess would be that when I try and insert HTML, but that HTML is a plain text string, your code wraps it in a or and then applies some default formatting to it. And either this is new behaviour since V5 or the default format has changed.
I just did some further investigation, and it appears to be an issue with the HTML insert function. As best I can tell when using “InsertHtml” it removes all formatting present in the document at that point and uses the default document formatting.
Times New Roman 10pt. Nothing is bold any more. And the footer has lost its italics, and had the font size increased.
As an aside, what is the performance going to be like using an event handler on every merge field, then creating a new document builder, then moving to the merge field? I’m a bit worried its going to be slow.
Hi Dale,
Hi Dale,
Dale:
If I was to guess what the issue is, my guess would be that when I try and insert HTML, but that HTML is a plain text string, your code wraps it in a orand then applies some default formatting to it. And either this is new behaviour since V5 or the default format has changed.
and then tags as follows. Here, you can see some default formatting is also specified:
<span style="font-family: 'Times New Roman'; font-size: 12pt">this is a plain text</span>
</p>
Dale:
I just did some further investigation, and it appears to be an issue with the HTML insert function. As best I can tell when using "InsertHtml" it removes all formatting present in the document at that point and uses the default document formatting.You can see in the header its removed the bold and underline. Every merge field has used Times New Roman 12pt, even though the main content is Times New Roman 10pt. Nothing is bold any more. And the footer has lost its italics, and had the font size increased.
Dale:
As an aside, what is the performance going to be like using an event handler on every merge field, then creating a new document builder, then moving to the merge field? I'm a bit worried its going to be slow.
Thanks Awais,
Hi Dale,
Document(@“C:\test\mf.docx”);
DocumentBuilder builder = new DocumentBuilder(doc);
builder.MoveToMergeField("mf", false, false);
InsertHtmlWithBuilderFormatting(builder, "plain html string");
//InsertHtmlWithBuilderFormatting(builder, "formatted html string");
// Just to remove the mergefield
builder.MoveToMergeField("mf");
doc.Save(@"C:\test\out.docx");
public static void InsertHtmlWithBuilderFormatting(DocumentBuilder builder, string html)
{
ArrayList nodes = new ArrayList();
Document doc = builder.Document;
// Store any callback already set on this document
INodeChangingCallback origCallback = doc.NodeChangingCallback;
// Stores nodes inserted during the InsertHtml call.
doc.NodeChangingCallback = new HandleNodeChanging(nodes);
// Some properties may be changed during InsertHTML, try using a brand new builder instead.
DocumentBuilder htmlBuilder = new DocumentBuilder(doc);
// Move to current paragraph of the original builder
if (builder.CurrentParagraph != null)
htmlBuilder.MoveTo(builder.CurrentParagraph);
// Check if a specific inline node is selected move to this instead
if (builder.CurrentNode != null)
htmlBuilder.MoveTo(builder.CurrentNode);
// Insert HTML.
htmlBuilder.InsertHtml(html);
// Restore the original callback
doc.NodeChangingCallback = origCallback;
// Go through every inserted node and copy formatting from the DocumentBuilder to the apporpriate nodes.
foreach (Node node in nodes)
{
if (node.NodeType == NodeType.Run)
{
Run run = (Run)node;
// Copy formatting of the builder's font to the font of the run.
CopyFormatting(builder.Font, run.Font, htmlBuilder.Font);
}
else if (node.NodeType == NodeType.Paragraph)
{
Paragraph para = (Paragraph)node;
// Copy formatting of the builder's paragraph and list formatting to the formatting of the paragraph.
CopyFormatting(builder.ParagraphFormat, para.ParagraphFormat, htmlBuilder.ParagraphFormat);
CopyFormatting(builder.ListFormat, para.ListFormat, htmlBuilder.ListFormat);
}
else if (node.NodeType == NodeType.Cell)
{
Cell cell = (Cell)node;
// Copy formatting of the builder's cell formatting to the cell.
CopyFormatting(builder.CellFormat, cell.CellFormat, htmlBuilder.CellFormat);
}
else if (node.NodeType == NodeType.Row)
{
Row row = (Row)node;
// Copy formatting of the builder's row formatting to the row
CopyFormatting(builder.RowFormat, row.RowFormat, htmlBuilder.RowFormat);
}
}
// Move the original builder to where the temporary builder ended up
if (htmlBuilder.CurrentParagraph != null)
builder.MoveTo(htmlBuilder.CurrentParagraph);
// Move to specific inline node if possible.
if (htmlBuilder.CurrentNode != null)
builder.MoveTo(htmlBuilder.CurrentNode);
}
public class HandleNodeChanging : INodeChangingCallback
{
ArrayList mNodes;
public HandleNodeChanging(ArrayList nodes)
{
mNodes = nodes;
}
void INodeChangingCallback.NodeInserted(NodeChangingArgs args)
{
mNodes.Add(args.Node);
}
void INodeChangingCallback.NodeInserting(NodeChangingArgs args)
{
// Do Nothing
}
void INodeChangingCallback.NodeRemoved(NodeChangingArgs args)
{
// Do Nothing
}
void INodeChangingCallback.NodeRemoving(NodeChangingArgs args)
{
// Do Nothing
}
}
public static void CopyFormatting(Object source, Object dest, Object compare)
{
if (source.GetType() != dest.GetType() && source.GetType() != compare.GetType())
throw new ArgumentException("All objects must be of the same type");
// Iterate through each property in the source object.
foreach (PropertyInfo prop in source.GetType().GetProperties())
{
// Skip indexed access items. Skip setting the internals of a style as these should not be changed.
if (prop.Name == "Item" || prop.Name == "Style")
continue;
object value;
// Wrap this call as it can throw an exception. Skip if thrown
try
{
value = prop.GetValue(source, null);
}
catch (Exception)
{
continue;
}
// Skip if value can not be retrieved.
if (value != null)
{
// If this property returns a class which belongs to the
if (value.GetType().IsClass && prop.GetGetMethod().ReturnType.Assembly.ManifestModule.Name == "Aspose.Words.dll")
{
// Recurse into this class.
CopyFormatting(prop.GetValue(source, null), prop.GetValue(dest, null), prop.GetValue(compare, null));
}
else if (prop.CanWrite)
{
// dest value != default dont copy
if (prop.GetValue(dest, null).Equals(prop.GetValue(compare, null)))
{
// If we can write to this property then copy the value across.
prop.SetValue(dest, prop.GetValue(source, null), null);
}
}
}
}
}
Hi Awais,

else if (prop.CanWrite)<o:p style="position: relative; "></o:p>
{<o:p style="position: relative; "></o:p>
// dest value != default dont copy<o:p style="position: relative; "></o:p>
if (!prop.GetValue(dest, null).Equals(prop.GetValue(compare, null)))<o:p style="position: relative; "></o:p>
{<o:p style="position: relative; "></o:p>
// If we can write to this property then copy the value across.<o:p style="position: relative; "></o:p>
prop.SetValue(dest, prop.GetValue(source, null), null);<o:p style="position: relative; "></o:p>
}<o:p style="position: relative; "></o:p>
}<o:p style="position: relative; "></o:p>
Hi Dale,
Document(@“C:\test\in.docx”);
DocumentBuilder builder = new DocumentBuilder(doc);
builder.MoveToMergeField("testField");
string html = "html text with formatting";
MemoryStream htmlStream = new MemoryStream(Encoding.UTF8.GetBytes(html));
Words.LoadOptions options = new Words.LoadOptions();
options.LoadFormat = LoadFormat.Html;
Document htmlDoc = new Document(htmlStream, options);
MemoryStream textStream = new MemoryStream();
htmlDoc.Save(textStream, SaveFormat.Text);
builder.Write(Encoding.UTF8.GetString(textStream.GetBuffer()));
doc.Save(@"C:\test\out.docx");
Hi Dale,
Thanks guys… I appreciate your patience and assistance with this issue.
Hi Guys,
Test
” and “Test
” will both return the same font if the document font is also “times new roman” but is there any way to detect that the first instance inherited it from the document default and the second was deliberately set.Hi Dale,
Thanks Adam, appreciate your quick response - especially since I’m just down the road so to speak :).
Hello, this is an italic test
. Now I would expect the inserted content to be the same font, font-size, bold and the word italic to be italic.Hi Dale,

