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