1. /*
  2. * @(#)GIFImageMetadata.java 1.27 03/01/23
  3. *
  4. * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package com.sun.imageio.plugins.gif;
  8. import java.io.UnsupportedEncodingException;
  9. import java.util.ArrayList;
  10. import java.util.Iterator;
  11. import java.util.List;
  12. import javax.imageio.ImageTypeSpecifier;
  13. import javax.imageio.metadata.IIOMetadata;
  14. import javax.imageio.metadata.IIOMetadataNode;
  15. import javax.imageio.metadata.IIOMetadataFormat;
  16. import javax.imageio.metadata.IIOMetadataFormatImpl;
  17. import org.w3c.dom.Node;
  18. /**
  19. * @version 0.5
  20. */
  21. public class GIFImageMetadata extends IIOMetadata {
  22. // package scope
  23. static final String
  24. nativeMetadataFormatName = "javax_imageio_gif_image_1.0";
  25. static final String[] disposalMethodNames = {
  26. "none",
  27. "doNotDispose",
  28. "restoreToBackgroundColor",
  29. "restoreToPrevious",
  30. "undefinedDisposalMethod4",
  31. "undefinedDisposalMethod5",
  32. "undefinedDisposalMethod6",
  33. "undefinedDisposalMethod7"
  34. };
  35. // Fields from Image Descriptor
  36. public int imageLeftPosition;
  37. public int imageTopPosition;
  38. public int imageWidth;
  39. public int imageHeight;
  40. public boolean interlaceFlag = false;
  41. public boolean sortFlag = false;
  42. public byte[] localColorTable = null;
  43. // Fields from Graphic Control Extension
  44. public int disposalMethod = 0;
  45. public boolean userInputFlag = false;
  46. public boolean transparentColorFlag = false;
  47. public int delayTime = 0;
  48. public int transparentColorIndex = 0;
  49. // Fields from Plain Text Extension
  50. public boolean hasPlainTextExtension = false;
  51. public int textGridLeft;
  52. public int textGridTop;
  53. public int textGridWidth;
  54. public int textGridHeight;
  55. public int characterCellWidth;
  56. public int characterCellHeight;
  57. public int textForegroundColor;
  58. public int textBackgroundColor;
  59. public byte[] text;
  60. // Fields from ApplicationExtension
  61. // List of byte[]
  62. public List applicationIDs = null; // new ArrayList();
  63. // List of byte[]
  64. public List authenticationCodes = null; // new ArrayList();
  65. // List of byte[]
  66. public List applicationData = null; // new ArrayList();
  67. // Fields from CommentExtension
  68. // List of byte[]
  69. public List comments = null; // new ArrayList();
  70. public GIFImageMetadata() {
  71. super(true,
  72. nativeMetadataFormatName,
  73. "com.sun.imageio.plugins.gif.GIFImageMetadataFormat",
  74. null, null);
  75. }
  76. public boolean isReadOnly() {
  77. return true;
  78. }
  79. public Node getAsTree(String formatName) {
  80. if (formatName.equals(nativeMetadataFormatName)) {
  81. return getNativeTree();
  82. } else if (formatName.equals
  83. (IIOMetadataFormatImpl.standardMetadataFormatName)) {
  84. return getStandardTree();
  85. } else {
  86. throw new IllegalArgumentException("Not a recognized format!");
  87. }
  88. }
  89. private String toISO8859(byte[] data) {
  90. try {
  91. return new String(data, "ISO-8859-1");
  92. } catch (UnsupportedEncodingException e) {
  93. return "";
  94. }
  95. }
  96. private Node getNativeTree() {
  97. IIOMetadataNode node; // scratch node
  98. IIOMetadataNode root =
  99. new IIOMetadataNode(nativeMetadataFormatName);
  100. // Image descriptor
  101. node = new IIOMetadataNode("ImageDescriptor");
  102. node.setAttribute("imageLeftPosition",
  103. Integer.toString(imageLeftPosition));
  104. node.setAttribute("imageTopPosition",
  105. Integer.toString(imageTopPosition));
  106. node.setAttribute("imageWidth", Integer.toString(imageWidth));
  107. node.setAttribute("imageHeight", Integer.toString(imageHeight));
  108. node.setAttribute("interlaceFlag",
  109. interlaceFlag ? "true" : "false");
  110. root.appendChild(node);
  111. // Local color table
  112. if (localColorTable != null) {
  113. node = new IIOMetadataNode("LocalColorTable");
  114. int numEntries = localColorTable.length3;
  115. node.setAttribute("sizeOfLocalColorTable",
  116. Integer.toString(numEntries));
  117. node.setAttribute("sortFlag",
  118. sortFlag ? "TRUE" : "FALSE");
  119. for (int i = 0; i < numEntries; i++) {
  120. IIOMetadataNode entry =
  121. new IIOMetadataNode("ColorTableEntry");
  122. entry.setAttribute("index", Integer.toString(i));
  123. int r = localColorTable[3*i] & 0xff;
  124. int g = localColorTable[3*i + 1] & 0xff;
  125. int b = localColorTable[3*i + 2] & 0xff;
  126. entry.setAttribute("red", Integer.toString(r));
  127. entry.setAttribute("green", Integer.toString(g));
  128. entry.setAttribute("blue", Integer.toString(b));
  129. node.appendChild(entry);
  130. }
  131. root.appendChild(node);
  132. }
  133. // Graphic control extension
  134. node = new IIOMetadataNode("GraphicControlExtension");
  135. node.setAttribute("disposalMethod",
  136. disposalMethodNames[disposalMethod]);
  137. node.setAttribute("userInputFlag",
  138. userInputFlag ? "true" : "false");
  139. node.setAttribute("transparentColorFlag",
  140. transparentColorFlag ? "true" : "false");
  141. node.setAttribute("delayTime",
  142. Integer.toString(delayTime));
  143. node.setAttribute("transparentColorIndex",
  144. Integer.toString(transparentColorIndex));
  145. root.appendChild(node);
  146. if (hasPlainTextExtension) {
  147. node = new IIOMetadataNode("PlainTextExtension");
  148. node.setAttribute("textGridLeft",
  149. Integer.toString(textGridLeft));
  150. node.setAttribute("textGridTop",
  151. Integer.toString(textGridTop));
  152. node.setAttribute("textGridWidth",
  153. Integer.toString(textGridWidth));
  154. node.setAttribute("textGridHeight",
  155. Integer.toString(textGridHeight));
  156. node.setAttribute("characterCellWidth",
  157. Integer.toString(characterCellWidth));
  158. node.setAttribute("characterCellHeight",
  159. Integer.toString(characterCellHeight));
  160. node.setAttribute("textForegroundColor",
  161. Integer.toString(textForegroundColor));
  162. node.setAttribute("textBackgroundColor",
  163. Integer.toString(textBackgroundColor));
  164. node.setAttribute("text", toISO8859(text));
  165. root.appendChild(node);
  166. }
  167. // Application extensions
  168. int numAppExtensions = applicationIDs == null ?
  169. 0 : applicationIDs.size();
  170. if (numAppExtensions > 0) {
  171. node = new IIOMetadataNode("ApplicationExtensions");
  172. for (int i = 0; i < numAppExtensions; i++) {
  173. IIOMetadataNode appExtNode =
  174. new IIOMetadataNode("ApplicationExtension");
  175. byte[] applicationID = (byte[])applicationIDs.get(i);
  176. appExtNode.setAttribute("applicationID",
  177. toISO8859(applicationID));
  178. byte[] authenticationCode = (byte[])authenticationCodes.get(i);
  179. appExtNode.setAttribute("authenticationCode",
  180. toISO8859(authenticationCode));
  181. byte[] appData = (byte[])applicationData.get(i);
  182. appExtNode.setUserObject((byte[])appData.clone());
  183. node.appendChild(appExtNode);
  184. }
  185. root.appendChild(node);
  186. }
  187. // Comment extensions
  188. int numComments = comments == null ? 0 : comments.size();
  189. if (numComments > 0) {
  190. node = new IIOMetadataNode("CommentExtensions");
  191. for (int i = 0; i < numComments; i++) {
  192. IIOMetadataNode commentNode =
  193. new IIOMetadataNode("CommentExtension");
  194. byte[] comment = (byte[])comments.get(i);
  195. commentNode.setAttribute("value", toISO8859(comment));
  196. node.appendChild(commentNode);
  197. }
  198. root.appendChild(node);
  199. }
  200. return root;
  201. }
  202. public IIOMetadataNode getStandardChromaNode() {
  203. IIOMetadataNode chroma_node = new IIOMetadataNode("Chroma");
  204. IIOMetadataNode node = null; // scratch node
  205. node = new IIOMetadataNode("ColorSpaceType");
  206. node.setAttribute("name", "RGB");
  207. chroma_node.appendChild(node);
  208. node = new IIOMetadataNode("NumChannels");
  209. node.setAttribute("value", transparentColorFlag ? "4" : "3");
  210. chroma_node.appendChild(node);
  211. // Gamma not in format
  212. node = new IIOMetadataNode("BlackIsZero");
  213. node.setAttribute("value", "TRUE");
  214. chroma_node.appendChild(node);
  215. if (localColorTable != null) {
  216. node = new IIOMetadataNode("Palette");
  217. int numEntries = localColorTable.length3;
  218. for (int i = 0; i < numEntries; i++) {
  219. IIOMetadataNode entry =
  220. new IIOMetadataNode("PaletteEntry");
  221. entry.setAttribute("index", Integer.toString(i));
  222. entry.setAttribute("red",
  223. Integer.toString(localColorTable[3*i] & 0xff));
  224. entry.setAttribute("green",
  225. Integer.toString(localColorTable[3*i + 1] & 0xff));
  226. entry.setAttribute("blue",
  227. Integer.toString(localColorTable[3*i + 2] & 0xff));
  228. node.appendChild(entry);
  229. }
  230. chroma_node.appendChild(node);
  231. }
  232. // BackgroundIndex not in image
  233. // BackgroundColor not in format
  234. return chroma_node;
  235. }
  236. public IIOMetadataNode getStandardCompressionNode() {
  237. IIOMetadataNode compression_node = new IIOMetadataNode("Compression");
  238. IIOMetadataNode node = null; // scratch node
  239. node = new IIOMetadataNode("CompressionTypeName");
  240. node.setAttribute("value", "lzw");
  241. compression_node.appendChild(node);
  242. node = new IIOMetadataNode("Lossless");
  243. node.setAttribute("value", "TRUE");
  244. compression_node.appendChild(node);
  245. node = new IIOMetadataNode("NumProgressiveScans");
  246. node.setAttribute("value", interlaceFlag ? "4" : "1");
  247. compression_node.appendChild(node);
  248. // BitRate not in format
  249. return compression_node;
  250. }
  251. public IIOMetadataNode getStandardDataNode() {
  252. IIOMetadataNode data_node = new IIOMetadataNode("Data");
  253. IIOMetadataNode node = null; // scratch node
  254. // PlanarConfiguration not in format
  255. node = new IIOMetadataNode("SampleFormat");
  256. node.setAttribute("value", "Index");
  257. data_node.appendChild(node);
  258. // BitsPerSample not in image
  259. // SignificantBitsPerSample not in format
  260. // SampleMSB not in format
  261. return data_node;
  262. }
  263. public IIOMetadataNode getStandardDimensionNode() {
  264. IIOMetadataNode dimension_node = new IIOMetadataNode("Dimension");
  265. IIOMetadataNode node = null; // scratch node
  266. // PixelAspectRatio not in image
  267. node = new IIOMetadataNode("ImageOrientation");
  268. node.setAttribute("value", "Normal");
  269. dimension_node.appendChild(node);
  270. // HorizontalPixelSize not in format
  271. // VerticalPixelSize not in format
  272. // HorizontalPhysicalPixelSpacing not in format
  273. // VerticalPhysicalPixelSpacing not in format
  274. // HorizontalPosition not in format
  275. // VerticalPosition not in format
  276. node = new IIOMetadataNode("HorizontalPixelOffset");
  277. node.setAttribute("value", Integer.toString(imageLeftPosition));
  278. dimension_node.appendChild(node);
  279. node = new IIOMetadataNode("VerticalPixelOffset");
  280. node.setAttribute("value", Integer.toString(imageTopPosition));
  281. dimension_node.appendChild(node);
  282. // HorizontalScreenSize not in image
  283. // VerticalScreenSize not in image
  284. return dimension_node;
  285. }
  286. // Document not in image
  287. public IIOMetadataNode getStandardTextNode() {
  288. if (comments == null) {
  289. return null;
  290. }
  291. Iterator commentIter = comments.iterator();
  292. if (!commentIter.hasNext()) {
  293. return null;
  294. }
  295. IIOMetadataNode text_node = new IIOMetadataNode("Text");
  296. IIOMetadataNode node = null; // scratch node
  297. while (commentIter.hasNext()) {
  298. byte[] comment = (byte[])commentIter.next();
  299. String s = null;
  300. try {
  301. s = new String(comment, "ISO-8859-1");
  302. } catch (UnsupportedEncodingException e) {
  303. throw new RuntimeException("Encoding ISO-8859-1 unknown!");
  304. }
  305. node = new IIOMetadataNode("TextEntry");
  306. node.setAttribute("value", s);
  307. node.setAttribute("encoding", "ISO-8859-1");
  308. node.setAttribute("compression", "none");
  309. text_node.appendChild(node);
  310. }
  311. return text_node;
  312. }
  313. public IIOMetadataNode getStandardTransparencyNode() {
  314. if (!transparentColorFlag) {
  315. return null;
  316. }
  317. IIOMetadataNode transparency_node =
  318. new IIOMetadataNode("Transparency");
  319. IIOMetadataNode node = null; // scratch node
  320. // Alpha not in format
  321. node = new IIOMetadataNode("TransparentIndex");
  322. node.setAttribute("value",
  323. Integer.toString(transparentColorIndex));
  324. transparency_node.appendChild(node);
  325. // TransparentColor not in format
  326. // TileTransparencies not in format
  327. // TileOpacities not in format
  328. return transparency_node;
  329. }
  330. public void setFromTree(String formatName, Node root) {
  331. throw new IllegalStateException("Metadata is read-only!");
  332. }
  333. public void mergeTree(String formatName, Node root) {
  334. throw new IllegalStateException("Metadata is read-only!");
  335. }
  336. public void reset() {
  337. throw new IllegalStateException("Metadata is read-only!");
  338. }
  339. }