You are viewing a plain text version of this content. The canonical link for it is here.
Posted to sanselan-commits@incubator.apache.org by cm...@apache.org on 2009/01/18 17:18:26 UTC
svn commit: r735506 - in /incubator/sanselan/trunk: ./
src/main/java/org/apache/sanselan/formats/tiff/write/
src/main/java/org/apache/sanselan/util/
src/test/java/org/apache/sanselan/sampleUsage/
Author: cmchen
Date: Sun Jan 18 09:18:25 2009
New Revision: 735506
URL: http://svn.apache.org/viewvc?rev=735506&view=rev
Log:
* Improved the examples illustrating how to change EXIF metadata.
Modified:
incubator/sanselan/trunk/RELEASE_NOTES
incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/tiff/write/TiffOutputSet.java
incubator/sanselan/trunk/src/main/java/org/apache/sanselan/util/IOUtils.java
incubator/sanselan/trunk/src/main/java/org/apache/sanselan/util/UnicodeUtils.java
incubator/sanselan/trunk/src/test/java/org/apache/sanselan/sampleUsage/WriteExifMetadataExample.java
Modified: incubator/sanselan/trunk/RELEASE_NOTES
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/RELEASE_NOTES?rev=735506&r1=735505&r2=735506&view=diff
==============================================================================
--- incubator/sanselan/trunk/RELEASE_NOTES (original)
+++ incubator/sanselan/trunk/RELEASE_NOTES Sun Jan 18 09:18:25 2009
@@ -12,6 +12,24 @@
Version History:
----------------
+Release 0.97
+------------
+
+ * Improved the examples illustrating how to change EXIF metadata.
+ * Applied a patch from Niall Pemberton around jdk1.5 compatibility:
+ """
+ Sanselan claims JDK 1.4 compatibility, but a JDK 1.5 method (Class's getSimpleName() [1]) has been used in JpegRewriter
+ """
+ * Applied a "Build Improvements" patch from Niall Pemberton:
+ """
+ I took a look at the propsed 0.96 relelase and have some suggestions to improve the build:
+ * Add standard manifest entries to jar
+ * Lock down version numbers for maven compiler, surefire and javadoc plugins
+ * generate sources jar for the release
+ * include the RELEASE-NOTES in the binary distribution
+ * include NOTICE/LICENSE files in the javadoc jar
+ """
+
Release 0.96
------------
Modified: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/tiff/write/TiffOutputSet.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/tiff/write/TiffOutputSet.java?rev=735506&r1=735505&r2=735506&view=diff
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/tiff/write/TiffOutputSet.java (original)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/tiff/write/TiffOutputSet.java Sun Jan 18 09:18:25 2009
@@ -135,8 +135,12 @@
return null;
}
- /*
- * Expects longitude in degrees E, latitude in degrees N
+ /**
+ * A convenience method to update GPS values in EXIF metadata.
+ *
+ * @param longitude Longitude in degrees E, negative values are W.
+ * @param latitude latitude in degrees N, negative values are S.
+ * @throws ImageWriteException
*/
public void setGPSInDegrees(double longitude, double latitude)
throws ImageWriteException
Modified: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/util/IOUtils.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/util/IOUtils.java?rev=735506&r1=735505&r2=735506&view=diff
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/util/IOUtils.java (original)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/util/IOUtils.java Sun Jan 18 09:18:25 2009
@@ -26,16 +26,25 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.nio.channels.FileChannel;
import org.apache.sanselan.SanselanConstants;
-public abstract class IOUtils implements SanselanConstants
+public class IOUtils implements SanselanConstants
{
+ /**
+ * This class should never be instantiated.
+ */
+ private IOUtils()
+ {
+ }
/**
* Reads an InputStream to the end.
* <p>
- * @param is The InputStream to read.
+ *
+ * @param is
+ * The InputStream to read.
* @return A byte array containing the contents of the InputStream
* @see InputStream
*/
@@ -59,15 +68,13 @@
os.flush();
return os.toByteArray();
- }
- finally
+ } finally
{
try
{
if (os != null)
os.close();
- }
- catch (IOException e)
+ } catch (IOException e)
{
Debug.debug(e);
}
@@ -77,7 +84,9 @@
/**
* Reads a File into memory.
* <p>
- * @param file The File to read.
+ *
+ * @param file
+ * The File to read.
* @return A byte array containing the contents of the File
* @see InputStream
*/
@@ -90,15 +99,13 @@
is = new FileInputStream(file);
return getInputStreamBytes(is);
- }
- finally
+ } finally
{
try
{
if (is != null)
is.close();
- }
- catch (IOException e)
+ } catch (IOException e)
{
Debug.debug(e);
}
@@ -114,15 +121,13 @@
stream = new ByteArrayInputStream(src);
putInputStreamToFile(stream, file);
- }
- finally
+ } finally
{
try
{
if (stream != null)
stream.close();
- }
- catch (Exception e)
+ } catch (Exception e)
{
Debug.debug(e);
@@ -142,15 +147,13 @@
stream = new FileOutputStream(file);
copyStreamToStream(src, stream);
- }
- finally
+ } finally
{
try
{
if (stream != null)
stream.close();
- }
- catch (Exception e)
+ } catch (Exception e)
{
Debug.debug(e);
}
@@ -180,8 +183,7 @@
dst.write(buffer, 0, count);
bos.flush();
- }
- finally
+ } finally
{
if (close_streams)
{
@@ -189,8 +191,7 @@
{
if (bis != null)
bis.close();
- }
- catch (IOException e)
+ } catch (IOException e)
{
Debug.debug(e);
}
@@ -198,8 +199,7 @@
{
if (bos != null)
bos.close();
- }
- catch (IOException e)
+ } catch (IOException e)
{
Debug.debug(e);
}
@@ -208,4 +208,62 @@
}
+ public static final boolean copyFileNio(File src, File dst)
+ throws IOException
+ {
+ FileChannel srcChannel = null, dstChannel = null;
+ try
+ {
+ // Create channel on the source
+ srcChannel = new FileInputStream(src).getChannel();
+
+ // Create channel on the destination
+ dstChannel = new FileOutputStream(dst).getChannel();
+
+ // // Copy file contents from source to destination
+ // dstChannel.transferFrom(srcChannel, 0, srcChannel.size());
+
+ {
+ // long theoretical_max = (64 * 1024 * 1024) - (32 * 1024);
+ int safe_max = (64 * 1024 * 1024) / 4;
+ long size = srcChannel.size();
+ long position = 0;
+ while (position < size)
+ {
+ position += srcChannel.transferTo(position, safe_max,
+ dstChannel);
+ }
+ }
+
+ // Close the channels
+ srcChannel.close();
+ srcChannel = null;
+ dstChannel.close();
+ dstChannel = null;
+
+ return true;
+ }
+ finally
+ {
+ try
+ {
+ if (srcChannel != null)
+ srcChannel.close();
+ } catch (IOException e)
+ {
+ Debug.debug(e);
+
+ }
+ try
+ {
+ if (dstChannel != null)
+ dstChannel.close();
+ } catch (IOException e)
+ {
+ Debug.debug(e);
+
+ }
+ }
+ }
+
}
\ No newline at end of file
Modified: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/util/UnicodeUtils.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/util/UnicodeUtils.java?rev=735506&r1=735505&r2=735506&view=diff
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/util/UnicodeUtils.java (original)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/util/UnicodeUtils.java Sun Jan 18 09:18:25 2009
@@ -23,6 +23,13 @@
public abstract class UnicodeUtils implements BinaryConstants
{
+ /**
+ * This class should never be instantiated.
+ */
+ private UnicodeUtils()
+ {
+ }
+
public static class UnicodeException extends Exception
{
public UnicodeException(String message)
Modified: incubator/sanselan/trunk/src/test/java/org/apache/sanselan/sampleUsage/WriteExifMetadataExample.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/test/java/org/apache/sanselan/sampleUsage/WriteExifMetadataExample.java?rev=735506&r1=735505&r2=735506&view=diff
==============================================================================
--- incubator/sanselan/trunk/src/test/java/org/apache/sanselan/sampleUsage/WriteExifMetadataExample.java (original)
+++ incubator/sanselan/trunk/src/test/java/org/apache/sanselan/sampleUsage/WriteExifMetadataExample.java Sun Jan 18 09:18:25 2009
@@ -33,6 +33,7 @@
import org.apache.sanselan.formats.tiff.write.TiffOutputDirectory;
import org.apache.sanselan.formats.tiff.write.TiffOutputField;
import org.apache.sanselan.formats.tiff.write.TiffOutputSet;
+import org.apache.sanselan.util.IOUtils;
public class WriteExifMetadataExample
{
@@ -46,28 +47,37 @@
os = new BufferedOutputStream(os);
new ExifRewriter().removeExifMetadata(jpegImageFile, os);
- }
- finally
+ } finally
{
if (os != null)
try
{
os.close();
- }
- catch (IOException e)
+ } catch (IOException e)
{
}
}
}
+ /**
+ * This example illustrates how to add/update EXIF metadata in a JPEG file.
+ *
+ * @param jpegImageFile
+ * A source image file.
+ * @param dst
+ * The output file.
+ * @throws IOException
+ * @throws ImageReadException
+ * @throws ImageWriteException
+ */
public void changeExifMetadata(File jpegImageFile, File dst)
throws IOException, ImageReadException, ImageWriteException
{
OutputStream os = null;
try
{
- TiffOutputSet outputSet = null;
+ TiffOutputSet outputSet = null;
// note that metadata might be null if no metadata is found.
IImageMetadata metadata = Sanselan.getMetadata(jpegImageFile);
@@ -82,53 +92,47 @@
// TiffImageMetadata class is immutable (read-only).
// TiffOutputSet class represents the Exif data to write.
//
- // Usually, we want to update existing Exif metadata by changing
+ // Usually, we want to update existing Exif metadata by
+ // changing
// the values of a few fields, or adding a field.
// In these cases, it is easiest to use getOutputSet() to
// start with a "copy" of the fields read from the image.
- outputSet = exif.getOutputSet();
+ outputSet = exif.getOutputSet();
}
}
- if(null==outputSet)
- outputSet = new TiffOutputSet();
-
- {
- // Example of how to remove a single tag/field.
- //
- // Note that this approach is crude: Exif data is organized in
- // directories. The same tag/field may appear in more than one
- // directory.
- TiffOutputField aperture = outputSet
- .findField(TiffConstants.EXIF_TAG_APERTURE_VALUE);
- if (null != aperture)
- {
- // set contains aperture tag/field.
- outputSet
- .removeField(TiffConstants.EXIF_TAG_APERTURE_VALUE);
- }
- }
+ // if file does not contain any exif metadata, we create an empty
+ // set of exif metadata. Otherwise, we keep all of the other
+ // existing tags.
+ if (null == outputSet)
+ outputSet = new TiffOutputSet();
{
// Example of how to add a field/tag to the output set.
//
- // Note that you should first remove the field/tag if it already exists
- // in this directory. See above.
+ // Note that you should first remove the field/tag if it already
+ // exists in this directory, or you may end up with duplicate
+ // tags. See above.
//
// Certain fields/tags are expected in certain Exif directories;
- // Others can occur in more than one directory (and often have a different
- // meaning in different directories).
+ // Others can occur in more than one directory (and often have a
+ // different meaning in different directories).
//
- // TagInfo constants often contain a description of what directories
- // are associated with a given tag.
+ // TagInfo constants often contain a description of what
+ // directories are associated with a given tag.
//
- // see org.apache.sanselan.formats.tiff.constants.AllTagConstants
+ // see
+ // org.apache.sanselan.formats.tiff.constants.AllTagConstants
//
TiffOutputField aperture = TiffOutputField.create(
TiffConstants.EXIF_TAG_APERTURE_VALUE,
outputSet.byteOrder, new Double(0.3));
TiffOutputDirectory exifDirectory = outputSet
.getOrCreateExifDirectory();
+ // make sure to remove old value if present (this method will
+ // not fail if the tag does not exist).
+ exifDirectory
+ .removeField(TiffConstants.EXIF_TAG_APERTURE_VALUE);
exifDirectory.add(aperture);
}
@@ -137,27 +141,207 @@
// New York City
double longitude = -74.0; // 74 degrees W (in Degrees East)
- double latitude = 40 + 43 / 60.0; // 40 degrees N (in Degrees North)
+ double latitude = 40 + 43 / 60.0; // 40 degrees N (in Degrees
+ // North)
outputSet.setGPSInDegrees(longitude, latitude);
}
- // printTagValue(jpegMetadata, TiffConstants.TIFF_TAG_DATE_TIME);
+ // printTagValue(jpegMetadata, TiffConstants.TIFF_TAG_DATE_TIME);
os = new FileOutputStream(dst);
os = new BufferedOutputStream(os);
new ExifRewriter().updateExifMetadataLossless(jpegImageFile, os,
outputSet);
+
+ os.close();
+ os = null;
+ } finally
+ {
+ if (os != null)
+ try
+ {
+ os.close();
+ } catch (IOException e)
+ {
+
+ }
}
- finally
+ }
+
+ /**
+ * This example illustrates how to remove a tag (if present) from EXIF
+ * metadata in a JPEG file.
+ *
+ * In this case, we remove the "aperture" tag from the EXIF metadata if
+ * present.
+ *
+ * @param jpegImageFile
+ * A source image file.
+ * @param dst
+ * The output file.
+ * @throws IOException
+ * @throws ImageReadException
+ * @throws ImageWriteException
+ */
+ public void removeExifTag(File jpegImageFile, File dst) throws IOException,
+ ImageReadException, ImageWriteException
+ {
+ OutputStream os = null;
+ try
+ {
+ TiffOutputSet outputSet = null;
+
+ // note that metadata might be null if no metadata is found.
+ IImageMetadata metadata = Sanselan.getMetadata(jpegImageFile);
+ JpegImageMetadata jpegMetadata = (JpegImageMetadata) metadata;
+ if (null != jpegMetadata)
+ {
+ // note that exif might be null if no Exif metadata is found.
+ TiffImageMetadata exif = jpegMetadata.getExif();
+
+ if (null != exif)
+ {
+ // TiffImageMetadata class is immutable (read-only).
+ // TiffOutputSet class represents the Exif data to write.
+ //
+ // Usually, we want to update existing Exif metadata by
+ // changing
+ // the values of a few fields, or adding a field.
+ // In these cases, it is easiest to use getOutputSet() to
+ // start with a "copy" of the fields read from the image.
+ outputSet = exif.getOutputSet();
+ }
+ }
+
+ if (null == outputSet)
+ {
+ // file does not contain any exif metadata. We don't need to
+ // update the file; just copy it.
+ IOUtils.copyFileNio(jpegImageFile, dst);
+ return;
+ }
+
+ {
+ // Example of how to remove a single tag/field.
+ // There are two ways to do this.
+
+ // Option 1: brute force
+ // Note that this approach is crude: Exif data is organized in
+ // directories. The same tag/field may appear in more than one
+ // directory, and have different meanings in each.
+ outputSet.removeField(TiffConstants.EXIF_TAG_APERTURE_VALUE);
+
+ // Option 2: precision
+ // We know the exact directory the tag should appear in, in this
+ // case the "exif" directory.
+ // One complicating factor is that in some cases, manufacturers
+ // will place the same tag in different directories.
+ // To learn which directory a tag appears in, either refer to
+ // the constants in ExifTagConstants.java or go to Phil Harvey's
+ // EXIF website.
+ TiffOutputDirectory exifDirectory = outputSet
+ .getExifDirectory();
+ if (null != exifDirectory)
+ exifDirectory
+ .removeField(TiffConstants.EXIF_TAG_APERTURE_VALUE);
+ }
+
+ os = new FileOutputStream(dst);
+ os = new BufferedOutputStream(os);
+
+ new ExifRewriter().updateExifMetadataLossless(jpegImageFile, os,
+ outputSet);
+
+ os.close();
+ os = null;
+ } finally
{
if (os != null)
try
{
os.close();
+ } catch (IOException e)
+ {
+
+ }
+ }
+ }
+
+ /**
+ * This example illustrates how to set the GPS values in JPEG EXIF metadata.
+ *
+ * @param jpegImageFile
+ * A source image file.
+ * @param dst
+ * The output file.
+ * @throws IOException
+ * @throws ImageReadException
+ * @throws ImageWriteException
+ */
+ public void setExifGPSTag(File jpegImageFile, File dst) throws IOException,
+ ImageReadException, ImageWriteException
+ {
+ OutputStream os = null;
+ try
+ {
+ TiffOutputSet outputSet = null;
+
+ // note that metadata might be null if no metadata is found.
+ IImageMetadata metadata = Sanselan.getMetadata(jpegImageFile);
+ JpegImageMetadata jpegMetadata = (JpegImageMetadata) metadata;
+ if (null != jpegMetadata)
+ {
+ // note that exif might be null if no Exif metadata is found.
+ TiffImageMetadata exif = jpegMetadata.getExif();
+
+ if (null != exif)
+ {
+ // TiffImageMetadata class is immutable (read-only).
+ // TiffOutputSet class represents the Exif data to write.
+ //
+ // Usually, we want to update existing Exif metadata by
+ // changing
+ // the values of a few fields, or adding a field.
+ // In these cases, it is easiest to use getOutputSet() to
+ // start with a "copy" of the fields read from the image.
+ outputSet = exif.getOutputSet();
}
- catch (IOException e)
+ }
+
+ // if file does not contain any exif metadata, we create an empty
+ // set of exif metadata. Otherwise, we keep all of the other
+ // existing tags.
+ if (null == outputSet)
+ outputSet = new TiffOutputSet();
+
+ {
+ // Example of how to add/update GPS info to output set.
+
+ // New York City
+ double longitude = -74.0; // 74 degrees W (in Degrees East)
+ double latitude = 40 + 43 / 60.0; // 40 degrees N (in Degrees
+ // North)
+
+ outputSet.setGPSInDegrees(longitude, latitude);
+ }
+
+ os = new FileOutputStream(dst);
+ os = new BufferedOutputStream(os);
+
+ new ExifRewriter().updateExifMetadataLossless(jpegImageFile, os,
+ outputSet);
+
+ os.close();
+ os = null;
+ } finally
+ {
+ if (os != null)
+ try
+ {
+ os.close();
+ } catch (IOException e)
{
}