ExifHelper.java

  1/*
  2 * Copyright (C) 2011 The Android Open Source Project
  3 *
  4 * Licensed under the Apache License, Version 2.0 (the "License");
  5 * you may not use this file except in compliance with the License.
  6 * You may obtain a copy of the License at
  7 *
  8 *      http://www.apache.org/licenses/LICENSE-2.0
  9 *
 10 * Unless required by applicable law or agreed to in writing, software
 11 * distributed under the License is distributed on an "AS IS" BASIS,
 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13 * See the License for the specific language governing permissions and
 14 * limitations under the License.
 15 */
 16
 17package eu.siacs.conversations.utils;
 18
 19import android.util.Log;
 20
 21import java.io.IOException;
 22import java.io.InputStream;
 23
 24public class ExifHelper {
 25    private static final String TAG = "CameraExif";
 26
 27    public static int getOrientation(InputStream is) {
 28        if (is == null) {
 29            return 0;
 30        }
 31
 32        byte[] buf = new byte[8];
 33        int length = 0;
 34
 35        // ISO/IEC 10918-1:1993(E)
 36        while (read(is, buf, 2) && (buf[0] & 0xFF) == 0xFF) {
 37            int marker = buf[1] & 0xFF;
 38
 39            // Check if the marker is a padding.
 40            if (marker == 0xFF) {
 41                continue;
 42            }
 43
 44            // Check if the marker is SOI or TEM.
 45            if (marker == 0xD8 || marker == 0x01) {
 46                continue;
 47            }
 48            // Check if the marker is EOI or SOS.
 49            if (marker == 0xD9 || marker == 0xDA) {
 50                return 0;
 51            }
 52
 53            // Get the length and check if it is reasonable.
 54            if (!read(is, buf, 2)) {
 55                return 0;
 56            }
 57            length = pack(buf, 0, 2, false);
 58            if (length < 2) {
 59                Log.e(TAG, "Invalid length");
 60                return 0;
 61            }
 62            length -= 2;
 63
 64            // Break if the marker is EXIF in APP1.
 65            if (marker == 0xE1 && length >= 6) {
 66                if (!read(is, buf, 6)) return 0;
 67                length -= 6;
 68                if (pack(buf, 0, 4, false) == 0x45786966 &&
 69                    pack(buf, 4, 2, false) == 0) {
 70                    break;
 71                }
 72            }
 73
 74            // Skip other markers.
 75            try {
 76                is.skip(length);
 77            } catch (IOException ex) {
 78                return 0;
 79            }
 80            length = 0;
 81        }
 82
 83        // JEITA CP-3451 Exif Version 2.2
 84        if (length > 8) {
 85            int offset = 0;
 86            byte[] jpeg = new byte[length];
 87            if (!read(is, jpeg, length)) {
 88                return 0;
 89            }
 90
 91            // Identify the byte order.
 92            int tag = pack(jpeg, offset, 4, false);
 93            if (tag != 0x49492A00 && tag != 0x4D4D002A) {
 94                Log.e(TAG, "Invalid byte order");
 95                return 0;
 96            }
 97            boolean littleEndian = (tag == 0x49492A00);
 98
 99            // Get the offset and check if it is reasonable.
100            int count = pack(jpeg, offset + 4, 4, littleEndian) + 2;
101            if (count < 10 || count > length) {
102                Log.e(TAG, "Invalid offset");
103                return 0;
104            }
105            offset += count;
106            length -= count;
107
108            // Get the count and go through all the elements.
109            count = pack(jpeg, offset - 2, 2, littleEndian);
110            while (count-- > 0 && length >= 12) {
111                // Get the tag and check if it is orientation.
112                tag = pack(jpeg, offset, 2, littleEndian);
113                if (tag == 0x0112) {
114                    // We do not really care about type and count, do we?
115                    int orientation = pack(jpeg, offset + 8, 2, littleEndian);
116                    switch (orientation) {
117                        case 1:
118                            return 0;
119                        case 3:
120                            return 180;
121                        case 6:
122                            return 90;
123                        case 8:
124                            return 270;
125                    }
126                    Log.i(TAG, "Unsupported orientation");
127                    return 0;
128                }
129                offset += 12;
130                length -= 12;
131            }
132        }
133
134        Log.i(TAG, "Orientation not found");
135        return 0;
136    }
137
138    private static int pack(byte[] bytes, int offset, int length,
139            boolean littleEndian) {
140        int step = 1;
141        if (littleEndian) {
142            offset += length - 1;
143            step = -1;
144        }
145
146        int value = 0;
147        while (length-- > 0) {
148            value = (value << 8) | (bytes[offset] & 0xFF);
149            offset += step;
150        }
151        return value;
152    }
153
154    private static boolean read(InputStream is, byte[] buf, int length) {
155        try {
156            return is.read(buf, 0, length) == length;
157        } catch (IOException ex) {
158            return false;
159        }
160    }
161}