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