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