
com.day.cq.dam.handler.standard.ooxml.MSPowerPointOOXMLHandler Maven / Gradle / Ivy
/*************************************************************************
*
* ADOBE CONFIDENTIAL
* ___________________
*
* Copyright 2013 Adobe Systems Incorporated
* All Rights Reserved.
*
* NOTICE: All information contained herein is, and remains
* the property of Adobe Systems Incorporated and its suppliers,
* if any. The intellectual and technical concepts contained
* herein are proprietary to Adobe Systems Incorporated and its
* suppliers and are protected by trade secret or copyright law.
* Dissemination of this information or reproduction of this material
* is strictly forbidden unless prior written permission is obtained
* from Adobe Systems Incorporated.
**************************************************************************/
package com.day.cq.dam.handler.standard.ooxml;
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.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.concurrent.Callable;
import java.util.Collection;
import java.util.ArrayList;
import java.util.List;
import java.util.Date;
import java.util.Map;
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.POIXMLProperties;
import org.apache.poi.xslf.usermodel.XSLFTheme;
import org.apache.poi.xslf.usermodel.XSLFNotes;
import org.apache.poi.xslf.usermodel.XMLSlideShow;
import org.apache.poi.xslf.usermodel.XSLFSlide;
import org.apache.poi.xslf.usermodel.XSLFSlideLayout;
import org.apache.poi.xslf.usermodel.XSLFSlideMaster;
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;
/**
* Asset Handler for Open XML PPTX files, handles sub assets for PPTX as well.
*
*/
@Component(inherit = true, metatype = false)
@Service
public class MSPowerPointOOXMLHandler extends OpenOfficeHandler {
private static final String SUB_ASSET_PREFIX = "slide_";
private static final String PPTX_EXT = ".pptx";
private static final Logger log = LoggerFactory.getLogger(MSPowerPointOOXMLHandler.class);
public static final String MIMETYPE_PPTX
= "application/vnd.openxmlformats-officedocument.presentationml.presentation";
public static final String DEFAULT_PAGES_REGEX = "^slide_[0-9]*.pptx";
@Property(name = "cq.dam.pptx.pages.regex", value= DEFAULT_PAGES_REGEX, label = "pptx 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.pptx.pages.regex";
private String pagesRegex;
@Override
public boolean canHandleSubAssets() {
return true;
}
/**
* {@inheritDoc}
*/
@Override
public String[] getMimeTypes() {
return new String[]{MIMETYPE_PPTX};
}
private void removeSubAssets(final Asset asset, AssetManager assetManager) {
//cleanup subassts
Collection subAssets = asset.getSubAssets();
for (Asset subAsset:subAssets) {
assetManager.removeAsset(subAsset.getPath());
}
}
/**
* {@inheritDoc}
*/
@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);
XMLSlideShow slideShow = new XMLSlideShow(is);
int i=0;
for (XSLFSlide srcSlide : slideShow.getSlides()) {
int slideNumber = ++i;
XMLSlideShow extractedSlide = extractSlideByIndex(slideShow, asset, slideNumber);
FileOutputStream itout = null;
File pptTmpFile = null;
InputStream iis = null;
try {
pptTmpFile = File.createTempFile("pptx", ".tmp");
itout = FileUtils.openOutputStream(pptTmpFile);
extractedSlide.write(itout);
String fileName = SUB_ASSET_PREFIX + slideNumber + PPTX_EXT;
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 pull each slide from source PPTX and then save it in a new pptx.
* This process must be repeated for each slide that we want to extract.
*
* extractSlideByIndex extracts a slide, given the slideNumber (starts at 1)
*/
private XMLSlideShow extractSlideByIndex(final XMLSlideShow slideShow, final Asset asset, int slideNumber) {
XMLSlideShow slideShowNew = new XMLSlideShow();
try {
slideShowNew.setPageSize(slideShow.getPageSize()); // required for thumbnail generation else thumbnails would be generated with default pageSize
final List slides = slideShow.getSlides();
int i=1;
for(XSLFSlide srcSlide : slides){
if(i==slideNumber) {
XSLFSlideLayout src_layout =srcSlide.getSlideLayout();
XSLFSlideMaster src_master = srcSlide.getSlideMaster();
XSLFTheme src_theme = srcSlide.getTheme();
XSLFNotes src_notes = srcSlide.getNotes();
XSLFSlide newSlide = slideShowNew.createSlide();
if (src_master != null ) newSlide.getSlideMaster().importContent(src_master);
if (src_theme != null )newSlide.getTheme().importTheme(src_theme);
if (src_layout != null ) newSlide.getSlideLayout().importContent(src_layout);
//Add notes to the slide from source slide : only available since poi 3.11
XSLFNotes newNotesSlide = slideShowNew.getNotesSlide(newSlide);
if (src_notes != null )newNotesSlide.importContent(src_notes);
try {
// Add Core Properties to new pptx subasset
POIXMLProperties src_properties = slideShow.getProperties();
if (src_properties != null) {
slideShowNew.getProperties().getCoreProperties().setTitle(SUB_ASSET_PREFIX + slideNumber + "_" + src_properties.getCoreProperties().getTitle());
slideShowNew.getProperties().getCoreProperties().setDescription(src_properties.getCoreProperties().getDescription());
slideShowNew.getProperties().getCoreProperties().setCreated(getFormattedDate(src_properties.getCoreProperties().getCreated()));
slideShowNew.getProperties().getCoreProperties().setCreator(src_properties.getCoreProperties().getCreator());
slideShowNew.getProperties().getCoreProperties().setModified(getFormattedDate(src_properties.getCoreProperties().getModified()));
slideShowNew.getProperties().getCoreProperties().setRevision(src_properties.getCoreProperties().getRevision());
slideShowNew.getProperties().getCoreProperties().setContentType(src_properties.getCoreProperties().getContentType());
slideShowNew.getProperties().getCoreProperties().setCategory(src_properties.getCoreProperties().getCategory());
slideShowNew.getProperties().getCoreProperties().setKeywords(src_properties.getCoreProperties().getKeywords());
}
}
catch (Exception e) {
log.debug("extractSlideByIndex : Error extracting core properties for slide number "+ Integer.toString(slideNumber) +" from asset "+ asset.getPath() + " reason "+ e.getMessage());
}
newSlide.importContent(srcSlide); // import the actual slide content here
break;
}
i++;
}
} catch (Exception e) {
log.warn("extractSlideByIndex : Error extracting explicit slide number "+ Integer.toString(slideNumber) +" from asset "+ asset.getPath() + " reason "+ e.getMessage());
if (log.isDebugEnabled()) {
log.debug("Stack Trace", e);
}
}
return slideShowNew;
}
@Override
public BufferedImage getImage(final Rendition rendition, final Dimension dim) throws IOException {
try {
return callWithThreadContextClassLoader(new Callable() {
public BufferedImage call() throws Exception {
return MSPowerPointOOXMLHandler.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 XMLSlideShow slideShow = new XMLSlideShow(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;
}
@Activate
private void activate(Map config) throws IOException {
pagesRegex = OsgiUtil.toString(config.get(PAGES_REGEX),
DEFAULT_PAGES_REGEX);
}
private DateFormat df = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss");
private String getFormattedDate(Date dt){
return df.format(dt);
}
// 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);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy