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 }