Merge word docx documents from a template and replace words

Asked

Viewed 2,351 times

12

I need to get the contents of a file docx as template, which contains as content a text and some identifiers to be changed with other data, and from that template create a single other docx with the same content but replicated and the appropriate values modified.

As an example, I have in my document a text containing the following content:

Hello <NOME>! Welcome home ... Your address <ENDERECO>.

And then I need to create a single other docx containing something like:

Hello Fulano 1! Welcome home ... Your address ENDERECO 1.
Hello Fulano 2! Welcome home ... Your address ENDERECO 2.
Hello Fulano 3! Welcome home ... Your address ENDERECO 3.

Where each line would be at the beginning of a new page.

That’s what I’m trying to do (TAKING A TEST) using the plugin Docx installed in the project via Nuget.
Another way to accomplish the process will be welcome!

My example for problem presentation:

public static void Main(string[] args)
{
    var dictionary = new Dictionary<string, string>();
    dictionary.Add("Fulano 1", "Endereco 1");
    dictionary.Add("Fulano 2", "Endereco 2");
    dictionary.Add("Fulano 3", "Endereco 3");

    var template = new FileStream("D:\\template.docx", FileMode.Open, FileAccess.Read);
    var docs = new List<DocX>();

    var x = 0;
    foreach (var item in dictionary)
    {
        x++;
        var doc = DocX.Create("D:\\temp_NUM.docx".Replace("NUM", x.ToString()));
        doc.ApplyTemplate(template, true);
        doc.ReplaceText("<NOME>", item.Key);
        doc.ReplaceText("<ENDERECO>", item.Value);
        doc.Save();
        docs.Add(doc);
    }

    var newDoc = DocX.Create("D:\\newDoc.docx");
    foreach (var doc in docs)
        newDoc.InsertDocument(doc);

    newDoc.Save();
}

Although it is using a simple data source, built with a Dictionary, the source of the data will be the database of the system where this should be implemented.

The archives temp_1.docx, temp_2.docx and temp_3.docx are created and the content appears correctly.
But on the line newDoc.InsertDocument(doc); in:

foreach (var doc in docs)
    newDoc.InsertDocument(doc);

I get the following error: The sequence contains no elements. And with that the "merge" is not done.

How can I solve the problem?

  • Um... sounds pretty simple (and you didn’t mention if the data has a specific origin besides manually informed). So why didn’t you just use the Direct Mail in Directory native to MS Word?

  • The link from my previous comment has the information but you find tutorials like this on Youtube that do step by step. I did not find a tutorial in Portuguese, but what it is doing you find in the "Correspondence" tab, "Start Direct Mail" button, "Direct Mail Wizard Step by Step".

  • Anyway, my question was more in the sense of why you’re trying to develop something for it. Is there any additional reason (like, for example, you have a large number of docx files and so want to automate the process)? If so, it might be easier to use the Office API, as suggested in a reply, or even the [tag:vba] directly from Word...

  • Sorry, but it is not clear. Which software requirement? What is actually this requirement? Because, as I said, to do this that you ask in the question (and even send by email or print) is something that MS Word ALREADY DOES without having to implement anything. See this other video here: https://www.youtube.com/watch?v=kUPmA8e549s

  • I was the one who voted against the question, because I think it is poorly worded. Although well written, the way you are asking does not make sense (at least for me), because your "system" is generating direct mail using Word, which already does it alone. I tried to ask to better understand (or make you improve the issue in this sense), but the arguments "of course I need this" and "my software has to do this" are not really useful to the community because they simply don’t describe their real need. This is why I voted no.

  • If you try to improve the question, I promise to at least take my negative vote. :)

Show 2 more comments

4 answers

2

Example using the library DocX.

