Open XML SDK: gerando arquivos .docx a partir de templates

Adotado a partir da versão 2007 do pacote Office, o formato Open XML é uma alternativa aberta e flexível voltada à geração de documentos de texto, planilhas e apresentações com os mais diversos propósitos. Combinando o uso de XML a técnicas de compactação baseadas no padrão ZIP, esta especificação foi concebida de forma a facilitar a sua integração com as mais diferentes tecnologias. Um bom exemplo disto é o Open XML SDK, um framework open source disponibilizado pela própria Microsoft e que viabiliza a manipulação de arquivos Open XML na plataforma .NET.

Documentos do Word são utilizados em muitas organizações como formulários para o preenchimento de pesquisas ou, até mesmo, em apontamentos envolvendo eventos relevantes para um contexto específico. O objetivo deste artigo é demonstrar a utilização da biblioteca Open XML SDK na geração de tais arquivos, com isto acontecendo por meio de um exemplo prático descrito nas próximas seções.

Exemplo de utilização do Open XML SDK

Para implementar os projetos demonstrados nesta e na próxima seção foram utilizados os seguintes recursos:

  • O Microsoft Visual Studio Professional 2013 Update 4 como IDE de desenvolvimento;
  • O .NET Framework 4.5.1;
  • O framework MS Test para a codificação de um teste unitário a ser executado através do Visual Studio.

A Solution apresentada neste artigo terá por nome “OpenXml.Word” (Imagem 1), sendo que a mesma poderá ser baixada a partir do GitHub em um link indicado no final deste artigo.

openxml-word-01
Imagem 1. Criando a Solution para testes

Um dos projetos desta solução será uma Class Library chamada “OpenXml.Word.Extensions” (Figura 2). Nesta biblioteca será definida uma classe para a geração de novos arquivos .docx por meio de documentos pré-existentes. Na prática este processo acontecerá através da substituição de trechos de texto empregados como marcações, usando para isto valores que atendem a alguma necessidade específica.

openxml-word-02
Imagem 2. Criando a Class Library OpenXml.Word.Extensions

Como próximo passo será necessário adicionar uma referência ao Open XML SDK via utilitário NuGet. Clicar para isto com o botão direito do mouse sobre o item “References” do projeto OpenXml.Word.Extensions, acionando em seguida a opção “Manage NuGet Packages…” (Imagem 3).

openxml-word-03
Imagem 3. Acessando o utilitário NuGet para a inclusão de uma referência

Com a interface do NuGet ativa será necessário instalar o pacote DocumentFormat.OpenXml, como indicado na Imagem 4.

openxml-word-04
Imagem 4. Adicionando o Open XML SDK ao projeto OpenXml.Word.Extensions

Os recursos de compactação utilizados pelo Open XML SDK encontram-se em um arquivo chamado WindowsBase.dll. Logo, uma referência apontando para esta biblioteca também deverá ser adicionada ao projeto (Imagem 5).

openxml-word-04
Imagem 5. Adicionando ao projeto uma referência à biblioteca WindowsBase.dll

Na Listagem 1 está a definição do tipo estático DocxTemplate:

  • O método CriarNovoDocumento receberá como parâmetros o arquivo .docx a ser empregado como template, o caminho do novo documento que será gerado, além de uma instância do tipo Dictionary (namespace System.Collections.Generic) com as substituições necessárias;
  • A primeira ação executada em CriarNovoDocumento será a cópia do template, com a geração de um novo arquivo .docx por meio de uma chamada ao método Copy da classe File (namespace System.IO);
  • Uma instância do tipo WordprocessingDocument (variável “wordDoc”) é então criada dentro de uma cláusula using, com o objeto em questão correspondendo à representação em memória do novo documento do Word a ser produzido pela operação CriarNovoDocumento. O construtor de WordprocessingDocument recebe como parâmetros o caminho do arquivo .docx, bem como um flag que indica a abertura para leitura/escrita de tal documento;
  • O próximo passo será a criação de uma referência do tipo StreamReader (namespace System.IO), tomando por base um stream retornado pela propriedade MainDocument do objeto associado à variável wordDoc. Na sequência o método ReadToEnd será invocado, de forma que ocorra a leitura de todo o texto que forma o arquivo .docx (ainda com as marcações aguardando substituição);
  • As marcações serão substituídas a partir de um laço foreach, utilizando para isso instâncias da classe Regex (namespace System.Text.RegularExpressions). Com os objetos gerados dentro do loop será acionada a operação Replace, procedimento este que servirá de base para se chegar ao texto final do documento;
  • Por fim, uma referência do tipo StreamWriter (namespace System.IO) será criada, por meio do stream vinculado à propriedade MainDocument da variável wordDoc. A gravação do novo texto no arquivo ocorrerá através da invocação do método Write.

