changeset 2025:cb6c55ac1cd6

Merge
author Andrew John Hughes <ahughes@redhat.com>
date Tue, 18 Jan 2011 14:25:25 +0000
parents 0ee477f3daf2 (current diff) 3eec8b65af65 (diff)
children 9cfe7a59b531
files ChangeLog NEWS
diffstat 7 files changed, 4928 insertions(+), 42 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Tue Jan 18 14:15:15 2011 +0000
+++ b/ChangeLog	Tue Jan 18 14:25:25 2011 +0000
@@ -1,3 +1,21 @@
+2010-12-24  Deepak Bhole <dbhole@redhat.com>
+
+	Backport S6687968, S6541476, S6782079
+	* Makefile.am: Add missing "\" to the patch list, update to include 
+	new patches.
+	* NEWS: Updated.
+	* patches/openjdk/6687968-pngimagereader_mem_leak.patch: Backport to allow
+	S6541476 fix to apply cleanly.
+	* patches/openjdk/6541476-png-iTXt-chunk.patch: Backport to fix iTXt chunk
+	handling for png images. Also addresses RH665355.
+	* patches/openjdk/6782079-png_metadata_oom.patch: Backport to fix
+	potential OOM error when reading metadata on truncated pngs.
+
+2010-12-23  Jiri Vanek <jvanek@redhat.com>
+
+	* NEWS: Updated with rh.bugzilas.
+	* patches/fonts-rhel.patch:repaired rhel 6 fonts configuration.
+
 2010-12-16  Andrew John Hughes  <ahughes@redhat.com>
 
 	* NEWS: Fix formatting.
--- a/Makefile.am	Tue Jan 18 14:15:15 2011 +0000
+++ b/Makefile.am	Tue Jan 18 14:25:25 2011 +0000
@@ -380,7 +380,10 @@
 	patches/openjdk/6438179-systray_check.patch \
 	patches/openjdk/4356282-opentype.patch \
 	patches/openjdk/6954424-opentype_javadoc.patch \
-	patches/openjdk/6795356-proxylazyvalue-leak.patch
+	patches/openjdk/6795356-proxylazyvalue-leak.patch \
+	patches/openjdk/6687968-pngimagereader_mem_leak.patch \
+	patches/openjdk/6541476-png-iTXt-chunk.patch \
+	patches/openjdk/6782079-png_metadata_oom.patch
 
 if WITH_ALT_HSBUILD
 ICEDTEA_PATCHES += patches/hotspot/$(HSBUILD)/openjdk-6886353-ignore_deoptimizealot.patch \
--- a/NEWS	Tue Jan 18 14:15:15 2011 +0000
+++ b/NEWS	Tue Jan 18 14:25:25 2011 +0000
@@ -21,8 +21,12 @@
   - S6967433: dashed lines broken when using scaling transforms.
   - S6976265: No STROKE_CONTROL
   - S6967434, PR450, RH530642: Round joins/caps of scaled up lines have poor quality.
+  - S6687968: PNGImageReader leaks native memory through an Inflater
+  - S6541476, RH665355: PNG imageio plugin incorrectly handles iTXt chunk
+  - S6782079: PNG: reading metadata may cause OOM on truncated images
 * Fixes:
   - S7003777, RH647674: JTextPane produces incorrect content after parsing the html text
+  - RH647157,RH582455: Update fontconfig files for rhel 6
 
 New in release 1.7.6 (2010-11-24):
 
--- a/patches/fonts-rhel.patch	Tue Jan 18 14:15:15 2011 +0000
+++ b/patches/fonts-rhel.patch	Tue Jan 18 14:25:25 2011 +0000
@@ -11,10 +11,9 @@
  else
  
  FONTCONFIGS_SRC	= $(CLOSED_SRC)/solaris/classes/sun/awt/fontconfigs
-diff -Nru openjdk.orig/jdk/src/solaris/classes/sun/awt/fontconfigs/linux.fontconfig.RedHat.6.0.properties openjdk/jdk/src/solaris/classes/sun/awt/fontconfigs/linux.fontconfig.RedHat.6.0.properties
---- openjdk.orig/jdk/src/solaris/classes/sun/awt/fontconfigs/linux.fontconfig.RedHat.6.0.properties	1970-01-01 01:00:00.000000000 +0100
-+++ openjdk/jdk/src/solaris/classes/sun/awt/fontconfigs/linux.fontconfig.RedHat.6.0.properties	2010-08-12 22:52:55.716852554 +0100
-@@ -0,0 +1,376 @@
+--- /dev/null	2010-12-20 09:26:08.850062021 +0100
++++ openjdk/jdk/src/solaris/classes/sun/awt/fontconfigs/linux.fontconfig.RedHat.6.0.properties	2010-12-22 11:21:32.606781127 +0100
+@@ -0,0 +1,441 @@
 +# 
 +# Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved.
 +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
@@ -42,16 +41,19 @@
 +
 +# Version
 +
-+# Uses Fedora 9 fonts and file paths.
++# Uses Fedora 14  fonts and file paths, added VL-gothic
 +version=1
 +
 +# Component Font Mappings
 +
 +dialog.plain.latin-1=DejaVu Sans
-+dialog.plain.japanese-x0208=Sazanami Gothic
++#dialog.plain.japanese-x0208=Sazanami Gothic
++dialog.plain.japanese-x0208=VL PGothic
 +dialog.plain.korean=Baekmuk Gulim
 +dialog.plain.chinese-big5=AR PL ShanHeiSun Uni
 +dialog.plain.chinese-gb18030=AR PL ShanHeiSun Uni
++dialog.plain.assamese=Lohit Assamese
++dialog.plain.devanagari=Lohit Devanagari
 +dialog.plain.bengali=Lohit Bengali
 +dialog.plain.gujarati=Lohit Gujarati
 +dialog.plain.hindi=Lohit Hindi
@@ -63,10 +65,13 @@
 +dialog.plain.sinhala=LKLUG
 +
 +dialog.bold.latin-1=DejaVu Sans Bold
-+dialog.bold.japanese-x0208=Sazanami Gothic
++#dialog.bold.japanese-x0208=Sazanami Gothic
++dialog.bold.japanese-x0208=VL PGothic
 +dialog.bold.korean=Baekmuk Gulim
 +dialog.bold.chinese-big5=AR PL ShanHeiSun Uni
 +dialog.bold.chinese-gb18030=AR PL ShanHeiSun Uni
++dialog.bold.assamese=Lohit Assamese
++dialog.bold.devanagari=Lohit Devanagari
 +dialog.bold.bengali=Lohit Bengali
 +dialog.bold.gujarati=Lohit Gujarati
 +dialog.bold.hindi=Lohit Hindi
@@ -78,10 +83,13 @@
 +dialog.bold.sinhala=LKLUG
 +
 +dialog.italic.latin-1=DejaVu Sans Oblique
-+dialog.italic.japanese-x0208=Sazanami Gothic
++#dialog.italic.japanese-x0208=Sazanami Gothic
++dialog.italic.japanese-x0208=VL PGothic
 +dialog.italic.korean=Baekmuk Gulim
 +dialog.italic.chinese-big5=AR PL ShanHeiSun Uni
 +dialog.italic.chinese-gb18030=AR PL ShanHeiSun Uni
++dialog.italic.assamese=Lohit Assamese
++dialog.italic.devanagari=Lohit Devanagari
 +dialog.italic.bengali=Lohit Bengali
 +dialog.italic.gujarati=Lohit Gujarati
 +dialog.italic.hindi=Lohit Hindi
@@ -93,10 +101,13 @@
 +dialog.italic.sinhala=LKLUG
 +
 +dialog.bolditalic.latin-1=DejaVu Sans Bold Oblique
-+dialog.bolditalic.japanese-x0208=Sazanami Gothic
++#dialog.bolditalic.japanese-x0208=Sazanami Gothic
++dialog.bolditalic.japanese-x0208=VL PGothic
 +dialog.bolditalic.korean=Baekmuk Gulim
 +dialog.bolditalic.chinese-big5=AR PL ShanHeiSun Uni
 +dialog.bolditalic.chinese-gb18030=AR PL ShanHeiSun Uni
++dialog.bolditalic.assamese=Lohit Assamese
++dialog.bolditalic.devanagari=Lohit Devanagari
 +dialog.bolditalic.bengali=Lohit Bengali
 +dialog.bolditalic.gujarati=Lohit Gujarati
 +dialog.bolditalic.hindi=Lohit Hindi
@@ -108,10 +119,13 @@
 +dialog.bolditalic.sinhala=LKLUG
 +
 +sansserif.plain.latin-1=DejaVu Sans
-+sansserif.plain.japanese-x0208=Sazanami Gothic
++#sansserif.plain.japanese-x0208=Sazanami Gothic
++sansserif.plain.japanese-x0208=VL PGothic
 +sansserif.plain.korean=Baekmuk Gulim
 +sansserif.plain.chinese-big5=AR PL ShanHeiSun Uni
 +sansserif.plain.chinese-gb18030=AR PL ShanHeiSun Uni
++sansserif.plain.assamese=Lohit Assamese
++sansserif.plain.devanagari=Lohit Devanagari
 +sansserif.plain.bengali=Lohit Bengali
 +sansserif.plain.gujarati=Lohit Gujarati
 +sansserif.plain.hindi=Lohit Hindi
@@ -123,10 +137,13 @@
 +sansserif.plain.sinhala=LKLUG
 +
 +sansserif.bold.latin-1=DejaVu Sans Bold
-+sansserif.bold.japanese-x0208=Sazanami Gothic
++#sansserif.bold.japanese-x0208=Sazanami Gothic
++sansserif.bold.japanese-x0208=VL PGothic
 +sansserif.bold.korean=Baekmuk Gulim
 +sansserif.bold.chinese-big5=AR PL ShanHeiSun Uni
 +sansserif.bold.chinese-gb18030=AR PL ShanHeiSun Uni
++sansserif.bold.assamese=Lohit Assamese
++sansserif.bold.devanagari=Lohit Devanagari
 +sansserif.bold.bengali=Lohit Bengali
 +sansserif.bold.gujarati=Lohit Gujarati
 +sansserif.bold.hindi=Lohit Hindi
@@ -138,10 +155,13 @@
 +sansserif.bold.sinhala=LKLUG
 +
 +sansserif.italic.latin-1=DejaVu Sans Oblique
-+sansserif.italic.japanese-x0208=Sazanami Gothic
++#sansserif.italic.japanese-x0208=Sazanami Gothic
++sansserif.italic.japanese-x0208=VL PGothic
 +sansserif.italic.korean=Baekmuk Gulim
 +sansserif.italic.chinese-big5=AR PL ShanHeiSun Uni
 +sansserif.italic.chinese-gb18030=AR PL ShanHeiSun Uni
++sansserif.italic.assamese=Lohit Assamese
++sansserif.italic.devanagari=Lohit Devanagari
 +sansserif.italic.bengali=Lohit Bengali
 +sansserif.italic.gujarati=Lohit Gujarati
 +sansserif.italic.hindi=Lohit Hindi
@@ -153,10 +173,13 @@
 +sansserif.italic.sinhala=LKLUG
 +
 +sansserif.bolditalic.latin-1=DejaVu Sans Bold Oblique
-+sansserif.bolditalic.japanese-x0208=Sazanami Gothic
++#sansserif.bolditalic.japanese-x0208=Sazanami Gothic
++sansserif.bolditalic.japanese-x0208=VL PGothic
 +sansserif.bolditalic.korean=Baekmuk Gulim
 +sansserif.bolditalic.chinese-big5=AR PL ShanHeiSun Uni
 +sansserif.bolditalic.chinese-gb18030=AR PL ShanHeiSun Uni
++sansserif.bolditalic.assamese=Lohit Assamese
++sansserif.bolditalic.devanagari=Lohit Devanagari
 +sansserif.bolditalic.bengali=Lohit Bengali
 +sansserif.bolditalic.gujarati=Lohit Gujarati
 +sansserif.bolditalic.hindi=Lohit Hindi
@@ -168,10 +191,13 @@
 +sansserif.bolditalic.sinhala=LKLUG
 +
 +serif.plain.latin-1=DejaVu Serif
-+serif.plain.japanese-x0208=Sazanami Mincho
++#serif.plain.japanese-x0208=Sazanami Mincho
++serif.plain.japanese-x0208=VL PGothic
 +serif.plain.korean=Baekmuk Batang
 +serif.plain.chinese-big5=AR PL ZenKai Uni
 +serif.plain.chinese-gb18030=AR PL ZenKai Uni
++serif.plain.assamese=Lohit Assamese
++serif.plain.devanagari=Lohit Devanagari
 +serif.plain.bengali=Lohit Bengali
 +serif.plain.gujarati=Lohit Gujarati
 +serif.plain.hindi=Lohit Hindi
@@ -183,10 +209,13 @@
 +serif.plain.sinhala=LKLUG
 +
 +serif.bold.latin-1=DejaVu Serif Bold
-+serif.bold.japanese-x0208=Sazanami Mincho
++#serif.bold.japanese-x0208=Sazanami Mincho
++serif.bold.japanese-x0208=VL PGothic
 +serif.bold.korean=Baekmuk Batang
 +serif.bold.chinese-big5=AR PL ZenKai Uni
 +serif.bold.chinese-gb18030=AR PL ZenKai Uni
++serif.bold.assamese=Lohit Assamese
++serif.bold.devanagari=Lohit Devanagari
 +serif.bold.bengali=Lohit Bengali
 +serif.bold.gujarati=Lohit Gujarati
 +serif.bold.hindi=Lohit Hindi
@@ -198,10 +227,13 @@
 +serif.bold.sinhala=LKLUG
 +
 +serif.italic.latin-1=DejaVu Serif Oblique
-+serif.italic.japanese-x0208=Sazanami Mincho
++#serif.italic.japanese-x0208=Sazanami Mincho
++serif.italic.japanese-x0208=VL PGothic
 +serif.italic.korean=Baekmuk Batang
 +serif.italic.chinese-big5=AR PL ZenKai Uni
 +serif.italic.chinese-gb18030=AR PL ZenKai Uni
++serif.italic.assamese=Lohit Assamese
++serif.italic.devanagari=Lohit Devanagari
 +serif.italic.bengali=Lohit Bengali
 +serif.italic.gujarati=Lohit Gujarati
 +serif.italic.hindi=Lohit Hindi
@@ -213,10 +245,13 @@
 +serif.italic.sinhala=LKLUG
 +
 +serif.bolditalic.latin-1=DejaVu Serif Bold Oblique
-+serif.bolditalic.japanese-x0208=Sazanami Mincho
++#serif.bolditalic.japanese-x0208=Sazanami Mincho
++serif.bolditalic.japanese-x0208=VL PGothic
 +serif.bolditalic.korean=Baekmuk Batang
 +serif.bolditalic.chinese-big5=AR PL ZenKai Uni
 +serif.bolditalic.chinese-gb18030=AR PL ZenKai Uni
++serif.bolditalic.assamese=Lohit Assamese
++serif.bolditalic.devanagari=Lohit Devanagari
 +serif.bolditalic.bengali=Lohit Bengali
 +serif.bolditalic.gujarati=Lohit Gujarati
 +serif.bolditalic.hindi=Lohit Hindi
@@ -228,10 +263,13 @@
 +serif.bolditalic.sinhala=LKLUG
 +
 +monospaced.plain.latin-1=DejaVu Sans Mono
-+monospaced.plain.japanese-x0208=Sazanami Gothic
++#monospaced.plain.japanese-x0208=Sazanami Gothic
++monospaced.plain.japanese-x0208=VL Gothic
 +monospaced.plain.korean=Baekmuk Gulim
 +monospaced.plain.chinese-big5=AR PL ShanHeiSun Uni
 +monospaced.plain.chinese-gb18030=AR PL ShanHeiSun Uni
++monospaced.plain.assamese=Lohit Assamese
++monospaced.plain.devanagari=Lohit Devanagari
 +monospaced.plain.bengali=Lohit Bengali
 +monospaced.plain.gujarati=Lohit Gujarati
 +monospaced.plain.hindi=Lohit Hindi
@@ -243,10 +281,13 @@
 +monospaced.plain.sinhala=LKLUG
 +
 +monospaced.bold.latin-1=DejaVu Sans Mono Bold
-+monospaced.bold.japanese-x0208=Sazanami Gothic
++#monospaced.bold.japanese-x0208=Sazanami Gothic
++monospaced.bold.japanese-x0208=VL Gothic
 +monospaced.bold.korean=Baekmuk Gulim
 +monospaced.bold.chinese-big5=AR PL ShanHeiSun Uni
 +monospaced.bold.chinese-gb18030=AR PL ShanHeiSun Uni
++monospaced.bold.assamese=Lohit Assamese
++monospaced.bold.devanagari=Lohit Devanagari
 +monospaced.bold.bengali=Lohit Bengali
 +monospaced.bold.gujarati=Lohit Gujarati
 +monospaced.bold.hindi=Lohit Hindi
@@ -258,10 +299,13 @@
 +monospaced.bold.sinhala=LKLUG
 +
 +monospaced.italic.latin-1=DejaVu Sans Mono Oblique
-+monospaced.italic.japanese-x0208=Sazanami Gothic
++#monospaced.italic.japanese-x0208=Sazanami Gothic
++monospaced.italic.japanese-x0208=VL Gothic
 +monospaced.italic.korean=Baekmuk Gulim
 +monospaced.italic.chinese-big5=AR PL ShanHeiSun Uni
 +monospaced.italic.chinese-gb18030=AR PL ShanHeiSun Uni
++monospaced.italic.assamese=Lohit Assamese
++monospaced.italic.devanagari=Lohit Devanagari
 +monospaced.italic.bengali=Lohit Bengali
 +monospaced.italic.gujarati=Lohit Gujarati
 +monospaced.italic.hindi=Lohit Hindi
@@ -273,10 +317,13 @@
 +monospaced.italic.sinhala=LKLUG
 +
 +monospaced.bolditalic.latin-1=DejaVu Sans Mono Bold Oblique
-+monospaced.bolditalic.japanese-x0208=Sazanami Gothic
++#monospaced.bolditalic.japanese-x0208=Sazanami Gothic
++monospaced.bolditalic.japanese-x0208=VL Gothic
 +monospaced.bolditalic.korean=Baekmuk Gulim
 +monospaced.bolditalic.chinese-big5=AR PL ShanHeiSun Uni
 +monospaced.bolditalic.chinese-gb18030=AR PL ShanHeiSun Uni
++monospaced.bolditalic.assamese=Lohit Assamese
++monospaced.bolditalic.devanagari=Lohit Devanagari
 +monospaced.bolditalic.bengali=Lohit Bengali
 +monospaced.bolditalic.gujarati=Lohit Gujarati
 +monospaced.bolditalic.hindi=Lohit Hindi
@@ -288,10 +335,13 @@
 +monospaced.bolditalic.sinhala=LKLUG
 +
 +dialoginput.plain.latin-1=DejaVu Sans Mono
-+dialoginput.plain.japanese-x0208=Sazanami Gothic
++#dialoginput.plain.japanese-x0208=Sazanami Gothic
++dialoginput.plain.japanese-x0208=VL PGothic
 +dialoginput.plain.korean=Baekmuk Gulim
 +dialoginput.plain.chinese-big5=AR PL ShanHeiSun Uni
 +dialoginput.plain.chinese-gb18030=AR PL ShanHeiSun Uni
++dialoginput.plain.assamese=Lohit Assamese
++dialoginput.plain.devanagari=Lohit Devanagari
 +dialoginput.plain.bengali=Lohit Bengali
 +dialoginput.plain.gujarati=Lohit Gujarati
 +dialoginput.plain.hindi=Lohit Hindi
@@ -303,10 +353,13 @@
 +dialoginput.plain.sinhala=LKLUG
 +
 +dialoginput.bold.latin-1=DejaVu Sans Mono Bold
-+dialoginput.bold.japanese-x0208=Sazanami Gothic
++#dialoginput.bold.japanese-x0208=Sazanami Gothic
++dialoginput.bold.japanese-x0208=VL PGothic
 +dialoginput.bold.korean=Baekmuk Gulim
 +dialoginput.bold.chinese-big5=AR PL ShanHeiSun Uni
 +dialoginput.bold.chinese-gb18030=AR PL ShanHeiSun Uni
++dialoginput.bold.assamese=Lohit Assamese
++dialoginput.bold.devanagari=Lohit Devanagari
 +dialoginput.bold.bengali=Lohit Bengali
 +dialoginput.bold.gujarati=Lohit Gujarati
 +dialoginput.bold.hindi=Lohit Hindi
@@ -318,10 +371,13 @@
 +dialoginput.bold.sinhala=LKLUG
 +
 +dialoginput.italic.latin-1=DejaVu Sans Mono Oblique
-+dialoginput.italic.japanese-x0208=Sazanami Gothic
++#dialoginput.italic.japanese-x0208=Sazanami Gothic
++dialoginput.italic.japanese-x0208=VL PGothic
 +dialoginput.italic.korean=Baekmuk Gulim
 +dialoginput.italic.chinese-big5=AR PL ShanHeiSun Uni
 +dialoginput.italic.chinese-gb18030=AR PL ShanHeiSun Uni
++dialoginput.italic.assamese=Lohit Assamese
++dialoginput.italic.devanagari=Lohit Devanagari
 +dialoginput.italic.bengali=Lohit Bengali
 +dialoginput.italic.gujarati=Lohit Gujarati
 +dialoginput.italic.hindi=Lohit Hindi
@@ -333,10 +389,13 @@
 +dialoginput.italic.sinhala=LKLUG
 +
 +dialoginput.bolditalic.latin-1=DejaVu Sans Mono Bold Oblique
-+dialoginput.bolditalic.japanese-x0208=Sazanami Gothic
++#dialoginput.bolditalic.japanese-x0208=Sazanami Gothic
++dialoginput.bolditalic.japanese-x0208=VL PGothic
 +dialoginput.bolditalic.korean=Baekmuk Gulim
 +dialoginput.bolditalic.chinese-big5=AR PL ShanHeiSun Uni
 +dialoginput.bolditalic.chinese-gb18030=AR PL ShanHeiSun Uni
++dialoginput.bolditalic.assamese=Lohit Assamese
++dialoginput.bolditalic.devanagari=Lohit Devanagari
 +dialoginput.bolditalic.bengali=Lohit Bengali
 +dialoginput.bolditalic.gujarati=Lohit Gujarati
 +dialoginput.bolditalic.hindi=Lohit Hindi
@@ -370,24 +429,29 @@
 +
 +filename.DejaVu_Serif=/usr/share/fonts/dejavu/DejaVuSerif.ttf
 +filename.DejaVu_Serif_Bold=/usr/share/fonts/dejavu/DejaVuSerif-Bold.ttf
-+filename.DejaVu_Serif_Oblique=/usr/share/fonts/dejavu/DejaVuSerif-Oblique.ttf
++filename.DejaVu_Serif_Oblique=/usr/share/fonts/dejavu/DejaVuSerif-Oblique.ttfok
 +filename.DejaVu_Serif_Bold_Oblique=/usr/share/fonts/dejavu/DejaVuSerif-BoldOblique.ttf
 +
-+filename.Sazanami_Gothic=/usr/share/fonts/sazanami-fonts-gothic/sazanami-gothic.ttf
-+filename.Sazanami_Mincho=/usr/share/fonts/sazanami-fonts-mincho/sazanami-mincho.ttf
-+filename.AR_PL_ShanHeiSun_Uni=/usr/share/fonts/cjkunifonts-uming/uming.ttc
-+filename.AR_PL_ZenKai_Uni=/usr/share/fonts/cjkunifonts-ukai/ukai.ttc
-+filename.Baekmuk_Gulim=/usr/share/fonts/baekmuk-ttf-gulim/gulim.ttf
-+filename.Baekmuk_Batang=/usr/share/fonts/baekmuk-ttf-batang/batang.ttf
++#filename.Sazanami_Gothic=/usr/share/fonts/sazanami/gothic/sazanami-gothic.ttf
++#filename.Sazanami_Mincho=/usr/share/fonts/sazanami/mincho/sazanami-mincho.ttf
++filename.VL_Gothic=/usr/share/fonts/vlgothic/VL-Gothic-Regular.ttf
++filename.VL_PGothic=/usr/share/fonts/svlgothic/VL-PGothic-Regular.ttf
++filename.AR_PL_ShanHeiSun_Uni=/usr/share/fonts/cjkuni-uming/uming.ttc
++filename.AR_PL_ZenKai_Uni=/usr/share/fonts/cjkuni-ukai/ukai.ttc
++filename.Baekmuk_Gulim=/usr/share/fonts/baekmuk-ttf/gulim.ttf
++filename.Baekmuk_Batang=/usr/share/fonts/baekmuk-ttf/batang.ttf
 +
