This blog is mainly about Java...

Wednesday, December 3, 2008

Dynamically generate ODT and PDF documents from Java

I would like to generate a OpenOffice document and a PDF document without having a running OpenOffice service in Java. This was not as easy as it sounds, however I have found a solution.

You can create ODT document fairly easy without having a running instance of OpenOffice. However I have not found an easy way to convert that document to a PDF. However, I found a solution for the latter when running Linux. 

All the following libraries are Open Source .

The easiest way to generate ODT documents from templates is by using a (unmaintaned) library by the name of JOOReport. JOOReport uses Freemarker to create ODT documents based on templates. 

Basically what you need to do is create a template odt document in OpenOffice and whereever you want to insert something, you can insert it with the syntax 
${anythingGoesHere}
ie: My name is ${name}

When you have finished implementing the template, you must then create a properties file defining the values. 

ie. 
name=
age=
birthday=
address=


We can then from the Java program get the Properties file, and fill inn already pre defined variables. We then give the template and the properties file as well as the output odt file in arguments to the program. After the creation is successfull, the easiest way is to call a linux program called 
odt2pdf someFile.odt

which takes the odt file as argument and creates a pdf file with the same name. 

Vouila. As easy as that. You can also style the template as you like and the styling will also be implemented in the generated output file.

This is a standalone program that creates a document from a template and a data file and converts it to the specific format. 


// JOOReports - The Open Source Java/OpenOffice Report Engine 
// Copyright (C) 2004-2006 - Mirko Nasato 
// 
// This library is free software; you can redistribute it and/or 
// modify it under the terms of the GNU Lesser General Public 
// License as published by the Free Software Foundation; either 
// version 2.1 of the License, or (at your option) any later version. 
// 
// This library is distributed in the hope that it will be useful, 
// but WITHOUT ANY WARRANTY; without even the implied warranty of 
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
// Lesser General Public License for more details. 
// http://www.gnu.org/copyleft/lesser.html 
// 
package net.sf.jooreports.tools; 

import java.io.File; 
import java.io.FileInputStream; 
import java.io.FileOutputStream; 
import java.net.ConnectException; 
import java.util.Properties; 

import org.apache.commons.io.FilenameUtils; 

import net.sf.jooreports.converter.DocumentConverter; 
import net.sf.jooreports.openoffice.connection.OpenOfficeConnection; 
import net.sf.jooreports.openoffice.connection.SocketOpenOfficeConnection; 
import net.sf.jooreports.openoffice.converter.OpenOfficeDocumentConverter; 
import net.sf.jooreports.templates.DocumentTemplate; 
import net.sf.jooreports.templates.UnzippedDocumentTemplate; 
import net.sf.jooreports.templates.ZippedDocumentTemplate; 
import freemarker.ext.dom.NodeModel; 

/** 
 * Command line tool to create a document from a template and a data file 
 * and convert it to the specified format. 
 * 


 * The data file can be in XML format or a simple .properties file. 
 * 


 * Requires an OpenOffice.org service to be running on localhost:8100 
 * (if the output format is other than ODT). 
 */ 
public class CreateAndConvertDocument { 

  public static void main(String[] args) throws Exception { 
  if (args.length < 3) { System.err.println("USAGE: "+ CreateAndConvertDocument.class.getName() +" "); 
  System.exit(0); 
  }  
  File templateFile = new File(args[0]); 
  File dataFile = new File(args[1]); 
  File outputFile = new File(args[2]); 

  DocumentTemplate template = null; 
  if (templateFile.isDirectory()) { 
  template = new UnzippedDocumentTemplate(templateFile); 
  } else { 
  template = new ZippedDocumentTemplate(templateFile); 
  } 
   
  Object model = null; 
  String dataFileExtension = FilenameUtils.getExtension(dataFile.getName()); 
  if (dataFileExtension.equals("xml")) { 
  model = NodeModel.parse(dataFile); 
  } else if (dataFileExtension.equals("properties")) { 
  Properties properties = new Properties(); 
  properties.load(new FileInputStream(dataFile)); 
  model = properties; 
  } else { 
  throw new IllegalArgumentException("data file must be 'xml' or 'properties'; unsupported type: " + dataFileExtension); 
  } 
   
  if ("odt".equals(FilenameUtils.getExtension(outputFile.getName()))) { 
  template.createDocument(model, new FileOutputStream(outputFile)); 
  } else { 
  OpenOfficeConnection connection = new SocketOpenOfficeConnection(); 
  try { 
  connection.connect(); 
  } catch (ConnectException connectException) { 
  System.err.println("ERROR: connection failed. Please make sure OpenOffice.org is running and listening on port "+ SocketOpenOfficeConnection.DEFAULT_PORT +"."); 
  System.exit(1); 
  } 
   
  File temporaryFile = File.createTempFile("document", ".odt"); 
  temporaryFile.deleteOnExit(); 
  template.createDocument(model, new FileOutputStream(temporaryFile)); 
  
  try { 
  DocumentConverter converter = new OpenOfficeDocumentConverter(connection); 
  converter.convert(temporaryFile, outputFile); 
  } finally { 
  connection.disconnect(); 
  } 
  } 
  } 
}




8 comments:

martosfre said...

Supér interesante el post!!! Bendiciones

Cassio Seffrin said...

Hi Shervin,

first, very interesting solution, but about this part: "unmaintaned library by the name of JOOReport"

Can you tell me wich version of this library you used ?

I'm using jooreports-2.0.1.jar and

it's not working

See the exceptions:

Exception in thread "main" freemarker.core.ParseException: Encountered "<" at line 4, column 3699 in content.xml.
Was expecting one of:
...
...
"false" ...
"true" ...
...
...


Regards,
Cassio

Unknown said...

Hi Cassio. This exception has nothing to do with the version of JODReports. You get this error because you are expecting a string but you have a boolean. There are special expressions you have to use for boolean. I dont remember exactly what but google "freemarker boolean expression".
For instance if you want to print a Date object you must use.something like ${somedate?datetime}

Hope this helps

Cassio Seffrin said...

Hi Shervin,

Thks a lot for your prompt response.

There are some things in the programer life we cannot explain. I just reopen the .odt template file and save him again.

Now the same template with the same properties file and the same java source code works fine!


Just for keep in register:

- jooreports-2.0.1.jar
- jodconverter-2.2.2.jar
- freemarker-2.3.15.jar

Unknown said...

No problem.

You must be careful when entering expressions in OpenOffice.org.

At least if you are using older version of JODReports. Because it is very easy to get some formatting in between the expressions, and this messes up the XML.

However, with the new version of JODReports, you can add functions (CTRL - F2) and this way you won't get any formatting issues, which makes it a bit safer.

Cassio Seffrin said...

Very important issue Mr. Shervin.

Always better to use the latest version, that is what I'll do.

Thks a lot.

Anonymous said...

Thank you very much! This is a very userfriendly way to use and create templates for odt and pdf!

Zarfishan said...

i use this PDF Creator provided by Aspose because its a cloud based API so provides security to your documents also and it also allows me to read and convert my pdf files to many other formats.

Labels