Skip to main content

Merge Word Documents

  • 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.AppendDocumentContent
Allows you to append content from the specified file, range, or stream.
SubDocument.InsertDocumentContent
Allows 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.

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.OpenXml);
}

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 parameter:

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 code sample below shows how to merge multiple documents with different section parameters. The SectionsMerger class is used to copy section parameters, and append each document to a new page.

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.OpenXml);

    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);
    }
}

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.

The code sample below shows how to merge documents with different headers and footers. The SectionsMerger class is used to copy headers and footers of each document section.

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.OpenXml);

    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);
    }
}