-+filename.Lohit_Bengali=/usr/share/fonts/lohit-bengali/lohit_bn.ttf
-+filename.Lohit_Gujarati=/usr/share/fonts/lohit-gujarati/lohit_gu.ttf
-+filename.Lohit_Hindi=/usr/share/fonts/lohit-hindi/lohit_hi.ttf
-+filename.Lohit_Kannda=/usr/share/fonts/lohit-kannada/lohit_kn.ttf
-+filename.Lohit_Malayalam=/usr/share/fonts/lohit-malayalam/lohit_ml.ttf
-+filename.Lohit_Oriya=/usr/share/fonts/lohit-oriya/lohit_or.ttf
-+filename.Lohit_Punjabi=/usr/share/fonts/lohit-punjabi/lohit_pa.ttf
-+filename.Lohit_Tamil=/usr/share/fonts/lohit-tamil/lohit_ta.ttf
-+filename.Lohit_Telugu=/usr/share/fonts/lohit-telugu/lohit_te.ttf
++filename.Lohit_Assamese=/usr/share/fonts/lohit-assamese/Lohit-Assamese.ttf
++filename.Lohit_Devanagari=/usr/share/fonts/lohit-devanagari/Lohit-Devanagari.ttf
++filename.Lohit_Bengali=/usr/share/fonts/lohit-bengali/Lohit-Bengali.ttf
++filename.Lohit_Gujarati=/usr/share/fonts/lohit-gujarati/Lohit-Gujarati.ttf
++filename.Lohit_Hindi=/usr/share/fonts/lohit-devanagari/Lohit-Devanagari.ttf
++filename.Lohit_Kannda=/usr/share/fonts/lohit-kannada/Lohit-Kannada.ttf
++filename.Lohit_Malayalam=/usr/share/fonts/lohit-malayalam/Lohit-Malayalam.ttf
++filename.Lohit_Oriya=/usr/share/fonts/lohit-oriya/Lohit-Oriya.ttf
++filename.Lohit_Punjabi=/usr/share/fonts/lohit-punjabi/Lohit-Punjabi.ttf
++filename.Lohit_Tamil=/usr/share/fonts/lohit-tamil/Lohit-Tamil.ttf
++filename.Lohit_Telugu=/usr/share/fonts/lohit-telugu/Lohit-Telugu.ttf
++
 +filename.LKLUG=/usr/share/fonts/lklug/lklug.ttf
 +
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/patches/openjdk/6541476-png-iTXt-chunk.patch	Tue Jan 18 14:25:25 2011 +0000
@@ -0,0 +1,549 @@
+diff -urN openjdk.orig/jdk/src/share/classes/com/sun/imageio/plugins/png/PNGImageReader.java openjdk/jdk/src/share/classes/com/sun/imageio/plugins/png/PNGImageReader.java
+--- openjdk.orig/jdk/src/share/classes/com/sun/imageio/plugins/png/PNGImageReader.java	2010-12-23 17:11:16.193446425 -0500
++++ openjdk/jdk/src/share/classes/com/sun/imageio/plugins/png/PNGImageReader.java	2010-12-23 17:11:29.399447037 -0500
+@@ -44,7 +44,6 @@
+ import java.util.Arrays;
+ import java.util.Enumeration;
+ import java.util.Iterator;
+-import java.util.List;
+ import java.util.zip.Inflater;
+ import java.util.zip.InflaterInputStream;
+ import javax.imageio.IIOException;
+@@ -57,6 +56,7 @@
+ import com.sun.imageio.plugins.common.InputStreamAdapter;
+ import com.sun.imageio.plugins.common.ReaderUtil;
+ import com.sun.imageio.plugins.common.SubImageInputStream;
++import java.io.ByteArrayOutputStream;
+ import sun.awt.image.ByteInterleavedRaster;
+ 
+ class PNGImageDataEnumeration implements Enumeration {
+@@ -209,6 +209,15 @@
+         resetStreamSettings();
+     }
+ 
++    private String readNullTerminatedString(String charset) throws IOException {
++        ByteArrayOutputStream baos = new ByteArrayOutputStream();
++        int b;
++        while ((b = stream.read()) != 0) {
++            baos.write(b);
++        }
++        return new String(baos.toByteArray(), charset);
++    }
++
+     private String readNullTerminatedString() throws IOException {
+         StringBuilder b = new StringBuilder();
+         int c;
+@@ -447,26 +456,27 @@
+         metadata.iTXt_keyword.add(keyword);
+ 
+         int compressionFlag = stream.readUnsignedByte();
+-        metadata.iTXt_compressionFlag.add(new Integer(compressionFlag));
++        metadata.iTXt_compressionFlag.add(Boolean.valueOf(compressionFlag == 1));
+ 
+         int compressionMethod = stream.readUnsignedByte();
+-        metadata.iTXt_compressionMethod.add(new Integer(compressionMethod));
++        metadata.iTXt_compressionMethod.add(Integer.valueOf(compressionMethod));
+ 
+-        String languageTag = readNullTerminatedString();
++        String languageTag = readNullTerminatedString("UTF8");
+         metadata.iTXt_languageTag.add(languageTag);
+ 
+-        String translatedKeyword = stream.readUTF();
++        String translatedKeyword =
++            readNullTerminatedString("UTF8");
+         metadata.iTXt_translatedKeyword.add(translatedKeyword);
+-        stream.skipBytes(1); // Null separator
+ 
+         String text;
++        long pos = stream.getStreamPosition();
++        byte[] b = new byte[(int)(chunkStart + chunkLength - pos)];
++        stream.readFully(b);
++
+         if (compressionFlag == 1) { // Decompress the text
+-            long pos = stream.getStreamPosition();
+-            byte[] b = new byte[(int)(chunkStart + chunkLength - pos)];
+-            stream.readFully(b);
+-            text = inflate(b);
++            text = new String(inflate(b), "UTF8");
+         } else {
+-            text = stream.readUTF();
++            text = new String(b, "UTF8");
+         }
+         metadata.iTXt_text.add(text);
+     }
+@@ -615,20 +625,20 @@
+         metadata.tRNS_present = true;
+     }
+ 
+-    private static String inflate(byte[] b) throws IOException {
++    private static byte[] inflate(byte[] b) throws IOException {
+         InputStream bais = new ByteArrayInputStream(b);
+         InputStream iis = new InflaterInputStream(bais);
++        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ 
+-        StringBuilder sb = new StringBuilder(80);
+         int c;
+         try {
+             while ((c = iis.read()) != -1) {
+-                sb.append((char)c);
++                baos.write(c);
+             }
+         } finally {
+             iis.close();
+         }
+-        return sb.toString();
++        return baos.toByteArray();
+     }
+ 
+     private void parse_zTXt_chunk(int chunkLength) throws IOException {
+@@ -640,7 +650,7 @@
+ 
+         byte[] b = new byte[chunkLength - keyword.length() - 2];
+         stream.readFully(b);
+-        metadata.zTXt_text.add(inflate(b));
++        metadata.zTXt_text.add(new String(inflate(b)));
+     }
+ 
+     private void readMetadata() throws IIOException {
+diff -urN openjdk.orig/jdk/src/share/classes/com/sun/imageio/plugins/png/PNGImageWriter.java openjdk/jdk/src/share/classes/com/sun/imageio/plugins/png/PNGImageWriter.java
+--- openjdk.orig/jdk/src/share/classes/com/sun/imageio/plugins/png/PNGImageWriter.java	2010-12-23 17:11:16.193446425 -0500
++++ openjdk/jdk/src/share/classes/com/sun/imageio/plugins/png/PNGImageWriter.java	2010-12-23 17:11:29.400446993 -0500
+@@ -671,13 +671,13 @@
+         }
+     }
+ 
+-    private byte[] deflate(String s) throws IOException {
++    private byte[] deflate(byte[] b) throws IOException {
+         ByteArrayOutputStream baos = new ByteArrayOutputStream();
+         DeflaterOutputStream dos = new DeflaterOutputStream(baos);
+ 
+-        int len = s.length();
++        int len = b.length;
+         for (int i = 0; i < len; i++) {
+-            dos.write((int)s.charAt(i));
++            dos.write((int)(0xff & b[i]));
+         }
+         dos.close();
+ 
+@@ -685,38 +685,37 @@
+     }
+ 
+     private void write_iTXt() throws IOException {
+-        Iterator keywordIter = metadata.iTXt_keyword.iterator();
+-        Iterator flagIter = metadata.iTXt_compressionFlag.iterator();
+-        Iterator methodIter = metadata.iTXt_compressionMethod.iterator();
+-        Iterator languageIter = metadata.iTXt_languageTag.iterator();
+-        Iterator translatedKeywordIter =
++        Iterator<String> keywordIter = metadata.iTXt_keyword.iterator();
++        Iterator<Boolean> flagIter = metadata.iTXt_compressionFlag.iterator();
++        Iterator<Integer> methodIter = metadata.iTXt_compressionMethod.iterator();
++        Iterator<String> languageIter = metadata.iTXt_languageTag.iterator();
++        Iterator<String> translatedKeywordIter =
+             metadata.iTXt_translatedKeyword.iterator();
+-        Iterator textIter = metadata.iTXt_text.iterator();
++        Iterator<String> textIter = metadata.iTXt_text.iterator();
+ 
+         while (keywordIter.hasNext()) {
+             ChunkStream cs = new ChunkStream(PNGImageReader.iTXt_TYPE, stream);
+-            String keyword = (String)keywordIter.next();
+-            cs.writeBytes(keyword);
++
++            cs.writeBytes(keywordIter.next());
+             cs.writeByte(0);
+ 
+-            int flag = ((Integer)flagIter.next()).intValue();
+-            cs.writeByte(flag);
+-            int method = ((Integer)methodIter.next()).intValue();
+-            cs.writeByte(method);
++            Boolean compressed = flagIter.next();
++            cs.writeByte(compressed ? 1 : 0);
+ 
+-            String languageTag = (String)languageIter.next();
+-            cs.writeBytes(languageTag);
++            cs.writeByte(methodIter.next().intValue());
++
++            cs.writeBytes(languageIter.next());
+             cs.writeByte(0);
+ 
+-            String translatedKeyword = (String)translatedKeywordIter.next();
+-            cs.writeBytes(translatedKeyword);
++
++            cs.write(translatedKeywordIter.next().getBytes("UTF8"));
+             cs.writeByte(0);
+ 
+-            String text = (String)textIter.next();
+-            if (flag == 1) {
+-                cs.write(deflate(text));
++            String text = textIter.next();
++            if (compressed) {
++                cs.write(deflate(text.getBytes("UTF8")));
+             } else {
+-                cs.writeUTF(text);
++                cs.write(text.getBytes("UTF8"));
+             }
+             cs.finish();
+         }
+@@ -737,7 +736,7 @@
+             cs.writeByte(compressionMethod);
+ 
+             String text = (String)textIter.next();
+-            cs.write(deflate(text));
++            cs.write(deflate(text.getBytes()));
+             cs.finish();
+         }
+     }
+diff -urN openjdk.orig/jdk/src/share/classes/com/sun/imageio/plugins/png/PNGMetadata.java openjdk/jdk/src/share/classes/com/sun/imageio/plugins/png/PNGMetadata.java
+--- openjdk.orig/jdk/src/share/classes/com/sun/imageio/plugins/png/PNGMetadata.java	2010-06-21 17:15:11.000000000 -0400
++++ openjdk/jdk/src/share/classes/com/sun/imageio/plugins/png/PNGMetadata.java	2010-12-23 17:11:29.401446953 -0500
+@@ -176,12 +176,12 @@
+     public byte[] iCCP_compressedProfile;
+ 
+     // iTXt chunk
+-    public ArrayList iTXt_keyword = new ArrayList(); // Strings
+-    public ArrayList iTXt_compressionFlag = new ArrayList(); // Integers
+-    public ArrayList iTXt_compressionMethod = new ArrayList(); // Integers
+-    public ArrayList iTXt_languageTag = new ArrayList(); // Strings
+-    public ArrayList iTXt_translatedKeyword = new ArrayList(); // Strings
+-    public ArrayList iTXt_text = new ArrayList(); // Strings
++    public ArrayList<String> iTXt_keyword = new ArrayList<String>();
++    public ArrayList<Boolean> iTXt_compressionFlag = new ArrayList<Boolean>();
++    public ArrayList<Integer> iTXt_compressionMethod = new ArrayList<Integer>();
++    public ArrayList<String> iTXt_languageTag = new ArrayList<String>();
++    public ArrayList<String> iTXt_translatedKeyword = new ArrayList<String>();
++    public ArrayList<String> iTXt_text = new ArrayList<String>();
+ 
+     // pHYs chunk
+     public boolean pHYs_present;
+@@ -599,19 +599,17 @@
+         if (iTXt_keyword.size() > 0) {
+             IIOMetadataNode iTXt_parent = new IIOMetadataNode("iTXt");
+             for (int i = 0; i < iTXt_keyword.size(); i++) {
+-                Integer val;
+-
+                 IIOMetadataNode iTXt_node = new IIOMetadataNode("iTXtEntry");
+-                iTXt_node.setAttribute("keyword", (String)iTXt_keyword.get(i));
+-                val = (Integer)iTXt_compressionFlag.get(i);
+-                iTXt_node.setAttribute("compressionFlag", val.toString());
+-                val = (Integer)iTXt_compressionMethod.get(i);
+-                iTXt_node.setAttribute("compressionMethod", val.toString());
++                iTXt_node.setAttribute("keyword", iTXt_keyword.get(i));
++                iTXt_node.setAttribute("compressionFlag",
++                        iTXt_compressionFlag.get(i) ? "1" : "0");
++                iTXt_node.setAttribute("compressionMethod",
++                        iTXt_compressionMethod.get(i).toString());
+                 iTXt_node.setAttribute("languageTag",
+-                                       (String)iTXt_languageTag.get(i));
++                                       iTXt_languageTag.get(i));
+                 iTXt_node.setAttribute("translatedKeyword",
+-                                       (String)iTXt_translatedKeyword.get(i));
+-                iTXt_node.setAttribute("text", (String)iTXt_text.get(i));
++                                       iTXt_translatedKeyword.get(i));
++                iTXt_node.setAttribute("text", iTXt_text.get(i));
+ 
+                 iTXt_parent.appendChild(iTXt_node);
+             }
+@@ -1039,11 +1037,11 @@
+ 
+         for (int i = 0; i < iTXt_keyword.size(); i++) {
+             node = new IIOMetadataNode("TextEntry");
+-            node.setAttribute("keyword", (String)iTXt_keyword.get(i));
+-            node.setAttribute("value", (String)iTXt_text.get(i));
++            node.setAttribute("keyword", iTXt_keyword.get(i));
++            node.setAttribute("value", iTXt_text.get(i));
+             node.setAttribute("language",
+-                              (String)iTXt_languageTag.get(i));
+-            if (((Integer)iTXt_compressionFlag.get(i)).intValue() == 1) {
++                              iTXt_languageTag.get(i));
++            if (iTXt_compressionFlag.get(i)) {
+                 node.setAttribute("compression", "deflate");
+             } else {
+                 node.setAttribute("compression", "none");
+@@ -1429,11 +1427,11 @@
+ 
+                     boolean compressionFlag =
+                         getBooleanAttribute(iTXt_node, "compressionFlag");
+-                    iTXt_compressionFlag.add(new Boolean(compressionFlag));
++                    iTXt_compressionFlag.add(Boolean.valueOf(compressionFlag));
+ 
+                     String compressionMethod =
+                         getAttribute(iTXt_node, "compressionMethod");
+-                    iTXt_compressionMethod.add(compressionMethod);
++                    iTXt_compressionMethod.add(Integer.valueOf(compressionMethod));
+ 
+                     String languageTag =
+                         getAttribute(iTXt_node, "languageTag");
+@@ -1952,13 +1950,10 @@
+                                 tEXt_text.add(value);
+                             }
+                         } else {
+-                            int flag = compression.equals("zip") ?
+-                                1 : 0;
+-
+                             // Use an iTXt node
+                             iTXt_keyword.add(keyword);
+-                            iTXt_compressionFlag.add(new Integer(flag));
+-                            iTXt_compressionMethod.add(new Integer(0));
++                            iTXt_compressionFlag.add(Boolean.valueOf(compression.equals("zip")));
++                            iTXt_compressionMethod.add(Integer.valueOf(0));
+                             iTXt_languageTag.add(language);
+                             iTXt_translatedKeyword.add(keyword); // fake it
+                             iTXt_text.add(value);
+@@ -1995,12 +1990,12 @@
+         gAMA_present = false;
+         hIST_present = false;
+         iCCP_present = false;
+-        iTXt_keyword = new ArrayList();
+-        iTXt_compressionFlag = new ArrayList();
+-        iTXt_compressionMethod = new ArrayList();
+-        iTXt_languageTag = new ArrayList();
+-        iTXt_translatedKeyword = new ArrayList();
+-        iTXt_text = new ArrayList();
++        iTXt_keyword = new ArrayList<String>();
++        iTXt_compressionFlag = new ArrayList<Boolean>();
++        iTXt_compressionMethod = new ArrayList<Integer>();
++        iTXt_languageTag = new ArrayList<String>();
++        iTXt_translatedKeyword = new ArrayList<String>();
++        iTXt_text = new ArrayList<String>();
+         pHYs_present = false;
+         sBIT_present = false;
+         sPLT_present = false;
+diff -urN openjdk.orig/jdk/test/javax/imageio/plugins/png/ITXtTest.java openjdk/jdk/test/javax/imageio/plugins/png/ITXtTest.java
+--- openjdk.orig/jdk/test/javax/imageio/plugins/png/ITXtTest.java	1969-12-31 19:00:00.000000000 -0500
++++ openjdk/jdk/test/javax/imageio/plugins/png/ITXtTest.java	2010-12-23 17:12:58.538446639 -0500
+@@ -0,0 +1,236 @@
++/*
++ * Copyright (c) 2008, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
++ * or visit www.oracle.com if you need additional information or have any
++ * questions.
++ */
++
++/**
++ * @test
++ * @bug     6541476
++ * @summary Test verifies that ImageIO PNG plugin correcly handles the
++ *          iTxt chunk (International textual data).
++ *
++ * @run     main ITXtTest
++ */
++
++
++import java.awt.Color;
++import java.awt.Graphics2D;
++import java.awt.image.BufferedImage;
++import java.io.File;
++
++import javax.imageio.ImageIO;
++import javax.imageio.ImageReader;
++import javax.imageio.IIOImage;
++import javax.imageio.ImageTypeSpecifier;
++import javax.imageio.ImageWriter;
++import javax.imageio.metadata.IIOMetadata;
++import javax.imageio.metadata.IIOMetadataNode;
++import javax.imageio.stream.ImageOutputStream;
++import javax.imageio.stream.ImageInputStream;
++
++import org.w3c.dom.Node;
++
++public class ITXtTest {
++    static public void main(String args[]) {
++        ITXtTest t_en = new ITXtTest();
++        t_en.description = "xml - en";
++        t_en.keyword = "XML:com.adobe.xmp";
++        t_en.isCompressed = false;
++        t_en.compression = 0;
++        t_en.language = "en";
++        t_en.trasKeyword = "XML:com.adobe.xmp";
++        t_en.text = "<xml>Something</xml>";
++
++        doTest(t_en);
++
++        // check compression case
++        t_en.isCompressed = true;
++        t_en.description = "xml - en - compressed";
++
++        doTest(t_en);
++
++        ITXtTest t_ru = new ITXtTest();
++        t_ru.description = "xml - ru";
++        t_ru.keyword = "XML:com.adobe.xmp";
++        t_ru.isCompressed = false;
++        t_ru.compression = 0;
++        t_ru.language = "ru";
++        t_ru.trasKeyword = "\u0410\u0410\u0410\u0410\u0410 XML";
++        t_ru.text = "<xml>\u042A\u042F\u042F\u042F\u042F\u042F\u042F</xml>";
++
++        doTest(t_ru);
++
++        t_ru.isCompressed = true;
++        t_ru.description = "xml - ru - compressed";
++
++        doTest(t_ru);
++    }
++
++
++    String description;
++
++    String keyword;
++    boolean isCompressed;
++    int compression;
++    String language;
++    String trasKeyword;
++    String text;
++
++
++    public IIOMetadataNode getNode() {
++        IIOMetadataNode iTXt = new IIOMetadataNode("iTXt");
++        IIOMetadataNode iTXtEntry = new IIOMetadataNode("iTXtEntry");
++        iTXtEntry.setAttribute("keyword", keyword);
++        iTXtEntry.setAttribute("compressionFlag",
++                               isCompressed ? "true" : "false");
++        iTXtEntry.setAttribute("compressionMethod",
++                               Integer.toString(compression));
++        iTXtEntry.setAttribute("languageTag", language);
++        iTXtEntry.setAttribute("translatedKeyword",
++                               trasKeyword);
++        iTXtEntry.setAttribute("text", text);
++        iTXt.appendChild(iTXtEntry);
++        return iTXt;
++    }
++
++    public static ITXtTest getFromNode(IIOMetadataNode n) {
++        ITXtTest t = new ITXtTest();
++
++        if (!"iTXt".equals(n.getNodeName())) {
++            throw new RuntimeException("Invalid node");
++        }
++        IIOMetadataNode e = (IIOMetadataNode)n.getFirstChild();
++        if (!"iTXtEntry".equals(e.getNodeName())) {
++            throw new RuntimeException("Invalid entry node");
++        }
++        t.keyword = e.getAttribute("keyword");
++        t.isCompressed =
++            (Integer.valueOf(e.getAttribute("compressionFlag")).intValue() == 1);
++        t.compression =
++            Integer.valueOf(e.getAttribute("compressionMethod")).intValue();
++        t.language = e.getAttribute("languageTag");
++        t.trasKeyword = e.getAttribute("translatedKeyword");
++        t.text = e.getAttribute("text");
++
++        return t;
++    }
++
++    @Override
++    public boolean equals(Object o) {
++        if (! (o instanceof ITXtTest)) {
++            return false;
++        }
++        ITXtTest t = (ITXtTest)o;
++        if (!keyword.equals(t.keyword)) { return false; }
++        if (isCompressed != t.isCompressed) { return false; }
++        if (compression != t.compression) { return false; }
++        if (!language.equals(t.language)) { return false; }
++        if (!trasKeyword.equals(t.trasKeyword)) { return false; }
++        if (!text.equals(t.text)) { return false; }
++
++        return true;
++    }
++
++
++
++    private static void doTest(ITXtTest src) {
++
++        System.out.println("Test: " + src.description);
++
++        File file = new File("test.png");
++
++        writeTo(file, src);
++        ITXtTest dst = readFrom(file);
++
++        if (dst == null || !dst.equals(src)) {
++            throw new RuntimeException("Test failed.");
++        }
++
++        System.out.println("Test passed.");
++    }
++
++    private static void writeTo(File f, ITXtTest t) {
++        BufferedImage src = createBufferedImage();
++        try {
++            ImageOutputStream imageOutputStream =
++                ImageIO.createImageOutputStream(f);
++
++            ImageTypeSpecifier imageTypeSpecifier =
++                new ImageTypeSpecifier(src);
++            ImageWriter imageWriter =
++                ImageIO.getImageWritersByFormatName("PNG").next();
++
++            imageWriter.setOutput(imageOutputStream);
++
++            IIOMetadata m =
++                imageWriter.getDefaultImageMetadata(imageTypeSpecifier, null);
++
++            String format = m.getNativeMetadataFormatName();
++            Node root = m.getAsTree(format);
++
++            IIOMetadataNode iTXt = t.getNode();
++            root.appendChild(iTXt);
++            m.setFromTree(format, root);
++
++            imageWriter.write(new IIOImage(src, null, m));
++            imageOutputStream.close();
++            System.out.println("Writing done.");
++        } catch (Throwable e) {
++            throw new RuntimeException("Writing test failed.", e);
++        }
++    }
++
++    private static ITXtTest readFrom(File f) {
++        try {
++            ImageInputStream iis = ImageIO.createImageInputStream(f);
++            ImageReader r = ImageIO.getImageReaders(iis).next();
++            r.setInput(iis);
++
++            IIOImage dst = r.readAll(0, null);
++
++            // look for iTXt node
++            IIOMetadata m = dst.getMetadata();
++            Node root = m.getAsTree(m.getNativeMetadataFormatName());
++            Node n = root.getFirstChild();
++            while (n != null && !"iTXt".equals(n.getNodeName())) {
++                n = n.getNextSibling();
++            }
++            if (n == null) {
++                throw new RuntimeException("No iTXt node!");
++            }
++            ITXtTest t = ITXtTest.getFromNode((IIOMetadataNode)n);
++            return t;
++        } catch (Throwable e) {
++            throw new RuntimeException("Reading test failed.", e);
++        }
++    }
++
++    private static BufferedImage createBufferedImage() {
++        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();
++        return image;
++    }
++}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/patches/openjdk/6687968-pngimagereader_mem_leak.patch	Tue Jan 18 14:25:25 2011 +0000
@@ -0,0 +1,126 @@
+diff -urN openjdk.orig/jdk/src/share/classes/com/sun/imageio/plugins/png/PNGImageReader.java openjdk/jdk/src/share/classes/com/sun/imageio/plugins/png/PNGImageReader.java
+--- openjdk.orig/jdk/src/share/classes/com/sun/imageio/plugins/png/PNGImageReader.java	2010-06-21 17:15:11.000000000 -0400
++++ openjdk/jdk/src/share/classes/com/sun/imageio/plugins/png/PNGImageReader.java	2010-12-23 15:12:24.471446202 -0500
+@@ -618,10 +618,15 @@
+     private static String inflate(byte[] b) throws IOException {
+         InputStream bais = new ByteArrayInputStream(b);
+         InputStream iis = new InflaterInputStream(bais);
++
+         StringBuilder sb = new StringBuilder(80);
+         int c;
+-        while ((c = iis.read()) != -1) {
+-            sb.append((char)c);
++        try {
++            while ((c = iis.read()) != -1) {
++                sb.append((char)c);
++            }
++        } finally {
++            iis.close();
+         }
+         return sb.toString();
+     }
+@@ -1246,13 +1251,26 @@
+             destinationBands = param.getDestinationBands();
+             destinationOffset = param.getDestinationOffset();
+         }
+-
++        Inflater inf = null;
+         try {
+             stream.seek(imageStartPosition);
+ 
+             Enumeration e = new PNGImageDataEnumeration(stream);
+             InputStream is = new SequenceInputStream(e);
+-            is = new InflaterInputStream(is, new Inflater());
++
++           /* InflaterInputStream uses an Inflater instance which consumes
++            * native (non-GC visible) resources. This is normally implicitly
++            * freed when the stream is closed. However since the
++            * InflaterInputStream wraps a client-supplied input stream,
++            * we cannot close it.
++            * But the app may depend on GC finalization to close the stream.
++            * Therefore to ensure timely freeing of native resources we
++            * explicitly create the Inflater instance and free its resources
++            * when we are done with the InflaterInputStream by calling
++            * inf.end();
++            */
++            inf = new Inflater();
++            is = new InflaterInputStream(is, inf);
+             is = new BufferedInputStream(is);
+             this.pixelStream = new DataInputStream(is);
+ 
+@@ -1285,6 +1303,10 @@
+             }
+         } catch (IOException e) {
+             throw new IIOException("Error reading PNG image data", e);
++        } finally {
++            if (inf != null) {
++                inf.end();
++            }
+         }
+     }
+ 
+diff -urN openjdk.orig/jdk/src/share/classes/com/sun/imageio/plugins/png/PNGImageWriter.java openjdk/jdk/src/share/classes/com/sun/imageio/plugins/png/PNGImageWriter.java
+--- openjdk.orig/jdk/src/share/classes/com/sun/imageio/plugins/png/PNGImageWriter.java	2010-06-21 17:15:11.000000000 -0400
++++ openjdk/jdk/src/share/classes/com/sun/imageio/plugins/png/PNGImageWriter.java	2010-12-23 15:12:24.472446225 -0500
+@@ -244,13 +244,17 @@
+     }
+ 
+     public void finish() throws IOException {
+-        if (!def.finished()) {
+-            def.finish();
+-            while (!def.finished()) {
+-                deflate();
++        try {
++            if (!def.finished()) {
++                def.finish();
++                while (!def.finished()) {
++                    deflate();
++                }
+             }
++            finishChunk();
++        } finally {
++            def.end();
+         }
+-        finishChunk();
+     }
+ 
+     protected void finalize() throws Throwable {
+@@ -928,23 +932,24 @@
+     // Use sourceXOffset, etc.
+     private void write_IDAT(RenderedImage image) throws IOException {
+         IDATOutputStream ios = new IDATOutputStream(stream, 32768);
+-
+-        if (metadata.IHDR_interlaceMethod == 1) {
+-            for (int i = 0; i < 7; i++) {
+-                encodePass(ios, image,
+-                           PNGImageReader.adam7XOffset[i],
+-                           PNGImageReader.adam7YOffset[i],
+-                           PNGImageReader.adam7XSubsampling[i],
+-                           PNGImageReader.adam7YSubsampling[i]);
+-                if (abortRequested()) {
+-                    break;
++        try {
++            if (metadata.IHDR_interlaceMethod == 1) {
++                for (int i = 0; i < 7; i++) {
++                    encodePass(ios, image,
++                               PNGImageReader.adam7XOffset[i],
++                               PNGImageReader.adam7YOffset[i],
++                               PNGImageReader.adam7XSubsampling[i],
++                               PNGImageReader.adam7YSubsampling[i]);
++                    if (abortRequested()) {
++                        break;
++                    }
+                 }
++            } else {
++                encodePass(ios, image, 0, 0, 1, 1);
+             }
+-        } else {
+-            encodePass(ios, image, 0, 0, 1, 1);
++        } finally {
++            ios.finish();
+         }
+-
+-        ios.finish();
+     }
+ 
+     private void writeIEND() throws IOException {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/patches/openjdk/6782079-png_metadata_oom.patch	Tue Jan 18 14:25:25 2011 +0000
@@ -0,0 +1,4122 @@
+diff -urN openjdk.orig/jdk/src/share/classes/com/sun/imageio/plugins/png/PNGImageReader.java openjdk/jdk/src/share/classes/com/sun/imageio/plugins/png/PNGImageReader.java
+--- openjdk.orig/jdk/src/share/classes/com/sun/imageio/plugins/png/PNGImageReader.java	2010-12-24 11:22:28.802101947 -0500
++++ openjdk/jdk/src/share/classes/com/sun/imageio/plugins/png/PNGImageReader.java	2010-12-24 11:24:47.272101847 -0500
+@@ -37,6 +37,7 @@
+ import java.io.BufferedInputStream;
+ import java.io.ByteArrayInputStream;
+ import java.io.DataInputStream;
++import java.io.EOFException;
+ import java.io.InputStream;
+ import java.io.IOException;
+ import java.io.SequenceInputStream;
+@@ -59,7 +60,7 @@
+ import java.io.ByteArrayOutputStream;
+ import sun.awt.image.ByteInterleavedRaster;
+ 
+-class PNGImageDataEnumeration implements Enumeration {
++class PNGImageDataEnumeration implements Enumeration<InputStream> {
+ 
+     boolean firstTime = true;
+     ImageInputStream stream;
+@@ -72,7 +73,7 @@
+         int type = stream.readInt(); // skip chunk type
+     }
+ 
+-    public Object nextElement() {
++    public InputStream nextElement() {
+         try {
+             firstTime = false;
+             ImageInputStream iis = new SubImageInputStream(stream, length);
+@@ -209,25 +210,17 @@
+         resetStreamSettings();
+     }
+ 
+-    private String readNullTerminatedString(String charset) throws IOException {
++    private String readNullTerminatedString(String charset, int maxLen) throws IOException {
+         ByteArrayOutputStream baos = new ByteArrayOutputStream();
+         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);
+         }
+         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 {
+         if (gotHeader) {
+             return;
+@@ -436,7 +429,7 @@
+     }
+ 
+     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_compressionMethod = stream.readUnsignedByte();
+@@ -452,7 +445,7 @@
+     private void parse_iTXt_chunk(int chunkLength) throws IOException {
+         long chunkStart = stream.getStreamPosition();
+ 
+-        String keyword = readNullTerminatedString();
++        String keyword = readNullTerminatedString("ISO-8859-1", 80);
+         metadata.iTXt_keyword.add(keyword);
+ 
+         int compressionFlag = stream.readUnsignedByte();
+@@ -461,15 +454,17 @@
+         int compressionMethod = stream.readUnsignedByte();
+         metadata.iTXt_compressionMethod.add(Integer.valueOf(compressionMethod));
+ 
+-        String languageTag = readNullTerminatedString("UTF8");
++        String languageTag = readNullTerminatedString("UTF8", 80);
+         metadata.iTXt_languageTag.add(languageTag);
+ 
++        long pos = stream.getStreamPosition();
++        int maxLen = (int)(chunkStart + chunkLength - pos);
+         String translatedKeyword =
+-            readNullTerminatedString("UTF8");
++            readNullTerminatedString("UTF8", maxLen);
+         metadata.iTXt_translatedKeyword.add(translatedKeyword);
+ 
+         String text;
+-        long pos = stream.getStreamPosition();
++        pos = stream.getStreamPosition();
+         byte[] b = new byte[(int)(chunkStart + chunkLength - pos)];
+         stream.readFully(b);
+ 
+@@ -513,7 +508,7 @@
+ 
+     private void parse_sPLT_chunk(int chunkLength)
+         throws IOException, IIOException {
+-        metadata.sPLT_paletteName = readNullTerminatedString();
++        metadata.sPLT_paletteName = readNullTerminatedString("ISO-8859-1", 80);
+         chunkLength -= metadata.sPLT_paletteName.length() + 1;
+ 
+         int sampleDepth = stream.readUnsignedByte();
+@@ -556,12 +551,12 @@
+     }
+ 
+     private void parse_tEXt_chunk(int chunkLength) throws IOException {
+-        String keyword = readNullTerminatedString();
++        String keyword = readNullTerminatedString("ISO-8859-1", 80);
+         metadata.tEXt_keyword.add(keyword);
+ 
+         byte[] b = new byte[chunkLength - keyword.length() - 1];
+         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 {
+@@ -642,7 +637,7 @@
+     }
+ 
+     private void parse_zTXt_chunk(int chunkLength) throws IOException {
+-        String keyword = readNullTerminatedString();
++        String keyword = readNullTerminatedString("ISO-8859-1", 80);
+         metadata.zTXt_keyword.add(keyword);
+ 
+         int method = stream.readUnsignedByte();
+@@ -650,7 +645,7 @@
+ 
+         byte[] b = new byte[chunkLength - keyword.length() - 2];
+         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 {
+@@ -1265,7 +1260,7 @@
+         try {
+             stream.seek(imageStartPosition);
+ 
+-            Enumeration e = new PNGImageDataEnumeration(stream);
++            Enumeration<InputStream> e = new PNGImageDataEnumeration(stream);
+             InputStream is = new SequenceInputStream(e);
+ 
+            /* InflaterInputStream uses an Inflater instance which consumes
+diff -urN openjdk.orig/jdk/src/share/classes/com/sun/imageio/plugins/png/PNGImageReader.java.orig openjdk/jdk/src/share/classes/com/sun/imageio/plugins/png/PNGImageReader.java.orig
+--- openjdk.orig/jdk/src/share/classes/com/sun/imageio/plugins/png/PNGImageReader.java.orig	1969-12-31 19:00:00.000000000 -0500
++++ openjdk/jdk/src/share/classes/com/sun/imageio/plugins/png/PNGImageReader.java.orig	2010-12-24 11:22:50.400102016 -0500
+@@ -0,0 +1,1599 @@
++/*
++ * Copyright (c) 2000, 2006, Oracle and/or its affiliates. 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.  Oracle designates this
++ * particular file as subject to the "Classpath" exception as provided
++ * by Oracle in the LICENSE file that accompanied this code.
++ *
++ * 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
++ * or visit www.oracle.com if you need additional information or have any
++ * questions.
++ */
++
++package com.sun.imageio.plugins.png;
++
++import java.awt.Point;
++import java.awt.Rectangle;
++import java.awt.color.ColorSpace;
++import java.awt.image.BufferedImage;
++import java.awt.image.DataBuffer;
++import java.awt.image.DataBufferByte;
++import java.awt.image.DataBufferUShort;
++import java.awt.image.Raster;
++import java.awt.image.WritableRaster;
++import java.io.BufferedInputStream;
++import java.io.ByteArrayInputStream;
++import java.io.DataInputStream;
++import java.io.InputStream;
++import java.io.IOException;
++import java.io.SequenceInputStream;
++import java.util.ArrayList;
++import java.util.Arrays;
++import java.util.Enumeration;
++import java.util.Iterator;
++import java.util.zip.Inflater;
++import java.util.zip.InflaterInputStream;
++import javax.imageio.IIOException;
++import javax.imageio.ImageReader;
++import javax.imageio.ImageReadParam;
++import javax.imageio.ImageTypeSpecifier;
++import javax.imageio.metadata.IIOMetadata;
++import javax.imageio.spi.ImageReaderSpi;
++import javax.imageio.stream.ImageInputStream;
++import com.sun.imageio.plugins.common.InputStreamAdapter;
++import com.sun.imageio.plugins.common.ReaderUtil;
++import com.sun.imageio.plugins.common.SubImageInputStream;
++import java.io.ByteArrayOutputStream;
++import sun.awt.image.ByteInterleavedRaster;
++
++class PNGImageDataEnumeration implements Enumeration {
++
++    boolean firstTime = true;
++    ImageInputStream stream;
++    int length;
++
++    public PNGImageDataEnumeration(ImageInputStream stream)
++        throws IOException {
++        this.stream = stream;
++        this.length = stream.readInt();
++        int type = stream.readInt(); // skip chunk type
++    }
++
++    public Object nextElement() {
++        try {
++            firstTime = false;
++            ImageInputStream iis = new SubImageInputStream(stream, length);
++            return new InputStreamAdapter(iis);
++        } catch (IOException e) {
++            return null;
++        }
++    }
++
++    public boolean hasMoreElements() {
++        if (firstTime) {
++            return true;
++        }
++
++        try {
++            int crc = stream.readInt();
++            this.length = stream.readInt();
++            int type = stream.readInt();
++            if (type == PNGImageReader.IDAT_TYPE) {
++                return true;
++            } else {
++                return false;
++            }
++        } catch (IOException e) {
++            return false;
++        }
++    }
++}
++
++/**
++ */
++public class PNGImageReader extends ImageReader {
++
++    /*
++     * Note: The following chunk type constants are autogenerated.  Each
++     * one is derived from the ASCII values of its 4-character name.  For
++     * example, IHDR_TYPE is calculated as follows:
++     *            ('I' << 24) | ('H' << 16) | ('D' << 8) | 'R'
++     */
++
++    // Critical chunks
++    static final int IHDR_TYPE = 0x49484452;
++    static final int PLTE_TYPE = 0x504c5445;
++    static final int IDAT_TYPE = 0x49444154;
++    static final int IEND_TYPE = 0x49454e44;
++
++    // Ancillary chunks
++    static final int bKGD_TYPE = 0x624b4744;
++    static final int cHRM_TYPE = 0x6348524d;
++    static final int gAMA_TYPE = 0x67414d41;
++    static final int hIST_TYPE = 0x68495354;
++    static final int iCCP_TYPE = 0x69434350;
++    static final int iTXt_TYPE = 0x69545874;
++    static final int pHYs_TYPE = 0x70485973;
++    static final int sBIT_TYPE = 0x73424954;
++    static final int sPLT_TYPE = 0x73504c54;
++    static final int sRGB_TYPE = 0x73524742;
++    static final int tEXt_TYPE = 0x74455874;
++    static final int tIME_TYPE = 0x74494d45;
++    static final int tRNS_TYPE = 0x74524e53;
++    static final int zTXt_TYPE = 0x7a545874;
++
++    static final int PNG_COLOR_GRAY = 0;
++    static final int PNG_COLOR_RGB = 2;
++    static final int PNG_COLOR_PALETTE = 3;
++    static final int PNG_COLOR_GRAY_ALPHA = 4;
++    static final int PNG_COLOR_RGB_ALPHA = 6;
++
++    // The number of bands by PNG color type
++    static final int[] inputBandsForColorType = {
++         1, // gray
++        -1, // unused
++         3, // rgb
++         1, // palette
++         2, // gray + alpha
++        -1, // unused
++         4  // rgb + alpha
++    };
++
++    static final int PNG_FILTER_NONE = 0;
++    static final int PNG_FILTER_SUB = 1;
++    static final int PNG_FILTER_UP = 2;
++    static final int PNG_FILTER_AVERAGE = 3;
++    static final int PNG_FILTER_PAETH = 4;
++
++    static final int[] adam7XOffset = { 0, 4, 0, 2, 0, 1, 0 };
++    static final int[] adam7YOffset = { 0, 0, 4, 0, 2, 0, 1 };
++    static final int[] adam7XSubsampling = { 8, 8, 4, 4, 2, 2, 1, 1 };
++    static final int[] adam7YSubsampling = { 8, 8, 8, 4, 4, 2, 2, 1 };
++
++    private static final boolean debug = true;
++
++    ImageInputStream stream = null;
++
++    boolean gotHeader = false;
++    boolean gotMetadata = false;
++
++    ImageReadParam lastParam = null;
++
++    long imageStartPosition = -1L;
++
++    Rectangle sourceRegion = null;
++    int sourceXSubsampling = -1;
++    int sourceYSubsampling = -1;
++    int sourceMinProgressivePass = 0;
++    int sourceMaxProgressivePass = 6;
++    int[] sourceBands = null;
++    int[] destinationBands = null;
++    Point destinationOffset = new Point(0, 0);
++
++    PNGMetadata metadata = new PNGMetadata();
++
++    DataInputStream pixelStream = null;
++
++    BufferedImage theImage = null;
++
++    // The number of source pixels processed
++    int pixelsDone = 0;
++
++    // The total number of pixels in the source image
++    int totalPixels;
++
++    public PNGImageReader(ImageReaderSpi originatingProvider) {
++        super(originatingProvider);
++    }
++
++    public void setInput(Object input,
++                         boolean seekForwardOnly,
++                         boolean ignoreMetadata) {
++        super.setInput(input, seekForwardOnly, ignoreMetadata);
++        this.stream = (ImageInputStream)input; // Always works
++
++        // Clear all values based on the previous stream contents
++        resetStreamSettings();
++    }
++
++    private String readNullTerminatedString(String charset) throws IOException {
++        ByteArrayOutputStream baos = new ByteArrayOutputStream();
++        int b;
++        while ((b = stream.read()) != 0) {
++            baos.write(b);
++        }
++        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 {
++        if (gotHeader) {
++            return;
++        }
++        if (stream == null) {
++            throw new IllegalStateException("Input source not set!");
++        }
++
++        try {
++            byte[] signature = new byte[8];
++            stream.readFully(signature);
++
++            if (signature[0] != (byte)137 ||
++                signature[1] != (byte)80 ||
++                signature[2] != (byte)78 ||
++                signature[3] != (byte)71 ||
++                signature[4] != (byte)13 ||
++                signature[5] != (byte)10 ||
++                signature[6] != (byte)26 ||
++                signature[7] != (byte)10) {
++                throw new IIOException("Bad PNG signature!");
++            }
++
++            int IHDR_length = stream.readInt();
++            if (IHDR_length != 13) {
++                throw new IIOException("Bad length for IHDR chunk!");
++            }
++            int IHDR_type = stream.readInt();
++            if (IHDR_type != IHDR_TYPE) {
++                throw new IIOException("Bad type for IHDR chunk!");
++            }
++
++            this.metadata = new PNGMetadata();
++
++            int width = stream.readInt();
++            int height = stream.readInt();
++
++            // Re-use signature array to bulk-read these unsigned byte values
++            stream.readFully(signature, 0, 5);
++            int bitDepth          = signature[0] & 0xff;
++            int colorType         = signature[1] & 0xff;
++            int compressionMethod = signature[2] & 0xff;
++            int filterMethod      = signature[3] & 0xff;
++            int interlaceMethod   = signature[4] & 0xff;
++
++            // Skip IHDR CRC
++            stream.skipBytes(4);
++
++            stream.flushBefore(stream.getStreamPosition());
++
++            if (width == 0) {
++                throw new IIOException("Image width == 0!");
++            }
++            if (height == 0) {
++                throw new IIOException("Image height == 0!");
++            }
++            if (bitDepth != 1 && bitDepth != 2 && bitDepth != 4 &&
++                bitDepth != 8 && bitDepth != 16) {
++                throw new IIOException("Bit depth must be 1, 2, 4, 8, or 16!");
++            }
++            if (colorType != 0 && colorType != 2 && colorType != 3 &&
++                colorType != 4 && colorType != 6) {
++                throw new IIOException("Color type must be 0, 2, 3, 4, or 6!");
++            }
++            if (colorType == PNG_COLOR_PALETTE && bitDepth == 16) {
++                throw new IIOException("Bad color type/bit depth combination!");
++            }
++            if ((colorType == PNG_COLOR_RGB ||
++                 colorType == PNG_COLOR_RGB_ALPHA ||
++                 colorType == PNG_COLOR_GRAY_ALPHA) &&
++                (bitDepth != 8 && bitDepth != 16)) {
++                throw new IIOException("Bad color type/bit depth combination!");
++            }
++            if (compressionMethod != 0) {
++                throw new IIOException("Unknown compression method (not 0)!");
++            }
++            if (filterMethod != 0) {
++                throw new IIOException("Unknown filter method (not 0)!");
++            }
++            if (interlaceMethod != 0 && interlaceMethod != 1) {
++                throw new IIOException("Unknown interlace method (not 0 or 1)!");
++            }
++
++            metadata.IHDR_present = true;
++            metadata.IHDR_width = width;
++            metadata.IHDR_height = height;
++            metadata.IHDR_bitDepth = bitDepth;
++            metadata.IHDR_colorType = colorType;
++            metadata.IHDR_compressionMethod = compressionMethod;
++            metadata.IHDR_filterMethod = filterMethod;
++            metadata.IHDR_interlaceMethod = interlaceMethod;
++            gotHeader = true;
++        } catch (IOException e) {
++            throw new IIOException("I/O error reading PNG header!", e);
++        }
++    }
++
++    private void parse_PLTE_chunk(int chunkLength) throws IOException {
++        if (metadata.PLTE_present) {
++            processWarningOccurred(
++"A PNG image may not contain more than one PLTE chunk.\n" +
++"The chunk wil be ignored.");
++            return;
++        } else if (metadata.IHDR_colorType == PNG_COLOR_GRAY ||
++                   metadata.IHDR_colorType == PNG_COLOR_GRAY_ALPHA) {
++            processWarningOccurred(
++"A PNG gray or gray alpha image cannot have a PLTE chunk.\n" +
++"The chunk wil be ignored.");
++            return;
++        }
++
++        byte[] palette = new byte[chunkLength];
++        stream.readFully(palette);
++
++        int numEntries = chunkLength/3;
++        if (metadata.IHDR_colorType == PNG_COLOR_PALETTE) {
++            int maxEntries = 1 << metadata.IHDR_bitDepth;
++            if (numEntries > maxEntries) {
++                processWarningOccurred(
++"PLTE chunk contains too many entries for bit depth, ignoring extras.");
++                numEntries = maxEntries;
++            }
++            numEntries = Math.min(numEntries, maxEntries);
++        }
++
++        // Round array sizes up to 2^2^n
++        int paletteEntries;
++        if (numEntries > 16) {
++            paletteEntries = 256;
++        } else if (numEntries > 4) {
++            paletteEntries = 16;
++        } else if (numEntries > 2) {
++            paletteEntries = 4;
++        } else {
++            paletteEntries = 2;
++        }
++
++        metadata.PLTE_present = true;
++        metadata.PLTE_red = new byte[paletteEntries];
++        metadata.PLTE_green = new byte[paletteEntries];
++        metadata.PLTE_blue = new byte[paletteEntries];
++
++        int index = 0;
++        for (int i = 0; i < numEntries; i++) {
++            metadata.PLTE_red[i] = palette[index++];
++            metadata.PLTE_green[i] = palette[index++];
++            metadata.PLTE_blue[i] = palette[index++];
++        }
++    }
++
++    private void parse_bKGD_chunk() throws IOException {
++        if (metadata.IHDR_colorType == PNG_COLOR_PALETTE) {
++            metadata.bKGD_colorType = PNG_COLOR_PALETTE;
++            metadata.bKGD_index = stream.readUnsignedByte();
++        } else if (metadata.IHDR_colorType == PNG_COLOR_GRAY ||
++                   metadata.IHDR_colorType == PNG_COLOR_GRAY_ALPHA) {
++            metadata.bKGD_colorType = PNG_COLOR_GRAY;
++            metadata.bKGD_gray = stream.readUnsignedShort();
++        } else { // RGB or RGB_ALPHA
++            metadata.bKGD_colorType = PNG_COLOR_RGB;
++            metadata.bKGD_red = stream.readUnsignedShort();
++            metadata.bKGD_green = stream.readUnsignedShort();
++            metadata.bKGD_blue = stream.readUnsignedShort();
++        }
++
++        metadata.bKGD_present = true;
++    }
++
++    private void parse_cHRM_chunk() throws IOException {
++        metadata.cHRM_whitePointX = stream.readInt();
++        metadata.cHRM_whitePointY = stream.readInt();
++        metadata.cHRM_redX = stream.readInt();
++        metadata.cHRM_redY = stream.readInt();
++        metadata.cHRM_greenX = stream.readInt();
++        metadata.cHRM_greenY = stream.readInt();
++        metadata.cHRM_blueX = stream.readInt();
++        metadata.cHRM_blueY = stream.readInt();
++
++        metadata.cHRM_present = true;
++    }
++
++    private void parse_gAMA_chunk() throws IOException {
++        int gamma = stream.readInt();
++        metadata.gAMA_gamma = gamma;
++
++        metadata.gAMA_present = true;
++    }
++
++    private void parse_hIST_chunk(int chunkLength) throws IOException,
++        IIOException
++    {
++        if (!metadata.PLTE_present) {
++            throw new IIOException("hIST chunk without prior PLTE chunk!");
++        }
++
++        /* According to PNG specification length of
++         * hIST chunk is specified in bytes and
++         * hIST chunk consists of 2 byte elements
++         * (so we expect length is even).
++         */
++        metadata.hIST_histogram = new char[chunkLength/2];
++        stream.readFully(metadata.hIST_histogram,
++                         0, metadata.hIST_histogram.length);
++
++        metadata.hIST_present = true;
++    }
++
++    private void parse_iCCP_chunk(int chunkLength) throws IOException {
++        String keyword = readNullTerminatedString();
++        metadata.iCCP_profileName = keyword;
++
++        metadata.iCCP_compressionMethod = stream.readUnsignedByte();
++
++        byte[] compressedProfile =
++          new byte[chunkLength - keyword.length() - 2];
++        stream.readFully(compressedProfile);
++        metadata.iCCP_compressedProfile = compressedProfile;
++
++        metadata.iCCP_present = true;
++    }
++
++    private void parse_iTXt_chunk(int chunkLength) throws IOException {
++        long chunkStart = stream.getStreamPosition();
++
++        String keyword = readNullTerminatedString();
++        metadata.iTXt_keyword.add(keyword);
++
++        int compressionFlag = stream.readUnsignedByte();
++        metadata.iTXt_compressionFlag.add(Boolean.valueOf(compressionFlag == 1));
++
++        int compressionMethod = stream.readUnsignedByte();
++        metadata.iTXt_compressionMethod.add(Integer.valueOf(compressionMethod));
++
++        String languageTag = readNullTerminatedString("UTF8");
++        metadata.iTXt_languageTag.add(languageTag);
++
++        String translatedKeyword =
++            readNullTerminatedString("UTF8");
++        metadata.iTXt_translatedKeyword.add(translatedKeyword);
++
++        String text;
++        long pos = stream.getStreamPosition();
++        byte[] b = new byte[(int)(chunkStart + chunkLength - pos)];
++        stream.readFully(b);
++
++        if (compressionFlag == 1) { // Decompress the text
++            text = new String(inflate(b), "UTF8");
++        } else {
++            text = new String(b, "UTF8");
++        }
++        metadata.iTXt_text.add(text);
++    }
++
++    private void parse_pHYs_chunk() throws IOException {
++        metadata.pHYs_pixelsPerUnitXAxis = stream.readInt();
++        metadata.pHYs_pixelsPerUnitYAxis = stream.readInt();
++        metadata.pHYs_unitSpecifier = stream.readUnsignedByte();
++
++        metadata.pHYs_present = true;
++    }
++
++    private void parse_sBIT_chunk() throws IOException {
++        int colorType = metadata.IHDR_colorType;
++        if (colorType == PNG_COLOR_GRAY ||
++            colorType == PNG_COLOR_GRAY_ALPHA) {
++            metadata.sBIT_grayBits = stream.readUnsignedByte();
++        } else if (colorType == PNG_COLOR_RGB ||
++                   colorType == PNG_COLOR_PALETTE ||
++                   colorType == PNG_COLOR_RGB_ALPHA) {
++            metadata.sBIT_redBits = stream.readUnsignedByte();
++            metadata.sBIT_greenBits = stream.readUnsignedByte();
++            metadata.sBIT_blueBits = stream.readUnsignedByte();
++        }
++
++        if (colorType == PNG_COLOR_GRAY_ALPHA ||
++            colorType == PNG_COLOR_RGB_ALPHA) {
++            metadata.sBIT_alphaBits = stream.readUnsignedByte();
++        }
++
++        metadata.sBIT_colorType = colorType;
++        metadata.sBIT_present = true;
++    }
++
++    private void parse_sPLT_chunk(int chunkLength)
++        throws IOException, IIOException {
++        metadata.sPLT_paletteName = readNullTerminatedString();
++        chunkLength -= metadata.sPLT_paletteName.length() + 1;
++
++        int sampleDepth = stream.readUnsignedByte();
++        metadata.sPLT_sampleDepth = sampleDepth;
++
++        int numEntries = chunkLength/(4*(sampleDepth/8) + 2);
++        metadata.sPLT_red = new int[numEntries];
++        metadata.sPLT_green = new int[numEntries];
++        metadata.sPLT_blue = new int[numEntries];
++        metadata.sPLT_alpha = new int[numEntries];
++        metadata.sPLT_frequency = new int[numEntries];
++
++        if (sampleDepth == 8) {
++            for (int i = 0; i < numEntries; i++) {
++                metadata.sPLT_red[i] = stream.readUnsignedByte();
++                metadata.sPLT_green[i] = stream.readUnsignedByte();
++                metadata.sPLT_blue[i] = stream.readUnsignedByte();
++                metadata.sPLT_alpha[i] = stream.readUnsignedByte();
++                metadata.sPLT_frequency[i] = stream.readUnsignedShort();
++            }
++        } else if (sampleDepth == 16) {
++            for (int i = 0; i < numEntries; i++) {
++                metadata.sPLT_red[i] = stream.readUnsignedShort();
++                metadata.sPLT_green[i] = stream.readUnsignedShort();
++                metadata.sPLT_blue[i] = stream.readUnsignedShort();
++                metadata.sPLT_alpha[i] = stream.readUnsignedShort();
++                metadata.sPLT_frequency[i] = stream.readUnsignedShort();
++            }
++        } else {
++            throw new IIOException("sPLT sample depth not 8 or 16!");
++        }
++
++        metadata.sPLT_present = true;
++    }
++
++    private void parse_sRGB_chunk() throws IOException {
++        metadata.sRGB_renderingIntent = stream.readUnsignedByte();
++
++        metadata.sRGB_present = true;
++    }
++
++    private void parse_tEXt_chunk(int chunkLength) throws IOException {
++        String keyword = readNullTerminatedString();
++        metadata.tEXt_keyword.add(keyword);
++
++        byte[] b = new byte[chunkLength - keyword.length() - 1];
++        stream.readFully(b);
++        metadata.tEXt_text.add(new String(b));
++    }
++
++    private void parse_tIME_chunk() throws IOException {
++        metadata.tIME_year = stream.readUnsignedShort();
++        metadata.tIME_month = stream.readUnsignedByte();
++        metadata.tIME_day = stream.readUnsignedByte();
++        metadata.tIME_hour = stream.readUnsignedByte();
++        metadata.tIME_minute = stream.readUnsignedByte();
++        metadata.tIME_second = stream.readUnsignedByte();
++
++        metadata.tIME_present = true;
++    }
++
++    private void parse_tRNS_chunk(int chunkLength) throws IOException {
++        int colorType = metadata.IHDR_colorType;
++        if (colorType == PNG_COLOR_PALETTE) {
++            if (!metadata.PLTE_present) {
++                processWarningOccurred(
++"tRNS chunk without prior PLTE chunk, ignoring it.");
++                return;
++            }
++
++            // Alpha table may have fewer entries than RGB palette
++            int maxEntries = metadata.PLTE_red.length;
++            int numEntries = chunkLength;
++            if (numEntries > maxEntries) {
++                processWarningOccurred(
++"tRNS chunk has more entries than prior PLTE chunk, ignoring extras.");
++                numEntries = maxEntries;
++            }
++            metadata.tRNS_alpha = new byte[numEntries];
++            metadata.tRNS_colorType = PNG_COLOR_PALETTE;
++            stream.read(metadata.tRNS_alpha, 0, numEntries);
++            stream.skipBytes(chunkLength - numEntries);
++        } else if (colorType == PNG_COLOR_GRAY) {
++            if (chunkLength != 2) {
++                processWarningOccurred(
++"tRNS chunk for gray image must have length 2, ignoring chunk.");
++                stream.skipBytes(chunkLength);
++                return;
++            }
++            metadata.tRNS_gray = stream.readUnsignedShort();
++            metadata.tRNS_colorType = PNG_COLOR_GRAY;
++        } else if (colorType == PNG_COLOR_RGB) {
++            if (chunkLength != 6) {
++                processWarningOccurred(
++"tRNS chunk for RGB image must have length 6, ignoring chunk.");
++                stream.skipBytes(chunkLength);
++                return;
++            }
++            metadata.tRNS_red = stream.readUnsignedShort();
++            metadata.tRNS_green = stream.readUnsignedShort();
++            metadata.tRNS_blue = stream.readUnsignedShort();
++            metadata.tRNS_colorType = PNG_COLOR_RGB;
++        } else {
++            processWarningOccurred(
++"Gray+Alpha and RGBS images may not have a tRNS chunk, ignoring it.");
++            return;
++        }
++
++        metadata.tRNS_present = true;
++    }
++
++    private static byte[] inflate(byte[] b) throws IOException {
++        InputStream bais = new ByteArrayInputStream(b);
++        InputStream iis = new InflaterInputStream(bais);
++        ByteArrayOutputStream baos = new ByteArrayOutputStream();
++
++        int c;
++        try {
++            while ((c = iis.read()) != -1) {
++                baos.write(c);
++            }
++        } finally {
++            iis.close();
++        }
++        return baos.toByteArray();
++    }
++
++    private void parse_zTXt_chunk(int chunkLength) throws IOException {
++        String keyword = readNullTerminatedString();
++        metadata.zTXt_keyword.add(keyword);
++
++        int method = stream.readUnsignedByte();
++        metadata.zTXt_compressionMethod.add(new Integer(method));
++
++        byte[] b = new byte[chunkLength - keyword.length() - 2];
++        stream.readFully(b);
++        metadata.zTXt_text.add(new String(inflate(b)));
++    }
++
++    private void readMetadata() throws IIOException {
++        if (gotMetadata) {
++            return;
++        }
++
++        readHeader();
++
++        /*
++         * Optimization: We can skip the remaining metadata if the
++         * ignoreMetadata flag is set, and only if this is not a palette
++         * image (in that case, we need to read the metadata to get the
++         * tRNS chunk, which is needed for the getImageTypes() method).
++         */
++        int colorType = metadata.IHDR_colorType;
++        if (ignoreMetadata && colorType != PNG_COLOR_PALETTE) {
++            try {
++                while (true) {
++                    int chunkLength = stream.readInt();
++                    int chunkType = stream.readInt();
++
++                    if (chunkType == IDAT_TYPE) {
++                        // We've reached the image data
++                        stream.skipBytes(-8);
++                        imageStartPosition = stream.getStreamPosition();
++                        break;
++                    } else {
++                        // Skip the chunk plus the 4 CRC bytes that follow
++                        stream.skipBytes(chunkLength + 4);
++                    }
++                }
++            } catch (IOException e) {
++                throw new IIOException("Error skipping PNG metadata", e);
++            }
++
++            gotMetadata = true;
++            return;
++        }
++
++        try {
++            loop: while (true) {
++                int chunkLength = stream.readInt();
++                int chunkType = stream.readInt();
++
++                switch (chunkType) {
++                case IDAT_TYPE:
++                    // If chunk type is 'IDAT', we've reached the image data.
++                    stream.skipBytes(-8);
++                    imageStartPosition = stream.getStreamPosition();
++                    break loop;
++                case PLTE_TYPE:
++                    parse_PLTE_chunk(chunkLength);
++                    break;
++                case bKGD_TYPE:
++                    parse_bKGD_chunk();
++                    break;
++                case cHRM_TYPE:
++                    parse_cHRM_chunk();
++                    break;
++                case gAMA_TYPE:
++                    parse_gAMA_chunk();
++                    break;
++                case hIST_TYPE:
++                    parse_hIST_chunk(chunkLength);
++                    break;
++                case iCCP_TYPE:
++                    parse_iCCP_chunk(chunkLength);
++                    break;
++                case iTXt_TYPE:
++                    parse_iTXt_chunk(chunkLength);
++                    break;
++                case pHYs_TYPE:
++                    parse_pHYs_chunk();
++                    break;
++                case sBIT_TYPE:
++                    parse_sBIT_chunk();
++                    break;
++                case sPLT_TYPE:
++                    parse_sPLT_chunk(chunkLength);
++                    break;
++                case sRGB_TYPE:
++                    parse_sRGB_chunk();
++                    break;
++                case tEXt_TYPE:
++                    parse_tEXt_chunk(chunkLength);
++                    break;
++                case tIME_TYPE:
++                    parse_tIME_chunk();
++                    break;
++                case tRNS_TYPE:
++                    parse_tRNS_chunk(chunkLength);
++                    break;
++                case zTXt_TYPE:
++                    parse_zTXt_chunk(chunkLength);
++                    break;
++                default:
++                    // Read an unknown chunk
++                    byte[] b = new byte[chunkLength];
++                    stream.readFully(b);
++
++                    StringBuilder chunkName = new StringBuilder(4);
++                    chunkName.append((char)(chunkType >>> 24));
++                    chunkName.append((char)((chunkType >> 16) & 0xff));
++                    chunkName.append((char)((chunkType >> 8) & 0xff));
++                    chunkName.append((char)(chunkType & 0xff));
++
++                    int ancillaryBit = chunkType >>> 28;
++                    if (ancillaryBit == 0) {
++                        processWarningOccurred(
++"Encountered unknown chunk with critical bit set!");
++                    }
++
++                    metadata.unknownChunkType.add(chunkName.toString());
++                    metadata.unknownChunkData.add(b);
++                    break;
++                }
++
++                int chunkCRC = stream.readInt();
++                stream.flushBefore(stream.getStreamPosition());
++            }
++        } catch (IOException e) {
++            throw new IIOException("Error reading PNG metadata", e);
++        }
++
++        gotMetadata = true;
++    }
++
++    // Data filtering methods
++
++    private static void decodeSubFilter(byte[] curr, int coff, int count,
++                                        int bpp) {
++        for (int i = bpp; i < count; i++) {
++            int val;
++
++            val = curr[i + coff] & 0xff;
++            val += curr[i + coff - bpp] & 0xff;
++
++            curr[i + coff] = (byte)val;
++        }
++    }
++
++    private static void decodeUpFilter(byte[] curr, int coff,
++                                       byte[] prev, int poff,
++                                       int count) {
++        for (int i = 0; i < count; i++) {
++            int raw = curr[i + coff] & 0xff;
++            int prior = prev[i + poff] & 0xff;
++
++            curr[i + coff] = (byte)(raw + prior);
++        }
++    }
++
++    private static void decodeAverageFilter(byte[] curr, int coff,
++                                            byte[] prev, int poff,
++                                            int count, int bpp) {
++        int raw, priorPixel, priorRow;
++
++        for (int i = 0; i < bpp; i++) {
++            raw = curr[i + coff] & 0xff;
++            priorRow = prev[i + poff] & 0xff;
++
++            curr[i + coff] = (byte)(raw + priorRow/2);
++        }
++
++        for (int i = bpp; i < count; i++) {
++            raw = curr[i + coff] & 0xff;
++            priorPixel = curr[i + coff - bpp] & 0xff;
++            priorRow = prev[i + poff] & 0xff;
++
++            curr[i + coff] = (byte)(raw + (priorPixel + priorRow)/2);
++        }
++    }
++
++    private static int paethPredictor(int a, int b, int c) {
++        int p = a + b - c;
++        int pa = Math.abs(p - a);
++        int pb = Math.abs(p - b);
++        int pc = Math.abs(p - c);
++
++        if ((pa <= pb) && (pa <= pc)) {
++            return a;
++        } else if (pb <= pc) {
++            return b;
++        } else {
++            return c;
++        }
++    }
++
++    private static void decodePaethFilter(byte[] curr, int coff,
++                                          byte[] prev, int poff,
++                                          int count, int bpp) {
++        int raw, priorPixel, priorRow, priorRowPixel;
++
++        for (int i = 0; i < bpp; i++) {
++            raw = curr[i + coff] & 0xff;
++            priorRow = prev[i + poff] & 0xff;
++
++            curr[i + coff] = (byte)(raw + priorRow);
++        }
++
++        for (int i = bpp; i < count; i++) {
++            raw = curr[i + coff] & 0xff;
++            priorPixel = curr[i + coff - bpp] & 0xff;
++            priorRow = prev[i + poff] & 0xff;
++            priorRowPixel = prev[i + poff - bpp] & 0xff;
++
++            curr[i + coff] = (byte)(raw + paethPredictor(priorPixel,
++                                                         priorRow,
++                                                         priorRowPixel));
++        }
++    }
++
++    private static final int[][] bandOffsets = {
++        null,
++        { 0 }, // G
++        { 0, 1 }, // GA in GA order
++        { 0, 1, 2 }, // RGB in RGB order
++        { 0, 1, 2, 3 } // RGBA in RGBA order
++    };
++
++    private WritableRaster createRaster(int width, int height, int bands,
++                                        int scanlineStride,
++                                        int bitDepth) {
++
++        DataBuffer dataBuffer;
++        WritableRaster ras = null;
++        Point origin = new Point(0, 0);
++        if ((bitDepth < 8) && (bands == 1)) {
++            dataBuffer = new DataBufferByte(height*scanlineStride);
++            ras = Raster.createPackedRaster(dataBuffer,
++                                            width, height,
++                                            bitDepth,
++                                            origin);
++        } else if (bitDepth <= 8) {
++            dataBuffer = new DataBufferByte(height*scanlineStride);
++            ras = Raster.createInterleavedRaster(dataBuffer,
++                                                 width, height,
++                                                 scanlineStride,
++                                                 bands,
++                                                 bandOffsets[bands],
++                                                 origin);
++        } else {
++            dataBuffer = new DataBufferUShort(height*scanlineStride);
++            ras = Raster.createInterleavedRaster(dataBuffer,
++                                                 width, height,
++                                                 scanlineStride,
++                                                 bands,
++                                                 bandOffsets[bands],
++                                                 origin);
++        }
++
++        return ras;
++    }
++
++    private void skipPass(int passWidth, int passHeight)
++        throws IOException, IIOException  {
++        if ((passWidth == 0) || (passHeight == 0)) {
++            return;
++        }
++
++        int inputBands = inputBandsForColorType[metadata.IHDR_colorType];
++        int bytesPerRow = (inputBands*passWidth*metadata.IHDR_bitDepth + 7)/8;
++
++        // Read the image row-by-row
++        for (int srcY = 0; srcY < passHeight; srcY++) {
++            // Skip filter byte and the remaining row bytes
++            pixelStream.skipBytes(1 + bytesPerRow);
++
++            // If read has been aborted, just return
++            // processReadAborted will be called later
++            if (abortRequested()) {
++                return;
++            }
++        }
++    }
++
++    private void updateImageProgress(int newPixels) {
++        pixelsDone += newPixels;
++        processImageProgress(100.0F*pixelsDone/totalPixels);
++    }
++
++    private void decodePass(int passNum,
++                            int xStart, int yStart,
++                            int xStep, int yStep,
++                            int passWidth, int passHeight) throws IOException {
++
++        if ((passWidth == 0) || (passHeight == 0)) {
++            return;
++        }
++
++        WritableRaster imRas = theImage.getWritableTile(0, 0);
++        int dstMinX = imRas.getMinX();
++        int dstMaxX = dstMinX + imRas.getWidth() - 1;
++        int dstMinY = imRas.getMinY();
++        int dstMaxY = dstMinY + imRas.getHeight() - 1;
++
++        // Determine which pixels will be updated in this pass
++        int[] vals =
++          ReaderUtil.computeUpdatedPixels(sourceRegion,
++                                          destinationOffset,
++                                          dstMinX, dstMinY,
++                                          dstMaxX, dstMaxY,
++                                          sourceXSubsampling,
++                                          sourceYSubsampling,
++                                          xStart, yStart,
++                                          passWidth, passHeight,
++                                          xStep, yStep);
++        int updateMinX = vals[0];
++        int updateMinY = vals[1];
++        int updateWidth = vals[2];
++        int updateXStep = vals[4];
++        int updateYStep = vals[5];
++
++        int bitDepth = metadata.IHDR_bitDepth;
++        int inputBands = inputBandsForColorType[metadata.IHDR_colorType];
++        int bytesPerPixel = (bitDepth == 16) ? 2 : 1;
++        bytesPerPixel *= inputBands;
++
++        int bytesPerRow = (inputBands*passWidth*bitDepth + 7)/8;
++        int eltsPerRow = (bitDepth == 16) ? bytesPerRow/2 : bytesPerRow;
++
++        // If no pixels need updating, just skip the input data
++        if (updateWidth == 0) {
++            for (int srcY = 0; srcY < passHeight; srcY++) {
++                // Update count of pixels read
++                updateImageProgress(passWidth);
++                // Skip filter byte and the remaining row bytes
++                pixelStream.skipBytes(1 + bytesPerRow);
++            }
++            return;
++        }
++
++        // Backwards map from destination pixels
++        // (dstX = updateMinX + k*updateXStep)
++        // to source pixels (sourceX), and then
++        // to offset and skip in passRow (srcX and srcXStep)
++        int sourceX =
++            (updateMinX - destinationOffset.x)*sourceXSubsampling +
++            sourceRegion.x;
++        int srcX = (sourceX - xStart)/xStep;
++
++        // Compute the step factor in the source
++        int srcXStep = updateXStep*sourceXSubsampling/xStep;
++
++        byte[] byteData = null;
++        short[] shortData = null;
++        byte[] curr = new byte[bytesPerRow];
++        byte[] prior = new byte[bytesPerRow];
++
++        // Create a 1-row tall Raster to hold the data
++        WritableRaster passRow = createRaster(passWidth, 1, inputBands,
++                                              eltsPerRow,
++                                              bitDepth);
++
++        // Create an array suitable for holding one pixel
++        int[] ps = passRow.getPixel(0, 0, (int[])null);
++
++        DataBuffer dataBuffer = passRow.getDataBuffer();
++        int type = dataBuffer.getDataType();
++        if (type == DataBuffer.TYPE_BYTE) {
++            byteData = ((DataBufferByte)dataBuffer).getData();
++        } else {
++            shortData = ((DataBufferUShort)dataBuffer).getData();
++        }
++
++        processPassStarted(theImage,
++                           passNum,
++                           sourceMinProgressivePass,
++                           sourceMaxProgressivePass,
++                           updateMinX, updateMinY,
++                           updateXStep, updateYStep,
++                           destinationBands);
++
++        // Handle source and destination bands
++        if (sourceBands != null) {
++            passRow = passRow.createWritableChild(0, 0,
++                                                  passRow.getWidth(), 1,
++                                                  0, 0,
++                                                  sourceBands);
++        }
++        if (destinationBands != null) {
++            imRas = imRas.createWritableChild(0, 0,
++                                              imRas.getWidth(),
++                                              imRas.getHeight(),
++                                              0, 0,
++                                              destinationBands);
++        }
++
++        // Determine if all of the relevant output bands have the
++        // same bit depth as the source data
++        boolean adjustBitDepths = false;
++        int[] outputSampleSize = imRas.getSampleModel().getSampleSize();
++        int numBands = outputSampleSize.length;
++        for (int b = 0; b < numBands; b++) {
++            if (outputSampleSize[b] != bitDepth) {
++                adjustBitDepths = true;
++                break;
++            }
++        }
++
++        // If the bit depths differ, create a lookup table per band to perform
++        // the conversion
++        int[][] scale = null;
++        if (adjustBitDepths) {
++            int maxInSample = (1 << bitDepth) - 1;
++            int halfMaxInSample = maxInSample/2;
++            scale = new int[numBands][];
++            for (int b = 0; b < numBands; b++) {
++                int maxOutSample = (1 << outputSampleSize[b]) - 1;
++                scale[b] = new int[maxInSample + 1];
++                for (int s = 0; s <= maxInSample; s++) {
++                    scale[b][s] =
++                        (s*maxOutSample + halfMaxInSample)/maxInSample;
++                }
++            }
++        }
++
++        // Limit passRow to relevant area for the case where we
++        // will can setRect to copy a contiguous span
++        boolean useSetRect = srcXStep == 1 &&
++            updateXStep == 1 &&
++            !adjustBitDepths &&
++            (imRas instanceof ByteInterleavedRaster);
++
++        if (useSetRect) {
++            passRow = passRow.createWritableChild(srcX, 0,
++                                                  updateWidth, 1,
++                                                  0, 0,
++                                                  null);
++        }
++
++        // Decode the (sub)image row-by-row
++        for (int srcY = 0; srcY < passHeight; srcY++) {
++            // Update count of pixels read
++            updateImageProgress(passWidth);
++
++            // Read the filter type byte and a row of data
++            int filter = pixelStream.read();
++            try {
++                // Swap curr and prior
++                byte[] tmp = prior;
++                prior = curr;
++                curr = tmp;
++
++                pixelStream.readFully(curr, 0, bytesPerRow);
++            } catch (java.util.zip.ZipException ze) {
++                // TODO - throw a more meaningful exception
++                throw ze;
++            }
++
++            switch (filter) {
++            case PNG_FILTER_NONE:
++                break;
++            case PNG_FILTER_SUB:
++                decodeSubFilter(curr, 0, bytesPerRow, bytesPerPixel);
++                break;
++            case PNG_FILTER_UP:
++                decodeUpFilter(curr, 0, prior, 0, bytesPerRow);
++                break;
++            case PNG_FILTER_AVERAGE:
++                decodeAverageFilter(curr, 0, prior, 0, bytesPerRow,
++                                    bytesPerPixel);
++                break;
++            case PNG_FILTER_PAETH:
++                decodePaethFilter(curr, 0, prior, 0, bytesPerRow,
++                                  bytesPerPixel);
++                break;
++            default:
++                throw new IIOException("Unknown row filter type (= " +
++                                       filter + ")!");
++            }
++
++            // Copy data into passRow byte by byte
++            if (bitDepth < 16) {
++                System.arraycopy(curr, 0, byteData, 0, bytesPerRow);
++            } else {
++                int idx = 0;
++                for (int j = 0; j < eltsPerRow; j++) {
++                    shortData[j] =
++                        (short)((curr[idx] << 8) | (curr[idx + 1] & 0xff));
++                    idx += 2;
++                }
++            }
++
++            // True Y position in source
++            int sourceY = srcY*yStep + yStart;
++            if ((sourceY >= sourceRegion.y) &&
++                (sourceY < sourceRegion.y + sourceRegion.height) &&
++                (((sourceY - sourceRegion.y) %
++                  sourceYSubsampling) == 0)) {
++
++                int dstY = destinationOffset.y +
++                    (sourceY - sourceRegion.y)/sourceYSubsampling;
++                if (dstY < dstMinY) {
++                    continue;
++                }
++                if (dstY > dstMaxY) {
++                    break;
++                }
++
++                if (useSetRect) {
++                    imRas.setRect(updateMinX, dstY, passRow);
++                } else {
++                    int newSrcX = srcX;
++
++                    for (int dstX = updateMinX;
++                         dstX < updateMinX + updateWidth;
++                         dstX += updateXStep) {
++
++                        passRow.getPixel(newSrcX, 0, ps);
++                        if (adjustBitDepths) {
++                            for (int b = 0; b < numBands; b++) {
++                                ps[b] = scale[b][ps[b]];
++                            }
++                        }
++                        imRas.setPixel(dstX, dstY, ps);
++                        newSrcX += srcXStep;
++                    }
++                }
++
++                processImageUpdate(theImage,
++                                   updateMinX, dstY,
++                                   updateWidth, 1,
++                                   updateXStep, updateYStep,
++                                   destinationBands);
++
++                // If read has been aborted, just return
++                // processReadAborted will be called later
++                if (abortRequested()) {
++                    return;
++                }
++            }
++        }
++
++        processPassComplete(theImage);
++    }
++
++    private void decodeImage()
++        throws IOException, IIOException  {
++        int width = metadata.IHDR_width;
++        int height = metadata.IHDR_height;
++
++        this.pixelsDone = 0;
++        this.totalPixels = width*height;
++
++        clearAbortRequest();
++
++        if (metadata.IHDR_interlaceMethod == 0) {
++            decodePass(0, 0, 0, 1, 1, width, height);
++        } else {
++            for (int i = 0; i <= sourceMaxProgressivePass; i++) {
++                int XOffset = adam7XOffset[i];
++                int YOffset = adam7YOffset[i];
++                int XSubsampling = adam7XSubsampling[i];
++                int YSubsampling = adam7YSubsampling[i];
++                int xbump = adam7XSubsampling[i + 1] - 1;
++                int ybump = adam7YSubsampling[i + 1] - 1;
++
++                if (i >= sourceMinProgressivePass) {
++                    decodePass(i,
++                               XOffset,
++                               YOffset,
++                               XSubsampling,
++                               YSubsampling,
++                               (width + xbump)/XSubsampling,
++                               (height + ybump)/YSubsampling);
++                } else {
++                    skipPass((width + xbump)/XSubsampling,
++                             (height + ybump)/YSubsampling);
++                }
++
++                // If read has been aborted, just return
++                // processReadAborted will be called later
++                if (abortRequested()) {
++                    return;
++                }
++            }
++        }
++    }
++
++    private void readImage(ImageReadParam param) throws IIOException {
++        readMetadata();
++
++        int width = metadata.IHDR_width;
++        int height = metadata.IHDR_height;
++
++        // Init default values
++        sourceXSubsampling = 1;
++        sourceYSubsampling = 1;
++        sourceMinProgressivePass = 0;
++        sourceMaxProgressivePass = 6;
++        sourceBands = null;
++        destinationBands = null;
++        destinationOffset = new Point(0, 0);
++
++        // If an ImageReadParam is available, get values from it
++        if (param != null) {
++            sourceXSubsampling = param.getSourceXSubsampling();
++            sourceYSubsampling = param.getSourceYSubsampling();
++
++            sourceMinProgressivePass =
++                Math.max(param.getSourceMinProgressivePass(), 0);
++            sourceMaxProgressivePass =
++                Math.min(param.getSourceMaxProgressivePass(), 6);
++
++            sourceBands = param.getSourceBands();
++            destinationBands = param.getDestinationBands();
++            destinationOffset = param.getDestinationOffset();
++        }
++        Inflater inf = null;
++        try {
++            stream.seek(imageStartPosition);
++
++            Enumeration e = new PNGImageDataEnumeration(stream);
++            InputStream is = new SequenceInputStream(e);
++
++           /* InflaterInputStream uses an Inflater instance which consumes
++            * native (non-GC visible) resources. This is normally implicitly
++            * freed when the stream is closed. However since the
++            * InflaterInputStream wraps a client-supplied input stream,
++            * we cannot close it.
++            * But the app may depend on GC finalization to close the stream.
++            * Therefore to ensure timely freeing of native resources we
++            * explicitly create the Inflater instance and free its resources
++            * when we are done with the InflaterInputStream by calling
++            * inf.end();
++            */
++            inf = new Inflater();
++            is = new InflaterInputStream(is, inf);
++            is = new BufferedInputStream(is);
++            this.pixelStream = new DataInputStream(is);
++
++            theImage = getDestination(param,
++                                      getImageTypes(0),
++                                      width,
++                                      height);
++
++            Rectangle destRegion = new Rectangle(0, 0, 0, 0);
++            sourceRegion = new Rectangle(0, 0, 0, 0);
++            computeRegions(param, width, height,
++                           theImage,
++                           sourceRegion, destRegion);
++            destinationOffset.setLocation(destRegion.getLocation());
++
++            // At this point the header has been read and we know
++            // how many bands are in the image, so perform checking
++            // of the read param.
++            int colorType = metadata.IHDR_colorType;
++            checkReadParamBandSettings(param,
++                                       inputBandsForColorType[colorType],
++                                      theImage.getSampleModel().getNumBands());
++
++            processImageStarted(0);
++            decodeImage();
++            if (abortRequested()) {
++                processReadAborted();
++            } else {
++                processImageComplete();
++            }
++        } catch (IOException e) {
++            throw new IIOException("Error reading PNG image data", e);
++        } finally {
++            if (inf != null) {
++                inf.end();
++            }
++        }
++    }
++
++    public int getNumImages(boolean allowSearch) throws IIOException {
++        if (stream == null) {
++            throw new IllegalStateException("No input source set!");
++        }
++        if (seekForwardOnly && allowSearch) {
++            throw new IllegalStateException
++                ("seekForwardOnly and allowSearch can't both be true!");
++        }
++        return 1;
++    }
++
++    public int getWidth(int imageIndex) throws IIOException {
++        if (imageIndex != 0) {
++            throw new IndexOutOfBoundsException("imageIndex != 0!");
++        }
++
++        readHeader();
++
++        return metadata.IHDR_width;
++    }
++
++    public int getHeight(int imageIndex) throws IIOException {
++        if (imageIndex != 0) {
++            throw new IndexOutOfBoundsException("imageIndex != 0!");
++        }
++
++        readHeader();
++
++        return metadata.IHDR_height;
++    }
++
++    public Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex)
++      throws IIOException
++    {
++        if (imageIndex != 0) {
++            throw new IndexOutOfBoundsException("imageIndex != 0!");
++        }
++
++        readHeader();
++
++        ArrayList<ImageTypeSpecifier> l =
++            new ArrayList<ImageTypeSpecifier>(1);
++
++        ColorSpace rgb;
++        ColorSpace gray;
++        int[] bandOffsets;
++
++        int bitDepth = metadata.IHDR_bitDepth;
++        int colorType = metadata.IHDR_colorType;
++
++        int dataType;
++        if (bitDepth <= 8) {
++            dataType = DataBuffer.TYPE_BYTE;
++        } else {
++            dataType = DataBuffer.TYPE_USHORT;
++        }
++
++        switch (colorType) {
++        case PNG_COLOR_GRAY:
++            // Packed grayscale
++            l.add(ImageTypeSpecifier.createGrayscale(bitDepth,
++                                                     dataType,
++                                                     false));
++            break;
++
++        case PNG_COLOR_RGB:
++            if (bitDepth == 8) {
++                // some standard types of buffered images
++                // which can be used as destination
++                l.add(ImageTypeSpecifier.createFromBufferedImageType(
++                          BufferedImage.TYPE_3BYTE_BGR));
++
++                l.add(ImageTypeSpecifier.createFromBufferedImageType(
++                          BufferedImage.TYPE_INT_RGB));
++
++                l.add(ImageTypeSpecifier.createFromBufferedImageType(
++                          BufferedImage.TYPE_INT_BGR));
++
++            }
++            // Component R, G, B
++            rgb = ColorSpace.getInstance(ColorSpace.CS_sRGB);
++            bandOffsets = new int[3];
++            bandOffsets[0] = 0;
++            bandOffsets[1] = 1;
++            bandOffsets[2] = 2;
++            l.add(ImageTypeSpecifier.createInterleaved(rgb,
++                                                       bandOffsets,
++                                                       dataType,
++                                                       false,
++                                                       false));
++            break;
++
++        case PNG_COLOR_PALETTE:
++            readMetadata(); // Need tRNS chunk
++
++            /*
++             * The PLTE chunk spec says:
++             *
++             * The number of palette entries must not exceed the range that
++             * can be represented in the image bit depth (for example, 2^4 = 16
++             * for a bit depth of 4). It is permissible to have fewer entries
++             * than the bit depth would allow. In that case, any out-of-range
++             * pixel value found in the image data is an error.
++             *
++             * http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html#C.PLTE
++             *
++             * Consequently, the case when the palette length is smaller than
++             * 2^bitDepth is legal in the view of PNG spec.
++             *
++             * However the spec of createIndexed() method demands the exact
++             * equality of the palette lengh and number of possible palette
++             * entries (2^bitDepth).
++             *
++             * {@link javax.imageio.ImageTypeSpecifier.html#createIndexed}
++             *
++             * In order to avoid this contradiction we need to extend the
++             * palette arrays to the limit defined by the bitDepth.
++             */
++
++            int plength = 1 << bitDepth;
++
++            byte[] red = metadata.PLTE_red;
++            byte[] green = metadata.PLTE_green;
++            byte[] blue = metadata.PLTE_blue;
++
++            if (metadata.PLTE_red.length < plength) {
++                red = Arrays.copyOf(metadata.PLTE_red, plength);
++                Arrays.fill(red, metadata.PLTE_red.length, plength,
++                            metadata.PLTE_red[metadata.PLTE_red.length - 1]);
++
++                green = Arrays.copyOf(metadata.PLTE_green, plength);
++                Arrays.fill(green, metadata.PLTE_green.length, plength,
++                            metadata.PLTE_green[metadata.PLTE_green.length - 1]);
++
++                blue = Arrays.copyOf(metadata.PLTE_blue, plength);
++                Arrays.fill(blue, metadata.PLTE_blue.length, plength,
++                            metadata.PLTE_blue[metadata.PLTE_blue.length - 1]);
++
++            }
++
++            // Alpha from tRNS chunk may have fewer entries than
++            // the RGB LUTs from the PLTE chunk; if so, pad with
++            // 255.
++            byte[] alpha = null;
++            if (metadata.tRNS_present && (metadata.tRNS_alpha != null)) {
++                if (metadata.tRNS_alpha.length == red.length) {
++                    alpha = metadata.tRNS_alpha;
++                } else {
++                    alpha = Arrays.copyOf(metadata.tRNS_alpha, red.length);
++                    Arrays.fill(alpha,
++                                metadata.tRNS_alpha.length,
++                                red.length, (byte)255);
++                }
++            }
++
++            l.add(ImageTypeSpecifier.createIndexed(red, green,
++                                                   blue, alpha,
++                                                   bitDepth,
++                                                   DataBuffer.TYPE_BYTE));
++            break;
++
++        case PNG_COLOR_GRAY_ALPHA:
++            // Component G, A
++            gray = ColorSpace.getInstance(ColorSpace.CS_GRAY);
++            bandOffsets = new int[2];
++            bandOffsets[0] = 0;
++            bandOffsets[1] = 1;
++            l.add(ImageTypeSpecifier.createInterleaved(gray,
++                                                       bandOffsets,
++                                                       dataType,
++                                                       true,
++                                                       false));
++            break;
++
++        case PNG_COLOR_RGB_ALPHA:
++            if (bitDepth == 8) {
++                // some standard types of buffered images
++                // wich can be used as destination
++                l.add(ImageTypeSpecifier.createFromBufferedImageType(
++                          BufferedImage.TYPE_4BYTE_ABGR));
++
++                l.add(ImageTypeSpecifier.createFromBufferedImageType(
++                          BufferedImage.TYPE_INT_ARGB));
++            }
++
++            // Component R, G, B, A (non-premultiplied)
++            rgb = ColorSpace.getInstance(ColorSpace.CS_sRGB);
++            bandOffsets = new int[4];
++            bandOffsets[0] = 0;
++            bandOffsets[1] = 1;
++            bandOffsets[2] = 2;
++            bandOffsets[3] = 3;
++
++            l.add(ImageTypeSpecifier.createInterleaved(rgb,
++                                                       bandOffsets,
++                                                       dataType,
++                                                       true,
++                                                       false));
++            break;
++
++        default:
++            break;
++        }
++
++        return l.iterator();
++    }
++
++    /*
++     * Super class implementation uses first element
++     * of image types list as raw image type.
++     *
++     * Also, super implementation uses first element of this list
++     * as default destination type image read param does not specify
++     * anything other.
++     *
++     * However, in case of RGB and RGBA color types, raw image type
++     * produces buffered image of custom type. It causes some
++     * performance degradation of subsequent rendering operations.
++     *
++     * To resolve this contradiction we put standard image types
++     * at the first positions of image types list (to produce standard
++     * images by default) and put raw image type (which is custom)
++     * at the last position of this list.
++     *
++     * After this changes we should override getRawImageType()
++     * to return last element of image types list.
++     */
++    public ImageTypeSpecifier getRawImageType(int imageIndex)
++      throws IOException {
++
++        Iterator<ImageTypeSpecifier> types = getImageTypes(imageIndex);
++        ImageTypeSpecifier raw = null;
++        do {
++            raw = types.next();
++        } while (types.hasNext());
++        return raw;
++    }
++
++    public ImageReadParam getDefaultReadParam() {
++        return new ImageReadParam();
++    }
++
++    public IIOMetadata getStreamMetadata()
++        throws IIOException {
++        return null;
++    }
++
++    public IIOMetadata getImageMetadata(int imageIndex) throws IIOException {
++        if (imageIndex != 0) {
++            throw new IndexOutOfBoundsException("imageIndex != 0!");
++        }
++        readMetadata();
++        return metadata;
++    }
++
++    public BufferedImage read(int imageIndex, ImageReadParam param)
++        throws IIOException {
++        if (imageIndex != 0) {
++            throw new IndexOutOfBoundsException("imageIndex != 0!");
++        }
++
++        readImage(param);
++        return theImage;
++    }
++
++    public void reset() {
++        super.reset();
++        resetStreamSettings();
++    }
++
++    private void resetStreamSettings() {
++        gotHeader = false;
++        gotMetadata = false;
++        metadata = null;
++        pixelStream = null;
++    }
++}
+diff -urN openjdk.orig/jdk/src/share/classes/com/sun/imageio/plugins/png/PNGImageWriter.java openjdk/jdk/src/share/classes/com/sun/imageio/plugins/png/PNGImageWriter.java
+--- openjdk.orig/jdk/src/share/classes/com/sun/imageio/plugins/png/PNGImageWriter.java	2010-12-24 11:22:28.803101963 -0500
++++ openjdk/jdk/src/share/classes/com/sun/imageio/plugins/png/PNGImageWriter.java	2010-12-24 11:24:47.273101870 -0500
+@@ -674,13 +674,8 @@
+     private byte[] deflate(byte[] b) throws IOException {
+         ByteArrayOutputStream baos = new ByteArrayOutputStream();
+         DeflaterOutputStream dos = new DeflaterOutputStream(baos);
+-
+-        int len = b.length;
+-        for (int i = 0; i < len; i++) {
+-            dos.write((int)(0xff & b[i]));
+-        }
++        dos.write(b);
+         dos.close();
+-
+         return baos.toByteArray();
+     }
+ 
+@@ -736,7 +731,7 @@
+             cs.writeByte(compressionMethod);
+ 
+             String text = (String)textIter.next();
+-            cs.write(deflate(text.getBytes()));
++            cs.write(deflate(text.getBytes("ISO-8859-1")));
+             cs.finish();
+         }
+     }
+diff -urN openjdk.orig/jdk/src/share/classes/com/sun/imageio/plugins/png/PNGMetadata.java openjdk/jdk/src/share/classes/com/sun/imageio/plugins/png/PNGMetadata.java
+--- openjdk.orig/jdk/src/share/classes/com/sun/imageio/plugins/png/PNGMetadata.java	2010-12-24 11:22:28.803101963 -0500
++++ openjdk/jdk/src/share/classes/com/sun/imageio/plugins/png/PNGMetadata.java	2010-12-24 11:24:47.274101892 -0500
+@@ -213,8 +213,8 @@
+     public int sRGB_renderingIntent;
+ 
+     // tEXt chunk
+-    public ArrayList tEXt_keyword = new ArrayList(); // 1-79 char Strings
+-    public ArrayList tEXt_text = new ArrayList(); // Strings
++    public ArrayList<String> tEXt_keyword = new ArrayList<String>(); // 1-79 characters
++    public ArrayList<String> tEXt_text = new ArrayList<String>();
+ 
+     // tIME chunk
+     public boolean tIME_present;
+@@ -237,13 +237,13 @@
+     public int tRNS_blue;
+ 
+     // zTXt chunk
+-    public ArrayList zTXt_keyword = new ArrayList(); // Strings
+-    public ArrayList zTXt_compressionMethod = new ArrayList(); // Integers
+-    public ArrayList zTXt_text = new ArrayList(); // Strings
++    public ArrayList<String> zTXt_keyword = new ArrayList<String>();
++    public ArrayList<Integer> zTXt_compressionMethod = new ArrayList<Integer>();
++    public ArrayList<String> zTXt_text = new ArrayList<String>();
+ 
+     // Unknown chunks
+-    public ArrayList unknownChunkType = new ArrayList(); // Strings
+-    public ArrayList unknownChunkData = new ArrayList(); // byte arrays
++    public ArrayList<String> unknownChunkType = new ArrayList<String>();
++    public ArrayList<byte[]> unknownChunkData = new ArrayList<byte[]>();
+ 
+     public PNGMetadata() {
+         super(true,
+@@ -428,21 +428,14 @@
+         return false;
+     }
+ 
+-    private ArrayList cloneBytesArrayList(ArrayList in) {
++    private ArrayList<byte[]> cloneBytesArrayList(ArrayList<byte[]> in) {
+         if (in == null) {
+             return null;
+         } else {
+-            ArrayList list = new ArrayList(in.size());
+-            Iterator iter = in.iterator();
+-            while (iter.hasNext()) {
+-                Object o = iter.next();
+-                if (o == null) {
+-                    list.add(null);
+-                } else {
+-                    list.add(((byte[])o).clone());
+-                }
++            ArrayList<byte[]> list = new ArrayList<byte[]>(in.size());
++            for (byte[] b: in) {
++                list.add((b == null) ? null : (byte[])b.clone());
+             }
+-
+             return list;
+         }
+     }
+@@ -2000,14 +1993,14 @@
+         sBIT_present = false;
+         sPLT_present = false;
+         sRGB_present = false;
+-        tEXt_keyword = new ArrayList();
+-        tEXt_text = new ArrayList();
++        tEXt_keyword = new ArrayList<String>();
++        tEXt_text = new ArrayList<String>();
+         tIME_present = false;
+         tRNS_present = false;
+-        zTXt_keyword = new ArrayList();
+-        zTXt_compressionMethod = new ArrayList();
+-        zTXt_text = new ArrayList();
+-        unknownChunkType = new ArrayList();
+-        unknownChunkData = new ArrayList();
++        zTXt_keyword = new ArrayList<String>();
++        zTXt_compressionMethod = new ArrayList<Integer>();
++        zTXt_text = new ArrayList<String>();
++        unknownChunkType = new ArrayList<String>();
++        unknownChunkData = new ArrayList<byte[]>();
+     }
+ }
+diff -urN openjdk.orig/jdk/src/share/classes/com/sun/imageio/plugins/png/PNGMetadata.java.orig openjdk/jdk/src/share/classes/com/sun/imageio/plugins/png/PNGMetadata.java.orig
+--- openjdk.orig/jdk/src/share/classes/com/sun/imageio/plugins/png/PNGMetadata.java.orig	1969-12-31 19:00:00.000000000 -0500
++++ openjdk/jdk/src/share/classes/com/sun/imageio/plugins/png/PNGMetadata.java.orig	2010-12-24 11:22:50.401102014 -0500
+@@ -0,0 +1,2013 @@
++/*
++ * Copyright (c) 2000, 2001, Oracle and/or its affiliates. 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.  Oracle designates this
++ * particular file as subject to the "Classpath" exception as provided
++ * by Oracle in the LICENSE file that accompanied this code.
++ *
++ * 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
++ * or visit www.oracle.com if you need additional information or have any
++ * questions.
++ */
++
++package com.sun.imageio.plugins.png;
++
++import java.awt.image.ColorModel;
++import java.awt.image.IndexColorModel;
++import java.awt.image.SampleModel;
++import java.util.ArrayList;
++import java.util.Iterator;
++import java.util.StringTokenizer;
++import javax.imageio.ImageTypeSpecifier;
++import javax.imageio.metadata.IIOInvalidTreeException;
++import javax.imageio.metadata.IIOMetadata;
++import javax.imageio.metadata.IIOMetadataFormat;
++import javax.imageio.metadata.IIOMetadataFormatImpl;
++import javax.imageio.metadata.IIOMetadataNode;
++import org.w3c.dom.Node;
++
++/**
++ */
++public class PNGMetadata extends IIOMetadata implements Cloneable {
++
++    // package scope
++    public static final String
++        nativeMetadataFormatName = "javax_imageio_png_1.0";
++
++    protected static final String nativeMetadataFormatClassName
++        = "com.sun.imageio.plugins.png.PNGMetadataFormat";
++
++    // Color types for IHDR chunk
++    public static final String[] IHDR_colorTypeNames = {
++        "Grayscale", null, "RGB", "Palette",
++        "GrayAlpha", null, "RGBAlpha"
++    };
++
++    public static final int[] IHDR_numChannels = {
++        1, 0, 3, 3, 2, 0, 4
++    };
++
++    // Bit depths for IHDR chunk
++    public static final String[] IHDR_bitDepths = {
++        "1", "2", "4", "8", "16"
++    };
++
++    // Compression methods for IHDR chunk
++    public static final String[] IHDR_compressionMethodNames = {
++        "deflate"
++    };
++
++    // Filter methods for IHDR chunk
++    public static final String[] IHDR_filterMethodNames = {
++        "adaptive"
++    };
++
++    // Interlace methods for IHDR chunk
++    public static final String[] IHDR_interlaceMethodNames = {
++        "none", "adam7"
++    };
++
++    // Compression methods for iCCP chunk
++    public static final String[] iCCP_compressionMethodNames = {
++        "deflate"
++    };
++
++    // Compression methods for zTXt chunk
++    public static final String[] zTXt_compressionMethodNames = {
++        "deflate"
++    };
++
++    // "Unknown" unit for pHYs chunk
++    public static final int PHYS_UNIT_UNKNOWN = 0;
++
++    // "Meter" unit for pHYs chunk
++    public static final int PHYS_UNIT_METER = 1;
++
++    // Unit specifiers for pHYs chunk
++    public static final String[] unitSpecifierNames = {
++        "unknown", "meter"
++    };
++
++    // Rendering intents for sRGB chunk
++    public static final String[] renderingIntentNames = {
++        "Perceptual", // 0
++        "Relative colorimetric", // 1
++        "Saturation", // 2
++        "Absolute colorimetric" // 3
++
++    };
++
++    // Color space types for Chroma->ColorSpaceType node
++    public static final String[] colorSpaceTypeNames = {
++        "GRAY", null, "RGB", "RGB",
++        "GRAY", null, "RGB"
++    };
++
++    // IHDR chunk
++    public boolean IHDR_present;
++    public int IHDR_width;
++    public int IHDR_height;
++    public int IHDR_bitDepth;
++    public int IHDR_colorType;
++    public int IHDR_compressionMethod;
++    public int IHDR_filterMethod;
++    public int IHDR_interlaceMethod; // 0 == none, 1 == adam7
++
++    // PLTE chunk
++    public boolean PLTE_present;
++    public byte[] PLTE_red;
++    public byte[] PLTE_green;
++    public byte[] PLTE_blue;
++
++    // If non-null, used to reorder palette entries during encoding in
++    // order to minimize the size of the tRNS chunk.  Thus an index of
++    // 'i' in the source should be encoded as index 'PLTE_order[i]'.
++    // PLTE_order will be null unless 'initialize' is called with an
++    // IndexColorModel image type.
++    public int[] PLTE_order = null;
++
++    // bKGD chunk
++    // If external (non-PNG sourced) data has red = green = blue,
++    // always store it as gray and promote when writing
++    public boolean bKGD_present;
++    public int bKGD_colorType; // PNG_COLOR_GRAY, _RGB, or _PALETTE
++    public int bKGD_index;
++    public int bKGD_gray;
++    public int bKGD_red;
++    public int bKGD_green;
++    public int bKGD_blue;
++
++    // cHRM chunk
++    public boolean cHRM_present;
++    public int cHRM_whitePointX;
++    public int cHRM_whitePointY;
++    public int cHRM_redX;
++    public int cHRM_redY;
++    public int cHRM_greenX;
++    public int cHRM_greenY;
++    public int cHRM_blueX;
++    public int cHRM_blueY;
++
++    // gAMA chunk
++    public boolean gAMA_present;
++    public int gAMA_gamma;
++
++    // hIST chunk
++    public boolean hIST_present;
++    public char[] hIST_histogram;
++
++    // iCCP chunk
++    public boolean iCCP_present;
++    public String iCCP_profileName;
++    public int iCCP_compressionMethod;
++    public byte[] iCCP_compressedProfile;
++
++    // iTXt chunk
++    public ArrayList<String> iTXt_keyword = new ArrayList<String>();
++    public ArrayList<Boolean> iTXt_compressionFlag = new ArrayList<Boolean>();
++    public ArrayList<Integer> iTXt_compressionMethod = new ArrayList<Integer>();
++    public ArrayList<String> iTXt_languageTag = new ArrayList<String>();
++    public ArrayList<String> iTXt_translatedKeyword = new ArrayList<String>();
++    public ArrayList<String> iTXt_text = new ArrayList<String>();
++
++    // pHYs chunk
++    public boolean pHYs_present;
++    public int pHYs_pixelsPerUnitXAxis;
++    public int pHYs_pixelsPerUnitYAxis;
++    public int pHYs_unitSpecifier; // 0 == unknown, 1 == meter
++
++    // sBIT chunk
++    public boolean sBIT_present;
++    public int sBIT_colorType; // PNG_COLOR_GRAY, _GRAY_ALPHA, _RGB, _RGB_ALPHA
++    public int sBIT_grayBits;
++    public int sBIT_redBits;
++    public int sBIT_greenBits;
++    public int sBIT_blueBits;
++    public int sBIT_alphaBits;
++
++    // sPLT chunk
++    public boolean sPLT_present;
++    public String sPLT_paletteName; // 1-79 characters
++    public int sPLT_sampleDepth; // 8 or 16
++    public int[] sPLT_red;
++    public int[] sPLT_green;
++    public int[] sPLT_blue;
++    public int[] sPLT_alpha;
++    public int[] sPLT_frequency;
++
++    // sRGB chunk
++    public boolean sRGB_present;
++    public int sRGB_renderingIntent;
++
++    // tEXt chunk
++    public ArrayList tEXt_keyword = new ArrayList(); // 1-79 char Strings
++    public ArrayList tEXt_text = new ArrayList(); // Strings
++
++    // tIME chunk
++    public boolean tIME_present;
++    public int tIME_year;
++    public int tIME_month;
++    public int tIME_day;
++    public int tIME_hour;
++    public int tIME_minute;
++    public int tIME_second;
++
++    // tRNS chunk
++    // If external (non-PNG sourced) data has red = green = blue,
++    // always store it as gray and promote when writing
++    public boolean tRNS_present;
++    public int tRNS_colorType; // PNG_COLOR_GRAY, _RGB, or _PALETTE
++    public byte[] tRNS_alpha; // May have fewer entries than PLTE_red, etc.
++    public int tRNS_gray;
++    public int tRNS_red;
++    public int tRNS_green;
++    public int tRNS_blue;
++
++    // zTXt chunk
++    public ArrayList zTXt_keyword = new ArrayList(); // Strings
++    public ArrayList zTXt_compressionMethod = new ArrayList(); // Integers
++    public ArrayList zTXt_text = new ArrayList(); // Strings
++
++    // Unknown chunks
++    public ArrayList unknownChunkType = new ArrayList(); // Strings
++    public ArrayList unknownChunkData = new ArrayList(); // byte arrays
++
++    public PNGMetadata() {
++        super(true,
++              nativeMetadataFormatName,
++              nativeMetadataFormatClassName,
++              null, null);
++    }
++
++    public PNGMetadata(IIOMetadata metadata) {
++        // TODO -- implement
++    }
++
++    /**
++     * Sets the IHDR_bitDepth and IHDR_colorType variables.
++     * The <code>numBands</code> parameter is necessary since
++     * we may only be writing a subset of the image bands.
++     */
++    public void initialize(ImageTypeSpecifier imageType, int numBands) {
++        ColorModel colorModel = imageType.getColorModel();
++        SampleModel sampleModel = imageType.getSampleModel();
++
++        // Initialize IHDR_bitDepth
++        int[] sampleSize = sampleModel.getSampleSize();
++        int bitDepth = sampleSize[0];
++        // Choose max bit depth over all channels
++        // Fixes bug 4413109
++        for (int i = 1; i < sampleSize.length; i++) {
++            if (sampleSize[i] > bitDepth) {
++                bitDepth = sampleSize[i];
++            }
++        }
++        // Multi-channel images must have a bit depth of 8 or 16
++        if (sampleSize.length > 1 && bitDepth < 8) {
++            bitDepth = 8;
++        }
++
++        // Round bit depth up to a power of 2
++        if (bitDepth > 2 && bitDepth < 4) {
++            bitDepth = 4;
++        } else if (bitDepth > 4 && bitDepth < 8) {
++            bitDepth = 8;
++        } else if (bitDepth > 8 && bitDepth < 16) {
++            bitDepth = 16;
++        } else if (bitDepth > 16) {
++            throw new RuntimeException("bitDepth > 16!");
++        }
++        IHDR_bitDepth = bitDepth;
++
++        // Initialize IHDR_colorType
++        if (colorModel instanceof IndexColorModel) {
++            IndexColorModel icm = (IndexColorModel)colorModel;
++            int size = icm.getMapSize();
++
++            byte[] reds = new byte[size];
++            icm.getReds(reds);
++            byte[] greens = new byte[size];
++            icm.getGreens(greens);
++            byte[] blues = new byte[size];
++            icm.getBlues(blues);
++
++            // Determine whether the color tables are actually a gray ramp
++            // if the color type has not been set previously
++            boolean isGray = false;
++            if (!IHDR_present ||
++                (IHDR_colorType != PNGImageReader.PNG_COLOR_PALETTE)) {
++                isGray = true;
++                int scale = 255/((1 << IHDR_bitDepth) - 1);
++                for (int i = 0; i < size; i++) {
++                    byte red = reds[i];
++                    if ((red != (byte)(i*scale)) ||
++                        (red != greens[i]) ||
++                        (red != blues[i])) {
++                        isGray = false;
++                        break;
++                    }
++                }
++            }
++
++            // Determine whether transparency exists
++            boolean hasAlpha = colorModel.hasAlpha();
++
++            byte[] alpha = null;
++            if (hasAlpha) {
++                alpha = new byte[size];
++                icm.getAlphas(alpha);
++            }
++
++            /*
++             * NB: PNG_COLOR_GRAY_ALPHA color type may be not optimal for images
++             * contained more than 1024 pixels (or even than 768 pixels in case of
++             * single transparent pixel in palette).
++             * For such images alpha samples in raster will occupy more space than
++             * it is required to store palette so it could be reasonable to
++             * use PNG_COLOR_PALETTE color type for large images.
++             */
++
++            if (isGray && hasAlpha && (bitDepth == 8 || bitDepth == 16)) {
++                IHDR_colorType = PNGImageReader.PNG_COLOR_GRAY_ALPHA;
++            } else if (isGray && !hasAlpha) {
++                IHDR_colorType = PNGImageReader.PNG_COLOR_GRAY;
++            } else {
++                IHDR_colorType = PNGImageReader.PNG_COLOR_PALETTE;
++                PLTE_present = true;
++                PLTE_order = null;
++                PLTE_red = (byte[])reds.clone();
++                PLTE_green = (byte[])greens.clone();
++                PLTE_blue = (byte[])blues.clone();
++
++                if (hasAlpha) {
++                    tRNS_present = true;
++                    tRNS_colorType = PNGImageReader.PNG_COLOR_PALETTE;
++
++                    PLTE_order = new int[alpha.length];
++
++                    // Reorder the palette so that non-opaque entries
++                    // come first.  Since the tRNS chunk does not have
++                    // to store trailing 255's, this can save a
++                    // considerable amount of space when encoding
++                    // images with only one transparent pixel value,
++                    // e.g., images from GIF sources.
++
++                    byte[] newAlpha = new byte[alpha.length];
++
++                    // Scan for non-opaque entries and assign them
++                    // positions starting at 0.
++                    int newIndex = 0;
++                    for (int i = 0; i < alpha.length; i++) {
++                        if (alpha[i] != (byte)255) {
++                            PLTE_order[i] = newIndex;
++                            newAlpha[newIndex] = alpha[i];
++                            ++newIndex;
++                        }
++                    }
++                    int numTransparent = newIndex;
++
++                    // Scan for opaque entries and assign them
++                    // positions following the non-opaque entries.
++                    for (int i = 0; i < alpha.length; i++) {
++                        if (alpha[i] == (byte)255) {
++                            PLTE_order[i] = newIndex++;
++                        }
++                    }
++
++                    // Reorder the palettes
++                    byte[] oldRed = PLTE_red;
++                    byte[] oldGreen = PLTE_green;
++                    byte[] oldBlue = PLTE_blue;
++                    int len = oldRed.length; // All have the same length
++                    PLTE_red = new byte[len];
++                    PLTE_green = new byte[len];
++                    PLTE_blue = new byte[len];
++                    for (int i = 0; i < len; i++) {
++                        PLTE_red[PLTE_order[i]] = oldRed[i];
++                        PLTE_green[PLTE_order[i]] = oldGreen[i];
++                        PLTE_blue[PLTE_order[i]] = oldBlue[i];
++                    }
++
++                    // Copy only the transparent entries into tRNS_alpha
++                    tRNS_alpha = new byte[numTransparent];
++                    System.arraycopy(newAlpha, 0,
++                                     tRNS_alpha, 0, numTransparent);
++                }
++            }
++        } else {
++            if (numBands == 1) {
++                IHDR_colorType = PNGImageReader.PNG_COLOR_GRAY;
++            } else if (numBands == 2) {
++                IHDR_colorType = PNGImageReader.PNG_COLOR_GRAY_ALPHA;
++            } else if (numBands == 3) {
++                IHDR_colorType = PNGImageReader.PNG_COLOR_RGB;
++            } else if (numBands == 4) {
++                IHDR_colorType = PNGImageReader.PNG_COLOR_RGB_ALPHA;
++            } else {
++                throw new RuntimeException("Number of bands not 1-4!");
++            }
++        }
++
++        IHDR_present = true;
++    }
++
++    public boolean isReadOnly() {
++        return false;
++    }
++
++    private ArrayList cloneBytesArrayList(ArrayList in) {
++        if (in == null) {
++            return null;
++        } else {
++            ArrayList list = new ArrayList(in.size());
++            Iterator iter = in.iterator();
++            while (iter.hasNext()) {
++                Object o = iter.next();
++                if (o == null) {
++                    list.add(null);
++                } else {
++                    list.add(((byte[])o).clone());
++                }
++            }
++
++            return list;
++        }
++    }
++
++    // Deep clone
++    public Object clone() {
++        PNGMetadata metadata;
++        try {
++            metadata = (PNGMetadata)super.clone();
++        } catch (CloneNotSupportedException e) {
++            return null;
++        }
++
++        // unknownChunkData needs deep clone
++        metadata.unknownChunkData =
++            cloneBytesArrayList(this.unknownChunkData);
++
++        return metadata;
++    }
++
++    public Node getAsTree(String formatName) {
++        if (formatName.equals(nativeMetadataFormatName)) {
++            return getNativeTree();
++        } else if (formatName.equals
++                   (IIOMetadataFormatImpl.standardMetadataFormatName)) {
++            return getStandardTree();
++        } else {
++            throw new IllegalArgumentException("Not a recognized format!");
++        }
++    }
++
++    private Node getNativeTree() {
++        IIOMetadataNode node = null; // scratch node
++        IIOMetadataNode root = new IIOMetadataNode(nativeMetadataFormatName);
++
++        // IHDR
++        if (IHDR_present) {
++            IIOMetadataNode IHDR_node = new IIOMetadataNode("IHDR");
++            IHDR_node.setAttribute("width", Integer.toString(IHDR_width));
++            IHDR_node.setAttribute("height", Integer.toString(IHDR_height));
++            IHDR_node.setAttribute("bitDepth",
++                                   Integer.toString(IHDR_bitDepth));
++            IHDR_node.setAttribute("colorType",
++                                   IHDR_colorTypeNames[IHDR_colorType]);
++            // IHDR_compressionMethod must be 0 in PNG 1.1
++            IHDR_node.setAttribute("compressionMethod",
++                          IHDR_compressionMethodNames[IHDR_compressionMethod]);
++            // IHDR_filterMethod must be 0 in PNG 1.1
++            IHDR_node.setAttribute("filterMethod",
++                                    IHDR_filterMethodNames[IHDR_filterMethod]);
++            IHDR_node.setAttribute("interlaceMethod",
++                              IHDR_interlaceMethodNames[IHDR_interlaceMethod]);
++            root.appendChild(IHDR_node);
++        }
++
++        // PLTE
++        if (PLTE_present) {
++            IIOMetadataNode PLTE_node = new IIOMetadataNode("PLTE");
++            int numEntries = PLTE_red.length;
++            for (int i = 0; i < numEntries; i++) {
++                IIOMetadataNode entry = new IIOMetadataNode("PLTEEntry");
++                entry.setAttribute("index", Integer.toString(i));
++                entry.setAttribute("red",
++                                   Integer.toString(PLTE_red[i] & 0xff));
++                entry.setAttribute("green",
++                                   Integer.toString(PLTE_green[i] & 0xff));
++                entry.setAttribute("blue",
++                                   Integer.toString(PLTE_blue[i] & 0xff));
++                PLTE_node.appendChild(entry);
++            }
++
++            root.appendChild(PLTE_node);
++        }
++
++        // bKGD
++        if (bKGD_present) {
++            IIOMetadataNode bKGD_node = new IIOMetadataNode("bKGD");
++
++            if (bKGD_colorType == PNGImageReader.PNG_COLOR_PALETTE) {
++                node = new IIOMetadataNode("bKGD_Palette");
++                node.setAttribute("index", Integer.toString(bKGD_index));
++            } else if (bKGD_colorType == PNGImageReader.PNG_COLOR_GRAY) {
++                node = new IIOMetadataNode("bKGD_Grayscale");
++                node.setAttribute("gray", Integer.toString(bKGD_gray));
++            } else if (bKGD_colorType == PNGImageReader.PNG_COLOR_RGB) {
++                node = new IIOMetadataNode("bKGD_RGB");
++                node.setAttribute("red", Integer.toString(bKGD_red));
++                node.setAttribute("green", Integer.toString(bKGD_green));
++                node.setAttribute("blue", Integer.toString(bKGD_blue));
++            }
++            bKGD_node.appendChild(node);
++
++            root.appendChild(bKGD_node);
++        }
++
++        // cHRM
++        if (cHRM_present) {
++            IIOMetadataNode cHRM_node = new IIOMetadataNode("cHRM");
++            cHRM_node.setAttribute("whitePointX",
++                              Integer.toString(cHRM_whitePointX));
++            cHRM_node.setAttribute("whitePointY",
++                              Integer.toString(cHRM_whitePointY));
++            cHRM_node.setAttribute("redX", Integer.toString(cHRM_redX));
++            cHRM_node.setAttribute("redY", Integer.toString(cHRM_redY));
++            cHRM_node.setAttribute("greenX", Integer.toString(cHRM_greenX));
++            cHRM_node.setAttribute("greenY", Integer.toString(cHRM_greenY));
++            cHRM_node.setAttribute("blueX", Integer.toString(cHRM_blueX));
++            cHRM_node.setAttribute("blueY", Integer.toString(cHRM_blueY));
++
++            root.appendChild(cHRM_node);
++        }
++
++        // gAMA
++        if (gAMA_present) {
++            IIOMetadataNode gAMA_node = new IIOMetadataNode("gAMA");
++            gAMA_node.setAttribute("value", Integer.toString(gAMA_gamma));
++
++            root.appendChild(gAMA_node);
++        }
++
++        // hIST
++        if (hIST_present) {
++            IIOMetadataNode hIST_node = new IIOMetadataNode("hIST");
++
++            for (int i = 0; i < hIST_histogram.length; i++) {
++                IIOMetadataNode hist =
++                    new IIOMetadataNode("hISTEntry");
++                hist.setAttribute("index", Integer.toString(i));
++                hist.setAttribute("value",
++                                  Integer.toString(hIST_histogram[i]));
++                hIST_node.appendChild(hist);
++            }
++
++            root.appendChild(hIST_node);
++        }
++
++        // iCCP
++        if (iCCP_present) {
++            IIOMetadataNode iCCP_node = new IIOMetadataNode("iCCP");
++            iCCP_node.setAttribute("profileName", iCCP_profileName);
++            iCCP_node.setAttribute("compressionMethod",
++                          iCCP_compressionMethodNames[iCCP_compressionMethod]);
++
++            Object profile = iCCP_compressedProfile;
++            if (profile != null) {
++                profile = ((byte[])profile).clone();
++            }
++            iCCP_node.setUserObject(profile);
++
++            root.appendChild(iCCP_node);
++        }
++
++        // iTXt
++        if (iTXt_keyword.size() > 0) {
++            IIOMetadataNode iTXt_parent = new IIOMetadataNode("iTXt");
++            for (int i = 0; i < iTXt_keyword.size(); i++) {
++                IIOMetadataNode iTXt_node = new IIOMetadataNode("iTXtEntry");
++                iTXt_node.setAttribute("keyword", iTXt_keyword.get(i));
++                iTXt_node.setAttribute("compressionFlag",
++                        iTXt_compressionFlag.get(i) ? "1" : "0");
++                iTXt_node.setAttribute("compressionMethod",
++                        iTXt_compressionMethod.get(i).toString());
++                iTXt_node.setAttribute("languageTag",
++                                       iTXt_languageTag.get(i));
++                iTXt_node.setAttribute("translatedKeyword",
++                                       iTXt_translatedKeyword.get(i));
++                iTXt_node.setAttribute("text", iTXt_text.get(i));
++
++                iTXt_parent.appendChild(iTXt_node);
++            }
++
++            root.appendChild(iTXt_parent);
++        }
++
++        // pHYs
++        if (pHYs_present) {
++            IIOMetadataNode pHYs_node = new IIOMetadataNode("pHYs");
++            pHYs_node.setAttribute("pixelsPerUnitXAxis",
++                              Integer.toString(pHYs_pixelsPerUnitXAxis));
++            pHYs_node.setAttribute("pixelsPerUnitYAxis",
++                                   Integer.toString(pHYs_pixelsPerUnitYAxis));
++            pHYs_node.setAttribute("unitSpecifier",
++                                   unitSpecifierNames[pHYs_unitSpecifier]);
++
++            root.appendChild(pHYs_node);
++        }
++
++        // sBIT
++        if (sBIT_present) {
++            IIOMetadataNode sBIT_node = new IIOMetadataNode("sBIT");
++
++            if (sBIT_colorType == PNGImageReader.PNG_COLOR_GRAY) {
++                node = new IIOMetadataNode("sBIT_Grayscale");
++                node.setAttribute("gray",
++                                  Integer.toString(sBIT_grayBits));
++            } else if (sBIT_colorType == PNGImageReader.PNG_COLOR_GRAY_ALPHA) {
++                node = new IIOMetadataNode("sBIT_GrayAlpha");
++                node.setAttribute("gray",
++                                  Integer.toString(sBIT_grayBits));
++                node.setAttribute("alpha",
++                                  Integer.toString(sBIT_alphaBits));
++            } else if (sBIT_colorType == PNGImageReader.PNG_COLOR_RGB) {
++                node = new IIOMetadataNode("sBIT_RGB");
++                node.setAttribute("red",
++                                  Integer.toString(sBIT_redBits));
++                node.setAttribute("green",
++                                  Integer.toString(sBIT_greenBits));
++                node.setAttribute("blue",
++                                  Integer.toString(sBIT_blueBits));
++            } else if (sBIT_colorType == PNGImageReader.PNG_COLOR_RGB_ALPHA) {
++                node = new IIOMetadataNode("sBIT_RGBAlpha");
++                node.setAttribute("red",
++                                  Integer.toString(sBIT_redBits));
++                node.setAttribute("green",
++                                  Integer.toString(sBIT_greenBits));
++                node.setAttribute("blue",
++                                  Integer.toString(sBIT_blueBits));
++                node.setAttribute("alpha",
++                                  Integer.toString(sBIT_alphaBits));
++            } else if (sBIT_colorType == PNGImageReader.PNG_COLOR_PALETTE) {
++                node = new IIOMetadataNode("sBIT_Palette");
++                node.setAttribute("red",
++                                  Integer.toString(sBIT_redBits));
++                node.setAttribute("green",
++                                  Integer.toString(sBIT_greenBits));
++                node.setAttribute("blue",
++                                  Integer.toString(sBIT_blueBits));
++            }
++            sBIT_node.appendChild(node);
++
++            root.appendChild(sBIT_node);
++        }
++
++        // sPLT
++        if (sPLT_present) {
++            IIOMetadataNode sPLT_node = new IIOMetadataNode("sPLT");
++
++            sPLT_node.setAttribute("name", sPLT_paletteName);
++            sPLT_node.setAttribute("sampleDepth",
++                                   Integer.toString(sPLT_sampleDepth));
++
++            int numEntries = sPLT_red.length;
++            for (int i = 0; i < numEntries; i++) {
++                IIOMetadataNode entry = new IIOMetadataNode("sPLTEntry");
++                entry.setAttribute("index", Integer.toString(i));
++                entry.setAttribute("red", Integer.toString(sPLT_red[i]));
++                entry.setAttribute("green", Integer.toString(sPLT_green[i]));
++                entry.setAttribute("blue", Integer.toString(sPLT_blue[i]));
++                entry.setAttribute("alpha", Integer.toString(sPLT_alpha[i]));
++                entry.setAttribute("frequency",
++                                  Integer.toString(sPLT_frequency[i]));
++                sPLT_node.appendChild(entry);
++            }
++
++            root.appendChild(sPLT_node);
++        }
++
++        // sRGB
++        if (sRGB_present) {
++            IIOMetadataNode sRGB_node = new IIOMetadataNode("sRGB");
++            sRGB_node.setAttribute("renderingIntent",
++                                   renderingIntentNames[sRGB_renderingIntent]);
++
++            root.appendChild(sRGB_node);
++        }
++
++        // tEXt
++        if (tEXt_keyword.size() > 0) {
++            IIOMetadataNode tEXt_parent = new IIOMetadataNode("tEXt");
++            for (int i = 0; i < tEXt_keyword.size(); i++) {
++                IIOMetadataNode tEXt_node = new IIOMetadataNode("tEXtEntry");
++                tEXt_node.setAttribute("keyword" , (String)tEXt_keyword.get(i));
++                tEXt_node.setAttribute("value" , (String)tEXt_text.get(i));
++
++                tEXt_parent.appendChild(tEXt_node);
++            }
++
++            root.appendChild(tEXt_parent);
++        }
++
++        // tIME
++        if (tIME_present) {
++            IIOMetadataNode tIME_node = new IIOMetadataNode("tIME");
++            tIME_node.setAttribute("year", Integer.toString(tIME_year));
++            tIME_node.setAttribute("month", Integer.toString(tIME_month));
++            tIME_node.setAttribute("day", Integer.toString(tIME_day));
++            tIME_node.setAttribute("hour", Integer.toString(tIME_hour));
++            tIME_node.setAttribute("minute", Integer.toString(tIME_minute));
++            tIME_node.setAttribute("second", Integer.toString(tIME_second));
++
++            root.appendChild(tIME_node);
++        }
++
++        // tRNS
++        if (tRNS_present) {
++            IIOMetadataNode tRNS_node = new IIOMetadataNode("tRNS");
++
++            if (tRNS_colorType == PNGImageReader.PNG_COLOR_PALETTE) {
++                node = new IIOMetadataNode("tRNS_Palette");
++
++                for (int i = 0; i < tRNS_alpha.length; i++) {
++                    IIOMetadataNode entry =
++                        new IIOMetadataNode("tRNS_PaletteEntry");
++                    entry.setAttribute("index", Integer.toString(i));
++                    entry.setAttribute("alpha",
++                                       Integer.toString(tRNS_alpha[i] & 0xff));
++                    node.appendChild(entry);
++                }
++            } else if (tRNS_colorType == PNGImageReader.PNG_COLOR_GRAY) {
++                node = new IIOMetadataNode("tRNS_Grayscale");
++                node.setAttribute("gray", Integer.toString(tRNS_gray));
++            } else if (tRNS_colorType == PNGImageReader.PNG_COLOR_RGB) {
++                node = new IIOMetadataNode("tRNS_RGB");
++                node.setAttribute("red", Integer.toString(tRNS_red));
++                node.setAttribute("green", Integer.toString(tRNS_green));
++                node.setAttribute("blue", Integer.toString(tRNS_blue));
++            }
++            tRNS_node.appendChild(node);
++
++            root.appendChild(tRNS_node);
++        }
++
++        // zTXt
++        if (zTXt_keyword.size() > 0) {
++            IIOMetadataNode zTXt_parent = new IIOMetadataNode("zTXt");
++            for (int i = 0; i < zTXt_keyword.size(); i++) {
++                IIOMetadataNode zTXt_node = new IIOMetadataNode("zTXtEntry");
++                zTXt_node.setAttribute("keyword", (String)zTXt_keyword.get(i));
++
++                int cm = ((Integer)zTXt_compressionMethod.get(i)).intValue();
++                zTXt_node.setAttribute("compressionMethod",
++                                       zTXt_compressionMethodNames[cm]);
++
++                zTXt_node.setAttribute("text", (String)zTXt_text.get(i));
++
++                zTXt_parent.appendChild(zTXt_node);
++            }
++
++            root.appendChild(zTXt_parent);
++        }
++
++        // Unknown chunks
++        if (unknownChunkType.size() > 0) {
++            IIOMetadataNode unknown_parent =
++                new IIOMetadataNode("UnknownChunks");
++            for (int i = 0; i < unknownChunkType.size(); i++) {
++                IIOMetadataNode unknown_node =
++                    new IIOMetadataNode("UnknownChunk");
++                unknown_node.setAttribute("type",
++                                          (String)unknownChunkType.get(i));
++                unknown_node.setUserObject((byte[])unknownChunkData.get(i));
++
++                unknown_parent.appendChild(unknown_node);
++            }
++
++            root.appendChild(unknown_parent);
++        }
++
++        return root;
++    }
++
++    private int getNumChannels() {
++        // Determine number of channels
++        // Be careful about palette color with transparency
++        int numChannels = IHDR_numChannels[IHDR_colorType];
++        if (IHDR_colorType == PNGImageReader.PNG_COLOR_PALETTE &&
++            tRNS_present && tRNS_colorType == IHDR_colorType) {
++            numChannels = 4;
++        }
++        return numChannels;
++    }
++
++    public IIOMetadataNode getStandardChromaNode() {
++        IIOMetadataNode chroma_node = new IIOMetadataNode("Chroma");
++        IIOMetadataNode node = null; // scratch node
++
++        node = new IIOMetadataNode("ColorSpaceType");
++        node.setAttribute("name", colorSpaceTypeNames[IHDR_colorType]);
++        chroma_node.appendChild(node);
++
++        node = new IIOMetadataNode("NumChannels");
++        node.setAttribute("value", Integer.toString(getNumChannels()));
++        chroma_node.appendChild(node);
++
++        if (gAMA_present) {
++            node = new IIOMetadataNode("Gamma");
++            node.setAttribute("value", Float.toString(gAMA_gamma*1.0e-5F));
++            chroma_node.appendChild(node);
++        }
++
++        node = new IIOMetadataNode("BlackIsZero");
++        node.setAttribute("value", "true");
++        chroma_node.appendChild(node);
++
++        if (PLTE_present) {
++            boolean hasAlpha = tRNS_present &&
++                (tRNS_colorType == PNGImageReader.PNG_COLOR_PALETTE);
++
++            node = new IIOMetadataNode("Palette");
++            for (int i = 0; i < PLTE_red.length; i++) {
++                IIOMetadataNode entry =
++                    new IIOMetadataNode("PaletteEntry");
++                entry.setAttribute("index", Integer.toString(i));
++                entry.setAttribute("red",
++                                   Integer.toString(PLTE_red[i] & 0xff));
++                entry.setAttribute("green",
++                                   Integer.toString(PLTE_green[i] & 0xff));
++                entry.setAttribute("blue",
++                                   Integer.toString(PLTE_blue[i] & 0xff));
++                if (hasAlpha) {
++                    int alpha = (i < tRNS_alpha.length) ?
++                        (tRNS_alpha[i] & 0xff) : 255;
++                    entry.setAttribute("alpha", Integer.toString(alpha));
++                }
++                node.appendChild(entry);
++            }
++            chroma_node.appendChild(node);
++        }
++
++        if (bKGD_present) {
++            if (bKGD_colorType == PNGImageReader.PNG_COLOR_PALETTE) {
++                node = new IIOMetadataNode("BackgroundIndex");
++                node.setAttribute("value", Integer.toString(bKGD_index));
++            } else {
++                node = new IIOMetadataNode("BackgroundColor");
++                int r, g, b;
++
++                if (bKGD_colorType == PNGImageReader.PNG_COLOR_GRAY) {
++                    r = g = b = bKGD_gray;
++                } else {
++                    r = bKGD_red;
++                    g = bKGD_green;
++                    b = bKGD_blue;
++                }
++                node.setAttribute("red", Integer.toString(r));
++                node.setAttribute("green", Integer.toString(g));
++                node.setAttribute("blue", Integer.toString(b));
++            }
++            chroma_node.appendChild(node);
++        }
++
++        return chroma_node;
++    }
++
++    public IIOMetadataNode getStandardCompressionNode() {
++        IIOMetadataNode compression_node = new IIOMetadataNode("Compression");
++        IIOMetadataNode node = null; // scratch node
++
++        node = new IIOMetadataNode("CompressionTypeName");
++        node.setAttribute("value", "deflate");
++        compression_node.appendChild(node);
++
++        node = new IIOMetadataNode("Lossless");
++        node.setAttribute("value", "true");
++        compression_node.appendChild(node);
++
++        node = new IIOMetadataNode("NumProgressiveScans");
++        node.setAttribute("value",
++                          (IHDR_interlaceMethod == 0) ? "1" : "7");
++        compression_node.appendChild(node);
++
++        return compression_node;
++    }
++
++    private String repeat(String s, int times) {
++        if (times == 1) {
++            return s;
++        }
++        StringBuffer sb = new StringBuffer((s.length() + 1)*times - 1);
++        sb.append(s);
++        for (int i = 1; i < times; i++) {
++            sb.append(" ");
++            sb.append(s);
++        }
++        return sb.toString();
++    }
++
++    public IIOMetadataNode getStandardDataNode() {
++        IIOMetadataNode data_node = new IIOMetadataNode("Data");
++        IIOMetadataNode node = null; // scratch node
++
++        node = new IIOMetadataNode("PlanarConfiguration");
++        node.setAttribute("value", "PixelInterleaved");
++        data_node.appendChild(node);
++
++        node = new IIOMetadataNode("SampleFormat");
++        node.setAttribute("value",
++                          IHDR_colorType == PNGImageReader.PNG_COLOR_PALETTE ?
++                          "Index" : "UnsignedIntegral");
++        data_node.appendChild(node);
++
++        String bitDepth = Integer.toString(IHDR_bitDepth);
++        node = new IIOMetadataNode("BitsPerSample");
++        node.setAttribute("value", repeat(bitDepth, getNumChannels()));
++        data_node.appendChild(node);
++
++        if (sBIT_present) {
++            node = new IIOMetadataNode("SignificantBitsPerSample");
++            String sbits;
++            if (sBIT_colorType == PNGImageReader.PNG_COLOR_GRAY ||
++                sBIT_colorType == PNGImageReader.PNG_COLOR_GRAY_ALPHA) {
++                sbits = Integer.toString(sBIT_grayBits);
++            } else { // sBIT_colorType == PNGImageReader.PNG_COLOR_RGB ||
++                     // sBIT_colorType == PNGImageReader.PNG_COLOR_RGB_ALPHA
++                sbits = Integer.toString(sBIT_redBits) + " " +
++                    Integer.toString(sBIT_greenBits) + " " +
++                    Integer.toString(sBIT_blueBits);
++            }
++
++            if (sBIT_colorType == PNGImageReader.PNG_COLOR_GRAY_ALPHA ||
++                sBIT_colorType == PNGImageReader.PNG_COLOR_RGB_ALPHA) {
++                sbits += " " + Integer.toString(sBIT_alphaBits);
++            }
++
++            node.setAttribute("value", sbits);
++            data_node.appendChild(node);
++        }
++
++        // SampleMSB
++
++        return data_node;
++    }
++
++    public IIOMetadataNode getStandardDimensionNode() {
++        IIOMetadataNode dimension_node = new IIOMetadataNode("Dimension");
++        IIOMetadataNode node = null; // scratch node
++
++        node = new IIOMetadataNode("PixelAspectRatio");
++        float ratio = pHYs_present ?
++            (float)pHYs_pixelsPerUnitXAxis/pHYs_pixelsPerUnitYAxis : 1.0F;
++        node.setAttribute("value", Float.toString(ratio));
++        dimension_node.appendChild(node);
++
++        node = new IIOMetadataNode("ImageOrientation");
++        node.setAttribute("value", "Normal");
++        dimension_node.appendChild(node);
++
++        if (pHYs_present && pHYs_unitSpecifier == PHYS_UNIT_METER) {
++            node = new IIOMetadataNode("HorizontalPixelSize");
++            node.setAttribute("value",
++                              Float.toString(1000.0F/pHYs_pixelsPerUnitXAxis));
++            dimension_node.appendChild(node);
++
++            node = new IIOMetadataNode("VerticalPixelSize");
++            node.setAttribute("value",
++                              Float.toString(1000.0F/pHYs_pixelsPerUnitYAxis));
++            dimension_node.appendChild(node);
++        }
++
++        return dimension_node;
++    }
++
++    public IIOMetadataNode getStandardDocumentNode() {
++        if (!tIME_present) {
++            return null;
++        }
++
++        IIOMetadataNode document_node = new IIOMetadataNode("Document");
++        IIOMetadataNode node = null; // scratch node
++
++        node = new IIOMetadataNode("ImageModificationTime");
++        node.setAttribute("year", Integer.toString(tIME_year));
++        node.setAttribute("month", Integer.toString(tIME_month));
++        node.setAttribute("day", Integer.toString(tIME_day));
++        node.setAttribute("hour", Integer.toString(tIME_hour));
++        node.setAttribute("minute", Integer.toString(tIME_minute));
++        node.setAttribute("second", Integer.toString(tIME_second));
++        document_node.appendChild(node);
++
++        return document_node;
++    }
++
++    public IIOMetadataNode getStandardTextNode() {
++        int numEntries = tEXt_keyword.size() +
++            iTXt_keyword.size() + zTXt_keyword.size();
++        if (numEntries == 0) {
++            return null;
++        }
++
++        IIOMetadataNode text_node = new IIOMetadataNode("Text");
++        IIOMetadataNode node = null; // scratch node
++
++        for (int i = 0; i < tEXt_keyword.size(); i++) {
++            node = new IIOMetadataNode("TextEntry");
++            node.setAttribute("keyword", (String)tEXt_keyword.get(i));
++            node.setAttribute("value", (String)tEXt_text.get(i));
++            node.setAttribute("encoding", "ISO-8859-1");
++            node.setAttribute("compression", "none");
++
++            text_node.appendChild(node);
++        }
++
++        for (int i = 0; i < iTXt_keyword.size(); i++) {
++            node = new IIOMetadataNode("TextEntry");
++            node.setAttribute("keyword", iTXt_keyword.get(i));
++            node.setAttribute("value", iTXt_text.get(i));
++            node.setAttribute("language",
++                              iTXt_languageTag.get(i));
++            if (iTXt_compressionFlag.get(i)) {
++                node.setAttribute("compression", "deflate");
++            } else {
++                node.setAttribute("compression", "none");
++            }
++
++            text_node.appendChild(node);
++        }
++
++        for (int i = 0; i < zTXt_keyword.size(); i++) {
++            node = new IIOMetadataNode("TextEntry");
++            node.setAttribute("keyword", (String)zTXt_keyword.get(i));
++            node.setAttribute("value", (String)zTXt_text.get(i));
++            node.setAttribute("compression", "deflate");
++
++            text_node.appendChild(node);
++        }
++
++        return text_node;
++    }
++
++    public IIOMetadataNode getStandardTransparencyNode() {
++        IIOMetadataNode transparency_node =
++            new IIOMetadataNode("Transparency");
++        IIOMetadataNode node = null; // scratch node
++
++        node = new IIOMetadataNode("Alpha");
++        boolean hasAlpha =
++            (IHDR_colorType == PNGImageReader.PNG_COLOR_RGB_ALPHA) ||
++            (IHDR_colorType == PNGImageReader.PNG_COLOR_GRAY_ALPHA) ||
++            (IHDR_colorType == PNGImageReader.PNG_COLOR_PALETTE &&
++             tRNS_present &&
++             (tRNS_colorType == IHDR_colorType) &&
++             (tRNS_alpha != null));
++        node.setAttribute("value", hasAlpha ? "nonpremultipled" : "none");
++        transparency_node.appendChild(node);
++
++        if (tRNS_present) {
++            node = new IIOMetadataNode("TransparentColor");
++            if (tRNS_colorType == PNGImageReader.PNG_COLOR_RGB) {
++                node.setAttribute("value",
++                                  Integer.toString(tRNS_red) + " " +
++                                  Integer.toString(tRNS_green) + " " +
++                                  Integer.toString(tRNS_blue));
++            } else if (tRNS_colorType == PNGImageReader.PNG_COLOR_GRAY) {
++                node.setAttribute("value", Integer.toString(tRNS_gray));
++            }
++            transparency_node.appendChild(node);
++        }
++
++        return transparency_node;
++    }
++
++    // Shorthand for throwing an IIOInvalidTreeException
++    private void fatal(Node node, String reason)
++        throws IIOInvalidTreeException {
++        throw new IIOInvalidTreeException(reason, node);
++    }
++
++    // Get an integer-valued attribute
++    private String getStringAttribute(Node node, String name,
++                                      String defaultValue, boolean required)
++        throws IIOInvalidTreeException {
++        Node attr = node.getAttributes().getNamedItem(name);
++        if (attr == null) {
++            if (!required) {
++                return defaultValue;
++            } else {
++                fatal(node, "Required attribute " + name + " not present!");
++            }
++        }
++        return attr.getNodeValue();
++    }
++
++
++    // Get an integer-valued attribute
++    private int getIntAttribute(Node node, String name,
++                                int defaultValue, boolean required)
++        throws IIOInvalidTreeException {
++        String value = getStringAttribute(node, name, null, required);
++        if (value == null) {
++            return defaultValue;
++        }
++        return Integer.parseInt(value);
++    }
++
++    // Get a float-valued attribute
++    private float getFloatAttribute(Node node, String name,
++                                    float defaultValue, boolean required)
++        throws IIOInvalidTreeException {
++        String value = getStringAttribute(node, name, null, required);
++        if (value == null) {
++            return defaultValue;
++        }
++        return Float.parseFloat(value);
++    }
++
++    // Get a required integer-valued attribute
++    private int getIntAttribute(Node node, String name)
++        throws IIOInvalidTreeException {
++        return getIntAttribute(node, name, -1, true);
++    }
++
++    // Get a required float-valued attribute
++    private float getFloatAttribute(Node node, String name)
++        throws IIOInvalidTreeException {
++        return getFloatAttribute(node, name, -1.0F, true);
++    }
++
++    // Get a boolean-valued attribute
++    private boolean getBooleanAttribute(Node node, String name,
++                                        boolean defaultValue,
++                                        boolean required)
++        throws IIOInvalidTreeException {
++        Node attr = node.getAttributes().getNamedItem(name);
++        if (attr == null) {
++            if (!required) {
++                return defaultValue;
++            } else {
++                fatal(node, "Required attribute " + name + " not present!");
++            }
++        }
++        String value = attr.getNodeValue();
++        if (value.equals("true")) {
++            return true;
++        } else if (value.equals("false")) {
++            return false;
++        } else {
++            fatal(node, "Attribute " + name + " must be 'true' or 'false'!");
++            return false;
++        }
++    }
++
++    // Get a required boolean-valued attribute
++    private boolean getBooleanAttribute(Node node, String name)
++        throws IIOInvalidTreeException {
++        return getBooleanAttribute(node, name, false, true);
++    }
++
++    // Get an enumerated attribute as an index into a String array
++    private int getEnumeratedAttribute(Node node,
++                                       String name, String[] legalNames,
++                                       int defaultValue, boolean required)
++        throws IIOInvalidTreeException {
++        Node attr = node.getAttributes().getNamedItem(name);
++        if (attr == null) {
++            if (!required) {
++                return defaultValue;
++            } else {
++                fatal(node, "Required attribute " + name + " not present!");
++            }
++        }
++        String value = attr.getNodeValue();
++        for (int i = 0; i < legalNames.length; i++) {
++            if (value.equals(legalNames[i])) {
++                return i;
++            }
++        }
++
++        fatal(node, "Illegal value for attribute " + name + "!");
++        return -1;
++    }
++
++    // Get a required enumerated attribute as an index into a String array
++    private int getEnumeratedAttribute(Node node,
++                                       String name, String[] legalNames)
++        throws IIOInvalidTreeException {
++        return getEnumeratedAttribute(node, name, legalNames, -1, true);
++    }
++
++    // Get a String-valued attribute
++    private String getAttribute(Node node, String name,
++                                String defaultValue, boolean required)
++        throws IIOInvalidTreeException {
++        Node attr = node.getAttributes().getNamedItem(name);
++        if (attr == null) {
++            if (!required) {
++                return defaultValue;
++            } else {
++                fatal(node, "Required attribute " + name + " not present!");
++            }
++        }
++        return attr.getNodeValue();
++    }
++
++    // Get a required String-valued attribute
++    private String getAttribute(Node node, String name)
++        throws IIOInvalidTreeException {
++            return getAttribute(node, name, null, true);
++    }
++
++    public void mergeTree(String formatName, Node root)
++        throws IIOInvalidTreeException {
++        if (formatName.equals(nativeMetadataFormatName)) {
++            if (root == null) {
++                throw new IllegalArgumentException("root == null!");
++            }
++            mergeNativeTree(root);
++        } else if (formatName.equals
++                   (IIOMetadataFormatImpl.standardMetadataFormatName)) {
++            if (root == null) {
++                throw new IllegalArgumentException("root == null!");
++            }
++            mergeStandardTree(root);
++        } else {
++            throw new IllegalArgumentException("Not a recognized format!");
++        }
++    }
++
++    private void mergeNativeTree(Node root)
++        throws IIOInvalidTreeException {
++        Node node = root;
++        if (!node.getNodeName().equals(nativeMetadataFormatName)) {
++            fatal(node, "Root must be " + nativeMetadataFormatName);
++        }
++
++        node = node.getFirstChild();
++        while (node != null) {
++            String name = node.getNodeName();
++
++            if (name.equals("IHDR")) {
++                IHDR_width = getIntAttribute(node, "width");
++                IHDR_height = getIntAttribute(node, "height");
++                IHDR_bitDepth = getEnumeratedAttribute(node, "bitDepth",
++                                                       IHDR_bitDepths);
++                IHDR_colorType = getEnumeratedAttribute(node, "colorType",
++                                                        IHDR_colorTypeNames);
++                IHDR_compressionMethod =
++                    getEnumeratedAttribute(node, "compressionMethod",
++                                           IHDR_compressionMethodNames);
++                IHDR_filterMethod =
++                    getEnumeratedAttribute(node,
++                                           "filterMethod",
++                                           IHDR_filterMethodNames);
++                IHDR_interlaceMethod =
++                    getEnumeratedAttribute(node, "interlaceMethod",
++                                           IHDR_interlaceMethodNames);
++                IHDR_present = true;
++            } else if (name.equals("PLTE")) {
++                byte[] red = new byte[256];
++                byte[] green  = new byte[256];
++                byte[] blue = new byte[256];
++                int maxindex = -1;
++
++                Node PLTE_entry = node.getFirstChild();
++                if (PLTE_entry == null) {
++                    fatal(node, "Palette has no entries!");
++                }
++
++                while (PLTE_entry != null) {
++                    if (!PLTE_entry.getNodeName().equals("PLTEEntry")) {
++                        fatal(node,
++                              "Only a PLTEEntry may be a child of a PLTE!");
++                    }
++
++                    int index = getIntAttribute(PLTE_entry, "index");
++                    if (index < 0 || index > 255) {
++                        fatal(node,
++                              "Bad value for PLTEEntry attribute index!");
++                    }
++                    if (index > maxindex) {
++                        maxindex = index;
++                    }
++                    red[index] =
++                        (byte)getIntAttribute(PLTE_entry, "red");
++                    green[index] =
++                        (byte)getIntAttribute(PLTE_entry, "green");
++                    blue[index] =
++                        (byte)getIntAttribute(PLTE_entry, "blue");
++
++                    PLTE_entry = PLTE_entry.getNextSibling();
++                }
++
++                int numEntries = maxindex + 1;
++                PLTE_red = new byte[numEntries];
++                PLTE_green = new byte[numEntries];
++                PLTE_blue = new byte[numEntries];
++                System.arraycopy(red, 0, PLTE_red, 0, numEntries);
++                System.arraycopy(green, 0, PLTE_green, 0, numEntries);
++                System.arraycopy(blue, 0, PLTE_blue, 0, numEntries);
++                PLTE_present = true;
++            } else if (name.equals("bKGD")) {
++                bKGD_present = false; // Guard against partial overwrite
++                Node bKGD_node = node.getFirstChild();
++                if (bKGD_node == null) {
++                    fatal(node, "bKGD node has no children!");
++                }
++                String bKGD_name = bKGD_node.getNodeName();
++                if (bKGD_name.equals("bKGD_Palette")) {
++                    bKGD_index = getIntAttribute(bKGD_node, "index");
++                    bKGD_colorType = PNGImageReader.PNG_COLOR_PALETTE;
++                } else if (bKGD_name.equals("bKGD_Grayscale")) {
++                    bKGD_gray = getIntAttribute(bKGD_node, "gray");
++                    bKGD_colorType = PNGImageReader.PNG_COLOR_GRAY;
++                } else if (bKGD_name.equals("bKGD_RGB")) {
++                    bKGD_red = getIntAttribute(bKGD_node, "red");
++                    bKGD_green = getIntAttribute(bKGD_node, "green");
++                    bKGD_blue = getIntAttribute(bKGD_node, "blue");
++                    bKGD_colorType = PNGImageReader.PNG_COLOR_RGB;
++                } else {
++                    fatal(node, "Bad child of a bKGD node!");
++                }
++                if (bKGD_node.getNextSibling() != null) {
++                    fatal(node, "bKGD node has more than one child!");
++                }
++
++                bKGD_present = true;
++            } else if (name.equals("cHRM")) {
++                cHRM_whitePointX = getIntAttribute(node, "whitePointX");
++                cHRM_whitePointY = getIntAttribute(node, "whitePointY");
++                cHRM_redX = getIntAttribute(node, "redX");
++                cHRM_redY = getIntAttribute(node, "redY");
++                cHRM_greenX = getIntAttribute(node, "greenX");
++                cHRM_greenY = getIntAttribute(node, "greenY");
++                cHRM_blueX = getIntAttribute(node, "blueX");
++                cHRM_blueY = getIntAttribute(node, "blueY");
++
++                cHRM_present = true;
++            } else if (name.equals("gAMA")) {
++                gAMA_gamma = getIntAttribute(node, "value");
++                gAMA_present = true;
++            } else if (name.equals("hIST")) {
++                char[] hist = new char[256];
++                int maxindex = -1;
++
++                Node hIST_entry = node.getFirstChild();
++                if (hIST_entry == null) {
++                    fatal(node, "hIST node has no children!");
++                }
++
++                while (hIST_entry != null) {
++                    if (!hIST_entry.getNodeName().equals("hISTEntry")) {
++                        fatal(node,
++                              "Only a hISTEntry may be a child of a hIST!");
++                    }
++
++                    int index = getIntAttribute(hIST_entry, "index");
++                    if (index < 0 || index > 255) {
++                        fatal(node,
++                              "Bad value for histEntry attribute index!");
++                    }
++                    if (index > maxindex) {
++                        maxindex = index;
++                    }
++                    hist[index] =
++                        (char)getIntAttribute(hIST_entry, "value");
++
++                    hIST_entry = hIST_entry.getNextSibling();
++                }
++
++                int numEntries = maxindex + 1;
++                hIST_histogram = new char[numEntries];
++                System.arraycopy(hist, 0, hIST_histogram, 0, numEntries);
++
++                hIST_present = true;
++            } else if (name.equals("iCCP")) {
++                iCCP_profileName = getAttribute(node, "profileName");
++                iCCP_compressionMethod =
++                    getEnumeratedAttribute(node, "compressionMethod",
++                                           iCCP_compressionMethodNames);
++                Object compressedProfile =
++                    ((IIOMetadataNode)node).getUserObject();
++                if (compressedProfile == null) {
++                    fatal(node, "No ICCP profile present in user object!");
++                }
++                if (!(compressedProfile instanceof byte[])) {
++                    fatal(node, "User object not a byte array!");
++                }
++
++                iCCP_compressedProfile =
++                    (byte[])((byte[])compressedProfile).clone();
++
++                iCCP_present = true;
++            } else if (name.equals("iTXt")) {
++                Node iTXt_node = node.getFirstChild();
++                while (iTXt_node != null) {
++                    if (!iTXt_node.getNodeName().equals("iTXtEntry")) {
++                        fatal(node,
++                              "Only an iTXtEntry may be a child of an iTXt!");
++                    }
++
++                    String keyword = getAttribute(iTXt_node, "keyword");
++                    iTXt_keyword.add(keyword);
++
++                    boolean compressionFlag =
++                        getBooleanAttribute(iTXt_node, "compressionFlag");
++                    iTXt_compressionFlag.add(Boolean.valueOf(compressionFlag));
++
++                    String compressionMethod =
++                        getAttribute(iTXt_node, "compressionMethod");
++                    iTXt_compressionMethod.add(Integer.valueOf(compressionMethod));
++
++                    String languageTag =
++                        getAttribute(iTXt_node, "languageTag");
++                    iTXt_languageTag.add(languageTag);
++
++                    String translatedKeyword =
++                        getAttribute(iTXt_node, "translatedKeyword");
++                    iTXt_translatedKeyword.add(translatedKeyword);
++
++                    String text = getAttribute(iTXt_node, "text");
++                    iTXt_text.add(text);
++
++                    iTXt_node = iTXt_node.getNextSibling();
++                }
++            } else if (name.equals("pHYs")) {
++                pHYs_pixelsPerUnitXAxis =
++                    getIntAttribute(node, "pixelsPerUnitXAxis");
++                pHYs_pixelsPerUnitYAxis =
++                    getIntAttribute(node, "pixelsPerUnitYAxis");
++                pHYs_unitSpecifier =
++                    getEnumeratedAttribute(node, "unitSpecifier",
++                                           unitSpecifierNames);
++
++                pHYs_present = true;
++            } else if (name.equals("sBIT")) {
++                sBIT_present = false; // Guard against partial overwrite
++                Node sBIT_node = node.getFirstChild();
++                if (sBIT_node == null) {
++                    fatal(node, "sBIT node has no children!");
++                }
++                String sBIT_name = sBIT_node.getNodeName();
++                if (sBIT_name.equals("sBIT_Grayscale")) {
++                    sBIT_grayBits = getIntAttribute(sBIT_node, "gray");
++                    sBIT_colorType = PNGImageReader.PNG_COLOR_GRAY;
++                } else if (sBIT_name.equals("sBIT_GrayAlpha")) {
++                    sBIT_grayBits = getIntAttribute(sBIT_node, "gray");
++                    sBIT_alphaBits = getIntAttribute(sBIT_node, "alpha");
++                    sBIT_colorType = PNGImageReader.PNG_COLOR_GRAY_ALPHA;
++                } else if (sBIT_name.equals("sBIT_RGB")) {
++                    sBIT_redBits = getIntAttribute(sBIT_node, "red");
++                    sBIT_greenBits = getIntAttribute(sBIT_node, "green");
++                    sBIT_blueBits = getIntAttribute(sBIT_node, "blue");
++                    sBIT_colorType = PNGImageReader.PNG_COLOR_RGB;
++                } else if (sBIT_name.equals("sBIT_RGBAlpha")) {
++                    sBIT_redBits = getIntAttribute(sBIT_node, "red");
++                    sBIT_greenBits = getIntAttribute(sBIT_node, "green");
++                    sBIT_blueBits = getIntAttribute(sBIT_node, "blue");
++                    sBIT_alphaBits = getIntAttribute(sBIT_node, "alpha");
++                    sBIT_colorType = PNGImageReader.PNG_COLOR_RGB_ALPHA;
++                } else if (sBIT_name.equals("sBIT_Palette")) {
++                    sBIT_redBits = getIntAttribute(sBIT_node, "red");
++                    sBIT_greenBits = getIntAttribute(sBIT_node, "green");
++                    sBIT_blueBits = getIntAttribute(sBIT_node, "blue");
++                    sBIT_colorType = PNGImageReader.PNG_COLOR_PALETTE;
++                } else {
++                    fatal(node, "Bad child of an sBIT node!");
++                }
++                if (sBIT_node.getNextSibling() != null) {
++                    fatal(node, "sBIT node has more than one child!");
++                }
++
++                sBIT_present = true;
++            } else if (name.equals("sPLT")) {
++                sPLT_paletteName = getAttribute(node, "name");
++                sPLT_sampleDepth = getIntAttribute(node, "sampleDepth");
++
++                int[] red = new int[256];
++                int[] green  = new int[256];
++                int[] blue = new int[256];
++                int[] alpha = new int[256];
++                int[] frequency = new int[256];
++                int maxindex = -1;
++
++                Node sPLT_entry = node.getFirstChild();
++                if (sPLT_entry == null) {
++                    fatal(node, "sPLT node has no children!");
++                }
++
++                while (sPLT_entry != null) {
++                    if (!sPLT_entry.getNodeName().equals("sPLTEntry")) {
++                        fatal(node,
++                              "Only an sPLTEntry may be a child of an sPLT!");
++                    }
++
++                    int index = getIntAttribute(sPLT_entry, "index");
++                    if (index < 0 || index > 255) {
++                        fatal(node,
++                              "Bad value for PLTEEntry attribute index!");
++                    }
++                    if (index > maxindex) {
++                        maxindex = index;
++                    }
++                    red[index] = getIntAttribute(sPLT_entry, "red");
++                    green[index] = getIntAttribute(sPLT_entry, "green");
++                    blue[index] = getIntAttribute(sPLT_entry, "blue");
++                    alpha[index] = getIntAttribute(sPLT_entry, "alpha");
++                    frequency[index] =
++                        getIntAttribute(sPLT_entry, "frequency");
++
++                    sPLT_entry = sPLT_entry.getNextSibling();
++                }
++
++                int numEntries = maxindex + 1;
++                sPLT_red = new int[numEntries];
++                sPLT_green = new int[numEntries];
++                sPLT_blue = new int[numEntries];
++                sPLT_alpha = new int[numEntries];
++                sPLT_frequency = new int[numEntries];
++                System.arraycopy(red, 0, sPLT_red, 0, numEntries);
++                System.arraycopy(green, 0, sPLT_green, 0, numEntries);
++                System.arraycopy(blue, 0, sPLT_blue, 0, numEntries);
++                System.arraycopy(alpha, 0, sPLT_alpha, 0, numEntries);
++                System.arraycopy(frequency, 0,
++                                 sPLT_frequency, 0, numEntries);
++
++                sPLT_present = true;
++            } else if (name.equals("sRGB")) {
++                sRGB_renderingIntent =
++                    getEnumeratedAttribute(node, "renderingIntent",
++                                           renderingIntentNames);
++
++                sRGB_present = true;
++            } else if (name.equals("tEXt")) {
++                Node tEXt_node = node.getFirstChild();
++                while (tEXt_node != null) {
++                    if (!tEXt_node.getNodeName().equals("tEXtEntry")) {
++                        fatal(node,
++                              "Only an tEXtEntry may be a child of an tEXt!");
++                    }
++
++                    String keyword = getAttribute(tEXt_node, "keyword");
++                    tEXt_keyword.add(keyword);
++
++                    String text = getAttribute(tEXt_node, "value");
++                    tEXt_text.add(text);
++
++                    tEXt_node = tEXt_node.getNextSibling();
++                }
++            } else if (name.equals("tIME")) {
++                tIME_year = getIntAttribute(node, "year");
++                tIME_month = getIntAttribute(node, "month");
++                tIME_day = getIntAttribute(node, "day");
++                tIME_hour = getIntAttribute(node, "hour");
++                tIME_minute = getIntAttribute(node, "minute");
++                tIME_second = getIntAttribute(node, "second");
++
++                tIME_present = true;
++            } else if (name.equals("tRNS")) {
++                tRNS_present = false; // Guard against partial overwrite
++                Node tRNS_node = node.getFirstChild();
++                if (tRNS_node == null) {
++                    fatal(node, "tRNS node has no children!");
++                }
++                String tRNS_name = tRNS_node.getNodeName();
++                if (tRNS_name.equals("tRNS_Palette")) {
++                    byte[] alpha = new byte[256];
++                    int maxindex = -1;
++
++                    Node tRNS_paletteEntry = tRNS_node.getFirstChild();
++                    if (tRNS_paletteEntry == null) {
++                        fatal(node, "tRNS_Palette node has no children!");
++                    }
++                    while (tRNS_paletteEntry != null) {
++                        if (!tRNS_paletteEntry.getNodeName().equals(
++                                                        "tRNS_PaletteEntry")) {
++                            fatal(node,
++                 "Only a tRNS_PaletteEntry may be a child of a tRNS_Palette!");
++                        }
++                        int index =
++                            getIntAttribute(tRNS_paletteEntry, "index");
++                        if (index < 0 || index > 255) {
++                            fatal(node,
++                           "Bad value for tRNS_PaletteEntry attribute index!");
++                        }
++                        if (index > maxindex) {
++                            maxindex = index;
++                        }
++                        alpha[index] =
++                            (byte)getIntAttribute(tRNS_paletteEntry,
++                                                  "alpha");
++
++                        tRNS_paletteEntry =
++                            tRNS_paletteEntry.getNextSibling();
++                    }
++
++                    int numEntries = maxindex + 1;
++                    tRNS_alpha = new byte[numEntries];
++                    tRNS_colorType = PNGImageReader.PNG_COLOR_PALETTE;
++                    System.arraycopy(alpha, 0, tRNS_alpha, 0, numEntries);
++                } else if (tRNS_name.equals("tRNS_Grayscale")) {
++                    tRNS_gray = getIntAttribute(tRNS_node, "gray");
++                    tRNS_colorType = PNGImageReader.PNG_COLOR_GRAY;
++                } else if (tRNS_name.equals("tRNS_RGB")) {
++                    tRNS_red = getIntAttribute(tRNS_node, "red");
++                    tRNS_green = getIntAttribute(tRNS_node, "green");
++                    tRNS_blue = getIntAttribute(tRNS_node, "blue");
++                    tRNS_colorType = PNGImageReader.PNG_COLOR_RGB;
++                } else {
++                    fatal(node, "Bad child of a tRNS node!");
++                }
++                if (tRNS_node.getNextSibling() != null) {
++                    fatal(node, "tRNS node has more than one child!");
++                }
++
++                tRNS_present = true;
++            } else if (name.equals("zTXt")) {
++                Node zTXt_node = node.getFirstChild();
++                while (zTXt_node != null) {
++                    if (!zTXt_node.getNodeName().equals("zTXtEntry")) {
++                        fatal(node,
++                              "Only an zTXtEntry may be a child of an zTXt!");
++                    }
++
++                    String keyword = getAttribute(zTXt_node, "keyword");
++                    zTXt_keyword.add(keyword);
++
++                    int compressionMethod =
++                        getEnumeratedAttribute(zTXt_node, "compressionMethod",
++                                               zTXt_compressionMethodNames);
++                    zTXt_compressionMethod.add(new Integer(compressionMethod));
++
++                    String text = getAttribute(zTXt_node, "text");
++                    zTXt_text.add(text);
++
++                    zTXt_node = zTXt_node.getNextSibling();
++                }
++            } else if (name.equals("UnknownChunks")) {
++                Node unknown_node = node.getFirstChild();
++                while (unknown_node != null) {
++                    if (!unknown_node.getNodeName().equals("UnknownChunk")) {
++                        fatal(node,
++                   "Only an UnknownChunk may be a child of an UnknownChunks!");
++                    }
++                    String chunkType = getAttribute(unknown_node, "type");
++                    Object chunkData =
++                        ((IIOMetadataNode)unknown_node).getUserObject();
++
++                    if (chunkType.length() != 4) {
++                        fatal(unknown_node,
++                              "Chunk type must be 4 characters!");
++                    }
++                    if (chunkData == null) {
++                        fatal(unknown_node,
++                              "No chunk data present in user object!");
++                    }
++                    if (!(chunkData instanceof byte[])) {
++                        fatal(unknown_node,
++                              "User object not a byte array!");
++                    }
++                    unknownChunkType.add(chunkType);
++                    unknownChunkData.add(((byte[])chunkData).clone());
++
++                    unknown_node = unknown_node.getNextSibling();
++                }
++            } else {
++                fatal(node, "Unknown child of root node!");
++            }
++
++            node = node.getNextSibling();
++        }
++    }
++
++    private boolean isISOLatin(String s) {
++        int len = s.length();
++        for (int i = 0; i < len; i++) {
++            if (s.charAt(i) > 255) {
++                return false;
++            }
++        }
++        return true;
++    }
++
++    private void mergeStandardTree(Node root)
++        throws IIOInvalidTreeException {
++        Node node = root;
++        if (!node.getNodeName()
++            .equals(IIOMetadataFormatImpl.standardMetadataFormatName)) {
++            fatal(node, "Root must be " +
++                  IIOMetadataFormatImpl.standardMetadataFormatName);
++        }
++
++        node = node.getFirstChild();
++        while (node != null) {
++            String name = node.getNodeName();
++
++            if (name.equals("Chroma")) {
++                Node child = node.getFirstChild();
++                while (child != null) {
++                    String childName = child.getNodeName();
++                    if (childName.equals("Gamma")) {
++                        float gamma = getFloatAttribute(child, "value");
++                        gAMA_present = true;
++                        gAMA_gamma = (int)(gamma*100000 + 0.5);
++                    } else if (childName.equals("Palette")) {
++                        byte[] red = new byte[256];
++                        byte[] green = new byte[256];
++                        byte[] blue = new byte[256];
++                        int maxindex = -1;
++
++                        Node entry = child.getFirstChild();
++                        while (entry != null) {
++                            int index = getIntAttribute(entry, "index");
++                            if (index >= 0 && index <= 255) {
++                                red[index] =
++                                    (byte)getIntAttribute(entry, "red");
++                                green[index] =
++                                    (byte)getIntAttribute(entry, "green");
++                                blue[index] =
++                                    (byte)getIntAttribute(entry, "blue");
++                                if (index > maxindex) {
++                                    maxindex = index;
++                                }
++                            }
++                            entry = entry.getNextSibling();
++                        }
++
++                        int numEntries = maxindex + 1;
++                        PLTE_red = new byte[numEntries];
++                        PLTE_green = new byte[numEntries];
++                        PLTE_blue = new byte[numEntries];
++                        System.arraycopy(red, 0, PLTE_red, 0, numEntries);
++                        System.arraycopy(green, 0, PLTE_green, 0, numEntries);
++                        System.arraycopy(blue, 0, PLTE_blue, 0, numEntries);
++                        PLTE_present = true;
++                    } else if (childName.equals("BackgroundIndex")) {
++                        bKGD_present = true;
++                        bKGD_colorType = PNGImageReader.PNG_COLOR_PALETTE;
++                        bKGD_index = getIntAttribute(child, "value");
++                    } else if (childName.equals("BackgroundColor")) {
++                        int red = getIntAttribute(child, "red");
++                        int green = getIntAttribute(child, "green");
++                        int blue = getIntAttribute(child, "blue");
++                        if (red == green && red == blue) {
++                            bKGD_colorType = PNGImageReader.PNG_COLOR_GRAY;
++                            bKGD_gray = red;
++                        } else {
++                            bKGD_red = red;
++                            bKGD_green = green;
++                            bKGD_blue = blue;
++                        }
++                        bKGD_present = true;
++                    }
++//                  } else if (childName.equals("ColorSpaceType")) {
++//                  } else if (childName.equals("NumChannels")) {
++
++                    child = child.getNextSibling();
++                }
++            } else if (name.equals("Compression")) {
++                Node child = node.getFirstChild();
++                while (child != null) {
++                    String childName = child.getNodeName();
++                    if (childName.equals("NumProgressiveScans")) {
++                        // Use Adam7 if NumProgressiveScans > 1
++                        int scans = getIntAttribute(child, "value");
++                        IHDR_interlaceMethod = (scans > 1) ? 1 : 0;
++//                  } else if (childName.equals("CompressionTypeName")) {
++//                  } else if (childName.equals("Lossless")) {
++//                  } else if (childName.equals("BitRate")) {
++                    }
++                    child = child.getNextSibling();
++                }
++            } else if (name.equals("Data")) {
++                Node child = node.getFirstChild();
++                while (child != null) {
++                    String childName = child.getNodeName();
++                    if (childName.equals("BitsPerSample")) {
++                        String s = getAttribute(child, "value");
++                        StringTokenizer t = new StringTokenizer(s);
++                        int maxBits = -1;
++                        while (t.hasMoreTokens()) {
++                            int bits = Integer.parseInt(t.nextToken());
++                            if (bits > maxBits) {
++                                maxBits = bits;
++                            }
++                        }
++                        if (maxBits < 1) {
++                            maxBits = 1;
++                        }
++                        if (maxBits == 3) maxBits = 4;
++                        if (maxBits > 4 || maxBits < 8) {
++                            maxBits = 8;
++                        }
++                        if (maxBits > 8) {
++                            maxBits = 16;
++                        }
++                        IHDR_bitDepth = maxBits;
++                    } else if (childName.equals("SignificantBitsPerSample")) {
++                        String s = getAttribute(child, "value");
++                        StringTokenizer t = new StringTokenizer(s);
++                        int numTokens = t.countTokens();
++                        if (numTokens == 1) {
++                            sBIT_colorType = PNGImageReader.PNG_COLOR_GRAY;
++                            sBIT_grayBits = Integer.parseInt(t.nextToken());
++                        } else if (numTokens == 2) {
++                            sBIT_colorType =
++                              PNGImageReader.PNG_COLOR_GRAY_ALPHA;
++                            sBIT_grayBits = Integer.parseInt(t.nextToken());
++                            sBIT_alphaBits = Integer.parseInt(t.nextToken());
++                        } else if (numTokens == 3) {
++                            sBIT_colorType = PNGImageReader.PNG_COLOR_RGB;
++                            sBIT_redBits = Integer.parseInt(t.nextToken());
++                            sBIT_greenBits = Integer.parseInt(t.nextToken());
++                            sBIT_blueBits = Integer.parseInt(t.nextToken());
++                        } else if (numTokens == 4) {
++                            sBIT_colorType =
++                              PNGImageReader.PNG_COLOR_RGB_ALPHA;
++                            sBIT_redBits = Integer.parseInt(t.nextToken());
++                            sBIT_greenBits = Integer.parseInt(t.nextToken());
++                            sBIT_blueBits = Integer.parseInt(t.nextToken());
++                            sBIT_alphaBits = Integer.parseInt(t.nextToken());
++                        }
++                        if (numTokens >= 1 && numTokens <= 4) {
++                            sBIT_present = true;
++                        }
++//                      } else if (childName.equals("PlanarConfiguration")) {
++//                      } else if (childName.equals("SampleFormat")) {
++//                      } else if (childName.equals("SampleMSB")) {
++                    }
++                    child = child.getNextSibling();
++                }
++            } else if (name.equals("Dimension")) {
++                boolean gotWidth = false;
++                boolean gotHeight = false;
++                boolean gotAspectRatio = false;
++
++                float width = -1.0F;
++                float height = -1.0F;
++                float aspectRatio = -1.0F;
++
++                Node child = node.getFirstChild();
++                while (child != null) {
++                    String childName = child.getNodeName();
++                    if (childName.equals("PixelAspectRatio")) {
++                        aspectRatio = getFloatAttribute(child, "value");
++                        gotAspectRatio = true;
++                    } else if (childName.equals("HorizontalPixelSize")) {
++                        width = getFloatAttribute(child, "value");
++                        gotWidth = true;
++                    } else if (childName.equals("VerticalPixelSize")) {
++                        height = getFloatAttribute(child, "value");
++                        gotHeight = true;
++//                  } else if (childName.equals("ImageOrientation")) {
++//                  } else if
++//                      (childName.equals("HorizontalPhysicalPixelSpacing")) {
++//                  } else if
++//                      (childName.equals("VerticalPhysicalPixelSpacing")) {
++//                  } else if (childName.equals("HorizontalPosition")) {
++//                  } else if (childName.equals("VerticalPosition")) {
++//                  } else if (childName.equals("HorizontalPixelOffset")) {
++//                  } else if (childName.equals("VerticalPixelOffset")) {
++                    }
++                    child = child.getNextSibling();
++                }
++
++                if (gotWidth && gotHeight) {
++                    pHYs_present = true;
++                    pHYs_unitSpecifier = 1;
++                    pHYs_pixelsPerUnitXAxis = (int)(width*1000 + 0.5F);
++                    pHYs_pixelsPerUnitYAxis = (int)(height*1000 + 0.5F);
++                } else if (gotAspectRatio) {
++                    pHYs_present = true;
++                    pHYs_unitSpecifier = 0;
++
++                    // Find a reasonable rational approximation
++                    int denom = 1;
++                    for (; denom < 100; denom++) {
++                        int num = (int)(aspectRatio*denom);
++                        if (Math.abs(num/denom - aspectRatio) < 0.001) {
++                            break;
++                        }
++                    }
++                    pHYs_pixelsPerUnitXAxis = (int)(aspectRatio*denom);
++                    pHYs_pixelsPerUnitYAxis = denom;
++                }
++            } else if (name.equals("Document")) {
++                Node child = node.getFirstChild();
++                while (child != null) {
++                    String childName = child.getNodeName();
++                    if (childName.equals("ImageModificationTime")) {
++                        tIME_present = true;
++                        tIME_year = getIntAttribute(child, "year");
++                        tIME_month = getIntAttribute(child, "month");
++                        tIME_day = getIntAttribute(child, "day");
++                        tIME_hour =
++                            getIntAttribute(child, "hour", 0, false);
++                        tIME_minute =
++                            getIntAttribute(child, "minute", 0, false);
++                        tIME_second =
++                            getIntAttribute(child, "second", 0, false);
++//                  } else if (childName.equals("SubimageInterpretation")) {
++//                  } else if (childName.equals("ImageCreationTime")) {
++                    }
++                    child = child.getNextSibling();
++                }
++            } else if (name.equals("Text")) {
++                Node child = node.getFirstChild();
++                while (child != null) {
++                    String childName = child.getNodeName();
++                    if (childName.equals("TextEntry")) {
++                        String keyword = getAttribute(child, "keyword");
++                        String value = getAttribute(child, "value");
++                        String encoding = getAttribute(child, "encoding");
++                        String language = getAttribute(child, "language");
++                        String compression =
++                            getAttribute(child, "compression");
++
++                        if (isISOLatin(value)) {
++                            if (compression.equals("zip")) {
++                                // Use a zTXt node
++                                zTXt_keyword.add(keyword);
++                                zTXt_text.add(value);
++                                zTXt_compressionMethod.add(new Integer(0));
++                            } else {
++                                // Use a tEXt node
++                                tEXt_keyword.add(keyword);
++                                tEXt_text.add(value);
++                            }
++                        } else {
++                            // Use an iTXt node
++                            iTXt_keyword.add(keyword);
++                            iTXt_compressionFlag.add(Boolean.valueOf(compression.equals("zip")));
++                            iTXt_compressionMethod.add(Integer.valueOf(0));
++                            iTXt_languageTag.add(language);
++                            iTXt_translatedKeyword.add(keyword); // fake it
++                            iTXt_text.add(value);
++                        }
++                    }
++                    child = child.getNextSibling();
++                }
++//          } else if (name.equals("Transparency")) {
++//              Node child = node.getFirstChild();
++//              while (child != null) {
++//                  String childName = child.getNodeName();
++//                  if (childName.equals("Alpha")) {
++//                  } else if (childName.equals("TransparentIndex")) {
++//                  } else if (childName.equals("TransparentColor")) {
++//                  } else if (childName.equals("TileTransparencies")) {
++//                  } else if (childName.equals("TileOpacities")) {
++//                  }
++//                  child = child.getNextSibling();
++//              }
++//          } else {
++//              // fatal(node, "Unknown child of root node!");
++            }
++
++            node = node.getNextSibling();
++        }
++    }
++
++    // Reset all instance variables to their initial state
++    public void reset() {
++        IHDR_present = false;
++        PLTE_present = false;
++        bKGD_present = false;
++        cHRM_present = false;
++        gAMA_present = false;
++        hIST_present = false;
++        iCCP_present = false;
++        iTXt_keyword = new ArrayList<String>();
++        iTXt_compressionFlag = new ArrayList<Boolean>();
++        iTXt_compressionMethod = new ArrayList<Integer>();
++        iTXt_languageTag = new ArrayList<String>();
++        iTXt_translatedKeyword = new ArrayList<String>();
++        iTXt_text = new ArrayList<String>();
++        pHYs_present = false;
++        sBIT_present = false;
++        sPLT_present = false;
++        sRGB_present = false;
++        tEXt_keyword = new ArrayList();
++        tEXt_text = new ArrayList();
++        tIME_present = false;
++        tRNS_present = false;
++        zTXt_keyword = new ArrayList();
++        zTXt_compressionMethod = new ArrayList();
++        zTXt_text = new ArrayList();
++        unknownChunkType = new ArrayList();
++        unknownChunkData = new ArrayList();
++    }
++}
+diff -urN openjdk.orig/jdk/test/javax/imageio/plugins/png/ItxtUtf8Test.java openjdk/jdk/test/javax/imageio/plugins/png/ItxtUtf8Test.java
+--- openjdk.orig/jdk/test/javax/imageio/plugins/png/ItxtUtf8Test.java	1969-12-31 19:00:00.000000000 -0500
++++ openjdk/jdk/test/javax/imageio/plugins/png/ItxtUtf8Test.java	2010-12-24 11:25:19.144101998 -0500
+@@ -0,0 +1,241 @@
++/*
++ * Copyright (c) 2008, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
++ * or visit www.oracle.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;
++    }
++
++}