1 16 package org.outerj.daisy.presavehook.image; 17 18 import org.outerj.daisy.repository.*; 19 import org.apache.avalon.framework.service.Serviceable; 20 import org.apache.avalon.framework.service.ServiceManager; 21 import org.apache.avalon.framework.service.ServiceException; 22 import org.apache.avalon.framework.activity.Disposable; 23 import org.apache.avalon.framework.configuration.Configurable; 24 import org.apache.avalon.framework.configuration.Configuration; 25 import org.apache.avalon.framework.configuration.ConfigurationException; 26 import org.apache.avalon.framework.logger.LogEnabled; 27 import org.apache.avalon.framework.logger.Logger; 28 29 import javax.imageio.ImageIO ; 30 import java.awt.image.BufferedImage ; 31 import java.awt.*; 32 import java.io.InputStream ; 33 import java.io.ByteArrayOutputStream ; 34 import java.util.Map ; 35 import java.util.HashMap ; 36 import java.util.Iterator ; 37 import java.util.Collections ; 38 39 import EDU.oswego.cs.dl.util.concurrent.FIFOSemaphore; 40 import com.sun.image.codec.jpeg.JPEGImageDecoder; 41 import com.sun.image.codec.jpeg.JPEGCodec; 42 import com.sun.image.codec.jpeg.JPEGDecodeParam; 43 import com.drew.metadata.Metadata; 44 import com.drew.metadata.Directory; 45 import com.drew.metadata.MetadataException; 46 import com.drew.metadata.Tag; 47 import com.drew.metadata.exif.ExifDirectory; 48 import com.drew.imaging.jpeg.JpegMetadataReader; 49 50 57 public class ImagePreSaveHook implements PreSaveHook, Serviceable, Disposable, Configurable, LogEnabled { 58 private Logger logger; 59 private ServiceManager serviceManager; 60 private PreSaveHookRegistrar preSaveHookRegistrar; 61 private FIFOSemaphore imageProcessingSemaphore; 62 private Map docTypeConfigs = new HashMap (); 63 64 public void enableLogging(Logger logger) { 65 this.logger = logger; 66 } 67 68 71 public void service(ServiceManager serviceManager) throws ServiceException { 72 this.serviceManager = serviceManager; 73 preSaveHookRegistrar = (PreSaveHookRegistrar)serviceManager.lookup("presavehook-registrar"); 74 preSaveHookRegistrar.registerPreSaveHook(this); 75 } 76 77 public void configure(Configuration configuration) throws ConfigurationException { 78 int concurrentOperations = configuration.getChild("maxConcurrentOperations").getValueAsInteger(); 79 imageProcessingSemaphore = new FIFOSemaphore(concurrentOperations); 80 81 Configuration[] configs = configuration.getChildren("config"); 82 for (int i = 0; i < configs.length; i++) { 83 Configuration config = configs[i]; 84 String docType = config.getAttribute("documentType"); 85 86 if (docTypeConfigs.containsKey(docType)) { 87 continue; 90 } 91 92 DoctypeConfig dtconfig = new DoctypeConfig(); 93 dtconfig.enabled = config.getChild("enabled").getValueAsBoolean(true); 94 dtconfig.maxImageSize = config.getChild("maxImageSize").getValueAsInteger(3000000); 95 dtconfig.imagePartName = config.getChild("imagePartName").getValue(); 96 dtconfig.widthFieldName = config.getChild("widthFieldName").getValue(null); 97 dtconfig.heightFieldName = config.getChild("heightFieldName").getValue(null); 98 dtconfig.previewPartName = config.getChild("previewPartName").getValue(null); 99 dtconfig.previewMaxSize = config.getChild("previewMaxSize").getValueAsInteger(250); 100 dtconfig.thumbnailPartName = config.getChild("thumbnailPartName").getValue(null); 101 dtconfig.thumbnailMaxSize = config.getChild("thumbnailMaxSize").getValueAsInteger(125); 102 103 Configuration[] metadatas = config.getChildren("metadata"); 104 for (int k = 0; k < metadatas.length; k++) { 105 String type = metadatas[k].getAttribute("type"); 106 if (!METADATA_VALUE_GETTERS.containsKey(type)) 107 throw new ConfigurationException("ImagePreSaveHook: metadata/@type has an invalid value: " + type + " at " + metadatas[k].getLocation()); 108 dtconfig.addMetadata(metadatas[k].getAttribute("tag"), metadatas[k].getAttribute("field"), type); 109 } 110 111 docTypeConfigs.put(docType, dtconfig); 112 } 113 } 114 115 public void dispose() { 116 preSaveHookRegistrar.unregisterPreSaveHook(this); 117 serviceManager.release(preSaveHookRegistrar); 118 } 119 120 public String getName() { 121 return "image-pre-save-hook"; 122 } 123 124 public void process(Document document, Repository repository) throws Exception { 125 imageProcessingSemaphore.acquire(); 128 try { 129 String documentTypeName = repository.getRepositorySchema().getDocumentTypeById(document.getDocumentTypeId(), false).getName(); 130 DoctypeConfig dtconfig = (DoctypeConfig)docTypeConfigs.get(documentTypeName); 131 if (dtconfig == null) 132 return; 133 if (!dtconfig.enabled) 134 return; 135 136 if (dtconfig.previewPartName != null) 139 document.deletePart(dtconfig.previewPartName); 140 if (dtconfig.thumbnailPartName != null) 141 document.deletePart(dtconfig.thumbnailPartName); 142 if (dtconfig.widthFieldName != null) 143 document.deleteField(dtconfig.widthFieldName); 144 if (dtconfig.heightFieldName != null) 145 document.deleteField(dtconfig.heightFieldName); 146 Iterator metadataInfoIt = dtconfig.getMetadataInfoIterator(); 147 while (metadataInfoIt.hasNext()) { 148 MetadataInfo metadataInfo = (MetadataInfo)metadataInfoIt.next(); 149 document.deleteField(metadataInfo.field); 150 } 151 152 if (!document.hasPart(dtconfig.imagePartName)) 153 return; 154 155 Part part = document.getPart(dtconfig.imagePartName); 156 157 long size = part.getSize(); 159 if (size > dtconfig.maxImageSize) { 160 if (logger.isInfoEnabled()) { 161 logger.info("Skipped image information extraction as the image was too large."); 162 } 163 } else if (size == 0) { 164 logger.info("Skipped image information extraction as the image size was unknown."); 165 } 166 167 InputStream is = null; 169 BufferedImage sourceImage; 170 Metadata metadata = null; 171 try { 172 is = part.getDataStream(); 173 String mimeType = part.getMimeType(); 174 if (mimeType.equals("image/jpeg") || mimeType.equals("image/x-jpeg")) { 175 JPEGImageDecoder jpegDecoder = JPEGCodec.createJPEGDecoder(is); 176 sourceImage = jpegDecoder.decodeAsBufferedImage(); 177 JPEGDecodeParam decodeParam = jpegDecoder.getJPEGDecodeParam(); 178 metadata = JpegMetadataReader.readMetadata(decodeParam); 179 } else { 180 sourceImage = ImageIO.read(is); 181 } 182 } finally { 183 if (is != null) 184 is.close(); 185 } 186 187 if (sourceImage == null) { 188 return; 190 } 191 192 int width = sourceImage.getWidth(); 194 int height = sourceImage.getHeight(); 195 196 if (dtconfig.widthFieldName != null) 197 document.setField(dtconfig.widthFieldName, new Long (width)); 198 if (dtconfig.heightFieldName != null) 199 document.setField(dtconfig.heightFieldName, new Long (height)); 200 201 if (metadata != null && dtconfig.isSetMetadata()) { 202 Directory dir = metadata.getDirectory(ExifDirectory.class); 203 MetadataInfo metadataInfo; 204 205 Iterator tagIt = dir.getTagIterator(); 206 while (tagIt.hasNext()) { 207 Tag tag = (Tag)tagIt.next(); 208 if (logger.isDebugEnabled()) 209 logger.debug("[" + tag.getTagType() + "] " + tag.getTagName() + " : " + tag.getDescription() + " (" + dir.getObject(tag.getTagType()).getClass().getName() + ") (" + dir.getObject(tag.getTagType()) + ")"); 210 metadataInfo = dtconfig.getMetadataInfo(tag.getTagName()); 211 if (metadataInfo != null) { 212 Object value = metadataInfo.getValueGetter().getValue(tag.getTagType(), dir); 213 document.setField(metadataInfo.field, value); 214 } 215 } 216 } 217 218 if (dtconfig.previewPartName != null) { 220 BufferedImage previewImage = resizeImage(sourceImage, dtconfig.previewMaxSize); 221 sourceImage = previewImage; ByteArrayOutputStream previewData = new ByteArrayOutputStream (); 223 ImageIO.write(previewImage, "jpg", previewData); 224 document.setPart(dtconfig.previewPartName, "image/jpeg", previewData.toByteArray()); 225 } 226 227 if (dtconfig.thumbnailPartName != null) { 228 BufferedImage thumbnailImage = resizeImage(sourceImage, dtconfig.thumbnailMaxSize); 229 ByteArrayOutputStream thumbnailData = new ByteArrayOutputStream (); 230 ImageIO.write(thumbnailImage, "jpg", thumbnailData); 231 document.setPart(dtconfig.thumbnailPartName, "image/jpeg", thumbnailData.toByteArray()); 232 } 233 } finally { 234 imageProcessingSemaphore.release(); 235 } 236 } 237 238 private BufferedImage resizeImage(BufferedImage sourceImage, int maxSize) { 239 Image resizedImage; 240 int width = sourceImage.getWidth(); 241 int height = sourceImage.getHeight(); 242 243 if (Math.max(width, height) <= maxSize) 245 return sourceImage; 246 247 if (width > height) 248 resizedImage = sourceImage.getScaledInstance(maxSize, (maxSize * height) / width, Image.SCALE_SMOOTH); 249 else 250 resizedImage = sourceImage.getScaledInstance((maxSize * width) / height, maxSize, Image.SCALE_SMOOTH); 251 252 BufferedImage bufferedImage = new BufferedImage (resizedImage.getWidth(null), resizedImage.getHeight(null), 254 BufferedImage.TYPE_INT_RGB); 255 Graphics g = bufferedImage.createGraphics(); 257 258 g.setColor(Color.white); 260 g.fillRect(0, 0, resizedImage.getWidth(null), resizedImage.getHeight(null)); 261 262 g.drawImage(resizedImage, 0, 0, null); 263 g.dispose(); 264 return bufferedImage; 265 } 266 267 static class DoctypeConfig { 268 public boolean enabled; 269 public int maxImageSize; 270 public String imagePartName; 271 public String widthFieldName; 272 public String heightFieldName; 273 public String previewPartName; 274 public int previewMaxSize; 275 public String thumbnailPartName; 276 public int thumbnailMaxSize; 277 private Map metadatas; 278 279 public boolean isSetMetadata() { 280 return metadatas != null; 281 } 282 283 public MetadataInfo getMetadataInfo(String tagName) { 284 if (metadatas != null) 285 return (MetadataInfo)metadatas.get(tagName); 286 else 287 return null; 288 } 289 290 public Iterator getMetadataInfoIterator() { 291 if (metadatas != null) 292 return metadatas.values().iterator(); 293 else 294 return Collections.EMPTY_LIST.iterator(); 295 } 296 297 public void addMetadata(String tagName, String field, String type) { 298 if (metadatas == null) 299 metadatas = new HashMap (); 300 MetadataInfo metadataInfo = new MetadataInfo(); 301 metadataInfo.field = field; 302 metadataInfo.type = type; 303 metadatas.put(tagName, metadataInfo); 304 } 305 } 306 307 static class MetadataInfo { 308 public String field; 309 public String type; 310 311 public MetadataValueGetter getValueGetter() { 312 return (MetadataValueGetter)METADATA_VALUE_GETTERS.get(type); 313 } 314 } 315 316 static interface MetadataValueGetter { 317 Object getValue(int tagType, Directory directory) throws MetadataException; 318 } 319 320 static class StringValueGetter implements MetadataValueGetter { 321 public Object getValue(int tagType, Directory directory) { 322 return directory.getString(tagType); 323 } 324 } 325 326 static class DateValueGetter implements MetadataValueGetter { 327 public Object getValue(int tagType, Directory directory) throws MetadataException { 328 return directory.getDate(tagType); 329 } 330 } 331 332 static class LongValueGetter implements MetadataValueGetter { 333 public Object getValue(int tagType, Directory directory) throws MetadataException { 334 return new Long (directory.getLong(tagType)); 335 } 336 } 337 338 static class DoubleValueGetter implements MetadataValueGetter { 339 public Object getValue(int tagType, Directory directory) throws MetadataException { 340 return new Double (directory.getDouble(tagType)); 341 } 342 } 343 344 static class DescriptionValueGetter implements MetadataValueGetter { 345 public Object getValue(int tagType, Directory directory) throws MetadataException { 346 return directory.getDescription(tagType); 347 } 348 } 349 350 private static Map METADATA_VALUE_GETTERS; 351 static { 352 METADATA_VALUE_GETTERS = new HashMap (); 353 METADATA_VALUE_GETTERS.put("description", new DescriptionValueGetter()); 354 METADATA_VALUE_GETTERS.put("string", new StringValueGetter()); 355 METADATA_VALUE_GETTERS.put("datetime", new DateValueGetter()); 356 METADATA_VALUE_GETTERS.put("long", new LongValueGetter()); 357 METADATA_VALUE_GETTERS.put("double", new DoubleValueGetter()); 358 } 359 } 360 | Popular Tags |