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