1 /*
2  * Copyright (c) 2001, 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.jpeg;
27 
28 import javax.imageio.metadata.IIOInvalidTreeException;
29 import javax.imageio.metadata.IIOMetadataNode;
30 import javax.imageio.stream.ImageOutputStream;
31 import javax.imageio.IIOException;
32 
33 import java.io.IOException;
34 
35 import org.w3c.dom.Node;
36 import org.w3c.dom.NamedNodeMap;
37 
38 /**
39  * All metadata is stored in MarkerSegments.  Marker segments
40  * that we know about are stored in subclasses of this
41  * basic class, which used for unrecognized APPn marker
42  * segments.  XXX break out UnknownMarkerSegment as a subclass
43  * and make this abstract, avoiding unused data field.
44  */
45 class MarkerSegment implements Cloneable {
46     protected static final int LENGTH_SIZE = 2; // length is 2 bytes
47     int tag;      // See JPEG.java
48     int length;    /* Sometimes needed by subclasses; doesn't include
49                       itself.  Meaningful only if constructed from a stream */
50     byte [] data = null;  // Raw segment data, used for unrecognized segments
51     boolean unknown = false; // Set to true if the tag is not recognized
52 
53     /**
54      * Constructor for creating {@code MarkerSegment}s by reading
55      * from an {@code ImageInputStream}.
56      */
MarkerSegment(JPEGBuffer buffer)57     MarkerSegment(JPEGBuffer buffer) throws IOException {
58 
59         buffer.loadBuf(3);  // tag plus length
60         tag = buffer.buf[buffer.bufPtr++] & 0xff;
61         length = (buffer.buf[buffer.bufPtr++] & 0xff) << 8;
62         length |= buffer.buf[buffer.bufPtr++] & 0xff;
63         length -= 2;  // JPEG length includes itself, we don't
64 
65         if (length < 0) {
66             throw new IIOException("Invalid segment length: " + length);
67         }
68         buffer.bufAvail -= 3;
69         // Now that we know the true length, ensure that we've got it,
70         // or at least a bufferful if length is too big.
71         buffer.loadBuf(length);
72     }
73 
74     /**
75      * Constructor used when creating segments other than by
76      * reading them from a stream.
77      */
MarkerSegment(int tag)78     MarkerSegment(int tag) {
79         this.tag = tag;
80         length = 0;
81     }
82 
83     /**
84      * Construct a MarkerSegment from an "unknown" DOM Node.
85      */
MarkerSegment(Node node)86     MarkerSegment(Node node) throws IIOInvalidTreeException {
87         // The type of node should have been verified already.
88         // get the attribute and assign it to the tag
89         tag = getAttributeValue(node,
90                                 null,
91                                 "MarkerTag",
92                                 0, 255,
93                                 true);
94         length = 0;
95         // get the user object and clone it to the data
96         if (node instanceof IIOMetadataNode) {
97             IIOMetadataNode iioNode = (IIOMetadataNode) node;
98             try {
99                 data = (byte []) iioNode.getUserObject();
100             } catch (Exception e) {
101                 IIOInvalidTreeException newGuy =
102                     new IIOInvalidTreeException
103                     ("Can't get User Object", node);
104                 newGuy.initCause(e);
105                 throw newGuy;
106             }
107         } else {
108             throw new IIOInvalidTreeException
109                 ("Node must have User Object", node);
110         }
111     }
112 
113     /**
114      * Deep copy of data array.
115      */
clone()116     protected Object clone() {
117         MarkerSegment newGuy = null;
118         try {
119             newGuy = (MarkerSegment) super.clone();
120         } catch (CloneNotSupportedException e) {} // won't happen
121         if (this.data != null) {
122             newGuy.data = data.clone();
123         }
124         return newGuy;
125     }
126 
127     /**
128      * We have determined that we don't know the type, so load
129      * the data using the length parameter.
130      */
loadData(JPEGBuffer buffer)131     void loadData(JPEGBuffer buffer) throws IOException {
132         data = new byte[length];
133         buffer.readData(data);
134     }
135 
getNativeNode()136     IIOMetadataNode getNativeNode() {
137         IIOMetadataNode node = new IIOMetadataNode("unknown");
138         node.setAttribute("MarkerTag", Integer.toString(tag));
139         node.setUserObject(data);
140 
141         return node;
142     }
143 
getAttributeValue(Node node, NamedNodeMap attrs, String name, int min, int max, boolean required)144     static int getAttributeValue(Node node,
145                                  NamedNodeMap attrs,
146                                  String name,
147                                  int min,
148                                  int max,
149                                  boolean required)
150         throws IIOInvalidTreeException {
151         if (attrs == null) {
152             attrs = node.getAttributes();
153         }
154         String valueString = attrs.getNamedItem(name).getNodeValue();
155         int value = -1;
156         if (valueString == null) {
157             if (required) {
158                 throw new IIOInvalidTreeException
159                     (name + " attribute not found", node);
160             }
161         } else {
162               value = Integer.parseInt(valueString);
163               if ((value < min) || (value > max)) {
164                   throw new IIOInvalidTreeException
165                       (name + " attribute out of range", node);
166               }
167         }
168         return value;
169     }
170 
171     /**
172      * Writes the marker, tag, and length.  Note that length
173      * should be verified by the caller as a correct JPEG
174      * length, i.e it includes itself.
175      */
writeTag(ImageOutputStream ios)176     void writeTag(ImageOutputStream ios) throws IOException {
177         ios.write(0xff);
178         ios.write(tag);
179         write2bytes(ios, length);
180     }
181 
182     /**
183      * Writes the data for this segment to the stream in
184      * valid JPEG format.
185      */
write(ImageOutputStream ios)186     void write(ImageOutputStream ios) throws IOException {
187         length = 2 + ((data != null) ? data.length : 0);
188         writeTag(ios);
189         if (data != null) {
190             ios.write(data);
191         }
192     }
193 
write2bytes(ImageOutputStream ios, int value)194     static void write2bytes(ImageOutputStream ios,
195                             int value) throws IOException {
196         ios.write((value >> 8) & 0xff);
197         ios.write(value & 0xff);
198 
199     }
200 
printTag(String prefix)201     void printTag(String prefix) {
202         System.out.println(prefix + " marker segment - marker = 0x"
203                            + Integer.toHexString(tag));
204         System.out.println("length: " + length);
205     }
206 
print()207     void print() {
208         printTag("Unknown");
209         if (length > 10) {
210             System.out.print("First 5 bytes:");
211             for (int i=0;i<5;i++) {
212                 System.out.print(" Ox"
213                                  + Integer.toHexString((int)data[i]));
214             }
215             System.out.print("\nLast 5 bytes:");
216             for (int i=data.length-5;i<data.length;i++) {
217                 System.out.print(" Ox"
218                                  + Integer.toHexString((int)data[i]));
219             }
220         } else {
221             System.out.print("Data:");
222             for (int i=0;i<data.length;i++) {
223                 System.out.print(" Ox"
224                                  + Integer.toHexString((int)data[i]));
225             }
226         }
227         System.out.println();
228     }
229 }
230