Fix types written to some tags using the new EXIF writing interface.
Rename TagInfoText to TagIntoGpsText. Add TagInfoXpText for fields
like XPTITLE, and encode/decode it using UTF-16LE like Windows does.
Copy the new high-level EXIF interface to TiffImageMetadata too.

Jira issue key: SANSELAN-7



git-svn-id: https://svn.apache.org/repos/asf/commons/proper/sanselan/trunk@1290963 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/src/main/java/org/apache/commons/sanselan/formats/tiff/TiffDirectory.java b/src/main/java/org/apache/commons/sanselan/formats/tiff/TiffDirectory.java
index 6869b31..d4118a3 100644
--- a/src/main/java/org/apache/commons/sanselan/formats/tiff/TiffDirectory.java
+++ b/src/main/java/org/apache/commons/sanselan/formats/tiff/TiffDirectory.java
@@ -31,6 +31,7 @@
 import org.apache.commons.sanselan.formats.tiff.taginfos.TagInfoByte;
 import org.apache.commons.sanselan.formats.tiff.taginfos.TagInfoDouble;
 import org.apache.commons.sanselan.formats.tiff.taginfos.TagInfoFloat;
+import org.apache.commons.sanselan.formats.tiff.taginfos.TagInfoGpsText;
 import org.apache.commons.sanselan.formats.tiff.taginfos.TagInfoLong;
 import org.apache.commons.sanselan.formats.tiff.taginfos.TagInfoRational;
 import org.apache.commons.sanselan.formats.tiff.taginfos.TagInfoSByte;
@@ -38,6 +39,7 @@
 import org.apache.commons.sanselan.formats.tiff.taginfos.TagInfoSRational;
 import org.apache.commons.sanselan.formats.tiff.taginfos.TagInfoSShort;
 import org.apache.commons.sanselan.formats.tiff.taginfos.TagInfoShort;
+import org.apache.commons.sanselan.formats.tiff.taginfos.TagInfoXpString;
 
 public class TiffDirectory extends TiffElement implements TiffConstants
 //extends BinaryFileFunctions
@@ -329,6 +331,22 @@
         return tag.getValue(field.byteOrder, bytes);
     }
     
+    public String getFieldValue(TagInfoGpsText tag) throws ImageReadException {
+        TiffField field = findField(tag);
+        if (field == null) {
+            return null;
+        }
+        return tag.getValue(field);
+    }
+    
+    public String getFieldValue(TagInfoXpString tag) throws ImageReadException {
+        TiffField field = findField(tag);
+        if (field == null) {
+            return null;
+        }
+        return tag.getValue(field);
+    }
+    
     public final class ImageDataElement extends TiffElement
     {
         public ImageDataElement(int offset, int length)
diff --git a/src/main/java/org/apache/commons/sanselan/formats/tiff/TiffImageMetadata.java b/src/main/java/org/apache/commons/sanselan/formats/tiff/TiffImageMetadata.java
index a232e1e..a4ac6dd 100644
--- a/src/main/java/org/apache/commons/sanselan/formats/tiff/TiffImageMetadata.java
+++ b/src/main/java/org/apache/commons/sanselan/formats/tiff/TiffImageMetadata.java
@@ -33,6 +33,19 @@
 import org.apache.commons.sanselan.formats.tiff.constants.TiffDirectoryType;
 import org.apache.commons.sanselan.formats.tiff.fieldtypes.FieldType;
 import org.apache.commons.sanselan.formats.tiff.taginfos.TagInfo;
+import org.apache.commons.sanselan.formats.tiff.taginfos.TagInfoAscii;
+import org.apache.commons.sanselan.formats.tiff.taginfos.TagInfoByte;
+import org.apache.commons.sanselan.formats.tiff.taginfos.TagInfoDouble;
+import org.apache.commons.sanselan.formats.tiff.taginfos.TagInfoFloat;
+import org.apache.commons.sanselan.formats.tiff.taginfos.TagInfoGpsText;
+import org.apache.commons.sanselan.formats.tiff.taginfos.TagInfoLong;
+import org.apache.commons.sanselan.formats.tiff.taginfos.TagInfoRational;
+import org.apache.commons.sanselan.formats.tiff.taginfos.TagInfoSByte;
+import org.apache.commons.sanselan.formats.tiff.taginfos.TagInfoSLong;
+import org.apache.commons.sanselan.formats.tiff.taginfos.TagInfoSRational;
+import org.apache.commons.sanselan.formats.tiff.taginfos.TagInfoSShort;
+import org.apache.commons.sanselan.formats.tiff.taginfos.TagInfoShort;
+import org.apache.commons.sanselan.formats.tiff.taginfos.TagInfoXpString;
 import org.apache.commons.sanselan.formats.tiff.write.TiffOutputDirectory;
 import org.apache.commons.sanselan.formats.tiff.write.TiffOutputField;
 import org.apache.commons.sanselan.formats.tiff.write.TiffOutputSet;
@@ -307,6 +320,160 @@
 
         return null;
     }
