1 /* 2 * Copyright (c) 2000, 2014, 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.gif; 27 28 import java.io.UnsupportedEncodingException; 29 import java.util.ArrayList; 30 import java.util.Iterator; 31 import java.util.List; 32 import javax.imageio.ImageTypeSpecifier; 33 import javax.imageio.metadata.IIOInvalidTreeException; 34 import javax.imageio.metadata.IIOMetadata; 35 import javax.imageio.metadata.IIOMetadataNode; 36 import javax.imageio.metadata.IIOMetadataFormat; 37 import javax.imageio.metadata.IIOMetadataFormatImpl; 38 import org.w3c.dom.Node; 39 40 public class GIFImageMetadata extends GIFMetadata { 41 42 // package scope 43 static final String 44 nativeMetadataFormatName = "javax_imageio_gif_image_1.0"; 45 46 static final String[] disposalMethodNames = { 47 "none", 48 "doNotDispose", 49 "restoreToBackgroundColor", 50 "restoreToPrevious", 51 "undefinedDisposalMethod4", 52 "undefinedDisposalMethod5", 53 "undefinedDisposalMethod6", 54 "undefinedDisposalMethod7" 55 }; 56 57 // Fields from Image Descriptor 58 public int imageLeftPosition; 59 public int imageTopPosition; 60 public int imageWidth; 61 public int imageHeight; 62 public boolean interlaceFlag = false; 63 public boolean sortFlag = false; 64 public byte[] localColorTable = null; 65 66 // Fields from Graphic Control Extension 67 public int disposalMethod = 0; 68 public boolean userInputFlag = false; 69 public boolean transparentColorFlag = false; 70 public int delayTime = 0; 71 public int transparentColorIndex = 0; 72 73 // Fields from Plain Text Extension 74 public boolean hasPlainTextExtension = false; 75 public int textGridLeft; 76 public int textGridTop; 77 public int textGridWidth; 78 public int textGridHeight; 79 public int characterCellWidth; 80 public int characterCellHeight; 81 public int textForegroundColor; 82 public int textBackgroundColor; 83 public byte[] text; 84 85 // Fields from ApplicationExtension 86 // List of byte[] 87 public List<byte[]> applicationIDs = null; 88 89 // List of byte[] 90 public List<byte[]> authenticationCodes = null; 91 92 // List of byte[] 93 public List<byte[]> applicationData = null; 94 95 // Fields from CommentExtension 96 // List of byte[] 97 public List<byte[]> comments = null; 98 GIFImageMetadata(boolean standardMetadataFormatSupported, String nativeMetadataFormatName, String nativeMetadataFormatClassName, String[] extraMetadataFormatNames, String[] extraMetadataFormatClassNames)99 protected GIFImageMetadata(boolean standardMetadataFormatSupported, 100 String nativeMetadataFormatName, 101 String nativeMetadataFormatClassName, 102 String[] extraMetadataFormatNames, 103 String[] extraMetadataFormatClassNames) 104 { 105 super(standardMetadataFormatSupported, 106 nativeMetadataFormatName, 107 nativeMetadataFormatClassName, 108 extraMetadataFormatNames, 109 extraMetadataFormatClassNames); 110 } 111 GIFImageMetadata()112 public GIFImageMetadata() { 113 this(true, 114 nativeMetadataFormatName, 115 "com.sun.imageio.plugins.gif.GIFImageMetadataFormat", 116 null, null); 117 } 118 isReadOnly()119 public boolean isReadOnly() { 120 return true; 121 } 122 getAsTree(String formatName)123 public Node getAsTree(String formatName) { 124 if (formatName.equals(nativeMetadataFormatName)) { 125 return getNativeTree(); 126 } else if (formatName.equals 127 (IIOMetadataFormatImpl.standardMetadataFormatName)) { 128 return getStandardTree(); 129 } else { 130 throw new IllegalArgumentException("Not a recognized format!"); 131 } 132 } 133 toISO8859(byte[] data)134 private String toISO8859(byte[] data) { 135 try { 136 return new String(data, "ISO-8859-1"); 137 } catch (UnsupportedEncodingException e) { 138 return ""; 139 } 140 } 141 getNativeTree()142 private Node getNativeTree() { 143 IIOMetadataNode node; // scratch node 144 IIOMetadataNode root = 145 new IIOMetadataNode(nativeMetadataFormatName); 146 147 // Image descriptor 148 node = new IIOMetadataNode("ImageDescriptor"); 149 node.setAttribute("imageLeftPosition", 150 Integer.toString(imageLeftPosition)); 151 node.setAttribute("imageTopPosition", 152 Integer.toString(imageTopPosition)); 153 node.setAttribute("imageWidth", Integer.toString(imageWidth)); 154 node.setAttribute("imageHeight", Integer.toString(imageHeight)); 155 node.setAttribute("interlaceFlag", 156 interlaceFlag ? "TRUE" : "FALSE"); 157 root.appendChild(node); 158 159 // Local color table 160 if (localColorTable != null) { 161 node = new IIOMetadataNode("LocalColorTable"); 162 int numEntries = localColorTable.length/3; 163 node.setAttribute("sizeOfLocalColorTable", 164 Integer.toString(numEntries)); 165 node.setAttribute("sortFlag", 166 sortFlag ? "TRUE" : "FALSE"); 167 168 for (int i = 0; i < numEntries; i++) { 169 IIOMetadataNode entry = 170 new IIOMetadataNode("ColorTableEntry"); 171 entry.setAttribute("index", Integer.toString(i)); 172 int r = localColorTable[3*i] & 0xff; 173 int g = localColorTable[3*i + 1] & 0xff; 174 int b = localColorTable[3*i + 2] & 0xff; 175 entry.setAttribute("red", Integer.toString(r)); 176 entry.setAttribute("green", Integer.toString(g)); 177 entry.setAttribute("blue", Integer.toString(b)); 178 node.appendChild(entry); 179 } 180 root.appendChild(node); 181 } 182 183 // Graphic control extension 184 node = new IIOMetadataNode("GraphicControlExtension"); 185 node.setAttribute("disposalMethod", 186 disposalMethodNames[disposalMethod]); 187 node.setAttribute("userInputFlag", 188 userInputFlag ? "TRUE" : "FALSE"); 189 node.setAttribute("transparentColorFlag", 190 transparentColorFlag ? "TRUE" : "FALSE"); 191 node.setAttribute("delayTime", 192 Integer.toString(delayTime)); 193 node.setAttribute("transparentColorIndex", 194 Integer.toString(transparentColorIndex)); 195 root.appendChild(node); 196 197 if (hasPlainTextExtension) { 198 node = new IIOMetadataNode("PlainTextExtension"); 199 node.setAttribute("textGridLeft", 200 Integer.toString(textGridLeft)); 201 node.setAttribute("textGridTop", 202 Integer.toString(textGridTop)); 203 node.setAttribute("textGridWidth", 204 Integer.toString(textGridWidth)); 205 node.setAttribute("textGridHeight", 206 Integer.toString(textGridHeight)); 207 node.setAttribute("characterCellWidth", 208 Integer.toString(characterCellWidth)); 209 node.setAttribute("characterCellHeight", 210 Integer.toString(characterCellHeight)); 211 node.setAttribute("textForegroundColor", 212 Integer.toString(textForegroundColor)); 213 node.setAttribute("textBackgroundColor", 214 Integer.toString(textBackgroundColor)); 215 node.setAttribute("text", toISO8859(text)); 216 217 root.appendChild(node); 218 } 219 220 // Application extensions 221 int numAppExtensions = applicationIDs == null ? 222 0 : applicationIDs.size(); 223 if (numAppExtensions > 0) { 224 node = new IIOMetadataNode("ApplicationExtensions"); 225 for (int i = 0; i < numAppExtensions; i++) { 226 IIOMetadataNode appExtNode = 227 new IIOMetadataNode("ApplicationExtension"); 228 byte[] applicationID = applicationIDs.get(i); 229 appExtNode.setAttribute("applicationID", 230 toISO8859(applicationID)); 231 byte[] authenticationCode = authenticationCodes.get(i); 232 appExtNode.setAttribute("authenticationCode", 233 toISO8859(authenticationCode)); 234 byte[] appData = applicationData.get(i); 235 appExtNode.setUserObject(appData.clone()); 236 node.appendChild(appExtNode); 237 } 238 239 root.appendChild(node); 240 } 241 242 // Comment extensions 243 int numComments = comments == null ? 0 : comments.size(); 244 if (numComments > 0) { 245 node = new IIOMetadataNode("CommentExtensions"); 246 for (int i = 0; i < numComments; i++) { 247 IIOMetadataNode commentNode = 248 new IIOMetadataNode("CommentExtension"); 249 byte[] comment = comments.get(i); 250 commentNode.setAttribute("value", toISO8859(comment)); 251 node.appendChild(commentNode); 252 } 253 254 root.appendChild(node); 255 } 256 257 return root; 258 } 259 getStandardChromaNode()260 public IIOMetadataNode getStandardChromaNode() { 261 IIOMetadataNode chroma_node = new IIOMetadataNode("Chroma"); 262 IIOMetadataNode node = null; // scratch node 263 264 node = new IIOMetadataNode("ColorSpaceType"); 265 node.setAttribute("name", "RGB"); 266 chroma_node.appendChild(node); 267 268 node = new IIOMetadataNode("NumChannels"); 269 node.setAttribute("value", transparentColorFlag ? "4" : "3"); 270 chroma_node.appendChild(node); 271 272 // Gamma not in format 273 274 node = new IIOMetadataNode("BlackIsZero"); 275 node.setAttribute("value", "TRUE"); 276 chroma_node.appendChild(node); 277 278 if (localColorTable != null) { 279 node = new IIOMetadataNode("Palette"); 280 int numEntries = localColorTable.length/3; 281 for (int i = 0; i < numEntries; i++) { 282 IIOMetadataNode entry = 283 new IIOMetadataNode("PaletteEntry"); 284 entry.setAttribute("index", Integer.toString(i)); 285 entry.setAttribute("red", 286 Integer.toString(localColorTable[3*i] & 0xff)); 287 entry.setAttribute("green", 288 Integer.toString(localColorTable[3*i + 1] & 0xff)); 289 entry.setAttribute("blue", 290 Integer.toString(localColorTable[3*i + 2] & 0xff)); 291 node.appendChild(entry); 292 } 293 chroma_node.appendChild(node); 294 } 295 296 // BackgroundIndex not in image 297 // BackgroundColor not in format 298 299 return chroma_node; 300 } 301 getStandardCompressionNode()302 public IIOMetadataNode getStandardCompressionNode() { 303 IIOMetadataNode compression_node = new IIOMetadataNode("Compression"); 304 IIOMetadataNode node = null; // scratch node 305 306 node = new IIOMetadataNode("CompressionTypeName"); 307 node.setAttribute("value", "lzw"); 308 compression_node.appendChild(node); 309 310 node = new IIOMetadataNode("Lossless"); 311 node.setAttribute("value", "TRUE"); 312 compression_node.appendChild(node); 313 314 node = new IIOMetadataNode("NumProgressiveScans"); 315 node.setAttribute("value", interlaceFlag ? "4" : "1"); 316 compression_node.appendChild(node); 317 318 // BitRate not in format 319 320 return compression_node; 321 } 322 getStandardDataNode()323 public IIOMetadataNode getStandardDataNode() { 324 IIOMetadataNode data_node = new IIOMetadataNode("Data"); 325 IIOMetadataNode node = null; // scratch node 326 327 // PlanarConfiguration not in format 328 329 node = new IIOMetadataNode("SampleFormat"); 330 node.setAttribute("value", "Index"); 331 data_node.appendChild(node); 332 333 // BitsPerSample not in image 334 // SignificantBitsPerSample not in format 335 // SampleMSB not in format 336 337 return data_node; 338 } 339 getStandardDimensionNode()340 public IIOMetadataNode getStandardDimensionNode() { 341 IIOMetadataNode dimension_node = new IIOMetadataNode("Dimension"); 342 IIOMetadataNode node = null; // scratch node 343 344 // PixelAspectRatio not in image 345 346 node = new IIOMetadataNode("ImageOrientation"); 347 node.setAttribute("value", "Normal"); 348 dimension_node.appendChild(node); 349 350 // HorizontalPixelSize not in format 351 // VerticalPixelSize not in format 352 // HorizontalPhysicalPixelSpacing not in format 353 // VerticalPhysicalPixelSpacing not in format 354 // HorizontalPosition not in format 355 // VerticalPosition not in format 356 357 node = new IIOMetadataNode("HorizontalPixelOffset"); 358 node.setAttribute("value", Integer.toString(imageLeftPosition)); 359 dimension_node.appendChild(node); 360 361 node = new IIOMetadataNode("VerticalPixelOffset"); 362 node.setAttribute("value", Integer.toString(imageTopPosition)); 363 dimension_node.appendChild(node); 364 365 // HorizontalScreenSize not in image 366 // VerticalScreenSize not in image 367 368 return dimension_node; 369 } 370 371 // Document not in image 372 getStandardTextNode()373 public IIOMetadataNode getStandardTextNode() { 374 if (comments == null) { 375 return null; 376 } 377 Iterator<byte[]> commentIter = comments.iterator(); 378 if (!commentIter.hasNext()) { 379 return null; 380 } 381 382 IIOMetadataNode text_node = new IIOMetadataNode("Text"); 383 IIOMetadataNode node = null; // scratch node 384 385 while (commentIter.hasNext()) { 386 byte[] comment = commentIter.next(); 387 String s = null; 388 try { 389 s = new String(comment, "ISO-8859-1"); 390 } catch (UnsupportedEncodingException e) { 391 throw new RuntimeException("Encoding ISO-8859-1 unknown!"); 392 } 393 394 node = new IIOMetadataNode("TextEntry"); 395 node.setAttribute("value", s); 396 node.setAttribute("encoding", "ISO-8859-1"); 397 node.setAttribute("compression", "none"); 398 text_node.appendChild(node); 399 } 400 401 return text_node; 402 } 403 getStandardTransparencyNode()404 public IIOMetadataNode getStandardTransparencyNode() { 405 if (!transparentColorFlag) { 406 return null; 407 } 408 409 IIOMetadataNode transparency_node = 410 new IIOMetadataNode("Transparency"); 411 IIOMetadataNode node = null; // scratch node 412 413 // Alpha not in format 414 415 node = new IIOMetadataNode("TransparentIndex"); 416 node.setAttribute("value", 417 Integer.toString(transparentColorIndex)); 418 transparency_node.appendChild(node); 419 420 // TransparentColor not in format 421 // TileTransparencies not in format 422 // TileOpacities not in format 423 424 return transparency_node; 425 } 426 setFromTree(String formatName, Node root)427 public void setFromTree(String formatName, Node root) 428 throws IIOInvalidTreeException 429 { 430 throw new IllegalStateException("Metadata is read-only!"); 431 } 432 mergeNativeTree(Node root)433 protected void mergeNativeTree(Node root) throws IIOInvalidTreeException 434 { 435 throw new IllegalStateException("Metadata is read-only!"); 436 } 437 mergeStandardTree(Node root)438 protected void mergeStandardTree(Node root) throws IIOInvalidTreeException 439 { 440 throw new IllegalStateException("Metadata is read-only!"); 441 } 442 reset()443 public void reset() { 444 throw new IllegalStateException("Metadata is read-only!"); 445 } 446 } 447