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