EXIF格式分析及通过XML处理
下面的代码片断(Borland C++ Builder)实现了从EXIF数据到XML的转换:
//---------------------------------------------------------------------------
#include <pshpack1.h>
typedef struct {
WORD EntryTag;
WORD EntryType;
DWORD EntrySize;
DWORD EntryValue;
} TIFDEntry;
#include <poppack.h>
//---------------------------------------------------------------------------
BYTE * __fastcall TExifXML::GetIFD(_di_IXMLNode aNode, BYTE * aTIFFHeader, int aPosition, AnsiString aName)
{
_di_IXMLNode pIFD = aNode->AddChild( "IFD" );
if ( aName != "" )
pIFD->Attributes["name"] = aName;
BYTE * p = aTIFFHeader + aPosition;
WORD nWord;
memcpy( &nWord, p, sizeof ( nWord ) );
p += sizeof ( nWord );
_di_IXMLNode pChild = pIFD->AddChild( "Count" );
pChild->Text = Format( "0x%X", ARRAYOFCONST( ( ( int )nWord ) ) );
TIFDEntry ent;
_di_IXMLNode pEntry;
BYTE * pTemp;
for ( int i = nWord; i > 0; --i )
{
memcpy( &ent, p, sizeof ( ent ) );
p += sizeof ( ent );
pEntry = pIFD->AddChild( "Entry" );
pChild = pEntry->AddChild( "Tag" );
pChild->Text = Format( "0x%X", ARRAYOFCONST( ( ( int )ent.EntryTag ) ) );
pChild = pEntry->AddChild( "Type" );
pChild->Text = IntToStr( ent.EntryType );
pChild = pEntry->AddChild( "Size" );
pChild->Text = Format( "0x%X", ARRAYOFCONST( ( ( int )ent.EntrySize ) ) );
pChild = pEntry->AddChild( "Value" );
switch ( ent.EntryType ) {
case 1 : // BYTE
if ( ent.EntrySize == 1 )
pChild->Text = Format( "0x%.02X", ARRAYOFCONST( ( ( int )( BYTE )ent.EntryValue ) ) );
else
throw Exception( "Unsupported!" );
break;
case 2 : // ASCII
if ( ent.EntrySize <= 4 )
pChild->Text = reinterpret_cast<LPSTR>( &ent.EntryValue );
else
pChild->Text = reinterpret_cast<LPSTR>( aTIFFHeader + ent.EntryValue );
break;
case 3 : // SHORT
if ( ent.EntrySize == 1 )
pChild->Text = Format( "0x%.04X", ARRAYOFCONST( ( ( int )( WORD )ent.EntryValue ) ) );
else
throw Exception( "Unsupported!" );
break;
case 5 : // RATIONAL
pChild->Text = FloatToStr( *reinterpret_cast<DWORD *>( aTIFFHeader + ent.EntryValue )
/ ( double )( *reinterpret_cast<DWORD *>( aTIFFHeader + ent.EntryValue + sizeof ( DWORD ) ) ) );
break;
case 7 : // UNDEFINED
if ( ent.EntrySize <= 4 )
pTemp = reinterpret_cast<BYTE *>( &ent.EntryValue );
else
pTemp = aTIFFHeader + ent.EntryValue;
pChild->Text = "";
for ( int j = 0; j < ( int )ent.EntrySize; ++j )
{
pChild->Text = pChild->Text
+ Format( " 0x%.02X", ARRAYOFCONST( ( ( int )( BYTE )( *pTemp ) ) ) );
pTemp++;
if ( j % 16 == 15 )
pChild->Text = pChild->Text + "\r\n";
}
break;
case 9 : // SLONG
if ( ent.EntrySize == 1 )
pChild->Text = IntToStr( ent.EntryValue );
else
throw Exception( "Unsupported!" );
break;
case 10: // SRATIONAL
pChild->Text = FloatToStr( *reinterpret_cast<int *>( aTIFFHeader + ent.EntryValue )
/ ( double )( *reinterpret_cast<int *>( aTIFFHeader + ent.EntryValue + sizeof ( int ) ) ) );
break;
default: // LONG & other unknown type
pChild->Text = Format( "0x%.08X", ARRAYOFCONST( ( ( int )ent.EntryValue ) ) );
break;
}
switch ( ent.EntryTag ) {
case 0x8769 : // Exif IFD
GetIFD( aNode, aTIFFHeader, ent.EntryValue, "EXIF" );
break;
case 0x8805 : // GPS IFD
GetIFD( aNode, aTIFFHeader, ent.EntryValue, "GPS" );
break;
case 0xA005 : // Interoperability IFD
GetIFD( aNode, aTIFFHeader, ent.EntryValue, "InterOp" );
break;
}
}
return p;
}
//---------------------------------------------------------------------------
void __fastcall TExifXML::GetTIFFHeader(_di_IXMLNode aNode, BYTE * aTIFFHeader)
{
BYTE * p = aTIFFHeader;
char sByteOrder[3];
memcpy( sByteOrder, p, 2 );
p += 2;
sByteOrder[2] = 0;
_di_IXMLNode pChild = aNode->AddChild( "ByteOrder" );
pChild->Text = sByteOrder;
WORD nFlag;
memcpy( &nFlag, p, sizeof ( nFlag ) );
p += sizeof ( nFlag );
pChild = aNode->AddChild( "Flag" );
pChild->Text = Format( "0x%.04X", ARRAYOFCONST( ( ( int )nFlag ) ) );
DWORD nPointer;
memcpy( &nPointer, p, sizeof ( nPointer ) );
int i = 0;
while ( nPointer > 0 )
{
p = GetIFD( aNode, aTIFFHeader, nPointer, AnsiString( "IFD" ) + IntToStr( i++ ) );
if ( !p )
break;
memcpy( &nPointer, p, sizeof ( nPointer ) );
}
}
//---------------------------------------------------------------------------
int __fastcall TExifXML::LoadFromStream(TStream * aStream)
{
if ( !FXMLDoc )
throw Exception( "XMLDoc property is null!" );
TMauto_ptr<TMemoryStream> ms( new TMemoryStream( ) );
ms->CopyFrom( aStream, aStream->Size );
ms->Seek( 0, soFromBeginning );
FXMLDoc->FileName = "";
FXMLDoc->Active = true;
FXMLDoc->Version = "1.0";
FXMLDoc->Encoding = "GB2312";
_di_IXMLNode pNode = FXMLDoc->AddChild( "ExifAPP1" );
_di_IXMLNode pChild = pNode->AddChild( "ExifID" );
char sExifID[6];
ms->Read( sExifID, 6 );
pChild->Text = sExifID;
pChild = pNode->AddChild( "TIFFHeader" );
BYTE * pHeader = static_cast<BYTE *>( ms->Memory ) + ( int )ms->Position;
GetTIFFHeader( pChild, pHeader );
return ms->Size;
}
其中FXMLDoc是一个TXMLDocument控件,用于生成XML。LoadFromStream方法读入的内容为JPEG APP1这个Marker Segment的内容(注意,不是JPEG文件)。GetTIFFHeader方法用于读出TIFFHeader的内容,包括Image File Header和IFD链表。GetIFD则是用于解读IFD的具体内容,其中包括对EXIF的三个扩充IFD的递归解读,并且其中包含了将各种数据类型转换为字符串的部分,特别是对不定长的UNDEFINED类型的处理(其结果见下面转换后的XML)。
(待续)