Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Image cropping customisation #3297

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com)
### Changed

- #3267 - Remove JSR305 dependency
- #3296 - Add image cropping customisation

### Fixed

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
@Component(label = "ACS AEM Commons - Color Conversion", description = "ACS AEM Commons - Color Conversion", metatype = true)
@Service
@SuppressWarnings({"checkstyle:abbreviationaswordinname", "checkstyle:localvariablename"})
public final class ColorConversionImpl implements ColorConversion {
public final class ColorConversionImpl implements ColorConversion { //nosonar

private static final String DEFAULT_CMYK_PROFILE = "JapanColor2001Coated";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import com.day.cq.dam.api.Asset;
import com.day.cq.dam.api.Rendition;
import com.day.cq.dam.commons.util.DamUtil;
import com.day.cq.dam.commons.util.OrientationUtil;
import com.day.cq.wcm.api.NameConstants;
import com.day.cq.wcm.api.Page;
import com.day.cq.wcm.api.PageManager;
Expand Down Expand Up @@ -124,10 +125,9 @@
public class NamedTransformImageServlet extends SlingSafeMethodsServlet implements OptingServlet {

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

private static final Logger AVOID_USAGE_LOGGER =
LoggerFactory.getLogger(NamedTransformImageServlet.class.getName() + ".AvoidUsage");


private static final Logger AVOID_USAGE_LOGGER =
LoggerFactory.getLogger(NamedTransformImageServlet.class.getName() + ".AvoidUsage");

public static final String NAME_IMAGE = "image";

Expand All @@ -146,10 +146,12 @@ public class NamedTransformImageServlet extends SlingSafeMethodsServlet implemen
private static final String TYPE_QUALITY = "quality";

private static final String TYPE_PROGRESSIVE = "progressive";
private static final String PROP_ADD_URL_PARAMETERS = "addUrlParams";

/* Asset Rendition Pattern Picker */

private static final String DEFAULT_ASSET_RENDITION_PICKER_REGEX = "cq5dam\\.web\\.(.*)";
private static final String TIFF_ORIENTATION = "tiff:Orientation";
public static final String PARAM_SEPARATOR = ":";

@Property(label = "Asset Rendition Picker Regex",
description = "Regex to select the Rendition to transform when directly transforming a DAM Asset."
Expand Down Expand Up @@ -208,14 +210,12 @@ public final boolean accepts(final SlingHttpServletRequest request) {
@Override
protected final void doGet(final SlingHttpServletRequest request, final SlingHttpServletResponse response) throws
ServletException, IOException {


// Warn when this servlet is used
AVOID_USAGE_LOGGER.warn("An image is transformed on-the-fly, which can be a very resource intensive operation. "
+ "If done frequently, you should consider switching to dynamic AEM web-optimized images or creating such a rendition upfront using processing profiles. "
+ "See https://adobe-consulting-services.github.io/acs-aem-commons/features/named-image-transform/index.html for more details.");



// Warn when this servlet is used
AVOID_USAGE_LOGGER.warn("An image is transformed on-the-fly, which can be a very resource intensive operation. "
+ "If done frequently, you should consider switching to dynamic AEM web-optimized images or creating such a rendition upfront using processing profiles. "
+ "See https://adobe-consulting-services.github.io/acs-aem-commons/features/named-image-transform/index.html for more details.");

// Get the transform names from the suffix
final List<NamedImageTransformer> selectedNamedImageTransformers = getNamedImageTransformers(request);

Expand All @@ -225,14 +225,17 @@ protected final void doGet(final SlingHttpServletRequest request, final SlingHtt
final Image image = resolveImage(request);
final String mimeType = getMimeType(request, image);
Layer layer = getLayer(image);

// Adjust layer to image orientation
processImageOrientation(image.getResource(), layer);

if (layer == null) {
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
return;
}

// Transform the image
layer = this.transform(layer, imageTransformersWithParams);
layer = this.transform(layer, imageTransformersWithParams, request);

// Get the quality
final double quality = this.getQuality(mimeType,
Expand Down Expand Up @@ -260,7 +263,7 @@ protected final void doGet(final SlingHttpServletRequest request, final SlingHtt
* @param imageTransformersWithParams the transforms and their params
* @return the transformed Image layer
*/
protected final Layer transform(Layer layer, final ValueMap imageTransformersWithParams) {
protected final Layer transform(Layer layer, final ValueMap imageTransformersWithParams, SlingHttpServletRequest request) {
for (final String type : imageTransformersWithParams.keySet()) {
if (StringUtils.equals(TYPE_QUALITY, type)) {
// Do not process the "quality" transform in the usual manner
Expand All @@ -273,16 +276,81 @@ protected final Layer transform(Layer layer, final ValueMap imageTransformersWit
continue;
}

final ValueMap transformParams = imageTransformersWithParams.get(type, EMPTY_PARAMS);
ValueMap transformParams = imageTransformersWithParams.get(type, EMPTY_PARAMS);

if (transformParams != null) {
if (Boolean.valueOf(transformParams.get(PROP_ADD_URL_PARAMETERS, false))) {
LinkedHashMap<String, Object> cropParamsFromUrl = getCropParamsFromUrl(request);
if(!cropParamsFromUrl.isEmpty()) {
transformParams = new ValueMapDecorator(new LinkedHashMap<String, Object>(transformParams));
transformParams.putAll(cropParamsFromUrl);
}
}

layer = imageTransformer.transform(layer, transformParams);
}
}

return layer;
}

/**
* Rotate and flip image based on it's tiff:Orientation metadata.
* @param imageResource image resource
* @param layer image Layer object
*/
protected void processImageOrientation(Resource imageResource, Layer layer) {
ValueMap properties = getImageMetadataValueMap(imageResource);
if(properties != null) {
String orientation = properties.get(TIFF_ORIENTATION, String.class);
if(orientation != null && Short.parseShort(orientation) != OrientationUtil.ORIENTATION_NORMAL) {
switch(Short.parseShort(orientation)) {
case OrientationUtil.ORIENTATION_MIRROR_HORIZONTAL:
layer.flipHorizontally();
break;
case OrientationUtil.ORIENTATION_ROTATE_180:
layer.rotate(180);
break;
case OrientationUtil.ORIENTATION_MIRROR_VERTICAL:
layer.flipVertically();
break;
case OrientationUtil.ORIENTATION_MIRROR_HORIZONTAL_ROTATE_270_CW:
layer.flipHorizontally();
layer.rotate(270);
break;
case OrientationUtil.ORIENTATION_ROTATE_90_CW:
layer.rotate(90);
break;
case OrientationUtil.ORIENTATION_MIRROR_HORIZONTAL_ROTATE_90_CW:
layer.flipHorizontally();
layer.rotate(90);
break;
case OrientationUtil.ORIENTATION_ROTATE_270_CW:
layer.rotate(270);
break;
default:
break;
}
}
}
}

/**
* Returns ValueMap of the Image Metadata resource
* @param imageResource image resource
* @return metadata ValueMap, or null if given resource doesn't have jcr:content/metadata node.
*/
protected ValueMap getImageMetadataValueMap(Resource imageResource) {
ValueMap result = null;
final Resource metadata = imageResource.getChild("jcr:content/metadata");
if (metadata != null) {
result = metadata.adaptTo(ValueMap.class);
}
return result;
}

/**

/**
* Gets the NamedImageTransformers based on the Suffix segments in order.
*
Expand Down Expand Up @@ -442,6 +510,21 @@ private String getMimeType(final SlingHttpServletRequest request, final Image im
}
}

private LinkedHashMap<String, Object> getCropParamsFromUrl(SlingHttpServletRequest request) {
LinkedHashMap<String, Object> urlParams = new LinkedHashMap<String, Object>();

String transformName = PathInfoUtil.getFirstSuffixSegment(request);
String extension = PathInfoUtil.getLastSuffixSegment(request);

String paramsString = StringUtils.substringBetween(request.getRequestURI(), transformName + "/", extension);
String[] params = StringUtils.split(paramsString, "/");
for (String param : params) {
urlParams.put(StringUtils.substringBefore(param, ":"),
StringUtils.substringAfter(param, PARAM_SEPARATOR));
}
return urlParams;
}

/**
* Gets the Image layer.
*
Expand Down Expand Up @@ -543,14 +626,15 @@ protected final void activate(final Map<String, String> properties) {
DEFAULT_ASSET_RENDITION_PICKER_REGEX);
renditionPatternPicker = new RenditionPatternPicker(DEFAULT_ASSET_RENDITION_PICKER_REGEX);
}

/**
* We want to be able to determine if the absence of the messages of the AVOID_USAGE_LOGGER
* is caused by not using this feature or by disabling the WARN messages.
*/
* We want to be able to determine if the absence of the messages of the AVOID_USAGE_LOGGER
* is caused by not using this feature or by disabling the WARN messages.
*/
if (!AVOID_USAGE_LOGGER.isWarnEnabled()) {
log.info("Warnings for the use of the NamedTransfomringImageServlet disabled");
log.info("Warnings for the use of the NamedTransfomringImageServlet disabled");
}

}

protected final void bindNamedImageTransformers(final NamedImageTransformer service,
Expand Down Expand Up @@ -582,4 +666,5 @@ protected final void unbindImageTransformers(final ImageTransformer service, fin
imageTransformers.remove(type);
}
}
}
}

Loading
Loading