/* * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.gallery3d.exif; import java.io.DataInputStream; import java.io.IOException; import java.io.InputStream; import java.nio.ByteOrder; public class ExifParser { private static final String TAG = "ExifParser"; private static final short SOI = (short) 0xFFD8; // SOI marker of JPEG private static final short APP1 = (short) 0xFFE1; // APP1 marker of JPEG private static final int EXIF_HEADER = 0x45786966; // EXIF header "Exif" private static final short EXIF_HEADER_TAIL = (short) 0x0000; // EXIF header in APP1 // TIFF header private static final short LITTLE_ENDIAN_TAG = (short) 0x4949; // "II" private static final short BIG_ENDIAN_TAG = (short) 0x4d4d; // "MM" private static final short TIFF_HEADER_TAIL = 0x002A; public IfdParser parse(InputStream inputStream) throws ExifInvalidFormatException, IOException{ if (!seekTiffData(inputStream)) { return null; } TiffInputStream tiffStream = new TiffInputStream(inputStream); parseTiffHeader(tiffStream); long offset = tiffStream.readUnsignedInt(); if (offset > Integer.MAX_VALUE) { throw new ExifInvalidFormatException("Offset value is larger than Integer.MAX_VALUE"); } return new IfdParser(tiffStream, (int)offset); } private void parseTiffHeader(TiffInputStream tiffStream) throws IOException, ExifInvalidFormatException { short byteOrder = tiffStream.readShort(); ByteOrder order; if (LITTLE_ENDIAN_TAG == byteOrder) { tiffStream.setByteOrder(ByteOrder.LITTLE_ENDIAN); } else if (BIG_ENDIAN_TAG == byteOrder) { tiffStream.setByteOrder(ByteOrder.BIG_ENDIAN); } else { throw new ExifInvalidFormatException("Invalid TIFF header"); } if (tiffStream.readShort() != TIFF_HEADER_TAIL) { throw new ExifInvalidFormatException("Invalid TIFF header"); } } /** * Try to seek the tiff data. If there is no tiff data, return false, else return true and * the inputstream will be at the start of tiff data */ private boolean seekTiffData(InputStream inputStream) throws IOException, ExifInvalidFormatException { DataInputStream dataStream = new DataInputStream(inputStream); // SOI and APP1 if (dataStream.readShort() != SOI) { throw new ExifInvalidFormatException("Invalid JPEG format"); } if (dataStream.readShort() != APP1) { return false; } // APP1 length, it's not used for us dataStream.readShort(); // Exif header if (dataStream.readInt() != EXIF_HEADER || dataStream.readShort() != EXIF_HEADER_TAIL) { // There is no EXIF data; return false; } return true; } }