We are using Aspose.Words to insert the content of a second document into a main document during a mail merge of the main document.
Specifically using the same technique as described in the Aspose documentation:
Insert and Append Documents in C#|Aspose.Words for .NET - Section “Insert a Document During Mail Merge Operation”.
With reference to the example in the documentation above:
-
There is a merge event handler which is catching a merge event for specific merge fields.
-
The Document Builder’s ‘MoveToMergeField’ ‘finds’ the merge field for the merge field name contained within the event.
-
The content of a second document is inserted into the position found by the above ‘MoveToMergeField’ operation. [As the comment in the code example (in the above Aspose documentation) states: “We load the document and insert it into the current merge field.”]
The technique described in the documentation works, except for a particular situation where the same merge field (which will be replaced within an inserted document) is contained in the header (or footer) AND the body i.e.:
Document Header: {MERGEFIELD Document_1}
Document Body: {MERGEFIELD Document_1}
In this case the merge event handler is handling the merge for the header merge field first, but the ‘MoveToMergeField’ is finding the merge field in the documents body ‘first’ and replacing that – leaving the merge field in the header unmerged (un-replaced) – essentially skipping the necessary replacement in the header (or footer).
Is there a way, please, to ensure the ‘MoveToMergeField’ method finds the merge field being handled by the merge event handler?
Kind regards,
James
@JamesMurdoch You should use DocumentBuilder.MoveToField
instead of DocumentBuilder.MoveToMergeField
method. Please try using the following implementation of IFieldMergingCallback
:
internal class InsertDocumentFieldMergingCallback : IFieldMergingCallback
{
public void FieldMerging(FieldMergingArgs args)
{
if (args.FieldName.Equals("docField"))
{
DocumentBuilder builder = new DocumentBuilder(args.Document);
builder.MoveToField(args.Field, true);
// It is supposed that field value is path to the document is to be inserted.
builder.InsertDocument(new Document((string)args.FieldValue), ImportFormatMode.UseDestinationStyles);
args.FieldValue = null;
}
}
public void ImageFieldMerging(ImageFieldMergingArgs args)
{
// Do nothing
}
}
Alexey,
Thank you for replying so quickly. That seems to have worked – thank you very much.
Although, we are not actually sourcing the inserted document path from the data, but from the actual field name of the merge field – the merge field will not be present in the data.
So the result we get is:
Document Header: {MERGEFIELD insert1.docx}This is the text that was inserted from insert1.docx
Document Body: {MERGEFIELD insert1.docx}This is the text that was inserted from insert1.docx
… that being, the merge fields are still present in the resultant document.
I would imagine that is because the field is not represented in the data and is therefore left unmerged – which understand.
Question: So, for this implementation we could return args.Text = string.Empty instead of args.FieldValue = null (as per your example) to essentially ‘remove’ the merge field which is still contained in the resultant merged document? Or would args.Field.Remove() be a more recommended approach?
Regards,
James
@JamesMurdoch If the field is not in the data source, then FieldMerging
should not be lunched for such field.
In your case, you can simply remove all unused merge fields from the template using Document.MailMerge.DeleteFields()
or by specifying MailMergeCleanupOptions.RemoveUnusedFields
.
Alexey,
Thank you once again Alexey for your kind reply and apologies for carrying on this topic with a follow up point – I will try not to take too much more of your time.
I am not sure what you mean when you say “If the field is not in the data source, then FieldMerging should not be lunched [launched] for such field.”
As some background for this follow up query:
During a merge, we are using the IFieldMergingCallback.FieldMerging as a technique to catch merge field names with a specific pattern {MERGEFIELD *.docx}, where that field name pattern indicates to insert a document at that position (the document name being the merge field name) and not a field value.
We know that the merge fields {MERGEFIELD *.docx} will not exist in the data – they are ‘dummy’ non-data fields. Our code in IFieldMergingCallback.FieldMerging checks for a field name of *.docx and performs the insert of the document using DocumentBuilder.InsertDocument within IFieldMergingCallback.FieldMerging (as already discussed).
QUESTION1: In terms of Aspose itself, are you suggesting we should not be using IFieldMergingCallback.FieldMerging to catch and react to these non-data ‘dummy’ fields – it is wrong to do so? It does appear that FieldMerging is ‘launched’ and working for these non-data fields.
QUESTION2: If it is OK to use FieldMerging to trap and react to non-data fields: then to return text for these ‘dummy’ non-data fields (for the mail merge engine to insert) we can simply set FieldMergingArgs.Text = string.Empty in FieldMerging to force the merge of the dummy field to an empty string and hence remove the merge field?
Re the above, thank you for your suggestion on using Document.MailMerge.DeleteFields() or MailMergeCleanupOptions.RemoveUnusedFields to do this. Unfortunately, we would not want to do this, as, at the end of a merge, we will be checking that there are no unmerged fields in the resultant document to ensure the document author has not mistyped a merge field name accidentally.
Once again, thank you ever so much for your replies on this query – and apologies for the follow up: we want to make sure we are understanding this correctly.
Many thanks in advance,
James
@JamesMurdoch Thank you for you explanation. But could you please also attach your sample template, dummy data and code that will allow us to reproduce the problem on our side? This will help us to better understand what is going wrong.
@JamesMurdoch I have recreated template like yours on my side. You should use Field.Remove()
method to remove the mergefield. See the following modified code:
Document doc = new Document(@"C:\Temp\in.docx");
doc.MailMerge.FieldMergingCallback = new InsertDocumentFieldMergingCallback();
doc.MailMerge.Execute(new string[] { "test" }, new object[] { "tets field value" });
doc.Save(@"C:\Temp\out.docx");
internal class InsertDocumentFieldMergingCallback : IFieldMergingCallback
{
public void FieldMerging(FieldMergingArgs args)
{
if (args.FieldName.EndsWith(".docx"))
{
DocumentBuilder builder = new DocumentBuilder(args.Document);
builder.MoveToField(args.Field, true);
// It is supposed that field value is path to the document is to be inserted.
builder.InsertDocument(new Document("C:\\temp\\"+args.FieldName), ImportFormatMode.UseDestinationStyles);
args.FieldValue = null;
args.Field.Remove();
}
}
public void ImageFieldMerging(ImageFieldMergingArgs args)
{
// Do nothing
}
}
document.docx (12.4 KB)
in.docx (12.6 KB)
out.docx (10.0 KB)
Alexey,
Thank you very much Alexey for all your help on this – that answers my questions.
Kind regards and thanks,
James
1 Like