/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package lucee.runtime.img;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
import java.util.List;
import lucee.commons.io.IOUtil;
import lucee.commons.io.res.Resource;
import lucee.commons.io.res.ResourcesImpl;
import lucee.commons.lang.ExceptionUtil;
import lucee.commons.lang.StringUtil;
import lucee.runtime.op.Decision;
import lucee.runtime.type.Array;
import lucee.runtime.type.ArrayImpl;
import lucee.runtime.type.Struct;
import lucee.runtime.type.StructImpl;
import org.apache.commons.imaging.ImageReadException;
import org.apache.commons.imaging.Imaging;
import org.apache.commons.imaging.common.IImageMetadata;
import org.apache.commons.imaging.common.IImageMetadata.IImageMetadataItem;
import org.apache.commons.imaging.common.ImageMetadata.Item;
import org.apache.commons.imaging.common.RationalNumber;
import org.apache.commons.imaging.formats.jpeg.JpegImageMetadata;
import org.apache.commons.imaging.formats.jpeg.JpegPhotoshopMetadata;
import org.apache.commons.imaging.formats.tiff.TiffField;
import org.apache.commons.imaging.formats.tiff.TiffImageMetadata;
import org.apache.commons.imaging.formats.tiff.constants.GpsTagConstants;
import org.apache.commons.imaging.formats.tiff.taginfos.TagInfo;
public class Metadata {
public static void main(String[] args) {
Resource res = ResourcesImpl.getFileResourceProvider().getResource("/Users/mic/Projects/Lucee/Lucee5/test/functions/images/BigBen.jpg");
Struct info=new StructImpl();
addInfo("jpg", res, info);
}
public static void addExifInfo(String format, final Resource res, Struct info) {
InputStream is=null;
try {
is=res.getInputStream();
fillExif(format, is,info);
}
catch(Throwable t) {ExceptionUtil.rethrowIfNecessary(t);}
finally {
IOUtil.closeEL(is);
}
}
private static void fillExif(String format, InputStream is, Struct info) throws ImageReadException, IOException {
// get all metadata stored in EXIF format (ie. from JPEG or TIFF).
IImageMetadata metadata = Imaging.getMetadata(is,"test."+format);
if(metadata==null) return;
if (metadata instanceof JpegImageMetadata) {
final JpegImageMetadata jpegMetadata = (JpegImageMetadata) metadata;
// EXIF
if(jpegMetadata!=null) {
try{set(jpegMetadata.getExif().getItems(),info,null);}catch(Throwable t) {ExceptionUtil.rethrowIfNecessary(t);}
}
// GPS
try{gps(jpegMetadata,info);}catch(Throwable t) {ExceptionUtil.rethrowIfNecessary(t);}
}
}
public static void addInfo(String format, final Resource res, Struct info) {
InputStream is=null;
try {
is=res.getInputStream();
fill(format, is,info);
}
catch(Throwable t) {ExceptionUtil.rethrowIfNecessary(t);}
finally {
IOUtil.closeEL(is);
}
}
private static void fill(String format, InputStream is, Struct info) throws ImageReadException, IOException {
// get all metadata stored in EXIF format (ie. from JPEG or TIFF).
IImageMetadata metadata = Imaging.getMetadata(is,"test."+format);
if(metadata==null) return;
// System.out.println(metadata);
if (metadata instanceof JpegImageMetadata) {
final JpegImageMetadata jpegMetadata = (JpegImageMetadata) metadata;
try{set(jpegMetadata.getItems(),info,null);}catch(Throwable t) {ExceptionUtil.rethrowIfNecessary(t);}
try{set(metadata.getItems(),info,null);}catch(Throwable t) {ExceptionUtil.rethrowIfNecessary(t);}
// Photoshop
if(metadata instanceof JpegImageMetadata) {
JpegPhotoshopMetadata photoshop = ((JpegImageMetadata) metadata).getPhotoshop();
if(photoshop!=null) {
try{
List<? extends IImageMetadataItem> list = photoshop.getItems();
if(list!=null && !list.isEmpty()) {
Struct ps=new StructImpl();
info.setEL("photoshop", ps);
try{set(list,ps,null);}catch(Throwable t) {ExceptionUtil.rethrowIfNecessary(t);}
}
}catch(Throwable t) {ExceptionUtil.rethrowIfNecessary(t);}
}
}
// EXIF
if(jpegMetadata!=null) {
Struct exif=new StructImpl();
info.setEL("exif", exif);
try{set(jpegMetadata.getExif().getItems(),exif,null);}catch(Throwable t) {ExceptionUtil.rethrowIfNecessary(t);}
}
// GPS
try{gps(jpegMetadata,info);}catch(Throwable t) {ExceptionUtil.rethrowIfNecessary(t);}
}
}
private static void gps(JpegImageMetadata jpegMetadata, Struct info) throws ImageReadException {
Struct gps=new StructImpl();
info.setEL("gps", gps);
info=gps;
final TiffImageMetadata exifMetadata = jpegMetadata.getExif();
Double longitude = null;
Double latitude = null;
if (null != exifMetadata) {
final TiffImageMetadata.GPSInfo gpsInfo = exifMetadata.getGPS();
if (null != gpsInfo) {
//final String gpsDescription = gpsInfo.toString();
longitude = gpsInfo.getLongitudeAsDegreesEast();
latitude = gpsInfo.getLatitudeAsDegreesNorth();
}
}
// more specific example of how to manually access GPS values
final TiffField gpsLatitudeRefField = jpegMetadata.findEXIFValueWithExactMatch(
GpsTagConstants.GPS_TAG_GPS_LATITUDE_REF);
final TiffField gpsLatitudeField = jpegMetadata.findEXIFValueWithExactMatch(
GpsTagConstants.GPS_TAG_GPS_LATITUDE);
final TiffField gpsLongitudeRefField = jpegMetadata.findEXIFValueWithExactMatch(
GpsTagConstants.GPS_TAG_GPS_LONGITUDE_REF);
final TiffField gpsLongitudeField = jpegMetadata.findEXIFValueWithExactMatch(
GpsTagConstants.GPS_TAG_GPS_LONGITUDE);
if (gpsLatitudeRefField != null && gpsLatitudeField != null && gpsLongitudeRefField != null && gpsLongitudeField != null) {
// all of these values are strings.
final String gpsLatitudeRef = (String) gpsLatitudeRefField.getValue();
final RationalNumber gpsLatitude[] = (RationalNumber[]) (gpsLatitudeField.getValue());
final String gpsLongitudeRef = (String) gpsLongitudeRefField.getValue();
final RationalNumber gpsLongitude[] = (RationalNumber[]) gpsLongitudeField.getValue();
info.setEL("GPS Latitude",
gpsLatitude[0].toDisplayString() + "\""
+ gpsLatitude[1].toDisplayString() + "'"
+ gpsLatitude[2].toDisplayString());
info.setEL("GPS Latitude Ref", gpsLatitudeRef);
Struct sct=new StructImpl();
gps.setEL("latitude", sct);
sct.setEL("degrees",gpsLatitude[0].doubleValue());
sct.setEL("minutes",gpsLatitude[1].doubleValue());
sct.setEL("seconds",gpsLatitude[2].doubleValue());
sct.setEL("ref",gpsLatitudeRef);
sct.setEL("decimal",latitude);
info.setEL("GPS Longitude",
gpsLongitude[0].toDisplayString() + "\""
+ gpsLongitude[1].toDisplayString() + "'"
+ gpsLongitude[2].toDisplayString());
info.setEL("GPS Longitude Ref", gpsLongitudeRef);
sct=new StructImpl();
gps.setEL("longitude", sct);
sct.setEL("degrees",gpsLongitude[0].doubleValue());
sct.setEL("minutes",gpsLongitude[1].doubleValue());
sct.setEL("seconds",gpsLongitude[2].doubleValue());
sct.setEL("ref",gpsLongitudeRef);
sct.setEL("decimal",longitude);
}
}
private static void set(Struct sct1, Struct sct2, String name1, String name2, Object value) {
if(value instanceof CharSequence)value=StringUtil.unwrap(value.toString());
sct1.setEL(name1, value);
sct2.setEL(name2, value);
}
private static Object val(Object value) {
if(value==null) return null;
if(value instanceof CharSequence) return value.toString();
if(value instanceof Number) return ((Number)value).doubleValue();
if(Decision.isNativeArray(value) && !(value instanceof Object[])) return value;
if(value instanceof Object[]) {
Array trg=new ArrayImpl();
Object[] arr=(Object[]) value;
for(Object obj : arr) {
trg.appendEL(val(obj));
}
return trg;
}
if(value instanceof RationalNumber) {
RationalNumber rn=(RationalNumber) value;
return rn.toDisplayString();
}
return value;
}
private static void set(List<? extends IImageMetadataItem> items, Struct data1, Struct data2) {
Iterator<? extends IImageMetadataItem> it = items.iterator();
Item item;
while(it.hasNext()) {
item=(Item) it.next();
data1.setEL(item.getKeyword(),StringUtil.unwrap(item.getText()));
if(data2!=null)data2.setEL(item.getKeyword(),StringUtil.unwrap(item.getText()));
}
}
private static void set(final JpegImageMetadata jpegMetadata, final TagInfo tagInfo, Struct info) throws ImageReadException {
final TiffField field = jpegMetadata.findEXIFValueWithExactMatch(tagInfo);
if (field != null) {
if(!info.containsKey(tagInfo.name)){
Object val = val(field.getValue());
if(val!=null)info.setEL(tagInfo.name, val);
}
}
}
}