mirror of
https://github.com/openjdk/jdk.git
synced 2025-09-23 12:34:32 +02:00
6782079: PNG: reading metadata may cause OOM on truncated images
Reviewed-by: igor, prr
This commit is contained in:
parent
bf4d190698
commit
57a1271b06
4 changed files with 281 additions and 57 deletions
|
@ -37,6 +37,7 @@ import java.awt.image.WritableRaster;
|
||||||
import java.io.BufferedInputStream;
|
import java.io.BufferedInputStream;
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.DataInputStream;
|
import java.io.DataInputStream;
|
||||||
|
import java.io.EOFException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.SequenceInputStream;
|
import java.io.SequenceInputStream;
|
||||||
|
@ -59,7 +60,7 @@ import com.sun.imageio.plugins.common.SubImageInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import sun.awt.image.ByteInterleavedRaster;
|
import sun.awt.image.ByteInterleavedRaster;
|
||||||
|
|
||||||
class PNGImageDataEnumeration implements Enumeration {
|
class PNGImageDataEnumeration implements Enumeration<InputStream> {
|
||||||
|
|
||||||
boolean firstTime = true;
|
boolean firstTime = true;
|
||||||
ImageInputStream stream;
|
ImageInputStream stream;
|
||||||
|
@ -72,7 +73,7 @@ class PNGImageDataEnumeration implements Enumeration {
|
||||||
int type = stream.readInt(); // skip chunk type
|
int type = stream.readInt(); // skip chunk type
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object nextElement() {
|
public InputStream nextElement() {
|
||||||
try {
|
try {
|
||||||
firstTime = false;
|
firstTime = false;
|
||||||
ImageInputStream iis = new SubImageInputStream(stream, length);
|
ImageInputStream iis = new SubImageInputStream(stream, length);
|
||||||
|
@ -207,25 +208,17 @@ public class PNGImageReader extends ImageReader {
|
||||||
resetStreamSettings();
|
resetStreamSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
private String readNullTerminatedString(String charset) throws IOException {
|
private String readNullTerminatedString(String charset, int maxLen) throws IOException {
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
int b;
|
int b;
|
||||||
while ((b = stream.read()) != 0) {
|
int count = 0;
|
||||||
|
while ((maxLen > count++) && ((b = stream.read()) != 0)) {
|
||||||
|
if (b == -1) throw new EOFException();
|
||||||
baos.write(b);
|
baos.write(b);
|
||||||
}
|
}
|
||||||
return new String(baos.toByteArray(), charset);
|
return new String(baos.toByteArray(), charset);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String readNullTerminatedString() throws IOException {
|
|
||||||
StringBuilder b = new StringBuilder();
|
|
||||||
int c;
|
|
||||||
|
|
||||||
while ((c = stream.read()) != 0) {
|
|
||||||
b.append((char)c);
|
|
||||||
}
|
|
||||||
return b.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void readHeader() throws IIOException {
|
private void readHeader() throws IIOException {
|
||||||
if (gotHeader) {
|
if (gotHeader) {
|
||||||
return;
|
return;
|
||||||
|
@ -434,7 +427,7 @@ public class PNGImageReader extends ImageReader {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void parse_iCCP_chunk(int chunkLength) throws IOException {
|
private void parse_iCCP_chunk(int chunkLength) throws IOException {
|
||||||
String keyword = readNullTerminatedString();
|
String keyword = readNullTerminatedString("ISO-8859-1", 80);
|
||||||
metadata.iCCP_profileName = keyword;
|
metadata.iCCP_profileName = keyword;
|
||||||
|
|
||||||
metadata.iCCP_compressionMethod = stream.readUnsignedByte();
|
metadata.iCCP_compressionMethod = stream.readUnsignedByte();
|
||||||
|
@ -450,7 +443,7 @@ public class PNGImageReader extends ImageReader {
|
||||||
private void parse_iTXt_chunk(int chunkLength) throws IOException {
|
private void parse_iTXt_chunk(int chunkLength) throws IOException {
|
||||||
long chunkStart = stream.getStreamPosition();
|
long chunkStart = stream.getStreamPosition();
|
||||||
|
|
||||||
String keyword = readNullTerminatedString();
|
String keyword = readNullTerminatedString("ISO-8859-1", 80);
|
||||||
metadata.iTXt_keyword.add(keyword);
|
metadata.iTXt_keyword.add(keyword);
|
||||||
|
|
||||||
int compressionFlag = stream.readUnsignedByte();
|
int compressionFlag = stream.readUnsignedByte();
|
||||||
|
@ -459,15 +452,17 @@ public class PNGImageReader extends ImageReader {
|
||||||
int compressionMethod = stream.readUnsignedByte();
|
int compressionMethod = stream.readUnsignedByte();
|
||||||
metadata.iTXt_compressionMethod.add(Integer.valueOf(compressionMethod));
|
metadata.iTXt_compressionMethod.add(Integer.valueOf(compressionMethod));
|
||||||
|
|
||||||
String languageTag = readNullTerminatedString("UTF8");
|
String languageTag = readNullTerminatedString("UTF8", 80);
|
||||||
metadata.iTXt_languageTag.add(languageTag);
|
metadata.iTXt_languageTag.add(languageTag);
|
||||||
|
|
||||||
|
long pos = stream.getStreamPosition();
|
||||||
|
int maxLen = (int)(chunkStart + chunkLength - pos);
|
||||||
String translatedKeyword =
|
String translatedKeyword =
|
||||||
readNullTerminatedString("UTF8");
|
readNullTerminatedString("UTF8", maxLen);
|
||||||
metadata.iTXt_translatedKeyword.add(translatedKeyword);
|
metadata.iTXt_translatedKeyword.add(translatedKeyword);
|
||||||
|
|
||||||
String text;
|
String text;
|
||||||
long pos = stream.getStreamPosition();
|
pos = stream.getStreamPosition();
|
||||||
byte[] b = new byte[(int)(chunkStart + chunkLength - pos)];
|
byte[] b = new byte[(int)(chunkStart + chunkLength - pos)];
|
||||||
stream.readFully(b);
|
stream.readFully(b);
|
||||||
|
|
||||||
|
@ -511,7 +506,7 @@ public class PNGImageReader extends ImageReader {
|
||||||
|
|
||||||
private void parse_sPLT_chunk(int chunkLength)
|
private void parse_sPLT_chunk(int chunkLength)
|
||||||
throws IOException, IIOException {
|
throws IOException, IIOException {
|
||||||
metadata.sPLT_paletteName = readNullTerminatedString();
|
metadata.sPLT_paletteName = readNullTerminatedString("ISO-8859-1", 80);
|
||||||
chunkLength -= metadata.sPLT_paletteName.length() + 1;
|
chunkLength -= metadata.sPLT_paletteName.length() + 1;
|
||||||
|
|
||||||
int sampleDepth = stream.readUnsignedByte();
|
int sampleDepth = stream.readUnsignedByte();
|
||||||
|
@ -554,12 +549,12 @@ public class PNGImageReader extends ImageReader {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void parse_tEXt_chunk(int chunkLength) throws IOException {
|
private void parse_tEXt_chunk(int chunkLength) throws IOException {
|
||||||
String keyword = readNullTerminatedString();
|
String keyword = readNullTerminatedString("ISO-8859-1", 80);
|
||||||
metadata.tEXt_keyword.add(keyword);
|
metadata.tEXt_keyword.add(keyword);
|
||||||
|
|
||||||
byte[] b = new byte[chunkLength - keyword.length() - 1];
|
byte[] b = new byte[chunkLength - keyword.length() - 1];
|
||||||
stream.readFully(b);
|
stream.readFully(b);
|
||||||
metadata.tEXt_text.add(new String(b));
|
metadata.tEXt_text.add(new String(b, "ISO-8859-1"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void parse_tIME_chunk() throws IOException {
|
private void parse_tIME_chunk() throws IOException {
|
||||||
|
@ -640,7 +635,7 @@ public class PNGImageReader extends ImageReader {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void parse_zTXt_chunk(int chunkLength) throws IOException {
|
private void parse_zTXt_chunk(int chunkLength) throws IOException {
|
||||||
String keyword = readNullTerminatedString();
|
String keyword = readNullTerminatedString("ISO-8859-1", 80);
|
||||||
metadata.zTXt_keyword.add(keyword);
|
metadata.zTXt_keyword.add(keyword);
|
||||||
|
|
||||||
int method = stream.readUnsignedByte();
|
int method = stream.readUnsignedByte();
|
||||||
|
@ -648,7 +643,7 @@ public class PNGImageReader extends ImageReader {
|
||||||
|
|
||||||
byte[] b = new byte[chunkLength - keyword.length() - 2];
|
byte[] b = new byte[chunkLength - keyword.length() - 2];
|
||||||
stream.readFully(b);
|
stream.readFully(b);
|
||||||
metadata.zTXt_text.add(new String(inflate(b)));
|
metadata.zTXt_text.add(new String(inflate(b), "ISO-8859-1"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void readMetadata() throws IIOException {
|
private void readMetadata() throws IIOException {
|
||||||
|
@ -1263,7 +1258,7 @@ public class PNGImageReader extends ImageReader {
|
||||||
try {
|
try {
|
||||||
stream.seek(imageStartPosition);
|
stream.seek(imageStartPosition);
|
||||||
|
|
||||||
Enumeration e = new PNGImageDataEnumeration(stream);
|
Enumeration<InputStream> e = new PNGImageDataEnumeration(stream);
|
||||||
InputStream is = new SequenceInputStream(e);
|
InputStream is = new SequenceInputStream(e);
|
||||||
|
|
||||||
/* InflaterInputStream uses an Inflater instance which consumes
|
/* InflaterInputStream uses an Inflater instance which consumes
|
||||||
|
|
|
@ -674,13 +674,8 @@ public class PNGImageWriter extends ImageWriter {
|
||||||
private byte[] deflate(byte[] b) throws IOException {
|
private byte[] deflate(byte[] b) throws IOException {
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
DeflaterOutputStream dos = new DeflaterOutputStream(baos);
|
DeflaterOutputStream dos = new DeflaterOutputStream(baos);
|
||||||
|
dos.write(b);
|
||||||
int len = b.length;
|
|
||||||
for (int i = 0; i < len; i++) {
|
|
||||||
dos.write((int)(0xff & b[i]));
|
|
||||||
}
|
|
||||||
dos.close();
|
dos.close();
|
||||||
|
|
||||||
return baos.toByteArray();
|
return baos.toByteArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -736,7 +731,7 @@ public class PNGImageWriter extends ImageWriter {
|
||||||
cs.writeByte(compressionMethod);
|
cs.writeByte(compressionMethod);
|
||||||
|
|
||||||
String text = (String)textIter.next();
|
String text = (String)textIter.next();
|
||||||
cs.write(deflate(text.getBytes()));
|
cs.write(deflate(text.getBytes("ISO-8859-1")));
|
||||||
cs.finish();
|
cs.finish();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -211,8 +211,8 @@ public class PNGMetadata extends IIOMetadata implements Cloneable {
|
||||||
public int sRGB_renderingIntent;
|
public int sRGB_renderingIntent;
|
||||||
|
|
||||||
// tEXt chunk
|
// tEXt chunk
|
||||||
public ArrayList tEXt_keyword = new ArrayList(); // 1-79 char Strings
|
public ArrayList<String> tEXt_keyword = new ArrayList<String>(); // 1-79 characters
|
||||||
public ArrayList tEXt_text = new ArrayList(); // Strings
|
public ArrayList<String> tEXt_text = new ArrayList<String>();
|
||||||
|
|
||||||
// tIME chunk
|
// tIME chunk
|
||||||
public boolean tIME_present;
|
public boolean tIME_present;
|
||||||
|
@ -235,13 +235,13 @@ public class PNGMetadata extends IIOMetadata implements Cloneable {
|
||||||
public int tRNS_blue;
|
public int tRNS_blue;
|
||||||
|
|
||||||
// zTXt chunk
|
// zTXt chunk
|
||||||
public ArrayList zTXt_keyword = new ArrayList(); // Strings
|
public ArrayList<String> zTXt_keyword = new ArrayList<String>();
|
||||||
public ArrayList zTXt_compressionMethod = new ArrayList(); // Integers
|
public ArrayList<Integer> zTXt_compressionMethod = new ArrayList<Integer>();
|
||||||
public ArrayList zTXt_text = new ArrayList(); // Strings
|
public ArrayList<String> zTXt_text = new ArrayList<String>();
|
||||||
|
|
||||||
// Unknown chunks
|
// Unknown chunks
|
||||||
public ArrayList unknownChunkType = new ArrayList(); // Strings
|
public ArrayList<String> unknownChunkType = new ArrayList<String>();
|
||||||
public ArrayList unknownChunkData = new ArrayList(); // byte arrays
|
public ArrayList<byte[]> unknownChunkData = new ArrayList<byte[]>();
|
||||||
|
|
||||||
public PNGMetadata() {
|
public PNGMetadata() {
|
||||||
super(true,
|
super(true,
|
||||||
|
@ -426,21 +426,14 @@ public class PNGMetadata extends IIOMetadata implements Cloneable {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ArrayList cloneBytesArrayList(ArrayList in) {
|
private ArrayList<byte[]> cloneBytesArrayList(ArrayList<byte[]> in) {
|
||||||
if (in == null) {
|
if (in == null) {
|
||||||
return null;
|
return null;
|
||||||
} else {
|
} else {
|
||||||
ArrayList list = new ArrayList(in.size());
|
ArrayList<byte[]> list = new ArrayList<byte[]>(in.size());
|
||||||
Iterator iter = in.iterator();
|
for (byte[] b: in) {
|
||||||
while (iter.hasNext()) {
|
list.add((b == null) ? null : (byte[])b.clone());
|
||||||
Object o = iter.next();
|
|
||||||
if (o == null) {
|
|
||||||
list.add(null);
|
|
||||||
} else {
|
|
||||||
list.add(((byte[])o).clone());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2040,14 +2033,14 @@ public class PNGMetadata extends IIOMetadata implements Cloneable {
|
||||||
sBIT_present = false;
|
sBIT_present = false;
|
||||||
sPLT_present = false;
|
sPLT_present = false;
|
||||||
sRGB_present = false;
|
sRGB_present = false;
|
||||||
tEXt_keyword = new ArrayList();
|
tEXt_keyword = new ArrayList<String>();
|
||||||
tEXt_text = new ArrayList();
|
tEXt_text = new ArrayList<String>();
|
||||||
tIME_present = false;
|
tIME_present = false;
|
||||||
tRNS_present = false;
|
tRNS_present = false;
|
||||||
zTXt_keyword = new ArrayList();
|
zTXt_keyword = new ArrayList<String>();
|
||||||
zTXt_compressionMethod = new ArrayList();
|
zTXt_compressionMethod = new ArrayList<Integer>();
|
||||||
zTXt_text = new ArrayList();
|
zTXt_text = new ArrayList<String>();
|
||||||
unknownChunkType = new ArrayList();
|
unknownChunkType = new ArrayList<String>();
|
||||||
unknownChunkData = new ArrayList();
|
unknownChunkData = new ArrayList<byte[]>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
241
jdk/test/javax/imageio/plugins/png/ItxtUtf8Test.java
Normal file
241
jdk/test/javax/imageio/plugins/png/ItxtUtf8Test.java
Normal file
|
@ -0,0 +1,241 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||||
|
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||||
|
* have any questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
* @bug 6541476 6782079
|
||||||
|
* @summary Write and read a PNG file including an non-latin1 iTXt chunk
|
||||||
|
* Test also verifies that trunkated png images does not cause
|
||||||
|
* an OoutOfMemory error.
|
||||||
|
*
|
||||||
|
* @run main ItxtUtf8Test
|
||||||
|
*
|
||||||
|
* @run main/othervm/timeout=10 -Xmx2m ItxtUtf8Test truncate
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import javax.imageio.IIOException;
|
||||||
|
import javax.imageio.IIOImage;
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
import javax.imageio.ImageReader;
|
||||||
|
import javax.imageio.ImageTypeSpecifier;
|
||||||
|
import javax.imageio.ImageWriter;
|
||||||
|
import javax.imageio.metadata.IIOMetadata;
|
||||||
|
import javax.imageio.stream.ImageInputStream;
|
||||||
|
import javax.imageio.stream.ImageOutputStream;
|
||||||
|
import javax.imageio.stream.MemoryCacheImageInputStream;
|
||||||
|
import javax.imageio.stream.MemoryCacheImageOutputStream;
|
||||||
|
import org.w3c.dom.DOMImplementation;
|
||||||
|
import org.w3c.dom.Document;
|
||||||
|
import org.w3c.dom.Element;
|
||||||
|
import org.w3c.dom.Node;
|
||||||
|
import org.w3c.dom.bootstrap.DOMImplementationRegistry;
|
||||||
|
|
||||||
|
public class ItxtUtf8Test {
|
||||||
|
|
||||||
|
public static final String
|
||||||
|
TEXT = "\u24c9\u24d4\u24e7\u24e3" +
|
||||||
|
"\ud835\udc13\ud835\udc1e\ud835\udc31\ud835\udc2d" +
|
||||||
|
"\u24c9\u24d4\u24e7\u24e3", // a repetition for compression
|
||||||
|
VERBATIM = "\u24e5\u24d4\u24e1\u24d1\u24d0\u24e3\u24d8\u24dc",
|
||||||
|
COMPRESSED = "\u24d2\u24de\u24dc\u24df\u24e1\u24d4\u24e2\u24e2\u24d4\u24d3";
|
||||||
|
|
||||||
|
public static final byte[]
|
||||||
|
VBYTES = {
|
||||||
|
(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x56, // chunk length
|
||||||
|
(byte)0x69, (byte)0x54, (byte)0x58, (byte)0x74, // chunk type "iTXt"
|
||||||
|
(byte)0x76, (byte)0x65, (byte)0x72, (byte)0x62,
|
||||||
|
(byte)0x61, (byte)0x74, (byte)0x69, (byte)0x6d, // keyword "verbatim"
|
||||||
|
(byte)0x00, // separator terminating keyword
|
||||||
|
(byte)0x00, // compression flag
|
||||||
|
(byte)0x00, // compression method, must be zero
|
||||||
|
(byte)0x78, (byte)0x2d, (byte)0x63, (byte)0x69,
|
||||||
|
(byte)0x72, (byte)0x63, (byte)0x6c, (byte)0x65,
|
||||||
|
(byte)0x64, // language tag "x-circled"
|
||||||
|
(byte)0x00, // separator terminating language tag
|
||||||
|
(byte)0xe2, (byte)0x93, (byte)0xa5, // '\u24e5'
|
||||||
|
(byte)0xe2, (byte)0x93, (byte)0x94, // '\u24d4'
|
||||||
|
(byte)0xe2, (byte)0x93, (byte)0xa1, // '\u24e1'
|
||||||
|
(byte)0xe2, (byte)0x93, (byte)0x91, // '\u24d1'
|
||||||
|
(byte)0xe2, (byte)0x93, (byte)0x90, // '\u24d0'
|
||||||
|
(byte)0xe2, (byte)0x93, (byte)0xa3, // '\u24e3'
|
||||||
|
(byte)0xe2, (byte)0x93, (byte)0x98, // '\u24d8'
|
||||||
|
(byte)0xe2, (byte)0x93, (byte)0x9c, // '\u24dc'
|
||||||
|
(byte)0x00, // separator terminating the translated keyword
|
||||||
|
(byte)0xe2, (byte)0x93, (byte)0x89, // '\u24c9'
|
||||||
|
(byte)0xe2, (byte)0x93, (byte)0x94, // '\u24d4'
|
||||||
|
(byte)0xe2, (byte)0x93, (byte)0xa7, // '\u24e7'
|
||||||
|
(byte)0xe2, (byte)0x93, (byte)0xa3, // '\u24e3'
|
||||||
|
(byte)0xf0, (byte)0x9d, (byte)0x90, (byte)0x93, // '\ud835\udc13'
|
||||||
|
(byte)0xf0, (byte)0x9d, (byte)0x90, (byte)0x9e, // '\ud835\udc1e'
|
||||||
|
(byte)0xf0, (byte)0x9d, (byte)0x90, (byte)0xb1, // '\ud835\udc31'
|
||||||
|
(byte)0xf0, (byte)0x9d, (byte)0x90, (byte)0xad, // '\ud835\udc2d'
|
||||||
|
(byte)0xe2, (byte)0x93, (byte)0x89, // '\u24c9'
|
||||||
|
(byte)0xe2, (byte)0x93, (byte)0x94, // '\u24d4'
|
||||||
|
(byte)0xe2, (byte)0x93, (byte)0xa7, // '\u24e7'
|
||||||
|
(byte)0xe2, (byte)0x93, (byte)0xa3, // '\u24e3'
|
||||||
|
(byte)0xb5, (byte)0xcc, (byte)0x97, (byte)0x56 // CRC
|
||||||
|
},
|
||||||
|
CBYTES = {
|
||||||
|
// we don't want to check the chunk length,
|
||||||
|
// as this might depend on implementation.
|
||||||
|
(byte)0x69, (byte)0x54, (byte)0x58, (byte)0x74, // chunk type "iTXt"
|
||||||
|
(byte)0x63, (byte)0x6f, (byte)0x6d, (byte)0x70,
|
||||||
|
(byte)0x72, (byte)0x65, (byte)0x73, (byte)0x73,
|
||||||
|
(byte)0x65, (byte)0x64, // keyword "compressed"
|
||||||
|
(byte)0x00, // separator terminating keyword
|
||||||
|
(byte)0x01, // compression flag
|
||||||
|
(byte)0x00, // compression method, 0=deflate
|
||||||
|
(byte)0x78, (byte)0x2d, (byte)0x63, (byte)0x69,
|
||||||
|
(byte)0x72, (byte)0x63, (byte)0x6c, (byte)0x65,
|
||||||
|
(byte)0x64, // language tag "x-circled"
|
||||||
|
(byte)0x00, // separator terminating language tag
|
||||||
|
// we don't want to check the actual compressed data,
|
||||||
|
// as this might depend on implementation.
|
||||||
|
};
|
||||||
|
/*
|
||||||
|
*/
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
List argList = Arrays.asList(args);
|
||||||
|
if (argList.contains("truncate")) {
|
||||||
|
try {
|
||||||
|
runTest(false, true);
|
||||||
|
throw new AssertionError("Expect an error for truncated file");
|
||||||
|
}
|
||||||
|
catch (IIOException e) {
|
||||||
|
// expected an error for a truncated image file.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
runTest(argList.contains("dump"), false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void runTest(boolean dump, boolean truncate)
|
||||||
|
throws Exception
|
||||||
|
{
|
||||||
|
String format = "javax_imageio_png_1.0";
|
||||||
|
BufferedImage img =
|
||||||
|
new BufferedImage(16, 16, BufferedImage.TYPE_INT_RGB);
|
||||||
|
ImageWriter iw = ImageIO.getImageWritersByMIMEType("image/png").next();
|
||||||
|
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||||
|
ImageOutputStream ios = new MemoryCacheImageOutputStream(os);
|
||||||
|
iw.setOutput(ios);
|
||||||
|
IIOMetadata meta =
|
||||||
|
iw.getDefaultImageMetadata(new ImageTypeSpecifier(img), null);
|
||||||
|
DOMImplementationRegistry registry;
|
||||||
|
registry = DOMImplementationRegistry.newInstance();
|
||||||
|
DOMImplementation impl = registry.getDOMImplementation("XML 3.0");
|
||||||
|
Document doc = impl.createDocument(null, format, null);
|
||||||
|
Element root, itxt, entry;
|
||||||
|
root = doc.getDocumentElement();
|
||||||
|
root.appendChild(itxt = doc.createElement("iTXt"));
|
||||||
|
itxt.appendChild(entry = doc.createElement("iTXtEntry"));
|
||||||
|
entry.setAttribute("keyword", "verbatim");
|
||||||
|
entry.setAttribute("compressionFlag", "false");
|
||||||
|
entry.setAttribute("compressionMethod", "0");
|
||||||
|
entry.setAttribute("languageTag", "x-circled");
|
||||||
|
entry.setAttribute("translatedKeyword", VERBATIM);
|
||||||
|
entry.setAttribute("text", TEXT);
|
||||||
|
itxt.appendChild(entry = doc.createElement("iTXtEntry"));
|
||||||
|
entry.setAttribute("keyword", "compressed");
|
||||||
|
entry.setAttribute("compressionFlag", "true");
|
||||||
|
entry.setAttribute("compressionMethod", "0");
|
||||||
|
entry.setAttribute("languageTag", "x-circled");
|
||||||
|
entry.setAttribute("translatedKeyword", COMPRESSED);
|
||||||
|
entry.setAttribute("text", TEXT);
|
||||||
|
meta.mergeTree(format, root);
|
||||||
|
iw.write(new IIOImage(img, null, meta));
|
||||||
|
iw.dispose();
|
||||||
|
|
||||||
|
byte[] bytes = os.toByteArray();
|
||||||
|
if (dump)
|
||||||
|
System.out.write(bytes);
|
||||||
|
if (findBytes(VBYTES, bytes) < 0)
|
||||||
|
throw new AssertionError("verbatim block not found");
|
||||||
|
if (findBytes(CBYTES, bytes) < 0)
|
||||||
|
throw new AssertionError("compressed block not found");
|
||||||
|
int length = bytes.length;
|
||||||
|
if (truncate)
|
||||||
|
length = findBytes(VBYTES, bytes) + 32;
|
||||||
|
|
||||||
|
ImageReader ir = ImageIO.getImageReader(iw);
|
||||||
|
ByteArrayInputStream is = new ByteArrayInputStream(bytes, 0, length);
|
||||||
|
ImageInputStream iis = new MemoryCacheImageInputStream(is);
|
||||||
|
ir.setInput(iis);
|
||||||
|
meta = ir.getImageMetadata(0);
|
||||||
|
Node node = meta.getAsTree(format);
|
||||||
|
for (node = node.getFirstChild();
|
||||||
|
!"iTXt".equals(node.getNodeName());
|
||||||
|
node = node.getNextSibling());
|
||||||
|
boolean verbatimSeen = false, compressedSeen = false;
|
||||||
|
for (node = node.getFirstChild();
|
||||||
|
node != null;
|
||||||
|
node = node.getNextSibling()) {
|
||||||
|
entry = (Element)node;
|
||||||
|
String keyword = entry.getAttribute("keyword");
|
||||||
|
String translatedKeyword = entry.getAttribute("translatedKeyword");
|
||||||
|
String text = entry.getAttribute("text");
|
||||||
|
if ("verbatim".equals(keyword)) {
|
||||||
|
if (verbatimSeen) throw new AssertionError("Duplicate");
|
||||||
|
verbatimSeen = true;
|
||||||
|
if (!VERBATIM.equals(translatedKeyword))
|
||||||
|
throw new AssertionError("Wrong translated keyword");
|
||||||
|
if (!TEXT.equals(text))
|
||||||
|
throw new AssertionError("Wrong text");
|
||||||
|
}
|
||||||
|
else if ("compressed".equals(keyword)) {
|
||||||
|
if (compressedSeen) throw new AssertionError("Duplicate");
|
||||||
|
compressedSeen = true;
|
||||||
|
if (!COMPRESSED.equals(translatedKeyword))
|
||||||
|
throw new AssertionError("Wrong translated keyword");
|
||||||
|
if (!TEXT.equals(text))
|
||||||
|
throw new AssertionError("Wrong text");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new AssertionError("Unexpected keyword");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!(verbatimSeen && compressedSeen))
|
||||||
|
throw new AssertionError("Missing chunk");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final int findBytes(byte[] needle, byte[] haystack) {
|
||||||
|
HAYSTACK: for (int h = 0; h <= haystack.length - needle.length; ++h) {
|
||||||
|
for (int n = 0; n < needle.length; ++n) {
|
||||||
|
if (needle[n] != haystack[h + n]) {
|
||||||
|
continue HAYSTACK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue