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 2008/08/04 23:23:46 UTC

svn commit: r682538 - in /incubator/sanselan/trunk: ./ src/main/java/org/apache/sanselan/ src/main/java/org/apache/sanselan/common/ src/main/java/org/apache/sanselan/formats/png/ src/main/java/org/apache/sanselan/formats/png/chunks/ src/main/java/org/a...

Author: cmchen
Date: Mon Aug  4 16:23:45 2008
New Revision: 682538

URL: http://svn.apache.org/viewvc?rev=682538&view=rev
Log:
* Added improved support for reading and writing iTXt, tEXt, zTXt Png chunks.
 	Added a unit test that demonstrates the feature.
 * Found a new regression wherein DeflaterOutputStream needs to be closed.

Added:
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/SanselanException.java   (with props)
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/common/ZLibUtils.java   (with props)
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/PngImageInfo.java   (with props)
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/PngText.java   (with props)
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/util/UnicodeUtils.java   (with props)
    incubator/sanselan/trunk/src/test/java/org/apache/sanselan/formats/png/PngTextTest.java   (with props)
Removed:
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/common/ZLibInflater.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/sampleUsage/
Modified:
    incubator/sanselan/trunk/RELEASE_NOTES
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/ImageInfo.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/ImageReadException.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/ImageWriteException.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/common/BinaryConstants.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/PngConstants.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/PngImageParser.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/PngWriter.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunkiCCP.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunkiTXt.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunktEXt.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunkzTXt.java
    incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGTextChunk.java

Modified: incubator/sanselan/trunk/RELEASE_NOTES
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/RELEASE_NOTES?rev=682538&r1=682537&r2=682538&view=diff
==============================================================================
--- incubator/sanselan/trunk/RELEASE_NOTES (original)
+++ incubator/sanselan/trunk/RELEASE_NOTES Mon Aug  4 16:23:45 2008
@@ -15,6 +15,11 @@
 Release 0.95
 ------------
 
+ * Added improved support for reading and writing iTXt, tEXt, zTXt Png chunks.
+ 	Added a unit test that demonstrates the feature.
+ * Found a new regression wherein DeflaterOutputStream needs to be closed.
+ * Added the .tar.bz2 distributions back into the maven assembly descriptors.
+ * Moved the example/sample code to a new top-level source folder, "example."
  * Replaced dependency on java.util.zip.DeflaterOutputStream, which is only available in Java 1.6.
  * Added a unit test around reading and writing images in every format.
  * We now sort some (but not all) GIF color tables.

Modified: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/ImageInfo.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/ImageInfo.java?rev=682538&r1=682537&r2=682538&view=diff
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/ImageInfo.java (original)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/ImageInfo.java Mon Aug  4 16:23:45 2008
@@ -22,8 +22,8 @@
 import java.util.ArrayList;
 
 /**
- * ImageInfo represents a collection of basic properties of an image, 
- * such as width, height, format, bit depth, etc.
+ * ImageInfo represents a collection of basic properties of an image, such as
+ * width, height, format, bit depth, etc.
  */
 public class ImageInfo
 {
@@ -71,40 +71,40 @@
 
 	private final String compressionAlgorithm;
 
-	public ImageInfo(String FormatDetails, int BitsPerPixel,
-			ArrayList Comments, ImageFormat Format, String FormatName,
-			int Height, String MimeType, int NumberOfImages,
-			int PhysicalHeightDpi, float PhysicalHeightInch,
-			int PhysicalWidthDpi, float PhysicalWidthInch, int Width,
+	public ImageInfo(String formatDetails, int bitsPerPixel,
+			ArrayList comments, ImageFormat format, String formatName,
+			int height, String mimeType, int numberOfImages,
+			int physicalHeightDpi, float physicalHeightInch,
+			int physicalWidthDpi, float physicalWidthInch, int width,
 			boolean isProgressive, boolean isTransparent, boolean usesPalette,
-			int ColorType, String compressionAlgorithm)
+			int colorType, String compressionAlgorithm)
 	{
-		this.formatDetails = FormatDetails;
+		this.formatDetails = formatDetails;
 
-		this.bitsPerPixel = BitsPerPixel;
-		this.comments = Comments;
+		this.bitsPerPixel = bitsPerPixel;
+		this.comments = comments;
 
-		this.format = Format;
-		this.formatName = FormatName;
-		this.height = Height;
-		this.mimeType = MimeType;
-
-		this.numberOfImages = NumberOfImages;
-		this.physicalHeightDpi = PhysicalHeightDpi;
-		this.physicalHeightInch = PhysicalHeightInch;
-		this.physicalWidthDpi = PhysicalWidthDpi;
-		this.physicalWidthInch = PhysicalWidthInch;
-		this.width = Width;
+		this.format = format;
+		this.formatName = formatName;
+		this.height = height;
+		this.mimeType = mimeType;
+
+		this.numberOfImages = numberOfImages;
+		this.physicalHeightDpi = physicalHeightDpi;
+		this.physicalHeightInch = physicalHeightInch;
+		this.physicalWidthDpi = physicalWidthDpi;
+		this.physicalWidthInch = physicalWidthInch;
+		this.width = width;
 		this.isProgressive = isProgressive;
 
 		this.isTransparent = isTransparent;
 		this.usesPalette = usesPalette;
 
-		this.colorType = ColorType;
+		this.colorType = colorType;
 		this.compressionAlgorithm = compressionAlgorithm;
 	}
 
-	/** 
+	/**
 	 * Returns the bits per pixel of the image data.
 	 */
 	public int getBitsPerPixel()
@@ -112,30 +112,28 @@
 		return bitsPerPixel;
 	}
 
-	/** 
-	 * Returns a list of comments from the image file.
-	 * <p/>
-	 * This is mostly obsolete.
+	/**
+	 * Returns a list of comments from the image file. <p/> This is mostly
+	 * obsolete.
 	 */
 	public ArrayList getComments()
 	{
 		return new ArrayList(comments);
 	}
 
-	/** 
-	 * Returns the image file format, ie. ImageFormat.IMAGE_FORMAT_PNG.
-	 * <p/>
+	/**
+	 * Returns the image file format, ie. ImageFormat.IMAGE_FORMAT_PNG. <p/>
 	 * Returns ImageFormat.IMAGE_FORMAT_UNKNOWN if format is unknown.
 	 * 
-	 * @return      A constant defined in ImageFormat.
-	 * @see         ImageFormat
+	 * @return A constant defined in ImageFormat.
+	 * @see ImageFormat
 	 */
 	public ImageFormat getFormat()
 	{
 		return format;
 	}
 
-	/** 
+	/**
 	 * Returns a string with the name of the image file format.
 	 * 
 	 * @see #getFormat()
@@ -145,7 +143,7 @@
 		return formatName;
 	}
 
-	/** 
+	/**
 	 * Returns the height of the image in pixels.
 	 * 
 	 * @see #getWidth()
@@ -155,7 +153,7 @@
 		return height;
 	}
 
-	/** 
+	/**
 	 * Returns the MIME type of the image.
 	 * 
 	 * @see #getFormat()
@@ -165,70 +163,74 @@
 		return mimeType;
 	}
 
-	/** 
+	/**
 	 * Returns the number of images in the file.
 	 * <p>
-	 * Applies mostly to GIF and TIFF; reading PSD/Photoshop layers is not supported, 
-	 * and Jpeg/JFIF EXIF thumbnails are not included in this count.
+	 * Applies mostly to GIF and TIFF; reading PSD/Photoshop layers is not
+	 * supported, and Jpeg/JFIF EXIF thumbnails are not included in this count.
 	 */
 	public int getNumberOfImages()
 	{
 		return numberOfImages;
 	}
 