static void Main(string[] args)
{
    string templatePath = @"D:\\template.docx";
    string newFile = @"D:\\newDoc.docx";

    var template = new FileStream(templatePath, FileMode.Open, FileAccess.Read);

    string[] aNomes = { "Fulano1", "Fulano2", "Fulano3" };
    string[] aLocais = { "Endereco1", "Endereco2", "Endereco3" };

    using (DocX doc = DocX.Create(newFile))
    {
        doc.ApplyTemplate(template, true);
        int items = aNomes.Length;
        int x = 0;
        string modelo = "";

        while (items > x)
        {
            Paragraph par = doc.InsertParagraph();
            par.AppendLine(modelo);
            par.InsertPageBreakAfterSelf();

            foreach (var p in doc.Paragraphs)
            {
                if (p.Text.Contains("<NOME>") && (p.Text.Contains("<ENDERECO>")))
                {
                    modelo = p.Text;

                    p.ReplaceText("<NOME>", aNomes[x]);
                    p.ReplaceText("<ENDERECO>", aLocais[x]);
                    x++;
                 }

             }                   
          }
          doc.Save();
      }

      Console.WriteLine("Pressione alguma tecla para sair...");
      Console.ReadKey();
}

File structure docx template.:

--> Página 1:
    Olá <NOME>!  Seja bem vindo... Seu endereço <ENDERECO>.

The archive newDoc.docx should look like this:

--> Página 1:
    Olá Fulano1! Seja bem vindo ... Seu endereço Endereco1
--> Página 2:
    Olá Fulano2! Seja bem vindo ... Seu endereço Endereco2
--> Página 3:
    Olá Fulano3! Seja bem vindo ... Seu endereço Endereco3

I believe it should work the way you expect it to.

2


I would use the Office API provided by Microsoft itself, and then:

    wordApp = New Microsoft.Office.Interop.Word.Application;
    oDoc = New Microsoft.Office.Interop.Word.Document;

    oDoc = wordApp.Documents.Open("c:\....");

Take a look at the Microsoft website, you’ll find everything you need about it.

With this tool you can from adding macros (VB) to doing the merge.

A long, long time ago I made a dll for a personal project (http://pastebin.com/kDZVhpiz) who can help you.. don’t judge code kk I was 15 years old yet, but it might serve you as a guide.

2

1

This is a simple way I got to solve the problem:

using Novacode;
using System.Collections.Generic;
using Word = Microsoft.Office.Interop.Word;   
public class Program
{
    public static void Main(string[] args)
    {
        var docList = new List<string>();
        var templatePath = @"D:\template.docx";

        var fakeDAO = new Dictionary<string, string>();
        fakeDAO.Add("Fulano 1", "Endereco 1");
        fakeDAO.Add("Fulano 2", "Endereco 2");
        fakeDAO.Add("Fulano 3", "Endereco 3");

        var x = 0;
        foreach (var item in fakeDAO) {
            x++;
            var docFilePath = @"D:\temp_NUM.docx".Replace("NUM", x.ToString());
            var doc = DocX.Create(docFilePath);
            doc.ApplyTemplate(templatePath);

            doc.ReplaceText("<NOME>", item.Key);
            doc.ReplaceText("<ENDERECO>", item.Value);

            doc.Save();
            docList.Add(docFilePath);
        }
        Merge.DoMerge(@"D:\newFile.docx", docList);
    }
}

The class Merge:

...
public static class Merge
{
    public static void DoMerge(string newFilePath, List<string> docList)
    {
        object sectionBreak = Word.WdBreakType.wdSectionBreakNextPage;
        Word.Application app = new Word.Application();
        try {
            var doc = app.Documents.Add();
            var selection = app.Selection;
            var x = 0;
            foreach (var file in docList) {
                selection.InsertFile(file);
                x++;
                if (x < docList.Count)
                    selection.InsertBreak(ref sectionBreak);
            }
            doc.SaveAs(newFilePath);
        }
        finally {
            app.Quit();
        }
    }
}

Browser other questions tagged

You are not signed in. Login or sign up in order to post.