package com.constellio.data.utils;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import org.apache.commons.io.IOUtils;
import org.apache.poi.POIXMLDocument;
import org.apache.poi.hpsf.CustomProperties;
import org.apache.poi.hpsf.DocumentSummaryInformation;
import org.apache.poi.hpsf.MarkUnsupportedException;
import org.apache.poi.hpsf.NoPropertySetStreamException;
import org.apache.poi.hpsf.PropertySet;
import org.apache.poi.hpsf.PropertySetFactory;
import org.apache.poi.hpsf.UnexpectedPropertySetTypeException;
import org.apache.poi.hpsf.WritingNotSupportedException;
import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.poifs.filesystem.DirectoryEntry;
import org.apache.poi.poifs.filesystem.DocumentEntry;
import org.apache.poi.poifs.filesystem.DocumentInputStream;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.xslf.XSLFSlideShow;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.tika.Tika;
import org.apache.tika.mime.MimeTypeException;
import org.apache.xmlbeans.XmlException;
import org.openxmlformats.schemas.officeDocument.x2006.customProperties.CTProperty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.constellio.data.io.streamFactories.StreamFactory;
import com.constellio.data.utils.OfficeDocumentsServicesException.CannotReadDocumentsProperties;
import com.constellio.data.utils.OfficeDocumentsServicesException.NotCompatibleExtension;
import com.constellio.data.utils.OfficeDocumentsServicesException.PropertyDoesntExist;
import com.constellio.data.utils.OfficeDocumentsServicesException.RTFFileIsNotCompatible;
public class OfficeDocumentsServices {
private static final Logger LOGGER = LoggerFactory.getLogger(OfficeDocumentsServices.class);
public void setProperty(StreamFactory<InputStream> inputStreamFactory, StreamFactory<OutputStream> outputStreamFactory,
String propertyName, String propertyValue, String ext)
throws NotCompatibleExtension, WritingNotSupportedException, CannotReadDocumentsProperties, IOException,
PropertyDoesntExist, MimeTypeException, RTFFileIsNotCompatible {
Tika tika = new Tika();
String mimeType = null;
InputStream inputStream = inputStreamFactory.create(getClass().getName() + ".setProperty");
try {
mimeType = tika.detect(inputStream);
} finally {
IOUtils.closeQuietly(inputStream);
}
String modifiedExtension = ext;
if ("application/x-tika-msoffice".equals(mimeType)) {
setProperty(inputStreamFactory, outputStreamFactory, propertyName, propertyValue);
} else if ("application/x-tika-ooxml".equals(mimeType) || "application/zip".equals(mimeType)) {
modifiedExtension = addXToExtension(ext);
setPropertyNewDocument(modifiedExtension, inputStreamFactory, outputStreamFactory, propertyName, propertyValue);
} else if ("application/rtf".equals(mimeType)) {
throw new OfficeDocumentsServicesException.RTFFileIsNotCompatible();
} else {
throw new OfficeDocumentsServicesException.NotCompatibleExtension(ext);
}
}
public String getProperty(StreamFactory<InputStream> inputStreamFactory, String propertyName, String ext)
throws CannotReadDocumentsProperties, PropertyDoesntExist, IOException, NotCompatibleExtension,
RTFFileIsNotCompatible {
Tika tika = new Tika();
InputStream inputStream = inputStreamFactory.create(getClass().getName() + ".getProperty");
String mimeType = null;
try {
mimeType = tika.detect(inputStream);
} finally {
IOUtils.closeQuietly(inputStream);
}
String modifiedExtension = ext;
if ("application/x-tika-msoffice".equals(mimeType)) {
return getPropertyDocument(inputStreamFactory, propertyName);
} else if ("application/x-tika-ooxml".equals(mimeType) || "application/zip".equals(mimeType)) {
modifiedExtension = addXToExtension(modifiedExtension);
return getPropertyNewDocument(modifiedExtension, inputStreamFactory, propertyName);
} else if ("application/rtf".equals(mimeType)) {
throw new OfficeDocumentsServicesException.RTFFileIsNotCompatible();
} else {
throw new OfficeDocumentsServicesException.NotCompatibleExtension(modifiedExtension);
}
}
public String getPropertyDocument(StreamFactory<InputStream> inputStreamFactory, String propertyName)
throws IOException, CannotReadDocumentsProperties, PropertyDoesntExist {
String idIGIDStr = "";
// POIFSFileSystem will close this stream
POIFSFileSystem poifs = new POIFSFileSystem(inputStreamFactory.create(getClass().getName() + ".getPropertyDocument"));
DirectoryEntry dir = poifs.getRoot();
DocumentSummaryInformation dsi = getDocumentSummaryInfo(dir);
CustomProperties customProperties = dsi.getCustomProperties();
if (customProperties != null) {
idIGIDStr = (String) customProperties.get(propertyName);
if (idIGIDStr == null) {
throw new OfficeDocumentsServicesException.PropertyDoesntExist(propertyName);
}
}
return idIGIDStr;
}
private DocumentSummaryInformation getDocumentSummaryInfo(DirectoryEntry dir)
throws CannotReadDocumentsProperties {
DocumentSummaryInformation dsi;
try {
DocumentEntry dsiEntry = (DocumentEntry) dir.getEntry(DocumentSummaryInformation.DEFAULT_STREAM_NAME);
DocumentInputStream dis = new DocumentInputStream(dsiEntry);
PropertySet ps = new PropertySet(dis);
dis.close();
dsi = new DocumentSummaryInformation(ps);
} catch (FileNotFoundException ex) {
LOGGER.debug("No summary in Office document. Creating new DocumentSummaryInformation", ex);
dsi = PropertySetFactory.newDocumentSummaryInformation();
} catch (UnexpectedPropertySetTypeException | NoPropertySetStreamException | MarkUnsupportedException | IOException e) {
throw new OfficeDocumentsServicesException.CannotReadDocumentsProperties(e);
}
return dsi;
}
public String getPropertyNewDocument(String ext, StreamFactory<InputStream> inputStreamFactory, String propertyName)
throws IOException, PropertyDoesntExist, NotCompatibleExtension, CannotReadDocumentsProperties {
POIXMLDocument doc = parseDocument(ext, inputStreamFactory, propertyName);
org.openxmlformats.schemas.officeDocument.x2006.customProperties.CTProperties props = doc.getProperties()
.getCustomProperties().getUnderlyingProperties();
List<CTProperty> properties = props.getPropertyList();
for (CTProperty prop : properties) {
if (prop.getName().equals(propertyName)) {
return prop.getLpwstr();
}
}
throw new OfficeDocumentsServicesException.PropertyDoesntExist(propertyName);
}
private POIXMLDocument parseDocument(String ext, StreamFactory<InputStream> inputStreamFactory, String propertyName)
throws IOException, CannotReadDocumentsProperties, PropertyDoesntExist, NotCompatibleExtension {
POIXMLDocument doc;
if ("docx".equalsIgnoreCase(ext)) {
doc = new XWPFDocument(inputStreamFactory.create(getClass().getName() + ".parseDocument"));
} else if ("xlsx".equalsIgnoreCase(ext)) {
doc = new XSSFWorkbook(inputStreamFactory.create(getClass().getName() + ".parseDocument"));
} else if ("pptx".equalsIgnoreCase(ext)) {
try {
OPCPackage opcpPackage = OPCPackage.open(inputStreamFactory.create(getClass().getName() + ".parseDocument"));
if (opcpPackage == null) {
throw new OfficeDocumentsServicesException.PropertyDoesntExist(propertyName);
}
doc = new XSLFSlideShow(opcpPackage);
} catch (OpenXML4JException | XmlException e) {
throw new OfficeDocumentsServicesException.CannotReadDocumentsProperties(e);
}
} else {
throw new OfficeDocumentsServicesException.NotCompatibleExtension(ext);
}
return doc;
}
public void setProperty(StreamFactory<InputStream> inputStreamFactory, StreamFactory<OutputStream> outputStreamFactory,
String propertyName, String propertyValue)
throws IOException, CannotReadDocumentsProperties, WritingNotSupportedException {
OutputStream outputStream = outputStreamFactory.create(getClass().getName() + ".setProperty");
try {
POIFSFileSystem poifs = new POIFSFileSystem(inputStreamFactory.create(getClass().getName() + ".setProperty"));
DirectoryEntry dir = poifs.getRoot();
DocumentSummaryInformation dsi = getDocumentSummaryInfo(dir);
CustomProperties customProperties = dsi.getCustomProperties();
if (customProperties == null) {
customProperties = new CustomProperties();
}
customProperties.put(propertyName, propertyValue);
dsi.setCustomProperties(customProperties);
dsi.write(dir, DocumentSummaryInformation.DEFAULT_STREAM_NAME);
poifs.writeFilesystem(outputStream);
} finally {
IOUtils.closeQuietly(outputStream);
}
}
public void setPropertyNewDocument(String ext, StreamFactory<InputStream> inputStreamFactory,
StreamFactory<OutputStream> outputStreamFactory, String propertyName, String propertyValue)
throws IOException, PropertyDoesntExist, NotCompatibleExtension, CannotReadDocumentsProperties {
POIXMLDocument doc = parseDocument(ext, inputStreamFactory, propertyName);
org.apache.poi.POIXMLProperties.CustomProperties customProperties = doc.getProperties().getCustomProperties();
int index = 0;
for (CTProperty prop : customProperties.getUnderlyingProperties().getPropertyList()) {
if (prop.getName().equals(propertyName)) {
customProperties.getUnderlyingProperties().removeProperty(index);
}
index++;
}
customProperties.addProperty(propertyName, propertyValue);
doc.write(outputStreamFactory.create(getClass().getName() + ".setPropertyNewDocument"));
}
private String addXToExtension(String ext) {
String modifiedExtension = ext;
if ("xls".equals(ext) || "ppt".equals(ext) || "doc".equals(ext)) {
modifiedExtension = ext.replace("xls", "xlsx").replace("doc", "docx").replace("ppt", "pptx");
}
return modifiedExtension;
}
}