Open XML - merging (concatenating) Word documents

Viewed 138

I have a folder containing multiple .docx documents, and I want to create a new document that merges them all together in alphabetic order.

The code i am using is merging them using AltChunks - but the order is all wrong.

Instead of:

"a, b, c, d, e" 

I am getting

"a, e, d, c, b."  

It seems clear that all of the Inserts are happening at the same location, and I can't get the insertion correct (I am always inserting after the first document - whereas I want to insert after the last document)

My code follows:

    private void GenerateMergedDoc(string directoryPathToUse, string outputFolder)
    {
        string altChunkIdBase = "acID";
        int altChunkCounter = 1;
        string altChunkId = altChunkIdBase + altChunkCounter.ToString();

        MainDocumentPart wdDocTargetMainPart = null;
        DocumentFormat.OpenXml.Wordprocessing.Document docTarget = null;
        AlternativeFormatImportPartType afType;
        AlternativeFormatImportPart chunk = null;
        AltChunk ac = null;

    System.IO.DirectoryInfo di = new System.IO.DirectoryInfo(directoryPathToUse);
        IEnumerable<System.IO.FileInfo> docFiles = di.EnumerateFiles();
        List<string> filenames = new List<string>();
        foreach (var fileInfo in docFiles)
        {
            filenames.Add(fileInfo.FullName);
        }
        filenames.Sort();
       
        using (WordprocessingDocument wdPkgTarget = WordprocessingDocument.CreateFromTemplate(filenames[0], true))
        {
            wdDocTargetMainPart = wdPkgTarget.MainDocumentPart;
            if (wdDocTargetMainPart == null)
            {
                wdDocTargetMainPart = wdPkgTarget.AddMainDocumentPart();
                DocumentFormat.OpenXml.Wordprocessing.Document wdDoc = new DocumentFormat.OpenXml.Wordprocessing.Document(
                    new Body(
                        new DocumentFormat.OpenXml.Wordprocessing.Paragraph(
                            new Run(new Text() { Text = "First Para" })),
                            new DocumentFormat.OpenXml.Wordprocessing.Paragraph(new Run(new Text() { Text = "Second para" })),
                            new SectionProperties(
                                new SectionType() { Val = SectionMarkValues.NextPage },
                                new PageSize() { Code = 9 },
                                new PageMargin() { Gutter = 0, Bottom = 1134, Top = 1134, Left = 1318, Right = 1318, Footer = 709, Header = 709 },
                                new Columns() { Space = "708" },
                                new TitlePage())));
                wdDocTargetMainPart.Document = wdDoc;
            }
            docTarget = wdDocTargetMainPart.Document;
            SectionProperties secPropLast = docTarget.Body.Descendants<SectionProperties>().Last();
            SectionProperties secPropNew = (SectionProperties)secPropLast.CloneNode(true);
            //A section break must be in a ParagraphProperty
            DocumentFormat.OpenXml.Wordprocessing.Paragraph lastParaTarget = (DocumentFormat.OpenXml.Wordprocessing.Paragraph)docTarget.Body.Descendants<DocumentFormat.OpenXml.Wordprocessing.Paragraph>().Last();
            ParagraphProperties paraPropTarget = lastParaTarget.ParagraphProperties;
            if (paraPropTarget == null)
            {
                paraPropTarget = new ParagraphProperties();
            }
            paraPropTarget.Append(secPropNew);
            Run paraRun = lastParaTarget.Descendants<Run>().FirstOrDefault();
            //lastParaTarget.InsertBefore(paraPropTarget, paraRun);
            lastParaTarget.InsertAt(paraPropTarget, 0);

            //Process the individual files in the source folder.
            //Note that this process will permanently change the files by adding a section break.

            for (int i = 1; i < filenames.Count; i++)
            {                    
                using (WordprocessingDocument pkgSourceDoc = WordprocessingDocument.Open(filenames[i], true))
                {
                    IEnumerable<HeaderPart> partsHeader = pkgSourceDoc.MainDocumentPart.GetPartsOfType<HeaderPart>();
                    IEnumerable<FooterPart> partsFooter = pkgSourceDoc.MainDocumentPart.GetPartsOfType<FooterPart>();
                    //If the source document has headers or footers we want to retain them.
                    //This requires inserting a section break at the end of the document.
                    if (partsHeader.Count() > 0 || partsFooter.Count() > 0)
                    {
                        Body sourceBody = pkgSourceDoc.MainDocumentPart.Document.Body;
                        SectionProperties docSectionBreak = sourceBody.Descendants<SectionProperties>().Last();

                        SectionProperties copySectionBreak = (SectionProperties)docSectionBreak.CloneNode(true);
                        DocumentFormat.OpenXml.Wordprocessing.Paragraph lastpara = sourceBody.Descendants<DocumentFormat.OpenXml.Wordprocessing.Paragraph>().Last();
                        ParagraphProperties paraProps = lastpara.ParagraphProperties;
                        if (paraProps == null)
                        {
                            paraProps = new ParagraphProperties();
                            lastpara.Append(paraProps);
                        }
                        paraProps.Append(copySectionBreak);
                    }
                    pkgSourceDoc.MainDocumentPart.Document.Save();
                }
                //Insert the source file into the target file using AltChunk
                afType = AlternativeFormatImportPartType.WordprocessingML;
                chunk = wdDocTargetMainPart.AddAlternativeFormatImportPart(afType, altChunkId);
                System.IO.FileStream fsSourceDocument = new System.IO.FileStream(filenames[i], System.IO.FileMode.Open);
                chunk.FeedData(fsSourceDocument);
                //Create the chunk
                ac = new AltChunk();
                //Link it to the part
                ac.Id = altChunkId;
                docTarget.Body.InsertAfter(ac, docTarget.Body.Descendants<DocumentFormat.OpenXml.Wordprocessing.Paragraph>().Last());
                docTarget.Save();
                altChunkCounter++;
                altChunkId = altChunkIdBase + altChunkCounter;
                chunk = null;
                ac = null;
            }
            string volText = String.Empty;
            if (volumeToCreate.VolumeNumber > 1)
            {
                volText = "_Vol_" + volumeToCreate.VolumeNumber;
            }

            string categoryText = String.Empty;
            if (volumeToCreate.Prefix.Contains("SDS"))
            {
                categoryText = "-" + volumeToCreate.Category;
            }

            newMergeFileName = outputFolder + volumeToCreate.Prefix + "-Merged-V-V" + categoryText + volText + ".docx";


            wdPkgTarget.SaveAs(newMergeFileName);
        }
    }
1 Answers

If your assumption that you are appending after the first document is correct, you could just change the insertion order

 for (int i = filenames.Count-1; i > 0; i--)
 {
       Foo();
 }   

Then it would add e, d, c and then b to a, causing an end result of a,b,c,d,e. It might not be the solution you were after but it would solve your problem.

Related