Java ist auch eine Insel

Freitag, November 23, 2007

OpenOffice als Template-Engine: Dokument einlesen, verändern, schreiben

Da ich meine Rechnungen automatisch generiert und als PDF konvertiert haben möchte, habe nach einer Lösung gesucht, wie ich die Rechnungsdaten aus einer Datenquelle in der Template einsetzten kann, und am Ende eine PDF bekomme. Klar sind Report-Generatoren dafür auf der Welt, aber meine Vorlagen möchte ich nicht für Eclipse BIRT oder Jasper schreiben, sondern in Word bzw. OpenOffice. RTF ist relativ leicht zu schreiben und für ein Template meines Erachtens ganz gut. Meine Gedanken kreisten daher einige Zeit um RTF->PDF, doch da gibt es keine freie Lösung. Auch Wege wie RTF->FO->PDF sind möglich, aber dafür gibt es ebenfalls keine leichtgewichtigen freien Lösungen.

Schon für meinen PowerPoint->PDF-Konverter habe ich mit OpenOffice gearbeitet und das klappte ganz gut. Das habe ich für meine Templates nun wieder überlegt -- ein recht harter Weg für simple RTFs zwar, aber es funktioniert. Doch anstatt RTF zu nutzen, wollte ich gleich das XML-Format verwenden. Dazu muss man wissen, dass OO ein Zip-Archiv für das OO-Dokument vorsieht und dort in einer XML-Datei den Content ablegt. Mit der großartigen Open-Source-Bibliothek https://truezip.dev.java.net/ ist der Zugriff auf Archive sehr einfach.

Mit TrueZIP ist eine einfache Lösung entstanden, ein OO-Dokument mit Template-Anweisungen wie ${address} zu lesen, Ersetzungen vorzunehmen, und alles wieder zu schreiben. Diesen Text hier zu schreiben hat länger gedauert, als die 90 Zeilen Quellcode. Also los:


import java.io.*;
import java.nio.channels.FileChannel;
import de.schlichtherle.io.ArchiveDetector;

public class OpenOfficeUtils
{
  public static void main( String[] args )
  {
    String source      = "S:/Private/Traida/Bills/template.ods";
    String destination = "S:/Private/Traida/Bills/bill1.ods";

    copyFile( source, destination );

    String content = readOpenOfficeContent( source );

    content = content.replace( "${addressline1}", "Christian Ullenboom" );

    writeOpenOfficeContent( destination, content );
  }

  public static String readOpenOfficeContent( String filename )
  {
    Reader is = null;

    try
    {
      de.schlichtherle.io.File file = new de.schlichtherle.io.File( filename + "/content.xml", ArchiveDetector.ALL );
      char[] fileContent = new char[ (int) file.length() ];
      is = new de.schlichtherle.io.FileReader( file );  // TODO: <?xml version="1.0" encoding="UTF-8"?>
      is.read( fileContent );

      return new String(fileContent);
    }
    catch ( IOException e )
    {
      throw new IllegalArgumentException( e );
    }
    finally
    {
      try { is.close(); } catch ( Exception e ) { }
    }
  }

  public static void writeOpenOfficeContent( String filename, String content )
  {
    Writer os = null;

    try
    {
      de.schlichtherle.io.File file = new de.schlichtherle.io.File( filename + "/content.xml", ArchiveDetector.ALL );
      os = new de.schlichtherle.io.FileWriter( file );
      os.write( content );
    }
    catch ( IOException e )
    {
      throw new IllegalArgumentException( e );
    }
    finally
    {
      try { os.close(); } catch ( Exception e ) { }
    }
  }

  public static void copyFile( String in, String out )
  {
    FileChannel inChannel  = null;
    FileChannel outChannel = null;

    try
    {
      inChannel  = new FileInputStream( new File(in) ).getChannel();
      outChannel = new FileOutputStream( new File(out) ).getChannel();
      inChannel.transferTo( 0, inChannel.size(), outChannel );
    }
    catch ( IOException e )
    {
      throw new IllegalArgumentException( e );
    }
    finally
    {
      try { inChannel.close(); } catch ( Exception e ) { }
      try { outChannel.close(); } catch ( Exception e ) { }
    }
  }
}

Labels: ,

AddThis Social Bookmark Button

4 Comments:

  • Hallo Herr Ullenboom,

    auf der Suche nach einer Art Templateengine für Java mit dem dazugehörigen Editor, bin ich auf Ihren Artikel gelangt. Die Idee fand ich super klasse und habe mich direkt dazu entschlossen das ganze in meine RCP-Anwendung einzubauen. Nur sind mir ein paar Dinge aufgefallen, welche sich nicht so einfach und unkompliziert lösen lassen, wie zum Beispiel Tabellen mit dynamischen Zeilen. Da ich dies unbedingt benötige, bin ich durch etwas intensivere Suche auf das Projekt jooreports gestoßen. Dieses Projekt (http://jooreports.sourceforge.net) unterstützt dies.

    By Anonymous Anonym, at Dezember 19, 2007 4:29 PM  

  • Cool. Das probiere ich gleich mal aus. Bei meinem Ansatz musste ich auch ganz schön rumpfuschen, und der Programmcode wurde immer größer. Plötzlich kamen noch XML-Kodierung für die Umlaute dazu, dann kann man in der Tabellenkalkulation mit nummerischen Zellenelementen nicht einfach ${text} schreiben, usw.

    By Blogger Christian Ullenboom, at Dezember 19, 2007 4:35 PM  

  • i think you mean jodreports

    By Anonymous Anonym, at Juni 23, 2008 6:11 PM  

  • link: http://jodreports.sourceforge.net/

    By Anonymous Anonym, at Juni 23, 2008 6:11 PM  

Kommentar veröffentlichen

<< Home