com.day.cq.commons.ImageResource 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 java.awt.Dimension;
import java.awt.Rectangle;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;
import javax.jcr.Node;
import javax.jcr.Property;
import javax.jcr.RepositoryException;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.jackrabbit.JcrConstants;
import org.apache.sling.api.resource.NonExistingResource;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ValueMap;
import com.adobe.granite.xss.XSSAPI;
import com.day.cq.commons.impl.DiffedResourceWrapper;
import com.day.cq.widget.Doctype;
import com.day.image.Layer;
import com.day.text.Text;
/**
* Provides convenience methods for displaying images.
*/
public class ImageResource extends DownloadResource {
/**
* name of the html width property
*/
public static final String PN_HTML_WIDTH = "htmlWidth";
/**
* name of the html height property
*/
public static final String PN_HTML_HEIGHT = "htmlHeight";
/**
* name of the width property
*/
public static final String PN_WIDTH = "width";
/**
* name of the height property
*/
public static final String PN_HEIGHT = "height";
/**
* name of the minimal width property. used for resizing.
*/
public static final String PN_MIN_WIDTH = "minWidth";
/**
* name of the minimal height property. used for resizing.
*/
public static final String PN_MIN_HEIGHT = "minHeight";
/**
* name of the maximal width property. used for resizing.
*/
public static final String PN_MAX_WIDTH = "maxWidth";
/**
* name of the maximal height property. used for resizing.
*/
public static final String PN_MAX_HEIGHT = "maxHeight";
/**
* name of the alt name property
*/
public static final String PN_ALT = "alt";
/**
* name of the image crop property
*/
public static final String PN_IMAGE_CROP = "imageCrop";
/**
* name of the image rotation property
*/
public static final String PN_IMAGE_ROTATE = "imageRotate";
/**
* name of the link URL property
*/
public static final String PN_LINK_URL = "linkURL";
/**
* name of the default image path property
*/
public static final String PN_DEFAULT_IMAGE_PATH = "defaultImagePath";
/**
* the default image path
*/
public static final String DEFAULT_IMAGE_PATH = "etc/designs/default/0.gif";
/**
* the doc type. used for empty tag handling
*/
private Doctype doctype = Doctype.HTML_401_STRICT;
/**
* Extension from type.
*/
private String extFromType;
/**
* Resource resolver specific XSSAPI service
*/
private XSSAPI xssAPI;
/**
* Creates a new image based on the given resource. the image properties are
* considered to 'on' the given resource.
*
* @param resource resource of the image
* @throws IllegalArgumentException if the given resource is not adaptable to node.
*/
public ImageResource(Resource resource) {
super(resource);
super.setExtension(".png");
xssAPI = resource.getResourceResolver().adaptTo(XSSAPI.class);
// init suffix with last mod date to avoid browser caching issues.
// download class initializes it otherwise with the filename - which is not desirable
try {
String suffix = "";
long lastMod = 0;
if (node != null) {
if (node.hasProperty(JcrConstants.JCR_LASTMODIFIED)) {
lastMod = node.getProperty(JcrConstants.JCR_LASTMODIFIED).getLong();
} else if (node.hasProperty(JcrConstants.JCR_CREATED)) {
lastMod = node.getProperty(JcrConstants.JCR_CREATED).getLong();
}
} else {
ValueMap values = getResource().adaptTo(ValueMap.class);
if (values != null) { // values will be null for Sling NonExistingResource
Long value = values.get(JcrConstants.JCR_LASTMODIFIED, Long.class);
if (value == null) {
value = values.get(JcrConstants.JCR_CREATED, Long.class);
}
if (value != null) {
lastMod = value;
}
}
}
long fileLastMod = 0;
if (getFileReference().length() > 0) {
try {
Node refNode = resource.getResourceResolver().getResource(getFileReference()).adaptTo(Node.class);
if (refNode.getNode(JcrConstants.JCR_CONTENT).hasProperty(JcrConstants.JCR_LASTMODIFIED)) {
fileLastMod = refNode.getNode(JcrConstants.JCR_CONTENT).getProperty(JcrConstants.JCR_LASTMODIFIED).getLong();
} else if (refNode.hasProperty(JcrConstants.JCR_CREATED)) {
fileLastMod = refNode.getProperty(JcrConstants.JCR_CREATED).getLong();
}
} catch (Exception e) {
// e.g. asset not found; use lastMod
}
}
if (fileLastMod > lastMod) {
lastMod = fileLastMod;
}
if (lastMod != 0) {
suffix += lastMod;
suffix += getExtension();
}
setSuffix(suffix);
} catch (RepositoryException re) {
// ignore
}
}
/**
* Creates a new image based on the given resource. the image properties are
* considered to 'on' the given resource unless imageName
* is specified. then the respective child resource holds the image
* properties.
*
* @param resource current resource
* @param imageName name of the image resource
* @throws IllegalArgumentException if the given resource is not adaptable to node.
*/
public ImageResource(Resource resource, String imageName) {
this(getRelativeResource(resource, imageName));
}
/**
* Returns the relative resource or a {@link NonExistingResource}.
*
* @param resource "parent" resource
* @param relPath relative path
* @return the resource
*/
protected static Resource getRelativeResource(Resource resource, String relPath) {
if (relPath == null) {
return resource;
}
Resource res = resource.getResourceResolver().getResource(resource, relPath);
if (res == null) {
res = new NonExistingResource(resource.getResourceResolver(), resource.getPath() + "/" + relPath);
}
// add diff info wrapper if needed.
DiffInfo info = resource.adaptTo(DiffInfo.class);
if (info != null) {
Resource content = info.getContent();
if (content != null) {
content = content.getResourceResolver().getResource(content, relPath);
if (content == null) {
content = new NonExistingResource(resource.getResourceResolver(), info.getContent().getPath() + "/" + relPath);
}
info = new DiffInfo(content, info.getType());
}
res = new DiffedResourceWrapper(res, info);
}
return res;
}
/**
* Returns the image title as defined by 'getItemName(PN_TITLE)'
* or overridden by {@link #setTitle(String)}.
*
* @param escape if true
the string is HTML escaped
* @return the title
*/
public String getTitle(boolean escape) {
String title = get(getItemName(PN_TITLE));
if (title.equals("")) {
String refPath = getFileReference();
if (!refPath.equals("")) {
try {
Node node = getResourceResolver().getResource(refPath).adaptTo(Node.class);
title = node.getNode(JcrConstants.JCR_CONTENT).getNode("metadata").getProperty("dc:title").getString();
setTitle(title);
}
catch (Exception e) {
// dc:title does not exist, use file name without extension
title = refPath.lastIndexOf(".") > refPath.lastIndexOf("/") ?
refPath.substring(refPath.lastIndexOf("/") + 1, refPath.lastIndexOf(".")) :
refPath.substring(refPath.lastIndexOf("/") + 1);
setTitle(title);
}
}
}
return escape ? StringEscapeUtils.escapeHtml4(title) : title;
}
/**
* Returns the image alt name as defined by the {@value #PN_ALT}
* or overridden by {@link #setAlt(String)}.
*
* @return the alt name
* @see #PN_ALT
*/
public String getAlt() {
String alt = get(getItemName(PN_ALT));
if (alt.length() == 0) {
alt = getTitle();
}
return alt.length() == 0 ? getFileNodePath().substring(getFileNodePath().lastIndexOf("/") + 1) : alt;
}
/**
* Sets the alt name.
* @param alt the alt name.
*/
public void setAlt(String alt) {
set(PN_ALT, alt);
}
/**
* Returns the source attribute of this image. the source is computed as
* follows:
*
* - if a selector is defined the path of the current resource concatenated
* with the selector, extension, and suffix is used.
*
- if a file node path is defined it is concatenated with the extension and suffix.
*
- if a file reference is defined it is concatenated with the extension and suffix.
*
*
* @return the source attribute
*/
public String getSrc() {
return getHref();
}
/**
* Tries to calculate the extension from the mime-type of the underlying
* image.
* @return the mime-type dependant extension or ".png" if not determinable
*/
@Override
public String getExtension() {
if (extFromType == null) {
try {
extFromType = ImageHelper.getExtensionFromType(getMimeType());
} catch (RepositoryException e) {
// ignore
}
if (extFromType == null) {
extFromType = super.getExtension();
} else if (!extFromType.startsWith(".")) {
extFromType = "." + extFromType;
}
}
return extFromType;
}
@Override
public void setExtension(String extension) {
extFromType = extension;
super.setExtension(extension);
}
/**
* Sets the source attribute
* @param src the source attribute
*/
public void setSrc(String src) {
super.setHref(src);
}
/**
* Returns the doctype that is used when generating the HTML.
* Defaults to {@link Doctype#HTML_401_STRICT}.
*
* @return the doctype
* @since 5.3
* @deprecated use {@link #getImageDoctype()}
*/
@Deprecated
public com.day.cq.commons.Doctype getDoctype() {
return com.day.cq.commons.Doctype.valueOf(doctype.name());
}
/**
* Returns the doctype that is used when generating the HTML.
* Defaults to {@link Doctype#HTML_401_STRICT}.
*
* @return the doctype
*/
public Doctype getImageDoctype() {
return doctype;
}
/**
* Sets the doctype that is used when generating the HTML. If the given
* argument is null
the current doctype is not overridden.
* @param doctype the doctype
* @since 5.3
* @deprecated use {@link #setImageDoctype(Doctype)}
*/
@Deprecated
public void setDoctype(com.day.cq.commons.Doctype doctype) {
if (doctype != null) {
this.doctype = Doctype.valueOf(doctype.name());
}
}
/**
* Sets the doctype that is used when generating the HTML. If the given
* argument is null
the current doctype is not overridden.
* @param doctype the doctype
*/
public void setImageDoctype(Doctype doctype) {
if (doctype != null) {
this.doctype = doctype;
}
}
/**
* Writes this image as tag to the given writer by invoking
* {@link #doDraw(PrintWriter)} if {@link #canDraw()} returns true
.
*
* @param w the writer
* @throws IOException if an I/O error occurs
*/
public void draw(Writer w) throws IOException {
if (canDraw()) {
doDraw(new PrintWriter(w));
}
}
/**
* Writes this image as tag to the given writer by invoking the following
* - calls {@link #getImageTagAttributes()}
* - prints the link tag if needed
* - prints the image tag
* - prints the attributes
* - closes the image tag
* - closes the link tag
*
* @param out the writer
*/
protected void doDraw(PrintWriter out) {
Map attributes = getImageTagAttributes();
String linkURL = get(PN_LINK_URL);
if (linkURL != null && linkURL.length() > 0) {
linkURL = completeHREF(linkURL);
// check if its a valid href
linkURL = xssAPI.getValidHref(linkURL);
// if yes
if (linkURL.length() > 0){
// render the tag
out.printf("", linkURL);
}
}
out.print("");
} else {
out.print(">");
}
if (linkURL.length() > 0) {
out.print("");
}
}
/**
* checks if this image can be drawn.
* @return true
if it can be drawn
*/
protected boolean canDraw() {
return hasContent();
}
/**
* Collects the image tag attributes.
* @return the attributes
*/
protected Map getImageTagAttributes() {
Map attributes = new HashMap();
if (get(getItemName(PN_HTML_WIDTH)).length() > 0) {
attributes.put("width", get(getItemName(PN_HTML_WIDTH)));
}
if (get(getItemName(PN_HTML_HEIGHT)).length() > 0) {
attributes.put("height", get(getItemName(PN_HTML_HEIGHT)));
}
String src = getSrc();
if (src != null) {
String q = getQuery();
if (q == null) {
q = "";
}
attributes.put("src", Text.escape(src, '%', true) + q);
}
attributes.put("alt", getAlt());
attributes.put("title", getTitle());
// if (getTitle().equals("")) {
// String refPath = getFileReference();
// if (!refPath.equals("")) {
// try {
// Node node = getResourceResolver().getResource(refPath).adaptTo(Node.class);
// String title = node.getNode(JcrConstants.JCR_CONTENT).getNode("metadata").getProperty("dc:title").getString();
// attributes.put("title", title);
// }
// catch (Exception e) {
// // dc:title does not exist, use file name without extension
// attributes.put("title", refPath.lastIndexOf(".") > refPath.lastIndexOf("/") ?
// refPath.substring(refPath.lastIndexOf("/") + 1, refPath.lastIndexOf(".")) :
// refPath.substring(refPath.lastIndexOf("/") + 1));
// }
// }
// else {
// attributes.put("title", getTitle());
// }
// }
if (attrs != null) {
attributes.putAll(attrs);
}
return attributes;
}
/**
* Completes the href with the same formatting than link into RTE
* @param href the href
* @return the completed href
*/
private String completeHREF(String href) {
if (href != null && href.length() > 0) {
//only for internal links
if ((href.charAt(0) == '/') || (href.charAt(0) == '#')) {
int anchorPos = href.indexOf("#");
if (anchorPos == 0) {
// change nothing if we have an "anchor only"-HREF
return href;
}
String anchor = "";
if (anchorPos > 0) {
anchor = href.substring(anchorPos, href.length());
href = href.substring(0, anchorPos);
}
// add extension to href if necessary
int extSepPos = href.lastIndexOf(".");
int slashPos = href.lastIndexOf("/");
if ((extSepPos <= 0) || (extSepPos < slashPos)) {
href = Text.escape(href, '%', true) + ".html" + anchor;
}
}
}
return href;
}
/**
* Returns the cropping rectangle as defined by the {@value #PN_IMAGE_CROP}.
*
* @return the cropping rectangle or null
*/
public Rectangle getCropRect() {
String cropData = get(getItemName(PN_IMAGE_CROP));
if (cropData.length() > 0) {
return ImageHelper.getCropRect(cropData, getPath());
}
return null;
}
/**
* Returns the rotation angle as defined by the {@value #PN_IMAGE_ROTATE}.
*
* @return the rotation angle (in degrees)
*/
public int getRotation() {
String rotation = get(getItemName(PN_IMAGE_ROTATE));
if (rotation.length() > 0) {
try {
return Integer.parseInt(rotation);
} catch (NumberFormatException nfe) {
// use default return of 0
}
}
return 0;
}
/**
* Resizes the given layer according to the dimensions defined in this image.
* See {@link ImageHelper#resize(Layer, Dimension, Dimension, Dimension)}
* for more details about the resizing algorithm.
*
* @param layer the layer to resize
* @return the layer or null
if the layer is untouched
*/
public Layer resize(Layer layer) {
Dimension d = new Dimension(
get(getItemName(PN_WIDTH), 0),
get(getItemName(PN_HEIGHT), 0));
Dimension min = new Dimension(
get(getItemName(PN_MIN_WIDTH), 0),
get(getItemName(PN_MIN_HEIGHT), 0));
Dimension max = new Dimension(
get(getItemName(PN_MAX_WIDTH), 0),
get(getItemName(PN_MAX_HEIGHT), 0));
return ImageHelper.resize(layer, d, min, max);
}
/**
* Crops the layer using the internal crop rectangle. if the crop rectangle
* is empty no cropping is performed and null
is returned.
*
* @param layer the layer
* @return cropped layer or null
*/
public Layer crop(Layer layer) {
Rectangle rect = getCropRect();
if (rect != null) {
layer.crop(rect);
return layer;
}
return null;
}
/**
* Rotates the layer using the internal rotation angle. If no rotation other than
* 0 is defined, no rotation is performed and null
is returned.
*
* @param layer the layer
* @return cropped layer or null
*/
public Layer rotate(Layer layer) {
int rotation = getRotation();
if (rotation != 0) {
layer.rotate(rotation);
return layer;
}
return null;
}
/**
* Returns the layer addressed by this image.
*
* @param cropped apply cropping if true
* @param resized apply resizing if true
* @param rotated apply rotation if true
* @return the layer
* @throws IOException if an I/O error occurs.
* @throws RepositoryException if a repository error occurs.
*/
public Layer getLayer(boolean cropped, boolean resized, boolean rotated)
throws IOException, RepositoryException {
Layer layer = null;
Property data = getData();
if (data != null) {
layer = ImageHelper.createLayer(data);
if (layer != null && cropped) {
crop(layer);
}
if (layer != null && resized) {
resize(layer);
}
if (layer != null && rotated) {
rotate(layer);
}
}
return layer;
}
@Override
public Property getData() throws RepositoryException {
Property data = super.getData();
if (data != null) {
return data;
}
if (node == null) {
return null;
}
if (("/" + DEFAULT_IMAGE_PATH).equals(node.getPath())) {
Node fileNode = node;
if (fileNode.hasNode(JcrConstants.JCR_CONTENT)) {
fileNode = fileNode.getNode(JcrConstants.JCR_CONTENT);
}
if (fileNode.hasProperty(JcrConstants.JCR_DATA)) {
return fileNode.getProperty(JcrConstants.JCR_DATA);
}
}
return null;
}
}