com.day.cq.commons.ImageHelper Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of aem-sdk-api Show documentation
Show all versions of aem-sdk-api Show documentation
The Adobe Experience Manager SDK
The newest version!
/*
* 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);
}
}