
com.day.cq.dam.handler.standard.msoffice.MSPowerPointHandler Maven / Gradle / Ivy
/*
* Copyright 1997-2011 Day Management AG
* Barfuesserplatz 6, 4001 Basel, Switzerland
* All Rights Reserved.
*
* This software is the confidential and proprietary information of
* Day Management AG, ("Confidential Information"). You shall not
* disclose such Confidential Information and shall use it only in
* accordance with the terms of the license agreement you entered into
* with Day.
*/
package com.day.cq.dam.handler.standard.msoffice;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import javax.jcr.Node;
import javax.jcr.PathNotFoundException;
import javax.jcr.RepositoryException;
import com.adobe.granite.asset.api.AssetException;
import com.day.cq.commons.jcr.JcrConstants;
import com.day.cq.dam.api.DamConstants;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Service;
import org.apache.poi.hslf.usermodel.HSLFSlide ;
import org.apache.poi.hslf.usermodel.HSLFSlideShow ;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.commons.osgi.OsgiUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.adobe.granite.asset.api.AssetManager;
import com.day.cq.dam.api.Asset;
import com.day.cq.dam.api.Rendition;
import com.day.image.Layer;
/**
* PowerPoint documents require special handling to generate better quality preview image
* */
@Component(inherit = true, metatype = false)
@Service
public class MSPowerPointHandler extends MSOfficeHandler {
private static final Logger log = LoggerFactory.getLogger(MSPowerPointHandler.class);
public static final String MIMETYPE_MSPOWERPOINT = "application/mspowerpoint";
public static final String MIMETYPE_MSPOWERPOINT_VND = "application/vnd.ms-powerpoint";
public static final String[] MIMETYPES_SUPPORTED = new String[] {
MIMETYPE_MSPOWERPOINT,
MIMETYPE_MSPOWERPOINT_VND};
public static final String DEFAULT_PAGES_REGEX = "^slide_[0-9]*.ppt";
@Property(name = "cq.dam.ppt.pages.regex", value= DEFAULT_PAGES_REGEX, label = "ppt pages regex", description = "Regex for identifying pages in subassets folder. The pages will be shown in assets page viewer")
public static final String PAGES_REGEX = "cq.dam.ppt.pages.regex";
private String pagesRegex;
@Override
public String[] getMimeTypes() {
return MIMETYPES_SUPPORTED;
}
@Override
public BufferedImage getImage(final Rendition rendition, final Dimension dim) throws IOException {
try {
return callWithThreadContextClassLoader(new Callable() {
public BufferedImage call() throws Exception {
return MSPowerPointHandler.this.dogetImage(rendition, dim);
}
});
} catch (Exception e) {
log.error("getImage: Cannot read image from {}: {}", rendition.getPath(), e.getMessage());
}
return null;
}
public BufferedImage dogetImage(final Rendition rendition, Dimension dim) throws IOException {
final InputStream is = rendition.getStream();
final HSLFSlideShow slideShow = new HSLFSlideShow (is);
final List slides= slideShow.getSlides();
if (slides != null && slides.size() >0) {
// we need to override the dimension to match slide dimension
dim = slideShow.getPageSize();
try {
BufferedImage image = new BufferedImage(dim.width, dim.height, BufferedImage.TYPE_INT_RGB);
Graphics2D graphics = image.createGraphics();
slides.get(0).draw(graphics); // draw first slide as a thumbnail
return new Layer(image).getImage();
} catch (Exception e) {
log.warn("getImage: error while getting image for {} reason: {}", rendition.getPath(), e.getMessage());
if (log.isDebugEnabled()) {
log.debug("Stack Trace", e);
}
} finally {
IOUtils.closeQuietly(is);
}
}
IOUtils.closeQuietly(is);
return null;
}
@Override
public boolean canHandleSubAssets() {
return true;
}
private void removeSubAssets(final Asset asset, AssetManager assetManager) {
//cleanup subassts
Collection subAssets = asset.getSubAssets();
for (Asset subAsset:subAssets) {
assetManager.removeAsset(subAsset.getPath());
}
}
@Override
public List processSubAssets(final Asset asset) {
List subAssets = new ArrayList();
if (asset.isSubAsset()) {
// we do not continue processing here, otherwise we would enter an
// endless processing stack
return subAssets;
}
InputStream is = null;
try {
is = asset.getOriginal().getStream();
// changes made to the asset are not saved until manually later on.
final boolean oldBatchMode = asset.isBatchMode();
asset.setBatchMode(true);
AssetManager assetManager = asset.getOriginal().getResourceResolver().adaptTo(AssetManager.class);
removeSubAssets(asset, assetManager);
HSLFSlideShow slideShow = new HSLFSlideShow (is);
int i=0;
for (HSLFSlide srcSlide : slideShow.getSlides()) {
int slideNumber = ++i;
HSLFSlideShow extractedSlide = extractSlide(asset, slideNumber);
FileOutputStream itout = null;
File pptTmpFile = null;
InputStream iis = null;
try {
pptTmpFile = File.createTempFile("ppt", ".tmp");
itout = FileUtils.openOutputStream(pptTmpFile);
extractedSlide.write(itout);
String fileName = "slide_" + slideNumber + ".ppt";
iis = FileUtils.openInputStream(pptTmpFile);
Asset subAsset = asset.addSubAsset(fileName, asset.getMimeType(), iis);
subAssets.add(subAsset.getPath());
} catch (IOException e) {
log.warn("error extracting subassets from asset {0} reason {1}", asset.getPath(), e.getMessage());
if (log.isDebugEnabled()) {
log.debug("Stack Trace", e);
}
} finally {
IOUtils.closeQuietly(iis);
IOUtils.closeQuietly(itout);
FileUtils.deleteQuietly(pptTmpFile);
}
}
// now save the changes made to the asset.
asset.adaptTo(Node.class).getSession().save();
asset.setBatchMode(oldBatchMode);
} catch (IOException e) {
log.warn("error parsing asset {0} reason {1}", asset.getPath(), e.getMessage());
if (log.isDebugEnabled()) {
log.debug("Stack Trace", e);
}
} catch (RepositoryException e) {
log.warn("error parsing asset {0} reason {1}", asset.getPath(), e.getMessage());
if (log.isDebugEnabled()) {
log.debug("Stack Trace", e);
}
} finally {
IOUtils.closeQuietly(is);
}
cleanup(asset);
updatePageRelations(asset);
return subAssets;
}
private void cleanup(final Asset asset) {
com.adobe.granite.asset.api.AssetManager assetManager = asset.getOriginal().getResourceResolver().adaptTo(
com.adobe.granite.asset.api.AssetManager.class);
// clean up pages relation
com.adobe.granite.asset.api.Asset graniteAsset = asset.adaptTo(com.adobe.granite.asset.api.Asset.class);
try {
graniteAsset.removeRelation(DamConstants.RELATION_ASSET_PAGES);
} catch (AssetException ae) {
log.debug("Exception occured while deleting "
+ DamConstants.RELATION_ASSET_PAGES + " relation", ae);
}
}
/**
* creates pages relation for the asset with the subassets that matches
* pagesRegex
*
* @param asset
*/
private void updatePageRelations(Asset asset) {
Collection subAssets = asset.getSubAssets();
int numPages = 0;
if (!subAssets.isEmpty()) {
com.adobe.granite.asset.api.Asset graniteAsset = asset.adaptTo(com.adobe.granite.asset.api.Asset.class);
for (Asset subAsset : subAssets) {
if (subAsset.getName().matches(pagesRegex)) {
graniteAsset.addRelation(DamConstants.RELATION_ASSET_PAGES,
subAsset.getPath());
numPages++;
}
}
if (numPages > 0) {
ResourceResolver resolver = graniteAsset.getResourceResolver();
Node assetNode = resolver.getResource(asset.getPath()).adaptTo(
Node.class);
try {
Node metadataNode = assetNode.getNode(JcrConstants.JCR_CONTENT
+ "/" + DamConstants.METADATA_FOLDER);
metadataNode.setProperty("dam:numPages", numPages);
} catch (PathNotFoundException e) {
log.warn("Unable to set dam:numPages on " + asset.getPath());
log.debug("Exception while setting dam:numPages on "
+ asset.getPath(), e);
} catch (RepositoryException e) {
log.warn("Unable to set dam:numPages on " + asset.getPath());
log.debug("Exception while setting dam:numPages on "
+ asset.getPath(), e);
}
}
}
}
/**
* To extract each slide, we need to delete not required slide and then save.
* This process must be repeated for each slide that we want to extract.
*
* extractSlide extracts a slide, given the slideNumber (starts at 1)
*/
private HSLFSlideShow extractSlide(final Asset asset, int slideNumber) {
HSLFSlideShow slideShow = null;
InputStream is = null;
try {
is = asset.getOriginal().getStream();
slideShow = new HSLFSlideShow (is);
final List slides = slideShow.getSlides();
//removeslide shifts the slide numbers.
//following code makes the requested slide as the first slide i.e. at index 0
for (int i = 0; i < slideNumber - 1; i++) {
slideShow.removeSlide(0);
}
// delete all other slides. To delete slide, you need to keep deleting slide at index 1
// until it finishes as the slide number keeps shifting with each delete
for (int i = slideNumber; i < slides.size(); i++) {
slideShow.removeSlide(1);
}
} catch (IOException e) {
log.warn("error extracting explicit slide from asset {0} reason {1}", asset.getPath(), e.getMessage());
if (log.isDebugEnabled()) {
log.debug("Stack Trace", e);
}
} finally {
IOUtils.closeQuietly(is);
}
return slideShow;
}
// Check:- http://stackoverflow.com/questions/1043109/why-cant-jaxb-find-my-jaxb-index-when-running-inside-apache-felix
private T callWithThreadContextClassLoader(Callable callable) throws Exception {
ClassLoader old = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(org.apache.poi.sl.draw.binding.ObjectFactory.class.getClassLoader());
try {
return callable.call();
} finally {
Thread.currentThread().setContextClassLoader(old);
}
}
@Activate
private void activate(Map config) throws IOException {
pagesRegex = OsgiUtil.toString(config.get(PAGES_REGEX),
DEFAULT_PAGES_REGEX);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy