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.dynamicmedia;
21
22 import java.net.URLEncoder;
23 import java.nio.charset.StandardCharsets;
24
25 import org.apache.commons.lang3.StringUtils;
26 import org.jetbrains.annotations.NotNull;
27 import org.jetbrains.annotations.Nullable;
28 import org.slf4j.Logger;
29 import org.slf4j.LoggerFactory;
30
31 import io.wcm.handler.media.CropDimension;
32 import io.wcm.handler.media.Dimension;
33 import io.wcm.handler.media.format.Ratio;
34 import io.wcm.handler.media.impl.ImageQualityPercentage;
35 import io.wcm.handler.mediasource.dam.impl.DamContext;
36 import io.wcm.wcm.commons.contenttype.ContentType;
37
38
39
40
41 public final class DynamicMediaPath {
42
43
44
45
46 @SuppressWarnings("java:S1075")
47 private static final String IMAGE_SERVER_PATH = "/is/image/";
48
49
50
51
52 @SuppressWarnings("java:S1075")
53 private static final String CONTENT_SERVER_PATH = "/is/content/";
54
55
56
57
58
59
60 public static final String DOWNLOAD_SUFFIX = "?cdh=attachment";
61
62 private static final Logger log = LoggerFactory.getLogger(DynamicMediaPath.class);
63
64 private DynamicMediaPath() {
65
66 }
67
68
69
70
71
72
73
74 public static @NotNull String buildContent(@NotNull DamContext damContext, boolean contentDispositionAttachment) {
75 StringBuilder result = new StringBuilder();
76 result.append(CONTENT_SERVER_PATH).append(encodeDynamicMediaObject(damContext));
77 if (contentDispositionAttachment) {
78 result.append(DOWNLOAD_SUFFIX);
79 }
80 return result.toString();
81 }
82
83
84
85
86
87
88 public static @NotNull String buildImage(@NotNull DamContext damContext) {
89 return IMAGE_SERVER_PATH + encodeDynamicMediaObject(damContext);
90 }
91
92
93
94
95
96
97
98
99 public static @Nullable String buildImage(@NotNull DamContext damContext, long width, long height) {
100 return buildImage(damContext, width, height, null, null);
101 }
102
103
104
105
106
107
108
109
110
111
112 public static @Nullable String buildImage(@NotNull DamContext damContext, long width, long height,
113 @Nullable CropDimension cropDimension, @Nullable Integer rotation) {
114 Dimension dimension = calcWidthHeight(damContext, width, height);
115
116 StringBuilder result = new StringBuilder();
117 result.append(IMAGE_SERVER_PATH).append(encodeDynamicMediaObject(damContext));
118
119
120 if (SmartCrop.canApply(cropDimension, rotation)) {
121
122 NamedDimension smartCropDef = SmartCrop.getDimensionForWidthHeight(damContext.getImageProfile(), width, height);
123 if (smartCropDef != null) {
124 if (damContext.isDynamicMediaValidateSmartCropRenditionSizes()
125 && !SmartCrop.isMatchingSize(damContext.getAsset(), damContext.getResourceResolver(), smartCropDef, width, height)) {
126
127 logResult(damContext, "<too small for " + width + "x" + height + ">");
128 return null;
129 }
130 result.append("%3A").append(smartCropDef.getName()).append("?");
131 appendWidthHeigtFormatQuality(result, dimension, damContext);
132 logResult(damContext, result);
133 return result.toString();
134 }
135 }
136
137 result.append("?");
138 if (cropDimension != null) {
139 result.append("crop=").append(cropDimension.getCropStringWidthHeight()).append("&");
140 }
141 if (rotation != null) {
142 result.append("rotate=").append(rotation).append("&");
143 }
144 appendWidthHeigtFormatQuality(result, dimension, damContext);
145 logResult(damContext, result);
146 return result.toString();
147 }
148
149 private static void appendWidthHeigtFormatQuality(@NotNull StringBuilder result, @NotNull Dimension dimension, @NotNull DamContext damContext) {
150 result.append("wid=").append(dimension.getWidth()).append("&")
151 .append("hei=").append(dimension.getHeight()).append("&")
152
153 .append("fit=stretch");
154 if (isPNG(damContext)) {
155
156 result.append("&fmt=png-alpha");
157 }
158 else if (damContext.isDynamicMediaSetImageQuality()) {
159
160 result.append("&qlt=").append(ImageQualityPercentage.getAsInteger(damContext.getMediaArgs(), damContext.getMediaHandlerConfig()));
161 }
162 }
163
164 private static void logResult(@NotNull DamContext damContext, @NotNull CharSequence result) {
165 if (log.isTraceEnabled()) {
166 log.trace("Build dynamic media path for {}: {}", damContext.getAsset().getPath(), result);
167 }
168 }
169
170
171
172
173
174
175
176
177 private static Dimension calcWidthHeight(@NotNull DamContext damContext, long width, long height) {
178 Dimension sizeLimit = damContext.getDynamicMediaImageSizeLimit();
179 if (width > sizeLimit.getWidth()) {
180 double ratio = Ratio.get(width, height);
181 long newWidth = sizeLimit.getWidth();
182 long newHeight = Math.round(newWidth / ratio);
183 return calcWidthHeight(damContext, newWidth, newHeight);
184 }
185 if (height > sizeLimit.getHeight()) {
186 double ratio = Ratio.get(width, height);
187 long newHeight = sizeLimit.getHeight();
188 long newWidth = Math.round(newHeight * ratio);
189 return new Dimension(newWidth, newHeight);
190 }
191 return new Dimension(width, height);
192 }
193
194
195
196
197
198
199 private static String encodeDynamicMediaObject(@NotNull DamContext damContext) {
200 String[] pathParts = StringUtils.split(damContext.getDynamicMediaObject(), "/");
201 for (int i = 0; i < pathParts.length; i++) {
202 pathParts[i] = URLEncoder.encode(pathParts[i], StandardCharsets.UTF_8);
203
204 pathParts[i] = StringUtils.replace(pathParts[i], "+", "%20");
205 }
206 return StringUtils.join(pathParts, "/");
207 }
208
209 private static boolean isPNG(@NotNull DamContext damContext) {
210 return StringUtils.equals(damContext.getAsset().getMimeType(), ContentType.PNG);
211 }
212
213 }