1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package io.wcm.handler.mediasource.dam.impl;
21
22 import static io.wcm.handler.media.MediaNameConstants.URI_TEMPLATE_PLACEHOLDER_HEIGHT;
23 import static io.wcm.handler.media.MediaNameConstants.URI_TEMPLATE_PLACEHOLDER_WIDTH;
24
25 import org.apache.commons.lang3.StringUtils;
26 import org.apache.sling.api.resource.Resource;
27 import org.jetbrains.annotations.NotNull;
28 import org.jetbrains.annotations.Nullable;
29
30 import com.day.cq.dam.api.Rendition;
31
32 import io.wcm.handler.media.CropDimension;
33 import io.wcm.handler.media.Dimension;
34 import io.wcm.handler.media.MediaArgs;
35 import io.wcm.handler.media.UriTemplate;
36 import io.wcm.handler.media.UriTemplateType;
37 import io.wcm.handler.media.impl.ImageFileServlet;
38 import io.wcm.handler.media.impl.ImageFileServletSelector;
39 import io.wcm.handler.media.impl.MediaFileServletConstants;
40 import io.wcm.handler.mediasource.dam.impl.dynamicmedia.DynamicMediaPath;
41 import io.wcm.handler.mediasource.dam.impl.dynamicmedia.NamedDimension;
42 import io.wcm.handler.mediasource.dam.impl.dynamicmedia.SmartCrop;
43 import io.wcm.handler.mediasource.dam.impl.weboptimized.WebOptimizedImageDeliveryParams;
44 import io.wcm.handler.url.UrlHandler;
45 import io.wcm.sling.commons.adapter.AdaptTo;
46
47
48
49
50 final class DamUriTemplate implements UriTemplate {
51
52 private static final long DUMMY_WIDTH = 999991;
53 private static final long DUMMY_HEIGHT = 999992;
54
55 private final UriTemplateType type;
56 private final String uriTemplate;
57 private final Dimension dimension;
58
59 DamUriTemplate(@NotNull UriTemplateType type, @NotNull Dimension dimension,
60 @NotNull Rendition rendition, @Nullable CropDimension cropDimension, @Nullable Integer rotation,
61 @Nullable Double ratio, @NotNull DamContext damContext) {
62 this.type = type;
63
64 String url = null;
65 Dimension validatedDimension = null;
66 if (damContext.isDynamicMediaEnabled() && damContext.isDynamicMediaAsset()) {
67
68 NamedDimension smartCropDef = getDynamicMediaSmartCropDef(cropDimension, rotation, ratio, damContext);
69 url = buildUriTemplateDynamicMedia(type, cropDimension, rotation, smartCropDef, damContext);
70
71 if (url != null && smartCropDef != null) {
72 validatedDimension = SmartCrop.getCropDimensionForAsset(damContext.getAsset(), damContext.getResourceResolver(), smartCropDef);
73 }
74 }
75 if (url == null && (!damContext.isDynamicMediaEnabled() || !damContext.isDynamicMediaAemFallbackDisabled())) {
76 if (damContext.isWebOptimizedImageDeliveryEnabled()) {
77
78 url = buildUriTemplateWebOptimizedImageDelivery(type, cropDimension, rotation, damContext);
79 }
80 if (url == null) {
81
82 url = buildUriTemplateDam(type, rendition, cropDimension, rotation,
83 damContext.getMediaArgs().getImageQualityPercentage(), damContext);
84 }
85 }
86 this.uriTemplate = url;
87
88 if (validatedDimension == null) {
89 validatedDimension = dimension;
90 }
91 this.dimension = validatedDimension;
92 }
93
94 private static String buildUriTemplateDam(@NotNull UriTemplateType type, @NotNull Rendition rendition,
95 @Nullable CropDimension cropDimension, @Nullable Integer rotation, @Nullable Double imageQualityPercentage,
96 @NotNull DamContext damContext) {
97
98
99 MediaArgs mediaArgs = damContext.getMediaArgs();
100 String mediaPath = RenditionMetadata.buildMediaPath(rendition.getPath()
101 + "." + ImageFileServletSelector.build(DUMMY_WIDTH, DUMMY_HEIGHT, cropDimension, rotation, imageQualityPercentage, false)
102 + "." + MediaFileServletConstants.EXTENSION,
103 ImageFileServlet.getImageFileName(damContext.getAsset().getName(), mediaArgs.getEnforceOutputFileExtension()));
104 UrlHandler urlHandler = AdaptTo.notNull(damContext, UrlHandler.class);
105 String url = urlHandler.get(mediaPath).urlMode(mediaArgs.getUrlMode())
106 .buildExternalResourceUrl(damContext.getAsset().adaptTo(Resource.class));
107
108
109 switch (type) {
110 case CROP_CENTER:
111 url = StringUtils.replace(url, Long.toString(DUMMY_WIDTH), URI_TEMPLATE_PLACEHOLDER_WIDTH);
112 url = StringUtils.replace(url, Long.toString(DUMMY_HEIGHT), URI_TEMPLATE_PLACEHOLDER_HEIGHT);
113 break;
114 case SCALE_WIDTH:
115 url = StringUtils.replace(url, Long.toString(DUMMY_WIDTH), URI_TEMPLATE_PLACEHOLDER_WIDTH);
116 url = StringUtils.replace(url, Long.toString(DUMMY_HEIGHT), "0");
117 break;
118 case SCALE_HEIGHT:
119 url = StringUtils.replace(url, Long.toString(DUMMY_WIDTH), "0");
120 url = StringUtils.replace(url, Long.toString(DUMMY_HEIGHT), URI_TEMPLATE_PLACEHOLDER_HEIGHT);
121 break;
122 default:
123 throw new IllegalArgumentException("Unsupported type: " + type);
124 }
125 return url;
126 }
127
128 private static String buildUriTemplateWebOptimizedImageDelivery(@NotNull UriTemplateType type,
129 @Nullable CropDimension cropDimension, @Nullable Integer rotation, @NotNull DamContext damContext) {
130
131 if (type == UriTemplateType.SCALE_HEIGHT) {
132 return null;
133 }
134
135
136 String url = damContext.getWebOptimizedImageDeliveryUrl(new WebOptimizedImageDeliveryParams()
137 .width(DUMMY_WIDTH).cropDimension(cropDimension).rotation(rotation));
138 if (url == null) {
139 return null;
140 }
141
142
143 switch (type) {
144 case CROP_CENTER:
145 url = StringUtils.replace(url, Long.toString(DUMMY_WIDTH), URI_TEMPLATE_PLACEHOLDER_WIDTH);
146 break;
147 case SCALE_WIDTH:
148 url = StringUtils.replace(url, Long.toString(DUMMY_WIDTH), URI_TEMPLATE_PLACEHOLDER_WIDTH);
149 break;
150 default:
151 throw new IllegalArgumentException("Unsupported type for Web-optimized image delivery: " + type);
152 }
153 return url;
154 }
155
156 private static @Nullable String buildUriTemplateDynamicMedia(@NotNull UriTemplateType type,
157 @Nullable CropDimension cropDimension, @Nullable Integer rotation, @Nullable NamedDimension smartCropDef,
158 @NotNull DamContext damContext) {
159 String productionAssetUrl = damContext.getDynamicMediaServerUrl();
160 if (productionAssetUrl == null) {
161 return null;
162 }
163 StringBuilder result = new StringBuilder();
164 result.append(productionAssetUrl).append(DynamicMediaPath.buildImage(damContext));
165
166
167 if (smartCropDef != null) {
168 result.append("%3A").append(smartCropDef.getName()).append("?")
169 .append(getDynamicMediaWidthHeightParameters(type))
170 .append("&fit=constrain");
171 return result.toString();
172 }
173
174
175 result.append("?");
176 if (cropDimension != null) {
177 result.append("crop=").append(cropDimension.getCropStringWidthHeight()).append("&");
178 }
179 if (rotation != null) {
180 result.append("rotate=").append(rotation).append("&");
181 }
182 result.append(getDynamicMediaWidthHeightParameters(type));
183 return result.toString();
184 }
185
186 private static String getDynamicMediaWidthHeightParameters(UriTemplateType type) {
187 switch (type) {
188 case CROP_CENTER:
189 return "wid=" + URI_TEMPLATE_PLACEHOLDER_WIDTH + "&hei=" + URI_TEMPLATE_PLACEHOLDER_HEIGHT + "&fit=crop";
190 case SCALE_WIDTH:
191 return "wid=" + URI_TEMPLATE_PLACEHOLDER_WIDTH;
192 case SCALE_HEIGHT:
193 return "hei=" + URI_TEMPLATE_PLACEHOLDER_HEIGHT;
194 default:
195 throw new IllegalArgumentException("Unsupported type for Dynamic Media: " + type);
196 }
197 }
198
199 private static NamedDimension getDynamicMediaSmartCropDef(@Nullable CropDimension cropDimension, @Nullable Integer rotation,
200 @Nullable Double ratio, @NotNull DamContext damContext) {
201 if (SmartCrop.canApply(cropDimension, rotation) && ratio != null) {
202
203 return SmartCrop.getDimensionForRatio(damContext.getImageProfile(), ratio);
204 }
205 return null;
206 }
207
208 @Override
209 public @NotNull UriTemplateType getType() {
210 return type;
211 }
212
213 @Override
214 public @NotNull String getUriTemplate() {
215 return uriTemplate;
216 }
217
218 @Override
219 public long getMaxWidth() {
220 return dimension.getWidth();
221 }
222
223 @Override
224 public long getMaxHeight() {
225 return dimension.getHeight();
226 }
227
228 @Override
229 public String toString() {
230 return uriTemplate;
231 }
232
233 }