1

I have searched a lot for the solution but can't find any.

I have a .docx file inside my MVC project folder which I want to open to overwrite some text but I'm unable to do so.

Inside my project folder, I have a Template folder and in this folder a genrated.docx file that I want to open. Here is my code:

using (WordprocessingDocument doc = WordprocessingDocument.Open
    (@"~/Template/genrated.docx",true))
{
    var body = doc.MainDocumentPart.Document.Body;
    var paras = body.Elements<Paragraph>();

    foreach (var para in paras)
    {
        foreach (var run in para.Elements<Run>())
        {
            foreach (var text in run.Elements<Text>())
            {
                if (text.Text.Contains("to-replace"))
                {
                    text.Text = text.Text.Replace("to-replace", "replace-with");
                    run.AppendChild(new Break());
                }
            }
        }
    }
}

Please help me with this...

VillageTech
  • 1,968
  • 8
  • 18
Umar Gulzar
  • 17
  • 1
  • 10
  • Please have a look at my explanation below to understand why your code does not work in some cases at least. Then, please have a look at my answer on [finding and replacing multiple placeholders in a document template](https://stackoverflow.com/questions/59303646/open-xml-find-and-replace-multiple-placeholders-in-document-template/59328568#59328568). – Thomas Barnekow Dec 21 '19 at 14:09
  • Did those answers help solve your problem or is there anything else you need? – Thomas Barnekow Dec 29 '19 at 13:45

1 Answers1

0

Your simplistic approach to replacing text only works in simple cases. Unfortunately, as soon as you use Microsoft Word to edit your template, your text "to-replace" might get split in multiple runs. This then means that you can't find your text "to-replace" if you only look for it in a single Text instance.

The following unit test demonstrates that by creating a document with two paragraphs, one having a single Text instance with your text "to-replace" and another one in which that same text is split into two Run and Text instances.

using System.Collections.Generic;
using System.IO;
using System.Linq;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
using Xunit;

namespace CodeSnippets.Tests.OpenXml.Wordprocessing
{
    public class SimplisticTextReplacementTests
    {
        private const string ToReplace = "to-replace";
        private const string ReplaceWith = "replace-with";

        private static MemoryStream CreateWordprocessingDocument()
        {
            var stream = new MemoryStream();
            const WordprocessingDocumentType type = WordprocessingDocumentType.Document;

            using WordprocessingDocument wordDocument = WordprocessingDocument.Create(stream, type);
            MainDocumentPart mainDocumentPart = wordDocument.AddMainDocumentPart();
            mainDocumentPart.Document =
                new Document(
                    new Body(
                        new Paragraph(
                            new Run(
                                new Text(ToReplace))),
                        new Paragraph(
                            new Run(
                                new Text("to-")),
                            new Run(
                                new Text("replace")))));

            return stream;
        }

        private static void ReplaceText(MemoryStream stream)
        {
            using WordprocessingDocument doc = WordprocessingDocument.Open(stream, true);

            Body body = doc.MainDocumentPart.Document.Body;
            IEnumerable<Paragraph> paras = body.Elements<Paragraph>();

            foreach (Paragraph para in paras)
            {
                foreach (Run run in para.Elements<Run>())
                {
                    foreach (Text text in run.Elements<Text>())
                    {
                        if (text.Text.Contains(ToReplace))
                        {
                            text.Text = text.Text.Replace(ToReplace, ReplaceWith);
                            run.AppendChild(new Break());
                        }
                    }
                }
            }
        }

        [Fact]
        public void SimplisticTextReplacementOnlyWorksInSimpleCases()
        {
            // Arrange.
            using MemoryStream stream = CreateWordprocessingDocument();
            using (WordprocessingDocument wordDocument = WordprocessingDocument.Open(stream, false))
            {
                Document document = wordDocument.MainDocumentPart.Document;

                Paragraph firstParagraph = document.Descendants<Paragraph>().First();
                Assert.Equal(ToReplace, firstParagraph.InnerText);
                Assert.Contains(firstParagraph.Descendants<Text>(), t => t.Text == ToReplace);

                Paragraph lastParagraph = document.Descendants<Paragraph>().Last();
                Assert.Equal(ToReplace, lastParagraph.InnerText);
                Assert.DoesNotContain(lastParagraph.Descendants<Text>(), t => t.Text == ToReplace);
            }

            // Act.
            ReplaceText(stream);

            // Assert.
            using (WordprocessingDocument wordDocument = WordprocessingDocument.Open(stream, false))
            {
                Document document = wordDocument.MainDocumentPart.Document;

                Paragraph firstParagraph = document.Descendants<Paragraph>().First();
                Assert.Equal(ReplaceWith, firstParagraph.InnerText);
                Assert.Contains(firstParagraph.Descendants<Text>(), t => t.Text == ReplaceWith);

                Paragraph lastParagraph = document.Descendants<Paragraph>().Last();
                Assert.NotEqual(ReplaceWith, lastParagraph.InnerText);
                Assert.DoesNotContain(lastParagraph.Descendants<Text>(), t => t.Text == ReplaceWith);
            }
        }
    }
}
Thomas Barnekow
  • 2,059
  • 1
  • 12
  • 21