View Javadoc
1   /*
2    * Copyright (c) 2001, 2013, 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</code>s by reading
55       * from an <code>ImageInputStream</code>.
56       */
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       */
78      MarkerSegment(int tag) {
79          this.tag = tag;
80          length = 0;
81      }
82  
83      /**
84       * Construct a MarkerSegment from an "unknown" DOM Node.
85       */
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      */
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 = (byte[]) 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      */
131     void loadData(JPEGBuffer buffer) throws IOException {
132         data = new byte[length];
133         buffer.readData(data);
134     }
135 
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 
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      */
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      */
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 
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 
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 
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 }