-	/** 
+	/**
 	 * Returns horizontal dpi of the image, if available.
 	 * <p>
-	 * Applies to TIFF (optional), BMP (always), GIF (constant: 72),
-	 * Jpeg (optional), PNG (optional), PNM (constant: 72), PSD/Photoshop (constant: 72).
+	 * Applies to TIFF (optional), BMP (always), GIF (constant: 72), Jpeg
+	 * (optional), PNG (optional), PNM (constant: 72), PSD/Photoshop (constant:
+	 * 72).
 	 * 
-	 * @return      returns -1 if not present.
+	 * @return returns -1 if not present.
 	 */
 	public int getPhysicalHeightDpi()
 	{
 		return physicalHeightDpi;
 	}
 
-	/** 
+	/**
 	 * Returns physical height of the image in inches, if available.
 	 * <p>
-	 * Applies to TIFF (optional), BMP (always), GIF (constant: 72),
-	 * Jpeg (optional), PNG (optional), PNM (constant: 72), PSD/Photoshop (constant: 72).
+	 * Applies to TIFF (optional), BMP (always), GIF (constant: 72), Jpeg
+	 * (optional), PNG (optional), PNM (constant: 72), PSD/Photoshop (constant:
+	 * 72).
 	 * 
-	 * @return      returns -1 if not present.
+	 * @return returns -1 if not present.
 	 */
 	public float getPhysicalHeightInch()
 	{
 		return physicalHeightInch;
 	}
 
-	/** 
+	/**
 	 * Returns vertical dpi of the image, if available.
 	 * <p>
-	 * Applies to TIFF (optional), BMP (always), GIF (constant: 72),
-	 * Jpeg (optional), PNG (optional), PNM (constant: 72), PSD/Photoshop (constant: 72).
+	 * Applies to TIFF (optional), BMP (always), GIF (constant: 72), Jpeg
+	 * (optional), PNG (optional), PNM (constant: 72), PSD/Photoshop (constant:
+	 * 72).
 	 * 
-	 * @return      returns -1 if not present.
+	 * @return returns -1 if not present.
 	 */
 	public int getPhysicalWidthDpi()
 	{
 		return physicalWidthDpi;
 	}
 
-	/** 
+	/**
 	 * Returns physical width of the image in inches, if available.
 	 * <p>
-	 * Applies to TIFF (optional), BMP (always), GIF (constant: 72),
-	 * Jpeg (optional), PNG (optional), PNM (constant: 72), PSD/Photoshop (constant: 72).
+	 * Applies to TIFF (optional), BMP (always), GIF (constant: 72), Jpeg
+	 * (optional), PNG (optional), PNM (constant: 72), PSD/Photoshop (constant:
+	 * 72).
 	 * 
-	 * @return      returns -1 if not present.
+	 * @return returns -1 if not present.
 	 */
 	public float getPhysicalWidthInch()
 	{
 		return physicalWidthInch;
 	}
 
-	/** 
+	/**
 	 * Returns the width of the image in pixels.
 	 * 
 	 * @see #getHeight()
@@ -238,7 +240,7 @@
 		return width;
 	}
 
-	/** 
+	/**
 	 * Returns true if the image is progressive or interlaced.
 	 */
 	public boolean getIsProgressive()
@@ -246,8 +248,9 @@
 		return isProgressive;
 	}
 
-	/** 
-	 * Returns the color type of the image, as a constant (ie. ImageFormat.COLOR_TYPE_CMYK).
+	/**
+	 * Returns the color type of the image, as a constant (ie.
+	 * ImageFormat.COLOR_TYPE_CMYK).
 	 * 
 	 * @see #getColorTypeDescription()
 	 */
@@ -256,7 +259,7 @@
 		return colorType;
 	}
 
-	/** 
+	/**
 	 * Returns a description of the color type of the image.
 	 * 
 	 * @see #getColorType()
@@ -265,21 +268,21 @@
 	{
 		switch (colorType)
 		{
-			case COLOR_TYPE_BW :
-				return "Black and White";
-			case COLOR_TYPE_GRAYSCALE :
-				return "Grayscale";
-			case COLOR_TYPE_RGB :
-				return "RGB";
-			case COLOR_TYPE_CMYK :
-				return "CMYK";
-			case COLOR_TYPE_OTHER :
-				return "Other";
-			case COLOR_TYPE_UNKNOWN :
-				return "Unknown";
+		case COLOR_TYPE_BW:
+			return "Black and White";
+		case COLOR_TYPE_GRAYSCALE:
+			return "Grayscale";
+		case COLOR_TYPE_RGB:
+			return "RGB";
+		case COLOR_TYPE_CMYK:
+			return "CMYK";
+		case COLOR_TYPE_OTHER:
+			return "Other";
+		case COLOR_TYPE_UNKNOWN:
+			return "Unknown";
 
-			default :
-				return "Unknown";
+		default:
+			return "Unknown";
 		}
 
 	}
@@ -298,10 +301,9 @@
 
 			toString(pw, "");
 			pw.flush();
-			
+
 			return sw.toString();
-		}
-		catch (Exception e)
+		} catch (Exception e)
 		{
 			return "Image Data: Error";
 		}

Modified: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/ImageReadException.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/ImageReadException.java?rev=682538&r1=682537&r2=682538&view=diff
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/ImageReadException.java (original)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/ImageReadException.java Mon Aug  4 16:23:45 2008
@@ -16,7 +16,7 @@
  */
 package org.apache.sanselan;
 
