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