1 /* 2 * Copyright (c) 2000, 2001, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.imageio.plugins.png; 27 28 import java.awt.image.ColorModel; 29 import java.awt.image.IndexColorModel; 30 import java.awt.image.SampleModel; 31 import java.util.ArrayList; 32 import java.util.StringTokenizer; 33 import javax.imageio.ImageTypeSpecifier; 34 import javax.imageio.metadata.IIOInvalidTreeException; 35 import javax.imageio.metadata.IIOMetadata; 36 import javax.imageio.metadata.IIOMetadataFormatImpl; 37 import javax.imageio.metadata.IIOMetadataNode; 38 import org.w3c.dom.Node; 39 40 public class PNGMetadata extends IIOMetadata implements Cloneable { 41 42 // package scope 43 public static final String 44 nativeMetadataFormatName = "javax_imageio_png_1.0"; 45 46 protected static final String nativeMetadataFormatClassName 47 = "com.sun.imageio.plugins.png.PNGMetadataFormat"; 48 49 // Color types for IHDR chunk 50 static final String[] IHDR_colorTypeNames = { 51 "Grayscale", null, "RGB", "Palette", 52 "GrayAlpha", null, "RGBAlpha" 53 }; 54 55 static final int[] IHDR_numChannels = { 56 1, 0, 3, 3, 2, 0, 4 57 }; 58 59 // Bit depths for IHDR chunk 60 static final String[] IHDR_bitDepths = { 61 "1", "2", "4", "8", "16" 62 }; 63 64 // Compression methods for IHDR chunk 65 static final String[] IHDR_compressionMethodNames = { 66 "deflate" 67 }; 68 69 // Filter methods for IHDR chunk 70 static final String[] IHDR_filterMethodNames = { 71 "adaptive" 72 }; 73 74 // Interlace methods for IHDR chunk 75 static final String[] IHDR_interlaceMethodNames = { 76 "none", "adam7" 77 }; 78 79 // Compression methods for iCCP chunk 80 static final String[] iCCP_compressionMethodNames = { 81 "deflate" 82 }; 83 84 // Compression methods for zTXt chunk 85 static final String[] zTXt_compressionMethodNames = { 86 "deflate" 87 }; 88 89 // "Unknown" unit for pHYs chunk 90 public static final int PHYS_UNIT_UNKNOWN = 0; 91 92 // "Meter" unit for pHYs chunk 93 public static final int PHYS_UNIT_METER = 1; 94 95 // Unit specifiers for pHYs chunk 96 static final String[] unitSpecifierNames = { 97 "unknown", "meter" 98 }; 99 100 // Rendering intents for sRGB chunk 101 static final String[] renderingIntentNames = { 102 "Perceptual", // 0 103 "Relative colorimetric", // 1 104 "Saturation", // 2 105 "Absolute colorimetric" // 3 106 107 }; 108 109 // Color space types for Chroma->ColorSpaceType node 110 static final String[] colorSpaceTypeNames = { 111 "GRAY", null, "RGB", "RGB", 112 "GRAY", null, "RGB" 113 }; 114 115 // IHDR chunk 116 public boolean IHDR_present; 117 public int IHDR_width; 118 public int IHDR_height; 119 public int IHDR_bitDepth; 120 public int IHDR_colorType; 121 public int IHDR_compressionMethod; 122 public int IHDR_filterMethod; 123 public int IHDR_interlaceMethod; // 0 == none, 1 == adam7 124 125 // PLTE chunk 126 public boolean PLTE_present; 127 public byte[] PLTE_red; 128 public byte[] PLTE_green; 129 public byte[] PLTE_blue; 130 131 // If non-null, used to reorder palette entries during encoding in 132 // order to minimize the size of the tRNS chunk. Thus an index of 133 // 'i' in the source should be encoded as index 'PLTE_order[i]'. 134 // PLTE_order will be null unless 'initialize' is called with an 135 // IndexColorModel image type. 136 public int[] PLTE_order = null; 137 138 // bKGD chunk 139 // If external (non-PNG sourced) data has red = green = blue, 140 // always store it as gray and promote when writing 141 public boolean bKGD_present; 142 public int bKGD_colorType; // PNG_COLOR_GRAY, _RGB, or _PALETTE 143 public int bKGD_index; 144 public int bKGD_gray; 145 public int bKGD_red; 146 public int bKGD_green; 147 public int bKGD_blue; 148 149 // cHRM chunk 150 public boolean cHRM_present; 151 public int cHRM_whitePointX; 152 public int cHRM_whitePointY; 153 public int cHRM_redX; 154 public int cHRM_redY; 155 public int cHRM_greenX; 156 public int cHRM_greenY; 157 public int cHRM_blueX; 158 public int cHRM_blueY; 159 160 // gAMA chunk 161 public boolean gAMA_present; 162 public int gAMA_gamma; 163 164 // hIST chunk 165 public boolean hIST_present; 166 public char[] hIST_histogram; 167 168 // iCCP chunk 169 public boolean iCCP_present; 170 public String iCCP_profileName; 171 public int iCCP_compressionMethod; 172 public byte[] iCCP_compressedProfile; 173 174 // iTXt chunk 175 public ArrayList<String> iTXt_keyword = new ArrayList<String>(); 176 public ArrayList<Boolean> iTXt_compressionFlag = new ArrayList<Boolean>(); 177 public ArrayList<Integer> iTXt_compressionMethod = new ArrayList<Integer>(); 178 public ArrayList<String> iTXt_languageTag = new ArrayList<String>(); 179 public ArrayList<String> iTXt_translatedKeyword = new ArrayList<String>(); 180 public ArrayList<String> iTXt_text = new ArrayList<String>(); 181 182 // pHYs chunk 183 public boolean pHYs_present; 184 public int pHYs_pixelsPerUnitXAxis; 185 public int pHYs_pixelsPerUnitYAxis; 186 public int pHYs_unitSpecifier; // 0 == unknown, 1 == meter 187 188 // sBIT chunk 189 public boolean sBIT_present; 190 public int sBIT_colorType; // PNG_COLOR_GRAY, _GRAY_ALPHA, _RGB, _RGB_ALPHA 191 public int sBIT_grayBits; 192 public int sBIT_redBits; 193 public int sBIT_greenBits; 194 public int sBIT_blueBits; 195 public int sBIT_alphaBits; 196 197 // sPLT chunk 198 public boolean sPLT_present; 199 public String sPLT_paletteName; // 1-79 characters 200 public int sPLT_sampleDepth; // 8 or 16 201 public int[] sPLT_red; 202 public int[] sPLT_green; 203 public int[] sPLT_blue; 204 public int[] sPLT_alpha; 205 public int[] sPLT_frequency; 206 207 // sRGB chunk 208 public boolean sRGB_present; 209 public int sRGB_renderingIntent; 210 211 // tEXt chunk 212 public ArrayList<String> tEXt_keyword = new ArrayList<String>(); // 1-79 characters 213 public ArrayList<String> tEXt_text = new ArrayList<String>(); 214 215 // tIME chunk 216 public boolean tIME_present; 217 public int tIME_year; 218 public int tIME_month; 219 public int tIME_day; 220 public int tIME_hour; 221 public int tIME_minute; 222 public int tIME_second; 223 224 // tRNS chunk 225 // If external (non-PNG sourced) data has red = green = blue, 226 // always store it as gray and promote when writing 227 public boolean tRNS_present; 228 public int tRNS_colorType; // PNG_COLOR_GRAY, _RGB, or _PALETTE 229 public byte[] tRNS_alpha; // May have fewer entries than PLTE_red, etc. 230 public int tRNS_gray; 231 public int tRNS_red; 232 public int tRNS_green; 233 public int tRNS_blue; 234 235 // zTXt chunk 236 public ArrayList<String> zTXt_keyword = new ArrayList<String>(); 237 public ArrayList<Integer> zTXt_compressionMethod = new ArrayList<Integer>(); 238 public ArrayList<String> zTXt_text = new ArrayList<String>(); 239 240 // Unknown chunks 241 public ArrayList<String> unknownChunkType = new ArrayList<String>(); 242 public ArrayList<byte[]> unknownChunkData = new ArrayList<byte[]>(); 243 244 public PNGMetadata() { 245 super(true, 246 nativeMetadataFormatName, 247 nativeMetadataFormatClassName, 248 null, null); 249 } 250 251 public PNGMetadata(IIOMetadata metadata) { 252 // TODO -- implement 253 } 254 255 /** 256 * Sets the IHDR_bitDepth and IHDR_colorType variables. 257 * The <code>numBands</code> parameter is necessary since 258 * we may only be writing a subset of the image bands. 259 */ 260 public void initialize(ImageTypeSpecifier imageType, int numBands) { 261 ColorModel colorModel = imageType.getColorModel(); 262 SampleModel sampleModel = imageType.getSampleModel(); 263 264 // Initialize IHDR_bitDepth 265 int[] sampleSize = sampleModel.getSampleSize(); 266 int bitDepth = sampleSize[0]; 267 // Choose max bit depth over all channels 268 // Fixes bug 4413109 269 for (int i = 1; i < sampleSize.length; i++) { 270 if (sampleSize[i] > bitDepth) { 271 bitDepth = sampleSize[i]; 272 } 273 } 274 // Multi-channel images must have a bit depth of 8 or 16 275 if (sampleSize.length > 1 && bitDepth < 8) { 276 bitDepth = 8; 277 } 278 279 // Round bit depth up to a power of 2 280 if (bitDepth > 2 && bitDepth < 4) { 281 bitDepth = 4; 282 } else if (bitDepth > 4 && bitDepth < 8) { 283 bitDepth = 8; 284 } else if (bitDepth > 8 && bitDepth < 16) { 285 bitDepth = 16; 286 } else if (bitDepth > 16) { 287 throw new RuntimeException("bitDepth > 16!"); 288 } 289 IHDR_bitDepth = bitDepth; 290 291 // Initialize IHDR_colorType 292 if (colorModel instanceof IndexColorModel) { 293 IndexColorModel icm = (IndexColorModel)colorModel; 294 int size = icm.getMapSize(); 295 296 byte[] reds = new byte[size]; 297 icm.getReds(reds); 298 byte[] greens = new byte[size]; 299 icm.getGreens(greens); 300 byte[] blues = new byte[size]; 301 icm.getBlues(blues); 302 303 // Determine whether the color tables are actually a gray ramp 304 // if the color type has not been set previously 305 boolean isGray = false; 306 if (!IHDR_present || 307 (IHDR_colorType != PNGImageReader.PNG_COLOR_PALETTE)) { 308 isGray = true; 309 int scale = 255/((1 << IHDR_bitDepth) - 1); 310 for (int i = 0; i < size; i++) { 311 byte red = reds[i]; 312 if ((red != (byte)(i*scale)) || 313 (red != greens[i]) || 314 (red != blues[i])) { 315 isGray = false; 316 break; 317 } 318 } 319 } 320 321 // Determine whether transparency exists 322 boolean hasAlpha = colorModel.hasAlpha(); 323 324 byte[] alpha = null; 325 if (hasAlpha) { 326 alpha = new byte[size]; 327 icm.getAlphas(alpha); 328 } 329 330 /* 331 * NB: PNG_COLOR_GRAY_ALPHA color type may be not optimal for images 332 * contained more than 1024 pixels (or even than 768 pixels in case of 333 * single transparent pixel in palette). 334 * For such images alpha samples in raster will occupy more space than 335 * it is required to store palette so it could be reasonable to 336 * use PNG_COLOR_PALETTE color type for large images. 337 */ 338 339 if (isGray && hasAlpha && (bitDepth == 8 || bitDepth == 16)) { 340 IHDR_colorType = PNGImageReader.PNG_COLOR_GRAY_ALPHA; 341 } else if (isGray && !hasAlpha) { 342 IHDR_colorType = PNGImageReader.PNG_COLOR_GRAY; 343 } else { 344 IHDR_colorType = PNGImageReader.PNG_COLOR_PALETTE; 345 PLTE_present = true; 346 PLTE_order = null; 347 PLTE_red = (byte[])reds.clone(); 348 PLTE_green = (byte[])greens.clone(); 349 PLTE_blue = (byte[])blues.clone(); 350 351 if (hasAlpha) { 352 tRNS_present = true; 353 tRNS_colorType = PNGImageReader.PNG_COLOR_PALETTE; 354 355 PLTE_order = new int[alpha.length]; 356 357 // Reorder the palette so that non-opaque entries 358 // come first. Since the tRNS chunk does not have 359 // to store trailing 255's, this can save a 360 // considerable amount of space when encoding 361 // images with only one transparent pixel value, 362 // e.g., images from GIF sources. 363 364 byte[] newAlpha = new byte[alpha.length]; 365 366 // Scan for non-opaque entries and assign them 367 // positions starting at 0. 368 int newIndex = 0; 369 for (int i = 0; i < alpha.length; i++) { 370 if (alpha[i] != (byte)255) { 371 PLTE_order[i] = newIndex; 372 newAlpha[newIndex] = alpha[i]; 373 ++newIndex; 374 } 375 } 376 int numTransparent = newIndex; 377 378 // Scan for opaque entries and assign them 379 // positions following the non-opaque entries. 380 for (int i = 0; i < alpha.length; i++) { 381 if (alpha[i] == (byte)255) { 382 PLTE_order[i] = newIndex++; 383 } 384 } 385 386 // Reorder the palettes 387 byte[] oldRed = PLTE_red; 388 byte[] oldGreen = PLTE_green; 389 byte[] oldBlue = PLTE_blue; 390 int len = oldRed.length; // All have the same length 391 PLTE_red = new byte[len]; 392 PLTE_green = new byte[len]; 393 PLTE_blue = new byte[len]; 394 for (int i = 0; i < len; i++) { 395 PLTE_red[PLTE_order[i]] = oldRed[i]; 396 PLTE_green[PLTE_order[i]] = oldGreen[i]; 397 PLTE_blue[PLTE_order[i]] = oldBlue[i]; 398 } 399 400 // Copy only the transparent entries into tRNS_alpha 401 tRNS_alpha = new byte[numTransparent]; 402 System.arraycopy(newAlpha, 0, 403 tRNS_alpha, 0, numTransparent); 404 } 405 } 406 } else { 407 if (numBands == 1) { 408 IHDR_colorType = PNGImageReader.PNG_COLOR_GRAY; 409 } else if (numBands == 2) { 410 IHDR_colorType = PNGImageReader.PNG_COLOR_GRAY_ALPHA; 411 } else if (numBands == 3) { 412 IHDR_colorType = PNGImageReader.PNG_COLOR_RGB; 413 } else if (numBands == 4) { 414 IHDR_colorType = PNGImageReader.PNG_COLOR_RGB_ALPHA; 415 } else { 416 throw new RuntimeException("Number of bands not 1-4!"); 417 } 418 } 419 420 IHDR_present = true; 421 } 422 423 public boolean isReadOnly() { 424 return false; 425 } 426 427 private ArrayList<byte[]> cloneBytesArrayList(ArrayList<byte[]> in) { 428 if (in == null) { 429 return null; 430 } else { 431 ArrayList<byte[]> list = new ArrayList<byte[]>(in.size()); 432 for (byte[] b: in) { 433 list.add((b == null) ? null : (byte[])b.clone()); 434 } 435 return list; 436 } 437 } 438 439 // Deep clone 440 public Object clone() { 441 PNGMetadata metadata; 442 try { 443 metadata = (PNGMetadata)super.clone(); 444 } catch (CloneNotSupportedException e) { 445 return null; 446 } 447 448 // unknownChunkData needs deep clone 449 metadata.unknownChunkData = 450 cloneBytesArrayList(this.unknownChunkData); 451 452 return metadata; 453 } 454 455 public Node getAsTree(String formatName) { 456 if (formatName.equals(nativeMetadataFormatName)) { 457 return getNativeTree(); 458 } else if (formatName.equals 459 (IIOMetadataFormatImpl.standardMetadataFormatName)) { 460 return getStandardTree(); 461 } else { 462 throw new IllegalArgumentException("Not a recognized format!"); 463 } 464 } 465 466 private Node getNativeTree() { 467 IIOMetadataNode node = null; // scratch node 468 IIOMetadataNode root = new IIOMetadataNode(nativeMetadataFormatName); 469 470 // IHDR 471 if (IHDR_present) { 472 IIOMetadataNode IHDR_node = new IIOMetadataNode("IHDR"); 473 IHDR_node.setAttribute("width", Integer.toString(IHDR_width)); 474 IHDR_node.setAttribute("height", Integer.toString(IHDR_height)); 475 IHDR_node.setAttribute("bitDepth", 476 Integer.toString(IHDR_bitDepth)); 477 IHDR_node.setAttribute("colorType", 478 IHDR_colorTypeNames[IHDR_colorType]); 479 // IHDR_compressionMethod must be 0 in PNG 1.1 480 IHDR_node.setAttribute("compressionMethod", 481 IHDR_compressionMethodNames[IHDR_compressionMethod]); 482 // IHDR_filterMethod must be 0 in PNG 1.1 483 IHDR_node.setAttribute("filterMethod", 484 IHDR_filterMethodNames[IHDR_filterMethod]); 485 IHDR_node.setAttribute("interlaceMethod", 486 IHDR_interlaceMethodNames[IHDR_interlaceMethod]); 487 root.appendChild(IHDR_node); 488 } 489 490 // PLTE 491 if (PLTE_present) { 492 IIOMetadataNode PLTE_node = new IIOMetadataNode("PLTE"); 493 int numEntries = PLTE_red.length; 494 for (int i = 0; i < numEntries; i++) { 495 IIOMetadataNode entry = new IIOMetadataNode("PLTEEntry"); 496 entry.setAttribute("index", Integer.toString(i)); 497 entry.setAttribute("red", 498 Integer.toString(PLTE_red[i] & 0xff)); 499 entry.setAttribute("green", 500 Integer.toString(PLTE_green[i] & 0xff)); 501 entry.setAttribute("blue", 502 Integer.toString(PLTE_blue[i] & 0xff)); 503 PLTE_node.appendChild(entry); 504 } 505 506 root.appendChild(PLTE_node); 507 } 508 509 // bKGD 510 if (bKGD_present) { 511 IIOMetadataNode bKGD_node = new IIOMetadataNode("bKGD"); 512 513 if (bKGD_colorType == PNGImageReader.PNG_COLOR_PALETTE) { 514 node = new IIOMetadataNode("bKGD_Palette"); 515 node.setAttribute("index", Integer.toString(bKGD_index)); 516 } else if (bKGD_colorType == PNGImageReader.PNG_COLOR_GRAY) { 517 node = new IIOMetadataNode("bKGD_Grayscale"); 518 node.setAttribute("gray", Integer.toString(bKGD_gray)); 519 } else if (bKGD_colorType == PNGImageReader.PNG_COLOR_RGB) { 520 node = new IIOMetadataNode("bKGD_RGB"); 521 node.setAttribute("red", Integer.toString(bKGD_red)); 522 node.setAttribute("green", Integer.toString(bKGD_green)); 523 node.setAttribute("blue", Integer.toString(bKGD_blue)); 524 } 525 bKGD_node.appendChild(node); 526 527 root.appendChild(bKGD_node); 528 } 529 530 // cHRM 531 if (cHRM_present) { 532 IIOMetadataNode cHRM_node = new IIOMetadataNode("cHRM"); 533 cHRM_node.setAttribute("whitePointX", 534 Integer.toString(cHRM_whitePointX)); 535 cHRM_node.setAttribute("whitePointY", 536 Integer.toString(cHRM_whitePointY)); 537 cHRM_node.setAttribute("redX", Integer.toString(cHRM_redX)); 538 cHRM_node.setAttribute("redY", Integer.toString(cHRM_redY)); 539 cHRM_node.setAttribute("greenX", Integer.toString(cHRM_greenX)); 540 cHRM_node.setAttribute("greenY", Integer.toString(cHRM_greenY)); 541 cHRM_node.setAttribute("blueX", Integer.toString(cHRM_blueX)); 542 cHRM_node.setAttribute("blueY", Integer.toString(cHRM_blueY)); 543 544 root.appendChild(cHRM_node); 545 } 546 547 // gAMA 548 if (gAMA_present) { 549 IIOMetadataNode gAMA_node = new IIOMetadataNode("gAMA"); 550 gAMA_node.setAttribute("value", Integer.toString(gAMA_gamma)); 551 552 root.appendChild(gAMA_node); 553 } 554 555 // hIST 556 if (hIST_present) { 557 IIOMetadataNode hIST_node = new IIOMetadataNode("hIST"); 558 559 for (int i = 0; i < hIST_histogram.length; i++) { 560 IIOMetadataNode hist = 561 new IIOMetadataNode("hISTEntry"); 562 hist.setAttribute("index", Integer.toString(i)); 563 hist.setAttribute("value", 564 Integer.toString(hIST_histogram[i])); 565 hIST_node.appendChild(hist); 566 } 567 568 root.appendChild(hIST_node); 569 } 570 571 // iCCP 572 if (iCCP_present) { 573 IIOMetadataNode iCCP_node = new IIOMetadataNode("iCCP"); 574 iCCP_node.setAttribute("profileName", iCCP_profileName); 575 iCCP_node.setAttribute("compressionMethod", 576 iCCP_compressionMethodNames[iCCP_compressionMethod]); 577 578 Object profile = iCCP_compressedProfile; 579 if (profile != null) { 580 profile = ((byte[])profile).clone(); 581 } 582 iCCP_node.setUserObject(profile); 583 584 root.appendChild(iCCP_node); 585 } 586 587 // iTXt 588 if (iTXt_keyword.size() > 0) { 589 IIOMetadataNode iTXt_parent = new IIOMetadataNode("iTXt"); 590 for (int i = 0; i < iTXt_keyword.size(); i++) { 591 IIOMetadataNode iTXt_node = new IIOMetadataNode("iTXtEntry"); 592 iTXt_node.setAttribute("keyword", iTXt_keyword.get(i)); 593 iTXt_node.setAttribute("compressionFlag", 594 iTXt_compressionFlag.get(i) ? "TRUE" : "FALSE"); 595 iTXt_node.setAttribute("compressionMethod", 596 iTXt_compressionMethod.get(i).toString()); 597 iTXt_node.setAttribute("languageTag", 598 iTXt_languageTag.get(i)); 599 iTXt_node.setAttribute("translatedKeyword", 600 iTXt_translatedKeyword.get(i)); 601 iTXt_node.setAttribute("text", iTXt_text.get(i)); 602 603 iTXt_parent.appendChild(iTXt_node); 604 } 605 606 root.appendChild(iTXt_parent); 607 } 608 609 // pHYs 610 if (pHYs_present) { 611 IIOMetadataNode pHYs_node = new IIOMetadataNode("pHYs"); 612 pHYs_node.setAttribute("pixelsPerUnitXAxis", 613 Integer.toString(pHYs_pixelsPerUnitXAxis)); 614 pHYs_node.setAttribute("pixelsPerUnitYAxis", 615 Integer.toString(pHYs_pixelsPerUnitYAxis)); 616 pHYs_node.setAttribute("unitSpecifier", 617 unitSpecifierNames[pHYs_unitSpecifier]); 618 619 root.appendChild(pHYs_node); 620 } 621 622 // sBIT 623 if (sBIT_present) { 624 IIOMetadataNode sBIT_node = new IIOMetadataNode("sBIT"); 625 626 if (sBIT_colorType == PNGImageReader.PNG_COLOR_GRAY) { 627 node = new IIOMetadataNode("sBIT_Grayscale"); 628 node.setAttribute("gray", 629 Integer.toString(sBIT_grayBits)); 630 } else if (sBIT_colorType == PNGImageReader.PNG_COLOR_GRAY_ALPHA) { 631 node = new IIOMetadataNode("sBIT_GrayAlpha"); 632 node.setAttribute("gray", 633 Integer.toString(sBIT_grayBits)); 634 node.setAttribute("alpha", 635 Integer.toString(sBIT_alphaBits)); 636 } else if (sBIT_colorType == PNGImageReader.PNG_COLOR_RGB) { 637 node = new IIOMetadataNode("sBIT_RGB"); 638 node.setAttribute("red", 639 Integer.toString(sBIT_redBits)); 640 node.setAttribute("green", 641 Integer.toString(sBIT_greenBits)); 642 node.setAttribute("blue", 643 Integer.toString(sBIT_blueBits)); 644 } else if (sBIT_colorType == PNGImageReader.PNG_COLOR_RGB_ALPHA) { 645 node = new IIOMetadataNode("sBIT_RGBAlpha"); 646 node.setAttribute("red", 647 Integer.toString(sBIT_redBits)); 648 node.setAttribute("green", 649 Integer.toString(sBIT_greenBits)); 650 node.setAttribute("blue", 651 Integer.toString(sBIT_blueBits)); 652 node.setAttribute("alpha", 653 Integer.toString(sBIT_alphaBits)); 654 } else if (sBIT_colorType == PNGImageReader.PNG_COLOR_PALETTE) { 655 node = new IIOMetadataNode("sBIT_Palette"); 656 node.setAttribute("red", 657 Integer.toString(sBIT_redBits)); 658 node.setAttribute("green", 659 Integer.toString(sBIT_greenBits)); 660 node.setAttribute("blue", 661 Integer.toString(sBIT_blueBits)); 662 } 663 sBIT_node.appendChild(node); 664 665 root.appendChild(sBIT_node); 666 } 667 668 // sPLT 669 if (sPLT_present) { 670 IIOMetadataNode sPLT_node = new IIOMetadataNode("sPLT"); 671 672 sPLT_node.setAttribute("name", sPLT_paletteName); 673 sPLT_node.setAttribute("sampleDepth", 674 Integer.toString(sPLT_sampleDepth)); 675 676 int numEntries = sPLT_red.length; 677 for (int i = 0; i < numEntries; i++) { 678 IIOMetadataNode entry = new IIOMetadataNode("sPLTEntry"); 679 entry.setAttribute("index", Integer.toString(i)); 680 entry.setAttribute("red", Integer.toString(sPLT_red[i])); 681 entry.setAttribute("green", Integer.toString(sPLT_green[i])); 682 entry.setAttribute("blue", Integer.toString(sPLT_blue[i])); 683 entry.setAttribute("alpha", Integer.toString(sPLT_alpha[i])); 684 entry.setAttribute("frequency", 685 Integer.toString(sPLT_frequency[i])); 686 sPLT_node.appendChild(entry); 687 } 688 689 root.appendChild(sPLT_node); 690 } 691 692 // sRGB 693 if (sRGB_present) { 694 IIOMetadataNode sRGB_node = new IIOMetadataNode("sRGB"); 695 sRGB_node.setAttribute("renderingIntent", 696 renderingIntentNames[sRGB_renderingIntent]); 697 698 root.appendChild(sRGB_node); 699 } 700 701 // tEXt 702 if (tEXt_keyword.size() > 0) { 703 IIOMetadataNode tEXt_parent = new IIOMetadataNode("tEXt"); 704 for (int i = 0; i < tEXt_keyword.size(); i++) { 705 IIOMetadataNode tEXt_node = new IIOMetadataNode("tEXtEntry"); 706 tEXt_node.setAttribute("keyword" , (String)tEXt_keyword.get(i)); 707 tEXt_node.setAttribute("value" , (String)tEXt_text.get(i)); 708 709 tEXt_parent.appendChild(tEXt_node); 710 } 711 712 root.appendChild(tEXt_parent); 713 } 714 715 // tIME 716 if (tIME_present) { 717 IIOMetadataNode tIME_node = new IIOMetadataNode("tIME"); 718 tIME_node.setAttribute("year", Integer.toString(tIME_year)); 719 tIME_node.setAttribute("month", Integer.toString(tIME_month)); 720 tIME_node.setAttribute("day", Integer.toString(tIME_day)); 721 tIME_node.setAttribute("hour", Integer.toString(tIME_hour)); 722 tIME_node.setAttribute("minute", Integer.toString(tIME_minute)); 723 tIME_node.setAttribute("second", Integer.toString(tIME_second)); 724 725 root.appendChild(tIME_node); 726 } 727 728 // tRNS 729 if (tRNS_present) { 730 IIOMetadataNode tRNS_node = new IIOMetadataNode("tRNS"); 731 732 if (tRNS_colorType == PNGImageReader.PNG_COLOR_PALETTE) { 733 node = new IIOMetadataNode("tRNS_Palette"); 734 735 for (int i = 0; i < tRNS_alpha.length; i++) { 736 IIOMetadataNode entry = 737 new IIOMetadataNode("tRNS_PaletteEntry"); 738 entry.setAttribute("index", Integer.toString(i)); 739 entry.setAttribute("alpha", 740 Integer.toString(tRNS_alpha[i] & 0xff)); 741 node.appendChild(entry); 742 } 743 } else if (tRNS_colorType == PNGImageReader.PNG_COLOR_GRAY) { 744 node = new IIOMetadataNode("tRNS_Grayscale"); 745 node.setAttribute("gray", Integer.toString(tRNS_gray)); 746 } else if (tRNS_colorType == PNGImageReader.PNG_COLOR_RGB) { 747 node = new IIOMetadataNode("tRNS_RGB"); 748 node.setAttribute("red", Integer.toString(tRNS_red)); 749 node.setAttribute("green", Integer.toString(tRNS_green)); 750 node.setAttribute("blue", Integer.toString(tRNS_blue)); 751 } 752 tRNS_node.appendChild(node); 753 754 root.appendChild(tRNS_node); 755 } 756 757 // zTXt 758 if (zTXt_keyword.size() > 0) { 759 IIOMetadataNode zTXt_parent = new IIOMetadataNode("zTXt"); 760 for (int i = 0; i < zTXt_keyword.size(); i++) { 761 IIOMetadataNode zTXt_node = new IIOMetadataNode("zTXtEntry"); 762 zTXt_node.setAttribute("keyword", (String)zTXt_keyword.get(i)); 763 764 int cm = ((Integer)zTXt_compressionMethod.get(i)).intValue(); 765 zTXt_node.setAttribute("compressionMethod", 766 zTXt_compressionMethodNames[cm]); 767 768 zTXt_node.setAttribute("text", (String)zTXt_text.get(i)); 769 770 zTXt_parent.appendChild(zTXt_node); 771 } 772 773 root.appendChild(zTXt_parent); 774 } 775 776 // Unknown chunks 777 if (unknownChunkType.size() > 0) { 778 IIOMetadataNode unknown_parent = 779 new IIOMetadataNode("UnknownChunks"); 780 for (int i = 0; i < unknownChunkType.size(); i++) { 781 IIOMetadataNode unknown_node = 782 new IIOMetadataNode("UnknownChunk"); 783 unknown_node.setAttribute("type", 784 (String)unknownChunkType.get(i)); 785 unknown_node.setUserObject((byte[])unknownChunkData.get(i)); 786 787 unknown_parent.appendChild(unknown_node); 788 } 789 790 root.appendChild(unknown_parent); 791 } 792 793 return root; 794 } 795 796 private int getNumChannels() { 797 // Determine number of channels 798 // Be careful about palette color with transparency 799 int numChannels = IHDR_numChannels[IHDR_colorType]; 800 if (IHDR_colorType == PNGImageReader.PNG_COLOR_PALETTE && 801 tRNS_present && tRNS_colorType == IHDR_colorType) { 802 numChannels = 4; 803 } 804 return numChannels; 805 } 806 807 public IIOMetadataNode getStandardChromaNode() { 808 IIOMetadataNode chroma_node = new IIOMetadataNode("Chroma"); 809 IIOMetadataNode node = null; // scratch node 810 811 node = new IIOMetadataNode("ColorSpaceType"); 812 node.setAttribute("name", colorSpaceTypeNames[IHDR_colorType]); 813 chroma_node.appendChild(node); 814 815 node = new IIOMetadataNode("NumChannels"); 816 node.setAttribute("value", Integer.toString(getNumChannels())); 817 chroma_node.appendChild(node); 818 819 if (gAMA_present) { 820 node = new IIOMetadataNode("Gamma"); 821 node.setAttribute("value", Float.toString(gAMA_gamma*1.0e-5F)); 822 chroma_node.appendChild(node); 823 } 824 825 node = new IIOMetadataNode("BlackIsZero"); 826 node.setAttribute("value", "TRUE"); 827 chroma_node.appendChild(node); 828 829 if (PLTE_present) { 830 boolean hasAlpha = tRNS_present && 831 (tRNS_colorType == PNGImageReader.PNG_COLOR_PALETTE); 832 833 node = new IIOMetadataNode("Palette"); 834 for (int i = 0; i < PLTE_red.length; i++) { 835 IIOMetadataNode entry = 836 new IIOMetadataNode("PaletteEntry"); 837 entry.setAttribute("index", Integer.toString(i)); 838 entry.setAttribute("red", 839 Integer.toString(PLTE_red[i] & 0xff)); 840 entry.setAttribute("green", 841 Integer.toString(PLTE_green[i] & 0xff)); 842 entry.setAttribute("blue", 843 Integer.toString(PLTE_blue[i] & 0xff)); 844 if (hasAlpha) { 845 int alpha = (i < tRNS_alpha.length) ? 846 (tRNS_alpha[i] & 0xff) : 255; 847 entry.setAttribute("alpha", Integer.toString(alpha)); 848 } 849 node.appendChild(entry); 850 } 851 chroma_node.appendChild(node); 852 } 853 854 if (bKGD_present) { 855 if (bKGD_colorType == PNGImageReader.PNG_COLOR_PALETTE) { 856 node = new IIOMetadataNode("BackgroundIndex"); 857 node.setAttribute("value", Integer.toString(bKGD_index)); 858 } else { 859 node = new IIOMetadataNode("BackgroundColor"); 860 int r, g, b; 861 862 if (bKGD_colorType == PNGImageReader.PNG_COLOR_GRAY) { 863 r = g = b = bKGD_gray; 864 } else { 865 r = bKGD_red; 866 g = bKGD_green; 867 b = bKGD_blue; 868 } 869 node.setAttribute("red", Integer.toString(r)); 870 node.setAttribute("green", Integer.toString(g)); 871 node.setAttribute("blue", Integer.toString(b)); 872 } 873 chroma_node.appendChild(node); 874 } 875 876 return chroma_node; 877 } 878 879 public IIOMetadataNode getStandardCompressionNode() { 880 IIOMetadataNode compression_node = new IIOMetadataNode("Compression"); 881 IIOMetadataNode node = null; // scratch node 882 883 node = new IIOMetadataNode("CompressionTypeName"); 884 node.setAttribute("value", "deflate"); 885 compression_node.appendChild(node); 886 887 node = new IIOMetadataNode("Lossless"); 888 node.setAttribute("value", "TRUE"); 889 compression_node.appendChild(node); 890 891 node = new IIOMetadataNode("NumProgressiveScans"); 892 node.setAttribute("value", 893 (IHDR_interlaceMethod == 0) ? "1" : "7"); 894 compression_node.appendChild(node); 895 896 return compression_node; 897 } 898 899 private String repeat(String s, int times) { 900 if (times == 1) { 901 return s; 902 } 903 StringBuffer sb = new StringBuffer((s.length() + 1)*times - 1); 904 sb.append(s); 905 for (int i = 1; i < times; i++) { 906 sb.append(" "); 907 sb.append(s); 908 } 909 return sb.toString(); 910 } 911 912 public IIOMetadataNode getStandardDataNode() { 913 IIOMetadataNode data_node = new IIOMetadataNode("Data"); 914 IIOMetadataNode node = null; // scratch node 915 916 node = new IIOMetadataNode("PlanarConfiguration"); 917 node.setAttribute("value", "PixelInterleaved"); 918 data_node.appendChild(node); 919 920 node = new IIOMetadataNode("SampleFormat"); 921 node.setAttribute("value", 922 IHDR_colorType == PNGImageReader.PNG_COLOR_PALETTE ? 923 "Index" : "UnsignedIntegral"); 924 data_node.appendChild(node); 925 926 String bitDepth = Integer.toString(IHDR_bitDepth); 927 node = new IIOMetadataNode("BitsPerSample"); 928 node.setAttribute("value", repeat(bitDepth, getNumChannels())); 929 data_node.appendChild(node); 930 931 if (sBIT_present) { 932 node = new IIOMetadataNode("SignificantBitsPerSample"); 933 String sbits; 934 if (sBIT_colorType == PNGImageReader.PNG_COLOR_GRAY || 935 sBIT_colorType == PNGImageReader.PNG_COLOR_GRAY_ALPHA) { 936 sbits = Integer.toString(sBIT_grayBits); 937 } else { // sBIT_colorType == PNGImageReader.PNG_COLOR_RGB || 938 // sBIT_colorType == PNGImageReader.PNG_COLOR_RGB_ALPHA 939 sbits = Integer.toString(sBIT_redBits) + " " + 940 Integer.toString(sBIT_greenBits) + " " + 941 Integer.toString(sBIT_blueBits); 942 } 943 944 if (sBIT_colorType == PNGImageReader.PNG_COLOR_GRAY_ALPHA || 945 sBIT_colorType == PNGImageReader.PNG_COLOR_RGB_ALPHA) { 946 sbits += " " + Integer.toString(sBIT_alphaBits); 947 } 948 949 node.setAttribute("value", sbits); 950 data_node.appendChild(node); 951 } 952 953 // SampleMSB 954 955 return data_node; 956 } 957 958 public IIOMetadataNode getStandardDimensionNode() { 959 IIOMetadataNode dimension_node = new IIOMetadataNode("Dimension"); 960 IIOMetadataNode node = null; // scratch node 961 962 node = new IIOMetadataNode("PixelAspectRatio"); 963 float ratio = pHYs_present ? 964 (float)pHYs_pixelsPerUnitXAxis/pHYs_pixelsPerUnitYAxis : 1.0F; 965 node.setAttribute("value", Float.toString(ratio)); 966 dimension_node.appendChild(node); 967 968 node = new IIOMetadataNode("ImageOrientation"); 969 node.setAttribute("value", "Normal"); 970 dimension_node.appendChild(node); 971 972 if (pHYs_present && pHYs_unitSpecifier == PHYS_UNIT_METER) { 973 node = new IIOMetadataNode("HorizontalPixelSize"); 974 node.setAttribute("value", 975 Float.toString(1000.0F/pHYs_pixelsPerUnitXAxis)); 976 dimension_node.appendChild(node); 977 978 node = new IIOMetadataNode("VerticalPixelSize"); 979 node.setAttribute("value", 980 Float.toString(1000.0F/pHYs_pixelsPerUnitYAxis)); 981 dimension_node.appendChild(node); 982 } 983 984 return dimension_node; 985 } 986 987 public IIOMetadataNode getStandardDocumentNode() { 988 if (!tIME_present) { 989 return null; 990 } 991 992 IIOMetadataNode document_node = new IIOMetadataNode("Document"); 993 IIOMetadataNode node = null; // scratch node 994 995 node = new IIOMetadataNode("ImageModificationTime"); 996 node.setAttribute("year", Integer.toString(tIME_year)); 997 node.setAttribute("month", Integer.toString(tIME_month)); 998 node.setAttribute("day", Integer.toString(tIME_day)); 999 node.setAttribute("hour", Integer.toString(tIME_hour)); 1000 node.setAttribute("minute", Integer.toString(tIME_minute)); 1001 node.setAttribute("second", Integer.toString(tIME_second)); 1002 document_node.appendChild(node); 1003 1004 return document_node; 1005 } 1006 1007 public IIOMetadataNode getStandardTextNode() { 1008 int numEntries = tEXt_keyword.size() + 1009 iTXt_keyword.size() + zTXt_keyword.size(); 1010 if (numEntries == 0) { 1011 return null; 1012 } 1013 1014 IIOMetadataNode text_node = new IIOMetadataNode("Text"); 1015 IIOMetadataNode node = null; // scratch node 1016 1017 for (int i = 0; i < tEXt_keyword.size(); i++) { 1018 node = new IIOMetadataNode("TextEntry"); 1019 node.setAttribute("keyword", (String)tEXt_keyword.get(i)); 1020 node.setAttribute("value", (String)tEXt_text.get(i)); 1021 node.setAttribute("encoding", "ISO-8859-1"); 1022 node.setAttribute("compression", "none"); 1023 1024 text_node.appendChild(node); 1025 } 1026 1027 for (int i = 0; i < iTXt_keyword.size(); i++) { 1028 node = new IIOMetadataNode("TextEntry"); 1029 node.setAttribute("keyword", iTXt_keyword.get(i)); 1030 node.setAttribute("value", iTXt_text.get(i)); 1031 node.setAttribute("language", 1032 iTXt_languageTag.get(i)); 1033 if (iTXt_compressionFlag.get(i)) { 1034 node.setAttribute("compression", "zip"); 1035 } else { 1036 node.setAttribute("compression", "none"); 1037 } 1038 1039 text_node.appendChild(node); 1040 } 1041 1042 for (int i = 0; i < zTXt_keyword.size(); i++) { 1043 node = new IIOMetadataNode("TextEntry"); 1044 node.setAttribute("keyword", (String)zTXt_keyword.get(i)); 1045 node.setAttribute("value", (String)zTXt_text.get(i)); 1046 node.setAttribute("compression", "zip"); 1047 1048 text_node.appendChild(node); 1049 } 1050 1051 return text_node; 1052 } 1053 1054 public IIOMetadataNode getStandardTransparencyNode() { 1055 IIOMetadataNode transparency_node = 1056 new IIOMetadataNode("Transparency"); 1057 IIOMetadataNode node = null; // scratch node 1058 1059 node = new IIOMetadataNode("Alpha"); 1060 boolean hasAlpha = 1061 (IHDR_colorType == PNGImageReader.PNG_COLOR_RGB_ALPHA) || 1062 (IHDR_colorType == PNGImageReader.PNG_COLOR_GRAY_ALPHA) || 1063 (IHDR_colorType == PNGImageReader.PNG_COLOR_PALETTE && 1064 tRNS_present && 1065 (tRNS_colorType == IHDR_colorType) && 1066 (tRNS_alpha != null)); 1067 node.setAttribute("value", hasAlpha ? "nonpremultipled" : "none"); 1068 transparency_node.appendChild(node); 1069 1070 if (tRNS_present) { 1071 node = new IIOMetadataNode("TransparentColor"); 1072 if (tRNS_colorType == PNGImageReader.PNG_COLOR_RGB) { 1073 node.setAttribute("value", 1074 Integer.toString(tRNS_red) + " " + 1075 Integer.toString(tRNS_green) + " " + 1076 Integer.toString(tRNS_blue)); 1077 } else if (tRNS_colorType == PNGImageReader.PNG_COLOR_GRAY) { 1078 node.setAttribute("value", Integer.toString(tRNS_gray)); 1079 } 1080 transparency_node.appendChild(node); 1081 } 1082 1083 return transparency_node; 1084 } 1085 1086 // Shorthand for throwing an IIOInvalidTreeException 1087 private void fatal(Node node, String reason) 1088 throws IIOInvalidTreeException { 1089 throw new IIOInvalidTreeException(reason, node); 1090 } 1091 1092 // Get an integer-valued attribute 1093 private String getStringAttribute(Node node, String name, 1094 String defaultValue, boolean required) 1095 throws IIOInvalidTreeException { 1096 Node attr = node.getAttributes().getNamedItem(name); 1097 if (attr == null) { 1098 if (!required) { 1099 return defaultValue; 1100 } else { 1101 fatal(node, "Required attribute " + name + " not present!"); 1102 } 1103 } 1104 return attr.getNodeValue(); 1105 } 1106 1107 1108 // Get an integer-valued attribute 1109 private int getIntAttribute(Node node, String name, 1110 int defaultValue, boolean required) 1111 throws IIOInvalidTreeException { 1112 String value = getStringAttribute(node, name, null, required); 1113 if (value == null) { 1114 return defaultValue; 1115 } 1116 return Integer.parseInt(value); 1117 } 1118 1119 // Get a float-valued attribute 1120 private float getFloatAttribute(Node node, String name, 1121 float defaultValue, boolean required) 1122 throws IIOInvalidTreeException { 1123 String value = getStringAttribute(node, name, null, required); 1124 if (value == null) { 1125 return defaultValue; 1126 } 1127 return Float.parseFloat(value); 1128 } 1129 1130 // Get a required integer-valued attribute 1131 private int getIntAttribute(Node node, String name) 1132 throws IIOInvalidTreeException { 1133 return getIntAttribute(node, name, -1, true); 1134 } 1135 1136 // Get a required float-valued attribute 1137 private float getFloatAttribute(Node node, String name) 1138 throws IIOInvalidTreeException { 1139 return getFloatAttribute(node, name, -1.0F, true); 1140 } 1141 1142 // Get a boolean-valued attribute 1143 private boolean getBooleanAttribute(Node node, String name, 1144 boolean defaultValue, 1145 boolean required) 1146 throws IIOInvalidTreeException { 1147 Node attr = node.getAttributes().getNamedItem(name); 1148 if (attr == null) { 1149 if (!required) { 1150 return defaultValue; 1151 } else { 1152 fatal(node, "Required attribute " + name + " not present!"); 1153 } 1154 } 1155 String value = attr.getNodeValue(); 1156 // Allow lower case booleans for backward compatibility, #5082756 1157 if (value.equals("TRUE") || value.equals("true")) { 1158 return true; 1159 } else if (value.equals("FALSE") || value.equals("false")) { 1160 return false; 1161 } else { 1162 fatal(node, "Attribute " + name + " must be 'TRUE' or 'FALSE'!"); 1163 return false; 1164 } 1165 } 1166 1167 // Get a required boolean-valued attribute 1168 private boolean getBooleanAttribute(Node node, String name) 1169 throws IIOInvalidTreeException { 1170 return getBooleanAttribute(node, name, false, true); 1171 } 1172 1173 // Get an enumerated attribute as an index into a String array 1174 private int getEnumeratedAttribute(Node node, 1175 String name, String[] legalNames, 1176 int defaultValue, boolean required) 1177 throws IIOInvalidTreeException { 1178 Node attr = node.getAttributes().getNamedItem(name); 1179 if (attr == null) { 1180 if (!required) { 1181 return defaultValue; 1182 } else { 1183 fatal(node, "Required attribute " + name + " not present!"); 1184 } 1185 } 1186 String value = attr.getNodeValue(); 1187 for (int i = 0; i < legalNames.length; i++) { 1188 if (value.equals(legalNames[i])) { 1189 return i; 1190 } 1191 } 1192 1193 fatal(node, "Illegal value for attribute " + name + "!"); 1194 return -1; 1195 } 1196 1197 // Get a required enumerated attribute as an index into a String array 1198 private int getEnumeratedAttribute(Node node, 1199 String name, String[] legalNames) 1200 throws IIOInvalidTreeException { 1201 return getEnumeratedAttribute(node, name, legalNames, -1, true); 1202 } 1203 1204 // Get a String-valued attribute 1205 private String getAttribute(Node node, String name, 1206 String defaultValue, boolean required) 1207 throws IIOInvalidTreeException { 1208 Node attr = node.getAttributes().getNamedItem(name); 1209 if (attr == null) { 1210 if (!required) { 1211 return defaultValue; 1212 } else { 1213 fatal(node, "Required attribute " + name + " not present!"); 1214 } 1215 } 1216 return attr.getNodeValue(); 1217 } 1218 1219 // Get a required String-valued attribute 1220 private String getAttribute(Node node, String name) 1221 throws IIOInvalidTreeException { 1222 return getAttribute(node, name, null, true); 1223 } 1224 1225 public void mergeTree(String formatName, Node root) 1226 throws IIOInvalidTreeException { 1227 if (formatName.equals(nativeMetadataFormatName)) { 1228 if (root == null) { 1229 throw new IllegalArgumentException("root == null!"); 1230 } 1231 mergeNativeTree(root); 1232 } else if (formatName.equals 1233 (IIOMetadataFormatImpl.standardMetadataFormatName)) { 1234 if (root == null) { 1235 throw new IllegalArgumentException("root == null!"); 1236 } 1237 mergeStandardTree(root); 1238 } else { 1239 throw new IllegalArgumentException("Not a recognized format!"); 1240 } 1241 } 1242 1243 private void mergeNativeTree(Node root) 1244 throws IIOInvalidTreeException { 1245 Node node = root; 1246 if (!node.getNodeName().equals(nativeMetadataFormatName)) { 1247 fatal(node, "Root must be " + nativeMetadataFormatName); 1248 } 1249 1250 node = node.getFirstChild(); 1251 while (node != null) { 1252 String name = node.getNodeName(); 1253 1254 if (name.equals("IHDR")) { 1255 IHDR_width = getIntAttribute(node, "width"); 1256 IHDR_height = getIntAttribute(node, "height"); 1257 IHDR_bitDepth = getEnumeratedAttribute(node, "bitDepth", 1258 IHDR_bitDepths); 1259 IHDR_colorType = getEnumeratedAttribute(node, "colorType", 1260 IHDR_colorTypeNames); 1261 IHDR_compressionMethod = 1262 getEnumeratedAttribute(node, "compressionMethod", 1263 IHDR_compressionMethodNames); 1264 IHDR_filterMethod = 1265 getEnumeratedAttribute(node, 1266 "filterMethod", 1267 IHDR_filterMethodNames); 1268 IHDR_interlaceMethod = 1269 getEnumeratedAttribute(node, "interlaceMethod", 1270 IHDR_interlaceMethodNames); 1271 IHDR_present = true; 1272 } else if (name.equals("PLTE")) { 1273 byte[] red = new byte[256]; 1274 byte[] green = new byte[256]; 1275 byte[] blue = new byte[256]; 1276 int maxindex = -1; 1277 1278 Node PLTE_entry = node.getFirstChild(); 1279 if (PLTE_entry == null) { 1280 fatal(node, "Palette has no entries!"); 1281 } 1282 1283 while (PLTE_entry != null) { 1284 if (!PLTE_entry.getNodeName().equals("PLTEEntry")) { 1285 fatal(node, 1286 "Only a PLTEEntry may be a child of a PLTE!"); 1287 } 1288 1289 int index = getIntAttribute(PLTE_entry, "index"); 1290 if (index < 0 || index > 255) { 1291 fatal(node, 1292 "Bad value for PLTEEntry attribute index!"); 1293 } 1294 if (index > maxindex) { 1295 maxindex = index; 1296 } 1297 red[index] = 1298 (byte)getIntAttribute(PLTE_entry, "red"); 1299 green[index] = 1300 (byte)getIntAttribute(PLTE_entry, "green"); 1301 blue[index] = 1302 (byte)getIntAttribute(PLTE_entry, "blue"); 1303 1304 PLTE_entry = PLTE_entry.getNextSibling(); 1305 } 1306 1307 int numEntries = maxindex + 1; 1308 PLTE_red = new byte[numEntries]; 1309 PLTE_green = new byte[numEntries]; 1310 PLTE_blue = new byte[numEntries]; 1311 System.arraycopy(red, 0, PLTE_red, 0, numEntries); 1312 System.arraycopy(green, 0, PLTE_green, 0, numEntries); 1313 System.arraycopy(blue, 0, PLTE_blue, 0, numEntries); 1314 PLTE_present = true; 1315 } else if (name.equals("bKGD")) { 1316 bKGD_present = false; // Guard against partial overwrite 1317 Node bKGD_node = node.getFirstChild(); 1318 if (bKGD_node == null) { 1319 fatal(node, "bKGD node has no children!"); 1320 } 1321 String bKGD_name = bKGD_node.getNodeName(); 1322 if (bKGD_name.equals("bKGD_Palette")) { 1323 bKGD_index = getIntAttribute(bKGD_node, "index"); 1324 bKGD_colorType = PNGImageReader.PNG_COLOR_PALETTE; 1325 } else if (bKGD_name.equals("bKGD_Grayscale")) { 1326 bKGD_gray = getIntAttribute(bKGD_node, "gray"); 1327 bKGD_colorType = PNGImageReader.PNG_COLOR_GRAY; 1328 } else if (bKGD_name.equals("bKGD_RGB")) { 1329 bKGD_red = getIntAttribute(bKGD_node, "red"); 1330 bKGD_green = getIntAttribute(bKGD_node, "green"); 1331 bKGD_blue = getIntAttribute(bKGD_node, "blue"); 1332 bKGD_colorType = PNGImageReader.PNG_COLOR_RGB; 1333 } else { 1334 fatal(node, "Bad child of a bKGD node!"); 1335 } 1336 if (bKGD_node.getNextSibling() != null) { 1337 fatal(node, "bKGD node has more than one child!"); 1338 } 1339 1340 bKGD_present = true; 1341 } else if (name.equals("cHRM")) { 1342 cHRM_whitePointX = getIntAttribute(node, "whitePointX"); 1343 cHRM_whitePointY = getIntAttribute(node, "whitePointY"); 1344 cHRM_redX = getIntAttribute(node, "redX"); 1345 cHRM_redY = getIntAttribute(node, "redY"); 1346 cHRM_greenX = getIntAttribute(node, "greenX"); 1347 cHRM_greenY = getIntAttribute(node, "greenY"); 1348 cHRM_blueX = getIntAttribute(node, "blueX"); 1349 cHRM_blueY = getIntAttribute(node, "blueY"); 1350 1351 cHRM_present = true; 1352 } else if (name.equals("gAMA")) { 1353 gAMA_gamma = getIntAttribute(node, "value"); 1354 gAMA_present = true; 1355 } else if (name.equals("hIST")) { 1356 char[] hist = new char[256]; 1357 int maxindex = -1; 1358 1359 Node hIST_entry = node.getFirstChild(); 1360 if (hIST_entry == null) { 1361 fatal(node, "hIST node has no children!"); 1362 } 1363 1364 while (hIST_entry != null) { 1365 if (!hIST_entry.getNodeName().equals("hISTEntry")) { 1366 fatal(node, 1367 "Only a hISTEntry may be a child of a hIST!"); 1368 } 1369 1370 int index = getIntAttribute(hIST_entry, "index"); 1371 if (index < 0 || index > 255) { 1372 fatal(node, 1373 "Bad value for histEntry attribute index!"); 1374 } 1375 if (index > maxindex) { 1376 maxindex = index; 1377 } 1378 hist[index] = 1379 (char)getIntAttribute(hIST_entry, "value"); 1380 1381 hIST_entry = hIST_entry.getNextSibling(); 1382 } 1383 1384 int numEntries = maxindex + 1; 1385 hIST_histogram = new char[numEntries]; 1386 System.arraycopy(hist, 0, hIST_histogram, 0, numEntries); 1387 1388 hIST_present = true; 1389 } else if (name.equals("iCCP")) { 1390 iCCP_profileName = getAttribute(node, "profileName"); 1391 iCCP_compressionMethod = 1392 getEnumeratedAttribute(node, "compressionMethod", 1393 iCCP_compressionMethodNames); 1394 Object compressedProfile = 1395 ((IIOMetadataNode)node).getUserObject(); 1396 if (compressedProfile == null) { 1397 fatal(node, "No ICCP profile present in user object!"); 1398 } 1399 if (!(compressedProfile instanceof byte[])) { 1400 fatal(node, "User object not a byte array!"); 1401 } 1402 1403 iCCP_compressedProfile = 1404 (byte[])((byte[])compressedProfile).clone(); 1405 1406 iCCP_present = true; 1407 } else if (name.equals("iTXt")) { 1408 Node iTXt_node = node.getFirstChild(); 1409 while (iTXt_node != null) { 1410 if (!iTXt_node.getNodeName().equals("iTXtEntry")) { 1411 fatal(node, 1412 "Only an iTXtEntry may be a child of an iTXt!"); 1413 } 1414 1415 String keyword = getAttribute(iTXt_node, "keyword"); 1416 if (isValidKeyword(keyword)) { 1417 iTXt_keyword.add(keyword); 1418 1419 boolean compressionFlag = 1420 getBooleanAttribute(iTXt_node, "compressionFlag"); 1421 iTXt_compressionFlag.add(Boolean.valueOf(compressionFlag)); 1422 1423 String compressionMethod = 1424 getAttribute(iTXt_node, "compressionMethod"); 1425 iTXt_compressionMethod.add(Integer.valueOf(compressionMethod)); 1426 1427 String languageTag = 1428 getAttribute(iTXt_node, "languageTag"); 1429 iTXt_languageTag.add(languageTag); 1430 1431 String translatedKeyword = 1432 getAttribute(iTXt_node, "translatedKeyword"); 1433 iTXt_translatedKeyword.add(translatedKeyword); 1434 1435 String text = getAttribute(iTXt_node, "text"); 1436 iTXt_text.add(text); 1437 1438 } 1439 // silently skip invalid text entry 1440 1441 iTXt_node = iTXt_node.getNextSibling(); 1442 } 1443 } else if (name.equals("pHYs")) { 1444 pHYs_pixelsPerUnitXAxis = 1445 getIntAttribute(node, "pixelsPerUnitXAxis"); 1446 pHYs_pixelsPerUnitYAxis = 1447 getIntAttribute(node, "pixelsPerUnitYAxis"); 1448 pHYs_unitSpecifier = 1449 getEnumeratedAttribute(node, "unitSpecifier", 1450 unitSpecifierNames); 1451 1452 pHYs_present = true; 1453 } else if (name.equals("sBIT")) { 1454 sBIT_present = false; // Guard against partial overwrite 1455 Node sBIT_node = node.getFirstChild(); 1456 if (sBIT_node == null) { 1457 fatal(node, "sBIT node has no children!"); 1458 } 1459 String sBIT_name = sBIT_node.getNodeName(); 1460 if (sBIT_name.equals("sBIT_Grayscale")) { 1461 sBIT_grayBits = getIntAttribute(sBIT_node, "gray"); 1462 sBIT_colorType = PNGImageReader.PNG_COLOR_GRAY; 1463 } else if (sBIT_name.equals("sBIT_GrayAlpha")) { 1464 sBIT_grayBits = getIntAttribute(sBIT_node, "gray"); 1465 sBIT_alphaBits = getIntAttribute(sBIT_node, "alpha"); 1466 sBIT_colorType = PNGImageReader.PNG_COLOR_GRAY_ALPHA; 1467 } else if (sBIT_name.equals("sBIT_RGB")) { 1468 sBIT_redBits = getIntAttribute(sBIT_node, "red"); 1469 sBIT_greenBits = getIntAttribute(sBIT_node, "green"); 1470 sBIT_blueBits = getIntAttribute(sBIT_node, "blue"); 1471 sBIT_colorType = PNGImageReader.PNG_COLOR_RGB; 1472 } else if (sBIT_name.equals("sBIT_RGBAlpha")) { 1473 sBIT_redBits = getIntAttribute(sBIT_node, "red"); 1474 sBIT_greenBits = getIntAttribute(sBIT_node, "green"); 1475 sBIT_blueBits = getIntAttribute(sBIT_node, "blue"); 1476 sBIT_alphaBits = getIntAttribute(sBIT_node, "alpha"); 1477 sBIT_colorType = PNGImageReader.PNG_COLOR_RGB_ALPHA; 1478 } else if (sBIT_name.equals("sBIT_Palette")) { 1479 sBIT_redBits = getIntAttribute(sBIT_node, "red"); 1480 sBIT_greenBits = getIntAttribute(sBIT_node, "green"); 1481 sBIT_blueBits = getIntAttribute(sBIT_node, "blue"); 1482 sBIT_colorType = PNGImageReader.PNG_COLOR_PALETTE; 1483 } else { 1484 fatal(node, "Bad child of an sBIT node!"); 1485 } 1486 if (sBIT_node.getNextSibling() != null) { 1487 fatal(node, "sBIT node has more than one child!"); 1488 } 1489 1490 sBIT_present = true; 1491 } else if (name.equals("sPLT")) { 1492 sPLT_paletteName = getAttribute(node, "name"); 1493 sPLT_sampleDepth = getIntAttribute(node, "sampleDepth"); 1494 1495 int[] red = new int[256]; 1496 int[] green = new int[256]; 1497 int[] blue = new int[256]; 1498 int[] alpha = new int[256]; 1499 int[] frequency = new int[256]; 1500 int maxindex = -1; 1501 1502 Node sPLT_entry = node.getFirstChild(); 1503 if (sPLT_entry == null) { 1504 fatal(node, "sPLT node has no children!"); 1505 } 1506 1507 while (sPLT_entry != null) { 1508 if (!sPLT_entry.getNodeName().equals("sPLTEntry")) { 1509 fatal(node, 1510 "Only an sPLTEntry may be a child of an sPLT!"); 1511 } 1512 1513 int index = getIntAttribute(sPLT_entry, "index"); 1514 if (index < 0 || index > 255) { 1515 fatal(node, 1516 "Bad value for PLTEEntry attribute index!"); 1517 } 1518 if (index > maxindex) { 1519 maxindex = index; 1520 } 1521 red[index] = getIntAttribute(sPLT_entry, "red"); 1522 green[index] = getIntAttribute(sPLT_entry, "green"); 1523 blue[index] = getIntAttribute(sPLT_entry, "blue"); 1524 alpha[index] = getIntAttribute(sPLT_entry, "alpha"); 1525 frequency[index] = 1526 getIntAttribute(sPLT_entry, "frequency"); 1527 1528 sPLT_entry = sPLT_entry.getNextSibling(); 1529 } 1530 1531 int numEntries = maxindex + 1; 1532 sPLT_red = new int[numEntries]; 1533 sPLT_green = new int[numEntries]; 1534 sPLT_blue = new int[numEntries]; 1535 sPLT_alpha = new int[numEntries]; 1536 sPLT_frequency = new int[numEntries]; 1537 System.arraycopy(red, 0, sPLT_red, 0, numEntries); 1538 System.arraycopy(green, 0, sPLT_green, 0, numEntries); 1539 System.arraycopy(blue, 0, sPLT_blue, 0, numEntries); 1540 System.arraycopy(alpha, 0, sPLT_alpha, 0, numEntries); 1541 System.arraycopy(frequency, 0, 1542 sPLT_frequency, 0, numEntries); 1543 1544 sPLT_present = true; 1545 } else if (name.equals("sRGB")) { 1546 sRGB_renderingIntent = 1547 getEnumeratedAttribute(node, "renderingIntent", 1548 renderingIntentNames); 1549 1550 sRGB_present = true; 1551 } else if (name.equals("tEXt")) { 1552 Node tEXt_node = node.getFirstChild(); 1553 while (tEXt_node != null) { 1554 if (!tEXt_node.getNodeName().equals("tEXtEntry")) { 1555 fatal(node, 1556 "Only an tEXtEntry may be a child of an tEXt!"); 1557 } 1558 1559 String keyword = getAttribute(tEXt_node, "keyword"); 1560 tEXt_keyword.add(keyword); 1561 1562 String text = getAttribute(tEXt_node, "value"); 1563 tEXt_text.add(text); 1564 1565 tEXt_node = tEXt_node.getNextSibling(); 1566 } 1567 } else if (name.equals("tIME")) { 1568 tIME_year = getIntAttribute(node, "year"); 1569 tIME_month = getIntAttribute(node, "month"); 1570 tIME_day = getIntAttribute(node, "day"); 1571 tIME_hour = getIntAttribute(node, "hour"); 1572 tIME_minute = getIntAttribute(node, "minute"); 1573 tIME_second = getIntAttribute(node, "second"); 1574 1575 tIME_present = true; 1576 } else if (name.equals("tRNS")) { 1577 tRNS_present = false; // Guard against partial overwrite 1578 Node tRNS_node = node.getFirstChild(); 1579 if (tRNS_node == null) { 1580 fatal(node, "tRNS node has no children!"); 1581 } 1582 String tRNS_name = tRNS_node.getNodeName(); 1583 if (tRNS_name.equals("tRNS_Palette")) { 1584 byte[] alpha = new byte[256]; 1585 int maxindex = -1; 1586 1587 Node tRNS_paletteEntry = tRNS_node.getFirstChild(); 1588 if (tRNS_paletteEntry == null) { 1589 fatal(node, "tRNS_Palette node has no children!"); 1590 } 1591 while (tRNS_paletteEntry != null) { 1592 if (!tRNS_paletteEntry.getNodeName().equals( 1593 "tRNS_PaletteEntry")) { 1594 fatal(node, 1595 "Only a tRNS_PaletteEntry may be a child of a tRNS_Palette!"); 1596 } 1597 int index = 1598 getIntAttribute(tRNS_paletteEntry, "index"); 1599 if (index < 0 || index > 255) { 1600 fatal(node, 1601 "Bad value for tRNS_PaletteEntry attribute index!"); 1602 } 1603 if (index > maxindex) { 1604 maxindex = index; 1605 } 1606 alpha[index] = 1607 (byte)getIntAttribute(tRNS_paletteEntry, 1608 "alpha"); 1609 1610 tRNS_paletteEntry = 1611 tRNS_paletteEntry.getNextSibling(); 1612 } 1613 1614 int numEntries = maxindex + 1; 1615 tRNS_alpha = new byte[numEntries]; 1616 tRNS_colorType = PNGImageReader.PNG_COLOR_PALETTE; 1617 System.arraycopy(alpha, 0, tRNS_alpha, 0, numEntries); 1618 } else if (tRNS_name.equals("tRNS_Grayscale")) { 1619 tRNS_gray = getIntAttribute(tRNS_node, "gray"); 1620 tRNS_colorType = PNGImageReader.PNG_COLOR_GRAY; 1621 } else if (tRNS_name.equals("tRNS_RGB")) { 1622 tRNS_red = getIntAttribute(tRNS_node, "red"); 1623 tRNS_green = getIntAttribute(tRNS_node, "green"); 1624 tRNS_blue = getIntAttribute(tRNS_node, "blue"); 1625 tRNS_colorType = PNGImageReader.PNG_COLOR_RGB; 1626 } else { 1627 fatal(node, "Bad child of a tRNS node!"); 1628 } 1629 if (tRNS_node.getNextSibling() != null) { 1630 fatal(node, "tRNS node has more than one child!"); 1631 } 1632 1633 tRNS_present = true; 1634 } else if (name.equals("zTXt")) { 1635 Node zTXt_node = node.getFirstChild(); 1636 while (zTXt_node != null) { 1637 if (!zTXt_node.getNodeName().equals("zTXtEntry")) { 1638 fatal(node, 1639 "Only an zTXtEntry may be a child of an zTXt!"); 1640 } 1641 1642 String keyword = getAttribute(zTXt_node, "keyword"); 1643 zTXt_keyword.add(keyword); 1644 1645 int compressionMethod = 1646 getEnumeratedAttribute(zTXt_node, "compressionMethod", 1647 zTXt_compressionMethodNames); 1648 zTXt_compressionMethod.add(new Integer(compressionMethod)); 1649 1650 String text = getAttribute(zTXt_node, "text"); 1651 zTXt_text.add(text); 1652 1653 zTXt_node = zTXt_node.getNextSibling(); 1654 } 1655 } else if (name.equals("UnknownChunks")) { 1656 Node unknown_node = node.getFirstChild(); 1657 while (unknown_node != null) { 1658 if (!unknown_node.getNodeName().equals("UnknownChunk")) { 1659 fatal(node, 1660 "Only an UnknownChunk may be a child of an UnknownChunks!"); 1661 } 1662 String chunkType = getAttribute(unknown_node, "type"); 1663 Object chunkData = 1664 ((IIOMetadataNode)unknown_node).getUserObject(); 1665 1666 if (chunkType.length() != 4) { 1667 fatal(unknown_node, 1668 "Chunk type must be 4 characters!"); 1669 } 1670 if (chunkData == null) { 1671 fatal(unknown_node, 1672 "No chunk data present in user object!"); 1673 } 1674 if (!(chunkData instanceof byte[])) { 1675 fatal(unknown_node, 1676 "User object not a byte array!"); 1677 } 1678 unknownChunkType.add(chunkType); 1679 unknownChunkData.add(((byte[])chunkData).clone()); 1680 1681 unknown_node = unknown_node.getNextSibling(); 1682 } 1683 } else { 1684 fatal(node, "Unknown child of root node!"); 1685 } 1686 1687 node = node.getNextSibling(); 1688 } 1689 } 1690 1691 /* 1692 * Accrding to PNG spec, keywords are restricted to 1 to 79 bytes 1693 * in length. Keywords shall contain only printable Latin-1 characters 1694 * and spaces; To reduce the chances for human misreading of a keyword, 1695 * leading spaces, trailing spaces, and consecutive spaces are not 1696 * permitted in keywords. 1697 * 1698 * See: http://www.w3.org/TR/PNG/#11keywords 1699 */ 1700 private boolean isValidKeyword(String s) { 1701 int len = s.length(); 1702 if (len < 1 || len >= 80) { 1703 return false; 1704 } 1705 if (s.startsWith(" ") || s.endsWith(" ") || s.contains(" ")) { 1706 return false; 1707 } 1708 return isISOLatin(s, false); 1709 } 1710 1711 /* 1712 * According to PNG spec, keyword shall contain only printable 1713 * Latin-1 [ISO-8859-1] characters and spaces; that is, only 1714 * character codes 32-126 and 161-255 decimal are allowed. 1715 * For Latin-1 value fields the 0x10 (linefeed) control 1716 * character is aloowed too. 1717 * 1718 * See: http://www.w3.org/TR/PNG/#11keywords 1719 */ 1720 private boolean isISOLatin(String s, boolean isLineFeedAllowed) { 1721 int len = s.length(); 1722 for (int i = 0; i < len; i++) { 1723 char c = s.charAt(i); 1724 if (c < 32 || c > 255 || (c > 126 && c < 161)) { 1725 // not printable. Check whether this is an allowed 1726 // control char 1727 if (!isLineFeedAllowed || c != 0x10) { 1728 return false; 1729 } 1730 } 1731 } 1732 return true; 1733 } 1734 1735 private void mergeStandardTree(Node root) 1736 throws IIOInvalidTreeException { 1737 Node node = root; 1738 if (!node.getNodeName() 1739 .equals(IIOMetadataFormatImpl.standardMetadataFormatName)) { 1740 fatal(node, "Root must be " + 1741 IIOMetadataFormatImpl.standardMetadataFormatName); 1742 } 1743 1744 node = node.getFirstChild(); 1745 while (node != null) { 1746 String name = node.getNodeName(); 1747 1748 if (name.equals("Chroma")) { 1749 Node child = node.getFirstChild(); 1750 while (child != null) { 1751 String childName = child.getNodeName(); 1752 if (childName.equals("Gamma")) { 1753 float gamma = getFloatAttribute(child, "value"); 1754 gAMA_present = true; 1755 gAMA_gamma = (int)(gamma*100000 + 0.5); 1756 } else if (childName.equals("Palette")) { 1757 byte[] red = new byte[256]; 1758 byte[] green = new byte[256]; 1759 byte[] blue = new byte[256]; 1760 int maxindex = -1; 1761 1762 Node entry = child.getFirstChild(); 1763 while (entry != null) { 1764 int index = getIntAttribute(entry, "index"); 1765 if (index >= 0 && index <= 255) { 1766 red[index] = 1767 (byte)getIntAttribute(entry, "red"); 1768 green[index] = 1769 (byte)getIntAttribute(entry, "green"); 1770 blue[index] = 1771 (byte)getIntAttribute(entry, "blue"); 1772 if (index > maxindex) { 1773 maxindex = index; 1774 } 1775 } 1776 entry = entry.getNextSibling(); 1777 } 1778 1779 int numEntries = maxindex + 1; 1780 PLTE_red = new byte[numEntries]; 1781 PLTE_green = new byte[numEntries]; 1782 PLTE_blue = new byte[numEntries]; 1783 System.arraycopy(red, 0, PLTE_red, 0, numEntries); 1784 System.arraycopy(green, 0, PLTE_green, 0, numEntries); 1785 System.arraycopy(blue, 0, PLTE_blue, 0, numEntries); 1786 PLTE_present = true; 1787 } else if (childName.equals("BackgroundIndex")) { 1788 bKGD_present = true; 1789 bKGD_colorType = PNGImageReader.PNG_COLOR_PALETTE; 1790 bKGD_index = getIntAttribute(child, "value"); 1791 } else if (childName.equals("BackgroundColor")) { 1792 int red = getIntAttribute(child, "red"); 1793 int green = getIntAttribute(child, "green"); 1794 int blue = getIntAttribute(child, "blue"); 1795 if (red == green && red == blue) { 1796 bKGD_colorType = PNGImageReader.PNG_COLOR_GRAY; 1797 bKGD_gray = red; 1798 } else { 1799 bKGD_red = red; 1800 bKGD_green = green; 1801 bKGD_blue = blue; 1802 } 1803 bKGD_present = true; 1804 } 1805 // } else if (childName.equals("ColorSpaceType")) { 1806 // } else if (childName.equals("NumChannels")) { 1807 1808 child = child.getNextSibling(); 1809 } 1810 } else if (name.equals("Compression")) { 1811 Node child = node.getFirstChild(); 1812 while (child != null) { 1813 String childName = child.getNodeName(); 1814 if (childName.equals("NumProgressiveScans")) { 1815 // Use Adam7 if NumProgressiveScans > 1 1816 int scans = getIntAttribute(child, "value"); 1817 IHDR_interlaceMethod = (scans > 1) ? 1 : 0; 1818 // } else if (childName.equals("CompressionTypeName")) { 1819 // } else if (childName.equals("Lossless")) { 1820 // } else if (childName.equals("BitRate")) { 1821 } 1822 child = child.getNextSibling(); 1823 } 1824 } else if (name.equals("Data")) { 1825 Node child = node.getFirstChild(); 1826 while (child != null) { 1827 String childName = child.getNodeName(); 1828 if (childName.equals("BitsPerSample")) { 1829 String s = getAttribute(child, "value"); 1830 StringTokenizer t = new StringTokenizer(s); 1831 int maxBits = -1; 1832 while (t.hasMoreTokens()) { 1833 int bits = Integer.parseInt(t.nextToken()); 1834 if (bits > maxBits) { 1835 maxBits = bits; 1836 } 1837 } 1838 if (maxBits < 1) { 1839 maxBits = 1; 1840 } 1841 if (maxBits == 3) maxBits = 4; 1842 if (maxBits > 4 || maxBits < 8) { 1843 maxBits = 8; 1844 } 1845 if (maxBits > 8) { 1846 maxBits = 16; 1847 } 1848 IHDR_bitDepth = maxBits; 1849 } else if (childName.equals("SignificantBitsPerSample")) { 1850 String s = getAttribute(child, "value"); 1851 StringTokenizer t = new StringTokenizer(s); 1852 int numTokens = t.countTokens(); 1853 if (numTokens == 1) { 1854 sBIT_colorType = PNGImageReader.PNG_COLOR_GRAY; 1855 sBIT_grayBits = Integer.parseInt(t.nextToken()); 1856 } else if (numTokens == 2) { 1857 sBIT_colorType = 1858 PNGImageReader.PNG_COLOR_GRAY_ALPHA; 1859 sBIT_grayBits = Integer.parseInt(t.nextToken()); 1860 sBIT_alphaBits = Integer.parseInt(t.nextToken()); 1861 } else if (numTokens == 3) { 1862 sBIT_colorType = PNGImageReader.PNG_COLOR_RGB; 1863 sBIT_redBits = Integer.parseInt(t.nextToken()); 1864 sBIT_greenBits = Integer.parseInt(t.nextToken()); 1865 sBIT_blueBits = Integer.parseInt(t.nextToken()); 1866 } else if (numTokens == 4) { 1867 sBIT_colorType = 1868 PNGImageReader.PNG_COLOR_RGB_ALPHA; 1869 sBIT_redBits = Integer.parseInt(t.nextToken()); 1870 sBIT_greenBits = Integer.parseInt(t.nextToken()); 1871 sBIT_blueBits = Integer.parseInt(t.nextToken()); 1872 sBIT_alphaBits = Integer.parseInt(t.nextToken()); 1873 } 1874 if (numTokens >= 1 && numTokens <= 4) { 1875 sBIT_present = true; 1876 } 1877 // } else if (childName.equals("PlanarConfiguration")) { 1878 // } else if (childName.equals("SampleFormat")) { 1879 // } else if (childName.equals("SampleMSB")) { 1880 } 1881 child = child.getNextSibling(); 1882 } 1883 } else if (name.equals("Dimension")) { 1884 boolean gotWidth = false; 1885 boolean gotHeight = false; 1886 boolean gotAspectRatio = false; 1887 1888 float width = -1.0F; 1889 float height = -1.0F; 1890 float aspectRatio = -1.0F; 1891 1892 Node child = node.getFirstChild(); 1893 while (child != null) { 1894 String childName = child.getNodeName(); 1895 if (childName.equals("PixelAspectRatio")) { 1896 aspectRatio = getFloatAttribute(child, "value"); 1897 gotAspectRatio = true; 1898 } else if (childName.equals("HorizontalPixelSize")) { 1899 width = getFloatAttribute(child, "value"); 1900 gotWidth = true; 1901 } else if (childName.equals("VerticalPixelSize")) { 1902 height = getFloatAttribute(child, "value"); 1903 gotHeight = true; 1904 // } else if (childName.equals("ImageOrientation")) { 1905 // } else if 1906 // (childName.equals("HorizontalPhysicalPixelSpacing")) { 1907 // } else if 1908 // (childName.equals("VerticalPhysicalPixelSpacing")) { 1909 // } else if (childName.equals("HorizontalPosition")) { 1910 // } else if (childName.equals("VerticalPosition")) { 1911 // } else if (childName.equals("HorizontalPixelOffset")) { 1912 // } else if (childName.equals("VerticalPixelOffset")) { 1913 } 1914 child = child.getNextSibling(); 1915 } 1916 1917 if (gotWidth && gotHeight) { 1918 pHYs_present = true; 1919 pHYs_unitSpecifier = 1; 1920 pHYs_pixelsPerUnitXAxis = (int)(width*1000 + 0.5F); 1921 pHYs_pixelsPerUnitYAxis = (int)(height*1000 + 0.5F); 1922 } else if (gotAspectRatio) { 1923 pHYs_present = true; 1924 pHYs_unitSpecifier = 0; 1925 1926 // Find a reasonable rational approximation 1927 int denom = 1; 1928 for (; denom < 100; denom++) { 1929 int num = (int)(aspectRatio*denom); 1930 if (Math.abs(num/denom - aspectRatio) < 0.001) { 1931 break; 1932 } 1933 } 1934 pHYs_pixelsPerUnitXAxis = (int)(aspectRatio*denom); 1935 pHYs_pixelsPerUnitYAxis = denom; 1936 } 1937 } else if (name.equals("Document")) { 1938 Node child = node.getFirstChild(); 1939 while (child != null) { 1940 String childName = child.getNodeName(); 1941 if (childName.equals("ImageModificationTime")) { 1942 tIME_present = true; 1943 tIME_year = getIntAttribute(child, "year"); 1944 tIME_month = getIntAttribute(child, "month"); 1945 tIME_day = getIntAttribute(child, "day"); 1946 tIME_hour = 1947 getIntAttribute(child, "hour", 0, false); 1948 tIME_minute = 1949 getIntAttribute(child, "minute", 0, false); 1950 tIME_second = 1951 getIntAttribute(child, "second", 0, false); 1952 // } else if (childName.equals("SubimageInterpretation")) { 1953 // } else if (childName.equals("ImageCreationTime")) { 1954 } 1955 child = child.getNextSibling(); 1956 } 1957 } else if (name.equals("Text")) { 1958 Node child = node.getFirstChild(); 1959 while (child != null) { 1960 String childName = child.getNodeName(); 1961 if (childName.equals("TextEntry")) { 1962 String keyword = 1963 getAttribute(child, "keyword", "", false); 1964 String value = getAttribute(child, "value"); 1965 String language = 1966 getAttribute(child, "language", "", false); 1967 String compression = 1968 getAttribute(child, "compression", "none", false); 1969 1970 if (!isValidKeyword(keyword)) { 1971 // Just ignore this node, PNG requires keywords 1972 } else if (isISOLatin(value, true)) { 1973 if (compression.equals("zip")) { 1974 // Use a zTXt node 1975 zTXt_keyword.add(keyword); 1976 zTXt_text.add(value); 1977 zTXt_compressionMethod.add(Integer.valueOf(0)); 1978 } else { 1979 // Use a tEXt node 1980 tEXt_keyword.add(keyword); 1981 tEXt_text.add(value); 1982 } 1983 } else { 1984 // Use an iTXt node 1985 iTXt_keyword.add(keyword); 1986 iTXt_compressionFlag.add(Boolean.valueOf(compression.equals("zip"))); 1987 iTXt_compressionMethod.add(Integer.valueOf(0)); 1988 iTXt_languageTag.add(language); 1989 iTXt_translatedKeyword.add(keyword); // fake it 1990 iTXt_text.add(value); 1991 } 1992 } 1993 child = child.getNextSibling(); 1994 } 1995 // } else if (name.equals("Transparency")) { 1996 // Node child = node.getFirstChild(); 1997 // while (child != null) { 1998 // String childName = child.getNodeName(); 1999 // if (childName.equals("Alpha")) { 2000 // } else if (childName.equals("TransparentIndex")) { 2001 // } else if (childName.equals("TransparentColor")) { 2002 // } else if (childName.equals("TileTransparencies")) { 2003 // } else if (childName.equals("TileOpacities")) { 2004 // } 2005 // child = child.getNextSibling(); 2006 // } 2007 // } else { 2008 // // fatal(node, "Unknown child of root node!"); 2009 } 2010 2011 node = node.getNextSibling(); 2012 } 2013 } 2014 2015 // Reset all instance variables to their initial state 2016 public void reset() { 2017 IHDR_present = false; 2018 PLTE_present = false; 2019 bKGD_present = false; 2020 cHRM_present = false; 2021 gAMA_present = false; 2022 hIST_present = false; 2023 iCCP_present = false; 2024 iTXt_keyword = new ArrayList<String>(); 2025 iTXt_compressionFlag = new ArrayList<Boolean>(); 2026 iTXt_compressionMethod = new ArrayList<Integer>(); 2027 iTXt_languageTag = new ArrayList<String>(); 2028 iTXt_translatedKeyword = new ArrayList<String>(); 2029 iTXt_text = new ArrayList<String>(); 2030 pHYs_present = false; 2031 sBIT_present = false; 2032 sPLT_present = false; 2033 sRGB_present = false; 2034 tEXt_keyword = new ArrayList<String>(); 2035 tEXt_text = new ArrayList<String>(); 2036 tIME_present = false; 2037 tRNS_present = false; 2038 zTXt_keyword = new ArrayList<String>(); 2039 zTXt_compressionMethod = new ArrayList<Integer>(); 2040 zTXt_text = new ArrayList<String>(); 2041 unknownChunkType = new ArrayList<String>(); 2042 unknownChunkData = new ArrayList<byte[]>(); 2043 } 2044 }