View Javadoc
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 }