-public class ImageReadException extends Exception
+public class ImageReadException extends SanselanException
 {
 	static final long serialVersionUID = -1L;
 

Modified: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/ImageWriteException.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/ImageWriteException.java?rev=682538&r1=682537&r2=682538&view=diff
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/ImageWriteException.java (original)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/ImageWriteException.java Mon Aug  4 16:23:45 2008
@@ -16,7 +16,7 @@
  */
 package org.apache.sanselan;
 
-public class ImageWriteException extends Exception
+public class ImageWriteException extends SanselanException
 {
 	static final long serialVersionUID = -1L;
 

Added: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/SanselanException.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/SanselanException.java?rev=682538&view=auto
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/SanselanException.java (added)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/SanselanException.java Mon Aug  4 16:23:45 2008
@@ -0,0 +1,32 @@
+/*
+ * 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 org.apache.sanselan;
+
+public class SanselanException extends Exception
+{
+	static final long serialVersionUID = -1L;
+
+	public SanselanException(String s)
+	{
+		super(s);
+	}
+
+	public SanselanException(String s, Exception e)
+	{
+		super(s, e);
+	}
+}
\ No newline at end of file

Propchange: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/SanselanException.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/common/BinaryConstants.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/common/BinaryConstants.java?rev=682538&r1=682537&r2=682538&view=diff
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/common/BinaryConstants.java (original)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/common/BinaryConstants.java Mon Aug  4 16:23:45 2008
@@ -21,10 +21,12 @@
 	public static final int BYTE_ORDER_INTEL = 'I';
 	public static final int BYTE_ORDER_LEAST_SIGNIFICANT_BYTE = BYTE_ORDER_INTEL;
 	public static final int BYTE_ORDER_LSB = BYTE_ORDER_INTEL;
+	public static final int BYTE_ORDER_LITTLE_ENDIAN = BYTE_ORDER_INTEL;
 
 	public static final int BYTE_ORDER_MOTOROLA = 'M';
 	public static final int BYTE_ORDER_MOST_SIGNIFICANT_BYTE = BYTE_ORDER_MOTOROLA;
 	public static final int BYTE_ORDER_MSB = BYTE_ORDER_MOTOROLA;
 	public static final int BYTE_ORDER_NETWORK = BYTE_ORDER_MOTOROLA;
+	public static final int BYTE_ORDER_BIG_ENDIAN = BYTE_ORDER_MOTOROLA;
 
 }
\ No newline at end of file

Added: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/common/ZLibUtils.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/common/ZLibUtils.java?rev=682538&view=auto
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/common/ZLibUtils.java (added)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/common/ZLibUtils.java Mon Aug  4 16:23:45 2008
@@ -0,0 +1,44 @@
+/*
+ * 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 org.apache.sanselan.common;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.zip.DeflaterOutputStream;
+import java.util.zip.InflaterInputStream;
+
+public class ZLibUtils extends BinaryFileFunctions
+{
+	public final byte[] inflate(byte bytes[]) throws IOException
+	// slow, probably.
+	{
+		ByteArrayInputStream in = new ByteArrayInputStream(bytes);
+		InflaterInputStream zIn = new InflaterInputStream(in);
+		return getStreamBytes(zIn);
+	}
+
+	public final byte[] deflate(byte bytes[]) throws IOException
+	{
+		ByteArrayOutputStream baos = new ByteArrayOutputStream();
+		DeflaterOutputStream dos = new DeflaterOutputStream(baos);
+		dos.write(bytes);
+		dos.close();
+		return baos.toByteArray();
+	}
+
+}

Propchange: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/common/ZLibUtils.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/PngConstants.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/PngConstants.java?rev=682538&r1=682537&r2=682538&view=diff
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/PngConstants.java (original)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/PngConstants.java Mon Aug  4 16:23:45 2008
@@ -27,7 +27,24 @@
 	public final static byte[] PLTE_CHUNK_TYPE = new byte[] { 80, 76, 84, 69 };
 	public final static byte[] IEND_CHUNK_TYPE = new byte[] { 73, 69, 78, 68 };
 	public final static byte[] IDAT_CHUNK_TYPE = new byte[] { 73, 68, 65, 84 };
-	public final static byte[] iTXt_CHUNK_TYPE = new byte[] { 105, 84, 88, 116 };
+	public final static byte[] iTXt_CHUNK_TYPE = new byte[] { // 
+	105, // 
+			84, // 
+			88, // 
+			116, // 
+	};
+	public final static byte[] tEXt_CHUNK_TYPE = new byte[] { // 
+	0x74, //
+			0x45, //
+			0x58, //
+			0x74, //
+	};
+	public final static byte[] zTXt_CHUNK_TYPE = new byte[] { //
+	0x7A, //
+			0x54, //
+			0x58, //
+			0x74, //
+	};
 
 	public final static int IEND = PngImageParser.CharsToQuad('I', 'E', 'N',
 			'D');
@@ -108,4 +125,14 @@
 
 	public final String XMP_KEYWORD = "XML:com.adobe.xmp";
 
+	/**
+	 * Parameter key.
+	 * 
+	 * Only used when writing Png images.
+	 * <p>
+	 * Valid values: a list of WriteTexts.
+	 * <p>
+	 */
+	public static final String PARAM_KEY_PNG_TEXT_CHUNKS = "PNG_TEXT_CHUNKS";
+
 }
\ No newline at end of file

Added: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/PngImageInfo.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/PngImageInfo.java?rev=682538&view=auto
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/PngImageInfo.java (added)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/PngImageInfo.java Mon Aug  4 16:23:45 2008
@@ -0,0 +1,35 @@
+package org.apache.sanselan.formats.png;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.sanselan.ImageFormat;
+import org.apache.sanselan.ImageInfo;
+
+public class PngImageInfo extends ImageInfo
+{
+	private final List textChunks;
+
+	public PngImageInfo(String formatDetails, int bitsPerPixel,
+			ArrayList comments, ImageFormat format, String formatName,
+			int height, String mimeType, int numberOfImages,
+			int physicalHeightDpi, float physicalHeightInch,
+			int physicalWidthDpi, float physicalWidthInch, int width,
+			boolean isProgressive, boolean isTransparent, boolean usesPalette,
+			int colorType, String compressionAlgorithm, final List textChunks)
+	{
+		super(formatDetails, bitsPerPixel, comments, format, formatName,
+				height, mimeType, numberOfImages, physicalHeightDpi,
+				physicalHeightInch, physicalWidthDpi, physicalWidthInch, width,
+				isProgressive, isTransparent, usesPalette, colorType,
+				compressionAlgorithm);
+
+		this.textChunks = textChunks;
+	}
+
+	public List getTextChunks()
+	{
+		return new ArrayList(textChunks);
+	}
+
+}

Propchange: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/PngImageInfo.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/PngImageParser.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/PngImageParser.java?rev=682538&r1=682537&r2=682538&view=diff
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/PngImageParser.java (original)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/PngImageParser.java Mon Aug  4 16:23:45 2008
@@ -474,7 +474,7 @@
 			throws ImageReadException, IOException
 	{
 		ArrayList chunks = readChunks(byteSource, new int[] { IHDR, pHYs, tEXt,
-				zTXt, tRNS, PLTE, }, false);
+				zTXt, tRNS, PLTE, iTXt, }, false);
 
 		// if(chunks!=null)
 		// System.out.println("chunks: " + chunks.size());
@@ -510,35 +510,29 @@
 
 		ArrayList tEXts = filterChunks(chunks, tEXt);
 		ArrayList zTXts = filterChunks(chunks, zTXt);
-
-		// private class PNGChunkpHYs extends PNGChunk
-		// {
-		// public final int PixelsPerUnitXAxis;
-		// public final int PixelsPerUnitYAxis;
-		// public final int UnitSpecifier;
+		ArrayList iTXts = filterChunks(chunks, iTXt);
 
 		{
-			// private class PNGChunkIHDR extends PNGChunk
-			// {
-			// public final int Width;
-			// public final int Height;
-			// public final int BitDepth;
-			// public final int ColorType;
-			// public final int CompressionMethod;
-			// public final int FilterMethod;
-			// public final int InterlaceMethod;
-
-			ArrayList Comments = new ArrayList();
+			ArrayList comments = new ArrayList();
+			List textChunks = new ArrayList();
 
 			for (int i = 0; i < tEXts.size(); i++)
 			{
 				PNGChunktEXt pngChunktEXt = (PNGChunktEXt) tEXts.get(i);
-				Comments.add(pngChunktEXt.keyword + ": " + pngChunktEXt.text);
+				comments.add(pngChunktEXt.keyword + ": " + pngChunktEXt.text);
+				textChunks.add(pngChunktEXt.getContents());
 			}
 			for (int i = 0; i < zTXts.size(); i++)
 			{
 				PNGChunkzTXt pngChunkzTXt = (PNGChunkzTXt) zTXts.get(i);
-				Comments.add(pngChunkzTXt.keyword + ": " + pngChunkzTXt.text);
+				comments.add(pngChunkzTXt.keyword + ": " + pngChunkzTXt.text);
+				textChunks.add(pngChunkzTXt.getContents());
+			}
+			for (int i = 0; i < iTXts.size(); i++)
+			{
+				PNGChunkiTXt pngChunkiTXt = (PNGChunkiTXt) iTXts.get(i);
+				comments.add(pngChunkiTXt.keyword + ": " + pngChunkiTXt.text);
+				textChunks.add(pngChunkiTXt.getContents());
 			}
 
 			int BitsPerPixel = pngChunkIHDR.bitDepth
@@ -614,11 +608,12 @@
 
 			String compressionAlgorithm = ImageInfo.COMPRESSION_ALGORITHM_PNG_FILTER;
 
-			ImageInfo result = new ImageInfo(FormatDetails, BitsPerPixel,
-					Comments, Format, FormatName, Height, MimeType,
+			ImageInfo result = new PngImageInfo(FormatDetails, BitsPerPixel,
+					comments, Format, FormatName, Height, MimeType,
 					NumberOfImages, PhysicalHeightDpi, PhysicalHeightInch,
 					PhysicalWidthDpi, PhysicalWidthInch, Width, isProgressive,
-					isTransparent, usesPalette, ColorType, compressionAlgorithm);
+					isTransparent, usesPalette, ColorType,
+					compressionAlgorithm, textChunks);
 
 			return result;
 		}
