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

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

There is a newer version: 2024.11.18751.20241128T090041Z-241100
Show 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; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy