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.IOException; 22e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport java.io.InputStream; 23e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport java.nio.ByteBuffer; 24e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport java.nio.ByteOrder; 25e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport java.util.ArrayList; 26e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport java.util.List; 27e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 28e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaclass ExifModifier { 29e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public static final String TAG = "ExifModifier"; 30e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public static final boolean DEBUG = false; 31e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private final ByteBuffer mByteBuffer; 32e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private final ExifData mTagToModified; 33e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private final List<TagOffset> mTagOffsets = new ArrayList<TagOffset>(); 34e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private final ExifInterface mInterface; 35e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private int mOffsetBase; 36e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 37e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private static class TagOffset { 38e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final int mOffset; 39e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final ExifTag mTag; 40e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 41e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka TagOffset(ExifTag tag, int offset) { 42e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mTag = tag; 43e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mOffset = offset; 44e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 45e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 46e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 47e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka protected ExifModifier(ByteBuffer byteBuffer, ExifInterface iRef) throws IOException, 48e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka ExifInvalidFormatException { 49e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mByteBuffer = byteBuffer; 50e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mOffsetBase = byteBuffer.position(); 51e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mInterface = iRef; 52e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka InputStream is = null; 53e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka try { 54e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka is = new ByteBufferInputStream(byteBuffer); 55e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // Do not require any IFD; 56e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka ExifParser parser = ExifParser.parse(is, mInterface); 57e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mTagToModified = new ExifData(parser.getByteOrder()); 58e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mOffsetBase += parser.getTiffStartPosition(); 59e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mByteBuffer.position(0); 60e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } finally { 61e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka ExifInterface.closeSilently(is); 62e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 63e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 64e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 65e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka protected ByteOrder getByteOrder() { 66e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return mTagToModified.getByteOrder(); 67e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 68e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 69e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka protected boolean commit() throws IOException, ExifInvalidFormatException { 70e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka InputStream is = null; 71e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka try { 72e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka is = new ByteBufferInputStream(mByteBuffer); 73e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka int flag = 0; 74e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka IfdData[] ifdDatas = new IfdData[] { 75e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mTagToModified.getIfdData(IfdId.TYPE_IFD_0), 76e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mTagToModified.getIfdData(IfdId.TYPE_IFD_1), 77e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mTagToModified.getIfdData(IfdId.TYPE_IFD_EXIF), 78e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mTagToModified.getIfdData(IfdId.TYPE_IFD_INTEROPERABILITY), 79e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mTagToModified.getIfdData(IfdId.TYPE_IFD_GPS) 80e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka }; 81e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 82e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (ifdDatas[IfdId.TYPE_IFD_0] != null) { 83e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka flag |= ExifParser.OPTION_IFD_0; 84e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 85e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (ifdDatas[IfdId.TYPE_IFD_1] != null) { 86e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka flag |= ExifParser.OPTION_IFD_1; 87e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 88e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (ifdDatas[IfdId.TYPE_IFD_EXIF] != null) { 89e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka flag |= ExifParser.OPTION_IFD_EXIF; 90e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 91e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (ifdDatas[IfdId.TYPE_IFD_GPS] != null) { 92e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka flag |= ExifParser.OPTION_IFD_GPS; 93e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 94e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (ifdDatas[IfdId.TYPE_IFD_INTEROPERABILITY] != null) { 95e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka flag |= ExifParser.OPTION_IFD_INTEROPERABILITY; 96e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 97e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 98e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka ExifParser parser = ExifParser.parse(is, flag, mInterface); 99e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka int event = parser.next(); 100e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka IfdData currIfd = null; 101e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka while (event != ExifParser.EVENT_END) { 102e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka switch (event) { 103e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka case ExifParser.EVENT_START_OF_IFD: 104e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka currIfd = ifdDatas[parser.getCurrentIfd()]; 105e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (currIfd == null) { 106e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka parser.skipRemainingTagsInCurrentIfd(); 107e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 108e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka break; 109e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka case ExifParser.EVENT_NEW_TAG: 110e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka ExifTag oldTag = parser.getTag(); 111e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka ExifTag newTag = currIfd.getTag(oldTag.getTagId()); 112e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (newTag != null) { 113e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (newTag.getComponentCount() != oldTag.getComponentCount() 114e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka || newTag.getDataType() != oldTag.getDataType()) { 115e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return false; 116e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } else { 117e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mTagOffsets.add(new TagOffset(newTag, oldTag.getOffset())); 118e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka currIfd.removeTag(oldTag.getTagId()); 119e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (currIfd.getTagCount() == 0) { 120e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka parser.skipRemainingTagsInCurrentIfd(); 121e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 122e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 123e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 124e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka break; 125e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 126e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka event = parser.next(); 127e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 128e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka for (IfdData ifd : ifdDatas) { 129e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (ifd != null && ifd.getTagCount() > 0) { 130e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return false; 131e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 132e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 133e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka modify(); 134e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } finally { 135e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka ExifInterface.closeSilently(is); 136e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 137e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return true; 138e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 139e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 140e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private void modify() { 141e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mByteBuffer.order(getByteOrder()); 142e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka for (TagOffset tagOffset : mTagOffsets) { 143e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka writeTagValue(tagOffset.mTag, tagOffset.mOffset); 144e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 145e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 146e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 147e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private void writeTagValue(ExifTag tag, int offset) { 148e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (DEBUG) { 149e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Log.v(TAG, "modifying tag to: \n" + tag.toString()); 150e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Log.v(TAG, "at offset: " + offset); 151e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 152e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mByteBuffer.position(offset + mOffsetBase); 153e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka switch (tag.getDataType()) { 154e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka case ExifTag.TYPE_ASCII: 155e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka byte buf[] = tag.getStringByte(); 156e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (buf.length == tag.getComponentCount()) { 157e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka buf[buf.length - 1] = 0; 158e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mByteBuffer.put(buf); 159e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } else { 160e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mByteBuffer.put(buf); 161e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mByteBuffer.put((byte) 0); 162e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 163e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka break; 164e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka case ExifTag.TYPE_LONG: 165e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka case ExifTag.TYPE_UNSIGNED_LONG: 166e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka for (int i = 0, n = tag.getComponentCount(); i < n; i++) { 167e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mByteBuffer.putInt((int) tag.getValueAt(i)); 168e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 169e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka break; 170e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka case ExifTag.TYPE_RATIONAL: 171e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka case ExifTag.TYPE_UNSIGNED_RATIONAL: 172e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka for (int i = 0, n = tag.getComponentCount(); i < n; i++) { 173e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Rational v = tag.getRational(i); 174e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mByteBuffer.putInt((int) v.getNumerator()); 175e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mByteBuffer.putInt((int) v.getDenominator()); 176e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 177e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka break; 178e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka case ExifTag.TYPE_UNDEFINED: 179e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka case ExifTag.TYPE_UNSIGNED_BYTE: 180e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka buf = new byte[tag.getComponentCount()]; 181e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka tag.getBytes(buf); 182e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mByteBuffer.put(buf); 183e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka break; 184e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka case ExifTag.TYPE_UNSIGNED_SHORT: 185e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka for (int i = 0, n = tag.getComponentCount(); i < n; i++) { 186e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mByteBuffer.putShort((short) tag.getValueAt(i)); 187e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 188e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka break; 189e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 190e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 191e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 192e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public void modifyTag(ExifTag tag) { 193e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mTagToModified.addTag(tag); 194e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 195e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka} 196