001: /*
002: * $RCSfile: TIFFIFD.java,v $
003: *
004: *
005: * Copyright (c) 2005 Sun Microsystems, Inc. All Rights Reserved.
006: *
007: * Redistribution and use in source and binary forms, with or without
008: * modification, are permitted provided that the following conditions
009: * are met:
010: *
011: * - Redistribution of source code must retain the above copyright
012: * notice, this list of conditions and the following disclaimer.
013: *
014: * - Redistribution in binary form must reproduce the above copyright
015: * notice, this list of conditions and the following disclaimer in
016: * the documentation and/or other materials provided with the
017: * distribution.
018: *
019: * Neither the name of Sun Microsystems, Inc. or the names of
020: * contributors may be used to endorse or promote products derived
021: * from this software without specific prior written permission.
022: *
023: * This software is provided "AS IS," without a warranty of any
024: * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
025: * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
026: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
027: * EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
028: * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
029: * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
030: * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
031: * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
032: * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
033: * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
034: * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
035: * POSSIBILITY OF SUCH DAMAGES.
036: *
037: * You acknowledge that this software is not designed or intended for
038: * use in the design, construction, operation or maintenance of any
039: * nuclear facility.
040: *
041: * $Revision: 1.7 $
042: * $Date: 2006/09/27 23:56:30 $
043: * $State: Exp $
044: */
045: package com.sun.media.imageioimpl.plugins.tiff;
046:
047: import java.io.EOFException;
048: import java.io.IOException;
049: import java.util.ArrayList;
050: import java.util.Arrays;
051: import java.util.Iterator;
052: import java.util.List;
053: import java.util.Map;
054: import java.util.Set;
055: import java.util.TreeMap;
056: import java.util.Vector;
057: import javax.imageio.stream.ImageInputStream;
058: import javax.imageio.stream.ImageOutputStream;
059: import com.sun.media.imageio.plugins.tiff.BaselineTIFFTagSet;
060: import com.sun.media.imageio.plugins.tiff.TIFFDirectory;
061: import com.sun.media.imageio.plugins.tiff.TIFFField;
062: import com.sun.media.imageio.plugins.tiff.TIFFTag;
063: import com.sun.media.imageio.plugins.tiff.TIFFTagSet;
064:
065: public class TIFFIFD extends TIFFDirectory {
066:
067: private long stripOrTileByteCountsPosition = -1;
068: private long stripOrTileOffsetsPosition = -1;
069: private long lastPosition = -1;
070:
071: public static TIFFTag getTag(int tagNumber, List tagSets) {
072: Iterator iter = tagSets.iterator();
073: while (iter.hasNext()) {
074: TIFFTagSet tagSet = (TIFFTagSet) iter.next();
075: TIFFTag tag = tagSet.getTag(tagNumber);
076: if (tag != null) {
077: return tag;
078: }
079: }
080:
081: return null;
082: }
083:
084: public static TIFFTag getTag(String tagName, List tagSets) {
085: Iterator iter = tagSets.iterator();
086: while (iter.hasNext()) {
087: TIFFTagSet tagSet = (TIFFTagSet) iter.next();
088: TIFFTag tag = tagSet.getTag(tagName);
089: if (tag != null) {
090: return tag;
091: }
092: }
093:
094: return null;
095: }
096:
097: private static void writeTIFFFieldToStream(TIFFField field,
098: ImageOutputStream stream) throws IOException {
099: int count = field.getCount();
100: Object data = field.getData();
101:
102: switch (field.getType()) {
103: case TIFFTag.TIFF_ASCII:
104: for (int i = 0; i < count; i++) {
105: String s = ((String[]) data)[i];
106: int length = s.length();
107: for (int j = 0; j < length; j++) {
108: stream.writeByte(s.charAt(j) & 0xff);
109: }
110: stream.writeByte(0);
111: }
112: break;
113: case TIFFTag.TIFF_UNDEFINED:
114: case TIFFTag.TIFF_BYTE:
115: case TIFFTag.TIFF_SBYTE:
116: stream.write((byte[]) data);
117: break;
118: case TIFFTag.TIFF_SHORT:
119: stream.writeChars((char[]) data, 0, ((char[]) data).length);
120: break;
121: case TIFFTag.TIFF_SSHORT:
122: stream.writeShorts((short[]) data, 0,
123: ((short[]) data).length);
124: break;
125: case TIFFTag.TIFF_SLONG:
126: stream.writeInts((int[]) data, 0, ((int[]) data).length);
127: break;
128: case TIFFTag.TIFF_LONG:
129: for (int i = 0; i < count; i++) {
130: stream.writeInt((int) (((long[]) data)[i]));
131: }
132: break;
133: case TIFFTag.TIFF_IFD_POINTER:
134: stream.writeInt(0); // will need to be backpatched
135: break;
136: case TIFFTag.TIFF_FLOAT:
137: stream.writeFloats((float[]) data, 0,
138: ((float[]) data).length);
139: break;
140: case TIFFTag.TIFF_DOUBLE:
141: stream.writeDoubles((double[]) data, 0,
142: ((double[]) data).length);
143: break;
144: case TIFFTag.TIFF_SRATIONAL:
145: for (int i = 0; i < count; i++) {
146: stream.writeInt(((int[][]) data)[i][0]);
147: stream.writeInt(((int[][]) data)[i][1]);
148: }
149: break;
150: case TIFFTag.TIFF_RATIONAL:
151: for (int i = 0; i < count; i++) {
152: long num = ((long[][]) data)[i][0];
153: long den = ((long[][]) data)[i][1];
154: stream.writeInt((int) num);
155: stream.writeInt((int) den);
156: }
157: break;
158: default:
159: // error
160: }
161: }
162:
163: public TIFFIFD(List tagSets, TIFFTag parentTag) {
164: super ((TIFFTagSet[]) tagSets.toArray(new TIFFTagSet[tagSets
165: .size()]), parentTag);
166: }
167:
168: public TIFFIFD(List tagSets) {
169: this (tagSets, null);
170: }
171:
172: public List getTagSetList() {
173: return Arrays.asList(getTagSets());
174: }
175:
176: /**
177: * Returns an <code>Iterator</code> over the TIFF fields. The
178: * traversal is in the order of increasing tag number.
179: */
180: // Note: the sort is guaranteed for low fields by the use of an
181: // array wherein the index corresponds to the tag number and for
182: // the high fields by the use of a TreeMap with tag number keys.
183: public Iterator iterator() {
184: return Arrays.asList(getTIFFFields()).iterator();
185: }
186:
187: // Stream position initially at beginning, left at end
188: // if ignoreUnknownFields is true, do not load fields for which
189: // a tag cannot be found in an allowed TagSet.
190: public void initialize(ImageInputStream stream,
191: boolean ignoreUnknownFields) throws IOException {
192: removeTIFFFields();
193:
194: List tagSetList = getTagSetList();
195:
196: int numEntries = stream.readUnsignedShort();
197: for (int i = 0; i < numEntries; i++) {
198: // Read tag number, value type, and value count.
199: int tag = stream.readUnsignedShort();
200: int type = stream.readUnsignedShort();
201: int count = (int) stream.readUnsignedInt();
202:
203: // Get the associated TIFFTag.
204: TIFFTag tiffTag = getTag(tag, tagSetList);
205:
206: // Ignore unknown fields.
207: if (ignoreUnknownFields && tiffTag == null) {
208: // Skip the value/offset so as to leave the stream
209: // position at the start of the next IFD entry.
210: stream.skipBytes(4);
211:
212: // XXX Warning message ...
213:
214: // Continue with the next IFD entry.
215: continue;
216: }
217:
218: long nextTagOffset = stream.getStreamPosition() + 4;
219:
220: int sizeOfType = TIFFTag.getSizeOfType(type);
221: if (count * sizeOfType > 4) {
222: long value = stream.readUnsignedInt();
223: stream.seek(value);
224: }
225:
226: if (tag == BaselineTIFFTagSet.TAG_STRIP_BYTE_COUNTS
227: || tag == BaselineTIFFTagSet.TAG_TILE_BYTE_COUNTS
228: || tag == BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH) {
229: this .stripOrTileByteCountsPosition = stream
230: .getStreamPosition();
231: } else if (tag == BaselineTIFFTagSet.TAG_STRIP_OFFSETS
232: || tag == BaselineTIFFTagSet.TAG_TILE_OFFSETS
233: || tag == BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT) {
234: this .stripOrTileOffsetsPosition = stream
235: .getStreamPosition();
236: }
237:
238: Object obj = null;
239:
240: try {
241: switch (type) {
242: case TIFFTag.TIFF_BYTE:
243: case TIFFTag.TIFF_SBYTE:
244: case TIFFTag.TIFF_UNDEFINED:
245: case TIFFTag.TIFF_ASCII:
246: byte[] bvalues = new byte[count];
247: stream.readFully(bvalues, 0, count);
248:
249: if (type == TIFFTag.TIFF_ASCII) {
250: // Can be multiple strings
251: Vector v = new Vector();
252: boolean inString = false;
253: int prevIndex = 0;
254: for (int index = 0; index <= count; index++) {
255: if (index < count && bvalues[index] != 0) {
256: if (!inString) {
257: // start of string
258: prevIndex = index;
259: inString = true;
260: }
261: } else { // null or special case at end of string
262: if (inString) {
263: // end of string
264: String s = new String(bvalues,
265: prevIndex, index
266: - prevIndex);
267: v.add(s);
268: inString = false;
269: }
270: }
271: }
272:
273: count = v.size();
274: String[] strings;
275: if (count != 0) {
276: strings = new String[count];
277: for (int c = 0; c < count; c++) {
278: strings[c] = (String) v.elementAt(c);
279: }
280: } else {
281: // This case has been observed when the value of
282: // 'count' recorded in the field is non-zero but
283: // the value portion contains all nulls.
284: count = 1;
285: strings = new String[] { "" };
286: }
287:
288: obj = strings;
289: } else {
290: obj = bvalues;
291: }
292: break;
293:
294: case TIFFTag.TIFF_SHORT:
295: char[] cvalues = new char[count];
296: for (int j = 0; j < count; j++) {
297: cvalues[j] = (char) (stream.readUnsignedShort());
298: }
299: obj = cvalues;
300: break;
301:
302: case TIFFTag.TIFF_LONG:
303: case TIFFTag.TIFF_IFD_POINTER:
304: long[] lvalues = new long[count];
305: for (int j = 0; j < count; j++) {
306: lvalues[j] = stream.readUnsignedInt();
307: }
308: obj = lvalues;
309: break;
310:
311: case TIFFTag.TIFF_RATIONAL:
312: long[][] llvalues = new long[count][2];
313: for (int j = 0; j < count; j++) {
314: llvalues[j][0] = stream.readUnsignedInt();
315: llvalues[j][1] = stream.readUnsignedInt();
316: }
317: obj = llvalues;
318: break;
319:
320: case TIFFTag.TIFF_SSHORT:
321: short[] svalues = new short[count];
322: for (int j = 0; j < count; j++) {
323: svalues[j] = stream.readShort();
324: }
325: obj = svalues;
326: break;
327:
328: case TIFFTag.TIFF_SLONG:
329: int[] ivalues = new int[count];
330: for (int j = 0; j < count; j++) {
331: ivalues[j] = stream.readInt();
332: }
333: obj = ivalues;
334: break;
335:
336: case TIFFTag.TIFF_SRATIONAL:
337: int[][] iivalues = new int[count][2];
338: for (int j = 0; j < count; j++) {
339: iivalues[j][0] = stream.readInt();
340: iivalues[j][1] = stream.readInt();
341: }
342: obj = iivalues;
343: break;
344:
345: case TIFFTag.TIFF_FLOAT:
346: float[] fvalues = new float[count];
347: for (int j = 0; j < count; j++) {
348: fvalues[j] = stream.readFloat();
349: }
350: obj = fvalues;
351: break;
352:
353: case TIFFTag.TIFF_DOUBLE:
354: double[] dvalues = new double[count];
355: for (int j = 0; j < count; j++) {
356: dvalues[j] = stream.readDouble();
357: }
358: obj = dvalues;
359: break;
360:
361: default:
362: // XXX Warning
363: break;
364: }
365: } catch (EOFException eofe) {
366: // The TIFF 6.0 fields have tag numbers less than or equal
367: // to 532 (ReferenceBlackWhite) or equal to 33432 (Copyright).
368: // If there is an error reading a baseline tag, then re-throw
369: // the exception and fail; otherwise continue with the next
370: // field.
371: if (BaselineTIFFTagSet.getInstance().getTag(tag) == null) {
372: throw eofe;
373: }
374: }
375:
376: if (tiffTag == null) {
377: // XXX Warning: unknown tag
378: } else if (!tiffTag.isDataTypeOK(type)) {
379: // XXX Warning: bad data type
380: } else if (tiffTag.isIFDPointer() && obj != null) {
381: stream.mark();
382: stream.seek(((long[]) obj)[0]);
383:
384: List tagSets = new ArrayList(1);
385: tagSets.add(tiffTag.getTagSet());
386: TIFFIFD subIFD = new TIFFIFD(tagSets);
387:
388: // XXX Use same ignore policy for sub-IFD fields?
389: subIFD.initialize(stream, ignoreUnknownFields);
390: obj = subIFD;
391: stream.reset();
392: }
393:
394: if (tiffTag == null) {
395: tiffTag = new TIFFTag(null, tag, 1 << type, null);
396: }
397:
398: // Add the field if its contents have been initialized which
399: // will not be the case if an EOF was ignored above.
400: if (obj != null) {
401: TIFFField f = new TIFFField(tiffTag, type, count, obj);
402: addTIFFField(f);
403: }
404:
405: stream.seek(nextTagOffset);
406: }
407:
408: this .lastPosition = stream.getStreamPosition();
409: }
410:
411: public void writeToStream(ImageOutputStream stream)
412: throws IOException {
413:
414: int numFields = getNumTIFFFields();
415: stream.writeShort(numFields);
416:
417: long nextSpace = stream.getStreamPosition() + 12 * numFields
418: + 4;
419:
420: Iterator iter = iterator();
421: while (iter.hasNext()) {
422: TIFFField f = (TIFFField) iter.next();
423:
424: TIFFTag tag = f.getTag();
425:
426: int type = f.getType();
427: int count = f.getCount();
428:
429: // Hack to deal with unknown tags
430: if (type == 0) {
431: type = TIFFTag.TIFF_UNDEFINED;
432: }
433: int size = count * TIFFTag.getSizeOfType(type);
434:
435: if (type == TIFFTag.TIFF_ASCII) {
436: int chars = 0;
437: for (int i = 0; i < count; i++) {
438: chars += f.getAsString(i).length() + 1;
439: }
440: count = chars;
441: size = count;
442: }
443:
444: int tagNumber = f.getTagNumber();
445: stream.writeShort(tagNumber);
446: stream.writeShort(type);
447: stream.writeInt(count);
448:
449: // Write a dummy value to fill space
450: stream.writeInt(0);
451: stream.mark(); // Mark beginning of next field
452: stream.skipBytes(-4);
453:
454: long pos;
455:
456: if (size > 4 || tag.isIFDPointer()) {
457: // Ensure IFD or value is written on a word boundary
458: nextSpace = (nextSpace + 3) & ~0x3;
459:
460: stream.writeInt((int) nextSpace);
461: stream.seek(nextSpace);
462: pos = nextSpace;
463:
464: if (tag.isIFDPointer()) {
465: TIFFIFD subIFD = (TIFFIFD) f.getData();
466: subIFD.writeToStream(stream);
467: nextSpace = subIFD.lastPosition;
468: } else {
469: writeTIFFFieldToStream(f, stream);
470: nextSpace = stream.getStreamPosition();
471: }
472: } else {
473: pos = stream.getStreamPosition();
474: writeTIFFFieldToStream(f, stream);
475: }
476:
477: // If we are writing the data for the
478: // StripByteCounts, TileByteCounts, StripOffsets,
479: // TileOffsets, JPEGInterchangeFormat, or
480: // JPEGInterchangeFormatLength fields, record the current stream
481: // position for backpatching
482: if (tagNumber == BaselineTIFFTagSet.TAG_STRIP_BYTE_COUNTS
483: || tagNumber == BaselineTIFFTagSet.TAG_TILE_BYTE_COUNTS
484: || tagNumber == BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH) {
485: this .stripOrTileByteCountsPosition = pos;
486: } else if (tagNumber == BaselineTIFFTagSet.TAG_STRIP_OFFSETS
487: || tagNumber == BaselineTIFFTagSet.TAG_TILE_OFFSETS
488: || tagNumber == BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT) {
489: this .stripOrTileOffsetsPosition = pos;
490: }
491:
492: stream.reset(); // Go to marked position of next field
493: }
494:
495: this .lastPosition = nextSpace;
496: }
497:
498: public long getStripOrTileByteCountsPosition() {
499: return stripOrTileByteCountsPosition;
500: }
501:
502: public long getStripOrTileOffsetsPosition() {
503: return stripOrTileOffsetsPosition;
504: }
505:
506: public long getLastPosition() {
507: return lastPosition;
508: }
509:
510: void setPositions(long stripOrTileOffsetsPosition,
511: long stripOrTileByteCountsPosition, long lastPosition) {
512: this .stripOrTileOffsetsPosition = stripOrTileOffsetsPosition;
513: this .stripOrTileByteCountsPosition = stripOrTileByteCountsPosition;
514: this .lastPosition = lastPosition;
515: }
516:
517: /**
518: * Returns a <code>TIFFIFD</code> wherein all fields from the
519: * <code>BaselineTIFFTagSet</code> are copied by value and all other
520: * fields copied by reference.
521: */
522: public TIFFIFD getShallowClone() {
523: // Get the baseline TagSet.
524: TIFFTagSet baselineTagSet = BaselineTIFFTagSet.getInstance();
525:
526: // If the baseline TagSet is not included just return.
527: List tagSetList = getTagSetList();
528: if (!tagSetList.contains(baselineTagSet)) {
529: return this ;
530: }
531:
532: // Create a new object.
533: TIFFIFD shallowClone = new TIFFIFD(tagSetList, getParentTag());
534:
535: // Get the tag numbers in the baseline set.
536: Set baselineTagNumbers = baselineTagSet.getTagNumbers();
537:
538: // Iterate over the fields in this IFD.
539: Iterator fields = iterator();
540: while (fields.hasNext()) {
541: // Get the next field.
542: TIFFField field = (TIFFField) fields.next();
543:
544: // Get its tag number.
545: Integer tagNumber = new Integer(field.getTagNumber());
546:
547: // Branch based on membership in baseline set.
548: TIFFField fieldClone;
549: if (baselineTagNumbers.contains(tagNumber)) {
550: // Copy by value.
551: Object fieldData = field.getData();
552:
553: int fieldType = field.getType();
554:
555: try {
556: switch (fieldType) {
557: case TIFFTag.TIFF_BYTE:
558: case TIFFTag.TIFF_SBYTE:
559: case TIFFTag.TIFF_UNDEFINED:
560: fieldData = ((byte[]) fieldData).clone();
561: break;
562: case TIFFTag.TIFF_ASCII:
563: fieldData = ((String[]) fieldData).clone();
564: break;
565: case TIFFTag.TIFF_SHORT:
566: fieldData = ((char[]) fieldData).clone();
567: break;
568: case TIFFTag.TIFF_LONG:
569: case TIFFTag.TIFF_IFD_POINTER:
570: fieldData = ((long[]) fieldData).clone();
571: break;
572: case TIFFTag.TIFF_RATIONAL:
573: fieldData = ((long[][]) fieldData).clone();
574: break;
575: case TIFFTag.TIFF_SSHORT:
576: fieldData = ((short[]) fieldData).clone();
577: break;
578: case TIFFTag.TIFF_SLONG:
579: fieldData = ((int[]) fieldData).clone();
580: break;
581: case TIFFTag.TIFF_SRATIONAL:
582: fieldData = ((int[][]) fieldData).clone();
583: break;
584: case TIFFTag.TIFF_FLOAT:
585: fieldData = ((float[]) fieldData).clone();
586: break;
587: case TIFFTag.TIFF_DOUBLE:
588: fieldData = ((double[]) fieldData).clone();
589: break;
590: default:
591: // Shouldn't happen but do nothing ...
592: }
593: } catch (Exception e) {
594: // Ignore it and copy by reference ...
595: }
596:
597: fieldClone = new TIFFField(field.getTag(), fieldType,
598: field.getCount(), fieldData);
599: } else {
600: // Copy by reference.
601: fieldClone = field;
602: }
603:
604: // Add the field to the clone.
605: shallowClone.addTIFFField(fieldClone);
606: }
607:
608: // Set positions.
609: shallowClone.setPositions(stripOrTileOffsetsPosition,
610: stripOrTileByteCountsPosition, lastPosition);
611:
612: return shallowClone;
613: }
614: }
|