From ff0f96c07827c39df5a0b408a028e7198dc8294b Mon Sep 17 00:00:00 2001 From: Earl Ou Date: Fri, 19 Oct 2012 16:50:34 +0800 Subject: Handle the last null byte in Exif tag. Change-Id: Ic802810d11518dfeb80d1338db65b2e1e1cc8476 --- .../android/gallery3d/exif/ExifOutputStream.java | 3 +- .../src/com/android/gallery3d/exif/ExifParser.java | 23 +++---- .../src/com/android/gallery3d/exif/ExifTag.java | 77 +++++++++++++++++----- 3 files changed, 75 insertions(+), 28 deletions(-) (limited to 'gallerycommon/src') diff --git a/gallerycommon/src/com/android/gallery3d/exif/ExifOutputStream.java b/gallerycommon/src/com/android/gallery3d/exif/ExifOutputStream.java index e5e776098..46cd65594 100644 --- a/gallerycommon/src/com/android/gallery3d/exif/ExifOutputStream.java +++ b/gallerycommon/src/com/android/gallery3d/exif/ExifOutputStream.java @@ -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); diff --git a/gallerycommon/src/com/android/gallery3d/exif/ExifParser.java b/gallerycommon/src/com/android/gallery3d/exif/ExifParser.java index edc8f190f..2f5c5438d 100644 --- a/gallerycommon/src/com/android/gallery3d/exif/ExifParser.java +++ b/gallerycommon/src/com/android/gallery3d/exif/ExifParser.java @@ -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 +} diff --git a/gallerycommon/src/com/android/gallery3d/exif/ExifTag.java b/gallerycommon/src/com/android/gallery3d/exif/ExifTag.java index 1592479e2..37b6d9fd0 100644 --- a/gallerycommon/src/com/android/gallery3d/exif/ExifTag.java +++ b/gallerycommon/src/com/android/gallery3d/exif/ExifTag.java @@ -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; @@ -969,6 +971,15 @@ public class ExifTag { return mComponentCount; } + /** + * 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 { -- cgit v1.2.3