JDK-6541476 : PNG imageio plugin incorrectly handles iTXt chunk
  • Type: Bug
  • Component: client-libs
  • Sub-Component: javax.imageio
  • Affected Version: 5.0
  • Priority: P4
  • Status: Resolved
  • Resolution: Fixed
  • OS: solaris_2.5.1
  • CPU: x86
  • Submitted: 2007-04-02
  • Updated: 2011-01-19
  • Resolved: 2009-01-09
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 6 JDK 7
6u12Fixed 7 b43Fixed
Description
FULL PRODUCT VERSION :
java version "1.6.0",
java version "1.5.0_04",
java version "1.4.2_05"

ADDITIONAL OS VERSION INFORMATION :
Linux 2.6.19-1.2288.2.4.fc5,
Microsoft Windows 2000 [Version 5.00.2195]

A DESCRIPTION OF THE PROBLEM :
com.sun.imageio.plugins.pngPNGMetadata#mergeNativeTree incorrectly stores iTXt_compressionFlag values.   At line 1406 (iTXt_compressionFlag.add(new Boolean(compressionFlag));), values are added to the iTXt_compressionFlag ArrayList as Boolean objects.  If they are later retieved in a call to getNativeTree the objects are cast to Integer, which creates an exception.  Other references to iTXt_compressionFlag use Integer, so line 1406 should store the value as an integer.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
a new test.png file, with an iTXt cluster containing the text "<xml></xml>"
ACTUAL -
java.lang.ClassCastException, see below

ERROR MESSAGES/STACK TRACES THAT OCCUR :
java.lang.ClassCastException: java.lang.Boolean cannot be cast to java.lang.Integer
	at com.sun.imageio.plugins.png.PNGImageWriter.write_iTXt(PNGImageWriter.java:680)
	at com.sun.imageio.plugins.png.PNGImageWriter.write(PNGImageWriter.java:1120)
	at javax.imageio.ImageWriter.write(ImageWriter.java:580)
	at org.mbari.aosn.moqua.image.Test1.main(Test1.java:56)

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
package org.mbari.aosn.moqua.image;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;

import javax.imageio.IIOImage;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.ImageWriter;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.metadata.IIOMetadataFormat;
import javax.imageio.metadata.IIOMetadataNode;
import javax.imageio.stream.FileImageOutputStream;
import javax.imageio.stream.ImageOutputStream;

import org.w3c.dom.Node;

import com.sun.imageio.plugins.png.PNGImageWriter;
import com.sun.imageio.plugins.png.PNGImageWriterSpi;

public class Test1 {

	static public void main(String args[]) {
		try {
			File file = new File("test.png");
			BufferedImage image = new BufferedImage(128, 128,
					BufferedImage.TYPE_4BYTE_ABGR_PRE);
			Graphics2D graph = image.createGraphics();
			graph.setPaintMode();
			graph.setColor(Color.orange);
			graph.fillRect(32, 32, 64, 64);
			graph.dispose();
			ImageOutputStream imageOutputStream = new FileImageOutputStream(
					file);
			ImageTypeSpecifier imageTypeSpecifier = new ImageTypeSpecifier(
					image);
			ImageWriter imageWriter = new PNGImageWriter(
					new PNGImageWriterSpi());
			imageWriter.setOutput(imageOutputStream);
			IIOMetadata metadata = imageWriter.getDefaultImageMetadata(
					imageTypeSpecifier, null);
			String formatNames[] = metadata.getMetadataFormatNames();
			for (String formatName : formatNames) {
				IIOMetadataFormat format = metadata
						.getMetadataFormat(formatName);
				Node node = metadata.getAsTree(formatName);
				if (formatName.contains("png")) {
					IIOMetadataNode iTXt = new IIOMetadataNode("iTXt");
					IIOMetadataNode iTXtEntry = new IIOMetadataNode("iTXtEntry");
					iTXtEntry.setAttribute("keyword", "XML:com.adobe.xmp");
					iTXtEntry.setAttribute("compressionFlag", "false");
					iTXtEntry.setAttribute("compressionMethod", "0");
					iTXtEntry.setAttribute("languageTag", "en");
					iTXtEntry.setAttribute("translatedKeyword",
							"XML:com.adobe.xmp");
					iTXtEntry.setAttribute("text", "<xml></xml>");
					iTXt.appendChild(iTXtEntry);
					node.appendChild(iTXt);
					metadata.setFromTree(formatName, node);
					break;
				}
			}
			imageWriter.write(new IIOImage(image, null, metadata));
			imageOutputStream.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
before calling mageWriter.write(new IIOImage(image, null, metadata));
insert the following lines (which resolves this problem and a similar problem involving iTXt_compressionMethod):

			if(metadata instanceof PNGMetadata){
				List iTXt_compressionFlag = ((PNGMetadata)metadata).iTXt_compressionFlag;
				for(int i = 0; i < iTXt_compressionFlag.size(); ++i){
					Object flag = iTXt_compressionFlag.get(i);
					if(flag instanceof Boolean){
						iTXt_compressionFlag.set(i,Integer.valueOf(((Boolean)flag).booleanValue()?1:0));
					}
				}
				List iTXt_compressionMethod = ((PNGMetadata)metadata).iTXt_compressionMethod;
				for(int i = 0; i < iTXt_compressionMethod.size(); ++i){
					Object method = iTXt_compressionMethod.get(i);
					if(method instanceof String){
						try{
							iTXt_compressionMethod.set(i,Integer.valueOf((String)method));
						} catch (NumberFormatException NFE) {
							iTXt_compressionMethod.set(i,Integer.valueOf(0));
						}
					}
				}
			}

Comments
SUGGESTED FIX http://sa.sfbay.sun.com/projects/java2d_data/7/6541476.0
2008-11-01

EVALUATION The CR 6541476 is about problem with a writing of iTXt chunk but we read it incorrectly too if this chunk contains non-ascii data (namely, translatedKeyword and text attributes may contain UTF8 data). The main reason of reading failures is the DataInputStream.readUTF() function usage. This function interprets first two bytes in the input stream as a string data length and seems to be designed for de-serialization purposes only. The idea of fix is to avoid usage of this function and pay attention to expected data encoding on writing and reading stage. Another problem is that PNG metadata class uses collections (ArraList, for example) without specifying the type of stored data. It may lead to problem like described in this CR. To avoid this, we can specify data type for each collection.
2008-10-31