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.BufferedOutputStream;
22104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport java.io.FilterOutputStream;
23104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport java.io.IOException;
24104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport java.io.OutputStream;
25104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport java.nio.ByteBuffer;
26104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport java.nio.ByteOrder;
27104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport java.util.ArrayList;
28104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
29104c45677660586026a7e74ef8c47d396403d50eMichael Jurka/**
30104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * This class provides a way to replace the Exif header of a JPEG image.
31104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * <p>
32104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * Below is an example of writing EXIF data into a file
33104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *
34104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * <pre>
35104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * public static void writeExif(byte[] jpeg, ExifData exif, String path) {
36104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *     OutputStream os = null;
37104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *     try {
38104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *         os = new FileOutputStream(path);
39104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *         ExifOutputStream eos = new ExifOutputStream(os);
40104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *         // Set the exif header
41104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *         eos.setExifData(exif);
42104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *         // Write the original jpeg out, the header will be add into the file.
43104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *         eos.write(jpeg);
44104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *     } catch (FileNotFoundException e) {
45104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *         e.printStackTrace();
46104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *     } catch (IOException e) {
47104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *         e.printStackTrace();
48104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *     } finally {
49104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *         if (os != null) {
50104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *             try {
51104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *                 os.close();
52104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *             } catch (IOException e) {
53104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *                 e.printStackTrace();
54104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *             }
55104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *         }
56104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *     }
57104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * }
58104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * </pre>
59104c45677660586026a7e74ef8c47d396403d50eMichael Jurka */
60104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaclass ExifOutputStream extends FilterOutputStream {
61104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final String TAG = "ExifOutputStream";
62104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final boolean DEBUG = false;
63104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final int STREAMBUFFER_SIZE = 0x00010000; // 64Kb
64104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
65104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final int STATE_SOI = 0;
66104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final int STATE_FRAME_HEADER = 1;
67104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final int STATE_JPEG_DATA = 2;
68104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
69104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final int EXIF_HEADER = 0x45786966;
70104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final short TIFF_HEADER = 0x002A;
71104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final short TIFF_BIG_ENDIAN = 0x4d4d;
72104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final short TIFF_LITTLE_ENDIAN = 0x4949;
73104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final short TAG_SIZE = 12;
74104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final short TIFF_HEADER_SIZE = 8;
75104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final int MAX_EXIF_SIZE = 65535;
76104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
77104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private ExifData mExifData;
78104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private int mState = STATE_SOI;
79104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private int mByteToSkip;
80104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private int mByteToCopy;
81104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private byte[] mSingleByteArray = new byte[1];
82104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private ByteBuffer mBuffer = ByteBuffer.allocate(4);
83104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private final ExifInterface mInterface;
84104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
85104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected ExifOutputStream(OutputStream ou, ExifInterface iRef) {
86104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        super(new BufferedOutputStream(ou, STREAMBUFFER_SIZE));
87104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mInterface = iRef;
88104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
89104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
90104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    /**
91104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * Sets the ExifData to be written into the JPEG file. Should be called
92104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * before writing image data.
93104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     */
94104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected void setExifData(ExifData exifData) {
95104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mExifData = exifData;
96104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
97104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
98104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    /**
99104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * Gets the Exif header to be written into the JPEF file.
100104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     */
101104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected ExifData getExifData() {
102104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return mExifData;
103104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
104104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
105104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private int requestByteToBuffer(int requestByteCount, byte[] buffer
106104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            , int offset, int length) {
107104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int byteNeeded = requestByteCount - mBuffer.position();
108104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int byteToRead = length > byteNeeded ? byteNeeded : length;
109104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mBuffer.put(buffer, offset, byteToRead);
110104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return byteToRead;
111104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
112104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
113104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    /**
114104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * Writes the image out. The input data should be a valid JPEG format. After
115104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * writing, it's Exif header will be replaced by the given header.
116104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     */
117104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    @Override
118104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public void write(byte[] buffer, int offset, int length) throws IOException {
119104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        while ((mByteToSkip > 0 || mByteToCopy > 0 || mState != STATE_JPEG_DATA)
120104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                && length > 0) {
121104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (mByteToSkip > 0) {
122104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                int byteToProcess = length > mByteToSkip ? mByteToSkip : length;
123104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                length -= byteToProcess;
124104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                mByteToSkip -= byteToProcess;
125104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                offset += byteToProcess;
126104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
127104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (mByteToCopy > 0) {
128104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                int byteToProcess = length > mByteToCopy ? mByteToCopy : length;
129104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                out.write(buffer, offset, byteToProcess);
130104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                length -= byteToProcess;
131104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                mByteToCopy -= byteToProcess;
132104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                offset += byteToProcess;
133104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
134104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (length == 0) {
135104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                return;
136104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
137104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            switch (mState) {
138104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                case STATE_SOI:
139104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    int byteRead = requestByteToBuffer(2, buffer, offset, length);
140104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    offset += byteRead;
141104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    length -= byteRead;
142104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    if (mBuffer.position() < 2) {
143104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        return;
144104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    }
145104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    mBuffer.rewind();
146104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    if (mBuffer.getShort() != JpegHeader.SOI) {
147104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        throw new IOException("Not a valid jpeg image, cannot write exif");
148104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    }
149104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    out.write(mBuffer.array(), 0, 2);
150104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    mState = STATE_FRAME_HEADER;
151104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    mBuffer.rewind();
152104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    writeExifData();
153104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    break;
154104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                case STATE_FRAME_HEADER:
155104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    // We ignore the APP1 segment and copy all other segments
156104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    // until SOF tag.
157104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    byteRead = requestByteToBuffer(4, buffer, offset, length);
158104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    offset += byteRead;
159104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    length -= byteRead;
160104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    // Check if this image data doesn't contain SOF.
161104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    if (mBuffer.position() == 2) {
162104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        short tag = mBuffer.getShort();
163104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        if (tag == JpegHeader.EOI) {
164104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                            out.write(mBuffer.array(), 0, 2);
165104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                            mBuffer.rewind();
166104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        }
167104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    }
168104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    if (mBuffer.position() < 4) {
169104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        return;
170104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    }
171104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    mBuffer.rewind();
172104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    short marker = mBuffer.getShort();
173104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    if (marker == JpegHeader.APP1) {
174104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        mByteToSkip = (mBuffer.getShort() & 0x0000ffff) - 2;
175104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        mState = STATE_JPEG_DATA;
176104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    } else if (!JpegHeader.isSofMarker(marker)) {
177104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        out.write(mBuffer.array(), 0, 4);
178104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        mByteToCopy = (mBuffer.getShort() & 0x0000ffff) - 2;
179104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    } else {
180104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        out.write(mBuffer.array(), 0, 4);
181104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        mState = STATE_JPEG_DATA;
182104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    }
183104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    mBuffer.rewind();
184104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
185104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
186104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (length > 0) {
187104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            out.write(buffer, offset, length);
188104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
189104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
190104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
191104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    /**
192104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * Writes the one bytes out. The input data should be a valid JPEG format.
193104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * After writing, it's Exif header will be replaced by the given header.
194104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     */
195104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    @Override
196104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public void write(int oneByte) throws IOException {
197104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mSingleByteArray[0] = (byte) (0xff & oneByte);
198104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        write(mSingleByteArray);
199104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
200104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
201104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    /**
202104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * Equivalent to calling write(buffer, 0, buffer.length).
203104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     */
204104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    @Override
205104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public void write(byte[] buffer) throws IOException {
206104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        write(buffer, 0, buffer.length);
207104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
208104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
209104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private void writeExifData() throws IOException {
210104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (mExifData == null) {
211104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            return;
212104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
213104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (DEBUG) {
214104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            Log.v(TAG, "Writing exif data...");
215104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
216104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        ArrayList<ExifTag> nullTags = stripNullValueTags(mExifData);
217104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        createRequiredIfdAndTag();
218104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int exifSize = calculateAllOffset();
219104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (exifSize + 8 > MAX_EXIF_SIZE) {
220104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            throw new IOException("Exif header is too large (>64Kb)");
221104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
222104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        OrderedDataOutputStream dataOutputStream = new OrderedDataOutputStream(out);
223104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        dataOutputStream.setByteOrder(ByteOrder.BIG_ENDIAN);
224104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        dataOutputStream.writeShort(JpegHeader.APP1);
225104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        dataOutputStream.writeShort((short) (exifSize + 8));
226104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        dataOutputStream.writeInt(EXIF_HEADER);
227104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        dataOutputStream.writeShort((short) 0x0000);
228104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (mExifData.getByteOrder() == ByteOrder.BIG_ENDIAN) {
229104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            dataOutputStream.writeShort(TIFF_BIG_ENDIAN);
230104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        } else {
231104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            dataOutputStream.writeShort(TIFF_LITTLE_ENDIAN);
232104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
233104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        dataOutputStream.setByteOrder(mExifData.getByteOrder());
234104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        dataOutputStream.writeShort(TIFF_HEADER);
235104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        dataOutputStream.writeInt(8);
236104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        writeAllTags(dataOutputStream);
237104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        writeThumbnail(dataOutputStream);
238104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        for (ExifTag t : nullTags) {
239104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mExifData.addTag(t);
240104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
241104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
242104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
243104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private ArrayList<ExifTag> stripNullValueTags(ExifData data) {
244104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        ArrayList<ExifTag> nullTags = new ArrayList<ExifTag>();
245104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        for(ExifTag t : data.getAllTags()) {
246104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (t.getValue() == null && !ExifInterface.isOffsetTag(t.getTagId())) {
247104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                data.removeTag(t.getTagId(), t.getIfd());
248104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                nullTags.add(t);
249104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
250104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
251104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return nullTags;
252104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
253104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
254104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private void writeThumbnail(OrderedDataOutputStream dataOutputStream) throws IOException {
255104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (mExifData.hasCompressedThumbnail()) {
256104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            dataOutputStream.write(mExifData.getCompressedThumbnail());
257104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        } else if (mExifData.hasUncompressedStrip()) {
258104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            for (int i = 0; i < mExifData.getStripCount(); i++) {
259104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                dataOutputStream.write(mExifData.getStrip(i));
260104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
261104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
262104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
263104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
264104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private void writeAllTags(OrderedDataOutputStream dataOutputStream) throws IOException {
265104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        writeIfd(mExifData.getIfdData(IfdId.TYPE_IFD_0), dataOutputStream);
266104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        writeIfd(mExifData.getIfdData(IfdId.TYPE_IFD_EXIF), dataOutputStream);
267104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        IfdData interoperabilityIfd = mExifData.getIfdData(IfdId.TYPE_IFD_INTEROPERABILITY);
268104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (interoperabilityIfd != null) {
269104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            writeIfd(interoperabilityIfd, dataOutputStream);
270104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
271104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        IfdData gpsIfd = mExifData.getIfdData(IfdId.TYPE_IFD_GPS);
272104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (gpsIfd != null) {
273104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            writeIfd(gpsIfd, dataOutputStream);
274104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
275104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        IfdData ifd1 = mExifData.getIfdData(IfdId.TYPE_IFD_1);
276104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (ifd1 != null) {
277104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            writeIfd(mExifData.getIfdData(IfdId.TYPE_IFD_1), dataOutputStream);
278104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
279104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
280104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
281104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private void writeIfd(IfdData ifd, OrderedDataOutputStream dataOutputStream)
282104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            throws IOException {
283104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        ExifTag[] tags = ifd.getAllTags();
284104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        dataOutputStream.writeShort((short) tags.length);
285104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        for (ExifTag tag : tags) {
286104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            dataOutputStream.writeShort(tag.getTagId());
287104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            dataOutputStream.writeShort(tag.getDataType());
288104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            dataOutputStream.writeInt(tag.getComponentCount());
289104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (DEBUG) {
290104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                Log.v(TAG, "\n" + tag.toString());
291104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
292104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (tag.getDataSize() > 4) {
293104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                dataOutputStream.writeInt(tag.getOffset());
294104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            } else {
295104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                ExifOutputStream.writeTagValue(tag, dataOutputStream);
296104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                for (int i = 0, n = 4 - tag.getDataSize(); i < n; i++) {
297104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    dataOutputStream.write(0);
298104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                }
299104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
300104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
301104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        dataOutputStream.writeInt(ifd.getOffsetToNextIfd());
302104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        for (ExifTag tag : tags) {
303104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (tag.getDataSize() > 4) {
304104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                ExifOutputStream.writeTagValue(tag, dataOutputStream);
305104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
306104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
307104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
308104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
309104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private int calculateOffsetOfIfd(IfdData ifd, int offset) {
310104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        offset += 2 + ifd.getTagCount() * TAG_SIZE + 4;
311104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        ExifTag[] tags = ifd.getAllTags();
312104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        for (ExifTag tag : tags) {
313104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (tag.getDataSize() > 4) {
314104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                tag.setOffset(offset);
315104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                offset += tag.getDataSize();
316104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
317104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
318104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return offset;
319104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
320104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
321104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private void createRequiredIfdAndTag() throws IOException {
322104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        // IFD0 is required for all file
323104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        IfdData ifd0 = mExifData.getIfdData(IfdId.TYPE_IFD_0);
324104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (ifd0 == null) {
325104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            ifd0 = new IfdData(IfdId.TYPE_IFD_0);
326104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mExifData.addIfdData(ifd0);
327104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
328104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        ExifTag exifOffsetTag = mInterface.buildUninitializedTag(ExifInterface.TAG_EXIF_IFD);
329104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (exifOffsetTag == null) {
330104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            throw new IOException("No definition for crucial exif tag: "
331104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    + ExifInterface.TAG_EXIF_IFD);
332104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
333104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        ifd0.setTag(exifOffsetTag);
334104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
335104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        // Exif IFD is required for all files.
336104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        IfdData exifIfd = mExifData.getIfdData(IfdId.TYPE_IFD_EXIF);
337104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (exifIfd == null) {
338104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            exifIfd = new IfdData(IfdId.TYPE_IFD_EXIF);
339104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mExifData.addIfdData(exifIfd);
340104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
341104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
342104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        // GPS IFD
343104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        IfdData gpsIfd = mExifData.getIfdData(IfdId.TYPE_IFD_GPS);
344104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (gpsIfd != null) {
345104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            ExifTag gpsOffsetTag = mInterface.buildUninitializedTag(ExifInterface.TAG_GPS_IFD);
346104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (gpsOffsetTag == null) {
347104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                throw new IOException("No definition for crucial exif tag: "
348104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        + ExifInterface.TAG_GPS_IFD);
349104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
350104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            ifd0.setTag(gpsOffsetTag);
351104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
352104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
353104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        // Interoperability IFD
354104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        IfdData interIfd = mExifData.getIfdData(IfdId.TYPE_IFD_INTEROPERABILITY);
355104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (interIfd != null) {
356104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            ExifTag interOffsetTag = mInterface
357104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    .buildUninitializedTag(ExifInterface.TAG_INTEROPERABILITY_IFD);
358104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (interOffsetTag == null) {
359104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                throw new IOException("No definition for crucial exif tag: "
360104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        + ExifInterface.TAG_INTEROPERABILITY_IFD);
361104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
362104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            exifIfd.setTag(interOffsetTag);
363104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
364104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
365104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        IfdData ifd1 = mExifData.getIfdData(IfdId.TYPE_IFD_1);
366104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
367104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        // thumbnail
368104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (mExifData.hasCompressedThumbnail()) {
369104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
370104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (ifd1 == null) {
371104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                ifd1 = new IfdData(IfdId.TYPE_IFD_1);
372104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                mExifData.addIfdData(ifd1);
373104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
374104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
375104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            ExifTag offsetTag = mInterface
376104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    .buildUninitializedTag(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT);
377104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (offsetTag == null) {
378104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                throw new IOException("No definition for crucial exif tag: "
379104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        + ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT);
380104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
381104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
382104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            ifd1.setTag(offsetTag);
383104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            ExifTag lengthTag = mInterface
384104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    .buildUninitializedTag(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH);
385104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (lengthTag == null) {
386104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                throw new IOException("No definition for crucial exif tag: "
387104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        + ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH);
388104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
389104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
390104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            lengthTag.setValue(mExifData.getCompressedThumbnail().length);
391104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            ifd1.setTag(lengthTag);
392104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
393104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            // Get rid of tags for uncompressed if they exist.
394104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            ifd1.removeTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_STRIP_OFFSETS));
395104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            ifd1.removeTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_STRIP_BYTE_COUNTS));
396104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        } else if (mExifData.hasUncompressedStrip()) {
397104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (ifd1 == null) {
398104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                ifd1 = new IfdData(IfdId.TYPE_IFD_1);
399104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                mExifData.addIfdData(ifd1);
400104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
401104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            int stripCount = mExifData.getStripCount();
402104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            ExifTag offsetTag = mInterface.buildUninitializedTag(ExifInterface.TAG_STRIP_OFFSETS);
403104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (offsetTag == null) {
404104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                throw new IOException("No definition for crucial exif tag: "
405104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        + ExifInterface.TAG_STRIP_OFFSETS);
406104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
407104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            ExifTag lengthTag = mInterface
408104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    .buildUninitializedTag(ExifInterface.TAG_STRIP_BYTE_COUNTS);
409104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (lengthTag == null) {
410104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                throw new IOException("No definition for crucial exif tag: "
411104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        + ExifInterface.TAG_STRIP_BYTE_COUNTS);
412104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
413104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            long[] lengths = new long[stripCount];
414104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            for (int i = 0; i < mExifData.getStripCount(); i++) {
415104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                lengths[i] = mExifData.getStrip(i).length;
416104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
417104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            lengthTag.setValue(lengths);
418104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            ifd1.setTag(offsetTag);
419104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            ifd1.setTag(lengthTag);
420104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            // Get rid of tags for compressed if they exist.
421104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            ifd1.removeTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT));
422104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            ifd1.removeTag(ExifInterface
423104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    .getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH));
424104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        } else if (ifd1 != null) {
425104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            // Get rid of offset and length tags if there is no thumbnail.
426104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            ifd1.removeTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_STRIP_OFFSETS));
427104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            ifd1.removeTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_STRIP_BYTE_COUNTS));
428104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            ifd1.removeTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT));
429104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            ifd1.removeTag(ExifInterface
430104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    .getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH));
431104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
432104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
433104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
434104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private int calculateAllOffset() {
435104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int offset = TIFF_HEADER_SIZE;
436104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        IfdData ifd0 = mExifData.getIfdData(IfdId.TYPE_IFD_0);
437104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        offset = calculateOffsetOfIfd(ifd0, offset);
438104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        ifd0.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_EXIF_IFD)).setValue(offset);
439104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
440104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        IfdData exifIfd = mExifData.getIfdData(IfdId.TYPE_IFD_EXIF);
441104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        offset = calculateOffsetOfIfd(exifIfd, offset);
442104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
443104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        IfdData interIfd = mExifData.getIfdData(IfdId.TYPE_IFD_INTEROPERABILITY);
444104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (interIfd != null) {
445104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            exifIfd.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_INTEROPERABILITY_IFD))
446104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    .setValue(offset);
447104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            offset = calculateOffsetOfIfd(interIfd, offset);
448104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
449104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
450104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        IfdData gpsIfd = mExifData.getIfdData(IfdId.TYPE_IFD_GPS);
451104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (gpsIfd != null) {
452104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            ifd0.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_GPS_IFD)).setValue(offset);
453104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            offset = calculateOffsetOfIfd(gpsIfd, offset);
454104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
455104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
456104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        IfdData ifd1 = mExifData.getIfdData(IfdId.TYPE_IFD_1);
457104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (ifd1 != null) {
458104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            ifd0.setOffsetToNextIfd(offset);
459104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            offset = calculateOffsetOfIfd(ifd1, offset);
460104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
461104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
462104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        // thumbnail
463104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (mExifData.hasCompressedThumbnail()) {
464104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            ifd1.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT))
465104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    .setValue(offset);
466104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            offset += mExifData.getCompressedThumbnail().length;
467104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        } else if (mExifData.hasUncompressedStrip()) {
468104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            int stripCount = mExifData.getStripCount();
469104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            long[] offsets = new long[stripCount];
470104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            for (int i = 0; i < mExifData.getStripCount(); i++) {
471104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                offsets[i] = offset;
472104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                offset += mExifData.getStrip(i).length;
473104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
474104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            ifd1.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_STRIP_OFFSETS)).setValue(
475104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    offsets);
476104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
477104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return offset;
478104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
479104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
480104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    static void writeTagValue(ExifTag tag, OrderedDataOutputStream dataOutputStream)
481104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            throws IOException {
482104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        switch (tag.getDataType()) {
483104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            case ExifTag.TYPE_ASCII:
484104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                byte buf[] = tag.getStringByte();
485104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                if (buf.length == tag.getComponentCount()) {
486104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    buf[buf.length - 1] = 0;
487104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    dataOutputStream.write(buf);
488104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                } else {
489104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    dataOutputStream.write(buf);
490104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    dataOutputStream.write(0);
491104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                }
492104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                break;
493104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            case ExifTag.TYPE_LONG:
494104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            case ExifTag.TYPE_UNSIGNED_LONG:
495104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                for (int i = 0, n = tag.getComponentCount(); i < n; i++) {
496104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    dataOutputStream.writeInt((int) tag.getValueAt(i));
497104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                }
498104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                break;
499104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            case ExifTag.TYPE_RATIONAL:
500104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            case ExifTag.TYPE_UNSIGNED_RATIONAL:
501104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                for (int i = 0, n = tag.getComponentCount(); i < n; i++) {
502104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    dataOutputStream.writeRational(tag.getRational(i));
503104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                }
504104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                break;
505104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            case ExifTag.TYPE_UNDEFINED:
506104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            case ExifTag.TYPE_UNSIGNED_BYTE:
507104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                buf = new byte[tag.getComponentCount()];
508104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                tag.getBytes(buf);
509104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                dataOutputStream.write(buf);
510104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                break;
511104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            case ExifTag.TYPE_UNSIGNED_SHORT:
512104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                for (int i = 0, n = tag.getComponentCount(); i < n; i++) {
513104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    dataOutputStream.writeShort((short) tag.getValueAt(i));
514104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                }
515104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                break;
516104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
517104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
518104c45677660586026a7e74ef8c47d396403d50eMichael Jurka}
519