ExifModifier.java revision 104c45677660586026a7e74ef8c47d396403d50e
1104c45677660586026a7e74ef8c47d396403d50eMichael Jurka/*
2104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * Copyright (C) 2012 The Android Open Source Project
3104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *
4104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * Licensed under the Apache License, Version 2.0 (the "License");
5104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * you may not use this file except in compliance with the License.
6104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * You may obtain a copy of the License at
7104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *
8104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *      http://www.apache.org/licenses/LICENSE-2.0
9104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *
10104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * Unless required by applicable law or agreed to in writing, software
11104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * distributed under the License is distributed on an "AS IS" BASIS,
12104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * See the License for the specific language governing permissions and
14104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * limitations under the License.
15104c45677660586026a7e74ef8c47d396403d50eMichael Jurka */
16104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
17104c45677660586026a7e74ef8c47d396403d50eMichael Jurkapackage com.android.gallery3d.exif;
18104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
19104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport android.util.Log;
20104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
21104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport java.io.Closeable;
22104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport java.io.IOException;
23104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport java.io.InputStream;
24104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport java.nio.ByteBuffer;
25104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport java.nio.ByteOrder;
26104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport java.util.ArrayList;
27104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport java.util.List;
28104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
29104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaclass ExifModifier {
30104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public static final String TAG = "ExifModifier";
31104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public static final boolean DEBUG = false;
32104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private final ByteBuffer mByteBuffer;
33104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private final ExifData mTagToModified;
34104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private final List<TagOffset> mTagOffsets = new ArrayList<TagOffset>();
35104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private final ExifInterface mInterface;
36104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private int mOffsetBase;
37104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
38104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static class TagOffset {
39104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        final int mOffset;
40104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        final ExifTag mTag;
41104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
42104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        TagOffset(ExifTag tag, int offset) {
43104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mTag = tag;
44104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mOffset = offset;
45104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
46104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
47104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
48104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected ExifModifier(ByteBuffer byteBuffer, ExifInterface iRef) throws IOException,
49104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            ExifInvalidFormatException {
50104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mByteBuffer = byteBuffer;
51104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mOffsetBase = byteBuffer.position();
52104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mInterface = iRef;
53104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        InputStream is = null;
54104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        try {
55104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            is = new ByteBufferInputStream(byteBuffer);
56104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            // Do not require any IFD;
57104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            ExifParser parser = ExifParser.parse(is, mInterface);
58104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mTagToModified = new ExifData(parser.getByteOrder());
59104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mOffsetBase += parser.getTiffStartPosition();
60104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mByteBuffer.position(0);
61104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        } finally {
62104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            ExifInterface.closeSilently(is);
63104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
64104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
65104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
66104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected ByteOrder getByteOrder() {
67104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return mTagToModified.getByteOrder();
68104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
69104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
70104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected boolean commit() throws IOException, ExifInvalidFormatException {
71104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        InputStream is = null;
72104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        try {
73104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            is = new ByteBufferInputStream(mByteBuffer);
74104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            int flag = 0;
75104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            IfdData[] ifdDatas = new IfdData[] {
76104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    mTagToModified.getIfdData(IfdId.TYPE_IFD_0),
77104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    mTagToModified.getIfdData(IfdId.TYPE_IFD_1),
78104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    mTagToModified.getIfdData(IfdId.TYPE_IFD_EXIF),
79104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    mTagToModified.getIfdData(IfdId.TYPE_IFD_INTEROPERABILITY),
80104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    mTagToModified.getIfdData(IfdId.TYPE_IFD_GPS)
81104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            };
82104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
83104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (ifdDatas[IfdId.TYPE_IFD_0] != null) {
84104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                flag |= ExifParser.OPTION_IFD_0;
85104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
86104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (ifdDatas[IfdId.TYPE_IFD_1] != null) {
87104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                flag |= ExifParser.OPTION_IFD_1;
88104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
89104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (ifdDatas[IfdId.TYPE_IFD_EXIF] != null) {
90104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                flag |= ExifParser.OPTION_IFD_EXIF;
91104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
92104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (ifdDatas[IfdId.TYPE_IFD_GPS] != null) {
93104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                flag |= ExifParser.OPTION_IFD_GPS;
94104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
95104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (ifdDatas[IfdId.TYPE_IFD_INTEROPERABILITY] != null) {
96104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                flag |= ExifParser.OPTION_IFD_INTEROPERABILITY;
97104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
98104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
99104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            ExifParser parser = ExifParser.parse(is, flag, mInterface);
100104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            int event = parser.next();
101104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            IfdData currIfd = null;
102104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            while (event != ExifParser.EVENT_END) {
103104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                switch (event) {
104104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    case ExifParser.EVENT_START_OF_IFD:
105104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        currIfd = ifdDatas[parser.getCurrentIfd()];
106104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        if (currIfd == null) {
107104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                            parser.skipRemainingTagsInCurrentIfd();
108104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        }
109104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        break;
110104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    case ExifParser.EVENT_NEW_TAG:
111104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        ExifTag oldTag = parser.getTag();
112104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        ExifTag newTag = currIfd.getTag(oldTag.getTagId());
113104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        if (newTag != null) {
114104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                            if (newTag.getComponentCount() != oldTag.getComponentCount()
115104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                                    || newTag.getDataType() != oldTag.getDataType()) {
116104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                                return false;
117104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                            } else {
118104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                                mTagOffsets.add(new TagOffset(newTag, oldTag.getOffset()));
119104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                                currIfd.removeTag(oldTag.getTagId());
120104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                                if (currIfd.getTagCount() == 0) {
121104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                                    parser.skipRemainingTagsInCurrentIfd();
122104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                                }
123104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                            }
124104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        }
125104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        break;
126104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                }
127104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                event = parser.next();
128104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
129104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            for (IfdData ifd : ifdDatas) {
130104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                if (ifd != null && ifd.getTagCount() > 0) {
131104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    return false;
132104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                }
133104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
134104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            modify();
135104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        } finally {
136104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            ExifInterface.closeSilently(is);
137104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
138104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return true;
139104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
140104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
141104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private void modify() {
142104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mByteBuffer.order(getByteOrder());
143104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        for (TagOffset tagOffset : mTagOffsets) {
144104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            writeTagValue(tagOffset.mTag, tagOffset.mOffset);
145104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
146104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
147104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
148104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private void writeTagValue(ExifTag tag, int offset) {
149104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (DEBUG) {
150104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            Log.v(TAG, "modifying tag to: \n" + tag.toString());
151104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            Log.v(TAG, "at offset: " + offset);
152104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
153104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mByteBuffer.position(offset + mOffsetBase);
154104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        switch (tag.getDataType()) {
155104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            case ExifTag.TYPE_ASCII:
156104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                byte buf[] = tag.getStringByte();
157104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                if (buf.length == tag.getComponentCount()) {
158104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    buf[buf.length - 1] = 0;
159104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    mByteBuffer.put(buf);
160104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                } else {
161104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    mByteBuffer.put(buf);
162104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    mByteBuffer.put((byte) 0);
163104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                }
164104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                break;
165104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            case ExifTag.TYPE_LONG:
166104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            case ExifTag.TYPE_UNSIGNED_LONG:
167104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                for (int i = 0, n = tag.getComponentCount(); i < n; i++) {
168104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    mByteBuffer.putInt((int) tag.getValueAt(i));
169104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                }
170104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                break;
171104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            case ExifTag.TYPE_RATIONAL:
172104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            case ExifTag.TYPE_UNSIGNED_RATIONAL:
173104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                for (int i = 0, n = tag.getComponentCount(); i < n; i++) {
174104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    Rational v = tag.getRational(i);
175104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    mByteBuffer.putInt((int) v.getNumerator());
176104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    mByteBuffer.putInt((int) v.getDenominator());
177104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                }
178104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                break;
179104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            case ExifTag.TYPE_UNDEFINED:
180104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            case ExifTag.TYPE_UNSIGNED_BYTE:
181104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                buf = new byte[tag.getComponentCount()];
182104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                tag.getBytes(buf);
183104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                mByteBuffer.put(buf);
184104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                break;
185104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            case ExifTag.TYPE_UNSIGNED_SHORT:
186104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                for (int i = 0, n = tag.getComponentCount(); i < n; i++) {
187104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    mByteBuffer.putShort((short) tag.getValueAt(i));
188104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                }
189104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                break;
190104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
191104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
192104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
193104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public void modifyTag(ExifTag tag) {
194104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mTagToModified.addTag(tag);
195104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
196104c45677660586026a7e74ef8c47d396403d50eMichael Jurka}
197