ExifModifier.java revision e8d1bf7a439450b9979701909164a6baffbe8bae
1e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka/* 2e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * Copyright (C) 2012 The Android Open Source Project 3e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * 4e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * Licensed under the Apache License, Version 2.0 (the "License"); 5e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * you may not use this file except in compliance with the License. 6e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * You may obtain a copy of the License at 7e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * 8e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * http://www.apache.org/licenses/LICENSE-2.0 9e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * 10e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * Unless required by applicable law or agreed to in writing, software 11e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * distributed under the License is distributed on an "AS IS" BASIS, 12e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * See the License for the specific language governing permissions and 14e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * limitations under the License. 15e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka */ 16e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 17e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkapackage com.android.gallery3d.exif; 18e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 19e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.util.Log; 20e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 21e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport java.io.Closeable; 22e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport java.io.IOException; 23e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport java.io.InputStream; 24e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport java.nio.ByteBuffer; 25e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport java.nio.ByteOrder; 26e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport java.util.ArrayList; 27e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport java.util.List; 28e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 29e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaclass ExifModifier { 30e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public static final String TAG = "ExifModifier"; 31e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public static final boolean DEBUG = false; 32e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private final ByteBuffer mByteBuffer; 33e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private final ExifData mTagToModified; 34e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private final List<TagOffset> mTagOffsets = new ArrayList<TagOffset>(); 35e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private final ExifInterface mInterface; 36e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private int mOffsetBase; 37e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 38e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private static class TagOffset { 39e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final int mOffset; 40e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final ExifTag mTag; 41e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 42e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka TagOffset(ExifTag tag, int offset) { 43e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mTag = tag; 44e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mOffset = offset; 45e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 46e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 47e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 48e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka protected ExifModifier(ByteBuffer byteBuffer, ExifInterface iRef) throws IOException, 49e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka ExifInvalidFormatException { 50e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mByteBuffer = byteBuffer; 51e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mOffsetBase = byteBuffer.position(); 52e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mInterface = iRef; 53e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka InputStream is = null; 54e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka try { 55e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka is = new ByteBufferInputStream(byteBuffer); 56e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // Do not require any IFD; 57e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka ExifParser parser = ExifParser.parse(is, mInterface); 58e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mTagToModified = new ExifData(parser.getByteOrder()); 59e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mOffsetBase += parser.getTiffStartPosition(); 60e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mByteBuffer.position(0); 61e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } finally { 62e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka ExifInterface.closeSilently(is); 63e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 64e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 65e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 66e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka protected ByteOrder getByteOrder() { 67e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return mTagToModified.getByteOrder(); 68e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 69e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 70e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka protected boolean commit() throws IOException, ExifInvalidFormatException { 71e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka InputStream is = null; 72e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka try { 73e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka is = new ByteBufferInputStream(mByteBuffer); 74e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka int flag = 0; 75e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka IfdData[] ifdDatas = new IfdData[] { 76e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mTagToModified.getIfdData(IfdId.TYPE_IFD_0), 77e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mTagToModified.getIfdData(IfdId.TYPE_IFD_1), 78e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mTagToModified.getIfdData(IfdId.TYPE_IFD_EXIF), 79e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mTagToModified.getIfdData(IfdId.TYPE_IFD_INTEROPERABILITY), 80e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mTagToModified.getIfdData(IfdId.TYPE_IFD_GPS) 81e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka }; 82e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 83e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (ifdDatas[IfdId.TYPE_IFD_0] != null) { 84e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka flag |= ExifParser.OPTION_IFD_0; 85e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 86e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (ifdDatas[IfdId.TYPE_IFD_1] != null) { 87e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka flag |= ExifParser.OPTION_IFD_1; 88e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 89e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (ifdDatas[IfdId.TYPE_IFD_EXIF] != null) { 90e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka flag |= ExifParser.OPTION_IFD_EXIF; 91e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 92e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (ifdDatas[IfdId.TYPE_IFD_GPS] != null) { 93e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka flag |= ExifParser.OPTION_IFD_GPS; 94e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 95e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (ifdDatas[IfdId.TYPE_IFD_INTEROPERABILITY] != null) { 96e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka flag |= ExifParser.OPTION_IFD_INTEROPERABILITY; 97e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 98e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 99e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka ExifParser parser = ExifParser.parse(is, flag, mInterface); 100e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka int event = parser.next(); 101e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka IfdData currIfd = null; 102e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka while (event != ExifParser.EVENT_END) { 103e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka switch (event) { 104e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka case ExifParser.EVENT_START_OF_IFD: 105e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka currIfd = ifdDatas[parser.getCurrentIfd()]; 106e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (currIfd == null) { 107e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka parser.skipRemainingTagsInCurrentIfd(); 108e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 109e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka break; 110e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka case ExifParser.EVENT_NEW_TAG: 111e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka ExifTag oldTag = parser.getTag(); 112e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka ExifTag newTag = currIfd.getTag(oldTag.getTagId()); 113e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (newTag != null) { 114e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (newTag.getComponentCount() != oldTag.getComponentCount() 115e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka || newTag.getDataType() != oldTag.getDataType()) { 116e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return false; 117e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } else { 118e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mTagOffsets.add(new TagOffset(newTag, oldTag.getOffset())); 119e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka currIfd.removeTag(oldTag.getTagId()); 120e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (currIfd.getTagCount() == 0) { 121e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka parser.skipRemainingTagsInCurrentIfd(); 122e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 123e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 124e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 125e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka break; 126e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 127e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka event = parser.next(); 128e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 129e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka for (IfdData ifd : ifdDatas) { 130e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (ifd != null && ifd.getTagCount() > 0) { 131e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return false; 132e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 133e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 134e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka modify(); 135e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } finally { 136e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka ExifInterface.closeSilently(is); 137e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 138e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return true; 139e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 140e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 141e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private void modify() { 142e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mByteBuffer.order(getByteOrder()); 143e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka for (TagOffset tagOffset : mTagOffsets) { 144e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka writeTagValue(tagOffset.mTag, tagOffset.mOffset); 145e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 146e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 147e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 148e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private void writeTagValue(ExifTag tag, int offset) { 149e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (DEBUG) { 150e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Log.v(TAG, "modifying tag to: \n" + tag.toString()); 151e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Log.v(TAG, "at offset: " + offset); 152e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 153e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mByteBuffer.position(offset + mOffsetBase); 154e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka switch (tag.getDataType()) { 155e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka case ExifTag.TYPE_ASCII: 156e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka byte buf[] = tag.getStringByte(); 157e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (buf.length == tag.getComponentCount()) { 158e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka buf[buf.length - 1] = 0; 159e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mByteBuffer.put(buf); 160e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } else { 161e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mByteBuffer.put(buf); 162e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mByteBuffer.put((byte) 0); 163e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 164e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka break; 165e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka case ExifTag.TYPE_LONG: 166e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka case ExifTag.TYPE_UNSIGNED_LONG: 167e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka for (int i = 0, n = tag.getComponentCount(); i < n; i++) { 168e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mByteBuffer.putInt((int) tag.getValueAt(i)); 169e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 170e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka break; 171e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka case ExifTag.TYPE_RATIONAL: 172e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka case ExifTag.TYPE_UNSIGNED_RATIONAL: 173e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka for (int i = 0, n = tag.getComponentCount(); i < n; i++) { 174e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Rational v = tag.getRational(i); 175e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mByteBuffer.putInt((int) v.getNumerator()); 176e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mByteBuffer.putInt((int) v.getDenominator()); 177e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 178e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka break; 179e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka case ExifTag.TYPE_UNDEFINED: 180e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka case ExifTag.TYPE_UNSIGNED_BYTE: 181e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka buf = new byte[tag.getComponentCount()]; 182e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka tag.getBytes(buf); 183e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mByteBuffer.put(buf); 184e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka break; 185e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka case ExifTag.TYPE_UNSIGNED_SHORT: 186e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka for (int i = 0, n = tag.getComponentCount(); i < n; i++) { 187e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mByteBuffer.putShort((short) tag.getValueAt(i)); 188e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 189e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka break; 190e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 191e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 192e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 193e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public void modifyTag(ExifTag tag) { 194e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mTagToModified.addTag(tag); 195e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 196e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka} 197