1 /* 2 * Copyright (c) 2001, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.imageio.plugins.jpeg; 27 28 import javax.imageio.metadata.IIOInvalidTreeException; 29 import javax.imageio.metadata.IIOMetadataNode; 30 import javax.imageio.stream.ImageOutputStream; 31 32 import java.io.IOException; 33 34 import org.w3c.dom.Node; 35 import org.w3c.dom.NamedNodeMap; 36 37 /** 38 * All metadata is stored in MarkerSegments. Marker segments 39 * that we know about are stored in subclasses of this 40 * basic class, which used for unrecognized APPn marker 41 * segments. XXX break out UnknownMarkerSegment as a subclass 42 * and make this abstract, avoiding unused data field. 43 */ 44 class MarkerSegment implements Cloneable { 45 protected static final int LENGTH_SIZE = 2; // length is 2 bytes 46 int tag; // See JPEG.java 47 int length; /* Sometimes needed by subclasses; doesn't include 48 itself. Meaningful only if constructed from a stream */ 49 byte [] data = null; // Raw segment data, used for unrecognized segments 50 boolean unknown = false; // Set to true if the tag is not recognized 51 52 /** 53 * Constructor for creating <code>MarkerSegment</code>s by reading 54 * from an <code>ImageInputStream</code>. 55 */ 56 MarkerSegment(JPEGBuffer buffer) throws IOException { 57 58 buffer.loadBuf(3); // tag plus length 59 tag = buffer.buf[buffer.bufPtr++] & 0xff; 60 length = (buffer.buf[buffer.bufPtr++] & 0xff) << 8; 61 length |= buffer.buf[buffer.bufPtr++] & 0xff; 62 length -= 2; // JPEG length includes itself, we don't 63 buffer.bufAvail -= 3; 64 // Now that we know the true length, ensure that we've got it, 65 // or at least a bufferful if length is too big. 66 buffer.loadBuf(length); 67 } 68 69 /** 70 * Constructor used when creating segments other than by 71 * reading them from a stream. 72 */ 73 MarkerSegment(int tag) { 74 this.tag = tag; 75 length = 0; 76 } 77 78 /** 79 * Construct a MarkerSegment from an "unknown" DOM Node. 80 */ 81 MarkerSegment(Node node) throws IIOInvalidTreeException { 82 // The type of node should have been verified already. 83 // get the attribute and assign it to the tag 84 tag = getAttributeValue(node, 85 null, 86 "MarkerTag", 87 0, 255, 88 true); 89 length = 0; 90 // get the user object and clone it to the data 91 if (node instanceof IIOMetadataNode) { 92 IIOMetadataNode iioNode = (IIOMetadataNode) node; 93 try { 94 data = (byte []) iioNode.getUserObject(); 95 } catch (Exception e) { 96 IIOInvalidTreeException newGuy = 97 new IIOInvalidTreeException 98 ("Can't get User Object", node); 99 newGuy.initCause(e); 100 throw newGuy; 101 } 102 } else { 103 throw new IIOInvalidTreeException 104 ("Node must have User Object", node); 105 } 106 } 107 108 /** 109 * Deep copy of data array. 110 */ 111 protected Object clone() { 112 MarkerSegment newGuy = null; 113 try { 114 newGuy = (MarkerSegment) super.clone(); 115 } catch (CloneNotSupportedException e) {} // won't happen 116 if (this.data != null) { 117 newGuy.data = data.clone(); 118 } 119 return newGuy; 120 } 121 122 /** 123 * We have determined that we don't know the type, so load 124 * the data using the length parameter. 125 */ 126 void loadData(JPEGBuffer buffer) throws IOException { 127 data = new byte[length]; 128 buffer.readData(data); 129 } 130 131 IIOMetadataNode getNativeNode() { 132 IIOMetadataNode node = new IIOMetadataNode("unknown"); 133 node.setAttribute("MarkerTag", Integer.toString(tag)); 134 node.setUserObject(data); 135 136 return node; 137 } 138 139 static int getAttributeValue(Node node, 140 NamedNodeMap attrs, 141 String name, 142 int min, 143 int max, 144 boolean required) 145 throws IIOInvalidTreeException { 146 if (attrs == null) { 147 attrs = node.getAttributes(); 148 } 149 String valueString = attrs.getNamedItem(name).getNodeValue(); 150 int value = -1; 151 if (valueString == null) { 152 if (required) { 153 throw new IIOInvalidTreeException 154 (name + " attribute not found", node); 155 } 156 } else { 157 value = Integer.parseInt(valueString); 158 if ((value < min) || (value > max)) { 159 throw new IIOInvalidTreeException 160 (name + " attribute out of range", node); 161 } 162 } 163 return value; 164 } 165 166 /** 167 * Writes the marker, tag, and length. Note that length 168 * should be verified by the caller as a correct JPEG 169 * length, i.e it includes itself. 170 */ 171 void writeTag(ImageOutputStream ios) throws IOException { 172 ios.write(0xff); 173 ios.write(tag); 174 write2bytes(ios, length); 175 } 176 177 /** 178 * Writes the data for this segment to the stream in 179 * valid JPEG format. 180 */ 181 void write(ImageOutputStream ios) throws IOException { 182 length = 2 + ((data != null) ? data.length : 0); 183 writeTag(ios); 184 if (data != null) { 185 ios.write(data); 186 } 187 } 188 189 static void write2bytes(ImageOutputStream ios, 190 int value) throws IOException { 191 ios.write((value >> 8) & 0xff); 192 ios.write(value & 0xff); 193 194 } 195 196 void printTag(String prefix) { 197 System.out.println(prefix + " marker segment - marker = 0x" 198 + Integer.toHexString(tag)); 199 System.out.println("length: " + length); 200 } 201 202 void print() { 203 printTag("Unknown"); 204 if (length > 10) { 205 System.out.print("First 5 bytes:"); 206 for (int i=0;i<5;i++) { 207 System.out.print(" Ox" 208 + Integer.toHexString((int)data[i])); 209 } 210 System.out.print("\nLast 5 bytes:"); 211 for (int i=data.length-5;i<data.length;i++) { 212 System.out.print(" Ox" 213 + Integer.toHexString((int)data[i])); 214 } 215 } else { 216 System.out.print("Data:"); 217 for (int i=0;i<data.length;i++) { 218 System.out.print(" Ox" 219 + Integer.toHexString((int)data[i])); 220 } 221 } 222 System.out.println(); 223 } 224 }