From sanselan-commits-return-181-apmail-incubator-sanselan-commits-archive=incubator.apache.org@incubator.apache.org Sun Jan 18 17:19:02 2009 Return-Path: Delivered-To: apmail-incubator-sanselan-commits-archive@locus.apache.org Received: (qmail 26445 invoked from network); 18 Jan 2009 17:19:01 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (140.211.11.2) by minotaur.apache.org with SMTP; 18 Jan 2009 17:19:01 -0000 Received: (qmail 51747 invoked by uid 500); 18 Jan 2009 17:18:52 -0000 Delivered-To: apmail-incubator-sanselan-commits-archive@incubator.apache.org Received: (qmail 51704 invoked by uid 500); 18 Jan 2009 17:18:52 -0000 Mailing-List: contact sanselan-commits-help@incubator.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: sanselan-dev@incubator.apache.org Delivered-To: mailing list sanselan-commits@incubator.apache.org Received: (qmail 51615 invoked by uid 99); 18 Jan 2009 17:18:52 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Sun, 18 Jan 2009 09:18:52 -0800 X-ASF-Spam-Status: No, hits=-2000.0 required=10.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Sun, 18 Jan 2009 17:18:49 +0000 Received: by eris.apache.org (Postfix, from userid 65534) id BA5CF2388875; Sun, 18 Jan 2009 09:18:27 -0800 (PST) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: 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/ Date: Sun, 18 Jan 2009 17:18:26 -0000 To: sanselan-commits@incubator.apache.org From: cmchen@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20090118171827.BA5CF2388875@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org 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. *

- * @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. *

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