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.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 }