sábado, 26 de mayo de 2007

HTML a PDF y otras transformaciónes con Open Office como Server

El problema

Hace unos días me vi forzado a buscar una manera de convertir documentos HTML a PDF por un requerimento en el laburo. Los prerrequisitos eran: tiene que ser algo que se pueda hacer desde Java y que no haya que pagar ninguna licencia.

Primera aproximación

Comencé mi búsqueda en Google con cosas como html2pdf java. Más allá de que era complicado encontrar algún resultado copado, la mayoría de las estrategias que aparecían iban por el lado de:
  1. Convertir el HTML a XHTML (por ejemplo con JTidy)
  2. Convertir el XHTML a XSL-FO (Extensible Stylesheet Language Formatting Objects) usando una hoja de estilo XSL y un trasformador XSLT (por ejemplo con Xalan)
  3. Formatear el XSL-FO para finalmente generar un PDF (por ejemplo con FOP)
Esta guía explica muy bien cómo seguir esta estrategia. El asunto es que FOP aún no soporta por completo el estandar de XSL-FO y los PDF generados mediante esta técnica no quedan nada bien.

Cambio de estrategia

Juan me comentó acerca de este post en PC++ que hablaba de la posibilidad de usar a Open Office en modo servidor y enviarle mensajes mediante un socket. En esos mensajes se le podían decir cosas tan geniales como "abrí este archivo y exportalo a PDF". El post hablaba de usar un script en python, pero tenía que haber una manera de ejecutar eso desde Java (sin llegar a JPython).

Efectivamente, en la página de la API de Open Office encontré que está disponible para Java, C++ y Python.

La Solución

Luego de lidiar un rato con la API de Open Office dije "tiene que haber algo más fácil". Fue así como llegué a JODConverter (Java Open Document Converter).

JODConverter es una bibiloteca que encapsula las dificultades de conexión a Open Office que impone el uso de la API oficial y hace que su uso sea tan simple como esto:
  // it will guess the format based on the file extensions
File inputFile = new File("document.doc");
File outputFile = new File("document.pdf");

// connect to an OpenOffice.org instance running on port 8100
OpenOfficeConnection connection = new SocketOpenOfficeConnection(8100);
connection.connect();

// convert - input and output formats are guessed from file extensions
DocumentConverter converter = new OpenOfficeDocumentConverter(connection);
converter.convert(inputFile, outputFile);

// close the connection
connection.disconnect();
JODConverter tiene pocas dependencias, pero está en el mvnrepository, así que si usás maven vas a salir andando en 5 minutos.

Configurando Open Office

Y sí, algo hay que hacer para que Open Office responda mis pedidos de conversión de archivos, pero también es muy simple. Hay que iniciarlo en modo servidor y decirle que escuche en algún puerto (para estos ejemplos, uso el puerto 8100, pero cualquiera que esté libre debería servir). Suponiendo que el ejecutable es soffice:
soffice -headless -accept="socket,port=8100;urp;"
Issue conocido

Hay un problema reportado en Open Office que impide que el servidor de Open Office pueda correr en una computadora diferente a la que lo invoca.

En el ejemplo de más arriba usamos a la clase SocketOpenOfficeConnection. Esta clase lo que hace es usar el socket con Open Office para darle las instrucciones, pero el archivo se pasa por referencia y Open Office debe tener permiso para abrirlo del Filesystem (y permiso de escritura para guardar el resultado). Existe otra clase llamada StreamOpenOfficeDocumentConverter que envía y recibe el contenido de los archivos por socket. Esta clase es la que no funciona bien por el bug de Open Office.


Espero que a alguien le sirva toda esta data :D

Publicar un comentario