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