001: // Exif.java
002: // $Id: ExifData.java,v 1.1 2003/07/23 12:08:02 ylafon Exp $
003: // Copyright (c) 2003 Norman Walsh
004: // Please first read the full copyright statement in file COPYRIGHT
005:
006: package org.w3c.tools.jpeg;
007:
008: import java.util.Hashtable;
009:
010: /**
011: * An internal API for decoding EXIF data.
012: *
013: * <p>This class identifies a byte array as valid EXIF and provides methods
014: * for converting the internal representation of types to equivalent Java
015: * types.
016: * </p>
017: *
018: * <p>This is a separate class in order to facilitate writing decoders for
019: * unknown fields.</p>
020: *
021: * <p>Special note: string values are %-encoded to protect the caller from
022: * values that might not be legitimate UTF-8 strings.</p>
023: *
024: * <p><b>Bugs:</b></p>
025: * <p>The EXIF format includes unsigned integers which aren't directly
026: * available in Java. Unless I'm mistaken, they'd have to be turned into
027: * longs. But that would
028: * be inconvenient in other APIs. For the moment, I'm just treating them all as
029: * signed. I've never seen an EXIF unsigned value too large to represent
030: * in a Java int.</p>
031: *
032: * @version $Revision: 1.1 $
033: * @author Norman Walsh
034: * @see Exif
035: */
036: public class ExifData {
037: public static final int bytesPerFormat[] = { 0, 1, 1, 2, 4, 8, 1,
038: 1, 2, 4, 8, 4, 8 };
039: public static final int NUM_FORMATS = 12;
040: public static final int FMT_BYTE = 1;
041: public static final int FMT_STRING = 2;
042: public static final int FMT_USHORT = 3;
043: public static final int FMT_ULONG = 4;
044: public static final int FMT_URATIONAL = 5;
045: public static final int FMT_SBYTE = 6;
046: public static final int FMT_UNDEFINED = 7;
047: public static final int FMT_SSHORT = 8;
048: public static final int FMT_SLONG = 9;
049: public static final int FMT_SRATIONAL = 10;
050: public static final int FMT_SINGLE = 11;
051: public static final int FMT_DOUBLE = 12;
052:
053: private byte[] data = null;
054: private boolean intelOrder = false;
055:
056: public ExifData(byte[] exifData) {
057: String dataStr = new String(exifData);
058: if (exifData.length <= 4
059: || !"Exif".equals(dataStr.substring(0, 4))) {
060: // Not really EXIF data
061: return;
062: }
063:
064: String byteOrderMarker = dataStr.substring(6, 8);
065: if ("II".equals(byteOrderMarker)) {
066: intelOrder = true;
067: } else if ("MM".equals(byteOrderMarker)) {
068: intelOrder = false;
069: } else {
070: // bogus!
071: System.err.println("Bogus byte order in EXIF data.");
072: return;
073: }
074:
075: data = exifData;
076:
077: int checkValue = get16u(8);
078: if (checkValue != 0x2a) {
079: data = null;
080: System.err.println("Check value fails: 0x"
081: + Integer.toHexString(checkValue));
082: return;
083: }
084: }
085:
086: public boolean isExifData() {
087: return (data != null);
088: }
089:
090: public int get16s(int offset) {
091: if (data == null) {
092: return 0;
093: }
094:
095: int hi, lo;
096:
097: if (intelOrder) {
098: hi = data[offset + 1];
099: lo = data[offset];
100: } else {
101: hi = data[offset];
102: lo = data[offset + 1];
103: }
104:
105: lo = lo & 0xFF;
106: hi = hi & 0xFF;
107:
108: return (hi << 8) + lo;
109: }
110:
111: public int get16u(int offset) {
112: if (data == null) {
113: return 0;
114: }
115:
116: int value = get16s(offset);
117: value = value & 0xFFFF;
118: return value;
119: }
120:
121: public int get32s(int offset) {
122: if (data == null) {
123: return 0;
124: }
125:
126: int n1, n2, n3, n4;
127:
128: if (intelOrder) {
129: n1 = data[offset + 3] & 0xFF;
130: n2 = data[offset + 2] & 0xFF;
131: n3 = data[offset + 1] & 0xFF;
132: n4 = data[offset] & 0xFF;
133: } else {
134: n1 = data[offset] & 0xFF;
135: n2 = data[offset + 1] & 0xFF;
136: n3 = data[offset + 2] & 0xFF;
137: n4 = data[offset + 3] & 0xFF;
138: }
139:
140: int value = (n1 << 24) + (n2 << 16) + (n3 << 8) + n4;
141:
142: return value;
143: }
144:
145: public int get32u(int offset) {
146: if (data == null) {
147: return 0;
148: }
149:
150: // I don't know how to represent an unsigned in Java!
151: return get32s(offset);
152: }
153:
154: public byte[] getBytes(int offset, int length) {
155: if (data == null || length == 0) {
156: return null;
157: }
158:
159: byte[] raw = new byte[length];
160: for (int count = offset; length > 0; count++, length--) {
161: raw[count - offset] = data[count];
162: }
163:
164: return raw;
165: }
166:
167: public String getString(int offset, int length) {
168: return getString(offset, length, true);
169: }
170:
171: public String getUndefined(int offset, int length) {
172: return getString(offset, length, false);
173: }
174:
175: protected String getString(int offset, int length,
176: boolean nullTerminated) {
177: if (data == null) {
178: return "";
179: }
180:
181: String result = "";
182:
183: for (int count = offset; (length > 0)
184: && (!nullTerminated || data[count] != 0); count++, length--) {
185: short ub = data[count];
186: ub = (short) (ub & 0xFF);
187:
188: String ch = "" + (char) ub;
189: if ((ub == '%') || (ub < ' ') || (ub > '~')) {
190: ch = Integer.toHexString((char) ub);
191: if (ch.length() < 2) {
192: ch = "0" + ch;
193: }
194: ch = "%" + ch;
195: }
196: result += ch;
197: }
198:
199: return result;
200: }
201:
202: public double convertAnyValue(int format, int offset) {
203: if (data == null) {
204: return 0.0;
205: }
206:
207: double value = 0.0;
208:
209: switch (format) {
210: case FMT_SBYTE:
211: value = data[offset];
212: break;
213: case FMT_BYTE:
214: int iValue = data[offset];
215: iValue = iValue & 0xFF;
216: value = iValue;
217: break;
218: case FMT_USHORT:
219: value = get16u(offset);
220: break;
221: case FMT_ULONG:
222: value = get32u(offset);
223: break;
224: case FMT_URATIONAL:
225: case FMT_SRATIONAL:
226: int num = get32s(offset);
227: int den = get32s(offset + 4);
228:
229: if (den == 0) {
230: value = 0;
231: } else {
232: value = (double) num / (double) den;
233: }
234: break;
235: case FMT_SSHORT:
236: value = get16s(offset);
237: break;
238: case FMT_SLONG:
239: value = get32s(offset);
240: break;
241: default:
242: System.err.println("Unexpected number format: " + format);
243: }
244:
245: return value;
246: }
247: }
|