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