001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.imaging.formats.jpeg; 018 019import java.awt.Dimension; 020import java.awt.image.BufferedImage; 021import java.io.ByteArrayInputStream; 022import java.io.IOException; 023import java.util.ArrayList; 024import java.util.List; 025 026import javax.imageio.ImageIO; 027 028import org.apache.commons.imaging.ImageReadException; 029import org.apache.commons.imaging.Imaging; 030import org.apache.commons.imaging.ImagingException; 031import org.apache.commons.imaging.common.ImageMetadata; 032import org.apache.commons.imaging.formats.tiff.JpegImageData; 033import org.apache.commons.imaging.formats.tiff.TiffField; 034import org.apache.commons.imaging.formats.tiff.TiffImageData; 035import org.apache.commons.imaging.formats.tiff.TiffImageMetadata; 036import org.apache.commons.imaging.formats.tiff.taginfos.TagInfo; 037import org.apache.commons.imaging.internal.Debug; 038 039public class JpegImageMetadata implements ImageMetadata { 040 private final JpegPhotoshopMetadata photoshop; 041 private final TiffImageMetadata exif; 042 private static final String NEWLINE = System.getProperty("line.separator"); 043 044 public JpegImageMetadata(final JpegPhotoshopMetadata photoshop, 045 final TiffImageMetadata exif) { 046 this.photoshop = photoshop; 047 this.exif = exif; 048 } 049 050 public TiffImageMetadata getExif() { 051 return exif; 052 } 053 054 public JpegPhotoshopMetadata getPhotoshop() { 055 return photoshop; 056 } 057 058 public TiffField findEXIFValue(final TagInfo tagInfo) { 059 try { 060 return exif != null ? exif.findField(tagInfo) : null; 061 } catch (final ImageReadException cannotHappen) { 062 return null; 063 } 064 } 065 066 public TiffField findEXIFValueWithExactMatch(final TagInfo tagInfo) { 067 try { 068 return exif != null ? exif.findField(tagInfo, true) : null; 069 } catch (final ImageReadException cannotHappen) { 070 return null; 071 } 072 } 073 074 /** 075 * Returns the size of the first JPEG thumbnail found in the EXIF metadata. 076 * 077 * @return Thumbnail width and height or null if no thumbnail. 078 * @throws ImageReadException if it fails to read the image 079 * @throws IOException if it fails to read the image size 080 */ 081 public Dimension getEXIFThumbnailSize() throws ImageReadException, 082 IOException { 083 final byte[] data = getEXIFThumbnailData(); 084 085 if (data != null) { 086 return Imaging.getImageSize(data); 087 } 088 return null; 089 } 090 091 /** 092 * Returns the data of the first JPEG thumbnail found in the EXIF metadata. 093 * 094 * @return JPEG data or null if no thumbnail. 095 * @throws ImageReadException if it fails to read the image 096 * @throws IOException if an IO error occurred 097 */ 098 public byte[] getEXIFThumbnailData() throws ImageReadException, IOException { 099 if (exif == null) { 100 return null; 101 } 102 final List<? extends ImageMetadataItem> dirs = exif.getDirectories(); 103 for (final ImageMetadataItem d : dirs) { 104 final TiffImageMetadata.Directory dir = (TiffImageMetadata.Directory) d; 105 106 byte[] data = null; 107 if (dir.getJpegImageData() != null) { 108 data = dir.getJpegImageData().getData(); 109 } 110 // Support other image formats here. 111 112 if (data != null) { 113 // already cloned, safe to return this copy 114 return data; 115 } 116 } 117 return null; 118 } 119 120 /** 121 * Get the thumbnail image if available. 122 * 123 * @return the thumbnail image. May be {@code null} if no image could 124 * be found. 125 * @throws ImageReadException if it fails to read the image 126 * @throws IOException if it fails to get the thumbnail or to read the image data 127 */ 128 public BufferedImage getEXIFThumbnail() throws ImageReadException, 129 IOException { 130 131 if (exif == null) { 132 return null; 133 } 134 135 final List<? extends ImageMetadataItem> dirs = exif.getDirectories(); 136 for (final ImageMetadataItem d : dirs) { 137 final TiffImageMetadata.Directory dir = (TiffImageMetadata.Directory) d; 138 // Debug.debug("dir", dir); 139 BufferedImage image = dir.getThumbnail(); 140 if (null != image) { 141 return image; 142 } 143 144 final JpegImageData jpegImageData = dir.getJpegImageData(); 145 if (jpegImageData != null) { 146 // JPEG thumbnail as JPEG or other format; try to parse. 147 boolean imageSucceeded = false; 148 try { 149 image = Imaging.getBufferedImage(jpegImageData.getData()); 150 imageSucceeded = true; 151 } catch (final ImagingException imagingException) { // NOPMD 152 } catch (final IOException ioException) { // NOPMD 153 } finally { 154 // our JPEG reading is still a bit buggy - 155 // fall back to ImageIO on error 156 if (!imageSucceeded) { 157 final ByteArrayInputStream input = new ByteArrayInputStream( 158 jpegImageData.getData()); 159 image = ImageIO.read(input); 160 } 161 } 162 if (image != null) { 163 return image; 164 } 165 } 166 } 167 168 return null; 169 } 170 171 public TiffImageData getRawImageData() { 172 if (exif == null) { 173 return null; 174 } 175 final List<? extends ImageMetadataItem> dirs = exif.getDirectories(); 176 for (final ImageMetadataItem d : dirs) { 177 final TiffImageMetadata.Directory dir = (TiffImageMetadata.Directory) d; 178 // Debug.debug("dir", dir); 179 final TiffImageData rawImageData = dir.getTiffImageData(); 180 if (null != rawImageData) { 181 return rawImageData; 182 } 183 } 184 185 return null; 186 } 187 188 @Override 189 public List<ImageMetadataItem> getItems() { 190 final List<ImageMetadataItem> result = new ArrayList<>(); 191 192 if (null != exif) { 193 result.addAll(exif.getItems()); 194 } 195 196 if (null != photoshop) { 197 result.addAll(photoshop.getItems()); 198 } 199 200 return result; 201 } 202 203 @Override 204 public String toString() { 205 return toString(null); 206 } 207 208 @Override 209 public String toString(String prefix) { 210 if (prefix == null) { 211 prefix = ""; 212 } 213 214 final StringBuilder result = new StringBuilder(); 215 216 result.append(prefix); 217 if (null == exif) { 218 result.append("No Exif metadata."); 219 } else { 220 result.append("Exif metadata:"); 221 result.append(NEWLINE); 222 result.append(exif.toString("\t")); 223 } 224 225 // if (null != exif && null != photoshop) 226 result.append(NEWLINE); 227 228 result.append(prefix); 229 if (null == photoshop) { 230 result.append("No Photoshop (IPTC) metadata."); 231 } else { 232 result.append("Photoshop (IPTC) metadata:"); 233 result.append(NEWLINE); 234 result.append(photoshop.toString("\t")); 235 } 236 237 return result.toString(); 238 } 239 240 public void dump() { 241 Debug.debug(this.toString()); 242 } 243 244}