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