| /* |
| * Copyright (C) 2012 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.camera.exif; |
| |
| import com.android.camera.debug.Log; |
| |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.nio.ByteBuffer; |
| import java.nio.ByteOrder; |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| class ExifModifier { |
| public static final Log.Tag TAG = new Log.Tag("ExifModifier"); |
| public static final boolean DEBUG = false; |
| private final ByteBuffer mByteBuffer; |
| private final ExifData mTagToModified; |
| private final List<TagOffset> mTagOffsets = new ArrayList<TagOffset>(); |
| private final ExifInterface mInterface; |
| private int mOffsetBase; |
| |
| private static class TagOffset { |
| final int mOffset; |
| final ExifTag mTag; |
| |
| TagOffset(ExifTag tag, int offset) { |
| mTag = tag; |
| mOffset = offset; |
| } |
| } |
| |
| protected ExifModifier(ByteBuffer byteBuffer, ExifInterface iRef) throws IOException, |
| ExifInvalidFormatException { |
| mByteBuffer = byteBuffer; |
| mOffsetBase = byteBuffer.position(); |
| mInterface = iRef; |
| InputStream is = null; |
| try { |
| is = new ByteBufferInputStream(byteBuffer); |
| // Do not require any IFD; |
| ExifParser parser = ExifParser.parse(is, mInterface); |
| mTagToModified = new ExifData(parser.getByteOrder()); |
| mOffsetBase += parser.getTiffStartPosition(); |
| mByteBuffer.position(0); |
| } finally { |
| ExifInterface.closeSilently(is); |
| } |
| } |
| |
| protected ByteOrder getByteOrder() { |
| return mTagToModified.getByteOrder(); |
| } |
| |
| protected boolean commit() throws IOException, ExifInvalidFormatException { |
| InputStream is = null; |
| try { |
| is = new ByteBufferInputStream(mByteBuffer); |
| int flag = 0; |
| IfdData[] ifdDatas = new IfdData[] { |
| mTagToModified.getIfdData(IfdId.TYPE_IFD_0), |
| mTagToModified.getIfdData(IfdId.TYPE_IFD_1), |
| mTagToModified.getIfdData(IfdId.TYPE_IFD_EXIF), |
| mTagToModified.getIfdData(IfdId.TYPE_IFD_INTEROPERABILITY), |
| mTagToModified.getIfdData(IfdId.TYPE_IFD_GPS) |
| }; |
| |
| if (ifdDatas[IfdId.TYPE_IFD_0] != null) { |
| flag |= ExifParser.OPTION_IFD_0; |
| } |
| if (ifdDatas[IfdId.TYPE_IFD_1] != null) { |
| flag |= ExifParser.OPTION_IFD_1; |
| } |
| if (ifdDatas[IfdId.TYPE_IFD_EXIF] != null) { |
| flag |= ExifParser.OPTION_IFD_EXIF; |
| } |
| if (ifdDatas[IfdId.TYPE_IFD_GPS] != null) { |
| flag |= ExifParser.OPTION_IFD_GPS; |
| } |
| if (ifdDatas[IfdId.TYPE_IFD_INTEROPERABILITY] != null) { |
| flag |= ExifParser.OPTION_IFD_INTEROPERABILITY; |
| } |
| |
| ExifParser parser = ExifParser.parse(is, flag, mInterface); |
| int event = parser.next(); |
| IfdData currIfd = null; |
| while (event != ExifParser.EVENT_END) { |
| switch (event) { |
| case ExifParser.EVENT_START_OF_IFD: |
| currIfd = ifdDatas[parser.getCurrentIfd()]; |
| if (currIfd == null) { |
| parser.skipRemainingTagsInCurrentIfd(); |
| } |
| break; |
| case ExifParser.EVENT_NEW_TAG: |
| ExifTag oldTag = parser.getTag(); |
| ExifTag newTag = currIfd.getTag(oldTag.getTagId()); |
| if (newTag != null) { |
| if (newTag.getComponentCount() != oldTag.getComponentCount() |
| || newTag.getDataType() != oldTag.getDataType()) { |
| return false; |
| } else { |
| mTagOffsets.add(new TagOffset(newTag, oldTag.getOffset())); |
| currIfd.removeTag(oldTag.getTagId()); |
| if (currIfd.getTagCount() == 0) { |
| parser.skipRemainingTagsInCurrentIfd(); |
| } |
| } |
| } |
| break; |
| } |
| event = parser.next(); |
| } |
| for (IfdData ifd : ifdDatas) { |
| if (ifd != null && ifd.getTagCount() > 0) { |
| return false; |
| } |
| } |
| modify(); |
| } finally { |
| ExifInterface.closeSilently(is); |
| } |
| return true; |
| } |
| |
| private void modify() { |
| mByteBuffer.order(getByteOrder()); |
| for (TagOffset tagOffset : mTagOffsets) { |
| writeTagValue(tagOffset.mTag, tagOffset.mOffset); |
| } |
| } |
| |
| private void writeTagValue(ExifTag tag, int offset) { |
| if (DEBUG) { |
| Log.v(TAG, "modifying tag to: \n" + tag.toString()); |
| Log.v(TAG, "at offset: " + offset); |
| } |
| mByteBuffer.position(offset + mOffsetBase); |
| switch (tag.getDataType()) { |
| case ExifTag.TYPE_ASCII: |
| byte buf[] = tag.getStringByte(); |
| if (buf.length == tag.getComponentCount()) { |
| buf[buf.length - 1] = 0; |
| mByteBuffer.put(buf); |
| } else { |
| mByteBuffer.put(buf); |
| mByteBuffer.put((byte) 0); |
| } |
| break; |
| case ExifTag.TYPE_LONG: |
| case ExifTag.TYPE_UNSIGNED_LONG: |
| for (int i = 0, n = tag.getComponentCount(); i < n; i++) { |
| mByteBuffer.putInt((int) tag.getValueAt(i)); |
| } |
| break; |
| case ExifTag.TYPE_RATIONAL: |
| case ExifTag.TYPE_UNSIGNED_RATIONAL: |
| for (int i = 0, n = tag.getComponentCount(); i < n; i++) { |
| Rational v = tag.getRational(i); |
| mByteBuffer.putInt((int) v.getNumerator()); |
| mByteBuffer.putInt((int) v.getDenominator()); |
| } |
| break; |
| case ExifTag.TYPE_UNDEFINED: |
| case ExifTag.TYPE_UNSIGNED_BYTE: |
| buf = new byte[tag.getComponentCount()]; |
| tag.getBytes(buf); |
| mByteBuffer.put(buf); |
| break; |
| case ExifTag.TYPE_UNSIGNED_SHORT: |
| for (int i = 0, n = tag.getComponentCount(); i < n; i++) { |
| mByteBuffer.putShort((short) tag.getValueAt(i)); |
| } |
| break; |
| } |
| } |
| |
| public void modifyTag(ExifTag tag) { |
| mTagToModified.addTag(tag); |
| } |
| } |