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

com.day.cq.commons.ImageHelper Maven / Gradle / Ivy

/*
 * Copyright 1997-2008 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.commons;

import com.day.cq.commons.jcr.JcrConstants;
import com.day.image.Font;
import com.day.image.Layer;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.sling.api.resource.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.jcr.Item;
import javax.jcr.Node;
import javax.jcr.Property;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;

/**
 * ImageHelper...
 */
public class ImageHelper {

    private static final Logger log = LoggerFactory.getLogger(ImageHelper.class);

    /**                                                d
     * Parses a CSV string of the form "x1,y1,x2,y2" and returns the respective
     * rectangle. if the string could not be parsed, null is
     * returned. The method can deal with an aspect ratio that is appended by "/" (e.g.
     * "x1,y1,x2,y2/ratioX,ratioY".
     * @param rectCSV the rectangle coordinates
     * @param path optional path for debugging
     * @return a rectangle or null
     */
    public static Rectangle getCropRect(String rectCSV, String path) {
        if (rectCSV != null && rectCSV.length() > 0) {
            try {
                // handle appended aspect ratio correctly if present
                int ratioPos = rectCSV.indexOf("/");
                if (ratioPos >= 0) {
                    rectCSV = rectCSV.substring(0, ratioPos);
                }
                String[] cords = rectCSV.split(",");
                int x1 = Integer.parseInt(cords[0]);
                int y1 = Integer.parseInt(cords[1]);
                int x2 = Integer.parseInt(cords[2]);
                int y2 = Integer.parseInt(cords[3]);
                return new Rectangle(x1, y1, x2 - x1, y2 - y1);
            } catch (Exception e) {
                if (path != null) {
                    log.warn("cropRect at {} is not valid: {}", path, e.toString());
                }
            }
        }
        return null;
    }

    /**
     * Creates a layer either by the node addressed by imageName or
     * the referenced image addressed by the refName property.
     *
     * @param node the current node
     * @param imageName the name of the image node
     * @param refName the name of the reference property
     * @return a layer or null
     * @throws RepositoryException if a repository error occurs
     * @throws IOException if a I/O error occurs
     */
    public static Layer createLayer(Node node, String imageName, String refName)
            throws RepositoryException, IOException {
        Layer layer = null;
        if (node.hasNode(imageName)) {
            layer = ImageHelper.createLayer(node.getNode(imageName));
        }
        if (layer == null) {
            String imageRef = node.hasProperty(refName)
                    ? node.getProperty(refName).getString()
                    : "";
            layer = ImageHelper.createLayer(node.getSession(), imageRef);
        }
        return layer;
    }

    /**
     * Creates a layer of the given item addressed by the path. the item can be
     * a binary property, a nt:file node or a nt:resource node.
     * @param session to use for retrieving the item
     * @param path to the item
     * @return a layer or null
     * @throws RepositoryException if a repository error occurs
     * @throws IOException if a I/O error occurs
     */
    public static Layer createLayer(Session session, String path)
            throws RepositoryException, IOException {
        if (path.length() > 0 && session.itemExists(path)) {
            return createLayer(session.getItem(path));
        } else {
            return null;
        }
    }

    /**
     * Creates a layer from the given resource. If the resource is not
     * adaptable to {@link InputStream} null is returned.
     * @param resource resource
     * @return layer or null
     */
    public static Layer createLayer(Resource resource) {
        if (resource == null) {
            return null;
        }
        InputStream in = resource.adaptTo(InputStream.class);
        if (in != null) {
            try {
                return new Layer(in);
            } catch (IOException e) {
                log.error("Error while creating layer.");
            } finally {
                IOUtils.closeQuietly(in);
            }
        } else {
            log.warn("Given resource not adaptable to stream: " + resource.getPath());
        }
        return null;
    }

