Showing posts with label MS Word. Show all posts
Showing posts with label MS Word. Show all posts

Friday, March 11, 2016

Search and Replace in a MS Word document

I reviewed options for editing MS Word files in DOCX format and decided that the free and one that has the greatest chance of existing in 10 years is just using the Open XML Word Processing SDK (see my review for details).

For complex stuff maybe a different choice would make sense. However, my requirements are simple much like a mail merge:

  1. Taking an existing MS Word file (DOCX format) as input. (Use it as a template)
  2. Search the MS Word file for some placeholders / tags and replace with real data, but don't save any changes to the original file since it is my template.
  3. Be able to write changes to a new file or stream file to browser for download
As it turns out this can be done in very few lines of code and for FREE. Below is my solution.

// Sample command line application
static void Main(string[] args)
{
    string filename = "Test.docx";
    var oldNewValues = new Dictionary<string, string>();
    oldNewValues.Add("pear", "banana");
    oldNewValues.Add("love", "like");
    byte[] returnedBytes = SearchAndReplace(filename, oldNewValues);
    File.WriteAllBytes("Changed" + filename, returnedBytes);

}


// Does a search and replace in the content (body) of a MS Word DOCX file using only the DocumentFormat.OpenXml.Packaging namespace.
// Reference: http://justgeeks.blogspot.co.uk/2016/03/how-to-do-search-and-replace-in.html
public static byte[] SearchAndReplace(string filename, Dictionary<string, string> oldNewValues)
{
    // make a copy of the Word document and put it in memory.
    // The code below operates on this in memory copy, not the file itself.
    // When the OpenXml SDK Auto saves the changes (that is why we don't call save explicitly)
    // the in memory copy is updated, not the original file.
    byte[] byteArray = File.ReadAllBytes(filename);
    using (MemoryStream copyOfWordFile = new MemoryStream())
    {
        copyOfWordFile.Write(byteArray, 0, (int)byteArray.Length);

        // Open the Word document for editing
        using (WordprocessingDocument wordDoc = WordprocessingDocument.Open(copyOfWordFile, true))
        {

            // Get the Main Document Part. It is really just XML.
            // NOTE: There are other parts in the Word document that we are not changing
            string bodyAsXml = null;
            using (StreamReader sr = new StreamReader(wordDoc.MainDocumentPart.GetStream()))
            {
                bodyAsXml = sr.ReadToEnd();
            }

            foreach (var keyValue in oldNewValues)
            {
                string oldValueRegex = keyValue.Key;
                string newValue = keyValue.Value;

                // Do the search and replace. Here we are implementing the logic using REGEX replace.
                Regex regexText = new Regex(oldValueRegex);
                bodyAsXml = regexText.Replace(bodyAsXml, newValue);
            }

            // After making the changes to the string we need to write the updated XML string back to
            // the Word doc (remember it is in memory, not the original file itself)
            using (StreamWriter sw = new StreamWriter(wordDoc.MainDocumentPart.GetStream(FileMode.Create)))
            {
                sw.Write(bodyAsXml);
            }
        }

        // Convert the in memory stream to a byte array (binary data)
        // NOTE: These bytes can be written to a physical file or streamed back to the browser for download, etc.
        byte[] bytesOfEntireWordFile = copyOfWordFile.ToArray();

        return bytesOfEntireWordFile;
    }
}



After calling the SearchAndReplace method you have the bytes that make up the MS Word file. It is up to you what you want to do with it. You can save it a file or stream it to the browser when a user clicks a link to download a file.

To write the file to another file (leaving the original unchanged), use the following line:

File.WriteAllBytes(newFilename, returnedBytes);


To stream the bytes back to a browser via a Action method in a ASP.NET MVC controller, use the following:

return File(returnedBytes, "application/msword", "Filename to download as here.docx");


NOTE: Original code inspired by this MSDN example and additional a post or other page I can't remember (sorry).

Tuesday, December 1, 2015

Review of some products for working with MS Word documents using .NET / C#

I didn't want to use MS Automation because it is slow and not server friendly, so I am not covering that here. The solutions below do NOT require MS Word to be installed and they are server friendly.

Open XML Word Processing SDK
This is my first choice for completely free solutions because is relatively easy for basic things and it will likely always be supported by Microsoft. This is the Microsoft SDK (released as Open Source as of version 2.5) for creating and editing MS Word documents in DOCX format. It takes a bit to get used since it mirrors the internal DOCX format. The advantage is that pretty much everything you need to be done here. The disadvantage is that it may take more code than using other packages. The link above has sample code on how to do many common tasks. Here is a good place to start to familiarize yourself with the file formats, what resources are available, etc. To understand the DOCX file format, check this out. The DOCX format is much simpler than the XLSX format. I recommend giving it a try. The learning curve is pretty small for the DOCX file format and there is a lot of examples available. In the end it isn't so different than NPOI (see section below) for DOCX. Here is a list of how to do's.

It is unofficially distributed on NuGet as DocumentFormat.OpenXml, but it is unofficially distributed by someone other than Microsoft (even though it shows the author as Microsoft). If you want the official binaries they can be downloaded here. If you want the source code you can get it on GitHub. This video shows how to build the source code if you want to go that route. If you want to use 2.6 it has been released on NuGet as OpenXMLSDK-MOT. This is the same as official binaries and source code in GitHub (from what I can tell). All options install an assembly called DocumentFormat.OpenXml and if done through NuGet it is added a reference to your project.

If you want to validate that the document you create is valid, check out this code example.

Here is an example of how to do a search and replace in a DOCX file.

Here is the link to the Open Xml Developer website.

There are also tons of example on the Open Xml Power Tools project on GitHub and from NuGet.

Open-Xml-Power-Tools - DocumentAssembler
This is an Open Source module in Open Xml Power Tools that allows you to create templates using .DOCX files as templates, a xml data source, and generating a well formatted DOCX file with the data merged in to the template. The template allows such things as tables, conditions, etc using XPATH-like syntax. Here is a video on how it works, but not a step by step tutorial. Here is a getting started video / tutorial that walks you through using the product. It is important to watch the ending where he talks using <# #> instead of Content Controls in Word as the placeholders. It works similar to most reporting tools, but using MS Word as report definition (template) file and the output being a MS Word document as well. You can download the entire Open-Xml-Power-Tools suite that includes DocumentAssembler from here. It is not meant to be used to create DOCX documents from scratch. It always uses a template file to generate new DOCX files. However, Open-Xml-Power-Tools suite does have the ability to help work with DOCX files. NOTE: It is built on top of (requires it) the Open XML Word Processing  SDK (above).

DOCX
If you really don't want to learn anything about the DOCX format and want a more inuitive way to interact with the DOCX files then this may be a good option if you are okay with alpha software. A simple Open Source project available from codeplex.com or NuGet. It let's you interact with the Word document in an intuitive manor without understanding how Word documents internally work. The examples on his blog are good and good examples in source code. It is easy to use. It is still alpha, but has been around since 2009. It has growing popularity.

Free Spire.Doc
This is a free version with limitations for a professional package called Spire.Doc. It is quite powerful and also does conversions to PDF and many other formats. It does mail merges, etc. Is is nice for small projects that are below the limitations. The API is well thought out and works nicely.

NPOI
This is the .NET implementation of the popular POI Java Project. It is Open Source and totally free and also does MS Excel. The API is a bit low level at time, but works well. I recommend downloaded from github to work with examples. Also, you can install binaries via NuGet. The documentation and examples for XDOC are not really there yet though. Look for XWPF if you want to use DOCX file format. The problem is that it is not the exact same API as the POI Java Project, but it is similar. It is also not as mature and is thus missing some features. It is a bit more abstract that just using the Microsoft packages, but still quite a bit of DOCX format knowledge is needed. In general I found the experience a bit frustrating because of how the project is organized and the poor documentation and very few DOCX examples.