1 /** 2 * Copyright 2011, Aiki IT, FotoRenamer 3 * <p/> 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * <p/> 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * <p/> 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package de.aikiit.fotorenamer.image; 17 18 import com.google.common.base.Strings; 19 import org.apache.commons.imaging.ImageReadException; 20 import org.apache.commons.imaging.Imaging; 21 import org.apache.commons.imaging.common.ImageMetadata; 22 import org.apache.commons.imaging.formats.jpeg.JpegImageMetadata; 23 import org.apache.commons.imaging.formats.tiff.TiffField; 24 import org.apache.commons.imaging.formats.tiff.constants.ExifTagConstants; 25 import org.apache.commons.imaging.formats.tiff.taginfos.TagInfo; 26 import org.apache.log4j.Logger; 27 28 import java.io.File; 29 import java.io.IOException; 30 31 /** 32 * Helper class to extract metadata from given images. This class uses Apache 33 * Sanslean to perform the metadata extraction itself. 34 */ 35 final class MetaDataExtractor { 36 /** 37 * This class' logger. 38 */ 39 private static final Logger LOG = Logger.getLogger(MetaDataExtractor.class); 40 41 /** 42 * Constant for an empty string. 43 */ 44 private static final String EMPTY_STRING = ""; 45 46 /** 47 * Constant for a blank character. 48 */ 49 private static final String SPACE = " "; 50 51 /** 52 * Constant for an underscore character. 53 */ 54 private static final String UNDERSCORE = "_"; 55 56 /** 57 * Constant for a colon. 58 */ 59 private static final String COLON = ":"; 60 61 /** 62 * Constant for an apostrophe. 63 */ 64 private static final String APOSTROPHE = "'"; 65 66 /** 67 * Constant to describe a valid length of an EXIF date (currently 21). 68 */ 69 private static final int VALID_EXIF_DATE_LENGTH = 21; 70 71 /** 72 * Constructor is not visible to avoid instantiation. 73 */ 74 private MetaDataExtractor() { 75 // prevent instantiation of this utility class 76 } 77 78 /** 79 * Returns the requested tag as String from the image file. 80 * 81 * @param image Image file to extract Metadata from. 82 * @param tag Tag to extract from the given file, @see TiffConstants 83 * @return Returns exif tag value, in case of any errors the value is an 84 * empty String. 85 * @throws IOException if file cannot be accessed. 86 * @throws ImageReadException if an error occurred during image processing. 87 */ 88 public static String getExifMetadata(final File image, 89 final TagInfo tag) 90 throws IOException, ImageReadException { 91 assert image != null : "Parameter image must not be null"; 92 assert tag != null : "Parameter tag must not be null"; 93 94 String result = EMPTY_STRING; 95 ImageMetadata metadata = Imaging.getMetadata(image); 96 97 if (metadata instanceof JpegImageMetadata) { 98 JpegImageMetadata jpegMetadata = (JpegImageMetadata) metadata; 99 TiffField field = jpegMetadata.findEXIFValueWithExactMatch(tag); 100 if (field != null) { 101 result = field.getValueDescription(); 102 LOG.info("extraction of " + tag.getDescription() 103 + " yields " + result); 104 } 105 } 106 return result == null ? EMPTY_STRING : result; 107 } 108 109 /** 110 * Helper to extract the date this image was created to be used during the 111 * renaming process. 112 * <br> 113 * In the EXIF standard itself the following convention for dates as is 114 * defined: <i> D. Other Tags DateTime The date and time of image creation. 115 * In this standard it is the date and time the file was changed. The format 116 * is "YYYY:MM:DD HH:MM:SS" with time shown in 24-hour format, and the date 117 * and time separated by one blank character [20.H]. When the date and time 118 * are unknown, all the character spaces except colons (":") may be filled 119 * with blank characters, or else the Interoperability field may be filled 120 * with blank characters. The character string length is 20 bytes including 121 * NULL for termination. When the field is left blank, it is treated as 122 * unknown. Tag = 306 (132.H) Type = ASCII Count = 20 Default = none </i> 123 * <p> 124 * If the extracted date value is empty - no new file name is generated. 125 * 126 * @param image Image to extract metadata from. 127 * @return the date this image was created if found, format is 128 * @throws ImageReadException If image cannot be read. 129 * @throws IOException If an error occurs when accessing the image's 130 * metadata. 131 * @see <a href="http://www.exif.org/samples/canon-ixus.html">Canon EXIF 132 * example page</a> 133 * @see <a href="http://www.exif.org/specifications.html">EXIF 134 * specifications</a> 135 * @see <a href="http://www.exif.org/Exif2-2.PDF">EXIF2-2.pdf 136 * specification</a> 137 */ 138 public static String generateCreationDateInCorrectFormat(final File image) 139 throws ImageReadException, IOException { 140 141 String dateValue = 142 getExifMetadata(image, ExifTagConstants.EXIF_TAG_DATE_TIME_ORIGINAL); 143 144 LOG.info("EXIF date value is: " + dateValue); 145 146 // Invalid length of EXIF metadata, not complying to the standard. 147 if (Strings.isNullOrEmpty(dateValue) || dateValue.length() != VALID_EXIF_DATE_LENGTH) { 148 LOG.info("No valid creation date extracted from file " + image); 149 return EMPTY_STRING; 150 } 151 152 // Date parsing with apache.DateUtils or JDK-DateFormats 153 // does not work due to '-signs in the date string 154 // (unparseable pattern is "'yyyy:MM:dd HH:mm:ss'") 155 // replace special characters to extract digits only 156 dateValue = dateValue.replaceAll(APOSTROPHE, EMPTY_STRING); 157 dateValue = dateValue.replaceAll(COLON, EMPTY_STRING); 158 dateValue = dateValue.replaceAll(SPACE, UNDERSCORE); 159 dateValue += UNDERSCORE; 160 //convert '2011:01:30 13:11:02' to "yyyyMMdd_HHmm_"+fileName 161 dateValue += image.getName(); 162 163 LOG.info("Target filename is: " + dateValue); 164 return dateValue; 165 } 166 }