/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.sanselan.formats.tiff; import java.io.IOException; import java.io.PrintWriter; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.Hashtable; import java.util.List; import java.util.Map; import org.apache.sanselan.ImageReadException; import org.apache.sanselan.common.byteSources.ByteSource; import org.apache.sanselan.formats.tiff.constants.TagInfo; import org.apache.sanselan.formats.tiff.constants.TiffConstants; import org.apache.sanselan.formats.tiff.fieldtypes.FieldType; public class TiffField implements TiffConstants { public final TagInfo tagInfo; public final FieldType fieldType; public final int tag; public final int directoryType; public final int type; public final int length; public final int valueOffset; public final byte valueOffsetBytes[]; public byte oversizeValue[] = null; public final int byteOrder; public TiffField(int tag, int directoryType, int type, int Length, int ValueOffset, byte ValueOffsetBytes[], int byteOrder) { this.tag = tag; this.directoryType = directoryType; this.type = type; this.length = Length; this.valueOffset = ValueOffset; this.valueOffsetBytes = ValueOffsetBytes; this.byteOrder = byteOrder; fieldType = getFieldType(type); tagInfo = getTag(directoryType, tag); } private int sortHint = -1; public boolean isLocalValue() { return fieldType.isLocalValue(this); } public int getBytesLength() throws ImageReadException { return fieldType.getBytesLength(this); } public final class OversizeValueElement extends TiffElement { public OversizeValueElement(int offset, int length) { super(offset, length); } public String getElementDescription(boolean verbose) { if (verbose) return null; return "OversizeValueElement, tag: " + tagInfo.name + ", fieldType: " + fieldType.name; } } public TiffElement getOversizeValueElement() { if (fieldType.isLocalValue(this)) return null; return new OversizeValueElement(valueOffset, oversizeValue.length); } public void setOversizeValue(byte bytes[]) { this.oversizeValue = bytes; } private static FieldType getFieldType(int value) { for (int i = 0; i < FIELD_TYPES.length; i++) { FieldType fieldType = FIELD_TYPES[i]; if (fieldType.type == value) return fieldType; } return FIELD_TYPE_UNKNOWN; } private static TagInfo getTag(int directoryType, int tag, List possibleMatches) { if (possibleMatches.size() < 1) return null; // else if (possibleMatches.size() == 1) // { // TagInfo tagInfo = (TagInfo) possibleMatches.get(0); // return tagInfo; // } // first search for exact match. for (int i = 0; i < possibleMatches.size(); i++) { TagInfo tagInfo = (TagInfo) possibleMatches.get(i); if (tagInfo.directoryType == EXIF_DIRECTORY_UNKNOWN) // pass continue; else if (directoryType == DIRECTORY_TYPE_EXIF && tagInfo.directoryType == EXIF_DIRECTORY_EXIF_IFD) return tagInfo; else if (directoryType == DIRECTORY_TYPE_INTEROPERABILITY && tagInfo.directoryType == EXIF_DIRECTORY_INTEROP_IFD) return tagInfo; else if (directoryType == DIRECTORY_TYPE_GPS && tagInfo.directoryType == EXIF_DIRECTORY_GPS) return tagInfo; else if (directoryType == DIRECTORY_TYPE_MAKER_NOTES && tagInfo.directoryType == EXIF_DIRECTORY_MAKER_NOTES) return tagInfo; else if (directoryType == DIRECTORY_TYPE_DIR_0 && tagInfo.directoryType == TIFF_DIRECTORY_IFD0) return tagInfo; else if (directoryType == DIRECTORY_TYPE_DIR_1 && tagInfo.directoryType == TIFF_DIRECTORY_IFD1) return tagInfo; else if (directoryType == DIRECTORY_TYPE_DIR_2 && tagInfo.directoryType == TIFF_DIRECTORY_IFD2) return tagInfo; else if (directoryType == DIRECTORY_TYPE_DIR_3 && tagInfo.directoryType == TIFF_DIRECTORY_IFD3) return tagInfo; } // accept an inexact match. for (int i = 0; i < possibleMatches.size(); i++) { TagInfo tagInfo = (TagInfo) possibleMatches.get(i); if (tagInfo.directoryType == EXIF_DIRECTORY_UNKNOWN) // pass continue; else if (directoryType >= 0 && tagInfo.directoryType.isImageDirectory()) return tagInfo; else if (directoryType < 0 && !tagInfo.directoryType.isImageDirectory()) return tagInfo; } // accept a wildcard match. for (int i = 0; i < possibleMatches.size(); i++) { TagInfo tagInfo = (TagInfo) possibleMatches.get(i); if (tagInfo.directoryType == EXIF_DIRECTORY_UNKNOWN) return tagInfo; } // // accept a very rough match. // for (int i = 0; i < possibleMatches.size(); i++) // { // TagInfo tagInfo = (TagInfo) possibleMatches.get(i); // if (tagInfo.exifDirectory == EXIF_DIRECTORY_UNKNOWN) // return tagInfo; // else if (directoryType == DIRECTORY_TYPE_EXIF // && tagInfo.exifDirectory == EXIF_DIRECTORY_EXIF_IFD) // return tagInfo; // else if (directoryType == DIRECTORY_TYPE_INTEROPERABILITY // && tagInfo.exifDirectory == EXIF_DIRECTORY_INTEROP_IFD) // return tagInfo; // else if (directoryType == DIRECTORY_TYPE_GPS // && tagInfo.exifDirectory == EXIF_DIRECTORY_GPS) // return tagInfo; // else if (directoryType == DIRECTORY_TYPE_MAKER_NOTES // && tagInfo.exifDirectory == EXIF_DIRECTORY_MAKER_NOTES) // return tagInfo; // else if (directoryType >= 0 // && tagInfo.exifDirectory.isImageDirectory()) // return tagInfo; // else if (directoryType < 0 // && !tagInfo.exifDirectory.isImageDirectory()) // return tagInfo; // } return TIFF_TAG_UNKNOWN; // if (true) // throw new Error("Why didn't this algorithm work?"); // // { // TagInfo tagInfo = (TagInfo) possibleMatches.get(0); // return tagInfo; // } // Object key = new Integer(tag); // // if (directoryType == DIRECTORY_TYPE_EXIF // || directoryType == DIRECTORY_TYPE_INTEROPERABILITY) // { // if (EXIF_TAG_MAP.containsKey(key)) // return (TagInfo) EXIF_TAG_MAP.get(key); // } // else if (directoryType == DIRECTORY_TYPE_GPS) // { // if (GPS_TAG_MAP.containsKey(key)) // return (TagInfo) GPS_TAG_MAP.get(key); // } // else // { // if (TIFF_TAG_MAP.containsKey(key)) // return (TagInfo) TIFF_TAG_MAP.get(key); // } // // if (ALL_TAG_MAP.containsKey(key)) // return (TagInfo) ALL_TAG_MAP.get(key); // public static final int DIRECTORY_TYPE_EXIF = -2; // // public static final int DIRECTORY_TYPE_SUB = 5; // public static final int DIRECTORY_TYPE_GPS = -3; // public static final int DIRECTORY_TYPE_INTEROPERABILITY = -4; // // private static final Map GPS_TAG_MAP = makeTagMap(ALL_GPS_TAGS, // false); // private static final Map TIFF_TAG_MAP = makeTagMap(ALL_TIFF_TAGS, // false); // private static final Map EXIF_TAG_MAP = makeTagMap(ALL_EXIF_TAGS, // false); // private static final Map ALL_TAG_MAP = makeTagMap(ALL_TAGS, true); // // for (int i = 0; i < ALL_TAGS.length; i++) // { // TagInfo2 tag = ALL_TAGS[i]; // if (tag.tag == value) // return tag; // } // return TIFF_TAG_UNKNOWN; } private static TagInfo getTag(int directoryType, int tag) { Object key = new Integer(tag); List possibleMatches = (List) EXIF_TAG_MAP.get(key); if (null == possibleMatches) { return TIFF_TAG_UNKNOWN; } TagInfo result = getTag(directoryType, tag, possibleMatches); return result; } private int getValueLengthInBytes() { int unit_length = fieldType.length; int valueLength = unit_length * length; // Debug.debug("getValueLengthInBytes unit_length", unit_length); // Debug.debug("getValueLengthInBytes length", length); return valueLength; } public void fillInValue(ByteSource byteSource) throws ImageReadException, IOException { if (fieldType.isLocalValue(this)) return; int valueLength = getValueLengthInBytes(); // Debug.debug("fillInValue tag", tag); // Debug.debug("fillInValue tagInfo", tagInfo); // Debug.debug("fillInValue valueOffset", valueOffset); // Debug.debug("fillInValue valueLength", valueLength); byte bytes[] = byteSource.getBlock(valueOffset, valueLength); setOversizeValue(bytes); } public String getValueDescription() { try { return getValueDescription(getValue()); } catch (ImageReadException e) { return "Invalid value: " + e.getMessage(); } } private String getValueDescription(Object o) { if (o == null) return null; if (o instanceof Number) { return o.toString(); } else if (o instanceof String) { return "'" + o.toString().trim() + "'"; } else if (o instanceof Date) { DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ"); return df.format((Date) o); } else if (o instanceof Object[]) { Object objects[] = (Object[]) o; StringBuffer result = new StringBuffer(); for (int i = 0; i < objects.length; i++) { Object object = objects[i]; if (i > 50) { result.append("... (" + objects.length + ")"); break; } if (i > 0) result.append(", "); result.append("" + object); } return result.toString(); } // else if (o instanceof Number[]) // { // Number numbers[] = (Number[]) o; // StringBuffer result = new StringBuffer(); // // for (int i = 0; i < numbers.length; i++) // { // Number number = numbers[i]; // // if (i > 0) // result.append(", "); // result.append("" + number); // } // return result.toString(); // } else if (o instanceof int[]) { int values[] = (int[]) o; StringBuffer result = new StringBuffer(); for (int i = 0; i < values.length; i++) { int value = values[i]; if (i > 50) { result.append("... (" + values.length + ")"); break; } if (i > 0) result.append(", "); result.append("" + value); } return result.toString(); } else if (o instanceof long[]) { long values[] = (long[]) o; StringBuffer result = new StringBuffer(); for (int i = 0; i < values.length; i++) { long value = values[i]; if (i > 50) { result.append("... (" + values.length + ")"); break; } if (i > 0) result.append(", "); result.append("" + value); } return result.toString(); } else if (o instanceof double[]) { double values[] = (double[]) o; StringBuffer result = new StringBuffer(); for (int i = 0; i < values.length; i++) { double value = values[i]; if (i > 50) { result.append("... (" + values.length + ")"); break; } if (i > 0) result.append(", "); result.append("" + value); } return result.toString(); } else if (o instanceof byte[]) { byte values[] = (byte[]) o; StringBuffer result = new StringBuffer(); for (int i = 0; i < values.length; i++) { byte value = values[i]; if (i > 50) { result.append("... (" + values.length + ")"); break; } if (i > 0) result.append(", "); result.append("" + value); } return result.toString(); } else if (o instanceof char[]) { char values[] = (char[]) o; StringBuffer result = new StringBuffer(); for (int i = 0; i < values.length; i++) { char value = values[i]; if (i > 50) { result.append("... (" + values.length + ")"); break; } if (i > 0) result.append(", "); result.append("" + value); } return result.toString(); } else if (o instanceof float[]) { float values[] = (float[]) o; StringBuffer result = new StringBuffer(); for (int i = 0; i < values.length; i++) { float value = values[i]; if (i > 50) { result.append("... (" + values.length + ")"); break; } if (i > 0) result.append(", "); result.append("" + value); } return result.toString(); } // else if (o instanceof short[]) // { // short numbers[] = (short[]) o; // StringBuffer result = new StringBuffer(); // // for (int i = 0; i < numbers.length; i++) // { // short number = numbers[i]; // // if (i > 0) // result.append(", "); // result.append("" + number); // } // return result.toString(); // } return "Unknown: " + o.getClass().getName(); } public void dump() { PrintWriter pw = new PrintWriter(System.out); dump(pw); pw.flush(); } public void dump(PrintWriter pw) { dump(pw, null); } public void dump(PrintWriter pw, String prefix) { if (prefix != null) pw.print(prefix + ": "); pw.println(toString()); pw.flush(); } // private void errorDump() // { // Debug.debug("tagInfo", tagInfo); // Debug.debug("fieldType", fieldType); // Debug.debug("tag", tag); // Debug.debug("type", type); // Debug.debug("length", length); // Debug.debug("valueOffset", valueOffset); // Debug.debug("valueOffsetBytes", valueOffsetBytes); // Debug.debug("oversizeValue", oversizeValue); // Debug.debug("byteOrder", byteOrder); // } public String getDescriptionWithoutValue() { return tag + " (0x" + Integer.toHexString(tag) + ": " + tagInfo.name + "): "; } public String toString() { StringBuffer result = new StringBuffer(); result.append(tag + " (0x" + Integer.toHexString(tag) + ": " + tagInfo.name + "): "); result.append(getValueDescription() + " (" + length + " " + fieldType.name + ")"); return result.toString(); } public String getTagName() { if (tagInfo == TIFF_TAG_UNKNOWN) return tagInfo.name + " (0x" + Integer.toHexString(tag) + ")"; return tagInfo.name; } public String getFieldTypeName() { return fieldType.name; } public static final String Attribute_Tag = "Tag"; public Object getValue() throws ImageReadException { // System.out.print("getValue"); return tagInfo.getValue(this); } public String getStringValue() throws ImageReadException { Object o = getValue(); if (o == null) return null; if (!(o instanceof String)) throw new ImageReadException("Expected String value(" + tagInfo.getDescription() + "): " + o); return (String) o; } private static final Map makeTagMap(TagInfo tags[], boolean ignoreDuplicates, String name) { // make sure to use the thread-safe version; this is shared state. Map map = new Hashtable(); for (int i = 0; i < tags.length; i++) { TagInfo tag = tags[i]; Object key = new Integer(tag.tag); List tagList = (List) map.get(key); if (tagList == null) { tagList = new ArrayList(); map.put(key, tagList); } tagList.add(tag); // if (map.get(key) == null) // map.put(key, tag); // else if (!ignoreDuplicates) // { // System.out.println("Duplicate tag in " + name + ": " + tag.tag // + " (0x" + Integer.toHexString(tag.tag) + ")"); // System.out.println("\t" + "New name: " + tag.name); // System.out.println("\t" + "Old name: " // + ((TagInfo) map.get(key)).name); // } } return map; } private static final Map GPS_TAG_MAP = makeTagMap(ALL_GPS_TAGS, false, "GPS"); private static final Map TIFF_TAG_MAP = makeTagMap(ALL_TIFF_TAGS, false, "TIFF"); private static final Map EXIF_TAG_MAP = makeTagMap(ALL_EXIF_TAGS, true, "EXIF"); private static final Map ALL_TAG_MAP = makeTagMap(ALL_TAGS, true, "All"); // static // { // Map map = new HashMap(); // // for (int i = 0; i < ALL_TAGS.length; i++) // { // TagInfo2 tag = ALL_TAGS[i]; // Object o = map.get("" + tag.tag); // if (o == null) // map.put("" + tag.tag, tag); // else // { // System.out.println("Duplicate tag: " + tag.tag); // System.out.println("\t" + "New name: " + tag.name); // System.out.println("\t" + "Old name: " + ((TagInfo2) o).name); // } // } // // } // public static final TagInfo2 ALL_TAGS[] = TagConstantsUtils // .mergeTagLists(new TagInfo2[][]{ // ALL_EXIF_TAGS, ALL_TIFF_TAGS, ALL_GPS_TAGS, // }); // // public int[] getIntArrayValue() throws ImageReadException { Object o = getValue(); // if (o == null) // return null; if (o instanceof Number) return new int[] { ((Number) o).intValue() }; else if (o instanceof Number[]) { Number numbers[] = (Number[]) o; int result[] = new int[numbers.length]; for (int i = 0; i < numbers.length; i++) result[i] = numbers[i].intValue(); return result; } else if (o instanceof int[]) { int numbers[] = (int[]) o; int result[] = new int[numbers.length]; for (int i = 0; i < numbers.length; i++) result[i] = numbers[i]; return result; } throw new ImageReadException("Unknown value: " + o + " for: " + tagInfo.getDescription()); // return null; } public double[] getDoubleArrayValue() throws ImageReadException { Object o = getValue(); // if (o == null) // return null; if (o instanceof Number) { return new double[] { ((Number) o).doubleValue() }; } else if (o instanceof Number[]) { Number numbers[] = (Number[]) o; double result[] = new double[numbers.length]; for (int i = 0; i < numbers.length; i++) result[i] = numbers[i].doubleValue(); return result; } else if (o instanceof int[]) { int numbers[] = (int[]) o; double result[] = new double[numbers.length]; for (int i = 0; i < numbers.length; i++) result[i] = numbers[i]; return result; } else if (o instanceof float[]) { float numbers[] = (float[]) o; double result[] = new double[numbers.length]; for (int i = 0; i < numbers.length; i++) result[i] = numbers[i]; return result; } else if (o instanceof double[]) { double numbers[] = (double[]) o; double result[] = new double[numbers.length]; for (int i = 0; i < numbers.length; i++) result[i] = numbers[i]; return result; } throw new ImageReadException("Unknown value: " + o + " for: " + tagInfo.getDescription()); // return null; } public int getIntValueOrArraySum() throws ImageReadException { Object o = getValue(); // if (o == null) // return -1; if (o instanceof Number) return ((Number) o).intValue(); else if (o instanceof Number[]) { Number numbers[] = (Number[]) o; int sum = 0; for (int i = 0; i < numbers.length; i++) sum += numbers[i].intValue(); return sum; } else if (o instanceof int[]) { int numbers[] = (int[]) o; int sum = 0; for (int i = 0; i < numbers.length; i++) sum += numbers[i]; return sum; } throw new ImageReadException("Unknown value: " + o + " for: " + tagInfo.getDescription()); // return -1; } public int getIntValue() throws ImageReadException { Object o = getValue(); if (o == null) throw new ImageReadException("Missing value: " + tagInfo.getDescription()); return ((Number) o).intValue(); } public double getDoubleValue() throws ImageReadException { Object o = getValue(); if (o == null) throw new ImageReadException("Missing value: " + tagInfo.getDescription()); return ((Number) o).doubleValue(); } public byte[] getByteArrayValue() throws ImageReadException { return fieldType.getRawBytes(this); } public int getSortHint() { return sortHint; } public void setSortHint(int sortHint) { this.sortHint = sortHint; } }