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.IOException; 22104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport java.io.InputStream; 23104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport java.nio.ByteBuffer; 24104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport java.nio.ByteOrder; 25104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport java.util.ArrayList; 26104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport java.util.List; 27104c45677660586026a7e74ef8c47d396403d50eMichael Jurka 28104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaclass ExifModifier { 29104c45677660586026a7e74ef8c47d396403d50eMichael Jurka public static final String TAG = "ExifModifier"; 30104c45677660586026a7e74ef8c47d396403d50eMichael Jurka public static final boolean DEBUG = false; 31104c45677660586026a7e74ef8c47d396403d50eMichael Jurka private final ByteBuffer mByteBuffer; 32104c45677660586026a7e74ef8c47d396403d50eMichael Jurka private final ExifData mTagToModified; 33104c45677660586026a7e74ef8c47d396403d50eMichael Jurka private final List<TagOffset> mTagOffsets = new ArrayList<TagOffset>(); 34104c45677660586026a7e74ef8c47d396403d50eMichael Jurka private final ExifInterface mInterface; 35104c45677660586026a7e74ef8c47d396403d50eMichael Jurka private int mOffsetBase; 36104c45677660586026a7e74ef8c47d396403d50eMichael Jurka 37104c45677660586026a7e74ef8c47d396403d50eMichael Jurka private static class TagOffset { 38104c45677660586026a7e74ef8c47d396403d50eMichael Jurka final int mOffset; 39104c45677660586026a7e74ef8c47d396403d50eMichael Jurka final ExifTag mTag; 40104c45677660586026a7e74ef8c47d396403d50eMichael Jurka 41104c45677660586026a7e74ef8c47d396403d50eMichael Jurka TagOffset(ExifTag tag, int offset) { 42104c45677660586026a7e74ef8c47d396403d50eMichael Jurka mTag = tag; 43104c45677660586026a7e74ef8c47d396403d50eMichael Jurka mOffset = offset; 44104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 45104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 46104c45677660586026a7e74ef8c47d396403d50eMichael Jurka 47104c45677660586026a7e74ef8c47d396403d50eMichael Jurka protected ExifModifier(ByteBuffer byteBuffer, ExifInterface iRef) throws IOException, 48104c45677660586026a7e74ef8c47d396403d50eMichael Jurka ExifInvalidFormatException { 49104c45677660586026a7e74ef8c47d396403d50eMichael Jurka mByteBuffer = byteBuffer; 50104c45677660586026a7e74ef8c47d396403d50eMichael Jurka mOffsetBase = byteBuffer.position(); 51104c45677660586026a7e74ef8c47d396403d50eMichael Jurka mInterface = iRef; 52104c45677660586026a7e74ef8c47d396403d50eMichael Jurka InputStream is = null; 53104c45677660586026a7e74ef8c47d396403d50eMichael Jurka try { 54104c45677660586026a7e74ef8c47d396403d50eMichael Jurka is = new ByteBufferInputStream(byteBuffer); 55104c45677660586026a7e74ef8c47d396403d50eMichael Jurka // Do not require any IFD; 56104c45677660586026a7e74ef8c47d396403d50eMichael Jurka ExifParser parser = ExifParser.parse(is, mInterface); 57104c45677660586026a7e74ef8c47d396403d50eMichael Jurka mTagToModified = new ExifData(parser.getByteOrder()); 58104c45677660586026a7e74ef8c47d396403d50eMichael Jurka mOffsetBase += parser.getTiffStartPosition(); 59104c45677660586026a7e74ef8c47d396403d50eMichael Jurka mByteBuffer.position(0); 60104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } finally { 61104c45677660586026a7e74ef8c47d396403d50eMichael Jurka ExifInterface.closeSilently(is); 62104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 63104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 64104c45677660586026a7e74ef8c47d396403d50eMichael Jurka 65104c45677660586026a7e74ef8c47d396403d50eMichael Jurka protected ByteOrder getByteOrder() { 66104c45677660586026a7e74ef8c47d396403d50eMichael Jurka return mTagToModified.getByteOrder(); 67104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 68104c45677660586026a7e74ef8c47d396403d50eMichael Jurka 69104c45677660586026a7e74ef8c47d396403d50eMichael Jurka protected boolean commit() throws IOException, ExifInvalidFormatException { 70104c45677660586026a7e74ef8c47d396403d50eMichael Jurka InputStream is = null; 71104c45677660586026a7e74ef8c47d396403d50eMichael Jurka try { 72104c45677660586026a7e74ef8c47d396403d50eMichael Jurka is = new ByteBufferInputStream(mByteBuffer); 73104c45677660586026a7e74ef8c47d396403d50eMichael Jurka int flag = 0; 74104c45677660586026a7e74ef8c47d396403d50eMichael Jurka IfdData[] ifdDatas = new IfdData[] { 75104c45677660586026a7e74ef8c47d396403d50eMichael Jurka mTagToModified.getIfdData(IfdId.TYPE_IFD_0), 76104c45677660586026a7e74ef8c47d396403d50eMichael Jurka mTagToModified.getIfdData(IfdId.TYPE_IFD_1), 77104c45677660586026a7e74ef8c47d396403d50eMichael Jurka mTagToModified.getIfdData(IfdId.TYPE_IFD_EXIF), 78104c45677660586026a7e74ef8c47d396403d50eMichael Jurka mTagToModified.getIfdData(IfdId.TYPE_IFD_INTEROPERABILITY), 79104c45677660586026a7e74ef8c47d396403d50eMichael Jurka mTagToModified.getIfdData(IfdId.TYPE_IFD_GPS) 80104c45677660586026a7e74ef8c47d396403d50eMichael Jurka }; 81104c45677660586026a7e74ef8c47d396403d50eMichael Jurka 82104c45677660586026a7e74ef8c47d396403d50eMichael Jurka if (ifdDatas[IfdId.TYPE_IFD_0] != null) { 83104c45677660586026a7e74ef8c47d396403d50eMichael Jurka flag |= ExifParser.OPTION_IFD_0; 84104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 85104c45677660586026a7e74ef8c47d396403d50eMichael Jurka if (ifdDatas[IfdId.TYPE_IFD_1] != null) { 86104c45677660586026a7e74ef8c47d396403d50eMichael Jurka flag |= ExifParser.OPTION_IFD_1; 87104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 88104c45677660586026a7e74ef8c47d396403d50eMichael Jurka if (ifdDatas[IfdId.TYPE_IFD_EXIF] != null) { 89104c45677660586026a7e74ef8c47d396403d50eMichael Jurka flag |= ExifParser.OPTION_IFD_EXIF; 90104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 91104c45677660586026a7e74ef8c47d396403d50eMichael Jurka if (ifdDatas[IfdId.TYPE_IFD_GPS] != null) { 92104c45677660586026a7e74ef8c47d396403d50eMichael Jurka flag |= ExifParser.OPTION_IFD_GPS; 93104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 94104c45677660586026a7e74ef8c47d396403d50eMichael Jurka if (ifdDatas[IfdId.TYPE_IFD_INTEROPERABILITY] != null) { 95104c45677660586026a7e74ef8c47d396403d50eMichael Jurka flag |= ExifParser.OPTION_IFD_INTEROPERABILITY; 96104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 97104c45677660586026a7e74ef8c47d396403d50eMichael Jurka 98104c45677660586026a7e74ef8c47d396403d50eMichael Jurka ExifParser parser = ExifParser.parse(is, flag, mInterface); 99104c45677660586026a7e74ef8c47d396403d50eMichael Jurka int event = parser.next(); 100104c45677660586026a7e74ef8c47d396403d50eMichael Jurka IfdData currIfd = null; 101104c45677660586026a7e74ef8c47d396403d50eMichael Jurka while (event != ExifParser.EVENT_END) { 102104c45677660586026a7e74ef8c47d396403d50eMichael Jurka switch (event) { 103104c45677660586026a7e74ef8c47d396403d50eMichael Jurka case ExifParser.EVENT_START_OF_IFD: 104104c45677660586026a7e74ef8c47d396403d50eMichael Jurka currIfd = ifdDatas[parser.getCurrentIfd()]; 105104c45677660586026a7e74ef8c47d396403d50eMichael Jurka if (currIfd == null) { 106104c45677660586026a7e74ef8c47d396403d50eMichael Jurka parser.skipRemainingTagsInCurrentIfd(); 107104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 108104c45677660586026a7e74ef8c47d396403d50eMichael Jurka break; 109104c45677660586026a7e74ef8c47d396403d50eMichael Jurka case ExifParser.EVENT_NEW_TAG: 110104c45677660586026a7e74ef8c47d396403d50eMichael Jurka ExifTag oldTag = parser.getTag(); 111104c45677660586026a7e74ef8c47d396403d50eMichael Jurka ExifTag newTag = currIfd.getTag(oldTag.getTagId()); 112104c45677660586026a7e74ef8c47d396403d50eMichael Jurka if (newTag != null) { 113104c45677660586026a7e74ef8c47d396403d50eMichael Jurka if (newTag.getComponentCount() != oldTag.getComponentCount() 114104c45677660586026a7e74ef8c47d396403d50eMichael Jurka || newTag.getDataType() != oldTag.getDataType()) { 115104c45677660586026a7e74ef8c47d396403d50eMichael Jurka return false; 116104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } else { 117104c45677660586026a7e74ef8c47d396403d50eMichael Jurka mTagOffsets.add(new TagOffset(newTag, oldTag.getOffset())); 118104c45677660586026a7e74ef8c47d396403d50eMichael Jurka currIfd.removeTag(oldTag.getTagId()); 119104c45677660586026a7e74ef8c47d396403d50eMichael Jurka if (currIfd.getTagCount() == 0) { 120104c45677660586026a7e74ef8c47d396403d50eMichael Jurka parser.skipRemainingTagsInCurrentIfd(); 121104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 122104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 123104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 124104c45677660586026a7e74ef8c47d396403d50eMichael Jurka break; 125104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 126104c45677660586026a7e74ef8c47d396403d50eMichael Jurka event = parser.next(); 127104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 128104c45677660586026a7e74ef8c47d396403d50eMichael Jurka for (IfdData ifd : ifdDatas) { 129104c45677660586026a7e74ef8c47d396403d50eMichael Jurka if (ifd != null && ifd.getTagCount() > 0) { 130104c45677660586026a7e74ef8c47d396403d50eMichael Jurka return false; 131104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 132104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 133104c45677660586026a7e74ef8c47d396403d50eMichael Jurka modify(); 134104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } finally { 135104c45677660586026a7e74ef8c47d396403d50eMichael Jurka ExifInterface.closeSilently(is); 136104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 137104c45677660586026a7e74ef8c47d396403d50eMichael Jurka return true; 138104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 139104c45677660586026a7e74ef8c47d396403d50eMichael Jurka 140104c45677660586026a7e74ef8c47d396403d50eMichael Jurka private void modify() { 141104c45677660586026a7e74ef8c47d396403d50eMichael Jurka mByteBuffer.order(getByteOrder()); 142104c45677660586026a7e74ef8c47d396403d50eMichael Jurka for (TagOffset tagOffset : mTagOffsets) { 143104c45677660586026a7e74ef8c47d396403d50eMichael Jurka writeTagValue(tagOffset.mTag, tagOffset.mOffset); 144104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 145104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 146104c45677660586026a7e74ef8c47d396403d50eMichael Jurka 147104c45677660586026a7e74ef8c47d396403d50eMichael Jurka private void writeTagValue(ExifTag tag, int offset) { 148104c45677660586026a7e74ef8c47d396403d50eMichael Jurka if (DEBUG) { 149104c45677660586026a7e74ef8c47d396403d50eMichael Jurka Log.v(TAG, "modifying tag to: \n" + tag.toString()); 150104c45677660586026a7e74ef8c47d396403d50eMichael Jurka Log.v(TAG, "at offset: " + offset); 151104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 152104c45677660586026a7e74ef8c47d396403d50eMichael Jurka mByteBuffer.position(offset + mOffsetBase); 153104c45677660586026a7e74ef8c47d396403d50eMichael Jurka switch (tag.getDataType()) { 154104c45677660586026a7e74ef8c47d396403d50eMichael Jurka case ExifTag.TYPE_ASCII: 155104c45677660586026a7e74ef8c47d396403d50eMichael Jurka byte buf[] = tag.getStringByte(); 156104c45677660586026a7e74ef8c47d396403d50eMichael Jurka if (buf.length == tag.getComponentCount()) { 157104c45677660586026a7e74ef8c47d396403d50eMichael Jurka buf[buf.length - 1] = 0; 158104c45677660586026a7e74ef8c47d396403d50eMichael Jurka mByteBuffer.put(buf); 159104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } else { 160104c45677660586026a7e74ef8c47d396403d50eMichael Jurka mByteBuffer.put(buf); 161104c45677660586026a7e74ef8c47d396403d50eMichael Jurka mByteBuffer.put((byte) 0); 162104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 163104c45677660586026a7e74ef8c47d396403d50eMichael Jurka break; 164104c45677660586026a7e74ef8c47d396403d50eMichael Jurka case ExifTag.TYPE_LONG: 165104c45677660586026a7e74ef8c47d396403d50eMichael Jurka case ExifTag.TYPE_UNSIGNED_LONG: 166104c45677660586026a7e74ef8c47d396403d50eMichael Jurka for (int i = 0, n = tag.getComponentCount(); i < n; i++) { 167104c45677660586026a7e74ef8c47d396403d50eMichael Jurka mByteBuffer.putInt((int) tag.getValueAt(i)); 168104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 169104c45677660586026a7e74ef8c47d396403d50eMichael Jurka break; 170104c45677660586026a7e74ef8c47d396403d50eMichael Jurka case ExifTag.TYPE_RATIONAL: 171104c45677660586026a7e74ef8c47d396403d50eMichael Jurka case ExifTag.TYPE_UNSIGNED_RATIONAL: 172104c45677660586026a7e74ef8c47d396403d50eMichael Jurka for (int i = 0, n = tag.getComponentCount(); i < n; i++) { 173104c45677660586026a7e74ef8c47d396403d50eMichael Jurka Rational v = tag.getRational(i); 174104c45677660586026a7e74ef8c47d396403d50eMichael Jurka mByteBuffer.putInt((int) v.getNumerator()); 175104c45677660586026a7e74ef8c47d396403d50eMichael Jurka mByteBuffer.putInt((int) v.getDenominator()); 176104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 177104c45677660586026a7e74ef8c47d396403d50eMichael Jurka break; 178104c45677660586026a7e74ef8c47d396403d50eMichael Jurka case ExifTag.TYPE_UNDEFINED: 179104c45677660586026a7e74ef8c47d396403d50eMichael Jurka case ExifTag.TYPE_UNSIGNED_BYTE: 180104c45677660586026a7e74ef8c47d396403d50eMichael Jurka buf = new byte[tag.getComponentCount()]; 181104c45677660586026a7e74ef8c47d396403d50eMichael Jurka tag.getBytes(buf); 182104c45677660586026a7e74ef8c47d396403d50eMichael Jurka mByteBuffer.put(buf); 183104c45677660586026a7e74ef8c47d396403d50eMichael Jurka break; 184104c45677660586026a7e74ef8c47d396403d50eMichael Jurka case ExifTag.TYPE_UNSIGNED_SHORT: 185104c45677660586026a7e74ef8c47d396403d50eMichael Jurka for (int i = 0, n = tag.getComponentCount(); i < n; i++) { 186104c45677660586026a7e74ef8c47d396403d50eMichael Jurka mByteBuffer.putShort((short) tag.getValueAt(i)); 187104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 188104c45677660586026a7e74ef8c47d396403d50eMichael Jurka break; 189104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 190104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 191104c45677660586026a7e74ef8c47d396403d50eMichael Jurka 192104c45677660586026a7e74ef8c47d396403d50eMichael Jurka public void modifyTag(ExifTag tag) { 193104c45677660586026a7e74ef8c47d396403d50eMichael Jurka mTagToModified.addTag(tag); 194104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 195104c45677660586026a7e74ef8c47d396403d50eMichael Jurka} 196