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