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