+    
+    public Object getFieldValue(TagInfo tag) throws ImageReadException {
+        TiffField field = findField(tag);
+        if (field == null) {
+            return null;
+        }
+        return field.getValue();
+    }
+    
+    public byte[] getFieldValue(TagInfoByte tag) throws ImageReadException {
+        TiffField field = findField(tag);
+        if (field == null) {
+            return null;
+        }
+        if (!tag.dataTypes.contains(field.fieldType)) {
+            return null;
+        }
+        return field.fieldType.getRawBytes(field);
+    }
+    
+    public String[] getFieldValue(TagInfoAscii tag) throws ImageReadException {
+        TiffField field = findField(tag);
+        if (field == null) {
+            return null;
+        }
+        if (!tag.dataTypes.contains(field.fieldType)) {
+            return null;
+        }
+        byte[] bytes = field.fieldType.getRawBytes(field);
+        return tag.getValue(field.byteOrder, bytes);
+    }
+    
+    public short[] getFieldValue(TagInfoShort tag) throws ImageReadException {
+        TiffField field = findField(tag);
+        if (field == null) {
+            return null;
+        }
+        if (!tag.dataTypes.contains(field.fieldType)) {
+            return null;
+        }
+        byte[] bytes = field.fieldType.getRawBytes(field);
+        return tag.getValue(field.byteOrder, bytes);
+    }
+    
+    public int[] getFieldValue(TagInfoLong tag) throws ImageReadException {
+        TiffField field = findField(tag);
+        if (field == null) {
+            return null;
+        }
+        if (!tag.dataTypes.contains(field.fieldType)) {
+            return null;
+        }
+        byte[] bytes = field.fieldType.getRawBytes(field);
+        return tag.getValue(field.byteOrder, bytes);
+    }
+    
+    public RationalNumber[] getFieldValue(TagInfoRational tag) throws ImageReadException {
+        TiffField field = findField(tag);
+        if (field == null) {
+            return null;
+        }
+        if (!tag.dataTypes.contains(field.fieldType)) {
+            return null;
+        }
+        byte[] bytes = field.fieldType.getRawBytes(field);
+        return tag.getValue(field.byteOrder, bytes);
+    }
+    
+    public byte[] getFieldValue(TagInfoSByte tag) throws ImageReadException {
+        TiffField field = findField(tag);
+        if (field == null) {
+            return null;
+        }
+        if (!tag.dataTypes.contains(field.fieldType)) {
+            return null;
+        }
+        return field.fieldType.getRawBytes(field);
+    }
+    
+    public short[] getFieldValue(TagInfoSShort tag) throws ImageReadException {
+        TiffField field = findField(tag);
+        if (field == null) {
+            return null;
+        }
+        if (!tag.dataTypes.contains(field.fieldType)) {
+            return null;
+        }
+        byte[] bytes = field.fieldType.getRawBytes(field);
+        return tag.getValue(field.byteOrder, bytes);
+    }
+    
+    public int[] getFieldValue(TagInfoSLong tag) throws ImageReadException {
+        TiffField field = findField(tag);
+        if (field == null) {
+            return null;
+        }
+        if (!tag.dataTypes.contains(field.fieldType)) {
+            return null;
+        }
+        byte[] bytes = field.fieldType.getRawBytes(field);
+        return tag.getValue(field.byteOrder, bytes);
+    }
+    
+    public RationalNumber[] getFieldValue(TagInfoSRational tag) throws ImageReadException {
+        TiffField field = findField(tag);
+        if (field == null) {
+            return null;
+        }
+        if (!tag.dataTypes.contains(field.fieldType)) {
+            return null;
+        }
+        byte[] bytes = field.fieldType.getRawBytes(field);
+        return tag.getValue(field.byteOrder, bytes);
+    }
+    
+    public float[] getFieldValue(TagInfoFloat tag) throws ImageReadException {
+        TiffField field = findField(tag);
+        if (field == null) {
+            return null;
+        }
+        if (!tag.dataTypes.contains(field.fieldType)) {
+            return null;
+        }
+        byte[] bytes = field.fieldType.getRawBytes(field);
+        return tag.getValue(field.byteOrder, bytes);
+    }
+    
+    public double[] getFieldValue(TagInfoDouble tag) throws ImageReadException {
+        TiffField field = findField(tag);
+        if (field == null) {
+            return null;
+        }
+        if (!tag.dataTypes.contains(field.fieldType)) {
+            return null;
+        }
+        byte[] bytes = field.fieldType.getRawBytes(field);
+        return tag.getValue(field.byteOrder, bytes);
+    }
+    
+    public String getFieldValue(TagInfoGpsText tag) throws ImageReadException {
+        TiffField field = findField(tag);
+        if (field == null) {
+            return null;
+        }
+        return tag.getValue(field);
+    }
+    
+    public String getFieldValue(TagInfoXpString tag) throws ImageReadException {
+        TiffField field = findField(tag);
+        if (field == null) {
+            return null;
+        }
+        return tag.getValue(field);
+    }
 
     public TiffDirectory findDirectory(int directoryType)
     {
diff --git a/src/main/java/org/apache/commons/sanselan/formats/tiff/constants/ExifTagConstants.java b/src/main/java/org/apache/commons/sanselan/formats/tiff/constants/ExifTagConstants.java
index 3c48556..b5acc91 100644
--- a/src/main/java/org/apache/commons/sanselan/formats/tiff/constants/ExifTagConstants.java
+++ b/src/main/java/org/apache/commons/sanselan/formats/tiff/constants/ExifTagConstants.java
@@ -27,6 +27,7 @@
 import org.apache.commons.sanselan.formats.tiff.taginfos.TagInfoByteOrShort;
 import org.apache.commons.sanselan.formats.tiff.taginfos.TagInfoDouble;
 import org.apache.commons.sanselan.formats.tiff.taginfos.TagInfoFloat;
+import org.apache.commons.sanselan.formats.tiff.taginfos.TagInfoGpsText;
 import org.apache.commons.sanselan.formats.tiff.taginfos.TagInfoLong;
 import org.apache.commons.sanselan.formats.tiff.taginfos.TagInfoRational;
 import org.apache.commons.sanselan.formats.tiff.taginfos.TagInfoSLong;
@@ -36,9 +37,9 @@
 import org.apache.commons.sanselan.formats.tiff.taginfos.TagInfoShortOrLong;
 import org.apache.commons.sanselan.formats.tiff.taginfos.TagInfoShortOrLongOrRational;
 import org.apache.commons.sanselan.formats.tiff.taginfos.TagInfoShortOrRational;
-import org.apache.commons.sanselan.formats.tiff.taginfos.TagInfoText;
 import org.apache.commons.sanselan.formats.tiff.taginfos.TagInfoUndefined;
 import org.apache.commons.sanselan.formats.tiff.taginfos.TagInfoUnknown;
+import org.apache.commons.sanselan.formats.tiff.taginfos.TagInfoXpString;
 
 /**
  * References:
@@ -799,7 +800,7 @@
     //     skipping Maker Note!
     public static final TagInfoUndefined EXIF_TAG_MAKER_NOTE = new TagInfoUndefined("Maker Note",
             0x927c, 1, TiffDirectoryType.EXIF_DIRECTORY_EXIF_IFD);
-    public static final TagInfoText EXIF_TAG_USER_COMMENT = new TagInfoText(
+    public static final TagInfoGpsText EXIF_TAG_USER_COMMENT = new TagInfoGpsText(
             "UserComment", 0x9286, FIELD_TYPE_UNDEFINED, 1,
             TiffDirectoryType.EXIF_DIRECTORY_EXIF_IFD);
     public static final TagInfoAscii EXIF_TAG_SUB_SEC_TIME = new TagInfoAscii(
@@ -814,15 +815,15 @@
     public static final TagInfoUndefined EXIF_TAG_IMAGE_SOURCE_DATA = new TagInfoUndefined(
             "Image Source Data", 0x935c, 1,
             TiffDirectoryType.EXIF_DIRECTORY_IFD0);
-    public static final TagInfoByte EXIF_TAG_XPTITLE = new TagInfoByte("XPTitle",
+    public static final TagInfoXpString EXIF_TAG_XPTITLE = new TagInfoXpString("XPTitle",
             0x9c9b, -1, TiffDirectoryType.EXIF_DIRECTORY_IFD0);
-    public static final TagInfoByte EXIF_TAG_XPCOMMENT = new TagInfoByte("XPComment",
+    public static final TagInfoXpString EXIF_TAG_XPCOMMENT = new TagInfoXpString("XPComment",
             0x9c9c, -1, TiffDirectoryType.EXIF_DIRECTORY_IFD0);
-    public static final TagInfoByte EXIF_TAG_XPAUTHOR = new TagInfoByte("XPAuthor",
+    public static final TagInfoXpString EXIF_TAG_XPAUTHOR = new TagInfoXpString("XPAuthor",
             0x9c9d, -1, TiffDirectoryType.EXIF_DIRECTORY_IFD0);
-    public static final TagInfoByte EXIF_TAG_XPKEYWORDS = new TagInfoByte("XPKeywords",
+    public static final TagInfoXpString EXIF_TAG_XPKEYWORDS = new TagInfoXpString("XPKeywords",
             0x9c9e, -1, TiffDirectoryType.EXIF_DIRECTORY_IFD0);
-    public static final TagInfoByte EXIF_TAG_XPSUBJECT = new TagInfoByte("XPSubject",
+    public static final TagInfoXpString EXIF_TAG_XPSUBJECT = new TagInfoXpString("XPSubject",
             0x9c9f, -1, TiffDirectoryType.EXIF_DIRECTORY_IFD0);
     public static final TagInfoUndefined EXIF_TAG_FLASHPIX_VERSION = new TagInfoUndefined(
             "Flashpix Version", 0xa000, 1,
diff --git a/src/main/java/org/apache/commons/sanselan/formats/tiff/constants/GpsTagConstants.java b/src/main/java/org/apache/commons/sanselan/formats/tiff/constants/GpsTagConstants.java
index b64ea04..86fbe96 100644
--- a/src/main/java/org/apache/commons/sanselan/formats/tiff/constants/GpsTagConstants.java
+++ b/src/main/java/org/apache/commons/sanselan/formats/tiff/constants/GpsTagConstants.java
@@ -25,7 +25,7 @@
 import org.apache.commons.sanselan.formats.tiff.taginfos.TagInfoByte;
 import org.apache.commons.sanselan.formats.tiff.taginfos.TagInfoRational;
 import org.apache.commons.sanselan.formats.tiff.taginfos.TagInfoShort;
-import org.apache.commons.sanselan.formats.tiff.taginfos.TagInfoText;
+import org.apache.commons.sanselan.formats.tiff.taginfos.TagInfoGpsText;
 
 public interface GpsTagConstants
         extends
@@ -192,12 +192,12 @@
             TiffDirectoryType.EXIF_DIRECTORY_GPS);
 
     // ************************************************************
-    public static final TagInfoText GPS_TAG_GPS_PROCESSING_METHOD = new TagInfoText(
+    public static final TagInfoGpsText GPS_TAG_GPS_PROCESSING_METHOD = new TagInfoGpsText(
             "GPS Processing Method", 0x001b, FIELD_TYPE_UNKNOWN,
             -1, TiffDirectoryType.EXIF_DIRECTORY_GPS);
 
     // ************************************************************
-    public static final TagInfoText GPS_TAG_GPS_AREA_INFORMATION = new TagInfoText(
+    public static final TagInfoGpsText GPS_TAG_GPS_AREA_INFORMATION = new TagInfoGpsText(
             "GPS Area Information", 0x001c, FIELD_TYPE_UNKNOWN, -1,
             TiffDirectoryType.EXIF_DIRECTORY_GPS);
 
diff --git a/src/main/java/org/apache/commons/sanselan/formats/tiff/taginfos/TagInfoText.java b/src/main/java/org/apache/commons/sanselan/formats/tiff/taginfos/TagInfoGpsText.java
similarity index 75%
rename from src/main/java/org/apache/commons/sanselan/formats/tiff/taginfos/TagInfoText.java
rename to src/main/java/org/apache/commons/sanselan/formats/tiff/taginfos/TagInfoGpsText.java
index 4a9a734..798389b 100644
--- a/src/main/java/org/apache/commons/sanselan/formats/tiff/taginfos/TagInfoText.java
+++ b/src/main/java/org/apache/commons/sanselan/formats/tiff/taginfos/TagInfoGpsText.java
@@ -26,9 +26,9 @@
 import org.apache.commons.sanselan.formats.tiff.fieldtypes.FieldType;
 import org.apache.commons.sanselan.util.Debug;
 
-public final class TagInfoText extends TagInfo
+public final class TagInfoGpsText extends TagInfo
 {
-    public TagInfoText(String name, int tag, FieldType dataType, int length,
+    public TagInfoGpsText(String name, int tag, FieldType dataType, int length,
             TiffDirectoryType exifDirectory)
     {
         super(name, tag, dataType, length, exifDirectory);
@@ -51,36 +51,37 @@
         }
     }
 
-    private static final TagInfoText.TextEncoding TEXT_ENCODING_ASCII = new TextEncoding(
+    private static final TagInfoGpsText.TextEncoding TEXT_ENCODING_ASCII = new TextEncoding(
             new byte[]{
                     0x41, 0x53, 0x43, 0x49, 0x49, 0x00, 0x00, 0x00,
             }, "US-ASCII"); // ITU-T T.50 IA5
-    private static final TagInfoText.TextEncoding TEXT_ENCODING_JIS = new TextEncoding(
+    private static final TagInfoGpsText.TextEncoding TEXT_ENCODING_JIS = new TextEncoding(
             new byte[]{
                     0x4A, 0x49, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00,
             }, "JIS"); // JIS X208-1990
-    private static final TagInfoText.TextEncoding TEXT_ENCODING_UNICODE = new TextEncoding(
+    private static final TagInfoGpsText.TextEncoding TEXT_ENCODING_UNICODE = new TextEncoding(
             new byte[]{
                     0x55, 0x4E, 0x49, 0x43, 0x4F, 0x44, 0x45, 0x00,
             // Which Unicode encoding to use, UTF-8?
             }, "UTF-8"); // Unicode Standard
-    private static final TagInfoText.TextEncoding TEXT_ENCODING_UNDEFINED = new TextEncoding(
+    private static final TagInfoGpsText.TextEncoding TEXT_ENCODING_UNDEFINED = new TextEncoding(
             new byte[]{
                     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
             // Try to interpret an undefined text as ISO-8859-1 (Latin)
             }, "ISO-8859-1"); // Undefined
-    private static final TagInfoText.TextEncoding TEXT_ENCODINGS[] = {
+    private static final TagInfoGpsText.TextEncoding TEXT_ENCODINGS[] = {
             TEXT_ENCODING_ASCII, //
             TEXT_ENCODING_JIS, //
             TEXT_ENCODING_UNICODE, //
             TEXT_ENCODING_UNDEFINED, //
     };
 
+    @Override
     public byte[] encodeValue(FieldType fieldType, Object value,
             int byteOrder) throws ImageWriteException
     {
         if (!(value instanceof String))
-            throw new ImageWriteException("Text value not String: " + value
+            throw new ImageWriteException("GPS text value not String: " + value
                     + " (" + Debug.getType(value) + ")");
         String s = (String) value;
 
@@ -124,25 +125,33 @@
         }
     }
 
-    public Object getValue(TiffField entry) throws ImageReadException
+    @Override
+    public String getValue(TiffField entry) throws ImageReadException
     {
-        //            Debug.debug("entry.type", entry.type);
-        //            Debug.debug("entry.type", entry.getDescriptionWithoutValue());
-        //            Debug.debug("entry.type", entry.fieldType);
-
-        if (entry.type == FIELD_TYPE_ASCII.type)
-            return FIELD_TYPE_ASCII.getSimpleValue(entry);
-        else if (entry.type == FIELD_TYPE_UNDEFINED.type)
-        { /* do nothing */ }
-        else if (entry.type == FIELD_TYPE_BYTE.type)
-        { /* do nothing */ }
-        else
-        {
+        if (entry.type == FIELD_TYPE_ASCII.type) {
+            Object object = FIELD_TYPE_ASCII.getSimpleValue(entry);
+            if (object instanceof String) {
+                return (String) object;
+            } else if (object instanceof String[]) {
+                // Use of arrays with the ASCII type
+                // should be extremely rare, and use of
+                // ASCII type in GPS fields should be
+                // forbidden. So assume the 2 never happen
+                // together and return incomplete strings if they do.
+                return ((String[]) object)[0];
+            } else {
+                throw new ImageReadException("Unexpected ASCII type decoded");
+            }
+        } else if (entry.type == FIELD_TYPE_UNDEFINED.type) {
+            /* later */
+        } else if (entry.type == FIELD_TYPE_BYTE.type) {
+            /* later */
+        } else {
             Debug.debug("entry.type", entry.type);
             Debug.debug("entry.directoryType", entry.directoryType);
             Debug.debug("entry.type", entry.getDescriptionWithoutValue());
             Debug.debug("entry.type", entry.fieldType);
-            throw new ImageReadException("Text field not encoded as bytes.");
+            throw new ImageReadException("GPS text field not encoded as bytes.");
         }
 
         byte bytes[] = entry.fieldType.getRawBytes(entry);
@@ -156,19 +165,18 @@
             catch (UnsupportedEncodingException e)
             {
                 throw new ImageReadException(
-                        "Text field missing encoding prefix.");
+                        "GPS text field missing encoding prefix.");
             }
         }
 
         for (int i = 0; i < TEXT_ENCODINGS.length; i++)
         {
-            TagInfoText.TextEncoding encoding = TEXT_ENCODINGS[i];
+            TagInfoGpsText.TextEncoding encoding = TEXT_ENCODINGS[i];
             if (BinaryFileFunctions.compareBytes(bytes, 0, encoding.prefix,
                     0, encoding.prefix.length))
             {
                 try
                 {
-                    //                        Debug.debug("encodingName", encoding.encodingName);
                     return new String(bytes, encoding.prefix.length,
                             bytes.length - encoding.prefix.length,
                             encoding.encodingName);
@@ -180,12 +188,6 @@
             }
         }
 
-        //                        Debug.debug("entry.tag", entry.tag + " (0x" + Integer.toHexString(entry.tag ) +")");
-        //                        Debug.debug("entry.type", entry.type);
-        //                        Debug.debug("bytes", bytes, 10);
-        //            throw new ImageReadException(
-        //                    "Unknown Text encoding prefix.");
-
         try
         {
             // try ASCII, with NO prefix.
@@ -193,7 +195,7 @@
         }
         catch (UnsupportedEncodingException e)
         {
-            throw new ImageReadException("Unknown text encoding prefix.");
+            throw new ImageReadException("Unknown GPS text encoding prefix.");
         }
 
     }
