View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.imaging.formats.jpeg;
18  
19  import java.awt.Dimension;
20  import java.awt.image.BufferedImage;
21  import java.io.ByteArrayInputStream;
22  import java.io.IOException;
23  import java.util.ArrayList;
24  import java.util.List;
25  
26  import javax.imageio.ImageIO;
27  
28  import org.apache.commons.imaging.ImageReadException;
29  import org.apache.commons.imaging.Imaging;
30  import org.apache.commons.imaging.ImagingException;
31  import org.apache.commons.imaging.common.ImageMetadata;
32  import org.apache.commons.imaging.formats.tiff.JpegImageData;
33  import org.apache.commons.imaging.formats.tiff.TiffField;
34  import org.apache.commons.imaging.formats.tiff.TiffImageData;
35  import org.apache.commons.imaging.formats.tiff.TiffImageMetadata;
36  import org.apache.commons.imaging.formats.tiff.taginfos.TagInfo;
37  import org.apache.commons.imaging.internal.Debug;
38  
39  public class JpegImageMetadata implements ImageMetadata {
40      private final JpegPhotoshopMetadata photoshop;
41      private final TiffImageMetadata exif;
42      private static final String NEWLINE = System.getProperty("line.separator");
43  
44      public JpegImageMetadata(final JpegPhotoshopMetadata photoshop,
45              final TiffImageMetadata exif) {
46          this.photoshop = photoshop;
47          this.exif = exif;
48      }
49  
50      public TiffImageMetadata getExif() {
51          return exif;
52      }
53  
54      public JpegPhotoshopMetadata getPhotoshop() {
55          return photoshop;
56      }
57  
58      public TiffField findEXIFValue(final TagInfo tagInfo) {
59          try {
60              return exif != null ? exif.findField(tagInfo) : null;
61          } catch (final ImageReadException cannotHappen) {
62              return null;
63          }
64      }
65  
66      public TiffField findEXIFValueWithExactMatch(final TagInfo tagInfo) {
67          try {
68              return exif != null ? exif.findField(tagInfo, true) : null;
69          } catch (final ImageReadException cannotHappen) {
70              return null;
71          }
72      }
73  
74      /**
75       * Returns the size of the first JPEG thumbnail found in the EXIF metadata.
76       *
77       * @return Thumbnail width and height or null if no thumbnail.
78       * @throws ImageReadException if it fails to read the image
79       * @throws IOException if it fails to read the image size
80       */
81      public Dimension getEXIFThumbnailSize() throws ImageReadException,
82              IOException {
83          final byte[] data = getEXIFThumbnailData();
84  
85          if (data != null) {
86              return Imaging.getImageSize(data);
87          }
88          return null;
89      }
90  
91      /**
92       * Returns the data of the first JPEG thumbnail found in the EXIF metadata.
93       *
94       * @return JPEG data or null if no thumbnail.
95       */
96      public byte[] getEXIFThumbnailData() {
97          if (exif == null) {
98              return null;
99          }
100         final List<? extends ImageMetadataItem> dirs = exif.getDirectories();
101         for (final ImageMetadataItem d : dirs) {
102             final TiffImageMetadata.Directory dir = (TiffImageMetadata.Directory) d;
103 
104             byte[] data = null;
105             if (dir.getJpegImageData() != null) {
106                 data = dir.getJpegImageData().getData();
107             }
108             // Support other image formats here.
109 
110             if (data != null) {
111                 // already cloned, safe to return this copy
112                 return data;
113             }
114         }
115         return null;
116     }
117 
118     /**
119      * Get the thumbnail image if available.
120      *
121      * @return the thumbnail image. May be {@code null} if no image could
122      *         be found.
123      * @throws ImageReadException if it fails to read the image
124      * @throws IOException if it fails to get the thumbnail or to read the image data
125      */
126     public BufferedImage getEXIFThumbnail() throws ImageReadException,
127             IOException {
128 
129         if (exif == null) {
130             return null;
131         }
132 
133         final List<? extends ImageMetadataItem> dirs = exif.getDirectories();
134         for (final ImageMetadataItem d : dirs) {
135             final TiffImageMetadata.Directory dir = (TiffImageMetadata.Directory) d;
136             // Debug.debug("dir", dir);
137             BufferedImage image = dir.getThumbnail();
138             if (null != image) {
139                 return image;
140             }
141 
142             final JpegImageData jpegImageData = dir.getJpegImageData();
143             if (jpegImageData != null) {
144                 // JPEG thumbnail as JPEG or other format; try to parse.
145                 boolean imageSucceeded = false;
146                 try {
147                     image = Imaging.getBufferedImage(jpegImageData.getData());
148                     imageSucceeded = true;
149                 } catch (final ImagingException | IOException ioException) { // NOPMD
150                 } finally {
151                     // our JPEG reading is still a bit buggy -
152                     // fall back to ImageIO on error
153                     if (!imageSucceeded) {
154                         final ByteArrayInputStream input = new ByteArrayInputStream(
155                                 jpegImageData.getData());
156                         image = ImageIO.read(input);
157                     }
158                 }
159                 if (image != null) {
160                     return image;
161                 }
162             }
163         }
164 
165         return null;
166     }
167 
168     public TiffImageData getRawImageData() {
169         if (exif == null) {
170             return null;
171         }
172         final List<? extends ImageMetadataItem> dirs = exif.getDirectories();
173         for (final ImageMetadataItem d : dirs) {
174             final TiffImageMetadata.Directory dir = (TiffImageMetadata.Directory) d;
175             // Debug.debug("dir", dir);
176             final TiffImageData rawImageData = dir.getTiffImageData();
177             if (null != rawImageData) {
178                 return rawImageData;
179             }
180         }
181 
182         return null;
183     }
184 
185     @Override
186     public List<ImageMetadataItem> getItems() {
187         final List<ImageMetadataItem> result = new ArrayList<>();
188 
189         if (null != exif) {
190             result.addAll(exif.getItems());
191         }
192 
193         if (null != photoshop) {
194             result.addAll(photoshop.getItems());
195         }
196 
197         return result;
198     }
199 
200     @Override
201     public String toString() {
202         return toString(null);
203     }
204 
205     @Override
206     public String toString(String prefix) {
207         if (prefix == null) {
208             prefix = "";
209         }
210 
211         final StringBuilder result = new StringBuilder();
212 
213         result.append(prefix);
214         if (null == exif) {
215             result.append("No Exif metadata.");
216         } else {
217             result.append("Exif metadata:");
218             result.append(NEWLINE);
219             result.append(exif.toString("\t"));
220         }
221 
222         // if (null != exif && null != photoshop)
223         result.append(NEWLINE);
224 
225         result.append(prefix);
226         if (null == photoshop) {
227             result.append("No Photoshop (IPTC) metadata.");
228         } else {
229             result.append("Photoshop (IPTC) metadata:");
230             result.append(NEWLINE);
231             result.append(photoshop.toString("\t"));
232         }
233 
234         return result.toString();
235     }
236 
237     public void dump() {
238         Debug.debug(this.toString());
239     }
240 
241 }