/*
* Weblounge: Web Content Management System
* Copyright (c) 2003 - 2011 The Weblounge Team
* http://entwinemedia.com/weblounge
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package ch.entwine.weblounge.common.impl.content.image;
import com.drew.imaging.ImageMetadataReader;
import com.drew.imaging.ImageProcessingException;
import com.drew.lang.Rational;
import com.drew.metadata.Directory;
import com.drew.metadata.Metadata;
import com.drew.metadata.MetadataException;
import com.drew.metadata.exif.ExifDirectory;
import com.drew.metadata.exif.GpsDirectory;
import com.drew.metadata.iptc.IptcDirectory;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.StringTokenizer;
/**
* Utility class for dealing with image meta data
*/
public final class ImageMetadataUtils {
/** The logging facility */
private static Logger logger = LoggerFactory.getLogger(ImageMetadataUtils.class);
/**
* This class is not meant to be instantiated.
*/
private ImageMetadataUtils() {
// Utility classes should not have a public or default constructor
}
/**
* This utility method extracts image metadata stored in EXIF and IPTC tags
* and returns the extracted information in a {@link ImageMetadata}.
*
* @param img
* image file
* @return extracted meta information
*/
public static ImageMetadata extractMetadata(File img) {
BufferedInputStream is = null;
try {
is = new BufferedInputStream(new FileInputStream(img));
return extractMetadata(is);
} catch (FileNotFoundException e) {
logger.warn("Tried to extract image metadata from none existing file '{}'", img.getName());
return null;
} finally {
IOUtils.closeQuietly(is);
}
}
/**
* This utility method extracts image metadata stored in EXIF and IPTC tags
* and returns the extracted information in a {@link ImageMetadata}.
*
* @param img
* image input stream
* @return extracted meta information
*/
public static ImageMetadata extractMetadata(BufferedInputStream img) {
Metadata meta;
try {
meta = ImageMetadataReader.readMetadata(img);
} catch (ImageProcessingException e) {
if ("File is not the correct format".equals(e.getMessage()))
return null;
logger.warn("Failed to extract image metadata from image: {}", e.getMessage());
return null;
}
if (meta == null) {
logger.debug("Extracted metadata is null");
return null;
} else {
ImageMetadata imgmeta = new ImageMetadata();
// Extract IPTC information
Directory iptc = meta.getDirectory(IptcDirectory.class);
if (iptc.containsTag(IptcDirectory.TAG_HEADLINE))
imgmeta.setCaption(iptc.getString(IptcDirectory.TAG_HEADLINE));
if (iptc.containsTag(IptcDirectory.TAG_CAPTION))
imgmeta.setLegend(iptc.getString(IptcDirectory.TAG_CAPTION));
if (iptc.containsTag(IptcDirectory.TAG_BY_LINE))
imgmeta.setPhotographer(iptc.getString(IptcDirectory.TAG_BY_LINE));
if (iptc.containsTag(IptcDirectory.TAG_COPYRIGHT_NOTICE))
imgmeta.setCopyright(iptc.getString(IptcDirectory.TAG_COPYRIGHT_NOTICE));
if (iptc.containsTag(IptcDirectory.TAG_CITY))
imgmeta.setLocation(iptc.getString(IptcDirectory.TAG_CITY));
if (iptc.containsTag(IptcDirectory.TAG_KEYWORDS)) {
StringTokenizer st = new StringTokenizer(iptc.getString(IptcDirectory.TAG_KEYWORDS), ",;");
while (st.hasMoreTokens()) {
imgmeta.addKeyword(st.nextToken());
}
}
// Extract EXIF information
Directory exif = meta.getDirectory(ExifDirectory.class);
if (exif.containsTag(ExifDirectory.TAG_DATETIME)) {
try {
imgmeta.setDateTaken(exif.getDate(ExifDirectory.TAG_DATETIME));
} catch (MetadataException e) {
}
}
if (exif.containsTag(ExifDirectory.TAG_ISO_EQUIVALENT)) {
try {
imgmeta.setFilmspeed(exif.getInt(ExifDirectory.TAG_ISO_EQUIVALENT));
} catch (MetadataException e) {
}
}
if (exif.containsTag(ExifDirectory.TAG_FNUMBER)) {
try {
imgmeta.setFNumber(exif.getFloat(ExifDirectory.TAG_FNUMBER));
} catch (MetadataException e) {
}
}
if (exif.containsTag(ExifDirectory.TAG_FOCAL_LENGTH)) {
try {
imgmeta.setFocalWidth(exif.getInt(ExifDirectory.TAG_FOCAL_LENGTH));
} catch (MetadataException e) {
}
}
if (exif.containsTag(ExifDirectory.TAG_EXPOSURE_TIME)) {
try {
imgmeta.setExposureTime(exif.getFloat(ExifDirectory.TAG_EXPOSURE_TIME));
} catch (MetadataException e) {
}
}
if (StringUtils.isBlank(imgmeta.getCopyright()) && exif.containsTag(ExifDirectory.TAG_COPYRIGHT))
imgmeta.setCopyright(exif.getString(ExifDirectory.TAG_COPYRIGHT));
// Extract GPS information
try {
Directory gps = meta.getDirectory(GpsDirectory.class);
if (gps.containsTag(GpsDirectory.TAG_GPS_LATITUDE)) {
Rational[] lat = gps.getRationalArray(GpsDirectory.TAG_GPS_LATITUDE);
String latRef = gps.getString(GpsDirectory.TAG_GPS_LATITUDE_REF);
double latitude = parseHMS(lat);
if (latitude != 0) {
if (StringUtils.isNotBlank(latRef) && "S".equalsIgnoreCase(latRef) && latitude > 0)
latitude *= -1;
}
imgmeta.setGpsLat(latitude);
}
if (gps.containsTag(GpsDirectory.TAG_GPS_LONGITUDE)) {
Rational[] lng = gps.getRationalArray(GpsDirectory.TAG_GPS_LONGITUDE);
String lngRef = gps.getString(GpsDirectory.TAG_GPS_LONGITUDE_REF);
double longitude = parseHMS(lng);
if (longitude != 0) {
if (StringUtils.isNotBlank(lngRef) && "W".equalsIgnoreCase(lngRef) && longitude > 0)
longitude *= -1;
}
imgmeta.setGpsLong(longitude);
}
} catch (MetadataException e) {
logger.info("Error while extracting GPS information out of an image.");
imgmeta.setGpsLat(0);
imgmeta.setGpsLong(0);
}
return imgmeta;
}
}
private static double parseHMS(Rational[] hms) {
return hms[0].doubleValue() + (hms[1].doubleValue() + (hms[2].doubleValue() / 60)) / 60;
}
}