Extracción de texto de documentos Office desde Java

2
31920

Extracción de texto de documento Office desde Java

Extracción de texto de documentos Office desde Java

Introducción

En este tutorial vamos a ver como podemos extraer texto de los documentos de Office (DOC, XLS y PPT) desde Java. Además, emplearemos para ello la suite OpenOffice de Sun que, como sabréis, es un magnífico reemplazo de Microsoft Office y, además, gratis y de codigo abierto.

La suite OpenOffice es capaz de leer documentos OLE2 (de Microsoft Office) con una compatibilidad cercana al 100%. ésto quiere decir que no todo lo que funciona en Microsoft Office funciona en OpenOffice pero, con peque&ntildeos retoques, se pueden migrar los documentos de una plataforma a otra sin problema.

Funcionamiento

Para llevar a cabo la extracción de texto es necesario tener instalado Sun OpenOffice junto con su SDK en Java. En Debian tenemos varios paquetes para ello. Los que no tengáis Debian debéis buscar la web de descargas de OpenOffice y bajaros el programa en si y el SDK. Las librerías Java de OpenOffice estarán en el directorio de instalación de OpenOffice, en el subdirectorio «program/classes». Además, en el subdirectorio «sdk» encontraremos todas la documentación necesaria para desarrollar.

A continuación explicaremos la arquitectura de conexión entre Java y OpenOffice: OpenOffice posee un API llamado UNO para desarrollar tanto aplicaciones Java que llamen a OpenOffice como extensiones Java que son llamadas por OpenOffice. A nosotros nos interesa el primer caso. Cuando una aplicación Java llama al API UNO de OpenOffice, lo primero que se hace es cargar una librería nativa (.DLL en Windows, .so en Unix) que lanza un ejecutable de OpenOffice oculto en segundo plano. A continuación, UNO se conecta al ejecutable de OpenOffice a traves de un socket y le envía los comandos pertinentes. Este método tiene la ventaja de que Java se comporta como un cliente de OpenOffice, con lo que se pueden hacer aplicaciones UNO distribuidas e incluso multiplataforma. Es decir, podemos, por ejemplo, conectar un programa Java en un Windows a un OpenOffice remoto en un Solaris. UNO se encarga de aislarnos de todas las diferencias entre plataformas (tama&ntildeo de palabra, orden de los bytes, etc.).

Una peculiaridad del API UNO es que es orientado a componentes. A los que hayáis programado ActiveX o cualquier componente en la plataforma COM de Microsoft, os resultaran muy familiares los mecanismos de obtención de interfaces de UNO. Básicamente, la única diferencia entre UNO y Java estándar es que en UNO no hay objetos en si sino componentes proveedores de interfaces. Por ejemplo: un documento de texto de OpenOffice es un componente que implementa las interfaces XTextDocument, XComponent, XTextRange, etc. A los componentes sólo se puede acceder a través de alguna de sus interfaces. A algunas interfaces, Sun las llama servicios y al principio de la guía de desarrollo hay una disquisición sobre la diferencia entre servicio e interfaz. Para el desarrollo es algo que nos da más o menos igual: las interfaces se comportan como servicios y viceversa.

Con lo dicho hasta ahora parecería que UNO es igual que Java estándar, pero no es así. En Java podemos tener clases que implementen múltiples interfaces y nos basta con hacer un casting del objeto al interfaz en cuestión para obtenerla. Por ejemplo, dada la clase:

Si creamos un objeto le podemos hacer casting directo a sus dos interfaces:

ésto en UNO no se puede hacer. Como el API es multiplataforma y distribuido, los objetos con los que trabajamos son proxis (al estilo de los de EJB). ésto quiere decir que, dado un componente, por ejemplo un documento de texto, para obtener sus distintas interfaces tenemos que llamar a un método especial del runtime de UNO. Ese método se llama UnoRuntime.queryInterface() Veamos un ejemplo:

Como se ve en la segunda instrucción, llamamos al método UnoRuntime.queryInterface() para hacer un casting de XComponent a XTextDocument.

La documentacion del SDK de OpenOffice (http://api.openoffice.org/docs/common/ref/com/sun/star/module-ix.html) explica que interfaces cumple cada objeto. Por ejemplo, la documentación del método loadComponentFromURL() nos dice que devuelve siempre un XComponent y que, en el caso de haber cargado un documento de texto (.DOC) se puede pasar el XComponent a XTextDocument. Aparte de ésto, la documentación de cada interfaz también incluye enlaces a la documentación explicativa del API UNO.

La miga: extracción del texto

Una vez entendido como funciona UNO, vamos a ver los tres trozos de código que permiten extraer texto de un documento .DOC (documento de texto de MS Word), de un documento .XLS (hoja de cálculo de MS Excel), y de un documento .PPT (presentación de MS PowerPoint). Para ejecutar estos fragmentos de código es necesario incluir en nuestro proyecto las librerías Java de UNO: aunque normalmente no son necesarios todos, lo más sencillo es incluir todos los archivos .JAR que hay en el subdirectorio program/classes del directorio de instalación de OpenOffice.

Código común a los tres ejemplos

Extracción de texto de un archivo .DOC

Para este tipo de documentos obtendremos una selección de todo el texto del documento, la convertiremos a cadena y ése será el texto extraído.

Extracción de texto de un archivo .XLS

Este tipo de archivos tienen el problema de que, al no ser el contenido texto secuencial, se puede extraer texto de distintas formas. Por ejemplo, para una hoja de cálculo podemos recorrer todas las hojas y, dentro de cada una, todas las celdas e ir concatenando su contenido. Este proceso es excesivamente lento y puede tardar mucho incluso para hojas muy sencillas (por ello se recomienda emplear el método alternativo, explicado después del siguiente ejemplo de código). Se haría con el siguiente codigo:

El método alternativo, mucho más rapido, consiste en salvar el archivo en formato CSV (Comma Separated Values), quitarle las comas y las comillas y, de esa forma, obtener el texto de las celdas. Para quitar dichos carácteres emplearemos expresiones regulares y el método replaceAll() de la clase String de Java. El código es el siguiente:

Extracción de texto de un archivo .PPT

Este es sin duda el archivo más complejo de los tres. El problema de las presentaciones es que no podemos recorrernos todos los objetos gráficos buscando texto (bueno, si podemos, pero sería un poco de locos). Además, tampoco podemos salvar las presentaciones como texto, al estilo de lo que hemos hecho con las hojas de cálculo. ¿Entonces? Entonces tenemos que recurrir a métodos indirectos. El método que se propone aquí es exportar la presentación como documento PDF y utilizar la librería PDFBox para extraer texto del PDF. La librería PDFBox es probablemente la mejor librería de manejo de PDFs en Java y se puede descargar de www.pdfbox.org. Despúes de descargar, se descomprime el ZIP en un directorio y se a&ntildeaden los .JAR de los directorios lib y external a nuestro proyecto. A continuación usamos el código siguiente para extraer el texto:

Problemas

En principio es bastante fácil hacer funcionar la integración Java-OpenOffice; no obstante se pueden presentar algunos problemas típicos. Es altamente recomendable leerse la documentación del SDK antes de empezar. La documentación es amplia, buena y trae muchos ejemplos, y nos puede ahorrar gran cantidad de tiempo.

Los problemas habituales con la plataforma UNO se dan al intentar arrancar OpenOffice. Dado que esto se hace mediante una librería nativa y JNI, éste suele ser el punto débil. Hay dos problemas principales:

  • No se puede cargar la librería nativa de OpenOffice (java.lang.UnsatisfiedLinkError: createJNI): La librería nativa que Java intenta cargar mediante JNI está en el subdirectorio program del directorio de instalación de OpenOffice. UNO busca dicha librería en el CLASSPATH, por lo que no sirve de nada poner el directorio program en la propiedad de sistema de Java java.library.path, o en las variables de sistema PATH o LD_LIBRARY_PATH (en Unix). Hay que a&ntildeadir el directorio program al CLASSPATH de Java. Por ejemplo, en Debian/Unix, basta con a&ntildeadir /usr/lib/openoffice/program al CLASSPATH.
  • No se puede lanzar el ejecutable de OpenOffice (com.sun.star.comp.helper.BootstrapException: no office executable found!): Este problema se produce porque Java no encuentra el ejecutable de OpenOffice. Para solucionarlo no sirve poner el ejecutable en el PATH del sistema, sino que hay que tener el directorio del ejecutable en el CLASSPATH de Java, que es donde lo busca UNO. Por ejemplo, en Debian/Unix basta con a&ntildeadir /usr/bin al CLASSPATH.

2 Comentarios

  1. Buenas, en el ejemplo codificado, dónde se guarda el nuevo fichero generado. Me explico,
    File tmp = File.createTempFile(«extract»,»openoffice»,new File(«/tmp»));
    (/tmp), es una ruta local del cliente o es una ruta dle servidor ¿…?
    Gracias.

Dejar respuesta

Please enter your comment!
Please enter your name here