@@ -841,7 +836,7 @@
 		pw.println("");
 
 		pw.flush();
-		
+
 		return true;
 	}
 
@@ -882,7 +877,8 @@
 		if (xmpChunks.size() < 1)
 			return null;
 		if (xmpChunks.size() > 1)
-			throw new ImageReadException("PNG contains more than one XMP chunk.");
+			throw new ImageReadException(
+					"PNG contains more than one XMP chunk.");
 
 		PNGChunkiTXt chunk = (PNGChunkiTXt) xmpChunks.get(0);
 		return chunk.getText();

Added: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/PngText.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/PngText.java?rev=682538&view=auto
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/PngText.java (added)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/PngText.java Mon Aug  4 16:23:45 2008
@@ -0,0 +1,59 @@
+package org.apache.sanselan.formats.png;
+
+import org.apache.sanselan.ImageWriteException;
+import org.apache.sanselan.SanselanException;
+import org.apache.sanselan.util.UnicodeUtils;
+
+public abstract class PngText
+{
+	public PngText(String keyword, String text)
+	{
+		this.keyword = keyword;
+		this.text = text;
+	}
+
+	public final String keyword, text;
+
+	public static class tEXt extends PngText
+	{
+		public tEXt(String keyword, String text)
+		{
+			super(keyword, text);
+		}
+	}
+
+	public static class zTXt extends PngText
+	{
+		public zTXt(String keyword, String text)
+		{
+			super(keyword, text);
+		}
+	}
+
+	public static class iTXt extends PngText
+	{
+
+		/*
+		 * The language tag defined in [RFC-3066] indicates the human language
+		 * used by the translated keyword and the text. Unlike the keyword, the
+		 * language tag is case-insensitive. It is an ISO 646.IRV:1991 [ISO 646]
+		 * string consisting of hyphen-separated words of 1-8 alphanumeric
+		 * characters each (for example cn, en-uk, no-bok, x-klingon,
+		 * x-KlInGoN). If the first word is two or three letters long, it is an
+		 * ISO language code [ISO-639]. If the language tag is empty, the
+		 * language is unspecified.
+		 */
+		public final String languageTag;
+
+		public final String translatedKeyword;
+
+		public iTXt(String keyword, String text, String languageTag,
+				String translatedKeyword)
+		{
+			super(keyword, text);
+			this.languageTag = languageTag;
+			this.translatedKeyword = translatedKeyword;
+		}
+	}
+
+}

Propchange: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/PngText.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/PngWriter.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/PngWriter.java?rev=682538&r1=682537&r2=682538&view=diff
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/PngWriter.java (original)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/PngWriter.java Mon Aug  4 16:23:45 2008
@@ -21,16 +21,18 @@
 import java.io.IOException;
 import java.io.OutputStream;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.zip.DeflaterOutputStream;
 
 import org.apache.sanselan.ImageWriteException;
-import org.apache.sanselan.common.ZLibInflater;
+import org.apache.sanselan.common.ZLibUtils;
 import org.apache.sanselan.palette.MedianCutQuantizer;
 import org.apache.sanselan.palette.Palette;
 import org.apache.sanselan.palette.PaletteFactory;
 import org.apache.sanselan.util.Debug;
 import org.apache.sanselan.util.ParamMap;
+import org.apache.sanselan.util.UnicodeUtils;
 
 public class PngWriter implements PngConstants
 {
@@ -72,23 +74,22 @@
 		os.write(0xff & (value >> 0));
 	}
 
