package net.sourceforge.gpstools.exif;
/* gpsdings
* Copyright (C) 2006 Moritz Ringler
* $Id: GpsExifWriter.java 358 2008-11-24 19:06:17Z ringler $
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import java.io.File;
import java.io.IOException;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.io.BufferedOutputStream;
import net.sourceforge.gpstools.gpx.Wpt;
import java.util.Date;
import java.util.Calendar;
import java.util.TimeZone;
import org.apache.sanselan.ImageReadException;
import org.apache.sanselan.ImageWriteException;
import org.apache.sanselan.formats.jpeg.exifRewrite.ExifRewriter;
import org.apache.sanselan.formats.tiff.TiffImageMetadata;
import org.apache.sanselan.formats.tiff.constants.TiffConstants;
import org.apache.sanselan.formats.tiff.constants.TagInfo;
import org.apache.sanselan.formats.tiff.write.TiffOutputDirectory;
import org.apache.sanselan.formats.tiff.write.TiffOutputField;
import org.apache.sanselan.formats.tiff.write.TiffOutputSet;
/**
Writes gps information to the GPS section of the Exif headers of
digital camera picture files.
**/
public class SanselanExifWriter implements GpsExifWriter {
final private ExifRewriter rewriter = new ExifRewriter();
static TiffOutputSet getExif(File jpegImageFile)
throws IOException, ImageReadException, ImageWriteException{
TiffImageMetadata exif = SanselanExifReader.getExif(jpegImageFile);
TiffOutputSet outputSet = null;
if (null != exif) {
outputSet = exif.getOutputSet();
}
// if file does not contain any exif metadata, we create an empty
// set of exif metadata.
if (null == outputSet){
outputSet = new TiffOutputSet();
}
return outputSet;
}
private boolean writeExif(File jpeg, TiffOutputSet outputSet)
throws IOException, ImageReadException, ImageWriteException{
final File dir = jpeg.getAbsoluteFile().getParentFile();
String prefix = jpeg.getName();
if(prefix.length() < 3){
prefix += "___";
}
final File tmp = File.createTempFile(prefix, null, dir);
OutputStream os = new FileOutputStream(tmp);
os = new BufferedOutputStream(os);
try {
System.err.println("LOSSLESS?");
rewriter.updateExifMetadataLossless(jpeg, os, outputSet);
} finally {
os.close();
}
if(!jpeg.delete() || !tmp.renameTo(jpeg)){
System.err.println("Error renaming " + tmp + " to " + jpeg);
return false;
}
return true;
}
/**
Writes the GPS info from the specified GPX waypoint to the Exif
header of <code>jpeg</code>.
@param jpeg the jpeg file whose exif header will be modified
@param wpt the coordinates, alitude and time to be associated with
the jpeg.
@param overwrite if the writer should overwrite existing gps
information.
@see #isAlwaysOverwrite
**/
@Override
public boolean writeExifGpsInfo(File jpeg, Wpt wpt, boolean overwrite){
try{
TiffOutputSet outputSet = SanselanExifWriter.getExif(jpeg);
outputSet.setGPSInDegrees(wpt.getLon().doubleValue(), wpt.getLat().doubleValue());
TiffOutputDirectory gpsIfd = outputSet.getGPSDirectory();
TiffOutputField field;
final int byteOrder = outputSet.byteOrder;
/* version */
field = makeField(
TiffConstants.GPS_TAG_GPS_VERSION_ID,
new byte[]{
(byte) 2, (byte) 2, (byte) 0, (byte) 0});
gpsIfd.removeField(TiffConstants.GPS_TAG_GPS_VERSION_ID);
gpsIfd.add(field);
/* map datum */
//System.err.println("Writing map datum.");
field = makeField(
TiffConstants.GPS_TAG_GPS_MAP_DATUM,
byteOrder, "WGS-84");
gpsIfd.removeField(TiffConstants.GPS_TAG_GPS_MAP_DATUM);
gpsIfd.add(field);
/* elevation */
if(wpt.getEle() != null){
//System.err.println("Writing ele ref.");
double ele = wpt.getEle().doubleValue();
final Byte eleref = new Byte(
(byte) ((ele < 0)
? TiffConstants.GPS_TAG_GPS_ALTITUDE_REF_VALUE_BELOW_SEA_LEVEL
: TiffConstants.GPS_TAG_GPS_ALTITUDE_REF_VALUE_ABOVE_SEA_LEVEL)
);
// use custom tag info because GPS_TAG_GPS_ALTITUDE_REF has length -1
TagInfo tag = new TagInfo(
"GPS Altitude Ref",
0x0005,
TiffConstants.FIELD_TYPE_DESCRIPTION_BYTE,
1,
TiffConstants.EXIF_DIRECTORY_GPS);
field = TiffOutputField.create(
tag, byteOrder, eleref);
gpsIfd.removeField(TiffConstants.GPS_TAG_GPS_ALTITUDE_REF);
gpsIfd.add(field);
//System.err.println("Writing ele.");
// use custom tag info because GPS_TAG_GPS_ALTITUDE_REF has length -1
tag = new TagInfo(
"GPS Altitude",
0x0006,
TiffConstants.FIELD_TYPE_DESCRIPTION_RATIONAL,
1,
TiffConstants.EXIF_DIRECTORY_GPS);
field = TiffOutputField.create(
tag,
byteOrder, new Double(ele));
gpsIfd.removeField(TiffConstants.GPS_TAG_GPS_ALTITUDE);
gpsIfd.add(field);
}
/* dateTime */
//System.err.println("Writing date/time.");
if(wpt.getTime() != null){
Date dateTime = wpt.getTime();
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
cal.setTime(dateTime);
field = TiffOutputField.create(
TiffConstants.GPS_TAG_GPS_TIME_STAMP,
byteOrder, new Double[]{
new Double(cal.get(Calendar.HOUR_OF_DAY)),
new Double(cal.get(Calendar.MINUTE)),
new Double(cal.get(Calendar.SECOND))
});
gpsIfd.removeField(TiffConstants.GPS_TAG_GPS_TIME_STAMP);
gpsIfd.add(field);
field = makeField(
TiffConstants.GPS_TAG_GPS_DATE_STAMP,
byteOrder,
AbstractExecExifWriter.formatAsExifDate(dateTime));
gpsIfd.removeField(TiffConstants.GPS_TAG_GPS_DATE_STAMP);
gpsIfd.add(field);
}
return writeExif(jpeg, outputSet);
} catch (Exception ex) {
System.err.println(ex);
ex.printStackTrace();
return false;
}
}
private static TiffOutputField makeField(TagInfo tag, byte[] bytes){
return new TiffOutputField(tag, tag.dataTypes[0], tag.length, bytes);
}
private static TiffOutputField makeField(TagInfo tag, int byteOrder, String str) throws ImageWriteException{
return new TiffOutputField(tag, tag.dataTypes[0], str.length() + 1,
tag.dataTypes[0].writeData(str + (char) 0, byteOrder));
}
/**
Returns whether a specific writer implementation will or will not
overwrite existing gps information regardless of the <code>overwrite</code>
parameter passed to {@link #writeExifGpsInfo writeExifGpsInfo}
**/
@Override
public boolean isAlwaysOverwrite(){
return true;
}
}