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