Back to Devexpress

Merge Word Documents

officefileapi-119802-word-processing-document-api-merge-and-split-documents-merge-documents.md

latest25.7 KB
Original Source

Merge Word Documents

  • Apr 15, 2025
  • 11 minutes to read

This topic describes how to use the Word Processing Document API to merge documents and keep document formatting.

Merge Documents into a Word File

Use one of the following methods to insert the content of one document into another:

SubDocument.AppendDocumentContentAllows you to append content from the specified file, range, or stream.SubDocument.InsertDocumentContentAllows you to insert content from a file, range, or stream in the specified document position.

Call the RichEditDocumentServer.SaveDocument method to save the resulting document.

csharp
using (var wordProcessor = new RichEditDocumentServer()) {

    // Load a document
    wordProcessor.LoadDocument("document1.docx");

    // Append another document's content
    wordProcessor.Document.AppendDocumentContent("document2.docx");

    // Save the result
    wordProcessor.SaveDocument("result.docx", DocumentFormat.Docx);
}
vb
Using wordProcessor = New RichEditDocumentServer()

    ' Load a document
    wordProcessor.LoadDocument("document1.docx")

    ' Append another document's content
    wordProcessor.Document.AppendDocumentContent("document2.docx")

    ' Save the result
    wordProcessor.SaveDocument("result.docx", DocumentFormat.Docx)
End Using

Merge Documents into a PDF File

Call the RichEditDocumentServer.ExportToPdf method to export the result to a PDF file. You can also use the PDF Document API to export all documents to separate PDF files and merge them. Refer to the following topic for more information:

Read Tutorial: Merge Documents

Keep Document Formatting

Direct Formatting and Styles

Pass one of the InsertOptions enumeration members to the AppendDocumentContent or InsertDocumentContent method as an insertOptions parameter to specify how the formatting is applied to the inserted content.

The table below shows how formatting is applied to the pasted document elements depending on the InsertOptions property value:

|

Enumeration Value

|

Direct Formatting

|

Styles (character, paragraph, table)

|

Non-textual objects (tables, floating objects, comments, etc.)

|

Page Properties and Tabs

| | --- | --- | --- | --- | --- | |

KeepSourceFormatting

|

The inserted text retains its formatting.

|

New styles (styles that don’t exist in the target document) are copied. If character or paragraph style conflicts occur, all inserted style properties are applied as direct formatting, but the style itself is not transferred. Destination document styles with default settings are replaced with the inserted styles. Conflicting table and numbering list styles are copied to the document — the new style name is a combination of conflicting style names (for example, Normal1 and Normal1 are combined into Normal11).

|

Non-textual elements are retained.

|

All page properties are retained.

| |

KeepTextOnly

|

The inserted text loses its formatting and takes on direct formatting applied to the target text range. Numbering lists are converted to simple text.

|

All inserted styles are not retained.

|

Floating objects, shapes and comments are discarded. Tables are converted into a series of paragraphs.

|

All page properties, including tabs, are lost.

| |

MatchDestinationFormatting

|

The inserted text retains its formatting.

|

All inserted styles are not retained.

|

Non-textual elements are retained.

|

All page properties are retained.

| |

MergeFormatting

|

The inserted text retains the following character properties:

Other character properties and all paragraph properties are ignored.

|

Inserted style’s character properties are applied as direct, but the style itself is not copied to the target document. Style paragraph properties are ignored. Table styles are not retained. Numbering list styles are transferred to the target document.

|

Non-textual elements are retained.

|

Page properties are retained. Tabs are ignored.

| |

UseDestinationStyles

|

The inserted text keeps its format options.

|

New styles (styles that don’t exist in the target document) are copied. If style conflicts occur, the inserted content’s styles are ignored. The destination document’s styles with default settings are replaced with the inserted styles.

|

Non-textual elements are retained.

|

All page properties are retained.

|

Note

When you merge documents with themes, the result may lose theme-related formatting (color, paragraph spacing).

Multiple Sections with Different Page Layout Settings

If you merge documents with different page layout settings or with multiple sections, insert document content into a new section. Remember to copy all required section information to the target section.

The following code sample merges multiple documents with different section parameters. The SectionsMerger class is used to copy section parameters, and append each document to a new page.

csharp
using DevExpress.XtraRichEdit;
using DevExpress.XtraRichEdit.API.Native;
using System.Diagnostics;

using (var wordProcessor = new RichEditDocumentServer())
{
    // Define a list of documents to merge
    List<string> filenames = new List<string>() {
                 @"Documents\FirstLook.docx",
                 @"Documents\HeadersFooters.docx",
                 @"Documents\HansInLuck.docx"
            };

    // Merge documents
    Document mergedDoc = MergeDocuments(filenames);
    // Append the resulting content to the word processor
    wordProcessor.Document.AppendDocumentContent(mergedDoc.Range);

    // Save the result
    wordProcessor.SaveDocument("result.docx", DocumentFormat.Docx);

    Process.Start(new ProcessStartInfo("result.docx") { UseShellExecute = true });
}

static Document MergeDocuments(List<string> filenames)
{
    // Create two word processor instances
    RichEditDocumentServer targetProcessor = new RichEditDocumentServer();
    Document targetDoc = targetProcessor.Document;

    RichEditDocumentServer sourceProcessor = new RichEditDocumentServer();
    Document sourceDoc = sourceProcessor.Document;

    // Load each document to the source processor
    for (int i = 0; i < filenames.Count; i++)
    {
        sourceProcessor.LoadDocument(filenames[i]);
        foreach (Section section in sourceDoc.Sections)
        {
            // Append each document section one-by-one
            SectionsMerger.AppendSection(targetDoc, sourceDoc, section);
        }
    }
    return targetProcessor.Document;
}

public class SectionsMerger
{
    public static void AppendSection(Document target, Document source, Section sourceSection)
    {
        Section section;
        if (target.IsEmpty)
            section = target.Sections[0];
        else
            // Append a section break
            // to insert each merged section to the new page
            section = target.AppendSection();
        section.StartType = SectionStartType.NextPage;

        target.Unit = source.Unit;

        // Copy a section's page layout parameters
        ApplySectionSettings(sourceSection, section);

        // Append section content
        DocumentRange range = target.AppendDocumentContent(sourceSection.Range, InsertOptions.KeepSourceFormatting);

        // Align shape z-order
        ReadOnlyShapeCollection targetShapes = target.Shapes.Get(range);
        foreach (Shape shape in targetShapes)
            shape.ZOrder = 0;
    }

    // This method copies page layout parameters
    static void ApplySectionSettings(Section sourceSection, Section targetSection)
    {
        SectionPage pageSettings = targetSection.Page;
        pageSettings.PaperKind = sourceSection.Page.PaperKind;
        if (pageSettings.PaperKind == DevExpress.Drawing.Printing.DXPaperKind.Custom)
        {
            pageSettings.Width = sourceSection.Page.Width;
            pageSettings.Height = sourceSection.Page.Height;
        }

        pageSettings.Landscape = sourceSection.Page.Landscape; 

        SectionMargins margins = targetSection.Margins;
        margins.Left = sourceSection.Margins.Left;
        margins.Right = sourceSection.Margins.Right;
        margins.Top = sourceSection.Margins.Top;
        margins.Bottom = sourceSection.Margins.Bottom;

        margins.HeaderOffset = sourceSection.Margins.HeaderOffset;
        margins.FooterOffset = sourceSection.Margins.FooterOffset;

        targetSection.DifferentFirstPage = sourceSection.DifferentFirstPage;

        targetSection.StartType = sourceSection.StartType;

        SectionColumnCollection columns = sourceSection.Columns.GetColumns();
        targetSection.Columns.SetColumns(columns);
    }
}
vb
Imports DevExpress.XtraRichEdit
Imports DevExpress.XtraRichEdit.API.Native
Imports System.Diagnostics

Using wordProcessor = New RichEditDocumentServer()
    Dim filenames As New List(Of String)() From {"Documents\FirstLook.docx", "Documents\HeadersFooters.docx", "Documents\HansInLuck.docx"}

    Dim mergedDoc As Document = MergeDocuments(filenames)
    wordProcessor.Document.AppendDocumentContent(mergedDoc.Range)
    wordProcessor.SaveDocument("result.docx", DocumentFormat.Docx)
    Process.Start(New ProcessStartInfo("result.docx") With {.UseShellExecute = True})
End Using

Shared Function MergeDocuments(ByVal filenames As List(Of String)) As Document
    ' Create two word processor instances
    Dim targetProcessor As New RichEditDocumentServer()
    Dim targetDoc As Document = targetProcessor.Document

    Dim sourceProcessor As New RichEditDocumentServer()
    Dim sourceDoc As Document = sourceProcessor.Document

    ' Load each document to the source processor
    For i As Integer = 0 To filenames.Count - 1
        sourceProcessor.LoadDocument(filenames(i))
        For Each section As Section In sourceDoc.Sections
            ' Append each document section one-by-one
            SectionsMerger.AppendSection(targetDoc, sourceDoc, section)
        Next section
    Next i
    Return targetProcessor.Document
End Function

Public Class SectionsMerger
    Public Shared Sub AppendSection(ByVal target As Document, ByVal source As Document, ByVal sourceSection As Section)
        Dim section As Section
        If target.IsEmpty Then
            section = target.Sections(0)
        Else
            ' Append a section break
            ' to insert each merged section to the new page
            section = target.AppendSection()
        End If
        section.StartType = SectionStartType.NextPage

        target.Unit = source.Unit

        ' Copy a section's page layout parameters
        ApplySectionSettings(sourceSection, section)

        ' Append section content
        Dim range As DocumentRange = target.AppendDocumentContent(sourceSection.Range, InsertOptions.KeepSourceFormatting)

        ' Align shape z-order
        Dim targetShapes As ReadOnlyShapeCollection = target.Shapes.Get(range)
        For Each shape As Shape In targetShapes
            shape.ZOrder = 0
        Next shape
    End Sub

    ' This method copies page layout parameters
    Private Shared Sub ApplySectionSettings(ByVal sourceSection As Section, ByVal targetSection As Section)
        Dim pageSettings As SectionPage = targetSection.Page
        pageSettings.PaperKind = sourceSection.Page.PaperKind
        If pageSettings.PaperKind = System.Drawing.Printing.PaperKind.Custom Then
            pageSettings.Width = sourceSection.Page.Width
            pageSettings.Height = sourceSection.Page.Height
        End If

        pageSettings.Landscape = sourceSection.Page.Landscape

        Dim margins As SectionMargins = targetSection.Margins
        margins.Left = sourceSection.Margins.Left
        margins.Right = sourceSection.Margins.Right
        margins.Top = sourceSection.Margins.Top
        margins.Bottom = sourceSection.Margins.Bottom

        margins.HeaderOffset = sourceSection.Margins.HeaderOffset
        margins.FooterOffset = sourceSection.Margins.FooterOffset

        targetSection.DifferentFirstPage = sourceSection.DifferentFirstPage

        targetSection.StartType = sourceSection.StartType

        Dim columns As SectionColumnCollection = sourceSection.Columns.GetColumns()
        targetSection.Columns.SetColumns(columns)
    End Sub
End Class

Headers and Footers

Headers and footers belong to document sections. To merge documents with different headers or footers, insert document content into a new section. Remember to copy all required header and footer information to the target section.

View Example: Word Processing API - Merge Documents with Headers and Footers

The following code sample merges documents with different headers and footers. The SectionsMerger class is used to copy headers and footers of each document section.

csharp
using DevExpress.XtraRichEdit;
using DevExpress.XtraRichEdit.API.Native;
using System.Diagnostics;

using (var wordProcessor = new RichEditDocumentServer())
{
    // Define a list of documents to merge
    List<string> filenames = new List<string>() {
                 @"Documents\FirstLook.docx",
                 @"Documents\HeadersFooters.docx",
                 @"Documents\HansInLuck.docx"
            };

    // Merge documents
    Document mergedDoc = MergeDocuments(filenames);
    // Append the resulting content to the word processor
    wordProcessor.Document.AppendDocumentContent(mergedDoc.Range);

    // Save the result
    wordProcessor.SaveDocument("result.docx", DocumentFormat.Docx);

    Process.Start(new ProcessStartInfo("result.docx") { UseShellExecute = true });
}

static Document MergeDocuments(List<string> filenames)
{
    // Create two word processor instances
    RichEditDocumentServer targetProcessor = new RichEditDocumentServer();
    Document targetDoc = targetProcessor.Document;

    RichEditDocumentServer sourceProcessor = new RichEditDocumentServer();
    Document sourceDoc = sourceProcessor.Document;

    for (int i = 0; i < filenames.Count; i++)
    {
        sourceProcessor.LoadDocument(filenames[i]);

        // Unlink all headers and footers
        Section targetSection = targetDoc.Sections[targetDoc.Sections.Count - 1];
        targetSection.UnlinkHeaderFromPrevious();
        targetSection.UnlinkFooterFromPrevious();

        targetSection.UnlinkHeaderFromPrevious(HeaderFooterType.First);
        targetSection.UnlinkFooterFromPrevious(HeaderFooterType.First);

        targetSection.UnlinkHeaderFromPrevious(HeaderFooterType.Even);
        targetSection.UnlinkFooterFromPrevious(HeaderFooterType.Even);

        // Append each document section one-by-one
        SectionsMerger.Append(sourceDoc, targetDoc);

        if (i == filenames.Count - 1)
            return targetDoc;

        // Append a new section that starts
        // on the new page
        targetDoc.AppendSection();
        targetDoc.Sections.Last().StartType = SectionStartType.NextPage;
    }

    return targetDoc;
}

