none
Merging multiple Word documents into one Word document (template) RRS feed

  • Question

  • Hi,

    I have multiple Word documents with various content. I also have a Word document that serves as a template for the final result. This document has a few Content Controls (rich text) where I want to place the content of the other documents.

    I'm using Open XML 2.0.

    Can anyone provide me with some good examples on how to accomplish this? I have googled quite a bit but haven't found any good examples (obviously).

    Thanks!

    Tuesday, April 2, 2013 1:29 PM

Answers

  • Have you checked whether this is doing what you expect?

                   mem.Write(byteArray, 0, (int)byteArray.Length);
                    mem
    .Seek(0, SeekOrigin.Begin);
                    chunk
    .FeedData(mem);

    Usually, I open the document to be inserted as an OpenXML document and use GetStream() on it to feed the data (see example on my blog).

    You might also look at the package created by the code result and see 1) whether it's inserted an altChunk part at all (would be in the Word folder) and, if it has 2) whether a reference has been inserted in document.xml and 3) where that is in relationship to other XML elements in the document.xml (although I'd expect an error messasge if it were inserted incorrectly).


    Cindy Meister, VSTO/Word MVP, my blog

    Monday, April 8, 2013 9:04 AM
    Moderator

All replies

  • Hi Relapse

    What kind of content would these Word documents contain, besides formatted text?

    The simplest solution would be to use the altChunk method - sort of like using InsertFile in the APIs, but there are certain kinds of things it doesn't handle well.

    Other than that, it becomes a matter of copying in the content of the document.xml, as well as any other information that's referenced (styles, graphics, headers, footers...) You might want to look at this article to get an idea of what's involved:

    http://blogs.msmvps.com/wordmeister/2012/11/15/merge-word-docs-openxmlsdk/


    Cindy Meister, VSTO/Word MVP, my blog

    • Marked as answer by HelgeNorvang Wednesday, April 3, 2013 7:18 AM
    • Unmarked as answer by HelgeNorvang Wednesday, April 3, 2013 11:57 AM
    Tuesday, April 2, 2013 3:31 PM
    Moderator
  • Hi,

    Thanks, not 100% certain, but I believe it's just plain formatted text.

    I'll read the article you provided.

    Wednesday, April 3, 2013 7:18 AM
  • Hi again,

    I ended up trying an example from this page, but it does not work. Nothing happens to my destination document.

    Here's my code:

    class Program
        {
            private static int id = 1;
    
            static void Main(string[] args)
            {
                using (var site = new SPSite("http://myserver"))
                using (var web = site.RootWeb)
                {
                    //var docLib = web.Lists["MergeDocuments"];
    
                    var sourceFolder = web.Folders["MergeDocuments"];
                    var destFolder = web.Folders["MergedDocuments"];
    
                    var template = sourceFolder.Files["template.docx"];
                    template.CopyTo(destFolder.Url + "/offer.docx", true);
    
                    SPFile destinationFile = destFolder.Files["offer.docx"];
                    SPFile sourceFile = sourceFolder.Files["maincontent.docx"];
                    
                    using (WordprocessingDocument myDoc = WordprocessingDocument.Open(destinationFile.OpenBinaryStream(), true)) 
                    { 
                        MainDocumentPart mainPart = myDoc.MainDocumentPart; 
                        //Find content controls that have the name of the source file as 
                        // an alias value. 
                        List<Word.SdtBlock> sdtList = mainPart.Document.Descendants<Word.SdtBlock>().Where(s => s.SdtProperties.GetFirstChild<Word.Tag>().Val.Value.Contains("Header")).ToList();
                            
                        if (sdtList.Count != 0)
                        {
                            foreach (Word.SdtBlock sdt in sdtList)
                            {
                                AddAltChunk(mainPart, sdt, sourceFile);
                            }
    
                            mainPart.Document.Save();
                        }
                    } 
                }
    
    
                Console.WriteLine("Done....");
                Console.ReadLine();
            }
    
            static void AddAltChunk(MainDocumentPart mainPart, Word.SdtElement sdt, SPFile filename)
            {
                string altChunkId = "AltChunkId" + id;
                id++;
                byte[] byteArray = filename.OpenBinary();
    
                AlternativeFormatImportPart chunk = mainPart.AddAlternativeFormatImportPart(
                AlternativeFormatImportPartType.WordprocessingML, altChunkId);
    
                using (MemoryStream mem = new MemoryStream())
                {
                    mem.Write(byteArray, 0, (int)byteArray.Length);
                    mem.Seek(0, SeekOrigin.Begin);
                    chunk.FeedData(mem);
                }
    
                Word.AltChunk altChunk = new Word.AltChunk();
                altChunk.Id = altChunkId;
    
                // Replace content control with altChunk information.  
                OpenXmlElement parent = sdt.Parent;
                parent.InsertAfter(altChunk, sdt);
                sdt.Remove();
            }

    This is what my destination document looks like in terms of content controls:

    

    Note that the code mainPart.Document.Save(); was not mentioned in the code example, but I found it in some other example. However, it doesn't work either way.

    If you're wondering, I have a document library where I keep the source files, including the template. I don't want to overwrite the template document, so that's why I copy it to a different library before merging (or at least try to merge :)).

    Can someone help me out with this?

    Thanks!

    Wednesday, April 3, 2013 12:14 PM
  • Have you checked whether this is doing what you expect?

                   mem.Write(byteArray, 0, (int)byteArray.Length);
                    mem
    .Seek(0, SeekOrigin.Begin);
                    chunk
    .FeedData(mem);

    Usually, I open the document to be inserted as an OpenXML document and use GetStream() on it to feed the data (see example on my blog).

    You might also look at the package created by the code result and see 1) whether it's inserted an altChunk part at all (would be in the Word folder) and, if it has 2) whether a reference has been inserted in document.xml and 3) where that is in relationship to other XML elements in the document.xml (although I'd expect an error messasge if it were inserted incorrectly).


    Cindy Meister, VSTO/Word MVP, my blog

    Monday, April 8, 2013 9:04 AM
    Moderator