OSDN Git Service

Handle the last null byte in Exif tag.
authorEarl Ou <shunhsingou@google.com>
Fri, 19 Oct 2012 08:50:34 +0000 (16:50 +0800)
committerEarl Ou <shunhsingou@google.com>
Wed, 31 Oct 2012 06:48:57 +0000 (14:48 +0800)
Change-Id: Ic802810d11518dfeb80d1338db65b2e1e1cc8476

gallerycommon/src/com/android/gallery3d/exif/ExifOutputStream.java
gallerycommon/src/com/android/gallery3d/exif/ExifParser.java
gallerycommon/src/com/android/gallery3d/exif/ExifTag.java

index e5e7760..46cd655 100644 (file)
@@ -21,7 +21,6 @@ import java.io.IOException;
 import java.io.OutputStream;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
-import java.nio.charset.Charset;
 
 public class ExifOutputStream extends FilterOutputStream {
     private static final String TAG = "ExifOutputStream";
@@ -216,7 +215,7 @@ public class ExifOutputStream extends FilterOutputStream {
             throws IOException {
         switch (tag.getDataType()) {
             case ExifTag.TYPE_ASCII:
-                byte buf[] = tag.getString().getBytes(Charset.forName("US-ASCII"));
+                byte buf[] = tag.getStringByte();
                 if (buf.length == tag.getComponentCount()) {
                     buf[buf.length - 1] = 0;
                     dataOutputStream.write(buf);
index edc8f19..2f5c543 100644 (file)
@@ -136,6 +136,8 @@ public class ExifParser {
     private static final int TAG_SIZE = 12;
     private static final int OFFSET_SIZE = 2;
 
+    private static final Charset US_ASCII = Charset.forName("US-ASCII");
+
     private final CountedDataInputStream mTiffStream;
     private final int mOptions;
     private int mIfdStartOffset = 0;
@@ -665,26 +667,25 @@ public class ExifParser {
 
     /**
      * Reads a String from the InputStream with US-ASCII charset.
+     * The parser will read n bytes and convert it to ascii string.
      * This is used for reading values of type {@link ExifTag#TYPE_ASCII}.
      */
     public String readString(int n) throws IOException {
-        if (n > 0) {
-            byte[] buf = new byte[n];
-            mTiffStream.readOrThrow(buf);
-            return new String(buf, 0, n - 1, "US-ASCII");
-        } else {
-            return "";
-        }
+        return readString(n, US_ASCII);
     }
 
     /**
      * Reads a String from the InputStream with the given charset.
+     * The parser will read n bytes and convert it to string.
      * This is used for reading values of type {@link ExifTag#TYPE_ASCII}.
      */
     public String readString(int n, Charset charset) throws IOException {
-        byte[] buf = new byte[n];
-        mTiffStream.readOrThrow(buf);
-        return new String(buf, 0, n - 1, charset);
+        if (n > 0) {
+            byte[] buf = new byte[n];
+            return mTiffStream.readString(n, charset);
+        } else {
+            return "";
+        }
     }
 
     /**
@@ -763,4 +764,4 @@ public class ExifParser {
     public ByteOrder getByteOrder() {
         return mTiffStream.getByteOrder();
     }
-}
\ No newline at end of file
+}
index 1592479..37b6d9f 100644 (file)
@@ -18,6 +18,7 @@ package com.android.gallery3d.exif;
 
 import android.util.SparseArray;
 
+import java.nio.charset.Charset;
 import java.text.SimpleDateFormat;
 import java.util.Arrays;
 import java.util.Date;
@@ -833,6 +834,7 @@ public class ExifTag {
                 (IfdId.TYPE_IFD_GPS << 24) | TYPE_UNSIGNED_SHORT << 16 | 11);
     }
 
+    private static Charset US_ASCII = Charset.forName("US-ASCII");
     private final short mTagId;
     private final short mDataType;
     private final int mIfd;
@@ -970,6 +972,15 @@ public class ExifTag {
     }
 
     /**
+     * Sets the component count of this tag.
+     * Call this function before setValue() if the length of value does not
+     * match the component count.
+     */
+    public void setComponentCount(int count) {
+        mComponentCount = count;
+    }
+
+    /**
      * Returns true if this ExifTag contains value; otherwise, this tag will contain an offset value
      * that links to the area where the actual value is located.
      *
@@ -1171,18 +1182,37 @@ public class ExifTag {
     }
 
     /**
-     * Sets string values into this tag.
+     * Sets a string value into this tag. The value is treated as an ASCII string where we only
+     * preserve the lower byte of each character. The length of the string should be equal
+     * to either (component count -1) or (component count). A "0" byte will be appeneded while
+     * written to the EXIF file. If the length equals (component count), the final byte will be
+     * replaced by a "0" byte.
+     *
      * @exception IllegalArgumentException If the data type is not {@link #TYPE_ASCII}
-     * or value.length() + 1 does NOT fit the definition of the component count in the
-     * EXIF standard.
+     * or the length of the string is not equal to (component count -1) and (component count)
      */
     public void setValue(String value) {
-        checkComponentCountOrThrow(value.length() + 1);
         if (mDataType != TYPE_ASCII) {
             throwTypeNotMatchedException("String");
         }
-        mComponentCount = value.length() + 1;
-        mValue = value;
+
+        byte[] buf = new byte[value.length()];
+        for (int i = 0, n = value.length(); i < n; i++) {
+            buf[i] = (byte) value.charAt(i);
+        }
+
+        int count = buf.length;
+        if (mComponentCountDefined) {
+            if (mComponentCount != count && mComponentCount != count + 1) {
+                throw new IllegalArgumentException("Tag " + mTagId + ": Required "
+                        + mComponentCount + " or " + (mComponentCount + 1)
+                        + " components but was given " + count
+                        + " component(s)");
+            }
+        } else {
+            mComponentCount = buf[count - 1] == 0 ? count : count + 1;
+        }
+        mValue = buf;
     }
 
     /**
@@ -1311,7 +1341,14 @@ public class ExifTag {
             throw new IllegalArgumentException("Cannot get ASCII value from "
                     + convertTypeToString(mDataType));
         }
-        return (String) mValue;
+        return new String((byte[]) mValue, US_ASCII);
+    }
+
+    /*
+     * Get the converted ascii byte. Used by ExifOutputStream.
+     */
+    byte[] getStringByte() {
+        return (byte[]) mValue;
     }
 
     /**
@@ -1356,18 +1393,28 @@ public class ExifTag {
 
     private String undefinedTypeValueToString() {
         StringBuilder sbuilder = new StringBuilder();
+        byte buf[] = (byte[]) mValue;
         switch (mTagId) {
             case TAG_COMPONENTS_CONFIGURATION:
-            case TAG_FILE_SOURCE:
-            case TAG_SCENE_TYPE:
-                byte buf[] = (byte[]) mValue;
                 for(int i = 0, n = getComponentCount(); i < n; i++) {
                     if(i != 0) sbuilder.append(" ");
                     sbuilder.append(buf[i]);
                 }
                 break;
             default:
-                sbuilder.append(new String((byte[]) mValue));
+                if (buf.length == 1) {
+                    sbuilder.append(buf[0]);
+                } else {
+                    for (int i = 0, n = buf.length; i < n; i++) {
+                        byte code = buf[i];
+                        if (code == 0) continue;
+                        if (code > 31 && code < 127) {
+                            sbuilder.append((char) code);
+                        } else {
+                            sbuilder.append('.');
+                        }
+                    }
+                }
         }
         return sbuilder.toString();
     }
@@ -1389,10 +1436,10 @@ public class ExifTag {
                 }
                 break;
             case ExifTag.TYPE_ASCII:
-                String s = getString();
-                for (int i = 0, n = s.length(); i < n; i++) {
-                    int code = s.codePointAt(i);
-                    if (code == 0) continue;
+                buf = (byte[]) mValue;
+                for (int i = 0, n = buf.length; i < n; i++) {
+                    byte code = buf[i];
+                    if (code == 0) break;
                     if (code > 31 && code < 127) {
                         sbuilder.append((char) code);
                     } else {