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 079 * @throws IOException 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 096 * @throws IOException 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(); // TODO clone? 109 } 110 // Support other image formats here. 111 112 if (data != null) { 113 return data; 114 } 115 } 116 return null; 117 } 118 119 /** 120 * Get the thumbnail image if available. 121 * 122 * @return the thumbnail image. May be <code>null</code> if no image could 123 * be found. 124 * @throws ImageReadException 125 * @throws IOException 126 */ 127 public BufferedImage getEXIFThumbnail() throws ImageReadException, 128 IOException { 129 130 if (exif == null) { 131 return null; 132 } 133 134 final List<? extends ImageMetadataItem> dirs = exif.getDirectories(); 135 for (final ImageMetadataItem d : dirs) { 136 final TiffImageMetadata.Directory dir = (TiffImageMetadata.Directory) d; 137 // Debug.debug("dir", dir); 138 BufferedImage image = dir.getThumbnail(); 139 if (null != image) { 140 return image; 141 } 142 143 final JpegImageData jpegImageData = dir.getJpegImageData(); 144 if (jpegImageData != null) { 145 // JPEG thumbnail as JPEG or other format; try to parse. 146 boolean imageSucceeded = false; 147 try { 148 image = Imaging.getBufferedImage(jpegImageData.getData()); 149 imageSucceeded = true; 150 } catch (final ImagingException imagingException) { // NOPMD 151 } catch (final IOException ioException) { // NOPMD 152 } finally { 153 // our JPEG reading is still a bit buggy - 154 // fall back to ImageIO on error 155 if (!imageSucceeded) { 156 final ByteArrayInputStream input = new ByteArrayInputStream( 157 jpegImageData.getData()); 158 image = ImageIO.read(input); 159 } 160 } 161 if (image != null) { 162 return image; 163 } 164 } 165 } 166 167 return null; 168 } 169 170 public TiffImageData getRawImageData() { 171 if (exif == null) { 172 return null; 173 } 174 final List<? extends ImageMetadataItem> dirs = exif.getDirectories(); 175 for (final ImageMetadataItem d : dirs) { 176 final TiffImageMetadata.Directory dir = (TiffImageMetadata.Directory) d; 177 // Debug.debug("dir", dir); 178 final TiffImageData rawImageData = dir.getTiffImageData(); 179 if (null != rawImageData) { 180 return rawImageData; 181 } 182 } 183 184 return null; 185 } 186 187 @Override 188 public List<ImageMetadataItem> getItems() { 189 final List<ImageMetadataItem> result = new ArrayList<>(); 190 191 if (null != exif) { 192 result.addAll(exif.getItems()); 193 } 194 195 if (null != photoshop) { 196 result.addAll(photoshop.getItems()); 197 } 198 199 return result; 200 } 201 202 @Override 203 public String toString() { 204 return toString(null); 205 } 206 207 @Override 208 public String toString(String prefix) { 209 if (prefix == null) { 210 prefix = ""; 211 } 212 213 final StringBuilder result = new StringBuilder(); 214 215 result.append(prefix); 216 if (null == exif) { 217 result.append("No Exif metadata."); 218 } else { 219 result.append("Exif metadata:"); 220 result.append(NEWLINE); 221 result.append(exif.toString("\t")); 222 } 223 224 // if (null != exif && null != photoshop) 225 result.append(NEWLINE); 226 227 result.append(prefix); 228 if (null == photoshop) { 229 result.append("No Photoshop (IPTC) metadata."); 230 } else { 231 result.append("Photoshop (IPTC) metadata:"); 232 result.append(NEWLINE); 233 result.append(photoshop.toString("\t")); 234 } 235 236 return result.toString(); 237 } 238 239 public void dump() { 240 Debug.debug(this.toString()); 241 } 242 243}