public class SectionsMerger
{
    public static void Append(Document source, Document target)
    {
        int lastSectionIndexBeforeAppending = target.Sections.Count - 1;
        int sourceSectionCount = source.Sections.Count;

        // Append document body
        target.AppendDocumentContent(source.Range);
        target.DifferentOddAndEvenPages = source.DifferentOddAndEvenPages;

        for (int i = 0; i < sourceSectionCount; i++)
        {
            Section sourceSection = source.Sections[i];
            Section targetSection = target.Sections[lastSectionIndexBeforeAppending + i];

            // Copy headers and footers
            AppendHeader(sourceSection, targetSection, HeaderFooterType.Odd);
            AppendFooter(sourceSection, targetSection, HeaderFooterType.Odd);

            AppendHeader(sourceSection, targetSection, HeaderFooterType.Even);
            AppendFooter(sourceSection, targetSection, HeaderFooterType.Even);

            AppendHeader(sourceSection, targetSection, HeaderFooterType.First);
            AppendFooter(sourceSection, targetSection, HeaderFooterType.First);
        }
    }

    private static void AppendHeader(Section sourceSection, Section targetSection, HeaderFooterType headerFooterType)
    {
        // Clear headers in the target document
        if (!sourceSection.HasHeader(headerFooterType))
        {
            if (targetSection.HasHeader(headerFooterType))
            {
                SubDocument targetHeader = targetSection.BeginUpdateHeader(headerFooterType);
                targetHeader.Delete(targetHeader.Range);
                targetSection.EndUpdateHeader(targetHeader);
            }
            return;
        }

        // Insert header content
        SubDocument source = sourceSection.BeginUpdateHeader(headerFooterType);
        SubDocument target = targetSection.BeginUpdateHeader(headerFooterType);
        target.Delete(target.Range);
        target.InsertDocumentContent(target.Range.Start, source.Range, InsertOptions.KeepSourceFormatting);

        // Delete empty paragraphs
        DocumentRange emptyParagraph = target.CreateRange(target.Range.End.ToInt() - 1, 1);
        target.Delete(emptyParagraph);

        sourceSection.EndUpdateHeader(source);
        targetSection.EndUpdateHeader(target);
    }

    private static void AppendFooter(Section sourceSection, Section targetSection, HeaderFooterType headerFooterType)
    {
        // Clear footers in the target document
        if (!sourceSection.HasFooter(headerFooterType))
        {
            if (targetSection.HasFooter(headerFooterType))
            {
                SubDocument targetFooter = targetSection.BeginUpdateFooter(headerFooterType);
                targetFooter.Delete(targetFooter.Range);
                targetSection.EndUpdateFooter(targetFooter);
            }
            return;
        }

        // Insert footer content
        SubDocument source = sourceSection.BeginUpdateFooter(headerFooterType);
        SubDocument target = targetSection.BeginUpdateFooter(headerFooterType);
        target.Delete(target.Range);
        target.InsertDocumentContent(target.Range.Start, source.Range, InsertOptions.KeepSourceFormatting);

        // Delete empty paragraphs
        DocumentRange emptyParagraph = target.CreateRange(target.Range.End.ToInt() - 1, 1);
        target.Delete(emptyParagraph);

        sourceSection.EndUpdateFooter(source);
        targetSection.EndUpdateFooter(target);
    }
}
vb
Imports DevExpress.XtraRichEdit
Imports DevExpress.XtraRichEdit.API.Native
Imports System.Diagnostics

Using wordProcessor = New RichEditDocumentServer()
    Dim filenames As New List(Of String)() From {"Documents\FirstLook.docx", "Documents\HeadersFooters.docx", "Documents\HansInLuck.docx"}

    Dim mergedDoc As Document = MergeDocuments(filenames)
    wordProcessor.Document.AppendDocumentContent(mergedDoc.Range)
    wordProcessor.SaveDocument("result.docx", DocumentFormat.Docx)
    Process.Start(New ProcessStartInfo("result.docx") With {.UseShellExecute = True})
End Using

