1e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor/*
2e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor * Copyright (C) 2012 The Android Open Source Project
3e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor *
4e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor * Licensed under the Apache License, Version 2.0 (the "License");
5e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor * you may not use this file except in compliance with the License.
6e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor * You may obtain a copy of the License at
7e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor *
8e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor *      http://www.apache.org/licenses/LICENSE-2.0
9e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor *
10e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor * Unless required by applicable law or agreed to in writing, software
11e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor * distributed under the License is distributed on an "AS IS" BASIS,
12e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor * See the License for the specific language governing permissions and
14e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor * limitations under the License.
15e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor */
16e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
17e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylorpackage com.android.mms.exif;
18e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
19e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylorimport android.util.Log;
20e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
21e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylorimport java.io.BufferedOutputStream;
22e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylorimport java.io.FilterOutputStream;
23e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylorimport java.io.IOException;
24e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylorimport java.io.OutputStream;
25e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylorimport java.nio.ByteBuffer;
26e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylorimport java.nio.ByteOrder;
27e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylorimport java.util.ArrayList;
28e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
29e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor/**
30e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor * This class provides a way to replace the Exif header of a JPEG image.
31e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor * <p>
32e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor * Below is an example of writing EXIF data into a file
33e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor *
34e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor * <pre>
35e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor * public static void writeExif(byte[] jpeg, ExifData exif, String path) {
36e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor *     OutputStream os = null;
37e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor *     try {
38e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor *         os = new FileOutputStream(path);
39e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor *         ExifOutputStream eos = new ExifOutputStream(os);
40e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor *         // Set the exif header
41e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor *         eos.setExifData(exif);
42e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor *         // Write the original jpeg out, the header will be add into the file.
43e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor *         eos.write(jpeg);
44e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor *     } catch (FileNotFoundException e) {
45e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor *         e.printStackTrace();
46e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor *     } catch (IOException e) {
47e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor *         e.printStackTrace();
48e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor *     } finally {
49e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor *         if (os != null) {
50e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor *             try {
51e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor *                 os.close();
52e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor *             } catch (IOException e) {
53e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor *                 e.printStackTrace();
54e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor *             }
55e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor *         }
56e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor *     }
57e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor * }
58e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor * </pre>
59e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor */
60e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylorclass ExifOutputStream extends FilterOutputStream {
61e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private static final String TAG = "ExifOutputStream";
62e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private static final boolean DEBUG = false;
63e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private static final int STREAMBUFFER_SIZE = 0x00010000; // 64Kb
64e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
65e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private static final int STATE_SOI = 0;
66e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private static final int STATE_FRAME_HEADER = 1;
67e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private static final int STATE_JPEG_DATA = 2;
68e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
69e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private static final int EXIF_HEADER = 0x45786966;
70e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private static final short TIFF_HEADER = 0x002A;
71e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private static final short TIFF_BIG_ENDIAN = 0x4d4d;
72e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private static final short TIFF_LITTLE_ENDIAN = 0x4949;
73e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private static final short TAG_SIZE = 12;
74e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private static final short TIFF_HEADER_SIZE = 8;
75e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private static final int MAX_EXIF_SIZE = 65535;
76e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
77e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private ExifData mExifData;
78e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private int mState = STATE_SOI;
79e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private int mByteToSkip;
80e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private int mByteToCopy;
81e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private final byte[] mSingleByteArray = new byte[1];
82e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private final ByteBuffer mBuffer = ByteBuffer.allocate(4);
83e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private final ExifInterface mInterface;
84e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
85e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    protected ExifOutputStream(OutputStream ou, ExifInterface iRef) {
86e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        super(new BufferedOutputStream(ou, STREAMBUFFER_SIZE));
87e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        mInterface = iRef;
88e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    }
89e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
90e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    /**
91e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * Sets the ExifData to be written into the JPEG file. Should be called
92e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * before writing image data.
93e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     */
94e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    protected void setExifData(ExifData exifData) {
95e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        mExifData = exifData;
96e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    }
97e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
98e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    /**
99e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * Gets the Exif header to be written into the JPEF file.
100e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     */
101e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    protected ExifData getExifData() {
102e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        return mExifData;
103e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    }
104e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
105e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private int requestByteToBuffer(int requestByteCount, byte[] buffer
106e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            , int offset, int length) {
107e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        int byteNeeded = requestByteCount - mBuffer.position();
108e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        int byteToRead = length > byteNeeded ? byteNeeded : length;
109e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        mBuffer.put(buffer, offset, byteToRead);
110e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        return byteToRead;
111e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    }
112e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
113e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    /**
114e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * Writes the image out. The input data should be a valid JPEG format. After
115e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * writing, it's Exif header will be replaced by the given header.
116e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     */
117e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    @Override
118e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    public void write(byte[] buffer, int offset, int length) throws IOException {
119e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        while ((mByteToSkip > 0 || mByteToCopy > 0 || mState != STATE_JPEG_DATA)
120e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                && length > 0) {
121e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            if (mByteToSkip > 0) {
122e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                int byteToProcess = length > mByteToSkip ? mByteToSkip : length;
123e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                length -= byteToProcess;
124e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                mByteToSkip -= byteToProcess;
125e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                offset += byteToProcess;
126e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            }
127e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            if (mByteToCopy > 0) {
128e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                int byteToProcess = length > mByteToCopy ? mByteToCopy : length;
129e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                out.write(buffer, offset, byteToProcess);
130e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                length -= byteToProcess;
131e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                mByteToCopy -= byteToProcess;
132e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                offset += byteToProcess;
133e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            }
134e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            if (length == 0) {
135e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                return;
136e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            }
137e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            switch (mState) {
138e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                case STATE_SOI:
139e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    int byteRead = requestByteToBuffer(2, buffer, offset, length);
140e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    offset += byteRead;
141e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    length -= byteRead;
142e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    if (mBuffer.position() < 2) {
143e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                        return;
144e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    }
145e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    mBuffer.rewind();
146e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    if (mBuffer.getShort() != JpegHeader.SOI) {
147e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                        throw new IOException("Not a valid jpeg image, cannot write exif");
148e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    }
149e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    out.write(mBuffer.array(), 0, 2);
150e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    mState = STATE_FRAME_HEADER;
151e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    mBuffer.rewind();
152e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    writeExifData();
153e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    break;
154e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                case STATE_FRAME_HEADER:
155e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    // We ignore the APP1 segment and copy all other segments
156e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    // until SOF tag.
157e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    byteRead = requestByteToBuffer(4, buffer, offset, length);
158e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    offset += byteRead;
159e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    length -= byteRead;
160e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    // Check if this image data doesn't contain SOF.
161e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    if (mBuffer.position() == 2) {
162e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                        short tag = mBuffer.getShort();
163e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                        if (tag == JpegHeader.EOI) {
164e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                            out.write(mBuffer.array(), 0, 2);
165e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                            mBuffer.rewind();
166e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                        }
167e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    }
168e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    if (mBuffer.position() < 4) {
169e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                        return;
170e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    }
171e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    mBuffer.rewind();
172e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    short marker = mBuffer.getShort();
173e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    if (marker == JpegHeader.APP1) {
174e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                        mByteToSkip = (mBuffer.getShort() & 0x0000ffff) - 2;
175e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                        mState = STATE_JPEG_DATA;
176e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    } else if (!JpegHeader.isSofMarker(marker)) {
177e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                        out.write(mBuffer.array(), 0, 4);
178e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                        mByteToCopy = (mBuffer.getShort() & 0x0000ffff) - 2;
179e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    } else {
180e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                        out.write(mBuffer.array(), 0, 4);
181e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                        mState = STATE_JPEG_DATA;
182e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    }
183e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    mBuffer.rewind();
184e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            }
185e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        }
186e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        if (length > 0) {
187e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            out.write(buffer, offset, length);
188e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        }
189e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    }
190e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
191e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    /**
192e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * Writes the one bytes out. The input data should be a valid JPEG format.
193e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * After writing, it's Exif header will be replaced by the given header.
194e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     */
195e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    @Override
196e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    public void write(int oneByte) throws IOException {
197e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        mSingleByteArray[0] = (byte) (0xff & oneByte);
198e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        write(mSingleByteArray);
199e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    }
200e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
201e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    /**
202e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     * Equivalent to calling write(buffer, 0, buffer.length).
203e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor     */
204e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    @Override
205e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    public void write(byte[] buffer) throws IOException {
206e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        write(buffer, 0, buffer.length);
207e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    }
208e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
209e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private void writeExifData() throws IOException {
210e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        if (mExifData == null) {
211e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            return;
212e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        }
213e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        if (DEBUG) {
214e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            Log.v(TAG, "Writing exif data...");
215e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        }
216e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        ArrayList<ExifTag> nullTags = stripNullValueTags(mExifData);
217e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        createRequiredIfdAndTag();
218e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        int exifSize = calculateAllOffset();
219e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        if (exifSize + 8 > MAX_EXIF_SIZE) {
220e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            throw new IOException("Exif header is too large (>64Kb)");
221e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        }
222e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        OrderedDataOutputStream dataOutputStream = new OrderedDataOutputStream(out);
223e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        dataOutputStream.setByteOrder(ByteOrder.BIG_ENDIAN);
224e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        dataOutputStream.writeShort(JpegHeader.APP1);
225e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        dataOutputStream.writeShort((short) (exifSize + 8));
226e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        dataOutputStream.writeInt(EXIF_HEADER);
227e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        dataOutputStream.writeShort((short) 0x0000);
228e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        if (mExifData.getByteOrder() == ByteOrder.BIG_ENDIAN) {
229e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            dataOutputStream.writeShort(TIFF_BIG_ENDIAN);
230e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        } else {
231e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            dataOutputStream.writeShort(TIFF_LITTLE_ENDIAN);
232e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        }
233e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        dataOutputStream.setByteOrder(mExifData.getByteOrder());
234e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        dataOutputStream.writeShort(TIFF_HEADER);
235e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        dataOutputStream.writeInt(8);
236e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        writeAllTags(dataOutputStream);
237e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        writeThumbnail(dataOutputStream);
238e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        for (ExifTag t : nullTags) {
239e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            mExifData.addTag(t);
240e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        }
241e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    }
242e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
243e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private ArrayList<ExifTag> stripNullValueTags(ExifData data) {
244e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        ArrayList<ExifTag> nullTags = new ArrayList<ExifTag>();
245e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        for(ExifTag t : data.getAllTags()) {
246e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            if (t.getValue() == null && !ExifInterface.isOffsetTag(t.getTagId())) {
247e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                data.removeTag(t.getTagId(), t.getIfd());
248e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                nullTags.add(t);
249e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            }
250e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        }
251e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        return nullTags;
252e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    }
253e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
254e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private void writeThumbnail(OrderedDataOutputStream dataOutputStream) throws IOException {
255e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        if (mExifData.hasCompressedThumbnail()) {
256e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            dataOutputStream.write(mExifData.getCompressedThumbnail());
257e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        } else if (mExifData.hasUncompressedStrip()) {
258e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            for (int i = 0; i < mExifData.getStripCount(); i++) {
259e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                dataOutputStream.write(mExifData.getStrip(i));
260e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            }
261e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        }
262e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    }
263e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
264e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private void writeAllTags(OrderedDataOutputStream dataOutputStream) throws IOException {
265e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        writeIfd(mExifData.getIfdData(IfdId.TYPE_IFD_0), dataOutputStream);
266e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        writeIfd(mExifData.getIfdData(IfdId.TYPE_IFD_EXIF), dataOutputStream);
267e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        IfdData interoperabilityIfd = mExifData.getIfdData(IfdId.TYPE_IFD_INTEROPERABILITY);
268e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        if (interoperabilityIfd != null) {
269e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            writeIfd(interoperabilityIfd, dataOutputStream);
270e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        }
271e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        IfdData gpsIfd = mExifData.getIfdData(IfdId.TYPE_IFD_GPS);
272e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        if (gpsIfd != null) {
273e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            writeIfd(gpsIfd, dataOutputStream);
274e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        }
275e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        IfdData ifd1 = mExifData.getIfdData(IfdId.TYPE_IFD_1);
276e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        if (ifd1 != null) {
277e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            writeIfd(mExifData.getIfdData(IfdId.TYPE_IFD_1), dataOutputStream);
278e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        }
279e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    }
280e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
281e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private void writeIfd(IfdData ifd, OrderedDataOutputStream dataOutputStream)
282e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            throws IOException {
283e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        ExifTag[] tags = ifd.getAllTags();
284e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        dataOutputStream.writeShort((short) tags.length);
285e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        for (ExifTag tag : tags) {
286e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            dataOutputStream.writeShort(tag.getTagId());
287e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            dataOutputStream.writeShort(tag.getDataType());
288e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            dataOutputStream.writeInt(tag.getComponentCount());
289e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            if (DEBUG) {
290e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                Log.v(TAG, "\n" + tag.toString());
291e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            }
292e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            if (tag.getDataSize() > 4) {
293e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                dataOutputStream.writeInt(tag.getOffset());
294e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            } else {
295e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                ExifOutputStream.writeTagValue(tag, dataOutputStream);
296e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                for (int i = 0, n = 4 - tag.getDataSize(); i < n; i++) {
297e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    dataOutputStream.write(0);
298e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                }
299e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            }
300e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        }
301e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        dataOutputStream.writeInt(ifd.getOffsetToNextIfd());
302e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        for (ExifTag tag : tags) {
303e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            if (tag.getDataSize() > 4) {
304e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                ExifOutputStream.writeTagValue(tag, dataOutputStream);
305e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            }
306e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        }
307e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    }
308e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
309e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private int calculateOffsetOfIfd(IfdData ifd, int offset) {
310e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        offset += 2 + ifd.getTagCount() * TAG_SIZE + 4;
311e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        ExifTag[] tags = ifd.getAllTags();
312e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        for (ExifTag tag : tags) {
313e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            if (tag.getDataSize() > 4) {
314e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                tag.setOffset(offset);
315e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                offset += tag.getDataSize();
316e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            }
317e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        }
318e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        return offset;
319e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    }
320e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
321e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private void createRequiredIfdAndTag() throws IOException {
322e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        // IFD0 is required for all file
323e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        IfdData ifd0 = mExifData.getIfdData(IfdId.TYPE_IFD_0);
324e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        if (ifd0 == null) {
325e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            ifd0 = new IfdData(IfdId.TYPE_IFD_0);
326e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            mExifData.addIfdData(ifd0);
327e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        }
328e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        ExifTag exifOffsetTag = mInterface.buildUninitializedTag(ExifInterface.TAG_EXIF_IFD);
329e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        if (exifOffsetTag == null) {
330e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            throw new IOException("No definition for crucial exif tag: "
331e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    + ExifInterface.TAG_EXIF_IFD);
332e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        }
333e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        ifd0.setTag(exifOffsetTag);
334e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
335e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        // Exif IFD is required for all files.
336e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        IfdData exifIfd = mExifData.getIfdData(IfdId.TYPE_IFD_EXIF);
337e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        if (exifIfd == null) {
338e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            exifIfd = new IfdData(IfdId.TYPE_IFD_EXIF);
339e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            mExifData.addIfdData(exifIfd);
340e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        }
341e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
342e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        // GPS IFD
343e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        IfdData gpsIfd = mExifData.getIfdData(IfdId.TYPE_IFD_GPS);
344e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        if (gpsIfd != null) {
345e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            ExifTag gpsOffsetTag = mInterface.buildUninitializedTag(ExifInterface.TAG_GPS_IFD);
346e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            if (gpsOffsetTag == null) {
347e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                throw new IOException("No definition for crucial exif tag: "
348e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                        + ExifInterface.TAG_GPS_IFD);
349e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            }
350e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            ifd0.setTag(gpsOffsetTag);
351e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        }
352e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
353e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        // Interoperability IFD
354e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        IfdData interIfd = mExifData.getIfdData(IfdId.TYPE_IFD_INTEROPERABILITY);
355e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        if (interIfd != null) {
356e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            ExifTag interOffsetTag = mInterface
357e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    .buildUninitializedTag(ExifInterface.TAG_INTEROPERABILITY_IFD);
358e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            if (interOffsetTag == null) {
359e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                throw new IOException("No definition for crucial exif tag: "
360e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                        + ExifInterface.TAG_INTEROPERABILITY_IFD);
361e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            }
362e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            exifIfd.setTag(interOffsetTag);
363e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        }
364e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
365e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        IfdData ifd1 = mExifData.getIfdData(IfdId.TYPE_IFD_1);
366e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
367e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        // thumbnail
368e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        if (mExifData.hasCompressedThumbnail()) {
369e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
370e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            if (ifd1 == null) {
371e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                ifd1 = new IfdData(IfdId.TYPE_IFD_1);
372e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                mExifData.addIfdData(ifd1);
373e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            }
374e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
375e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            ExifTag offsetTag = mInterface
376e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    .buildUninitializedTag(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT);
377e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            if (offsetTag == null) {
378e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                throw new IOException("No definition for crucial exif tag: "
379e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                        + ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT);
380e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            }
381e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
382e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            ifd1.setTag(offsetTag);
383e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            ExifTag lengthTag = mInterface
384e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    .buildUninitializedTag(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH);
385e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            if (lengthTag == null) {
386e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                throw new IOException("No definition for crucial exif tag: "
387e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                        + ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH);
388e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            }
389e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
390e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            lengthTag.setValue(mExifData.getCompressedThumbnail().length);
391e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            ifd1.setTag(lengthTag);
392e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
393e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            // Get rid of tags for uncompressed if they exist.
394e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            ifd1.removeTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_STRIP_OFFSETS));
395e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            ifd1.removeTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_STRIP_BYTE_COUNTS));
396e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        } else if (mExifData.hasUncompressedStrip()) {
397e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            if (ifd1 == null) {
398e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                ifd1 = new IfdData(IfdId.TYPE_IFD_1);
399e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                mExifData.addIfdData(ifd1);
400e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            }
401e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            int stripCount = mExifData.getStripCount();
402e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            ExifTag offsetTag = mInterface.buildUninitializedTag(ExifInterface.TAG_STRIP_OFFSETS);
403e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            if (offsetTag == null) {
404e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                throw new IOException("No definition for crucial exif tag: "
405e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                        + ExifInterface.TAG_STRIP_OFFSETS);
406e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            }
407e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            ExifTag lengthTag = mInterface
408e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    .buildUninitializedTag(ExifInterface.TAG_STRIP_BYTE_COUNTS);
409e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            if (lengthTag == null) {
410e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                throw new IOException("No definition for crucial exif tag: "
411e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                        + ExifInterface.TAG_STRIP_BYTE_COUNTS);
412e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            }
413e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            long[] lengths = new long[stripCount];
414e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            for (int i = 0; i < mExifData.getStripCount(); i++) {
415e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                lengths[i] = mExifData.getStrip(i).length;
416e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            }
417e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            lengthTag.setValue(lengths);
418e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            ifd1.setTag(offsetTag);
419e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            ifd1.setTag(lengthTag);
420e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            // Get rid of tags for compressed if they exist.
421e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            ifd1.removeTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT));
422e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            ifd1.removeTag(ExifInterface
423e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    .getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH));
424e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        } else if (ifd1 != null) {
425e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            // Get rid of offset and length tags if there is no thumbnail.
426e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            ifd1.removeTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_STRIP_OFFSETS));
427e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            ifd1.removeTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_STRIP_BYTE_COUNTS));
428e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            ifd1.removeTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT));
429e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            ifd1.removeTag(ExifInterface
430e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    .getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH));
431e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        }
432e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    }
433e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
434e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    private int calculateAllOffset() {
435e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        int offset = TIFF_HEADER_SIZE;
436e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        IfdData ifd0 = mExifData.getIfdData(IfdId.TYPE_IFD_0);
437e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        offset = calculateOffsetOfIfd(ifd0, offset);
438e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        ifd0.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_EXIF_IFD)).setValue(offset);
439e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
440e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        IfdData exifIfd = mExifData.getIfdData(IfdId.TYPE_IFD_EXIF);
441e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        offset = calculateOffsetOfIfd(exifIfd, offset);
442e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
443e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        IfdData interIfd = mExifData.getIfdData(IfdId.TYPE_IFD_INTEROPERABILITY);
444e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        if (interIfd != null) {
445e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            exifIfd.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_INTEROPERABILITY_IFD))
446e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    .setValue(offset);
447e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            offset = calculateOffsetOfIfd(interIfd, offset);
448e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        }
449e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
450e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        IfdData gpsIfd = mExifData.getIfdData(IfdId.TYPE_IFD_GPS);
451e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        if (gpsIfd != null) {
452e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            ifd0.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_GPS_IFD)).setValue(offset);
453e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            offset = calculateOffsetOfIfd(gpsIfd, offset);
454e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        }
455e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
456e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        IfdData ifd1 = mExifData.getIfdData(IfdId.TYPE_IFD_1);
457e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        if (ifd1 != null) {
458e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            ifd0.setOffsetToNextIfd(offset);
459e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            offset = calculateOffsetOfIfd(ifd1, offset);
460e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        }
461e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
462e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        // thumbnail
463e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        if (mExifData.hasCompressedThumbnail()) {
464e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            ifd1.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT))
465e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    .setValue(offset);
466e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            offset += mExifData.getCompressedThumbnail().length;
467e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        } else if (mExifData.hasUncompressedStrip()) {
468e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            int stripCount = mExifData.getStripCount();
469e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            long[] offsets = new long[stripCount];
470e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            for (int i = 0; i < mExifData.getStripCount(); i++) {
471e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                offsets[i] = offset;
472e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                offset += mExifData.getStrip(i).length;
473e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            }
474e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            ifd1.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_STRIP_OFFSETS)).setValue(
475e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    offsets);
476e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        }
477e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        return offset;
478e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    }
479e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor
480e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    static void writeTagValue(ExifTag tag, OrderedDataOutputStream dataOutputStream)
481e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            throws IOException {
482e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        switch (tag.getDataType()) {
483e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            case ExifTag.TYPE_ASCII:
484e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                byte buf[] = tag.getStringByte();
485e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                if (buf.length == tag.getComponentCount()) {
486e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    buf[buf.length - 1] = 0;
487e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    dataOutputStream.write(buf);
488e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                } else {
489e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    dataOutputStream.write(buf);
490e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    dataOutputStream.write(0);
491e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                }
492e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                break;
493e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            case ExifTag.TYPE_LONG:
494e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            case ExifTag.TYPE_UNSIGNED_LONG:
495e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                for (int i = 0, n = tag.getComponentCount(); i < n; i++) {
496e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    dataOutputStream.writeInt((int) tag.getValueAt(i));
497e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                }
498e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                break;
499e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            case ExifTag.TYPE_RATIONAL:
500e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            case ExifTag.TYPE_UNSIGNED_RATIONAL:
501e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                for (int i = 0, n = tag.getComponentCount(); i < n; i++) {
502e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    dataOutputStream.writeRational(tag.getRational(i));
503e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                }
504e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                break;
505e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            case ExifTag.TYPE_UNDEFINED:
506e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            case ExifTag.TYPE_UNSIGNED_BYTE:
507e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                buf = new byte[tag.getComponentCount()];
508e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                tag.getBytes(buf);
509e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                dataOutputStream.write(buf);
510e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                break;
511e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor            case ExifTag.TYPE_UNSIGNED_SHORT:
512e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                for (int i = 0, n = tag.getComponentCount(); i < n; i++) {
513e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                    dataOutputStream.writeShort((short) tag.getValueAt(i));
514e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                }
515e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor                break;
516e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor        }
517e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor    }
518e8a0d786fba1960818cb3da938cdbf2b2b11b340Tom Taylor}
519