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)
 				{
 
 				}