blob: 0531cbad99edb25656aae5e7c36f6978e6b54229 [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
Michael Jurkae8d1bf72013-09-09 15:58:54 +020021import java.io.IOException;
22import java.io.InputStream;
23import java.nio.ByteBuffer;
24import java.nio.ByteOrder;
25import java.util.ArrayList;
26import java.util.List;
27
28class ExifModifier {
29 public static final String TAG = "ExifModifier";
30 public static final boolean DEBUG = false;
31 private final ByteBuffer mByteBuffer;
32 private final ExifData mTagToModified;
33 private final List<TagOffset> mTagOffsets = new ArrayList<TagOffset>();
34 private final ExifInterface mInterface;
35 private int mOffsetBase;
36
37 private static class TagOffset {
38 final int mOffset;
39 final ExifTag mTag;
40
41 TagOffset(ExifTag tag, int offset) {
42 mTag = tag;
43 mOffset = offset;
44 }
45 }
46
47 protected ExifModifier(ByteBuffer byteBuffer, ExifInterface iRef) throws IOException,
48 ExifInvalidFormatException {
49 mByteBuffer = byteBuffer;
50 mOffsetBase = byteBuffer.position();
51 mInterface = iRef;
52 InputStream is = null;
53 try {
54 is = new ByteBufferInputStream(byteBuffer);
55 // Do not require any IFD;
56 ExifParser parser = ExifParser.parse(is, mInterface);
57 mTagToModified = new ExifData(parser.getByteOrder());
58 mOffsetBase += parser.getTiffStartPosition();
59 mByteBuffer.position(0);
60 } finally {
61 ExifInterface.closeSilently(is);
62 }
63 }
64
65 protected ByteOrder getByteOrder() {
66 return mTagToModified.getByteOrder();
67 }
68
69 protected boolean commit() throws IOException, ExifInvalidFormatException {
70 InputStream is = null;
71 try {
72 is = new ByteBufferInputStream(mByteBuffer);
73 int flag = 0;
74 IfdData[] ifdDatas = new IfdData[] {
75 mTagToModified.getIfdData(IfdId.TYPE_IFD_0),
76 mTagToModified.getIfdData(IfdId.TYPE_IFD_1),
77 mTagToModified.getIfdData(IfdId.TYPE_IFD_EXIF),
78 mTagToModified.getIfdData(IfdId.TYPE_IFD_INTEROPERABILITY),
79 mTagToModified.getIfdData(IfdId.TYPE_IFD_GPS)
80 };
81
82 if (ifdDatas[IfdId.TYPE_IFD_0] != null) {
83 flag |= ExifParser.OPTION_IFD_0;
84 }
85 if (ifdDatas[IfdId.TYPE_IFD_1] != null) {
86 flag |= ExifParser.OPTION_IFD_1;
87 }
88 if (ifdDatas[IfdId.TYPE_IFD_EXIF] != null) {
89 flag |= ExifParser.OPTION_IFD_EXIF;
90 }
91 if (ifdDatas[IfdId.TYPE_IFD_GPS] != null) {
92 flag |= ExifParser.OPTION_IFD_GPS;
93 }
94 if (ifdDatas[IfdId.TYPE_IFD_INTEROPERABILITY] != null) {
95 flag |= ExifParser.OPTION_IFD_INTEROPERABILITY;
96 }
97
98 ExifParser parser = ExifParser.parse(is, flag, mInterface);
99 int event = parser.next();
100 IfdData currIfd = null;
101 while (event != ExifParser.EVENT_END) {
102 switch (event) {
103 case ExifParser.EVENT_START_OF_IFD:
104 currIfd = ifdDatas[parser.getCurrentIfd()];
105 if (currIfd == null) {
106 parser.skipRemainingTagsInCurrentIfd();
107 }
108 break;
109 case ExifParser.EVENT_NEW_TAG:
110 ExifTag oldTag = parser.getTag();
111 ExifTag newTag = currIfd.getTag(oldTag.getTagId());
112 if (newTag != null) {
113 if (newTag.getComponentCount() != oldTag.getComponentCount()
114 || newTag.getDataType() != oldTag.getDataType()) {
115 return false;
116 } else {
117 mTagOffsets.add(new TagOffset(newTag, oldTag.getOffset()));
118 currIfd.removeTag(oldTag.getTagId());
119 if (currIfd.getTagCount() == 0) {
120 parser.skipRemainingTagsInCurrentIfd();
121 }
122 }
123 }
124 break;
125 }
126 event = parser.next();
127 }
128 for (IfdData ifd : ifdDatas) {
129 if (ifd != null && ifd.getTagCount() > 0) {
130 return false;
131 }
132 }
133 modify();
134 } finally {
135 ExifInterface.closeSilently(is);
136 }
137 return true;
138 }
139
140 private void modify() {
141 mByteBuffer.order(getByteOrder());
142 for (TagOffset tagOffset : mTagOffsets) {
143 writeTagValue(tagOffset.mTag, tagOffset.mOffset);
144 }
145 }
146
147 private void writeTagValue(ExifTag tag, int offset) {
148 if (DEBUG) {
149 Log.v(TAG, "modifying tag to: \n" + tag.toString());
150 Log.v(TAG, "at offset: " + offset);
151 }
152 mByteBuffer.position(offset + mOffsetBase);
153 switch (tag.getDataType()) {
154 case ExifTag.TYPE_ASCII:
155 byte buf[] = tag.getStringByte();
156 if (buf.length == tag.getComponentCount()) {
157 buf[buf.length - 1] = 0;
158 mByteBuffer.put(buf);
159 } else {
160 mByteBuffer.put(buf);
161 mByteBuffer.put((byte) 0);
162 }
163 break;
164 case ExifTag.TYPE_LONG:
165 case ExifTag.TYPE_UNSIGNED_LONG:
166 for (int i = 0, n = tag.getComponentCount(); i < n; i++) {
167 mByteBuffer.putInt((int) tag.getValueAt(i));
168 }
169 break;
170 case ExifTag.TYPE_RATIONAL:
171 case ExifTag.TYPE_UNSIGNED_RATIONAL:
172 for (int i = 0, n = tag.getComponentCount(); i < n; i++) {
173 Rational v = tag.getRational(i);
174 mByteBuffer.putInt((int) v.getNumerator());
175 mByteBuffer.putInt((int) v.getDenominator());
176 }
177 break;
178 case ExifTag.TYPE_UNDEFINED:
179 case ExifTag.TYPE_UNSIGNED_BYTE:
180 buf = new byte[tag.getComponentCount()];
181 tag.getBytes(buf);
182 mByteBuffer.put(buf);
183 break;
184 case ExifTag.TYPE_UNSIGNED_SHORT:
185 for (int i = 0, n = tag.getComponentCount(); i < n; i++) {
186 mByteBuffer.putShort((short) tag.getValueAt(i));
187 }
188 break;
189 }
190 }
191
192 public void modifyTag(ExifTag tag) {
193 mTagToModified.addTag(tag);
194 }
195}