io.rubrica.util.OfficeAnalizer Maven / Gradle / Ivy
/*
* Copyright 2009-2018 Rubrica
*
* This program 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 3 of the License, or
* (at your option) any later version.
*
* This program 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.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*/
package io.rubrica.util;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import io.rubrica.xml.FileUtils;
/**
* Clase para el análisis de ficheros OOXML, ODF y Microsoft Office
* 97/2003.
*/
public final class OfficeAnalizer {
private OfficeAnalizer() {
// No permitimos la instanciacion
}
private static final String ZIP_MIMETYPE = "application/zip";
/** MimeTypes reconocidos del formato OOXML. */
private static final Set OOXML_MIMETYPES = new HashSet(17);
/** MimeTypes reconocidos del formato ODF. */
private static final Set ODF_MIMETYPES = new HashSet(15);
/** Extensiones de fichero asignadas a cada uno de los mimetypes. */
private static final Map FILE_EXTENSIONS = new HashMap();
static {
// MimeTypes reconocidos del formato OOXML
OOXML_MIMETYPES.add("application/vnd.ms-word.document.macroEnabled.12");
OOXML_MIMETYPES.add("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
OOXML_MIMETYPES.add("application/vnd.ms-word.template.macroEnabled.12");
OOXML_MIMETYPES.add("application/vnd.openxmlformats-officedocument.wordprocessingml.template");
OOXML_MIMETYPES.add("application/vnd.ms-powerpoint.template.macroEnabled.12");
OOXML_MIMETYPES.add("application/vnd.openxmlformats-officedocument.presentationml.template");
OOXML_MIMETYPES.add("application/vnd.ms-powerpoint.addin.macroEnabled.12");
OOXML_MIMETYPES.add("application/vnd.ms-powerpoint.slideshow.macroEnabled.12");
OOXML_MIMETYPES.add("application/vnd.openxmlformats-officedocument.presentationml.slideshow");
OOXML_MIMETYPES.add("application/vnd.ms-powerpoint.presentation.macroEnabled.12");
OOXML_MIMETYPES.add("application/vnd.openxmlformats-officedocument.presentationml.presentation");
OOXML_MIMETYPES.add("application/vnd.ms-excel.addin.macroEnabled.12");
OOXML_MIMETYPES.add("application/vnd.ms-excel.sheet.binary.macroEnabled.12");
OOXML_MIMETYPES.add("application/vnd.ms-excel.sheet.macroEnabled.12");
OOXML_MIMETYPES.add("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
OOXML_MIMETYPES.add("application/vnd.ms-excel.template.macroEnabled.12");
OOXML_MIMETYPES.add("application/vnd.openxmlformats-officedocument.spreadsheetml.template");
// MimeTypes reconocidos del formato ODF
ODF_MIMETYPES.add("application/vnd.oasis.opendocument.text");
ODF_MIMETYPES.add("application/vnd.oasis.opendocument.text-template");
ODF_MIMETYPES.add("application/vnd.oasis.opendocument.text-web");
ODF_MIMETYPES.add("application/vnd.oasis.opendocument.text-master");
ODF_MIMETYPES.add("application/vnd.oasis.opendocument.graphics");
ODF_MIMETYPES.add("application/vnd.oasis.opendocument.graphics-template");
ODF_MIMETYPES.add("application/vnd.oasis.opendocument.presentation");
ODF_MIMETYPES.add("application/vnd.oasis.opendocument.presentation-template");
ODF_MIMETYPES.add("application/vnd.oasis.opendocument.spreadsheet");
ODF_MIMETYPES.add("application/vnd.oasis.opendocument.spreadsheet-template");
ODF_MIMETYPES.add("application/vnd.oasis.opendocument.chart");
ODF_MIMETYPES.add("application/vnd.oasis.opendocument.formula");
ODF_MIMETYPES.add("application/vnd.oasis.opendocument.database");
ODF_MIMETYPES.add("application/vnd.oasis.opendocument.image");
ODF_MIMETYPES.add("application/vnd.openofficeorg.extension");
// Extensiones de fichero
FILE_EXTENSIONS.put("application/zip", "zip");
FILE_EXTENSIONS.put("application/vnd.openxmlformats-officedocument.wordprocessingml.document", "docx");
FILE_EXTENSIONS.put("application/vnd.openxmlformats-officedocument.presentationml.presentation", "pptx");
FILE_EXTENSIONS.put("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "xlsx");
FILE_EXTENSIONS.put("application/vnd.oasis.opendocument.text", "odt");
FILE_EXTENSIONS.put("application/vnd.oasis.opendocument.presentation", "odp");
FILE_EXTENSIONS.put("application/vnd.oasis.opendocument.spreadsheet", "ods");
FILE_EXTENSIONS.put("application/vnd.oasis.opendocument.graphics", "odg");
FILE_EXTENSIONS.put("application/vnd.oasis.opendocument.chart", "odc");
FILE_EXTENSIONS.put("application/vnd.oasis.opendocument.formula", "odf");
FILE_EXTENSIONS.put("application/vnd.oasis.opendocument.database", "odb");
FILE_EXTENSIONS.put("application/vnd.oasis.opendocument.image", "odi");
FILE_EXTENSIONS.put("application/vnd.oasis.opendocument.text-master", "odm");
}
private static final Logger logger = Logger.getLogger(OfficeAnalizer.class.getName());
/**
* Devuelve el MimeType correspondiente al documento ofimático
* proporcionado (ODF u OOXML). Si el fichero no se corresponde con ninguno de
* ellos pero es un Zip se devolverá el MimeType del Zip
* (application/zip) y si no es Zip se devolverá {@code null}.
*
* @param data
* Fichero ODF, OOXML o Microsoft Office 97/2003
* @return MimeType.
* @throws IOException
* Si no se puede leer el fichero
*/
static String getMimeType(final byte[] data) throws IOException {
ZipFile zipFile = null;
try {
zipFile = FileUtils.createTempZipFile(data);
String mimetype = ZIP_MIMETYPE;
String tempMimetype = null;
if (isODFFile(zipFile)) {
tempMimetype = getODFMimeType(zipFile.getInputStream(zipFile.getEntry("mimetype")));
} else if (isOOXMLFile(zipFile)) {
tempMimetype = getOOXMLMimeType(zipFile.getInputStream(zipFile.getEntry("[Content_Types].xml")));
} else {
tempMimetype = getMimeTypeOffice97(data);
}
if (tempMimetype != null) {
mimetype = tempMimetype;
}
return mimetype;
} catch (final ZipException e1) {
logger.warning("El fichero indicado no es un ZIP: " + e1);
} finally {
if (zipFile != null) {
zipFile.close();
}
}
final String retVal = getMimeTypeOffice97(data);
if (retVal != null) {
return retVal;
}
return "application/octect-stream";
}
private static String getMimeTypeOffice97(final byte[] data) {
// Comprobamos si se trata de un documento de Office 97-2003 con una
// estructura zip interna
final String testString = new String(data);
if (testString.contains("Microsoft Excel")) {
return "application/vnd.ms-excel";
}
if (testString.contains("Microsoft Office Word")) {
return "application/msword";
}
if (testString.contains("Microsoft Office PowerPoint")) {
return "application/vnd.ms-powerpoint";
}
if (testString.contains("Microsoft Project")) {
return "application/vnd.ms-project";
}
if (testString.contains("Microsoft Visio")) {
return "application/vnd.visio";
}
return null;
}
/**
* Devuelve la extensión correspondiente al documento ofimático
* proporcionado (ODF u OOXML). Si el fichero no se corresponde con ninguno de
* ellos pero es un Zip se devolverá la extensión "zip" y si no es
* Zip se devolverá {@code null}.
*
* @param zipData
* Fichero ODF u OOXML
* @return Extensión.
* @throws IOException
* Cuando ocurre algún error en la lectura de los datos.
*/
static String getExtension(final byte[] zipData) throws IOException {
final String mimetype = getMimeType(zipData);
if (mimetype == null) {
return null;
}
return FILE_EXTENSIONS.get(mimetype);
}
/**
* Indica si un fichero tiene la estructura de un documento OOXML.
*
* @param document
* Fichero a analizar
* @return Devuelve true
si el fichero era un OOXML,
* false
en caso contrario.
* @throws IOException
* SI ocurren problemas leyendo el fichero
*/
public static boolean isOOXMLDocument(final byte[] document) throws IOException {
final ZipFile zipFile = FileUtils.createTempZipFile(document);
final boolean ret = isOOXMLFile(zipFile);
zipFile.close();
return ret;
}
/**
* Indica si un fichero Zip tiene la estructura de un documento OOXML soportado.
*
* @param zipFile
* Fichero zip que deseamos comprobar.
* @return Devuelve true
si el fichero era un OOXML soportado,
* false
en caso contrario.
*/
private static boolean isOOXMLFile(final ZipFile zipFile) {
// Comprobamos si estan todos los ficheros principales del documento
return zipFile.getEntry("[Content_Types].xml") != null && zipFile.getEntry("_rels/.rels") != null
&& zipFile.getEntry("docProps/app.xml") != null && zipFile.getEntry("docProps/core.xml") != null;
}
/**
* Recupera el MimeType del XML "[Content_Type].xml" de un OOXML. Si el
* documento no es correcto o no se reconoce el Mimetype se devuelve null.
*
* @param contentTypeIs
* XML "[Content_Type].xml".
* @return Devuelve el MimeType del OOXML o, si no es un OOXML reconocido,
* devuelve {@code null}.
*/
public static String getOOXMLMimeType(final InputStream contentTypeIs) {
final Document doc;
try {
doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(contentTypeIs);
} catch (final Exception e) {
return null;
}
// Obtenemos la raiz
final Element root = doc.getDocumentElement();
if (!root.getNodeName().equalsIgnoreCase("Types")) {
return null;
}
Node node = null;
final NodeList nodes = root.getChildNodes();
for (int i = 0; i < nodes.getLength(); i++) {
node = nodes.item(i);
if (node.getNodeName().equalsIgnoreCase("Override")) {
final NamedNodeMap nodeAttributes = node.getAttributes();
Node nodeAttribute = null;
for (int j = 0; j < nodeAttributes.getLength(); j++) {
if (nodeAttributes.item(j).getNodeName().equalsIgnoreCase("ContentType")) {
nodeAttribute = nodeAttributes.item(j);
break;
}
}
if (nodeAttribute != null) {
String value = nodeAttribute.getNodeValue();
if (value.indexOf('.') != -1) {
value = value.substring(0, value.lastIndexOf('.'));
}
if (OOXML_MIMETYPES.contains(value)) {
return value;
}
}
}
}
return null;
}
/**
* Indica si un fichero tiene la estructura de un documento ODF.
*
* @param document
* Fichero a analizar
* @return Devuelve true
si el fichero era un ODF,
* false
en caso contrario.
* @throws IOException
* Si ocurren problemas leyendo el fichero
*/
public static boolean isODFDocument(final byte[] document) throws IOException {
final ZipFile zipFile = FileUtils.createTempZipFile(document);
final boolean ret = isODFFile(zipFile);
zipFile.close();
return ret;
}
/**
* Indica si un fichero Zip tiene la estructura de un documento ODF soportado.
*
* @param zipFile
* Fichero zip que deseamos comprobar.
* @return Devuelve true
si el fichero era un ODF soportado,
* false
en caso contrario.
*/
private static boolean isODFFile(final ZipFile zipFile) {
// Comprobamos si estan todos los ficheros principales del documento
// Se separan las comprobaciones en varios if para no tener una sola
// sentencia condicional muy larga
if (zipFile.getEntry("mimetype") == null) {
return false;
}
if (zipFile.getEntry("content.xml") == null) {
return false;
}
if (zipFile.getEntry("meta.xml") == null) {
return false;
}
if (zipFile.getEntry("settings.xml") == null) {
return false;
}
if (zipFile.getEntry("styles.xml") == null) {
return false;
}
if (zipFile.getEntry("META-INF/manifest.xml") == null) {
return false;
}
return true;
}
/**
* Recupera la extensión apropiada para un documento ODF. Si el fichero
* no era un documento ODF soportado, se devolverá null
.
*
* @param contentTypeIs
* Fichero del que deseamos obtener la extensión.
* @return Extensión del documento.
*/
private static String getODFMimeType(final InputStream contentTypeIs) {
final String contentTypeData;
try {
contentTypeData = new String(Utils.getDataFromInputStream(contentTypeIs));
} catch (final Exception e) {
return null;
}
if (ODF_MIMETYPES.contains(contentTypeData)) {
return contentTypeData;
}
return null;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy