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.IOException;
22104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport java.io.InputStream;
23104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport java.nio.ByteBuffer;
24104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport java.nio.ByteOrder;
25104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport java.util.ArrayList;
26104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport java.util.List;
27104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
28104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaclass ExifModifier {
29104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public static final String TAG = "ExifModifier";
30104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public static final boolean DEBUG = false;
31104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private final ByteBuffer mByteBuffer;
32104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private final ExifData mTagToModified;
33104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private final List<TagOffset> mTagOffsets = new ArrayList<TagOffset>();
34104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private final ExifInterface mInterface;
35104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private int mOffsetBase;
36104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
37104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static class TagOffset {
38104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        final int mOffset;
39104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        final ExifTag mTag;
40104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
41104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        TagOffset(ExifTag tag, int offset) {
42104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mTag = tag;
43104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mOffset = offset;
44104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
45104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
46104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
47104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected ExifModifier(ByteBuffer byteBuffer, ExifInterface iRef) throws IOException,
48104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            ExifInvalidFormatException {
49104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mByteBuffer = byteBuffer;
50104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mOffsetBase = byteBuffer.position();
51104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mInterface = iRef;
52104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        InputStream is = null;
53104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        try {
54104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            is = new ByteBufferInputStream(byteBuffer);
55104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            // Do not require any IFD;
56104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            ExifParser parser = ExifParser.parse(is, mInterface);
57104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mTagToModified = new ExifData(parser.getByteOrder());
58104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mOffsetBase += parser.getTiffStartPosition();
59104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mByteBuffer.position(0);
60104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        } finally {
61104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            ExifInterface.closeSilently(is);
62104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
63104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
64104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
65104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected ByteOrder getByteOrder() {
66104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return mTagToModified.getByteOrder();
67104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
68104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
69104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected boolean commit() throws IOException, ExifInvalidFormatException {
70104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        InputStream is = null;
71104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        try {
72104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            is = new ByteBufferInputStream(mByteBuffer);
73104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            int flag = 0;
74104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            IfdData[] ifdDatas = new IfdData[] {
75104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    mTagToModified.getIfdData(IfdId.TYPE_IFD_0),
76104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    mTagToModified.getIfdData(IfdId.TYPE_IFD_1),
77104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    mTagToModified.getIfdData(IfdId.TYPE_IFD_EXIF),
78104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    mTagToModified.getIfdData(IfdId.TYPE_IFD_INTEROPERABILITY),
79104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    mTagToModified.getIfdData(IfdId.TYPE_IFD_GPS)
80104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            };
81104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
82104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (ifdDatas[IfdId.TYPE_IFD_0] != null) {
83104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                flag |= ExifParser.OPTION_IFD_0;
84104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
85104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (ifdDatas[IfdId.TYPE_IFD_1] != null) {
86104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                flag |= ExifParser.OPTION_IFD_1;
87104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
88104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (ifdDatas[IfdId.TYPE_IFD_EXIF] != null) {
89104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                flag |= ExifParser.OPTION_IFD_EXIF;
90104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
91104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (ifdDatas[IfdId.TYPE_IFD_GPS] != null) {
92104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                flag |= ExifParser.OPTION_IFD_GPS;
93104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
94104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (ifdDatas[IfdId.TYPE_IFD_INTEROPERABILITY] != null) {
95104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                flag |= ExifParser.OPTION_IFD_INTEROPERABILITY;
96104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
97104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
98104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            ExifParser parser = ExifParser.parse(is, flag, mInterface);
99104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            int event = parser.next();
100104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            IfdData currIfd = null;
101104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            while (event != ExifParser.EVENT_END) {
102104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                switch (event) {
103104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    case ExifParser.EVENT_START_OF_IFD:
104104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        currIfd = ifdDatas[parser.getCurrentIfd()];
105104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        if (currIfd == null) {
106104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                            parser.skipRemainingTagsInCurrentIfd();
107104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        }
108104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        break;
109104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    case ExifParser.EVENT_NEW_TAG:
110104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        ExifTag oldTag = parser.getTag();
111104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        ExifTag newTag = currIfd.getTag(oldTag.getTagId());
112104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        if (newTag != null) {
113104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                            if (newTag.getComponentCount() != oldTag.getComponentCount()
114104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                                    || newTag.getDataType() != oldTag.getDataType()) {
115104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                                return false;
116104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                            } else {
117104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                                mTagOffsets.add(new TagOffset(newTag, oldTag.getOffset()));
118104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                                currIfd.removeTag(oldTag.getTagId());
119104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                                if (currIfd.getTagCount() == 0) {
120104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                                    parser.skipRemainingTagsInCurrentIfd();
121104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                                }
122104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                            }
123104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        }
124104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        break;
125104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                }
126104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                event = parser.next();
127104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
128104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            for (IfdData ifd : ifdDatas) {
129104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                if (ifd != null && ifd.getTagCount() > 0) {
130104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    return false;
131104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                }
132104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
133104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            modify();
134104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        } finally {
135104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            ExifInterface.closeSilently(is);
136104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
137104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return true;
138104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
139104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
140104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private void modify() {
141104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mByteBuffer.order(getByteOrder());
142104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        for (TagOffset tagOffset : mTagOffsets) {
143104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            writeTagValue(tagOffset.mTag, tagOffset.mOffset);
144104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
145104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
146104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
147104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private void writeTagValue(ExifTag tag, int offset) {
148104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (DEBUG) {
149104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            Log.v(TAG, "modifying tag to: \n" + tag.toString());
150104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            Log.v(TAG, "at offset: " + offset);
151104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
152104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mByteBuffer.position(offset + mOffsetBase);
153104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        switch (tag.getDataType()) {
154104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            case ExifTag.TYPE_ASCII:
155104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                byte buf[] = tag.getStringByte();
156104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                if (buf.length == tag.getComponentCount()) {
157104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    buf[buf.length - 1] = 0;
158104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    mByteBuffer.put(buf);
159104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                } else {
160104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    mByteBuffer.put(buf);
161104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    mByteBuffer.put((byte) 0);
162104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                }
163104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                break;
164104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            case ExifTag.TYPE_LONG:
165104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            case ExifTag.TYPE_UNSIGNED_LONG:
166104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                for (int i = 0, n = tag.getComponentCount(); i < n; i++) {
167104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    mByteBuffer.putInt((int) tag.getValueAt(i));
168104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                }
169104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                break;
170104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            case ExifTag.TYPE_RATIONAL:
171104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            case ExifTag.TYPE_UNSIGNED_RATIONAL:
172104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                for (int i = 0, n = tag.getComponentCount(); i < n; i++) {
173104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    Rational v = tag.getRational(i);
174104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    mByteBuffer.putInt((int) v.getNumerator());
175104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    mByteBuffer.putInt((int) v.getDenominator());
176104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                }
177104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                break;
178104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            case ExifTag.TYPE_UNDEFINED:
179104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            case ExifTag.TYPE_UNSIGNED_BYTE:
180104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                buf = new byte[tag.getComponentCount()];
181104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                tag.getBytes(buf);
182104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                mByteBuffer.put(buf);
183104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                break;
184104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            case ExifTag.TYPE_UNSIGNED_SHORT:
185104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                for (int i = 0, n = tag.getComponentCount(); i < n; i++) {
186104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    mByteBuffer.putShort((short) tag.getValueAt(i));
187104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                }
188104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                break;
189104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
190104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
191104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
192104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public void modifyTag(ExifTag tag) {
193104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mTagToModified.addTag(tag);
194104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
195104c45677660586026a7e74ef8c47d396403d50eMichael Jurka}
196