Shared Function MergeDocuments(ByVal filenames As List(Of String)) As Document
    ' Create two word processor instances
    Dim targetProcessor As New RichEditDocumentServer()
    Dim targetDoc As Document = targetProcessor.Document

    Dim sourceProcessor As New RichEditDocumentServer()
    Dim sourceDoc As Document = sourceProcessor.Document

    For i As Integer = 0 To filenames.Count - 1
        sourceProcessor.LoadDocument(filenames(i))

        ' Unlink all headers and footers
        Dim targetSection As Section = targetDoc.Sections(targetDoc.Sections.Count - 1)
        targetSection.UnlinkHeaderFromPrevious()
        targetSection.UnlinkFooterFromPrevious()

        targetSection.UnlinkHeaderFromPrevious(HeaderFooterType.First)
        targetSection.UnlinkFooterFromPrevious(HeaderFooterType.First)

        targetSection.UnlinkHeaderFromPrevious(HeaderFooterType.Even)
        targetSection.UnlinkFooterFromPrevious(HeaderFooterType.Even)

        ' Append each document section one-by-one
        SectionsMerger.Append(sourceDoc, targetDoc)

        If i = filenames.Count - 1 Then
            Return targetDoc
        End If

        ' Append a new section that starts
        ' on the new page
        targetDoc.AppendSection()
        targetDoc.Sections.Last().StartType = SectionStartType.NextPage
    Next i

    Return targetDoc
End Function

Public Class SectionsMerger
    Public Shared Sub Append(ByVal source As Document, ByVal target As Document)
        Dim lastSectionIndexBeforeAppending As Integer = target.Sections.Count - 1
        Dim sourceSectionCount As Integer = source.Sections.Count

        ' Append document body
        target.AppendDocumentContent(source.Range)
        target.DifferentOddAndEvenPages = source.DifferentOddAndEvenPages

        For i As Integer = 0 To sourceSectionCount - 1
            Dim sourceSection As Section = source.Sections(i)
            Dim targetSection As Section = target.Sections(lastSectionIndexBeforeAppending + i)

            ' Copy headers and footers
            AppendHeader(sourceSection, targetSection, HeaderFooterType.Odd)
            AppendFooter(sourceSection, targetSection, HeaderFooterType.Odd)

            AppendHeader(sourceSection, targetSection, HeaderFooterType.Even)
            AppendFooter(sourceSection, targetSection, HeaderFooterType.Even)

            AppendHeader(sourceSection, targetSection, HeaderFooterType.First)
            AppendFooter(sourceSection, targetSection, HeaderFooterType.First)
        Next i
    End Sub

    Private Shared Sub AppendHeader(ByVal sourceSection As Section, ByVal targetSection As Section, ByVal headerFooterType As HeaderFooterType)
        ' Clear headers in the target document
        If Not sourceSection.HasHeader(headerFooterType) Then
            If targetSection.HasHeader(headerFooterType) Then
                Dim targetHeader As SubDocument = targetSection.BeginUpdateHeader(headerFooterType)
                targetHeader.Delete(targetHeader.Range)
                targetSection.EndUpdateHeader(targetHeader)
            End If
            Return
        End If

        ' Insert header content
        Dim source As SubDocument = sourceSection.BeginUpdateHeader(headerFooterType)
        Dim target As SubDocument = targetSection.BeginUpdateHeader(headerFooterType)
        target.Delete(target.Range)
        target.InsertDocumentContent(target.Range.Start, source.Range, InsertOptions.KeepSourceFormatting)

        ' Delete empty paragraphs
        Dim emptyParagraph As DocumentRange = target.CreateRange(target.Range.End.ToInt() - 1, 1)
        target.Delete(emptyParagraph)

        sourceSection.EndUpdateHeader(source)
        targetSection.EndUpdateHeader(target)
    End Sub

    Private Shared Sub AppendFooter(ByVal sourceSection As Section, ByVal targetSection As Section, ByVal headerFooterType As HeaderFooterType)
        ' Clear footers in the target document
        If Not sourceSection.HasFooter(headerFooterType) Then
            If targetSection.HasFooter(headerFooterType) Then
                Dim targetFooter As SubDocument = targetSection.BeginUpdateFooter(headerFooterType)
                targetFooter.Delete(targetFooter.Range)
                targetSection.EndUpdateFooter(targetFooter)
            End If
            Return
        End If

        ' Insert footer content
        Dim source As SubDocument = sourceSection.BeginUpdateFooter(headerFooterType)
        Dim target As SubDocument = targetSection.BeginUpdateFooter(headerFooterType)
        target.Delete(target.Range)
        target.InsertDocumentContent(target.Range.Start, source.Range, InsertOptions.KeepSourceFormatting)

        ' Delete empty paragraphs
        Dim emptyParagraph As DocumentRange = target.CreateRange(target.Range.End.ToInt() - 1, 1)
        target.Delete(emptyParagraph)

        sourceSection.EndUpdateFooter(source)
        targetSection.EndUpdateFooter(target)
    End Sub
End Class