diff --git a/src/main/java/org/apache/commons/sanselan/formats/tiff/taginfos/TagInfoXpString.java b/src/main/java/org/apache/commons/sanselan/formats/tiff/taginfos/TagInfoXpString.java
new file mode 100644
index 0000000..bc3e684
--- /dev/null
+++ b/src/main/java/org/apache/commons/sanselan/formats/tiff/taginfos/TagInfoXpString.java
@@ -0,0 +1,64 @@
+/*
+ * 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.commons.sanselan.formats.tiff.taginfos;
+
+import java.io.UnsupportedEncodingException;
+import java.util.Arrays;
+
+import org.apache.commons.sanselan.ImageReadException;
+import org.apache.commons.sanselan.ImageWriteException;
+import org.apache.commons.sanselan.formats.tiff.TiffField;
+import org.apache.commons.sanselan.formats.tiff.constants.TiffDirectoryType;
+import org.apache.commons.sanselan.formats.tiff.fieldtypes.FieldType;
+import org.apache.commons.sanselan.util.Debug;
+
+/**
+ * Windows XP onwards store some tags using UTF-16LE, but the field type is byte -
+ * here we deal with this.
+ */
+public class TagInfoXpString extends TagInfo {
+    public TagInfoXpString(String name, int tag, int length, TiffDirectoryType directoryType) {
+        super(name, tag, Arrays.asList(FIELD_TYPE_UNDEFINED), length, directoryType);
+    }
+    
+    @Override
+    public byte[] encodeValue(FieldType fieldType, Object value,
+            int byteOrder) throws ImageWriteException
+    {
+        if (!(value instanceof String))
+            throw new ImageWriteException("Text value not String: " + value
+                    + " (" + Debug.getType(value) + ")");
+        String s = (String) value;
+        try {
+            return s.getBytes("UTF-16LE");
+        } catch (UnsupportedEncodingException cannotHappen) {
+            return null;
+        }
+    }
+    
+    @Override
+    public String getValue(TiffField entry) throws ImageReadException {
+        if (entry.type != FIELD_TYPE_BYTE.type) {
+            throw new ImageReadException("Text field not encoded as bytes.");
+        }
+        try {
+            return new String(entry.getByteArrayValue(), "UTF-16LE");
+        } catch (UnsupportedEncodingException cannotHappen) {
+            return null;
+        }
+    }
+}
diff --git a/src/main/java/org/apache/commons/sanselan/formats/tiff/write/TiffOutputDirectory.java b/src/main/java/org/apache/commons/sanselan/formats/tiff/write/TiffOutputDirectory.java
index deca567..b9b3207 100644
--- a/src/main/java/org/apache/commons/sanselan/formats/tiff/write/TiffOutputDirectory.java
+++ b/src/main/java/org/apache/commons/sanselan/formats/tiff/write/TiffOutputDirectory.java
@@ -41,6 +41,7 @@
 import org.apache.commons.sanselan.formats.tiff.taginfos.TagInfoByteOrShort;
 import org.apache.commons.sanselan.formats.tiff.taginfos.TagInfoDouble;
 import org.apache.commons.sanselan.formats.tiff.taginfos.TagInfoFloat;
+import org.apache.commons.sanselan.formats.tiff.taginfos.TagInfoGpsText;
 import org.apache.commons.sanselan.formats.tiff.taginfos.TagInfoLong;
 import org.apache.commons.sanselan.formats.tiff.taginfos.TagInfoRational;
 import org.apache.commons.sanselan.formats.tiff.taginfos.TagInfoSByte;
@@ -51,7 +52,7 @@
 import org.apache.commons.sanselan.formats.tiff.taginfos.TagInfoShortOrLong;
 import org.apache.commons.sanselan.formats.tiff.taginfos.TagInfoShortOrLongOrRational;
 import org.apache.commons.sanselan.formats.tiff.taginfos.TagInfoShortOrRational;
-import org.apache.commons.sanselan.formats.tiff.taginfos.TagInfoText;
+import org.apache.commons.sanselan.formats.tiff.taginfos.TagInfoXpString;
 
 public final class TiffOutputDirectory extends TiffOutputItem implements
         TiffConstants
@@ -200,7 +201,7 @@
         }
         byte[] bytes = tagInfo.encodeValue(byteOrder, values);
         TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo,
-                TiffFieldTypeConstants.FIELD_TYPE_SHORT, values.length, bytes);
+                TiffFieldTypeConstants.FIELD_TYPE_BYTE, values.length, bytes);
         add(tiffOutputField);
     }
 
@@ -222,7 +223,7 @@
         }
         byte[] bytes = tagInfo.encodeValue(byteOrder, values);
         TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo,
-                TiffFieldTypeConstants.FIELD_TYPE_LONG, values.length, bytes);
+                TiffFieldTypeConstants.FIELD_TYPE_SHORT, values.length, bytes);
         add(tiffOutputField);
     }
 
@@ -244,7 +245,7 @@
         }
         byte[] bytes = tagInfo.encodeValue(byteOrder, values);
         TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo,
-                TiffFieldTypeConstants.FIELD_TYPE_RATIONAL, values.length, bytes);
+                TiffFieldTypeConstants.FIELD_TYPE_SHORT, values.length, bytes);
         add(tiffOutputField);
     }
     
@@ -255,7 +256,7 @@
         }
         byte[] bytes = tagInfo.encodeValue(byteOrder, values);
         TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo,
-                TiffFieldTypeConstants.FIELD_TYPE_RATIONAL, values.length, bytes);
+                TiffFieldTypeConstants.FIELD_TYPE_LONG, values.length, bytes);
         add(tiffOutputField);
     }
 
@@ -277,7 +278,7 @@
         }
         byte[] bytes = tagInfo.encodeValue(byteOrder, values);
         TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo,
-                TiffFieldTypeConstants.FIELD_TYPE_RATIONAL, values.length, bytes);
+                TiffFieldTypeConstants.FIELD_TYPE_SHORT, values.length, bytes);
         add(tiffOutputField);
     }
 
@@ -292,10 +293,17 @@
         add(tiffOutputField);
     }
 
-    public void add(TagInfoText tagInfo, String value) throws ImageWriteException {
+    public void add(TagInfoGpsText tagInfo, String value) throws ImageWriteException {
         byte[] bytes = tagInfo.encodeValue(TiffFieldTypeConstants.FIELD_TYPE_UNKNOWN, value, byteOrder);
         TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo,
-                TiffFieldTypeConstants.FIELD_TYPE_UNKNOWN, bytes.length, bytes);
+                tagInfo.dataTypes.get(0), bytes.length, bytes);
+        add(tiffOutputField);
+    }
+    
+    public void add(TagInfoXpString tagInfo, String value) throws ImageWriteException {
+        byte[] bytes = tagInfo.encodeValue(TiffFieldTypeConstants.FIELD_TYPE_BYTE, value, byteOrder);
+        TiffOutputField tiffOutputField = new TiffOutputField(tagInfo.tag, tagInfo,
+                TiffFieldTypeConstants.FIELD_TYPE_BYTE, bytes.length, bytes);
         add(tiffOutputField);
     }