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

com.day.cq.dam.handler.standard.psd.PsdHandler Maven / Gradle / Ivy

/*
 * Copyright 1997-2009 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.psd;

import com.adobe.granite.asset.api.AssetRelation;
import com.adobe.xmp.core.XMPMetadata;
import com.adobe.xmp.core.serializer.RDFXMLSerializer;
import com.adobe.xmp.core.serializer.RDFXMLSerializerContext;
import com.day.cq.dam.api.Asset;
import com.day.cq.dam.api.AssetReferenceResolver;
import com.day.cq.dam.api.DamConstants;
import com.day.cq.dam.api.Rendition;
import com.day.cq.dam.api.handler.xmp.XMPHandler;
import com.day.cq.dam.api.metadata.ExtractedMetadata;
import com.day.cq.dam.commons.handler.AbstractAssetHandler;
import com.day.image.Layer;
import com.twelvemonkeys.imageio.plugins.psd.PSDImageReader;
import com.twelvemonkeys.imageio.plugins.psd.PSDImageReaderSpi;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.felix.scr.annotations.Service;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferencePolicy;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Activate;
import org.apache.sling.commons.osgi.PropertiesUtil;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.stream.FileCacheImageInputStream;
import javax.imageio.stream.ImageInputStream;
import javax.jcr.RepositoryException;

import java.awt.Dimension;
import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.io.IOException;
import java.io.InputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.ByteArrayInputStream;
import java.util.Iterator;

import static com.day.cq.dam.api.DamConstants.TIFF_IMAGELENGTH;
import static com.day.cq.dam.api.DamConstants.TIFF_IMAGEWIDTH;

/**
 * Handler for Photoshop documents.
 */
@Component(inherit = true, metatype = true)
@Service
public class PsdHandler extends AbstractAssetHandler {

    /**
     * The default logger
     */
    private static final Logger log = LoggerFactory.getLogger(PsdHandler.class);
    
    private static final ImageReaderSpi provider = new PSDImageReaderSpi();

    public static final String CONFIG_LARGE_FILE_THRESHOLD = "large_file_threshold";
    private static final long DEFAULT_LARGE_FILE_THRESHOLD = 500L;

    @Property(longValue = DEFAULT_LARGE_FILE_THRESHOLD, name = CONFIG_LARGE_FILE_THRESHOLD,
            label = "Threshold size in MB to use intermediate temporary file",
            description = "Asset size greater than threshold use temporary file instead of memory buffer to avoid OutOfMemoryError." +
                    " Value of -1 means that the use of temporary file is disabled.")
    private long largeFileThreshold = DEFAULT_LARGE_FILE_THRESHOLD;

    /**
     * Mime type
     */
    public static final String PHOTOSHOP_MIMETYPE_1 = "application/x-photoshop";
    public static final String PHOTOSHOP_MIMETYPE_2 = "application/photoshop";
    public static final String PHOTOSHOP_MIMETYPE_3 = "image/vnd.adobe.photoshop";
    public static final String PHOTOSHOP_MIMETYPE_4 = "application/vnd.adobe.photoshop";
    public static final String PHOTOSHOP_MIMETYPE_5 = "application/vnd.3gpp.pic-bw-small";

    @Reference(policy = ReferencePolicy.STATIC)
    private AssetReferenceResolver refResolver;

    @Reference(policy = ReferencePolicy.DYNAMIC, cardinality = ReferenceCardinality.OPTIONAL_UNARY)
    protected XMPHandler xmpHandler;

    synchronized void bindXmpHandler(final XMPHandler handler) {
        xmpHandler = handler;
        log.debug("binding xmp handler");
    }

    synchronized void unbindXmpHandler(final XMPHandler handler) {
        xmpHandler = null;
        log.debug("un-binding xmp handler");
    }


    public BufferedImage getImage(final Rendition rendition) throws IOException {
        return getImage(rendition, null);
    }

    public BufferedImage getImage(final Rendition rendition, Dimension dim) throws IOException {

        final InputStream is = rendition.getStream();
        ImageInputStream stream = null;
        boolean debug = log.isDebugEnabled();

        try {
            PSDImageReader reader = new PSDImageReader(provider);
            if (doFileBuffering(rendition)) {
                stream = new FileCacheImageInputStream(is, null);
                log.debug("Used FileCacheInputImageStream for rendition size [{}]", rendition.getSize());
            } else {
                stream = ImageIO.createImageInputStream(is);
            }
            reader.setInput(stream);
            
            long start = System.currentTimeMillis();
             
            BufferedImage image = null;
            ImageReadParam param = new ImageReadParam();
            image = reader.read(0, param);

			if (debug) {
                log.debug("time: " + (System.currentTimeMillis() - start));
                log.debug("image: " + image);
            }
            ColorModel imageColorModel = image.getColorModel();
            if (imageColorModel.getColorSpace().getType() == ColorSpace.TYPE_CMYK) {
                try {
                    /*ColorConvertOp op = new ColorConvertOp(ColorSpace
                            .getInstance(ColorSpace.CS_sRGB), null);
                    image = op.filter(image, new BufferedImage(
                            image.getWidth(), image.getHeight(),
                            BufferedImage.TYPE_4BYTE_ABGR_PRE));*/
                	BufferedImage tmpImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_RGB);
                    int[] rgbArray = image.getRGB(0, 0, image.getWidth(), image.getHeight(), null, 0, image.getWidth());
                    tmpImage.setRGB(0, 0, image.getWidth(), image.getHeight(), rgbArray, 0, image.getWidth());
                    image = tmpImage;
                } catch (Exception e) {
                    log.error("Exception", e);
                }
                if (debug) {
                    log.debug("time: " + (System.currentTimeMillis() - start));
                    log.debug("image: " + image);
                }
            }
            //return image;
            // start of "hack"
            Layer layer;
            // workaround since the gfx lib sometimes is not able to resize tiff images (BufferedImage!!)
            FileOutputStream itout = null;
            InputStream iis = null;
            File imageTmpFile = null;
            try {
                imageTmpFile = File.createTempFile("image", ".tmp");
                layer = new Layer(image);
                itout = FileUtils.openOutputStream(imageTmpFile);
                layer.write(DamConstants.THUMBNAIL_MIMETYPE, 1.0, itout);
                iis = FileUtils.openInputStream(imageTmpFile);
                return (new Layer(iis, dim)).getImage();
            } finally {
                IOUtils.closeQuietly(iis);
                IOUtils.closeQuietly(itout);
                FileUtils.deleteQuietly(imageTmpFile);
            }
            // eoh
        } catch (Exception e) {
            log.warn("getImage: error while getting image for PSD [{}]: ", rendition.getPath(), e);
        } finally {
            IOUtils.closeQuietly(is);
            try {
                stream.close();
            } catch (Exception ignore) {

            }
        }
        return null;
    }

    /**
     * {@inheritDoc}
     */
    public ExtractedMetadata extractMetadata(final Asset asset) {

        final ExtractedMetadata metadata = new ExtractedMetadata();
        final InputStream is = asset.getOriginal().getStream();
        ImageInputStream stream = null;
        try {
            final PSDImageReader reader = new PSDImageReader(null);
            if (doFileBuffering(asset.getOriginal())) {
                stream = new FileCacheImageInputStream(is, null);
                log.debug("Used FileCacheInputImageStream for size [{}]", asset.getOriginal().getSize());
            } else {
                stream = ImageIO.createImageInputStream(is);
            }
            reader.setInput(stream);
            metadata.setMetaDataProperty(TIFF_IMAGEWIDTH,reader.getWidth(0));
            metadata.setMetaDataProperty(TIFF_IMAGELENGTH, reader.getHeight(0));
            setMimetype(metadata, asset);
        } catch (IOException e) {
            log.error("I/O error while getting metadata", e);
        } finally {
            IOUtils.closeQuietly(is);
            try {
                stream.close();
            } catch (Exception ignore) {

            }
        }
        InputStream xmps = null;

        if (xmpHandler != null) {  //it could be null on some platform like AIX
            try {
                XMPMetadata xmpMetadata = xmpHandler.readXmpMetadata(asset);
                if (xmpMetadata != null) {
	                byte[] xmpBytes = (new RDFXMLSerializer()).serializeToBuffer(
	                        xmpMetadata, new RDFXMLSerializerContext());
	                xmps = new ByteArrayInputStream(xmpBytes);
	                metadata.setXmp(xmps);
                }

            } catch (Exception e) {
                if(log.isDebugEnabled())
                    log.error("Couldn't extract metadata using XMPhandler, attempting brute-force extraction", e);
                else
                    log.warn("Couldn't extract metadata using XMPhandler, attempting brute-force extraction");
                // fallback to 'brute-force'
                execGenericProcessor(asset.getOriginal().getStream(), metadata);
            } finally {
                IOUtils.closeQuietly(xmps);
            }
        } else {
            execGenericProcessor(asset.getOriginal().getStream(), metadata);
        }
        return metadata;
    }

    /**
     * {@inheritDoc}
     */
    public String[] getMimeTypes() {
        return new String[]{PHOTOSHOP_MIMETYPE_1, PHOTOSHOP_MIMETYPE_2, PHOTOSHOP_MIMETYPE_3, PHOTOSHOP_MIMETYPE_4, PHOTOSHOP_MIMETYPE_5};
    }

    public Iterator processRelated(final Asset asset) {
        return refResolver.resolve(asset);
    }

    @Activate
    @SuppressWarnings("unused")
    protected void activate(final ComponentContext context) throws RepositoryException {
        largeFileThreshold = PropertiesUtil.toLong(context.getProperties().get(CONFIG_LARGE_FILE_THRESHOLD), DEFAULT_LARGE_FILE_THRESHOLD);
    }

    private boolean doFileBuffering(final Rendition r) {
        return (largeFileThreshold != -1) && (largeFileThreshold <= r.getSize());
    }
    
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy