EXIF格式分析及通过XML处理(4)

    技术2022-05-11  90

    EXIF格式分析及通过XML处理

     

    猛禽[Mental Studio](个人专栏)(BLOG)

    http://mental.mentsu.com

     

    下面的代码片断(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控件,用于生成XMLLoadFromStream方法读入的内容为JPEG APP1这个Marker Segment的内容(注意,不是JPEG文件)。GetTIFFHeader方法用于读出TIFFHeader的内容,包括Image File HeaderIFD链表。GetIFD则是用于解读IFD的具体内容,其中包括对EXIF的三个扩充IFD的递归解读,并且其中包含了将各种数据类型转换为字符串的部分,特别是对不定长的UNDEFINED类型的处理(其结果见下面转换后的XML)。

    (待续)


    最新回复(0)