/*
* 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.gallery3d.exif;
import android.util.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 String 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);
}
}