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