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

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;
		try {
			slideShow = new HSLFSlideShow (asset.getOriginal().getStream());
			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);
            }
		}
		
		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