    /**
     * Creates a layer of the given item. the item can be a binary property,
     * a nt:file node or a nt:resource node.
     * @param item the item
     * @return a layer or null
     * @throws RepositoryException if a repository error occurs
     * @throws IOException if a I/O error occurs
     */
    public static Layer createLayer(Item item) throws RepositoryException,
            IOException {
        InputStream in = null;
        if (item.isNode()) {
            Node node = (Node) item;
            if (node.hasNode(JcrConstants.JCR_CONTENT)) {
                node = node.getNode(JcrConstants.JCR_CONTENT);
            }
            if (node.hasProperty(JcrConstants.JCR_DATA)) {
                in = node.getProperty(JcrConstants.JCR_DATA).getStream();
            }
        } else {
            in = ((Property) item).getStream();
        }
        try {
            if (in == null) {
                return null;
            } else {
                Layer layer = new Layer(in);
                if (layer.getImage() == null) {
                    layer = null;
                }
                return layer;
            }
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    // ignore
                }
            }
        }
    }

    /**
     * Parses the font style from the given string(s). the styles can be combined
     * by by separating them by spaces, comas or pipes, or specifying them multiple.
     *
     * 
     * 
     * 
     * 
     * 
     * 
     * 
     * 
style string return
bold {@link Font#BOLD}
italic {@link Font#ITALIC}
underline {@link Font#UNDERLINE}
strikeout {@link Font#STRIKEOUT}
all other{@link Font#PLAIN}
* * @param styles the styles * @return style constant */ public static int parseFontStyle(String ... styles) { int style = 0; for (String s: styles) { for (String st: s.split("[, |]+")) { st = st.toLowerCase(); if (st.equals("bold")) { style |= Font.BOLD; } else if (st.equals("italic")) { style |= Font.ITALIC; } else if (st.equals("underline")) { style |= Font.UNDERLINE; } else if (st.equals("strikeout")) { style |= Font.STRIKEOUT; } } } return style; } /** * Resizes the given layer according to the given dimensions. * If both width and height are defined a non-proportional resizing is done. * if one of the dimensions is missing or 0, a proportional resizing * performed. the non-empty dimensions are trimmed to respect the minimal * and maximal dimension constraints. for proportional resizing the constraints * apply to the recalculated dimensions. when no dimensions are specified, * the layer is resized to fit into the constraint dimensions accordingly. * if the constraints cannot be applied to the dimensions, eg if no * values can be found that match the constraints, the layer is left * untouched. In any case, if no resizing is performed, null * is returned. * * Examples: * * | lW | lH | dW | dH | minW | maxW | minH | maxH | w | h | comment | * | | | 400 | 200 | 0 | 0 | 0 | 0 | 400 | 200 | no resizing | * | | | 400 | 200 | 0 | 200 | 0 | 0 | 200 | 100 | 200 max width applies | * | 120 | 80 | 0 | 0 | 100 | 400 | 50 | 100 | 120 | 80 | within bounds | * | 120 | 80 | 0 | 0 | 0 | 100 | 50 | 100 | 100 | 60 | 100 max width applies | * | 400 | 100 | 300 | 0 | 0 | 0 | 0 | 0 | 300 | 75 | resize proportional | * | 400 | 100 | 300 | 0 | 0 | 0 | 0 | 50 | 200 | 50 | max height applies | * * * @param layer layer to resize. * @param d dimension * @param min minimal dimension constraints * @param max maximal dimension constraints * @return the resized layer or null if untouched. */ public static Layer resize(Layer layer, Dimension d, Dimension min, Dimension max) { int width = d == null ? 0 : (int) d.getWidth(); int height = d == null ? 0 : (int) d.getHeight(); int minWidth = min == null ? 0 : (int) min.getWidth(); int minHeight = min == null ? 0 : (int) min.getHeight(); int maxWidth = max == null ? 0 : (int) max.getWidth(); int maxHeight = max == null ? 0 : (int) max.getHeight(); if (maxWidth == 0) { maxWidth = Integer.MAX_VALUE; } if (maxHeight == 0) { maxHeight = Integer.MAX_VALUE; } int ratioW = layer.getWidth(); int ratioH = layer.getHeight(); int loopProtect = 32; while (loopProtect-- > 0) { if (width == 0 && height == 0) { width = layer.getWidth(); height = layer.getHeight(); } else if (width == 0) { if (height < minHeight) { height = minHeight; } else if (height > maxHeight) { height = maxHeight; } width = height * ratioW / ratioH; } else if (height == 0) { if (width < minWidth) { width = minWidth; } else if (width > maxWidth) { width = maxWidth; } height = width * ratioH / ratioW; } else { ratioW = width; ratioH = height; if (width < minWidth) { width = minWidth; height = 0; } else if (width > maxWidth) { width = maxWidth; height = 0; } else if (height < minHeight) { height = minHeight; width = 0; } else if (height > maxHeight) { height = maxHeight; width = 0; } else { // dimensions ok. break; } } } if (loopProtect > 0 && (width != layer.getWidth() || height != layer.getHeight())) { layer.resize(width, height); return layer; } return null; } /** * Converts a String to an integer and returns the * specified Color. This method handles string * formats that are used to represent octal and hexadecimal numbers. * * If the string cannot be converted an transparent black is returned. * * @param s a String that represents an RGBA color as a 32-bit integer * @return the new Color object. * @see java.lang.Integer#decode */ public static Color parseColor(String s) { try { int i = Integer.decode(s); return new Color((i >> 16) & 0xFF, (i >> 8) & 0xFF, i & 0xFF, (i >> 24) & 0xFF); } catch (NumberFormatException e) { return new Color(0, 0, 0, 0); } } /** * Converts a String to an integer and returns the * specified Color. This method handles string * formats that are used to represent octal and hexadecimal numbers. * * If the string cannot be converted an transparent black is returned. * * @param s a String that represents an RGB color as a 24-bit integer * @param alpha override the alpha setting * @return the new Color object. * @see java.lang.Integer#decode */ public static Color parseColor(String s, int alpha) { try { int i = Integer.decode(s); return new Color((i >> 16) & 0xFF, (i >> 8) & 0xFF, i & 0xFF, alpha); } catch (NumberFormatException e) { return new Color(0, 0, 0, 0); } } /** * Saves the layer as nt:file below the given node. * * @param layer the layer to save * @param type image type. eg "image/png" * @param quality image quality. eg 1.0 * @param parent parent node * @param filename file name * @param replace if true existing node are replaced rather than updated. * @return the newly created and saved file node * @throws RepositoryException if a repository error occurrs * @throws IOException if an I/O error occurrs */ public static Node saveLayer(Layer layer, String type, double quality, Node parent, String filename, boolean replace) throws RepositoryException, IOException { Node fileNode = null; if (parent.hasNode(filename)) { if (replace) { parent.getNode(filename).remove(); } else { fileNode = parent.getNode(filename); } } if (fileNode == null) { fileNode = parent.addNode(filename, JcrConstants.NT_FILE); } Node content = fileNode.hasNode(JcrConstants.JCR_CONTENT) ? fileNode.getNode(JcrConstants.JCR_CONTENT) : fileNode.addNode(JcrConstants.JCR_CONTENT, JcrConstants.NT_RESOURCE); content.setProperty(JcrConstants.JCR_MIMETYPE, type); content.setProperty(JcrConstants.JCR_LASTMODIFIED, Calendar.getInstance()); OutputStream out = null; InputStream in = null; File tmpFile = null; try { tmpFile = File.createTempFile("imgheler", "img"); out = FileUtils.openOutputStream(tmpFile); layer.write(type, quality, out); out.close(); out = null; in = FileUtils.openInputStream(tmpFile); content.setProperty(JcrConstants.JCR_DATA, in); parent.save(); } finally { IOUtils.closeQuietly(out); IOUtils.closeQuietly(in); if (tmpFile != null) { tmpFile.delete(); } } return fileNode; } private static Map typeFromExt = new HashMap(); private static Map extFromType = new HashMap(); static { typeFromExt.put("png", "image/png"); extFromType.put("image/png", "png"); typeFromExt.put("jpg", "image/jpg"); typeFromExt.put("jpeg", "image/jpeg"); extFromType.put("image/jpg", "jpg"); extFromType.put("image/jpeg", "jpg"); typeFromExt.put("gif", "image/gif"); extFromType.put("image/gif", "gif"); } /** * Returns the image type for the given extension. currently there are only * "png", "gif", "jpeg" and "jpg" supported. * * @param ext the extension * @return the image type or null. */ public static String getTypeFromExtension(String ext) { return ext == null ? null : typeFromExt.get(ext); } /** * Returns the extension from the given image type. currently there are only * "png", "gif", "jpeg" and "jpg" supported. * * @param type the mime type * @return the extension or null. */ public static String getExtensionFromType(String type) { return type == null ? null :extFromType.get(type); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy