ExifModifier.java revision 104c45677660586026a7e74ef8c47d396403d50e
1104c45677660586026a7e74ef8c47d396403d50eMichael Jurka/* 2104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * Copyright (C) 2012 The Android Open Source Project 3104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * 4104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * Licensed under the Apache License, Version 2.0 (the "License"); 5104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * you may not use this file except in compliance with the License. 6104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * You may obtain a copy of the License at 7104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * 8104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * http://www.apache.org/licenses/LICENSE-2.0 9104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * 10104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * Unless required by applicable law or agreed to in writing, software 11104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * distributed under the License is distributed on an "AS IS" BASIS, 12104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * See the License for the specific language governing permissions and 14104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * limitations under the License. 15104c45677660586026a7e74ef8c47d396403d50eMichael Jurka */ 16104c45677660586026a7e74ef8c47d396403d50eMichael Jurka 17104c45677660586026a7e74ef8c47d396403d50eMichael Jurkapackage com.android.gallery3d.exif; 18104c45677660586026a7e74ef8c47d396403d50eMichael Jurka 19104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport android.util.Log; 20104c45677660586026a7e74ef8c47d396403d50eMichael Jurka 21104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport java.io.Closeable; 22104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport java.io.IOException; 23104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport java.io.InputStream; 24104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport java.nio.ByteBuffer; 25104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport java.nio.ByteOrder; 26104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport java.util.ArrayList; 27104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport java.util.List; 28104c45677660586026a7e74ef8c47d396403d50eMichael Jurka 29104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaclass ExifModifier { 30104c45677660586026a7e74ef8c47d396403d50eMichael Jurka public static final String TAG = "ExifModifier"; 31104c45677660586026a7e74ef8c47d396403d50eMichael Jurka public static final boolean DEBUG = false; 32104c45677660586026a7e74ef8c47d396403d50eMichael Jurka private final ByteBuffer mByteBuffer; 33104c45677660586026a7e74ef8c47d396403d50eMichael Jurka private final ExifData mTagToModified; 34104c45677660586026a7e74ef8c47d396403d50eMichael Jurka private final List<TagOffset> mTagOffsets = new ArrayList<TagOffset>(); 35104c45677660586026a7e74ef8c47d396403d50eMichael Jurka private final ExifInterface mInterface; 36104c45677660586026a7e74ef8c47d396403d50eMichael Jurka private int mOffsetBase; 37104c45677660586026a7e74ef8c47d396403d50eMichael Jurka 38104c45677660586026a7e74ef8c47d396403d50eMichael Jurka private static class TagOffset { 39104c45677660586026a7e74ef8c47d396403d50eMichael Jurka final int mOffset; 40104c45677660586026a7e74ef8c47d396403d50eMichael Jurka final ExifTag mTag; 41104c45677660586026a7e74ef8c47d396403d50eMichael Jurka 42104c45677660586026a7e74ef8c47d396403d50eMichael Jurka TagOffset(ExifTag tag, int offset) { 43104c45677660586026a7e74ef8c47d396403d50eMichael Jurka mTag = tag; 44104c45677660586026a7e74ef8c47d396403d50eMichael Jurka mOffset = offset; 45104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 46104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 47104c45677660586026a7e74ef8c47d396403d50eMichael Jurka 48104c45677660586026a7e74ef8c47d396403d50eMichael Jurka protected ExifModifier(ByteBuffer byteBuffer, ExifInterface iRef) throws IOException, 49104c45677660586026a7e74ef8c47d396403d50eMichael Jurka ExifInvalidFormatException { 50104c45677660586026a7e74ef8c47d396403d50eMichael Jurka mByteBuffer = byteBuffer; 51104c45677660586026a7e74ef8c47d396403d50eMichael Jurka mOffsetBase = byteBuffer.position(); 52104c45677660586026a7e74ef8c47d396403d50eMichael Jurka mInterface = iRef; 53104c45677660586026a7e74ef8c47d396403d50eMichael Jurka InputStream is = null; 54104c45677660586026a7e74ef8c47d396403d50eMichael Jurka try { 55104c45677660586026a7e74ef8c47d396403d50eMichael Jurka is = new ByteBufferInputStream(byteBuffer); 56104c45677660586026a7e74ef8c47d396403d50eMichael Jurka // Do not require any IFD; 57104c45677660586026a7e74ef8c47d396403d50eMichael Jurka ExifParser parser = ExifParser.parse(is, mInterface); 58104c45677660586026a7e74ef8c47d396403d50eMichael Jurka mTagToModified = new ExifData(parser.getByteOrder()); 59104c45677660586026a7e74ef8c47d396403d50eMichael Jurka mOffsetBase += parser.getTiffStartPosition(); 60104c45677660586026a7e74ef8c47d396403d50eMichael Jurka mByteBuffer.position(0); 61104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } finally { 62104c45677660586026a7e74ef8c47d396403d50eMichael Jurka ExifInterface.closeSilently(is); 63104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 64104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 65104c45677660586026a7e74ef8c47d396403d50eMichael Jurka 66104c45677660586026a7e74ef8c47d396403d50eMichael Jurka protected ByteOrder getByteOrder() { 67104c45677660586026a7e74ef8c47d396403d50eMichael Jurka return mTagToModified.getByteOrder(); 68104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 69104c45677660586026a7e74ef8c47d396403d50eMichael Jurka 70104c45677660586026a7e74ef8c47d396403d50eMichael Jurka protected boolean commit() throws IOException, ExifInvalidFormatException { 71104c45677660586026a7e74ef8c47d396403d50eMichael Jurka InputStream is = null; 72104c45677660586026a7e74ef8c47d396403d50eMichael Jurka try { 73104c45677660586026a7e74ef8c47d396403d50eMichael Jurka is = new ByteBufferInputStream(mByteBuffer); 74104c45677660586026a7e74ef8c47d396403d50eMichael Jurka int flag = 0; 75104c45677660586026a7e74ef8c47d396403d50eMichael Jurka IfdData[] ifdDatas = new IfdData[] { 76104c45677660586026a7e74ef8c47d396403d50eMichael Jurka mTagToModified.getIfdData(IfdId.TYPE_IFD_0), 77104c45677660586026a7e74ef8c47d396403d50eMichael Jurka mTagToModified.getIfdData(IfdId.TYPE_IFD_1), 78104c45677660586026a7e74ef8c47d396403d50eMichael Jurka mTagToModified.getIfdData(IfdId.TYPE_IFD_EXIF), 79104c45677660586026a7e74ef8c47d396403d50eMichael Jurka mTagToModified.getIfdData(IfdId.TYPE_IFD_INTEROPERABILITY), 80104c45677660586026a7e74ef8c47d396403d50eMichael Jurka mTagToModified.getIfdData(IfdId.TYPE_IFD_GPS) 81104c45677660586026a7e74ef8c47d396403d50eMichael Jurka }; 82104c45677660586026a7e74ef8c47d396403d50eMichael Jurka 83104c45677660586026a7e74ef8c47d396403d50eMichael Jurka if (ifdDatas[IfdId.TYPE_IFD_0] != null) { 84104c45677660586026a7e74ef8c47d396403d50eMichael Jurka flag |= ExifParser.OPTION_IFD_0; 85104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 86104c45677660586026a7e74ef8c47d396403d50eMichael Jurka if (ifdDatas[IfdId.TYPE_IFD_1] != null) { 87104c45677660586026a7e74ef8c47d396403d50eMichael Jurka flag |= ExifParser.OPTION_IFD_1; 88104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 89104c45677660586026a7e74ef8c47d396403d50eMichael Jurka if (ifdDatas[IfdId.TYPE_IFD_EXIF] != null) { 90104c45677660586026a7e74ef8c47d396403d50eMichael Jurka flag |= ExifParser.OPTION_IFD_EXIF; 91104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 92104c45677660586026a7e74ef8c47d396403d50eMichael Jurka if (ifdDatas[IfdId.TYPE_IFD_GPS] != null) { 93104c45677660586026a7e74ef8c47d396403d50eMichael Jurka flag |= ExifParser.OPTION_IFD_GPS; 94104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 95104c45677660586026a7e74ef8c47d396403d50eMichael Jurka if (ifdDatas[IfdId.TYPE_IFD_INTEROPERABILITY] != null) { 96104c45677660586026a7e74ef8c47d396403d50eMichael Jurka flag |= ExifParser.OPTION_IFD_INTEROPERABILITY; 97104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 98104c45677660586026a7e74ef8c47d396403d50eMichael Jurka 99104c45677660586026a7e74ef8c47d396403d50eMichael Jurka ExifParser parser = ExifParser.parse(is, flag, mInterface); 100104c45677660586026a7e74ef8c47d396403d50eMichael Jurka int event = parser.next(); 101104c45677660586026a7e74ef8c47d396403d50eMichael Jurka IfdData currIfd = null; 102104c45677660586026a7e74ef8c47d396403d50eMichael Jurka while (event != ExifParser.EVENT_END) { 103104c45677660586026a7e74ef8c47d396403d50eMichael Jurka switch (event) { 104104c45677660586026a7e74ef8c47d396403d50eMichael Jurka case ExifParser.EVENT_START_OF_IFD: 105104c45677660586026a7e74ef8c47d396403d50eMichael Jurka currIfd = ifdDatas[parser.getCurrentIfd()]; 106104c45677660586026a7e74ef8c47d396403d50eMichael Jurka if (currIfd == null) { 107104c45677660586026a7e74ef8c47d396403d50eMichael Jurka parser.skipRemainingTagsInCurrentIfd(); 108104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 109104c45677660586026a7e74ef8c47d396403d50eMichael Jurka break; 110104c45677660586026a7e74ef8c47d396403d50eMichael Jurka case ExifParser.EVENT_NEW_TAG: 111104c45677660586026a7e74ef8c47d396403d50eMichael Jurka ExifTag oldTag = parser.getTag(); 112104c45677660586026a7e74ef8c47d396403d50eMichael Jurka ExifTag newTag = currIfd.getTag(oldTag.getTagId()); 113104c45677660586026a7e74ef8c47d396403d50eMichael Jurka if (newTag != null) { 114104c45677660586026a7e74ef8c47d396403d50eMichael Jurka if (newTag.getComponentCount() != oldTag.getComponentCount() 115104c45677660586026a7e74ef8c47d396403d50eMichael Jurka || newTag.getDataType() != oldTag.getDataType()) { 116104c45677660586026a7e74ef8c47d396403d50eMichael Jurka return false; 117104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } else { 118104c45677660586026a7e74ef8c47d396403d50eMichael Jurka mTagOffsets.add(new TagOffset(newTag, oldTag.getOffset())); 119104c45677660586026a7e74ef8c47d396403d50eMichael Jurka currIfd.removeTag(oldTag.getTagId()); 120104c45677660586026a7e74ef8c47d396403d50eMichael Jurka if (currIfd.getTagCount() == 0) { 121104c45677660586026a7e74ef8c47d396403d50eMichael Jurka parser.skipRemainingTagsInCurrentIfd(); 122104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 123104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 124104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 125104c45677660586026a7e74ef8c47d396403d50eMichael Jurka break; 126104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 127104c45677660586026a7e74ef8c47d396403d50eMichael Jurka event = parser.next(); 128104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 129104c45677660586026a7e74ef8c47d396403d50eMichael Jurka for (IfdData ifd : ifdDatas) { 130104c45677660586026a7e74ef8c47d396403d50eMichael Jurka if (ifd != null && ifd.getTagCount() > 0) { 131104c45677660586026a7e74ef8c47d396403d50eMichael Jurka return false; 132104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 133104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 134104c45677660586026a7e74ef8c47d396403d50eMichael Jurka modify(); 135104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } finally { 136104c45677660586026a7e74ef8c47d396403d50eMichael Jurka ExifInterface.closeSilently(is); 137104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 138104c45677660586026a7e74ef8c47d396403d50eMichael Jurka return true; 139104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 140104c45677660586026a7e74ef8c47d396403d50eMichael Jurka 141104c45677660586026a7e74ef8c47d396403d50eMichael Jurka private void modify() { 142104c45677660586026a7e74ef8c47d396403d50eMichael Jurka mByteBuffer.order(getByteOrder()); 143104c45677660586026a7e74ef8c47d396403d50eMichael Jurka for (TagOffset tagOffset : mTagOffsets) { 144104c45677660586026a7e74ef8c47d396403d50eMichael Jurka writeTagValue(tagOffset.mTag, tagOffset.mOffset); 145104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 146104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 147104c45677660586026a7e74ef8c47d396403d50eMichael Jurka 148104c45677660586026a7e74ef8c47d396403d50eMichael Jurka private void writeTagValue(ExifTag tag, int offset) { 149104c45677660586026a7e74ef8c47d396403d50eMichael Jurka if (DEBUG) { 150104c45677660586026a7e74ef8c47d396403d50eMichael Jurka Log.v(TAG, "modifying tag to: \n" + tag.toString()); 151104c45677660586026a7e74ef8c47d396403d50eMichael Jurka Log.v(TAG, "at offset: " + offset); 152104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 153104c45677660586026a7e74ef8c47d396403d50eMichael Jurka mByteBuffer.position(offset + mOffsetBase); 154104c45677660586026a7e74ef8c47d396403d50eMichael Jurka switch (tag.getDataType()) { 155104c45677660586026a7e74ef8c47d396403d50eMichael Jurka case ExifTag.TYPE_ASCII: 156104c45677660586026a7e74ef8c47d396403d50eMichael Jurka byte buf[] = tag.getStringByte(); 157104c45677660586026a7e74ef8c47d396403d50eMichael Jurka if (buf.length == tag.getComponentCount()) { 158104c45677660586026a7e74ef8c47d396403d50eMichael Jurka buf[buf.length - 1] = 0; 159104c45677660586026a7e74ef8c47d396403d50eMichael Jurka mByteBuffer.put(buf); 160104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } else { 161104c45677660586026a7e74ef8c47d396403d50eMichael Jurka mByteBuffer.put(buf); 162104c45677660586026a7e74ef8c47d396403d50eMichael Jurka mByteBuffer.put((byte) 0); 163104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 164104c45677660586026a7e74ef8c47d396403d50eMichael Jurka break; 165104c45677660586026a7e74ef8c47d396403d50eMichael Jurka case ExifTag.TYPE_LONG: 166104c45677660586026a7e74ef8c47d396403d50eMichael Jurka case ExifTag.TYPE_UNSIGNED_LONG: 167104c45677660586026a7e74ef8c47d396403d50eMichael Jurka for (int i = 0, n = tag.getComponentCount(); i < n; i++) { 168104c45677660586026a7e74ef8c47d396403d50eMichael Jurka mByteBuffer.putInt((int) tag.getValueAt(i)); 169104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 170104c45677660586026a7e74ef8c47d396403d50eMichael Jurka break; 171104c45677660586026a7e74ef8c47d396403d50eMichael Jurka case ExifTag.TYPE_RATIONAL: 172104c45677660586026a7e74ef8c47d396403d50eMichael Jurka case ExifTag.TYPE_UNSIGNED_RATIONAL: 173104c45677660586026a7e74ef8c47d396403d50eMichael Jurka for (int i = 0, n = tag.getComponentCount(); i < n; i++) { 174104c45677660586026a7e74ef8c47d396403d50eMichael Jurka Rational v = tag.getRational(i); 175104c45677660586026a7e74ef8c47d396403d50eMichael Jurka mByteBuffer.putInt((int) v.getNumerator()); 176104c45677660586026a7e74ef8c47d396403d50eMichael Jurka mByteBuffer.putInt((int) v.getDenominator()); 177104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 178104c45677660586026a7e74ef8c47d396403d50eMichael Jurka break; 179104c45677660586026a7e74ef8c47d396403d50eMichael Jurka case ExifTag.TYPE_UNDEFINED: 180104c45677660586026a7e74ef8c47d396403d50eMichael Jurka case ExifTag.TYPE_UNSIGNED_BYTE: 181104c45677660586026a7e74ef8c47d396403d50eMichael Jurka buf = new byte[tag.getComponentCount()]; 182104c45677660586026a7e74ef8c47d396403d50eMichael Jurka tag.getBytes(buf); 183104c45677660586026a7e74ef8c47d396403d50eMichael Jurka mByteBuffer.put(buf); 184104c45677660586026a7e74ef8c47d396403d50eMichael Jurka break; 185104c45677660586026a7e74ef8c47d396403d50eMichael Jurka case ExifTag.TYPE_UNSIGNED_SHORT: 186104c45677660586026a7e74ef8c47d396403d50eMichael Jurka for (int i = 0, n = tag.getComponentCount(); i < n; i++) { 187104c45677660586026a7e74ef8c47d396403d50eMichael Jurka mByteBuffer.putShort((short) tag.getValueAt(i)); 188104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 189104c45677660586026a7e74ef8c47d396403d50eMichael Jurka break; 190104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 191104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 192104c45677660586026a7e74ef8c47d396403d50eMichael Jurka 193104c45677660586026a7e74ef8c47d396403d50eMichael Jurka public void modifyTag(ExifTag tag) { 194104c45677660586026a7e74ef8c47d396403d50eMichael Jurka mTagToModified.addTag(tag); 195104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 196104c45677660586026a7e74ef8c47d396403d50eMichael Jurka} 197