All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.rubrica.util.OfficeAnalizer Maven / Gradle / Ivy

The newest version!
/*
 * 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 - 2024 Weber Informatics LLC | Privacy Policy