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;
21
22 import static com.day.cq.commons.jcr.JcrConstants.JCR_CONTENT;
23 import static com.day.cq.dam.api.DamConstants.EXIF_PIXELXDIMENSION;
24 import static com.day.cq.dam.api.DamConstants.EXIF_PIXELYDIMENSION;
25 import static com.day.cq.dam.api.DamConstants.METADATA_FOLDER;
26 import static com.day.cq.dam.api.DamConstants.ORIGINAL_FILE;
27 import static com.day.cq.dam.api.DamConstants.TIFF_IMAGELENGTH;
28 import static com.day.cq.dam.api.DamConstants.TIFF_IMAGEWIDTH;
29 import static io.wcm.handler.mediasource.dam.impl.metadata.RenditionMetadataNameConstants.NN_RENDITIONS_METADATA;
30 import static io.wcm.handler.mediasource.dam.impl.metadata.RenditionMetadataNameConstants.PN_IMAGE_HEIGHT;
31 import static io.wcm.handler.mediasource.dam.impl.metadata.RenditionMetadataNameConstants.PN_IMAGE_WIDTH;
32
33 import java.io.IOException;
34 import java.io.InputStream;
35
36 import org.apache.commons.io.FilenameUtils;
37 import org.apache.commons.lang3.StringUtils;
38 import org.apache.commons.lang3.math.NumberUtils;
39 import org.apache.sling.api.resource.Resource;
40 import org.apache.sling.api.resource.ValueMap;
41 import org.jetbrains.annotations.NotNull;
42 import org.jetbrains.annotations.Nullable;
43 import org.osgi.annotation.versioning.ProviderType;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
46
47 import com.day.cq.dam.api.Asset;
48 import com.day.cq.dam.api.Rendition;
49 import com.day.image.Layer;
50
51 import io.wcm.handler.media.Dimension;
52 import io.wcm.handler.media.MediaFileType;
53 import io.wcm.sling.commons.adapter.AdaptTo;
54
55
56
57
58 @ProviderType
59 public final class AssetRendition {
60
61 private static final Logger log = LoggerFactory.getLogger(AssetRendition.class);
62
63 private AssetRendition() {
64
65 }
66
67
68
69
70
71
72
73
74
75
76
77
78
79 public static @Nullable Dimension getDimension(@NotNull Rendition rendition) {
80 return getDimension(rendition, false);
81 }
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97 public static @Nullable Dimension getDimension(@NotNull Rendition rendition,
98 boolean suppressLogWarningNoRenditionsMetadata) {
99
100 boolean isOriginal = isOriginal(rendition);
101 String fileExtension = FilenameUtils.getExtension(getFilename(rendition));
102
103
104 Dimension dimension = null;
105 if (isOriginal) {
106
107 dimension = getDimensionFromOriginal(rendition);
108 }
109
110
111 if (MediaFileType.isImage(fileExtension)) {
112 if (dimension == null) {
113
114 dimension = getDimensionFromAemRenditionMetadata(rendition);
115 }
116
117 if (dimension == null) {
118
119 dimension = getDimensionFromMediaHandlerRenditionMetadata(rendition);
120 }
121
122
123
124 if (dimension == null) {
125 dimension = getDimensionFromImageBinary(rendition, suppressLogWarningNoRenditionsMetadata);
126 }
127 }
128
129 return dimension;
130 }
131
132
133
134
135
136
137 private static @Nullable Dimension getDimensionFromOriginal(@NotNull Rendition rendition) {
138 Asset asset = rendition.getAsset();
139
140 long width = getAssetMetadataValueAsLong(asset, TIFF_IMAGEWIDTH, EXIF_PIXELXDIMENSION);
141 long height = getAssetMetadataValueAsLong(asset, TIFF_IMAGELENGTH, EXIF_PIXELYDIMENSION);
142 return toValidDimension(width, height);
143 }
144
145 private static long getAssetMetadataValueAsLong(Asset asset, String... propertyNames) {
146 for (String propertyName : propertyNames) {
147 long value = NumberUtils.toLong(StringUtils.defaultString(asset.getMetadataValueFromJcr(propertyName), "0"));
148 if (value > 0L) {
149 return value;
150 }
151 }
152 return 0L;
153 }
154
155
156
157
158
159
160 @SuppressWarnings("java:S1075")
161 private static @Nullable Dimension getDimensionFromMediaHandlerRenditionMetadata(@NotNull Rendition rendition) {
162 Asset asset = rendition.getAsset();
163 String metadataPath = JCR_CONTENT + "/" + NN_RENDITIONS_METADATA + "/" + rendition.getName();
164 Resource metadataResource = AdaptTo.notNull(asset, Resource.class).getChild(metadataPath);
165 if (metadataResource != null) {
166 ValueMap props = metadataResource.getValueMap();
167 long width = props.get(PN_IMAGE_WIDTH, 0L);
168 long height = props.get(PN_IMAGE_HEIGHT, 0L);
169 return toValidDimension(width, height);
170 }
171 return null;
172 }
173
174
175
176
177
178
179
180
181 private static @Nullable Dimension getDimensionFromAemRenditionMetadata(@NotNull Rendition rendition) {
182 Resource metadataResource = rendition.getChild(JCR_CONTENT + "/" + METADATA_FOLDER);
183 if (metadataResource != null) {
184 ValueMap props = metadataResource.getValueMap();
185 long width = props.get(TIFF_IMAGEWIDTH, 0L);
186 long height = props.get(TIFF_IMAGELENGTH, 0L);
187 return toValidDimension(width, height);
188 }
189 return null;
190 }
191
192
193
194
195
196
197
198
199 @SuppressWarnings("PMD.GuardLogStatement")
200 private static @Nullable Dimension getDimensionFromImageBinary(@NotNull Rendition rendition,
201 boolean suppressLogWarningNoRenditionsMetadata) {
202 try (InputStream is = rendition.getStream()) {
203 if (is != null) {
204 Layer layer = new Layer(is);
205 long width = layer.getWidth();
206 long height = layer.getHeight();
207 Dimension dimension = toValidDimension(width, height);
208 if (!suppressLogWarningNoRenditionsMetadata) {
209 log.warn("Unable to detect rendition metadata for {}, "
210 + "fallback to inefficient detection by loading image into in memory (detected dimension={}). "
211 + "Please check if the service user for the bundle 'io.wcm.handler.media' is configured properly.",
212 rendition.getPath(), dimension);
213 }
214 return dimension;
215 }
216 else {
217 log.warn("Unable to get binary stream for rendition {}", rendition.getPath());
218 }
219 }
220 catch (IOException ex) {
221 log.warn("Unable to read binary stream to layer for rendition {}", rendition.getPath(), ex);
222 }
223 return null;
224 }
225
226
227
228
229
230
231
232 private static @Nullable Dimension toValidDimension(long width, long height) {
233 if (width > 0L && height > 0L) {
234 return new Dimension(width, height);
235 }
236 return null;
237 }
238
239
240
241
242
243
244 public static boolean isOriginal(@NotNull Rendition rendition) {
245 return StringUtils.equals(rendition.getName(), ORIGINAL_FILE);
246 }
247
248
249
250
251
252
253
254 public static boolean isThumbnailRendition(@NotNull Rendition rendition) {
255 return AemRenditionType.THUMBNAIL_RENDITION.matches(rendition);
256 }
257
258
259
260
261
262
263
264 public static boolean isWebRendition(@NotNull Rendition rendition) {
265 return AemRenditionType.WEB_RENDITION.matches(rendition);
266 }
267
268
269
270
271
272
273 public static String getFilename(@NotNull Rendition rendition) {
274 boolean isOriginal = isOriginal(rendition);
275 if (isOriginal) {
276 return rendition.getAsset().getName();
277 }
278 else {
279 return rendition.getName();
280 }
281 }
282
283 }