Urgent:Mail Merge & Multiple document combining

I have read through some of the forum post related to these topics and I haven’t found an approach that accounts for the product requirements I have been given.

First let me start by asking, what is the best/recommended practice for inserting contents on external documents into a mail merge document while respecting all the source section settings (ex. styles, font, orientation, etc). Unfortunately I cannot just append all the document content to the end of the mail merge document, that seems to work fine. Its seems that using DocumentBuilder.InsertBreak(BreakType.SectionBreakNewPage) has something to do with the "reference node is not a child of this node " exception that I am seeing. (Using Aspose.Words for .NET 11.10.0.0)

So the scenario is:

Section
MergeField

MergeField (This one is asking for attachments/external documents)

MergeField

It seems like I need to be able to do the following:
Section
MergeField

(Insert Page and Section Break)

(Add Sections from external document in place of second merge field)

Section

Section

(Then have a Section with the last Merge field remaining )

Section
MergeField

If that makes sense. If I dont execute the insertbreak() the code works exactly as expected only the sections are @ the end of the document. If I execute the insertbreak() code without the code that is attempting to insert external document content, I still get the above mentioned exception. So I’m not sure if it is a bug internal to that method or if there is something else in the mail merge process that I have yet to identify.

Mail merge is executing with regions (and nested regions for that matter).

Consider this Merge Template (or view attachment for specifics):

{MERGEFIELD Tracking_ID }

{MERGEFIELD
TableStart:Level_2 * MERGEFORMAT}

{MERGEFIELD Attachments * MERGEFORMAT}

{MERGEFIELD TableStart:Level_3 * MERGEFORMAT} 

{MERGEFIELD More_Attachments * MERGEFORMAT}

{MERGEFIELD TableEnd:Level_3 * MERGEFORMAT}

{MERGEFIELD TableEnd:Level_2 * MERGEFORMAT}

{MERGEFIELD Description * MERGEFORMAT}

Essentially, I am able to detect the “attachment” merge fields and I attempt to page and section break using insert break but that’s not working. I need the final output to be the data for the first merge field then all the attachment data “inline” then the data for the last merge field.

I can get code snippets if needed, hoping this has been encountered before and there is a better approach.

Calling pattern in code is basically:

Merge()
{
…
Document.MailMerge.ExecuteWithRegions(myDataSource);
…
}

MyMailMerge_MergeFieldCallback(FieldMergingArgs e) 
{
Process(e.Document, e.Field.GetFieldCode(), e.FieldName, e.FieldValue);
}

Process(Document document, string fieldCode, string fieldName, object fieldValue)
{
…
Builder.MoveToMergeField(fieldName);
…
(if my value is a memory stream)

Section sectionBeforeBreak = Builder.CurrentSection;
Builder.InsertBreak(BreakType.SectionBreakNewPage);

Document supportingDocumentation = new Document((MemoryStream)value);
InsertDocument(Builder, supportingDocumentation, sectionBeforeBreak );

}

InsertDocument(DocumentBuilder builder, Document sourceDocument, Section currentSection)
{
if (sourceDocument.Sections == null || sourceDocument.Sections.Count == 0) return;

foreach (Section srcSection in sourceDocument.Sections)
{
srcSection.ClearHeadersFooters();
Section sectionToInsert = (Section)builder.Document.ImportNode(srcSection, true, ImportFormatMode.KeepSourceFormatting);
builder.Document.InsertAfter(sectionToInsert, currentSection);
currentSection = sectionToInsert;
}
}

Let me know what you think or if you need any additional details.

Thanks
Vernon

Hi Vernon,

Thanks for your inquiry. I would suggest you please upgrade to the latest version (v13.3.0.1) from here and let us know how it goes on your side.

Please note that ImportFormatMode defines how conflicting styles are handled when one document is imported into the other. When you append different document at the end of parent document this option specifies how formatting is resolved when both documents have a
style with the same name, but different formatting.

I suggest you please read difference between ImportFormat Modes from here:
https://docs.aspose.com/words/net/insert-and-append-documents/

In your case, I suggest you please implement the IFieldMergingCallback interface as shown in following code snippet to achieve your requiremetns. Hope this helps you.

private class HandleMergeFieldInsertDoc : IFieldMergingCallback
{
    /// 
    /// This is called when merge field is actually merged with data in the document.
    /// 
    void IFieldMergingCallback.FieldMerging(FieldMergingArgs e)
    {
        if (e.FieldName.Contains("attchment"))
        {
            // Insert the text for this merge field as HTML data, using DocumentBuilder.
            DocumentBuilder builder = new DocumentBuilder(e.Document);
            builder.MoveToMergeField(e.FieldName, true, false);
            builder.InsertBreak(BreakType.SectionBreakNewPage);
            Document doc = new Document(e.FieldValue.ToString());
            InsertDocument(builder.CurrentParagraph, doc);
            e.Text = "";
        }
    }
    void IFieldMergingCallback.ImageFieldMerging(ImageFieldMergingArgs e)
    {
        // Do nothing.
    }
}

Thank you for the response!
I am already implementing the IFieldMergingCallback interface. In the pseudo code provided the Process() is called in the context of the merge field callback. In testing your sample above, the same "reference node is not a child of this node " exception is observed. If I remove the line that inserts the break it works as expected only the source documents are @ the end of the destination document which is not desired. I tested with the latest ddls’ (13.3.0.1) and that did not seem to make a difference same exception. Please advise.
Thanks,
Vernon

Hi Vernon,

Thanks for your inquiry. In your case, I suggest you following solution.

  1. Implement IFieldMergingCallback interface, track for each occurrence of attachment field and insert a temporary Bookmark at mail merge field. Please see the following code:
public class HandleMergeFieldInsertDoc : IFieldMergingCallback
{
    int i = 0;
    public Hashtable docs = new Hashtable();
    void IFieldMergingCallback.FieldMerging(FieldMergingArgs e)
    {
        if (e.FieldName.Contains("attach"))
        {
            DocumentBuilder builder = new DocumentBuilder(e.Document);
            builder.MoveToMergeField(e.FieldName, true, true);
            builder.StartBookmark("bm_" + i);
            builder.EndBookmark("bm_" + i);
            Stream stream = File.OpenRead(e.FieldValue.ToString());
            docs.Add("bm_" + i, stream);
            i++;
            e.Text = "";
        }
    }
    void IFieldMergingCallback.ImageFieldMerging(ImageFieldMergingArgs e)
    {
        // Do nothing.
    }
}
  1. After calling the ExecuteWithRegions method, iterate through all bookmarks, insert Section Break and Document at the places where you inserted temporary Bookmarks in step 1. After that remove temporary Bookmarks from document: Please see the following code snippet:
Document doc = new Document(MyDir + "SampleMailMergeTemplate).docx");
DocumentBuilder builder = new DocumentBuilder(doc);
DataTable dt = new DataTable("BU_2");
dt.Columns.Add("Tracking_ID", typeof(int));
dt.Columns.Add("List:attach.Data", typeof(string));
dt.Rows.Add(1, @"in.docx");
dt.Rows.Add(1, @"in2.docx");
DataTable dt2 = new DataTable("BU_3");
dt2.Columns.Add("Tracking_ID", typeof(int));
dt2.Columns.Add("List:attach.Data", typeof(string));
dt2.Rows.Add(1, @"in3.docx");
dt2.Rows.Add(1, @"in4.docx");
DataSet ds = new DataSet();
ds.Tables.Add(dt);
ds.Tables.Add(dt2);
HandleMergeFieldInsertDoc callback = new HandleMergeFieldInsertDoc();
doc.MailMerge.FieldMergingCallback = callback;
doc.MailMerge.ExecuteWithRegions(ds);
foreach (String bookmark in callback.docs.Keys)
{
    builder.MoveToBookmark(bookmark);
    builder.InsertBreak(BreakType.SectionBreakNewPage);
    InsertDocument(builder.CurrentParagraph, new Document((Stream)callback.docs[bookmark]));
}
doc.Range.Bookmarks.Clear();
doc.Save(MyDir + "out.docx");

Hope this helps you. Please let us know if you have any more queries.