É importante frisar que todo o processamento realizado pela classe DocxTemplate acontece de forma independente do Word. Na prática, o grande benefício em se utilizar o Open XML SDK está na possibilidade de criação de documentos Office isoladamente, sem a necessidade dos aplicativos que compõem esta suíte.


using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.IO;
using DocumentFormat.OpenXml.Packaging;

namespace OpenXml.Word.Extensions
{
    public static class DocxTemplate
    {
        public static void CriarNovoDocumento(
            string caminhoTemplate,
            string caminhoArquivoDestino,
            Dictionary substituicoes)
        {
            File.Copy(caminhoTemplate, caminhoArquivoDestino);

            using (WordprocessingDocument wordDoc =
                WordprocessingDocument.Open(caminhoArquivoDestino, true))
            {
                string docText = null;
                using (StreamReader sr =
                    new StreamReader(wordDoc.MainDocumentPart.GetStream()))
                {
                    docText = sr.ReadToEnd();
                }

                Regex regexText;
                foreach (var itemSubstituicao in substituicoes)
                {
                    regexText = new Regex(itemSubstituicao.Key);
                    docText = regexText.Replace(docText, itemSubstituicao.Value);
                }

                using (StreamWriter sw = new StreamWriter(
                    wordDoc.MainDocumentPart.GetStream(FileMode.Create)))
                {
                    sw.Write(docText);
                }
            }
        }
    }
}

Testes

Visando à realização de um teste na biblioteca implementada na seção anterior, será criado agora um projeto do tipo “Unit Test Project”. É o que demonstra a Imagem 6, na qual foi especificado o nome “OpenXml.Word.Tests” para tal projeto.

openxml-word-06
Imagem 6. Criando o projeto OpenXml.Word.Tests

Deverão ser incluídas em OpenXml.Word.Tests referências à Class Library OpenXml.Word.Extensions (Imagem 7), às bibliotecas System.Configuration (Imagem 8) e WindowsBase (Imagem 9), além do framework Open XML SDK (Imagem 10).

openxml-word-07
Imagem 7. Adicionando ao projeto de testes uma referência à Class Library OpenXml.Word.Extensions

openxml-word-08
Imagem 8. Adicionando ao projeto de testes uma referência à biblioteca System.Configuration.dll

openxml-word-09
Imagem 9. Adicionando ao projeto de testes uma referência à biblioteca WindowsBase.dll

openxml-word-10
Imagem 10. Adicionando o Open XML SDK ao projeto de testes

Um arquivo de configurações também será criado para o projeto OpenXml.Word.Extensions, como indicado na Imagem 11.

openxml-word-11
Imagem 11. Criando um arquivo de configurações para o projeto de testes

Na Listagem 2 é possível observar o conteúdo do arquivo app.config. Foram definidos neste documento XML itens de configuração que apontam para o template a ser utilizado como base, além do caminho em que serão criados os novos arquivos do Word.

openxml-appconfig
Listagem 2: Arquivo app.config (projeto OpenXml.Word.Tests)

Na Imagem 12 está o documento do Word que será utilizado como template. As marcações a serem substituídas estão assinaladas em vermelho: tais itens servirão de referência para que a classe DocxTemplate realize as modificações necessárias.

openxml-word-12
Imagem 12. Template que será utilizado no teste da classe DocxTemplate

Já na Listagem 3 encontra-se a implementação do tipo DocxTemplateTeste01:

  • Esta classe corresponde à representação de um teste unitário a ser executado a partir do Visual Studio (por isso foi marcada com o atributo TestClassAttribute), tendo por objetivo validar o funcionamento do método CriarNovoDocumento da classe DocxTemplate;
  • O método TestarGeracaoDocx também está associado a um atributo (no caso, à classe TestMethodAttribute), com tal indicação sendo utilizada pelo Visual Studio para a execução do teste automatizado. No início desta operação acontece a criação de um objeto Dictionary, o qual conterá as substituições a serem realizadas. O próximo passo será a obtenção do caminho do template, além da geração do nome do arquivo a ser gerado como resultado do teste. Por fim, o método CriarNovoDocumento de DocxTemplate será invocado; a chamada ao método IsTrue da classe Assert busca irá determinar se o arquivo esperado realmente foi criado (se este não for o caso, um erro será gerado e estará disponível para visualização dentro de uma janela específica no Visual Studio).

using System;
using System.Collections.Generic;
using System.IO;
using System.Configuration;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenXml.Word.Extensions;

namespace OpenXml.Word.Tests
{
    [TestClass]
    public class DocxTemplateTeste01
    {
        [TestMethod]
        public void TestarGeracaoDocx()
        {
            Dictionary substituicoes =
                new Dictionary();
            substituicoes["#NOME_CLIENTE#"] =
                "João da Silva";
            substituicoes["#ENDERECO_CLIENTE#"] =
                "Avenida Paulista, 950 - São Paulo - SP";
            substituicoes["#NOME_ASSINATURA#"] =
                "Pedro Oliveira";

            string caminhoTemplate =
                ConfigurationManager.AppSettings["CaminhoArquivoTemplate"];
            string caminhoArquivoDestino =
                ConfigurationManager.AppSettings["DiretorioGeracaoArquivoTeste"] +
                "Teste_" + DateTime.Now.ToString("dd-MM-yyyy_HH'h'mm'min'ss's'") + ".docx";

            DocxTemplate.CriarNovoDocumento(
                caminhoTemplate,
                caminhoArquivoDestino,
                substituicoes);

            Assert.IsTrue(File.Exists(caminhoArquivoDestino));
        }
    }
}

OBSERVAÇÕES:

  • As classes TestClassAttribute, TestMethodAttribute e Assert fazem parte do namespace Microsoft.VisualStudio.TestTools.UnitTesting;
  • Por convenção, nomes de classes que representam atributos terminam com o sufixo Attribute. A utilização de expressões que envolvam um atributo vinculando o mesmo a uma estrutura de código dispensa o uso de tal sufixo ao final do nome. Logo, ao se empregar o atributo TestClassAttribute, a classe marcada com essa construção estará associada apenas a uma instrução com o valor “TestClass” (entre colchetes).

A execução do teste unitário criado nesta seção pode ser feita clicando com o botão direito do mouse sobre o método TestarGeracaoDocx; selecionar em seguida a opção “Run Tests” (Imagem 13). Se não acontecerem problemas durante este processo, na janela “Test Explorer” estará um resultado similar ao que consta na Imagem 14.

openxml-word-13
Imagem 13. Executando o teste unitário via menu de atalho

openxml-word-14
Imagem 14. Execução com sucesso do teste unitário

A execução bem sucedida fará com que um arquivo .docx seja criado no diretório de testes (Imagem 15). Ao acessar este documento através do Word, será possível notar que as marcações foram substituídas pelos valores especificados no teste unitário (Imagem 16).

openxml-word-15
Imagem 15. Arquivo gerado após a execução do teste unitário

openxml-word-16
Imagem 16. Valores que substituíram as marcações destacados em vermelho

Conclusão

Este artigo procurou demonstrar uma das alternativas para uso do Open XML SDK, através da geração de novos documentos do Word tomando por base um arquivo pré-existente. Por mais que o exemplo apresentado seja relativamente simples, a criação de documentos mais elaborados com o Open XML SDK também é possível (muito embora exija maiores esforços de codificação).

Espero que o conteúdo aqui apresentado possa ser útil em algum momento.

Até uma próxima oportunidade!

Referências

Excel e Open XML SDK: gerando novas planilhas .xlsx a partir de templates
http://www.devmedia.com.br/excel-e-open-xml-sdk-gerando-novas-planilhas-xlsx-a-partir-de-templates/25854

Fontes da solução utilizada como exemplo no GitHub
https://github.com/renatogroffe/OpenXml.Word

Integração .NET x Microsoft Office: uma introdução ao Open XML SDK
http://www.devmedia.com.br/integracao-net-x-microsoft-office-uma-introducao-ao-open-xml-sdk/28315

Open XML SDK goes open source
http://blogs.office.com/2014/06/25/open-xml-sdk-goes-open-source/

Welcome to the Open XML SDK 2.5 for Office
https://msdn.microsoft.com/en-us/library/office/bb448854.aspx

  • Filipe Mendonça

    Olá, Renato Groffe. Tenho uma dúvida.
    Em alguns testes que realizei em minha máquina local constatei o seguinte:

    Escrevo um documento “docx” e erro o nome da variável salvando o arquivo.
    Quando reabro o arquivo para a correção do nome da variável, faço as devidas ressalvas alterando apenas o trecho errado (ex: #NOME_CNTE#, adicionando apenas as letras “L, I, E” ficando #NOME_CLIENTE#), salvando por fim o documento com a variável correta.

    Quando realizo novamente os testes, a variável não é substituída pela string correspondente retornando a nome da variável como consta no Modelo.docx.

    Poderia me dizer o que causa esse tipo de erro? E qual seria a solução plausível?