ddf.catalog.transformer.input.pptx.PptxInputTransformer.java Source code

Java tutorial

Introduction

Here is the source code for ddf.catalog.transformer.input.pptx.PptxInputTransformer.java

Source

/**
 * Copyright (c) Codice Foundation
 * <p/>
 * This 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 any later version.
 * <p/>
 * 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. A copy of the GNU Lesser General Public License
 * is distributed along with this program and can be found at
 * <http://www.gnu.org/licenses/lgpl.html>.
 */
package ddf.catalog.transformer.input.pptx;

import static org.apache.commons.lang3.Validate.notNull;

import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

import org.apache.commons.io.IOUtils;
import org.apache.pdfbox.tools.imageio.ImageIOUtil;
import org.apache.poi.sl.usermodel.SlideShow;
import org.apache.poi.sl.usermodel.SlideShowFactory;
import org.apache.poi.xslf.usermodel.XMLSlideShow;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.io.FileBackedOutputStream;

import ddf.catalog.data.Metacard;
import ddf.catalog.data.impl.AttributeImpl;
import ddf.catalog.transform.CatalogTransformerException;
import ddf.catalog.transform.InputTransformer;

/**
 * This is a decorator class that adds a thumbnail for PPTX files. It relies on
 * an injected {@link InputTransformer} to extract the metadata, and then it generates a
 * thumbnail image with Apache POI.
 */
public class PptxInputTransformer implements InputTransformer {

    private static final Logger LOGGER = LoggerFactory.getLogger(PptxInputTransformer.class);

    private static final int RESOLUTION_DPI = 44;

    private static final float IMAGE_QUALITY = 1.0f;

    private static final float IMAGE_HEIGHTWIDTH = 128;

    private static final String FORMAT_NAME = "jpg";

    private static final int FILE_BACKED_THRESHOLD = 1000000;

    private final InputTransformer inputTransformer;

    /**
     * The inputTransformer parameter will be used to generate the basic metadata. If the parameter
     * is null, then a {@link NullPointerException} will be thrown.
     *
     * @param inputTransformer must be non-null
     * @throws NullPointerException
     */
    public PptxInputTransformer(InputTransformer inputTransformer) {

        notNull(inputTransformer, "The inputTransformer parameter must be non-null");

        this.inputTransformer = inputTransformer;
    }

    @Override
    public Metacard transform(InputStream input) throws IOException, CatalogTransformerException {
        return transform(input, null);
    }

    @Override
    public Metacard transform(InputStream input, String id) throws IOException, CatalogTransformerException {

        if (input == null) {
            throw new CatalogTransformerException("Cannot transform null input.");
        }

        return transformLogic(input, id);
    }

    /**
     * This is a three step process. First, create a FileBackedOutputStream because we need to
     * consume the stream twice. Once for the injected inputTransformer and once for Apache POI.
     * Next, extract the metadata with the injected input transformer. And last, use Apache POI
     * to create the thumbnail.
     *
     * @param input
     * @param id
     * @return
     * @throws IOException
     * @throws CatalogTransformerException
     */
    private Metacard transformLogic(InputStream input, String id) throws IOException, CatalogTransformerException {

        try (FileBackedOutputStream fileBackedOutputStream = new FileBackedOutputStream(FILE_BACKED_THRESHOLD)) {
            try {
                int c = IOUtils.copy(input, fileBackedOutputStream);
                LOGGER.debug("copied {} bytes from input stream to file backed output stream", c);
            } catch (IOException e) {
                throw new CatalogTransformerException("Could not copy bytes of content message.", e);
            }

            Metacard metacard = extractInitialMetadata(fileBackedOutputStream.asByteSource().openStream());

            extractThumbnail(metacard, fileBackedOutputStream.asByteSource().openStream());

            return metacard;
        }

    }

    /**
     * Extract the initial metadata using the injected input transformer.
     *
     * @param input
     * @return
     * @throws IOException
     * @throws CatalogTransformerException
     */
    private Metacard extractInitialMetadata(InputStream input) throws IOException, CatalogTransformerException {
        return inputTransformer.transform(input);
    }

    /**
     * SlideShowFactory.create() will perform the tests for password protected files.
     * <p/>
     * Because Apache POI dynamically loads the classes needed to handle a PPTX file, the default
     * class loader is unable to find the dependencies during runtime. Therefore, the original class
     * loader is saved, then current class loader is set to this class's class loader, and finally
     * the original class loader is restored.
     *
     * @param metacard
     * @param input
     * @throws IOException
     */
    private void extractThumbnail(Metacard metacard, InputStream input) throws IOException {

        ClassLoader originalContextClassLoader = Thread.currentThread().getContextClassLoader();
        try {
            Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
            SlideShow<?, ?> genericSlideShow = SlideShowFactory.create(input);

            if (genericSlideShow instanceof XMLSlideShow) {
                XMLSlideShow xmlSlideShow = (XMLSlideShow) genericSlideShow;

                byte[] thumbnail = generatePptxThumbnail(xmlSlideShow);
                if (thumbnail != null) {
                    metacard.setAttribute(new AttributeImpl(Metacard.THUMBNAIL, thumbnail));
                }

            } else {
                LOGGER.debug("Cannot transform old style (OLE2) ppt : id = {}", metacard.getId());
            }

        } finally {
            Thread.currentThread().setContextClassLoader(originalContextClassLoader);
        }
    }

    /**
     * If the slide show doesn't contain any slides, then return null. Otherwise, return jpeg
     * image data of the first slide in the deck.
     *
     * @param slideShow
     * @return jpeg thumbnail or null if thumbnail can't be created
     * @throws IOException
     */
    private byte[] generatePptxThumbnail(XMLSlideShow slideShow) throws IOException {

        if (slideShow.getSlides().isEmpty()) {
            LOGGER.info("the powerpoint file does not contain any slides, skipping thumbnail generation");
            return null;
        }

        Dimension pgsize = slideShow.getPageSize();

        int largestDimension = (int) Math.max(pgsize.getHeight(), pgsize.getWidth());
        float scalingFactor = IMAGE_HEIGHTWIDTH / largestDimension;
        int scaledHeight = (int) (pgsize.getHeight() * scalingFactor);
        int scaledWidth = (int) (pgsize.getWidth() * scalingFactor);
        BufferedImage img = new BufferedImage(scaledWidth, scaledHeight, BufferedImage.TYPE_INT_RGB);

        Graphics2D graphics = img.createGraphics();

        try {
            graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            graphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
            graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
            graphics.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,
                    RenderingHints.VALUE_FRACTIONALMETRICS_ON);

            graphics.scale(scalingFactor, scalingFactor);

            slideShow.getSlides().get(0).draw(graphics);

            try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
                ImageIOUtil.writeImage(img, FORMAT_NAME, outputStream, RESOLUTION_DPI, IMAGE_QUALITY);
                return outputStream.toByteArray();
            }
        } catch (RuntimeException e) {
            if (e.getCause() instanceof javax.imageio.IIOException) {
                LOGGER.warn("unable to generate thumbnail for PPTX file", e);
            } else {
                throw e;
            }
        } finally {
            graphics.dispose();
        }

        return null;
    }

}