Produzir ficheiros no formato ODF em .NET

Introdução

Um dos desafios com que os programadores se deparam com alguma frequência é a criação de documentos de forma automática, de modo a optimizar e a reduzir o trabalho manual em aplicações de produtividade.

Quando o programador tem à sua disposição aplicações Microsoft Office, o seu trabalho está mais facilitado, especialmente se conjugado com tecnologia .NET. Mas, se quiser produzir documentos num formato aberto, como o ODF (Open Document Format), a documentação existente é muito escassa e as bibliotecas disponíveis são quase nulas e muito fracas.

Foi a necessidade de produzir documentos de texto ODF (ODT) em grande número, a partir de informação em bases de dados, que me levou a conhecer melhor este formato de ficheiros. A partir do conhecimento do formato ODF, foi possível desenvolver um mecanismo simples para o preenchimento de modelos de documentos de texto.

O intuito deste artigo é demonstrar como podemos preencher documentos através de um modelo já existente. O desenvolvimento foi feito em ASP.NET, mas o conceito pode ser extrapolado para qualquer linguagem de programação.

O Formato ODT (ODF para texto)

Para melhor entendermos este processo, necessitamos compreender a estrutura de ficheiro do documento ODT. Este documento não é mais do que um arquivo ZIP com uma estrutura definida, várias pastas e ficheiros, a maior parte em XML. Mais precisamente, trata-se de um ficheiro JAR (Java Archive). Podemos explorar o conteúdo de um ficheiro ODT de uma forma simples. Comece por criar o seu modelo de documento numa aplicação compatível, por exemplo, no LibreOffice. Experimente fazer o unZIP desse ODT, alterando previamente a extensão ODT para ZIP, se necessário. Repare que existem muitos ficheiros dentro do ZIP. No entanto, para o preenchimento dos modelos, vamos necessitar de utilizar apenas um desses ficheiros, que poderá explorar num editor de texto do tipo Notepad, o ficheiro content.xml.

Este ficheiro contém muitas meta tags, definições de formatação e, bem visível, o conteúdo do texto que escreveu no modelo. Facilmente percebemos que, ao alterar o conteúdo do content.xml, estamos a alterar o texto do documento ODT. Só teremos que colocar o ficheiro dentro do ZIP, novamente.

Há, no entanto, dois critérios a ter em conta, na construção do ficheiro ZIP:

  1. O ficheiro mimetype não deverá estar compactado;
  2. O ficheiro mimetype deverá ser o primeiro ficheiro do índice do ZIP.

Com estas duas condições, o ZIP com extensão ODT é um ficheiro válido.

Requisitos

Em .NET, este processo requer uma biblioteca de compactação ZIP. Neste caso, utilizei a biblioteca DotNetZip (Ionic.Zip.dll) para efetuar a compressão do ficheiro ODT. Os restantes componentes são os nativos de uma framework .NET 3.5 ou superior.

No caso prático que utilizei, recorri a um HTTP handler para invocar todo o processo até efetuar o download do ficheiro ODT final e o mostrar no browser. No entanto, para não estender demasiado o artigo, podemos omitir esta parte do código e mostrar apenas o processo de criação do ficheiro ZIP. O ficheiro pode ser criado sem um handler, ou até mesmo em Visual Basic/C#, numa aplicação de consola, por exemplo.

Processos

Para este caso prático de alteração do conteúdo do documento, não necessitamos de criar o arquivo ZIP de raiz. Vamos criar um modelo no editor de texto e, programaticamente, apenas necessitamos de recolocar o ficheiro content.xml dentro do arquivo ZIP.

Sabendo agora que um documento ODT é um ZIP com vários ficheiros, podemos definir todo o processo do seguinte modo:

  1. Criar o documento modelo no LibreOffice
    O modelo deverá ser criado num editor totalmente compatível com o formato ODF. É um processo simples, sem recurso a qualquer conhecimento técnico, onde poderemos formatar o documento a gosto. Deveremos colocar tags nas partes que pretendemos que sejam substituídas por campos da base de dados (exemplo: [lb_nome_proprio], onde queremos que apareça o nome próprio constante na base de dados). Chamemos a este ficheiro, modelo.odt.
    De seguida, é necessário descompactar o ficheiro content.xml de dentro do modelo ODT previamente criado. Este ficheiro vai ser lido pela nossa aplicação para a substituição das tags pelos valores corretos.
    Tanto o modelo.odt como o ficheiro content.xml vão ser acedidos pela aplicação para gerar o novo documento, pelo que deverão estar acessíveis no .NET.
  2. Ler e processar o content.xml
    A partir desta etapa, vamos meter as mãos na massa e começar a programar. A partir do .NET, vamos criar um procedimento para ler o content.xml para uma string e alterá-la ao nosso gosto, de modo a preencher o documento final com os dados necessários.

    1. Criar uma função para ler o ficheiro content.xml
      Dim content_xml As String = System.Web.HttpContext.Current.Server.MapPath("modelos\content.xml")
      Dim fileContents As String = My.Computer.FileSystem.ReadAllText(content_xml)
    2. Efetuar uma string.Replace das tags pelos dados provenientes da base de dados:
      fileContents = fileContents.Replace("[tag_nome]", r (4).ToString)
    3. Guardar o novo ficheiro content.xml numa área temporária:
      Dim content_xml_generated_folder As String = System.Web.HttpContext.Current.Server.MapPath ("modelos\tmp\")
      Dim content_xml_generated_file As String = content_xml_generated_folder & "content.xml" My.Computer.FileSystem.WriteAllText (content_xml_generated_file, fileContents, False)
  3. Gerar o ficheiro ZIP/ODT
    Depois de criado o ficheiro com o novo conteúdo do documento, é necessário colocá-lo novamente dentro do arquivo ZIP com extensão ODT. Vamos clonar o modelo.odt, carregando-o em memória, substituir pelo novo content.xml e gravar o modelo com outro nome.
    Exemplifica-se com um procedimento básico que deverá ser personalizado para criar ficheiros com sequência numérica, por exemplo, para que não se substituam sempre que invocado o procedimento:

    Private Sub atualizar_odt(ByVal content_xml_file As String, ByVal modelo_odt_file As String)
      Dim tmp_folder As String = System.Web.HttpContext.Current.Server.MapPath("modelos\tmp\")
      Dim modelo_file_generated As String = tmp_folder & ""
      Using zip As ZipFile = Ionic.Zip.ZipFile.Read(modelo_odt_file)
        'Cria um novo objeto do tipo ZipFile, cópia do modelo.odt'
        'Atualiza o novo ficheiro content.xml no zip modelo.odt'
        zip.UpdateItem(content_xml_file, "")
        Dim filePerm As New FileIOPermission(PermissionState.Unrestricted, tmp_folder)
        filePerm.Assert()
        'Stream do conteúdo do zip para memória'
        Dim msZippedContent As New MemoryStream
        zip.UseZip64WhenSaving = Zip64Option.Never
        zip.TempFileFolder = tmp_folder
        'Escreve o stram de memória em filesystem'
        zip.Save(msZippedContent)
        'Método para fazer download automático, opcional'
        HttpContext.Current.Response.ClearContent()
        HttpContext.Current.Response.Clear()
        HttpContext.Current.Response.ContentType = "application/zip"
        System.Web.HttpContext.Current.Response.AddHeader("Content-Disposition", "attachment; filename=NOVO_DOC.odt")
        msZippedContent.WriteTo(HttpContext.Current.Response.OutputStream)
        HttpContext.Current.Response.End()
        HttpContext.Current.ApplicationInstance.CompleteRequest()
      End Using
    End Sub

Conclusão

O processo de criação de um documento ODT não é complexo. No entanto, criá-lo de raiz é um trabalho moroso. Já existem algumas bibliotecas para o efeito mas, mesmo assim, gerar todo o documento programaticamente continua a ser demorado e exige muito código.

Para o preenchimento de documentos com base num modelo, encontrei esta solução prática. A criação de um modelo no LibreOffice é extremamente rápida. A alteração do modelo é simples. Se criarmos um procedimento comum na nossa biblioteca de código para invocar a criação do ODT com base num modelo, podemos criar relatórios, formulários, cartas, etc., com extrema facilidade e muito rapidamente.

Bibliografia Recomendada

  1. Estrutura do documento ODT: http://books.evc-cit.info/odbook/book.html
  2. O ficheiro JAR: http://en.wikipedia.org/wiki/JAR_(file_format)
  3. DotNetZip Library: http://dotnetzip.codeplex.com/

Publicado na edição 48 (PDF) da Revista PROGRAMAR.