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.Imaging; 029import org.apache.commons.imaging.ImagingException; 030import org.apache.commons.imaging.common.ImageMetadata; 031import org.apache.commons.imaging.formats.tiff.AbstractTiffImageData; 032import org.apache.commons.imaging.formats.tiff.JpegImageData; 033import org.apache.commons.imaging.formats.tiff.TiffField; 034import org.apache.commons.imaging.formats.tiff.TiffImageMetadata; 035import org.apache.commons.imaging.formats.tiff.taginfos.TagInfo; 036import org.apache.commons.imaging.internal.Debug; 037 038public class JpegImageMetadata implements ImageMetadata { 039 private static final String NEWLINE = System.lineSeparator(); 040 private final JpegPhotoshopMetadata photoshop; 041 private final TiffImageMetadata exif; 042 043 public JpegImageMetadata(final JpegPhotoshopMetadata photoshop, final TiffImageMetadata exif) { 044 this.photoshop = photoshop; 045 this.exif = exif; 046 } 047 048 public void dump() { 049 Debug.debug(this.toString()); 050 } 051 052 public TiffField findExifValue(final TagInfo tagInfo) { 053 try { 054 return exif != null ? exif.findField(tagInfo) : null; 055 } catch (final ImagingException cannotHappen) { 056 return null; 057 } 058 } 059 060 public TiffField findExifValueWithExactMatch(final TagInfo tagInfo) { 061 try { 062 return exif != null ? exif.findField(tagInfo, true) : null; 063 } catch (final ImagingException cannotHappen) { 064 return null; 065 } 066 } 067 068 public TiffImageMetadata getExif() { 069 return exif; 070 } 071 072 /** 073 * Gets the thumbnail image if available. 074 * 075 * @return the thumbnail image. May be {@code null} if no image could be found. 076 * @throws ImagingException if it fails to read the image 077 * @throws IOException if it fails to get the thumbnail or to read the image data 078 */ 079 public BufferedImage getExifThumbnail() throws ImagingException, IOException { 080 081 if (exif == null) { 082 return null; 083 } 084 085 final List<? extends ImageMetadataItem> dirs = exif.getDirectories(); 086 for (final ImageMetadataItem d : dirs) { 087 final TiffImageMetadata.Directory dir = (TiffImageMetadata.Directory) d; 088 // Debug.debug("dir", dir); 089 BufferedImage image = dir.getThumbnail(); 090 if (null != image) { 091 return image; 092 } 093 094 final JpegImageData jpegImageData = dir.getJpegImageData(); 095 if (jpegImageData != null) { 096 // JPEG thumbnail as JPEG or other format; try to parse. 097 boolean imageSucceeded = false; 098 try { 099 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}