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