-	private final void writeChunk(OutputStream os, byte chunk_type[],
+	private final void writeChunk(OutputStream os, byte chunkType[],
 			byte data[]) throws IOException
 	{
-		int data_length = data == null ? 0 : data.length;
-		writeInt(os, data_length);
-		os.write(chunk_type);
+		int dataLength = data == null ? 0 : data.length;
+		writeInt(os, dataLength);
+		os.write(chunkType);
 		if (data != null)
 			os.write(data);
 
-		// Debug.debug("writeChunk chunk_type", chunk_type);
+		// Debug.debug("writeChunk chunkType", chunkType);
 		// Debug.debug("writeChunk data", data);
 
 		{
 			PngCrc png_crc = new PngCrc();
 
-			long crc1 = png_crc
-					.start_partial_crc(chunk_type, chunk_type.length);
+			long crc1 = png_crc.start_partial_crc(chunkType, chunkType.length);
 			long crc2 = data == null ? crc1 : png_crc.continue_partial_crc(
 					crc1, data, data.length);
 			int crc = (int) png_crc.finish_partial_crc(crc2);
@@ -146,7 +147,89 @@
 		writeChunk(os, IHDR_CHUNK_TYPE, baos.toByteArray());
 	}
 
-	private void writeChunkiTXt(OutputStream os, String xmpXml)
+	private void writeChunkiTXt(OutputStream os, PngText.iTXt text)
+			throws IOException, ImageWriteException
+	{
+		if (!UnicodeUtils.isValidISO_8859_1(text.keyword))
+			throw new ImageWriteException(
+					"Png tEXt chunk keyword is not ISO-8859-1: " + text.keyword);
+		if (!UnicodeUtils.isValidISO_8859_1(text.languageTag))
+			throw new ImageWriteException(
+					"Png tEXt chunk language tag is not ISO-8859-1: "
+							+ text.languageTag);
+
+		ByteArrayOutputStream baos = new ByteArrayOutputStream();
+
+		// keyword
+		baos.write(text.keyword.getBytes("ISO-8859-1"));
+		baos.write(0);
+
+		baos.write(1); // compressed flag, true
+		baos.write(COMPRESSION_DEFLATE_INFLATE); // compression method
+
+		// language tag
+		baos.write(text.languageTag.getBytes("ISO-8859-1"));
+		baos.write(0);
+
+		// translated keyword
+		baos.write(text.translatedKeyword.getBytes("utf-8"));
+		baos.write(0);
+
+		baos.write(new ZLibUtils().deflate(text.text.getBytes("utf-8")));
+
+		writeChunk(os, iTXt_CHUNK_TYPE, baos.toByteArray());
+	}
+
+	private void writeChunkzTXt(OutputStream os, PngText.zTXt text)
+			throws IOException, ImageWriteException
+	{
+		if (!UnicodeUtils.isValidISO_8859_1(text.keyword))
+			throw new ImageWriteException(
+					"Png zTXt chunk keyword is not ISO-8859-1: " + text.keyword);
+		if (!UnicodeUtils.isValidISO_8859_1(text.text))
+			throw new ImageWriteException(
+					"Png zTXt chunk text is not ISO-8859-1: " + text.text);
+
+		ByteArrayOutputStream baos = new ByteArrayOutputStream();
+
+		// keyword
+		baos.write(text.keyword.getBytes("ISO-8859-1"));
+		baos.write(0);
+
+		// compression method
+		baos.write(PngConstants.COMPRESSION_DEFLATE_INFLATE);
+
+		// text
+		baos
+				.write(new ZLibUtils().deflate(text.text
+						.getBytes("ISO-8859-1")));
+
+		writeChunk(os, zTXt_CHUNK_TYPE, baos.toByteArray());
+	}
+
+	private void writeChunktEXt(OutputStream os, PngText.tEXt text)
+			throws IOException, ImageWriteException
+	{
+		if (!UnicodeUtils.isValidISO_8859_1(text.keyword))
+			throw new ImageWriteException(
+					"Png tEXt chunk keyword is not ISO-8859-1: " + text.keyword);
+		if (!UnicodeUtils.isValidISO_8859_1(text.text))
+			throw new ImageWriteException(
+					"Png tEXt chunk text is not ISO-8859-1: " + text.text);
+
+		ByteArrayOutputStream baos = new ByteArrayOutputStream();
+
+		// keyword
+		baos.write(text.keyword.getBytes("ISO-8859-1"));
+		baos.write(0);
+
+		// text
+		baos.write(text.text.getBytes("ISO-8859-1"));
+
+		writeChunk(os, tEXt_CHUNK_TYPE, baos.toByteArray());
+	}
+
+	private void writeChunkXmpiTXt(OutputStream os, String xmpXml)
 			throws IOException
 	{
 
@@ -165,7 +248,7 @@
 		baos.write(XMP_KEYWORD.getBytes("utf-8"));
 		baos.write(0);
 
-		baos.write(new ZLibInflater().deflate(xmpXml.getBytes("utf-8")));
+		baos.write(new ZLibUtils().deflate(xmpXml.getBytes("utf-8")));
 
 		writeChunk(os, iTXt_CHUNK_TYPE, baos.toByteArray());
 	}
@@ -298,6 +381,8 @@
 			params.remove(PARAM_KEY_PNG_BIT_DEPTH);
 		if (params.containsKey(PARAM_KEY_XMP_XML))
 			params.remove(PARAM_KEY_XMP_XML);
+		if (params.containsKey(PARAM_KEY_PNG_TEXT_CHUNKS))
+			params.remove(PARAM_KEY_PNG_TEXT_CHUNKS);
 		if (params.size() > 0)
 		{
 			Object firstKey = params.keySet().iterator().next();
@@ -395,7 +480,25 @@
 		if (params.containsKey(PARAM_KEY_XMP_XML))
 		{
 			String xmpXml = (String) params.get(PARAM_KEY_XMP_XML);
-			writeChunkiTXt(os, xmpXml);
+			writeChunkXmpiTXt(os, xmpXml);
+		}
+
+		if (params.containsKey(PARAM_KEY_PNG_TEXT_CHUNKS))
+		{
+			List outputTexts = (List) params.get(PARAM_KEY_PNG_TEXT_CHUNKS);
+			for (int i = 0; i < outputTexts.size(); i++)
+			{
+				PngText text = (PngText) outputTexts.get(i);
+				if (text instanceof PngText.tEXt)
+					writeChunktEXt(os, (PngText.tEXt) text);
+				else if (text instanceof PngText.zTXt)
+					writeChunkzTXt(os, (PngText.zTXt) text);
+				else if (text instanceof PngText.iTXt)
+					writeChunkiTXt(os, (PngText.iTXt) text);
+				else
+					throw new ImageWriteException(
+							"Unknown text to embed in PNG: " + text);
+			}
 		}
 
 		{
@@ -438,9 +541,10 @@
 								int gray = (red + green + blue) / 3;
 								// if(y==0)
 								// {
-								// Debug.debug("gray: " + x + ", " + y + " argb: 0x"
-								//		+ Integer.toHexString(argb) + " gray: 0x"
-								// 		+ Integer.toHexString(gray));
+								// Debug.debug("gray: " + x + ", " + y +
+								// " argb: 0x"
+								// + Integer.toHexString(argb) + " gray: 0x"
+								// + Integer.toHexString(gray));
 								// // Debug.debug(x + ", " + y + " gray", gray);
 								// // Debug.debug(x + ", " + y + " gray", gray);
 								// Debug.debug(x + ", " + y + " gray", gray +
@@ -462,7 +566,7 @@
 				uncompressed = baos.toByteArray();
 			}
 
-			 // Debug.debug("uncompressed", uncompressed.length);
+			// Debug.debug("uncompressed", uncompressed.length);
 
 			ByteArrayOutputStream baos = new ByteArrayOutputStream();
 			DeflaterOutputStream dos = new DeflaterOutputStream(baos);

Modified: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunkiCCP.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunkiCCP.java?rev=682538&r1=682537&r2=682538&view=diff
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunkiCCP.java (original)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunkiCCP.java Mon Aug  4 16:23:45 2008
@@ -19,7 +19,7 @@
 import java.io.IOException;
 
 import org.apache.sanselan.ImageReadException;
-import org.apache.sanselan.common.ZLibInflater;
+import org.apache.sanselan.common.ZLibUtils;
 
 public class PNGChunkiCCP extends PNGChunk
 {
@@ -63,7 +63,7 @@
 				System.out.println("bytes.length: " + bytes.length);
 			}
 
-			UncompressedProfile = new ZLibInflater()
+			UncompressedProfile = new ZLibUtils()
 					.inflate(CompressedProfile);
 
 			if (getDebug())

Modified: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunkiTXt.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunkiTXt.java?rev=682538&r1=682537&r2=682538&view=diff
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunkiTXt.java (original)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunkiTXt.java Mon Aug  4 16:23:45 2008
@@ -19,8 +19,9 @@
 import java.io.IOException;
 
 import org.apache.sanselan.ImageReadException;
-import org.apache.sanselan.common.ZLibInflater;
+import org.apache.sanselan.common.ZLibUtils;
 import org.apache.sanselan.formats.png.PngConstants;
+import org.apache.sanselan.formats.png.PngText;
 
 public class PNGChunkiTXt extends PNGTextChunk
 {
@@ -91,13 +92,14 @@
 
 			if (compressed)
 			{
-				int compressedTextLength = bytes.length - index;;
+				int compressedTextLength = bytes.length - index;
+				;
 				byte compressedText[] = new byte[compressedTextLength];
 				System.arraycopy(bytes, index, compressedText, 0,
 						compressedTextLength);
 
-				text = new String(new ZLibInflater()
-						.inflate(compressedText), "utf-8");
+				text = new String(new ZLibUtils().inflate(compressedText),
+						"utf-8");
 
 			} else
 				text = new String(bytes, index, bytes.length - index, "utf-8");
@@ -119,4 +121,9 @@
 	{
 		return text;
 	}
+
+	public PngText getContents()
+	{
+		return new PngText.iTXt(keyword, text, languageTag, translatedKeyword);
+	}
 }
\ No newline at end of file

Modified: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunktEXt.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunktEXt.java?rev=682538&r1=682537&r2=682538&view=diff
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunktEXt.java (original)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunktEXt.java Mon Aug  4 16:23:45 2008
@@ -19,6 +19,8 @@
 import java.io.IOException;
 
 import org.apache.sanselan.ImageReadException;
+import org.apache.sanselan.ImageWriteException;
+import org.apache.sanselan.formats.png.PngText;
 
 public class PNGChunktEXt extends PNGTextChunk
 {
@@ -31,7 +33,8 @@
 		{
 			int index = findNull(bytes);
 			if (index < 0)
-				throw new ImageReadException("PNG tEXt chunk keyword is not terminated.");
+				throw new ImageReadException(
+						"PNG tEXt chunk keyword is not terminated.");
 
 			keyword = new String(bytes, 0, index, "ISO-8859-1");
 
@@ -62,4 +65,10 @@
 	{
 		return text;
 	}
+
+	public PngText getContents()
+	{
+		return new PngText.tEXt(keyword, text);
+	}
+
 }
\ No newline at end of file

Modified: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunkzTXt.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunkzTXt.java?rev=682538&r1=682537&r2=682538&view=diff
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunkzTXt.java (original)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGChunkzTXt.java Mon Aug  4 16:23:45 2008
@@ -19,8 +19,10 @@
 import java.io.IOException;
 
 import org.apache.sanselan.ImageReadException;
-import org.apache.sanselan.common.ZLibInflater;
+import org.apache.sanselan.common.ZLibUtils;
 import org.apache.sanselan.formats.png.PngConstants;
+import org.apache.sanselan.formats.png.PngText;
+import org.apache.sanselan.util.Debug;
 
 public class PNGChunkzTXt extends PNGTextChunk
 {
@@ -39,19 +41,20 @@
 						"PNG zTXt chunk keyword is unterminated.");
 
 			keyword = new String(bytes, 0, index, "ISO-8859-1");
+			index++;
 
-			int compressionMethod = bytes[index + 1];
+			int compressionMethod = bytes[index++];
 			if (compressionMethod != PngConstants.COMPRESSION_DEFLATE_INFLATE)
 				throw new ImageReadException(
 						"PNG zTXt chunk has unexpected compression method: "
 								+ compressionMethod);
 
-			int compressedTextLength = bytes.length - (index + 1 + 1);
+			int compressedTextLength = bytes.length - index;
 			byte compressedText[] = new byte[compressedTextLength];
-			System.arraycopy(bytes, index + 1 + 1, compressedText, 0,
+			System.arraycopy(bytes, index, compressedText, 0,
 					compressedTextLength);
 
-			text = new String(new ZLibInflater().inflate(compressedText),
+			text = new String(new ZLibUtils().inflate(compressedText),
 					"ISO-8859-1");
 		}
 	}
@@ -72,4 +75,9 @@
 		return text;
 	}
 
+	public PngText getContents()
+	{
+		return new PngText.zTXt(keyword, text);
+	}
+
 }
\ No newline at end of file

Modified: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGTextChunk.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGTextChunk.java?rev=682538&r1=682537&r2=682538&view=diff
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGTextChunk.java (original)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/formats/png/chunks/PNGTextChunk.java Mon Aug  4 16:23:45 2008
@@ -18,6 +18,8 @@
 
 import java.io.IOException;
 
+import org.apache.sanselan.formats.png.PngText;
+
 public abstract class PNGTextChunk extends PNGChunk
 {
 
@@ -32,4 +34,6 @@
 
 	public abstract String getText();
 
+	public abstract PngText getContents();
+
 }
\ No newline at end of file

Added: 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=682538&view=auto
==============================================================================
--- incubator/sanselan/trunk/src/main/java/org/apache/sanselan/util/UnicodeUtils.java (added)
+++ incubator/sanselan/trunk/src/main/java/org/apache/sanselan/util/UnicodeUtils.java Mon Aug  4 16:23:45 2008
@@ -0,0 +1,457 @@
+/*
+ * 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 org.apache.sanselan.util;
+
+import java.io.UnsupportedEncodingException;
+
+import org.apache.sanselan.common.BinaryConstants;
+
+public abstract class UnicodeUtils implements BinaryConstants
+{
+	public static class UnicodeException extends Exception
+	{
+		public UnicodeException(String message)
+		{
+			super(message);
+		}
+	}
+
+	// A default single-byte charset.
+	public static final int CHAR_ENCODING_CODE_ISO_8859_1 = 0;
+	public static final int CHAR_ENCODING_CODE_UTF_16_BIG_ENDIAN_WITH_BOM = 1;
+	public static final int CHAR_ENCODING_CODE_UTF_16_LITTLE_ENDIAN_WITH_BOM = 2;
+	public static final int CHAR_ENCODING_CODE_UTF_16_BIG_ENDIAN_NO_BOM = 3;
+	public static final int CHAR_ENCODING_CODE_UTF_16_LITTLE_ENDIAN_NO_BOM = 4;
+	public static final int CHAR_ENCODING_CODE_UTF_8 = 5;
+	public static final int CHAR_ENCODING_CODE_AMBIGUOUS = -1;
+
+	// /*
+	// * Guess the character encoding of arbitrary character data in a data
+	// * buffer.
+	// *
+	// * The data may not run to the end of the buffer; it may be terminated.
+	// This
+	// * makes the problem much harder, since the character data may be followed
+	// * by arbitrary data.
+	// */
+	// public static int guessCharacterEncoding(byte bytes[], int index)
+	// {
+	// int length = bytes.length - index;
+	//
+	// if (length < 1)
+	// return CHAR_ENCODING_CODE_AMBIGUOUS;
+	//
+	// if (length >= 2)
+	// {
+	// // look for BOM.
+	//
+	// int c1 = 0xff & bytes[index];
+	// int c2 = 0xff & bytes[index + 1];
+	// if (c1 == 0xFF && c2 == 0xFE)
+	// return CHAR_ENCODING_CODE_UTF_16_LITTLE_ENDIAN_WITH_BOM;
+	// else if (c1 == 0xFE && c2 == 0xFF)
+	// return CHAR_ENCODING_CODE_UTF_16_BIG_ENDIAN_WITH_BOM;
+	// }
+	//
+	// }
+	//
+	// /*
+	// * Guess the character encoding of arbitrary character data in a data
+	// * buffer.
+	// *
+	// * The data fills the entire buffer. If it is terminated, the terminator
+	// * byte(s) will be the last bytes in the buffer.
+	// *
+	// * This makes the problem a bit easier.
+	// */
+	// public static int guessCharacterEncodingSimple(byte bytes[], int index)
+	// throws UnicodeException
+	// {
+	// int length = bytes.length - index;
+	//
+	// if (length < 1)
+	// return CHAR_ENCODING_CODE_AMBIGUOUS;
+	//
+	// if (length >= 2)
+	// {
+	// // identify or eliminate UTF-16 with a BOM.
+	//
+	// int c1 = 0xff & bytes[index];
+	// int c2 = 0xff & bytes[index + 1];
+	// if (c1 == 0xFF && c2 == 0xFE)
+	// return CHAR_ENCODING_CODE_UTF_16_LITTLE_ENDIAN_WITH_BOM;
+	// else if (c1 == 0xFE && c2 == 0xFF)
+	// return CHAR_ENCODING_CODE_UTF_16_BIG_ENDIAN_WITH_BOM;
+	// }
+	//
+	// if (length >= 2)
+	// {
+	// // look for optional double-byte terminator.
+	//
+	// int c1 = 0xff & bytes[bytes.length - 2];
+	// int c2 = 0xff & bytes[bytes.length - 1];
+	// if (c1 == 0 && c2 == 0)
+	// {
+	// // definitely a flavor of UTF-16.
+	// if (length % 2 != 0)
+	// throw new UnicodeException(
+	// "Character data with double-byte terminator has an odd length.");
+	//
+	// boolean mayHaveTerminator = true;
+	// boolean mustHaveTerminator = false;
+	// boolean possibleBigEndian = new UnicodeMetricsUTF16NoBOM(
+	// BYTE_ORDER_BIG_ENDIAN).isValid(bytes, index,
+	// mayHaveTerminator, mustHaveTerminator);
+	// boolean possibleLittleEndian = new UnicodeMetricsUTF16NoBOM(
+	// BYTE_ORDER_LITTLE_ENDIAN).isValid(bytes, index,
+	// mayHaveTerminator, mustHaveTerminator);
+	// if ((!possibleBigEndian) && (!possibleLittleEndian))
+	// throw new UnicodeException(
+	// "Invalid character data, possibly UTF-16.");
+	// if (possibleBigEndian && possibleLittleEndian)
+	// return CHAR_ENCODING_CODE_AMBIGUOUS;
+	// if (possibleBigEndian)
+	// return CHAR_ENCODING_CODE_UTF_16_BIG_ENDIAN_NO_BOM;
+	// if (possibleLittleEndian)
+	// return CHAR_ENCODING_CODE_UTF_16_LITTLE_ENDIAN_NO_BOM;
+	// }
+	// }
+	//
+	// List possibleEncodings = new ArrayList();
+	// if (length % 2 == 0)
+	// {
+	// boolean mayHaveTerminator = true;
+	// boolean mustHaveTerminator = false;
+	// boolean possibleBigEndian = new UnicodeMetricsUTF16NoBOM(
+	// BYTE_ORDER_BIG_ENDIAN).isValid(bytes, index,
+	// mayHaveTerminator, mustHaveTerminator);
+	// boolean possibleLittleEndian = new UnicodeMetricsUTF16NoBOM(
+	// BYTE_ORDER_LITTLE_ENDIAN).isValid(bytes, index,
+	// mayHaveTerminator, mustHaveTerminator);
+	//
+	// if (possibleBigEndian)
+	// return CHAR_ENCODING_CODE_UTF_16_BIG_ENDIAN_NO_BOM;
+	// if (possibleLittleEndian)
+	// return CHAR_ENCODING_CODE_UTF_16_LITTLE_ENDIAN_NO_BOM;
+	// }
+	//
+	// }
+
+	public static final boolean isValidISO_8859_1(String s)
+	{
+		try
+		{
+			String roundtrip = new String(s.getBytes("ISO-8859-1"),
+					"ISO-8859-1");
+			return s.equals(roundtrip);
+		} catch (UnsupportedEncodingException e)
+		{
+			// should never be thrown.
+			throw new RuntimeException("Error parsing string.", e);
+		}
+	}
+
+	/*
+	 * Return the index of the first utf-16 terminator (ie. two even-aligned
+	 * nulls). If not found, return -1.
+	 */
+	private static int findFirstDoubleByteTerminator(byte bytes[], int index)
+	{
+		for (int i = index; i < bytes.length - 1; i += 2)
+		{
+			int c1 = 0xff & bytes[index];
+			int c2 = 0xff & bytes[index + 1];
+			if (c1 == 0 && c2 == 0)
+				return i;
+		}
+		return -1;
+	}
+
+	public final int findEndWithTerminator(byte bytes[], int index)
+			throws UnicodeException
+	{
+		return findEnd(bytes, index, true);
+	}
+
+	public final int findEndWithoutTerminator(byte bytes[], int index)
+			throws UnicodeException
+	{
+		return findEnd(bytes, index, false);
+	}
+
+	protected abstract int findEnd(byte bytes[], int index,
+			boolean includeTerminator) throws UnicodeException;
+
+	public static UnicodeUtils getInstance(int charEncodingCode)
+			throws UnicodeException
+	{
+		switch (charEncodingCode)
+		{
+		case CHAR_ENCODING_CODE_ISO_8859_1:
+			return new UnicodeMetricsASCII();
+		case CHAR_ENCODING_CODE_UTF_8:
+			// Debug.debug("CHAR_ENCODING_CODE_UTF_8");
+			return new UnicodeMetricsUTF8();
+		case CHAR_ENCODING_CODE_UTF_16_BIG_ENDIAN_WITH_BOM:
+		case CHAR_ENCODING_CODE_UTF_16_LITTLE_ENDIAN_WITH_BOM:
+			// Debug.debug("CHAR_ENCODING_CODE_UTF_16_WITH_BOM");
+			return new UnicodeMetricsUTF16WithBOM();
+		case CHAR_ENCODING_CODE_UTF_16_BIG_ENDIAN_NO_BOM:
+			return new UnicodeMetricsUTF16NoBOM(BYTE_ORDER_BIG_ENDIAN);
+		case CHAR_ENCODING_CODE_UTF_16_LITTLE_ENDIAN_NO_BOM:
+			return new UnicodeMetricsUTF16NoBOM(BYTE_ORDER_LITTLE_ENDIAN);
+		default:
+			throw new UnicodeException("Unknown char encoding code: "
+					+ charEncodingCode);
+		}
+	}
+
+	private static class UnicodeMetricsASCII extends UnicodeUtils
+	{
+		public int findEnd(byte bytes[], int index, boolean includeTerminator)
+				throws UnicodeException
+		{
+			for (int i = index; i < bytes.length; i++)
+			{
+				if (bytes[i] == 0)
+					return includeTerminator ? i + 1 : i;
+			}
+			return bytes.length;
+			// throw new UnicodeException("Terminator not found.");
+		}
+	}
+
+	// private static class UnicodeMetricsISO_8859_1 extends UnicodeUtils
+	// {
+	// public int findEnd(byte bytes[], int index, boolean includeTerminator)
+	// throws UnicodeException
+	// {
+	// for (int i = index; i < bytes.length; i++)
+	// {
+	// if (bytes[i] == 0)
+	// return includeTerminator ? i + 1 : i;
+	// }
+	// return bytes.length;
+	// // throw new UnicodeException("Terminator not found.");
+	// }
+	// }
+
+	private static class UnicodeMetricsUTF8 extends UnicodeUtils
+	{
+
+		public int findEnd(byte bytes[], int index, boolean includeTerminator)
+				throws UnicodeException
+		{
+			// http://en.wikipedia.org/wiki/UTF-8
+
+			while (true)
+			{
+				if (index == bytes.length)
+					return bytes.length;
+				if (index > bytes.length)
+					throw new UnicodeException("Terminator not found.");
+
+				int c1 = 0xff & bytes[index++];
+				if (c1 == 0)
+					return includeTerminator ? index : index - 1;
+				else if (c1 <= 0x7f)
+					continue;
+				else if (c1 <= 0xDF)
+				{
+					if (index >= bytes.length)
+						throw new UnicodeException("Invalid unicode.");
+
+					int c2 = 0xff & bytes[index++];
+					if (c2 < 0x80 || c2 > 0xBF)
+						throw new UnicodeException("Invalid code point.");
+				} else if (c1 <= 0xEF)
+				{
+					if (index >= bytes.length - 1)
+						throw new UnicodeException("Invalid unicode.");
+
+					int c2 = 0xff & bytes[index++];
+					if (c2 < 0x80 || c2 > 0xBF)
+						throw new UnicodeException("Invalid code point.");
+					int c3 = 0xff & bytes[index++];
+					if (c3 < 0x80 || c3 > 0xBF)
+						throw new UnicodeException("Invalid code point.");
+				} else if (c1 <= 0xF4)
+				{
+					if (index >= bytes.length - 2)
+						throw new UnicodeException("Invalid unicode.");
+
+					int c2 = 0xff & bytes[index++];
+					if (c2 < 0x80 || c2 > 0xBF)
+						throw new UnicodeException("Invalid code point.");
+					int c3 = 0xff & bytes[index++];
+					if (c3 < 0x80 || c3 > 0xBF)
+						throw new UnicodeException("Invalid code point.");
+					int c4 = 0xff & bytes[index++];
+					if (c4 < 0x80 || c4 > 0xBF)
+						throw new UnicodeException("Invalid code point.");
+				} else
+					throw new UnicodeException("Invalid code point.");
+			}
+		}
+	}
+
+	private abstract static class UnicodeMetricsUTF16 extends UnicodeUtils
+	{
+		protected static final int BYTE_ORDER_BIG_ENDIAN = 0;
+		protected static final int BYTE_ORDER_LITTLE_ENDIAN = 1;
+		protected int byteOrder = BYTE_ORDER_BIG_ENDIAN;
+
+		public UnicodeMetricsUTF16(int byteOrder)
+		{
+			this.byteOrder = byteOrder;
+		}
+
+		public boolean isValid(byte bytes[], int index,
+				boolean mayHaveTerminator, boolean mustHaveTerminator)
+				throws UnicodeException
+		{
+			// http://en.wikipedia.org/wiki/UTF-16/UCS-2
+
+			while (true)
+			{
+				if (index == bytes.length)
+				{
+					// end of buffer, no terminator found.
+					return !mustHaveTerminator;
+				}
+
+				if (index >= bytes.length - 1)
+				{
+					// end of odd-length buffer, no terminator found.
+					return false;
+				}
+
+				int c1 = 0xff & bytes[index++];
+				int c2 = 0xff & bytes[index++];
+				int msb1 = byteOrder == BYTE_ORDER_BIG_ENDIAN ? c1 : c2;
+
+				if (c1 == 0 && c2 == 0)
+				{
+					// terminator found.
+					return mayHaveTerminator;
+				}
+
+				if (msb1 >= 0xD8)
+				{
+					// Surrogate pair found.
+
+					if (msb1 >= 0xDC)
+					{
+						// invalid first surrogate.
+						return false;
+					}
+
+					if (index >= bytes.length - 1)
+					{
+						// missing second surrogate.
+						return false;
+					}
+
+					// second word.
+					int c3 = 0xff & bytes[index++];
+					int c4 = 0xff & bytes[index++];
+					int msb2 = byteOrder == BYTE_ORDER_BIG_ENDIAN ? c3 : c4;
+					if (msb2 < 0xDC)
+					{
+						// invalid second surrogate.
+						return false;
+					}
+				}
+			}
+		}
+
+		public int findEnd(byte bytes[], int index, boolean includeTerminator)
+				throws UnicodeException
+		{
+			// http://en.wikipedia.org/wiki/UTF-16/UCS-2
+
+			while (true)
+			{
+				if (index == bytes.length)
+					return bytes.length;
+				if (index > bytes.length - 1)
+					throw new UnicodeException("Terminator not found.");
+
+				int c1 = 0xff & bytes[index++];
+				int c2 = 0xff & bytes[index++];
+				int msb1 = byteOrder == BYTE_ORDER_BIG_ENDIAN ? c1 : c2;
+
+				if (c1 == 0 && c2 == 0)
+				{
+					return includeTerminator ? index : index - 2;
+				} else if (msb1 >= 0xD8)
+				{
+					if (index > bytes.length - 1)
+						throw new UnicodeException("Terminator not found.");
+
+					// second word.
+					int c3 = 0xff & bytes[index++];
+					int c4 = 0xff & bytes[index++];
+					int msb2 = byteOrder == BYTE_ORDER_BIG_ENDIAN ? c3 : c4;
+					if (msb2 < 0xDC)
+						throw new UnicodeException("Invalid code point.");
+				}
+			}
+		}
+	}
+
+	private static class UnicodeMetricsUTF16NoBOM extends UnicodeMetricsUTF16
+	{
+
+		public UnicodeMetricsUTF16NoBOM(final int byteOrder)
+		{
+			super(byteOrder);
+		}
+
+	}
+
+	private static class UnicodeMetricsUTF16WithBOM extends UnicodeMetricsUTF16
+	{
+
+		public UnicodeMetricsUTF16WithBOM()
+		{
+			super(BYTE_ORDER_BIG_ENDIAN);
+		}
+
+		public int findEnd(byte bytes[], int index, boolean includeTerminator)
+				throws UnicodeException
+		{
+			// http://en.wikipedia.org/wiki/UTF-16/UCS-2
+
+			if (index >= bytes.length - 1)
+				throw new UnicodeException("Missing BOM.");
+
+			int c1 = 0xff & bytes[index++];
+			int c2 = 0xff & bytes[index++];
+			if (c1 == 0xFF && c2 == 0xFE)
+				byteOrder = BYTE_ORDER_LITTLE_ENDIAN;
+			else if (c1 == 0xFE && c2 == 0xFF)
+				byteOrder = BYTE_ORDER_BIG_ENDIAN;
+			else
+				throw new UnicodeException("Invalid byte order mark.");
+
+			return super.findEnd(bytes, index, includeTerminator);
+		}
+	}
+
+}

Propchange: incubator/sanselan/trunk/src/main/java/org/apache/sanselan/util/UnicodeUtils.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/sanselan/trunk/src/test/java/org/apache/sanselan/formats/png/PngTextTest.java
URL: http://svn.apache.org/viewvc/incubator/sanselan/trunk/src/test/java/org/apache/sanselan/formats/png/PngTextTest.java?rev=682538&view=auto
==============================================================================
--- incubator/sanselan/trunk/src/test/java/org/apache/sanselan/formats/png/PngTextTest.java (added)
+++ incubator/sanselan/trunk/src/test/java/org/apache/sanselan/formats/png/PngTextTest.java Mon Aug  4 16:23:45 2008
@@ -0,0 +1,96 @@
+/*
+ * 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 org.apache.sanselan.formats.png;
+
+import java.awt.Color;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.sanselan.ImageFormat;
+import org.apache.sanselan.ImageReadException;
+import org.apache.sanselan.ImageWriteException;
+import org.apache.sanselan.Sanselan;
+import org.apache.sanselan.util.IOUtils;
+
+public class PngTextTest extends PngBaseTest
+{
+
+	public void test() throws IOException, ImageReadException,
+			ImageWriteException
+	{
+		int width = 1;
+		int height = 1;
+		BufferedImage srcImage = new BufferedImage(width, height,
+				BufferedImage.TYPE_INT_ARGB);
+		srcImage.setRGB(0, 0, Color.red.getRGB());
+
+		Map writeParams = new HashMap();
+
+		List writeTexts = new ArrayList();
+		{
+			String keyword = "a";
+			String text = "b";
+			writeTexts.add(new PngText.tEXt(keyword, text));
+		}
+		{
+			String keyword = "c";
+			String text = "d";
+			writeTexts.add(new PngText.zTXt(keyword, text));
+		}
+		{
+			String keyword = "e";
+			String text = "f";
+			String languageTag = "g";
+			String translatedKeyword = "h";
+			writeTexts.add(new PngText.iTXt(keyword, text, languageTag,
+					translatedKeyword));
+		}
+
+		writeParams.put(PngConstants.PARAM_KEY_PNG_TEXT_CHUNKS, writeTexts);
+
+		byte bytes[] = Sanselan.writeImageToBytes(srcImage,
+				ImageFormat.IMAGE_FORMAT_PNG, writeParams);
+
+		File tempFile = createTempFile("temp", ".png");
+		IOUtils.writeToFile(bytes, tempFile);
+
+		PngImageInfo imageInfo = (PngImageInfo) Sanselan.getImageInfo(bytes);
+		assertTrue(null != imageInfo);
+
+		List readTexts = imageInfo.getTextChunks();
+		assertEquals(readTexts.size(), 3);
+		for (int i = 0; i < readTexts.size(); i++)
+		{
+			PngText text = (PngText) readTexts.get(i);
+			if (text.keyword.equals("a"))
+				assertEquals(text.text, "b");
+			else if (text.keyword.equals("c"))
+				assertEquals(text.text, "d");
+			else if (text.keyword.equals("e"))
+				assertEquals(text.text, "f");
+			else
+				fail("unknown text chunk.");
+		}
+	}
+
+}

Propchange: incubator/sanselan/trunk/src/test/java/org/apache/sanselan/formats/png/PngTextTest.java
------------------------------------------------------------------------------
    svn:eol-style = native