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.Imaging;
29  import org.apache.commons.imaging.ImagingException;
30  import org.apache.commons.imaging.common.ImageMetadata;
31  import org.apache.commons.imaging.formats.tiff.AbstractTiffImageData;
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.TiffImageMetadata;
35  import org.apache.commons.imaging.formats.tiff.taginfos.TagInfo;
36  import org.apache.commons.imaging.internal.Debug;
37  
38  public class JpegImageMetadata implements ImageMetadata {
39      private static final String NEWLINE = System.lineSeparator();
40      private final JpegPhotoshopMetadata photoshop;
41      private final TiffImageMetadata exif;
42  
43      public JpegImageMetadata(final JpegPhotoshopMetadata photoshop, final TiffImageMetadata exif) {
44          this.photoshop = photoshop;
45          this.exif = exif;
46      }
47  
48      public void dump() {
49          Debug.debug(this.toString());
50      }
51  
52      public TiffField findExifValue(final TagInfo tagInfo) {
53          try {
54              return exif != null ? exif.findField(tagInfo) : null;
55          } catch (final ImagingException cannotHappen) {
56              return null;
57          }
58      }
59  
60      public TiffField findExifValueWithExactMatch(final TagInfo tagInfo) {
61          try {
62              return exif != null ? exif.findField(tagInfo, true) : null;
63          } catch (final ImagingException cannotHappen) {
64              return null;
65          }
66      }
67  
68      public TiffImageMetadata getExif() {
69          return exif;
70      }
71  
72      /**
73       * Gets the thumbnail image if available.
74       *
75       * @return the thumbnail image. May be {@code null} if no image could be found.
76       * @throws ImagingException if it fails to read the image
77       * @throws IOException      if it fails to get the thumbnail or to read the image data
78       */
79      public BufferedImage getExifThumbnail() throws ImagingException, IOException {
80  
81          if (exif == null) {
82              return null;
83          }
84  
85          final List<? extends ImageMetadataItem> dirs = exif.getDirectories();
86          for (final ImageMetadataItem d : dirs) {
87              final TiffImageMetadata.Directory dir = (TiffImageMetadata.Directory) d;
88              // Debug.debug("dir", dir);
89              BufferedImage image = dir.getThumbnail();
90              if (null != image) {
91                  return image;
92              }
93  
94              final JpegImageData jpegImageData = dir.getJpegImageData();
95              if (jpegImageData != null) {
96                  // JPEG thumbnail as JPEG or other format; try to parse.
97                  boolean imageSucceeded = false;
98                  try {
99                      image = Imaging.getBufferedImage(jpegImageData.getData());
100                     imageSucceeded = true;
101                 } catch (final IOException ignored) { // NOPMD
102                 } finally {
103                     // our JPEG reading is still a bit buggy -
104                     // fall back to ImageIO on error
105                     if (!imageSucceeded) {
106                         final ByteArrayInputStream input = new ByteArrayInputStream(jpegImageData.getData());
107                         image = ImageIO.read(input);
108                     }
109                 }
110                 if (image != null) {
111                     return image;
112                 }
113             }
114         }
115 
116         return null;
117     }
118 
119     /**
120      * Returns the data of the first JPEG thumbnail found in the EXIF metadata.
121      *
122      * @return JPEG data or null if no thumbnail.
123      */
124     public byte[] getExifThumbnailData() {
125         if (exif == null) {
126             return null;
127         }
128         final List<? extends ImageMetadataItem> dirs = exif.getDirectories();
129         for (final ImageMetadataItem d : dirs) {
130             final TiffImageMetadata.Directory dir = (TiffImageMetadata.Directory) d;
131 
132             byte[] data = null;
133             if (dir.getJpegImageData() != null) {
134                 data = dir.getJpegImageData().getData();
135             }
136             // Support other image formats here.
137 
138             if (data != null) {
139                 // already cloned, safe to return this copy
140                 return data;
141             }
142         }
143         return null;
144     }
145 
146     /**
147      * Returns the size of the first JPEG thumbnail found in the EXIF metadata.
148      *
149      * @return Thumbnail width and height or null if no thumbnail.
150      * @throws ImagingException if it fails to read the image
151      * @throws IOException      if it fails to read the image size
152      */
153     public Dimension getExifThumbnailSize() throws ImagingException, IOException {
154         final byte[] data = getExifThumbnailData();
155 
156         if (data != null) {
157             return Imaging.getImageSize(data);
158         }
159         return null;
160     }
161 
162     @Override
163     public List<ImageMetadataItem> getItems() {
164         final List<ImageMetadataItem> result = new ArrayList<>();
165 
166         if (null != exif) {
167             result.addAll(exif.getItems());
168         }
169 
170         if (null != photoshop) {
171             result.addAll(photoshop.getItems());
172         }
173 
174         return result;
175     }
176 
177     public JpegPhotoshopMetadata getPhotoshop() {
178         return photoshop;
179     }
180 
181     public AbstractTiffImageData getRawImageData() {
182         if (exif == null) {
183             return null;
184         }
185         final List<? extends ImageMetadataItem> dirs = exif.getDirectories();
186         for (final ImageMetadataItem d : dirs) {
187             final TiffImageMetadata.Directory dir = (TiffImageMetadata.Directory) d;
188             // Debug.debug("dir", dir);
189             final AbstractTiffImageData rawImageData = dir.getTiffImageData();
190             if (null != rawImageData) {
191                 return rawImageData;
192             }
193         }
194 
195         return null;
196     }
197 
198     @Override
199     public String toString() {
200         return toString(null);
201     }
202 
203     @Override
204     public String toString(String prefix) {
205         if (prefix == null) {
206             prefix = "";
207         }
208 
209         final StringBuilder result = new StringBuilder();
210 
211         result.append(prefix);
212         if (null == exif) {
213             result.append("No Exif metadata.");
214         } else {
215             result.append("Exif metadata:");
216             result.append(NEWLINE);
217             result.append(exif.toString("\t"));
218         }
219 
220         // if (null != exif && null != photoshop)
221         result.append(NEWLINE);
222 
223         result.append(prefix);
224         if (null == photoshop) {
225             result.append("No Photoshop (IPTC) metadata.");
226         } else {
227             result.append("Photoshop (IPTC) metadata:");
228             result.append(NEWLINE);
229             result.append(photoshop.toString("\t"));
230         }
231 
232         return result.toString();
233     }
234 
235 }