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