14f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou/* 24f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou * Copyright (C) 2012 The Android Open Source Project 34f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou * 44f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou * Licensed under the Apache License, Version 2.0 (the "License"); 54f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou * you may not use this file except in compliance with the License. 64f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou * You may obtain a copy of the License at 74f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou * 84f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou * http://www.apache.org/licenses/LICENSE-2.0 94f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou * 104f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou * Unless required by applicable law or agreed to in writing, software 114f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou * distributed under the License is distributed on an "AS IS" BASIS, 124f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 134f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou * See the License for the specific language governing permissions and 144f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou * limitations under the License. 154f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou */ 164f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou 174f529e7cada294befb66a1fc9b72b1aa164597dfEarl Oupackage com.android.gallery3d.exif; 184f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou 196e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunkimport android.util.Log; 206e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk 214bee0f203dc96652aca02516f6f2d7d2493fab52Ruben Brunkimport java.io.BufferedOutputStream; 224f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ouimport java.io.FilterOutputStream; 234f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ouimport java.io.IOException; 244f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ouimport java.io.OutputStream; 254f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ouimport java.nio.ByteBuffer; 264f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ouimport java.nio.ByteOrder; 27c59be38287ed3d58f6945d63713b03ac20510c22Ruben Brunkimport java.util.ArrayList; 284f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou 293223b4c4505837f2e2347e813fca96bbbe3191aaEarl Ou/** 303223b4c4505837f2e2347e813fca96bbbe3191aaEarl Ou * This class provides a way to replace the Exif header of a JPEG image. 313223b4c4505837f2e2347e813fca96bbbe3191aaEarl Ou * <p> 323223b4c4505837f2e2347e813fca96bbbe3191aaEarl Ou * Below is an example of writing EXIF data into a file 336e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk * 343223b4c4505837f2e2347e813fca96bbbe3191aaEarl Ou * <pre> 353223b4c4505837f2e2347e813fca96bbbe3191aaEarl Ou * public static void writeExif(byte[] jpeg, ExifData exif, String path) { 363223b4c4505837f2e2347e813fca96bbbe3191aaEarl Ou * OutputStream os = null; 373223b4c4505837f2e2347e813fca96bbbe3191aaEarl Ou * try { 383223b4c4505837f2e2347e813fca96bbbe3191aaEarl Ou * os = new FileOutputStream(path); 393223b4c4505837f2e2347e813fca96bbbe3191aaEarl Ou * ExifOutputStream eos = new ExifOutputStream(os); 403223b4c4505837f2e2347e813fca96bbbe3191aaEarl Ou * // Set the exif header 413223b4c4505837f2e2347e813fca96bbbe3191aaEarl Ou * eos.setExifData(exif); 423223b4c4505837f2e2347e813fca96bbbe3191aaEarl Ou * // Write the original jpeg out, the header will be add into the file. 433223b4c4505837f2e2347e813fca96bbbe3191aaEarl Ou * eos.write(jpeg); 443223b4c4505837f2e2347e813fca96bbbe3191aaEarl Ou * } catch (FileNotFoundException e) { 453223b4c4505837f2e2347e813fca96bbbe3191aaEarl Ou * e.printStackTrace(); 463223b4c4505837f2e2347e813fca96bbbe3191aaEarl Ou * } catch (IOException e) { 473223b4c4505837f2e2347e813fca96bbbe3191aaEarl Ou * e.printStackTrace(); 483223b4c4505837f2e2347e813fca96bbbe3191aaEarl Ou * } finally { 493223b4c4505837f2e2347e813fca96bbbe3191aaEarl Ou * if (os != null) { 503223b4c4505837f2e2347e813fca96bbbe3191aaEarl Ou * try { 513223b4c4505837f2e2347e813fca96bbbe3191aaEarl Ou * os.close(); 523223b4c4505837f2e2347e813fca96bbbe3191aaEarl Ou * } catch (IOException e) { 533223b4c4505837f2e2347e813fca96bbbe3191aaEarl Ou * e.printStackTrace(); 543223b4c4505837f2e2347e813fca96bbbe3191aaEarl Ou * } 553223b4c4505837f2e2347e813fca96bbbe3191aaEarl Ou * } 563223b4c4505837f2e2347e813fca96bbbe3191aaEarl Ou * } 573223b4c4505837f2e2347e813fca96bbbe3191aaEarl Ou * } 583223b4c4505837f2e2347e813fca96bbbe3191aaEarl Ou * </pre> 593223b4c4505837f2e2347e813fca96bbbe3191aaEarl Ou */ 606e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunkclass ExifOutputStream extends FilterOutputStream { 614f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou private static final String TAG = "ExifOutputStream"; 626e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk private static final boolean DEBUG = false; 634bee0f203dc96652aca02516f6f2d7d2493fab52Ruben Brunk private static final int STREAMBUFFER_SIZE = 0x00010000; // 64Kb 644f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou 654f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou private static final int STATE_SOI = 0; 66b6cb8121aa89d06d1fc397d443d22224b634da88Earl Ou private static final int STATE_FRAME_HEADER = 1; 674f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou private static final int STATE_JPEG_DATA = 2; 684f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou 694f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou private static final int EXIF_HEADER = 0x45786966; 704f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou private static final short TIFF_HEADER = 0x002A; 714f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou private static final short TIFF_BIG_ENDIAN = 0x4d4d; 724f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou private static final short TIFF_LITTLE_ENDIAN = 0x4949; 734f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou private static final short TAG_SIZE = 12; 744f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou private static final short TIFF_HEADER_SIZE = 8; 756e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk private static final int MAX_EXIF_SIZE = 65535; 764f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou 774f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou private ExifData mExifData; 78e48c0f96ee3f9654c3538664dd59485927fb44d6Earl Ou private int mState = STATE_SOI; 794f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou private int mByteToSkip; 804f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou private int mByteToCopy; 8114954a62d0d103a5b120225ccf1d7da38b8694e3Ruben Brunk private byte[] mSingleByteArray = new byte[1]; 824f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou private ByteBuffer mBuffer = ByteBuffer.allocate(4); 836e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk private final ExifInterface mInterface; 844f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou 856e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk protected ExifOutputStream(OutputStream ou, ExifInterface iRef) { 864bee0f203dc96652aca02516f6f2d7d2493fab52Ruben Brunk super(new BufferedOutputStream(ou, STREAMBUFFER_SIZE)); 876e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk mInterface = iRef; 884f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou } 894f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou 903223b4c4505837f2e2347e813fca96bbbe3191aaEarl Ou /** 916e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk * Sets the ExifData to be written into the JPEG file. Should be called 926e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk * before writing image data. 933223b4c4505837f2e2347e813fca96bbbe3191aaEarl Ou */ 946e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk protected void setExifData(ExifData exifData) { 954f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou mExifData = exifData; 964f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou } 974f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou 983223b4c4505837f2e2347e813fca96bbbe3191aaEarl Ou /** 993223b4c4505837f2e2347e813fca96bbbe3191aaEarl Ou * Gets the Exif header to be written into the JPEF file. 1003223b4c4505837f2e2347e813fca96bbbe3191aaEarl Ou */ 1016e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk protected ExifData getExifData() { 1024f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou return mExifData; 1034f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou } 1044f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou 1054f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou private int requestByteToBuffer(int requestByteCount, byte[] buffer 1064f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou , int offset, int length) { 1074f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou int byteNeeded = requestByteCount - mBuffer.position(); 1084f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou int byteToRead = length > byteNeeded ? byteNeeded : length; 1094f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou mBuffer.put(buffer, offset, byteToRead); 1104f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou return byteToRead; 1114f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou } 1124f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou 1133223b4c4505837f2e2347e813fca96bbbe3191aaEarl Ou /** 1146e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk * Writes the image out. The input data should be a valid JPEG format. After 1156e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk * writing, it's Exif header will be replaced by the given header. 1163223b4c4505837f2e2347e813fca96bbbe3191aaEarl Ou */ 1174f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou @Override 1184f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou public void write(byte[] buffer, int offset, int length) throws IOException { 1196e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk while ((mByteToSkip > 0 || mByteToCopy > 0 || mState != STATE_JPEG_DATA) 1204f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou && length > 0) { 1214f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou if (mByteToSkip > 0) { 1224f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou int byteToProcess = length > mByteToSkip ? mByteToSkip : length; 1234f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou length -= byteToProcess; 1244f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou mByteToSkip -= byteToProcess; 1254f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou offset += byteToProcess; 1264f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou } 1274f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou if (mByteToCopy > 0) { 1284f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou int byteToProcess = length > mByteToCopy ? mByteToCopy : length; 1294f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou out.write(buffer, offset, byteToProcess); 1304f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou length -= byteToProcess; 1314f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou mByteToCopy -= byteToProcess; 1324f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou offset += byteToProcess; 1334f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou } 1346e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk if (length == 0) { 1356e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk return; 1366e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk } 1374f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou switch (mState) { 1384f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou case STATE_SOI: 1394f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou int byteRead = requestByteToBuffer(2, buffer, offset, length); 1404f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou offset += byteRead; 1414f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou length -= byteRead; 1426e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk if (mBuffer.position() < 2) { 1436e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk return; 1446e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk } 1454f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou mBuffer.rewind(); 1466e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk if (mBuffer.getShort() != JpegHeader.SOI) { 1476e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk throw new IOException("Not a valid jpeg image, cannot write exif"); 1486e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk } 1496e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk out.write(mBuffer.array(), 0, 2); 150b6cb8121aa89d06d1fc397d443d22224b634da88Earl Ou mState = STATE_FRAME_HEADER; 1514f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou mBuffer.rewind(); 152b6cb8121aa89d06d1fc397d443d22224b634da88Earl Ou writeExifData(); 1534f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou break; 154b6cb8121aa89d06d1fc397d443d22224b634da88Earl Ou case STATE_FRAME_HEADER: 1556e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk // We ignore the APP1 segment and copy all other segments 1566e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk // until SOF tag. 1574f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou byteRead = requestByteToBuffer(4, buffer, offset, length); 1584f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou offset += byteRead; 1594f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou length -= byteRead; 160b6cb8121aa89d06d1fc397d443d22224b634da88Earl Ou // Check if this image data doesn't contain SOF. 161b6cb8121aa89d06d1fc397d443d22224b634da88Earl Ou if (mBuffer.position() == 2) { 162b6cb8121aa89d06d1fc397d443d22224b634da88Earl Ou short tag = mBuffer.getShort(); 1634cec566b34655e76b90f8ca089f151e6f42ede0cEarl Ou if (tag == JpegHeader.EOI) { 164b6cb8121aa89d06d1fc397d443d22224b634da88Earl Ou out.write(mBuffer.array(), 0, 2); 165b6cb8121aa89d06d1fc397d443d22224b634da88Earl Ou mBuffer.rewind(); 166b6cb8121aa89d06d1fc397d443d22224b634da88Earl Ou } 167b6cb8121aa89d06d1fc397d443d22224b634da88Earl Ou } 1686e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk if (mBuffer.position() < 4) { 1696e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk return; 1706e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk } 1714f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou mBuffer.rewind(); 172b6cb8121aa89d06d1fc397d443d22224b634da88Earl Ou short marker = mBuffer.getShort(); 1734cec566b34655e76b90f8ca089f151e6f42ede0cEarl Ou if (marker == JpegHeader.APP1) { 1746e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk mByteToSkip = (mBuffer.getShort() & 0x0000ffff) - 2; 1754f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou mState = STATE_JPEG_DATA; 1764cec566b34655e76b90f8ca089f151e6f42ede0cEarl Ou } else if (!JpegHeader.isSofMarker(marker)) { 177b6cb8121aa89d06d1fc397d443d22224b634da88Earl Ou out.write(mBuffer.array(), 0, 4); 1786e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk mByteToCopy = (mBuffer.getShort() & 0x0000ffff) - 2; 1794f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou } else { 1804f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou out.write(mBuffer.array(), 0, 4); 1814f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou mState = STATE_JPEG_DATA; 1824f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou } 1834f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou mBuffer.rewind(); 1844f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou } 1854f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou } 1864f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou if (length > 0) { 1874f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou out.write(buffer, offset, length); 1884f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou } 1894f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou } 1904f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou 1913223b4c4505837f2e2347e813fca96bbbe3191aaEarl Ou /** 1926e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk * Writes the one bytes out. The input data should be a valid JPEG format. 1936e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk * After writing, it's Exif header will be replaced by the given header. 1943223b4c4505837f2e2347e813fca96bbbe3191aaEarl Ou */ 1954f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou @Override 1964f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou public void write(int oneByte) throws IOException { 19714954a62d0d103a5b120225ccf1d7da38b8694e3Ruben Brunk mSingleByteArray[0] = (byte) (0xff & oneByte); 19814954a62d0d103a5b120225ccf1d7da38b8694e3Ruben Brunk write(mSingleByteArray); 1994f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou } 2004f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou 2013223b4c4505837f2e2347e813fca96bbbe3191aaEarl Ou /** 2023223b4c4505837f2e2347e813fca96bbbe3191aaEarl Ou * Equivalent to calling write(buffer, 0, buffer.length). 2033223b4c4505837f2e2347e813fca96bbbe3191aaEarl Ou */ 2044f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou @Override 2054f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou public void write(byte[] buffer) throws IOException { 2064f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou write(buffer, 0, buffer.length); 2074f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou } 2084f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou 2094f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou private void writeExifData() throws IOException { 2106e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk if (mExifData == null) { 2116e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk return; 2126e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk } 2136e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk if (DEBUG) { 2146e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk Log.v(TAG, "Writing exif data..."); 2156e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk } 216c59be38287ed3d58f6945d63713b03ac20510c22Ruben Brunk ArrayList<ExifTag> nullTags = stripNullValueTags(mExifData); 2174f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou createRequiredIfdAndTag(); 2184f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou int exifSize = calculateAllOffset(); 2196e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk if (exifSize + 8 > MAX_EXIF_SIZE) { 2206e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk throw new IOException("Exif header is too large (>64Kb)"); 2216e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk } 2224f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou OrderedDataOutputStream dataOutputStream = new OrderedDataOutputStream(out); 223e48c0f96ee3f9654c3538664dd59485927fb44d6Earl Ou dataOutputStream.setByteOrder(ByteOrder.BIG_ENDIAN); 2244cec566b34655e76b90f8ca089f151e6f42ede0cEarl Ou dataOutputStream.writeShort(JpegHeader.APP1); 2254f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou dataOutputStream.writeShort((short) (exifSize + 8)); 2264f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou dataOutputStream.writeInt(EXIF_HEADER); 2274f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou dataOutputStream.writeShort((short) 0x0000); 2284f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou if (mExifData.getByteOrder() == ByteOrder.BIG_ENDIAN) { 2294f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou dataOutputStream.writeShort(TIFF_BIG_ENDIAN); 2304f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou } else { 2314f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou dataOutputStream.writeShort(TIFF_LITTLE_ENDIAN); 2324f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou } 2334f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou dataOutputStream.setByteOrder(mExifData.getByteOrder()); 2344f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou dataOutputStream.writeShort(TIFF_HEADER); 2354f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou dataOutputStream.writeInt(8); 236e48c0f96ee3f9654c3538664dd59485927fb44d6Earl Ou writeAllTags(dataOutputStream); 2374f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou writeThumbnail(dataOutputStream); 238c59be38287ed3d58f6945d63713b03ac20510c22Ruben Brunk for (ExifTag t : nullTags) { 239c59be38287ed3d58f6945d63713b03ac20510c22Ruben Brunk mExifData.addTag(t); 240c59be38287ed3d58f6945d63713b03ac20510c22Ruben Brunk } 241c59be38287ed3d58f6945d63713b03ac20510c22Ruben Brunk } 242c59be38287ed3d58f6945d63713b03ac20510c22Ruben Brunk 243c59be38287ed3d58f6945d63713b03ac20510c22Ruben Brunk private ArrayList<ExifTag> stripNullValueTags(ExifData data) { 244c59be38287ed3d58f6945d63713b03ac20510c22Ruben Brunk ArrayList<ExifTag> nullTags = new ArrayList<ExifTag>(); 245c59be38287ed3d58f6945d63713b03ac20510c22Ruben Brunk for(ExifTag t : data.getAllTags()) { 246c59be38287ed3d58f6945d63713b03ac20510c22Ruben Brunk if (t.getValue() == null && !ExifInterface.isOffsetTag(t.getTagId())) { 247c59be38287ed3d58f6945d63713b03ac20510c22Ruben Brunk data.removeTag(t.getTagId(), t.getIfd()); 248c59be38287ed3d58f6945d63713b03ac20510c22Ruben Brunk nullTags.add(t); 249c59be38287ed3d58f6945d63713b03ac20510c22Ruben Brunk } 250c59be38287ed3d58f6945d63713b03ac20510c22Ruben Brunk } 251c59be38287ed3d58f6945d63713b03ac20510c22Ruben Brunk return nullTags; 2524f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou } 2534f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou 2544f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou private void writeThumbnail(OrderedDataOutputStream dataOutputStream) throws IOException { 2554f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou if (mExifData.hasCompressedThumbnail()) { 2564f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou dataOutputStream.write(mExifData.getCompressedThumbnail()); 2574f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou } else if (mExifData.hasUncompressedStrip()) { 2584f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou for (int i = 0; i < mExifData.getStripCount(); i++) { 2594f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou dataOutputStream.write(mExifData.getStrip(i)); 2604f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou } 2614f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou } 2624f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou } 2634f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou 264e48c0f96ee3f9654c3538664dd59485927fb44d6Earl Ou private void writeAllTags(OrderedDataOutputStream dataOutputStream) throws IOException { 2654f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou writeIfd(mExifData.getIfdData(IfdId.TYPE_IFD_0), dataOutputStream); 2664f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou writeIfd(mExifData.getIfdData(IfdId.TYPE_IFD_EXIF), dataOutputStream); 2674f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou IfdData interoperabilityIfd = mExifData.getIfdData(IfdId.TYPE_IFD_INTEROPERABILITY); 2684f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou if (interoperabilityIfd != null) { 2694f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou writeIfd(interoperabilityIfd, dataOutputStream); 2704f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou } 2714f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou IfdData gpsIfd = mExifData.getIfdData(IfdId.TYPE_IFD_GPS); 2724f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou if (gpsIfd != null) { 2734f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou writeIfd(gpsIfd, dataOutputStream); 2744f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou } 2754f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou IfdData ifd1 = mExifData.getIfdData(IfdId.TYPE_IFD_1); 2764f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou if (ifd1 != null) { 2774f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou writeIfd(mExifData.getIfdData(IfdId.TYPE_IFD_1), dataOutputStream); 2784f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou } 2794f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou } 2804f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou 2814f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou private void writeIfd(IfdData ifd, OrderedDataOutputStream dataOutputStream) 2824f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou throws IOException { 2839bc2a5c83de59a3131bfeb31dad45df2b47715abEarl Ou ExifTag[] tags = ifd.getAllTags(); 2844f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou dataOutputStream.writeShort((short) tags.length); 2856e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk for (ExifTag tag : tags) { 2864f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou dataOutputStream.writeShort(tag.getTagId()); 2874f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou dataOutputStream.writeShort(tag.getDataType()); 2884f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou dataOutputStream.writeInt(tag.getComponentCount()); 2896e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk if (DEBUG) { 2906e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk Log.v(TAG, "\n" + tag.toString()); 2916e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk } 2924f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou if (tag.getDataSize() > 4) { 2934f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou dataOutputStream.writeInt(tag.getOffset()); 2944f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou } else { 2956e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk ExifOutputStream.writeTagValue(tag, dataOutputStream); 296e48c0f96ee3f9654c3538664dd59485927fb44d6Earl Ou for (int i = 0, n = 4 - tag.getDataSize(); i < n; i++) { 2974f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou dataOutputStream.write(0); 2984f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou } 2994f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou } 3004f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou } 3014f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou dataOutputStream.writeInt(ifd.getOffsetToNextIfd()); 3026e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk for (ExifTag tag : tags) { 3034f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou if (tag.getDataSize() > 4) { 3046e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk ExifOutputStream.writeTagValue(tag, dataOutputStream); 3054f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou } 3064f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou } 3074f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou } 3084f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou 3094f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou private int calculateOffsetOfIfd(IfdData ifd, int offset) { 3104f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou offset += 2 + ifd.getTagCount() * TAG_SIZE + 4; 3119bc2a5c83de59a3131bfeb31dad45df2b47715abEarl Ou ExifTag[] tags = ifd.getAllTags(); 3126e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk for (ExifTag tag : tags) { 3134f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou if (tag.getDataSize() > 4) { 3144f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou tag.setOffset(offset); 3154f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou offset += tag.getDataSize(); 3164f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou } 3174f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou } 3184f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou return offset; 3194f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou } 3204f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou 3216e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk private void createRequiredIfdAndTag() throws IOException { 3224f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou // IFD0 is required for all file 3234f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou IfdData ifd0 = mExifData.getIfdData(IfdId.TYPE_IFD_0); 3244f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou if (ifd0 == null) { 3254f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou ifd0 = new IfdData(IfdId.TYPE_IFD_0); 3264f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou mExifData.addIfdData(ifd0); 3274f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou } 3286e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk ExifTag exifOffsetTag = mInterface.buildUninitializedTag(ExifInterface.TAG_EXIF_IFD); 3296e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk if (exifOffsetTag == null) { 3306e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk throw new IOException("No definition for crucial exif tag: " 3316e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk + ExifInterface.TAG_EXIF_IFD); 3326e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk } 3334f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou ifd0.setTag(exifOffsetTag); 3344f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou 3356e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk // Exif IFD is required for all files. 3364f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou IfdData exifIfd = mExifData.getIfdData(IfdId.TYPE_IFD_EXIF); 3374f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou if (exifIfd == null) { 3384f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou exifIfd = new IfdData(IfdId.TYPE_IFD_EXIF); 3394f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou mExifData.addIfdData(exifIfd); 3404f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou } 3414f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou 3424f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou // GPS IFD 3434f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou IfdData gpsIfd = mExifData.getIfdData(IfdId.TYPE_IFD_GPS); 3444f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou if (gpsIfd != null) { 3456e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk ExifTag gpsOffsetTag = mInterface.buildUninitializedTag(ExifInterface.TAG_GPS_IFD); 3466e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk if (gpsOffsetTag == null) { 3476e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk throw new IOException("No definition for crucial exif tag: " 3486e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk + ExifInterface.TAG_GPS_IFD); 3496e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk } 3504f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou ifd0.setTag(gpsOffsetTag); 3514f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou } 3524f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou 3534f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou // Interoperability IFD 3544f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou IfdData interIfd = mExifData.getIfdData(IfdId.TYPE_IFD_INTEROPERABILITY); 3554f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou if (interIfd != null) { 3566e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk ExifTag interOffsetTag = mInterface 3576e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk .buildUninitializedTag(ExifInterface.TAG_INTEROPERABILITY_IFD); 3586e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk if (interOffsetTag == null) { 3596e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk throw new IOException("No definition for crucial exif tag: " 3606e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk + ExifInterface.TAG_INTEROPERABILITY_IFD); 3616e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk } 3624f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou exifIfd.setTag(interOffsetTag); 3634f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou } 3644f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou 3654f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou IfdData ifd1 = mExifData.getIfdData(IfdId.TYPE_IFD_1); 3664f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou 3674f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou // thumbnail 3684f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou if (mExifData.hasCompressedThumbnail()) { 3696e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk 3704f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou if (ifd1 == null) { 3714f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou ifd1 = new IfdData(IfdId.TYPE_IFD_1); 3724f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou mExifData.addIfdData(ifd1); 3734f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou } 3746e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk 3756e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk ExifTag offsetTag = mInterface 3766e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk .buildUninitializedTag(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT); 3776e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk if (offsetTag == null) { 3786e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk throw new IOException("No definition for crucial exif tag: " 3796e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk + ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT); 3806e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk } 3816e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk 3824f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou ifd1.setTag(offsetTag); 3836e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk ExifTag lengthTag = mInterface 3846e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk .buildUninitializedTag(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH); 3856e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk if (lengthTag == null) { 3866e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk throw new IOException("No definition for crucial exif tag: " 3876e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk + ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH); 3886e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk } 3896e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk 3904f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou lengthTag.setValue(mExifData.getCompressedThumbnail().length); 3914f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou ifd1.setTag(lengthTag); 3926e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk 3936e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk // Get rid of tags for uncompressed if they exist. 3946e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk ifd1.removeTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_STRIP_OFFSETS)); 3956e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk ifd1.removeTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_STRIP_BYTE_COUNTS)); 3966e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk } else if (mExifData.hasUncompressedStrip()) { 3974f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou if (ifd1 == null) { 3984f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou ifd1 = new IfdData(IfdId.TYPE_IFD_1); 3994f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou mExifData.addIfdData(ifd1); 4004f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou } 4014f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou int stripCount = mExifData.getStripCount(); 4026e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk ExifTag offsetTag = mInterface.buildUninitializedTag(ExifInterface.TAG_STRIP_OFFSETS); 4036e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk if (offsetTag == null) { 4046e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk throw new IOException("No definition for crucial exif tag: " 4056e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk + ExifInterface.TAG_STRIP_OFFSETS); 4066e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk } 4076e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk ExifTag lengthTag = mInterface 4086e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk .buildUninitializedTag(ExifInterface.TAG_STRIP_BYTE_COUNTS); 4096e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk if (lengthTag == null) { 4106e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk throw new IOException("No definition for crucial exif tag: " 4116e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk + ExifInterface.TAG_STRIP_BYTE_COUNTS); 4126e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk } 4134f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou long[] lengths = new long[stripCount]; 4144f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou for (int i = 0; i < mExifData.getStripCount(); i++) { 4154f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou lengths[i] = mExifData.getStrip(i).length; 4164f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou } 4174f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou lengthTag.setValue(lengths); 4184f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou ifd1.setTag(offsetTag); 4194f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou ifd1.setTag(lengthTag); 4206e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk // Get rid of tags for compressed if they exist. 4216e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk ifd1.removeTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT)); 4226e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk ifd1.removeTag(ExifInterface 4236e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk .getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH)); 424978f4aae1d2923fd125f85a6d5c568a26c571522nicolasroard } else if (ifd1 != null) { 4256e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk // Get rid of offset and length tags if there is no thumbnail. 4266e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk ifd1.removeTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_STRIP_OFFSETS)); 4276e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk ifd1.removeTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_STRIP_BYTE_COUNTS)); 4286e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk ifd1.removeTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT)); 4296e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk ifd1.removeTag(ExifInterface 4306e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk .getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH)); 4314f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou } 4324f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou } 4334f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou 4344f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou private int calculateAllOffset() { 4354f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou int offset = TIFF_HEADER_SIZE; 4364f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou IfdData ifd0 = mExifData.getIfdData(IfdId.TYPE_IFD_0); 4374f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou offset = calculateOffsetOfIfd(ifd0, offset); 4386e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk ifd0.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_EXIF_IFD)).setValue(offset); 4394f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou 4404f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou IfdData exifIfd = mExifData.getIfdData(IfdId.TYPE_IFD_EXIF); 4414f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou offset = calculateOffsetOfIfd(exifIfd, offset); 4424f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou 4434f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou IfdData interIfd = mExifData.getIfdData(IfdId.TYPE_IFD_INTEROPERABILITY); 4444f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou if (interIfd != null) { 4456e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk exifIfd.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_INTEROPERABILITY_IFD)) 4466e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk .setValue(offset); 4474f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou offset = calculateOffsetOfIfd(interIfd, offset); 4484f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou } 4494f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou 4504f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou IfdData gpsIfd = mExifData.getIfdData(IfdId.TYPE_IFD_GPS); 4514f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou if (gpsIfd != null) { 4526e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk ifd0.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_GPS_IFD)).setValue(offset); 4534f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou offset = calculateOffsetOfIfd(gpsIfd, offset); 4544f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou } 4554f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou 4564f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou IfdData ifd1 = mExifData.getIfdData(IfdId.TYPE_IFD_1); 4574f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou if (ifd1 != null) { 4584f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou ifd0.setOffsetToNextIfd(offset); 4594f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou offset = calculateOffsetOfIfd(ifd1, offset); 4604f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou } 4614f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou 4624f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou // thumbnail 4634f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou if (mExifData.hasCompressedThumbnail()) { 4646e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk ifd1.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT)) 4656e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk .setValue(offset); 4664f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou offset += mExifData.getCompressedThumbnail().length; 4676e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk } else if (mExifData.hasUncompressedStrip()) { 4684f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou int stripCount = mExifData.getStripCount(); 4694f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou long[] offsets = new long[stripCount]; 4704f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou for (int i = 0; i < mExifData.getStripCount(); i++) { 4714f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou offsets[i] = offset; 4724f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou offset += mExifData.getStrip(i).length; 4734f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou } 4746e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk ifd1.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_STRIP_OFFSETS)).setValue( 4756e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk offsets); 4764f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou } 4774f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou return offset; 4784f529e7cada294befb66a1fc9b72b1aa164597dfEarl Ou } 4796e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk 4806e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk static void writeTagValue(ExifTag tag, OrderedDataOutputStream dataOutputStream) 4816e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk throws IOException { 4826e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk switch (tag.getDataType()) { 4836e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk case ExifTag.TYPE_ASCII: 4846e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk byte buf[] = tag.getStringByte(); 4856e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk if (buf.length == tag.getComponentCount()) { 4866e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk buf[buf.length - 1] = 0; 4876e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk dataOutputStream.write(buf); 4886e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk } else { 4896e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk dataOutputStream.write(buf); 4906e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk dataOutputStream.write(0); 4916e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk } 4926e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk break; 4936e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk case ExifTag.TYPE_LONG: 4946e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk case ExifTag.TYPE_UNSIGNED_LONG: 4956e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk for (int i = 0, n = tag.getComponentCount(); i < n; i++) { 4966e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk dataOutputStream.writeInt((int) tag.getValueAt(i)); 4976e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk } 4986e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk break; 4996e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk case ExifTag.TYPE_RATIONAL: 5006e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk case ExifTag.TYPE_UNSIGNED_RATIONAL: 5016e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk for (int i = 0, n = tag.getComponentCount(); i < n; i++) { 5026e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk dataOutputStream.writeRational(tag.getRational(i)); 5036e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk } 5046e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk break; 5056e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk case ExifTag.TYPE_UNDEFINED: 5066e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk case ExifTag.TYPE_UNSIGNED_BYTE: 5076e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk buf = new byte[tag.getComponentCount()]; 5086e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk tag.getBytes(buf); 5096e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk dataOutputStream.write(buf); 5106e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk break; 5116e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk case ExifTag.TYPE_UNSIGNED_SHORT: 5126e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk for (int i = 0, n = tag.getComponentCount(); i < n; i++) { 5136e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk dataOutputStream.writeShort((short) tag.getValueAt(i)); 5146e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk } 5156e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk break; 5166e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk } 5176e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk } 5186e6a524390d8ddebce5de0dcc8ae258e652ec80aRuben Brunk} 519