package com.wistron.StreamHelper;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.io.StreamCorruptedException;
import java.io.StringWriter;
import java.nio.Buffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import org.apache.http.util.ByteArrayBuffer;
import android.R.bool;
import android.R.integer;
import android.media.ExifInterface;
import android.os.BadParcelableException;
import android.os.Debug;
import android.util.Log;
/**
* Exif信息解析类
* @author Cocoonshu
* @date 2012-02-02 13:53:23
*/
public class MPOExifInterface extends MPOFileStreamParser{
public static boolean DEBUG = true;
private static final String TAG = "MPOExifInterface";
private List<Integer> mPtrs = null; //Buffer中的定位点列表
private List<IFDInfo> mTagList = null; //Exif Tag List
private JPEGExif mIFDExif = null; //图片的Exif信息
private HashMap<Integer, Object> mINFOHashMap = new HashMap<Integer, Object>();
public MPOExifInterface(String filePath) throws FileNotFoundException, StreamCorruptedException {
// TODO 获取图片的所有Exif info,以便调用getAttribute时,不需要再次读取
super(filePath);
byte[] buffer = null;
mPtrs = new ArrayList<Integer>();
mTagList = new ArrayList<IFDInfo>();
mIFDExif = new JPEGExif();
if(mFin == null){
Log.e(TAG, "[MPOExifInterface]文件无法打开");
throw new FileNotFoundException("File cannot be open");
}
//读取文件大小
mINFOHashMap.clear();
try {
mINFOHashMap.put(MPOHexTAG.TID_IMAGESIZE, mFin.length());
} catch (IOException exp) {
exp.printStackTrace();
Log.e(TAG, "[MPOExifInterface]文件读取错误");
}
//开始读取APP1块
buffer = getAPP1Block(mFin);
if(mPtrs.size() < 1){
Log.e(TAG, "[MPOExifInterface]找不到APP1标签");
throw new StreamCorruptedException("Cannot find APP1 DATA TAG");
}
queryExif(buffer); //开始执行Exif查询过程
if(DEBUG){//////////////////////////////////////////////////////
if(mINFOHashMap != null){
Log.w(TAG, "[HASHMAP][" + mINFOHashMap.toString() + "]");
String GPSLatitude = "",
GPSLongitude = "";
if((String[])mINFOHashMap.get(2) != null)
for(int a = 0; a < ((String[])mINFOHashMap.get(2)).length; a++){
GPSLatitude += ((String[])mINFOHashMap.get(2))[a] + " ";
}
if((String[])mINFOHashMap.get(4) != null)
for(int a = 0; a < ((String[])mINFOHashMap.get(4)).length; a++){
GPSLongitude += ((String[])mINFOHashMap.get(4))[a] + " ";
}
Log.w(TAG, "[HASHMAP]GPSLatitude = " + GPSLatitude + " ,GPSLongitude = " + GPSLongitude);
}
}//////////////////////////////////////////////////////
//关闭文件流
try {
mFin.close();
} catch (IOException e) {
e.printStackTrace();
Log.e(TAG, "[MPOExifInterface]文件流已关闭");
}
System.gc();
}
/**
* 在静态表中查询TAG
* @return
*/
protected int queryTag(int tag) {
return BaselineUtls.BinarySearch(tag, MPOHexTAG.TID_LINK);
}
public String getAttributeString(int tag){
String res = "";
Object tmp = null;
try{
if(queryTag(tag) != -1){
if(mINFOHashMap != null){
tmp = mINFOHashMap.get(tag);
if(tmp != null){
switch (tag) {
case MPOHexTAG.TID_GPSLAT:
case MPOHexTAG.TID_GPSLON:{
//数组方式处理
String[] str = null;
str = (String[])tmp;
for(int i = 0; i < str.length; i++){
res += str[i] + ",";
}
res = res.substring(0, res.length() - 2);
}
break;
case MPOHexTAG.TID_FLASH:{
int key = (Integer) tmp;
switch (key) {
case MPOHexTAG.FLASH_AUTO_FIRED:
case MPOHexTAG.FLASH_AUTO_FIRED_DETECTED:
case MPOHexTAG.FLASH_AUTO_FIRED_UNDETECTED:
case MPOHexTAG.FLASH_AUTO_UNFIRED:
res = "Auto";
break;
case MPOHexTAG.FLASH_COMPULSORY_FIRED:
case MPOHexTAG.FLASH_COMPULSORY_FIRED_DETECTED:
case MPOHexTAG.FLASH_COMPULSORY_FIRED_UNDETECTED:
case MPOHexTAG.FLASH_COMPULSORY_UNFIRED:
res = "Compulsory";
case MPOHexTAG.FLASH_DETECTED:
case MPOHexTAG.FLASH_FIRED:
res = "Fired";
break;
case MPOHexTAG.FLASH_NO_FUNCTION:
res = "No flash";
break;
case MPOHexTAG.FLASH_REDEYE_AUTO:
case MPOHexTAG.FLASH_REDEYE_AUTO_DETECTED:
case MPOHexTAG.FLASH_REDEYE_AUTO_UNDETECTED:
case MPOHexTAG.FLASH_REDEYE_COMPULSORY_FIRED:
case MPOHexTAG.FLASH_REDEYE_COMPULSORY_FIRED_DETECTED:
case MPOHexTAG.FLASH_REDEYE_COMPULSORY_FIRED_UNDETECTED:
case MPOHexTAG.FLASH_REDEYE_FIRED:
case MPOHexTAG.FLASH_REDEYE_FIRED_DETECTED:
case MPOHexTAG.FLASH_REDEYE_FIRED_UNDETECTED:
res = "Red eye";
break;
case MPOHexTAG.FLASH_UNDETECTED:
case MPOHexTAG.FLASH_UNFIRED:
res = "Unfired";
break;
}
}
break;
case MPOHexTAG.TID_ORIENTATION:{
int key = (Integer) tmp;
switch (key) {
case MPOHexTAG.ORIENTATION_BOTLEFT:
//res = "180-roll";
res = "bottom-left";
break;
case MPOHexTAG.ORIENTATION_BOTRIGHT:
//res = "180";
res = "bottom-right";
break;
case MPOHexTAG.ORIENTATION_LEFTTOP:
//res = "270-roll";
res = "left-top";
break;
case MPOHexTAG.ORIENTATION_RIGHTBOT:
//res = "90-roll";
res = "right-bot";
break;
case MPOHexTAG.ORIENTATION_RIGHTTOP:
//res = "270";
res = "right-top";
break;
case MPOHexTAG.ORIENTATION_TOPLEFT:
//res = "0";
res = "top-left";
break;
case MPOHexTAG.ORIENTATION_TOPRIGHT:
//res = "0-roll";
res = "top-right";
break;
}
}
break;
case MPOHexTAG.TID_WHITEBALANCE:{
int key = (Integer) tmp;
switch (key) {
case MPOHexTAG.WHITE_BALANCE_AUTO:
res = "Auto";
break;
case MPOHexTAG.WHITE_BALANCE_MANUAL:
res = "Manual";
break;
}
}
break;
case MPOHexTAG.TID_GPSALTREF:{
int key = (Byte) tmp;
switch (key) {
case MPOHexTAG.GPS_ABOVE_SEA_LEVEL:
res = "+";
break;
case MPOHexTAG.GPS_BELOW_SEA_LEVEL:
res = "-";
break;
}
}
break;
default:
res = "" + tmp;
break;
}
}
}
}
}catch (Exception exp) {
exp.printStackTrace();
res = "";
}
return res;
}
public int getAttributeInt(int tag, int defaultValue){
int res = 0;
Object tmp = null;
try{
if(queryTag(tag) != -1){
if(mINFOHashMap != null){
tmp = mINFOHashMap.get(tag);
if(tmp != null){
switch (tag) {
case MPOHexTAG.TID_GPSLAT:
case MPOHexTAG.TID_GPSLON:
case MPOHexTAG.TID_GPSALT:{
res = defaultValue;
}
break;
case MPOHexTAG.TID_ORIENTATION:{
int key = (Integer) tmp;
switch (key) {
case MPOHexTAG.ORIENTATION_BOTLEFT:
//res = "180-roll";
res = 180;
break;
case MPOHexTAG.ORIENTATION_BOTRIGHT:
//res = "180";
res = 180;
break;
case MPOHexTAG.ORIENTATION_LEFTTOP:
//res = "270-roll";
res = 270;
break;
case MPOHexTAG.ORIENTATION_RIGHTBOT:
//res = "90-roll";
res = 90;
break;
case MPOHexTAG.ORIENTATION_RIGHTTOP:
//res = "270";
res = 270;
break;
case MPOHexTAG.ORIENTATION_TOPLEFT:
//res = "0";
res = 0;
break;
case MPOHexTAG.ORIENTATION_TOPRIGHT:
//res = "0-roll";
res = 0;
break;
}
}
break;
default:
try{
res = (Integer) tmp;
}catch(ClassCastException exp){
exp.printStackTrace();
long castData = (Long)tmp;
res = (int)castData;
}
break;
}
}
}
}
}catch (Exception exp) {
exp.printStackTrace();
res = defaultValue;
}
return res;
}
public double getAttributeDouble(int tag, double defaultValue){
double res = 0;
Object tmp = null;
try{
if(queryTag(tag) != -1){
if(mINFOHashMap != null){
tmp = mINFOHashMap.get(tag);
if(tmp != null){
switch (tag) {
case MPOHexTAG.TID_GPSLAT:
case MPOHexTAG.TID_GPSLON:{
String[] gpsStr = null;
double deg = 0, min = 0, sec = 0;
gpsStr = (String[])tmp;
if(gpsStr != null && gpsStr.length == 3){
deg = BaselineUtls.Rational2UDouble(gpsStr[0]);
min = BaselineUtls.Rational2UDouble(gpsStr[1])/60f;
sec = BaselineUtls.Rational2UDouble(gpsStr[2])/3600f;
res = deg + min + sec;
}
if(deg < 0 || min < 0 || sec < 0){//解析错误时,BaselineUtls.Rational2UDouble返回-1
res = defaultValue;
}
}
break;
case MPOHexTAG.TID_GPSALT:
//实数型处理
res = BaselineUtls.Rational2UDouble((String) tmp);
break;
default:
if(tmp instanceof String){
res = BaselineUtls.Rational2UDouble((String) tmp);
}else{
try{
res = (Long) tmp;
}catch(ClassCastException exp){
exp.printStackTrace();
double castData = (Integer)tmp;
res = (double)castData;
}
}
break;
}
}
}
}
}catch (Exception exp) {
exp.printStackTrace();
res = defaultValue;
}
return res;
}
@Deprecated
public void setAttribute(int tag, String value){
}
@Deprecated
public void saveAttributesIntoImage(){
}
/**
* 查询Exif信息的过程
* @param buffer APP1数据段
* @notice 若运算的结果mIFDExif为空,则说明buffer已损坏
*/
protected void queryExif(byte[] buffer) {
int StartPTR = -1, //函数中使用的Buffer位置锚
ExifPTR = -1, //Exif信息块索引
GPSPTR = -1; //GPS信息块索引
int BaselineInfoTagCount = 0, //基本信息的数量
ExifInfoTagCount = 0, //Exif信息的数量
GPSInfoTagCount = 0; //GPS信息的数量
int CodeOrder = MPOHexTAG.BIG_ENDIAN; //字节序
if(mTagList == null){
mTagList = new ArrayList<IFDInfo>();
}
if(mIFDExif == null){
mIFDExif = new JPEGExif();
}
//清空数据
mTagList.clear();
//读取基本信息块
try{
//1.判断数据头部是否为FF E1
if(buffer[0] == MPOHexTAG.TAG_APP1[0] && buffer[1] == MPOHexTAG.TAG_APP1[1])
{
//2.判断数据是否为Exif信息
if(BaselineUtls.Byte2String(new byte[]{buffer[4], buffer[5], buffer[6], buffer[7]}).equals("Exif")){
//3.读取字节序
switch (BaselineUtls.Byte2Int(new byte[]{buffer[10], buffer[11], buffer[12], buffer[13]}, MPOHexTAG.BIG_ENDIAN)) {
case MPOHexTAG.BIG_ENDIAN:
CodeOrder = MPOHexTAG.BIG_ENDIAN;
break;
case MPOHexTAG.LITTLE_ENDIAN:
CodeOrder = MPOHexTAG.LITTLE_ENDIAN;
break;
default:
mIFDExif = null;
return;
}
//读取偏移量,寻找基本信息定义表
StartPTR = 10 //基址
+ 2 //BaselineInfoTagCount的空间
+ BaselineUtls.Byte2Int(new byte[]{buffer[14], buffer[15], buffer[16], buffer[17]}, CodeOrder); //数据偏移量
BaselineInfoTagCount = BaselineUtls.Byte2Int(new byte[]{buffer[18], buffer[19]}, CodeOrder);
//开始读取基本信息
for(int i = 0; i < BaselineInfoTagCount; i++){
byte[] inbuffer = new byte[12];
for(int j = 0; j < 12; j++){
inbuffer[j] = buffer[StartPTR + i*12 + j];
}
mTagList.add(getIFDInfo(inbuffer, CodeOrder));
}
}else{
mIFDExif = null;
return;
}
}else{
mIFDExif = null;
return;
}
}catch (ArrayIndexOutOfBoundsException exp) {
exp.printStackTrace();
Log.e(TAG, "[queryExif]读取基本信息时失败,APP1 Buffer DATA BLOCK已损坏");
//说明buffer不完整,就已读取到的信息来返回
//继续下面的操作
}
//解析基本Exif信息(包括Exif信息块和GPS信息块的索引)
solveDataDetail(buffer, CodeOrder);
//查找Exif信息块和GPS信息块的索引
if(mINFOHashMap.get(MPOHexTAG.TID_EXIF_IFD_PTR) != null){
long tmp = (Long) mINFOHashMap.get(MPOHexTAG.TID_EXIF_IFD_PTR);
ExifPTR = (int)tmp;
}
if(mINFOHashMap.get(MPOHexTAG.TID_GPS_IFD_PTR) != null){
long tmp = (Long) mINFOHashMap.get(MPOHexTAG.TID_GPS_IFD_PTR);
GPSPTR = (int)tmp;
}
//读取Exif信息块
try{
if(ExifPTR >= 0){
ExifPTR += 10; //转换成buffer中的地址
ExifInfoTagCount = BaselineUtls.Byte2UInt(new byte[]{buffer[ExifPTR], buffer[ExifPTR + 1]}, CodeOrder);
//开始读取Exif信息
ExifPTR += 2; //重定位到Entry节点头
for(int i = 0; i < ExifInfoTagCount; i++){
byte[] inbuffer = new byte[12];
for(int j = 0; j < 12; j++){
inbuffer[j] = buffer[ExifPTR + i*12 + j];
}
mTagList.add(getIFDInfo(inbuffer, CodeOrder));
}
}
}catch (ArrayIndexOutOfBoundsException exp) {
exp.printStackTrace();
Log.e(TAG, "[queryExif]读取Exif信息时失败,APP1 Buffer DATA BLOCK已损坏");
//说明buffer不完整,就已读取到的信息来返回
//继续下面的操作
}
//读取GPS信息块
try{
if(GPSPTR >= 0){
GPSPTR += 10; //转换成buffer中的地址
GPSInfoTagCount = BaselineUtls.Byte2UInt(new byte[]{buffer[GPSPTR], buffer[GPSPTR + 1]}, CodeOrder);
//开始读取GPS信息
GPSPTR += 2; //重定位到Entry节点头
for(int i = 0; i < GPSInfoTagCount; i++){
byte[] inbuffer = new byte[12];
for(int j = 0; j < 12; j++){
inbuffer[j] = buffer[GPSPTR + i*12 + j];
}
mTagList.add(getIFDInfo(inbuffer, CodeOrder));
}
}
}catch (ArrayIndexOutOfBoundsException exp) {
exp.printStackTrace();
Log.e(TAG, "[queryExif]读取GPS信息时失败,APP1 Buffer DATA BLOCK已损坏");
//说明buffer不完整,就已读取到的信息来返回
//继续下面的操作
}
//解析Exif信息
solveDataDetail(buffer, CodeOrder);
}
/**
* 解析数据为可阅读信息
* @param buffer
* @param codeOrder
*/
protected void solveDataDetail(byte[] buffer, int codeOrder) {
int size = mTagList.size();
IFDInfo temp = null;
for(int i = 0; i < size; i++){
if(mTagList.get(i).mValue == null){
//检索Value
temp = mTagList.get(i);
Object value = null;
byte[] data = new byte[temp.mCount * BaselineUtls.sizeof(temp.mType)];
if(temp.mOffset + data.length + 10 < buffer.length){
//在buffer中获取
for(int j = 0; j < data.length; j++){
data[j] = buffer[temp.mOffset + 10 + j];
}
{//解析Value Data
if(temp.mType == MPOHexTAG.TAG_ASCII){
temp.mValue = BaselineUtls.getValue(data, temp.mType, codeOrder);
}else{
if(temp.mTagName == MPOHexTAG.TID_USERCOMMENTS){//COMMENT字段要除外,这个字段的数据为字符串,类型为UNDEFINED
temp.mValue = BaselineUtls.getValue(data, MPOHexTAG.TAG_ASCII, codeOrder);
}
if(temp.mCount < 2){
temp.mValue = BaselineUtls.getValue(data, temp.mType, codeOrder);
}else{
switch (temp.mType) {
case MPOHexTAG.TAG_RATIONAL:
String[] str = new String[temp.mCount];
for(int m = 0; m < temp.mCount; m++){
byte[] unitData = new byte[BaselineUtls.sizeof(temp.mType)];
for(int n = 0; n < unitData.length; n++){
unitData[n] = data[n + unitData.length * m];
}
str[m] = (String) BaselineUtls.getValue(unitData, temp.mType, codeOrder);
temp.mValue = str;
}
break;
default:
Object[] dbl = new Object[temp.mCount];
for(int m = 0; m < temp.mCount; m++){
byte[] unitData = new byte[BaselineUtls.sizeof(temp.mType)];
for(int n = 0; n < unitData.length; n++){
unitData[n] = data[n + unitData.length * m];
}
dbl[m] = (Object) BaselineUtls.getValue(unitData, temp.mType, codeOrder);
temp.mValue = dbl;
}
break;
}
}
}
}
mTagList.get(i).mValue = temp.mValue;
}else{
//在fileStream中获取,直接读取文件流
try {
if(mFin == null){
mFin = new RandomAccessFile(mFileName, "r");
}
mFin.seek(temp.mOffset + 12);
mFin.read(data, 0, data.length);
{//解析Value Data
if(temp.mType == MPOHexTAG.TAG_ASCII){
temp.mValue = BaselineUtls.getValue(data, temp.mType, codeOrder);
}else{
if(temp.mCount < 2){
temp.mValue = BaselineUtls.getValue(data, temp.mType, codeOrder);
}else{
if(temp.mCount >= mFin.length()){
//文件偏移错误,停止解析本TAG
temp.mValue = null;
continue;
}
switch (temp.mType) {
case MPOHexTAG.TAG_RATIONAL:
String[] str = new String[temp.mCount];
for(int m = 0; m < temp.mCount; m++){
byte[] unitData = new byte[BaselineUtls.sizeof(temp.mType)];
for(int n = 0; n < unitData.length; n++){
unitData[n] = data[n + unitData.length * m];
}
str[m] = (String) BaselineUtls.getValue(unitData, temp.mType, codeOrder);
temp.mValue = str;
}
break;
default:
Object[] dbl = new Object[temp.mCount];
for(int m = 0; m < temp.mCount; m++){
byte[] unitData = new byte[BaselineUtls.sizeof(temp.mType)];
for(int n = 0; n < unitData.length; n++){
unitData[n] = data[n + unitData.length * m];
}
dbl[m] = (Object) BaselineUtls.getValue(unitData, temp.mType, codeOrder);
temp.mValue = dbl;
}
break;
}
}
}
}
mTagList.get(i).mValue = temp.mValue;
} catch (IOException exp) {
exp.printStackTrace();
Log.e(TAG, "[queryExif]解析Exif信息时 " + temp.mTagName + " 失败");
//继续读取下一条
break;
}
}
}
mINFOHashMap.put(mTagList.get(i).mTagName, mTagList.get(i).mValue);
}
}
protected IFDInfo getIFDInfo(byte[] buffer, int codeOrder) {
IFDInfo res = new IFDInfo();
if(DEBUG){////////////////////////////////////////////////////////////////////////////////////
String str = "";
for(int a = 0; a < buffer.length; a++)
str += Integer.toHexString(BaselineUtls.Byte2UInt(new byte[]{buffer[a]}, MPOHexTAG.BIG_ENDIAN)) + " ";
Log.w(TAG, "[getIFDInfo]buffer = " + str );
}////////////////////////////////////////////////////////////////////////////////////
if(buffer.length < 12){
return null;
}
switch (codeOrder) {
case MPOHexTAG.BIG_ENDIAN:
res.mTagName = BaselineUtls.getTag(buffer[0], buffer[1]);
res.mType = BaselineUtls.getType(buffer[2], buffer[3]);
res.mCount = BaselineUtls.getCount(buffer[4], buffer[5], buffer[6], buffer[7]);
if(BaselineUtls.sizeof(res.mType)*res.mCount > 4){//最后一位存的是偏移量
res.mOffset = BaselineUtls.getOffset(buffer[8], buffer[9], buffer[10], buffer[11]);
res.mValue = null;
}else{//最后一位存的是值
res.mOffset = -1;
res.mValue = BaselineUtls.getValue(new byte[]{buffer[8], buffer[9], buffer[10], buffer[11]}, res.mType);
}
break;
case MPOHexTAG.LITTLE_ENDIAN:
res.mTagName = BaselineUtls.getTag(buffer[1], buffer[0]);
res.mType = BaselineUtls.getType(buffer[3], buffer[2]);
res.mCount = BaselineUtls.getCount(buffer[7], buffer[6], buffer[5], buffer[4]);
if(BaselineUtls.sizeof(res.mType)*res.mCount > 4){//最后一位存的是偏移量
res.mOffset = BaselineUtls.getOffset(buffer[11], buffer[10], buffer[9], buffer[8]);
res.mValue = null;
}else{//最后一位存的是值
res.mOffset = -1;
res.mValue = BaselineUtls.getValue(new byte[]{buffer[11], buffer[10], buffer[9], buffer[8]}, res.mType); /*mType为ASCII的时候要小心这里的字节序问题*/
}
break;
}
return res;
}
protected byte[] getAPP1Block(RandomAccessFile fin) {
byte[] res = new byte[0];
int pos = 0,
len = 0;
try{
if(fin == null){
return new byte[0];
}
if(mPtrs == null){
mPtrs = new ArrayList<Integer>();
}
boolean E1_OK = false;
byte[] buffer = new byte[1024];
int loopCount = (int) (fin.length()/1024f);
int loopMod = (int) (fin.length()%1024f);
for(int i = 0; i < loopCount; i++){
if(E1_OK){
break;
}
//1024倍数文件块
fin.read(buffer, 0, 1024);
for(int j = 0; j < buffer.length - 1; j++){
//FF E1
if(buffer[j] == MPOHexTAG.TAG_APP1[0] && buffer[j+1] == MPOHexTAG.TAG_APP1[1]){
mPtrs.add(i*buffer.length + j);
pos = i*buffer.length + j;
E1_OK = true;
break;
}
}
}
if((loopMod > 0) && (!E1_OK)){
//1024余数文件块
fin.read(buffer, 0, loopMod);
for(int j = 0; j < 1024 - 1; j++){
//FF E1
if(buffer[j] == MPOHexTAG.TAG_APP1[0] && buffer[j+1] == MPOHexTAG.TAG_APP1[1]){
mPtrs.add(loopCount*buffer.length + j);
pos = loopCount*buffer.length + j;
E1_OK = true;
break;
}
}
}
fin.seek(pos + 2);
byte[] tmp = new byte[2];
fin.read(tmp, 0, 2);
len = BaselineUtls.Byte2UInt(tmp, MPOHexTAG.BIG_ENDIAN);
res = new byte[len];
fin.seek(pos);
fin.read(res, 0, len);
fin.seek(0);
if(DEBUG){//打印LOG
String str = "";
for(int i = 0; i < mPtrs.size(); i++){
str += " " + mPtrs.get(i).toString();
}
Log.w(TAG, "[getMarkerList]PTRs : " + str);
}
}catch(IOException exp){
exp.printStackTrace();
return new byte[0];
}
System.gc();
return res;
}
}