diff options
author | Hubert Figuière <hub@figuiere.net> | 2013-06-29 22:05:20 -0400 |
---|---|---|
committer | Hubert Figuière <hub@figuiere.net> | 2013-06-29 22:08:01 -0400 |
commit | 4652015fe779e12fb06ff8fa56bf70e373cd3894 (patch) | |
tree | de8d78fb704c3eb86802c07eace17eaea0f2ddde /XMPFiles/source/FormatSupport | |
parent | 81a4c6bcb1879cb321246590faca595e9746f8e5 (diff) |
Update to XMP SDK CC 2013.06
Diffstat (limited to 'XMPFiles/source/FormatSupport')
32 files changed, 2512 insertions, 431 deletions
diff --git a/XMPFiles/source/FormatSupport/ASF_Support.cpp b/XMPFiles/source/FormatSupport/ASF_Support.cpp index 35adce6..709aea5 100644 --- a/XMPFiles/source/FormatSupport/ASF_Support.cpp +++ b/XMPFiles/source/FormatSupport/ASF_Support.cpp @@ -30,11 +30,11 @@ int IsEqualGUID ( const GUID& guid1, const GUID& guid2 ) } #endif -ASF_Support::ASF_Support() : legacyManager(0), posFileSizeInfo(0) {} +ASF_Support::ASF_Support() : legacyManager(0),progressTracker(0), posFileSizeInfo(0) {} -ASF_Support::ASF_Support ( ASF_LegacyManager* _legacyManager ) : posFileSizeInfo(0) +ASF_Support::ASF_Support ( ASF_LegacyManager* _legacyManager,XMP_ProgressTracker* _progressTracker ) + :legacyManager(_legacyManager),progressTracker(_progressTracker), posFileSizeInfo(0) { - legacyManager = _legacyManager; } ASF_Support::~ASF_Support() @@ -645,7 +645,11 @@ bool ASF_Support::WriteHeaderObject ( XMP_IO* sourceRef, XMP_IO* destRef, const // if we are operating on the same file (in-place update), place pointer before writing if ( sourceRef == destRef ) destRef->Seek ( object.pos, kXMP_SeekFromStart ); - + if ( this->progressTracker != 0 ) + { + XMP_Assert ( this->progressTracker->WorkInProgress() ); + this->progressTracker->AddTotalWork ( (float)header.size() ); + } // write header destRef->Write ( header.c_str(), header.size() ); @@ -1139,7 +1143,7 @@ void ASF_LegacyManager::ImportLegacy ( SXMPMeta* xmp ) FromUTF16 ( (UTF16Unit*)fields[fieldDescription].c_str(), (fields[fieldDescription].size() / 2), &utf8, false ); if ( ! utf8.empty() ) xmp->SetLocalizedText ( kXMP_NS_DC, "description", "", "x-default", utf8.c_str(), kXMP_DeleteExisting ); - if ( ! utf8.empty() ) xmp->SetProperty ( kXMP_NS_XMP_Rights, "WebStatement", fields[fieldCopyrightURL].c_str(), kXMP_DeleteExisting ); + if ( ! fields[fieldCopyrightURL].empty() ) xmp->SetProperty ( kXMP_NS_XMP_Rights, "WebStatement", fields[fieldCopyrightURL].c_str(), kXMP_DeleteExisting ); #if ! Exclude_LicenseURL_Recon if ( ! fields[fieldLicenseURL].empty() ) xmp->SetProperty ( kXMP_NS_XMP_Rights, "Certificate", fields[fieldLicenseURL].c_str(), kXMP_DeleteExisting ); diff --git a/XMPFiles/source/FormatSupport/ASF_Support.hpp b/XMPFiles/source/FormatSupport/ASF_Support.hpp index 9d9060b..3e170e3 100644 --- a/XMPFiles/source/FormatSupport/ASF_Support.hpp +++ b/XMPFiles/source/FormatSupport/ASF_Support.hpp @@ -16,6 +16,7 @@ #include "XMPFiles/source/XMPFiles_Impl.hpp" #include "XMPFiles/source/FormatSupport/Reconcile_Impl.hpp" #include "source/XIO.hpp" +#include "source/XMP_ProgressTracker.hpp" // currently exclude LicenseURL from reconciliation #define Exclude_LicenseURL_Recon 1 @@ -187,7 +188,7 @@ public: }; ASF_Support(); - ASF_Support ( ASF_LegacyManager* legacyManager ); + ASF_Support ( ASF_LegacyManager* legacyManager, XMP_ProgressTracker* _progressTracker); virtual ~ASF_Support(); long OpenASF ( XMP_IO* fileRef, ObjectState & inOutObjectState ); @@ -215,6 +216,7 @@ public: private: ASF_LegacyManager* legacyManager; + XMP_ProgressTracker* progressTracker;//not owned by ASF_Support XMP_Uns64 posFileSizeInfo; static std::string ReplaceString ( std::string& operand, std::string& str, int offset, int count ); diff --git a/XMPFiles/source/FormatSupport/ID3_Support.cpp b/XMPFiles/source/FormatSupport/ID3_Support.cpp index 2bc9a1f..e33e39e 100644 --- a/XMPFiles/source/FormatSupport/ID3_Support.cpp +++ b/XMPFiles/source/FormatSupport/ID3_Support.cpp @@ -18,32 +18,370 @@ #include <vector> -// ================================================================================================= - #define MIN(a,b) ((a) < (b) ? (a) : (b)) +namespace ID3_Support { + +// ================================================================================================= + ID3GenreMap* kMapID3GenreCodeToName = 0; // Map from a code like "21" or "RX" to the full name. ID3GenreMap* kMapID3GenreNameToCode = 0; // Map from the full name to a code like "21" or "RX". +static size_t numberedGenreCount = 0; // Set in InitializeGlobals, used in ID3v1Tag::read and write. + +struct GenreInfo { const char * code; const char * name; }; + +static const GenreInfo kAbbreviatedGenres[] = { // ID3 v3 or v4 genre abbreviations. + { "RX", "Remix" }, + { "CR", "Cover" }, + { 0, 0 } +}; + +static const GenreInfo kNumberedGenres[] = { // Numeric genre codes from ID3 v1, complete range of 0..125. + { "0", "Blues" }, + { "1", "Classic Rock" }, + { "2", "Country" }, + { "3", "Dance" }, + { "4", "Disco" }, + { "5", "Funk" }, + { "6", "Grunge" }, + { "7", "Hip-Hop" }, + { "8", "Jazz" }, + { "9", "Metal" }, + { "10", "New Age" }, + { "11", "Oldies" }, + { "12", "Other" }, + { "13", "Pop" }, + { "14", "R&B" }, + { "15", "Rap" }, + { "16", "Reggae" }, + { "17", "Rock" }, + { "18", "Techno" }, + { "19", "Industrial" }, + { "20", "Alternative" }, + { "21", "Ska" }, + { "22", "Death Metal" }, + { "23", "Pranks" }, + { "24", "Soundtrack" }, + { "25", "Euro-Techno" }, + { "26", "Ambient" }, + { "27", "Trip-Hop" }, + { "28", "Vocal" }, + { "29", "Jazz+Funk" }, + { "30", "Fusion" }, + { "31", "Trance" }, + { "32", "Classical" }, + { "33", "Instrumental" }, + { "34", "Acid" }, + { "35", "House" }, + { "36", "Game" }, + { "37", "Sound Clip" }, + { "38", "Gospel" }, + { "39", "Noise" }, + { "40", "AlternRock" }, + { "41", "Bass" }, + { "42", "Soul" }, + { "43", "Punk" }, + { "44", "Space" }, + { "45", "Meditative" }, + { "46", "Instrumental Pop" }, + { "47", "Instrumental Rock" }, + { "48", "Ethnic" }, + { "49", "Gothic" }, + { "50", "Darkwave" }, + { "51", "Techno-Industrial" }, + { "52", "Electronic" }, + { "53", "Pop-Folk" }, + { "54", "Eurodance" }, + { "55", "Dream" }, + { "56", "Southern Rock" }, + { "57", "Comedy" }, + { "58", "Cult" }, + { "59", "Gangsta" }, + { "60", "Top 40" }, + { "61", "Christian Rap" }, + { "62", "Pop/Funk" }, + { "63", "Jungle" }, + { "64", "Native American" }, + { "65", "Cabaret" }, + { "66", "New Wave" }, + { "67", "Psychadelic" }, + { "68", "Rave" }, + { "69", "Showtunes" }, + { "70", "Trailer" }, + { "71", "Lo-Fi" }, + { "72", "Tribal" }, + { "73", "Acid Punk" }, + { "74", "Acid Jazz" }, + { "75", "Polka" }, + { "76", "Retro" }, + { "77", "Musical" }, + { "78", "Rock & Roll" }, + { "79", "Hard Rock" }, + { "80", "Folk" }, + { "81", "Folk-Rock" }, + { "82", "National Folk" }, + { "83", "Swing" }, + { "84", "Fast Fusion" }, + { "85", "Bebob" }, + { "86", "Latin" }, + { "87", "Revival" }, + { "88", "Celtic" }, + { "89", "Bluegrass" }, + { "90", "Avantgarde" }, + { "91", "Gothic Rock" }, + { "92", "Progressive Rock" }, + { "93", "Psychedelic Rock" }, + { "94", "Symphonic Rock" }, + { "95", "Slow Rock" }, + { "96", "Big Band" }, + { "97", "Chorus" }, + { "98", "Easy Listening" }, + { "99", "Acoustic" }, + { "100", "Humour" }, + { "101", "Speech" }, + { "102", "Chanson" }, + { "103", "Opera" }, + { "104", "Chamber Music" }, + { "105", "Sonata" }, + { "106", "Symphony" }, + { "107", "Booty Bass" }, + { "108", "Primus" }, + { "109", "Porn Groove" }, + { "110", "Satire" }, + { "111", "Slow Jam" }, + { "112", "Club" }, + { "113", "Tango" }, + { "114", "Samba" }, + { "115", "Folklore" }, + { "116", "Ballad" }, + { "117", "Power Ballad" }, + { "118", "Rhythmic Soul" }, + { "119", "Freestyle" }, + { "120", "Duet" }, + { "121", "Punk Rock" }, + { "122", "Drum Solo" }, + { "123", "A capella" }, // ! Should be Acapella, keep space for compatibility with old code. + { "124", "Euro-House" }, + { "125", "Dance Hall" }, + { 0, 0 } +}; + // ================================================================================================= -bool ID3_Support::InitializeGlobals() +bool InitializeGlobals() { + + kMapID3GenreCodeToName = new ID3GenreMap; + if ( kMapID3GenreCodeToName == 0 ) return false; + kMapID3GenreNameToCode = new ID3GenreMap; + if ( kMapID3GenreNameToCode == 0 ) return false; + + ID3GenreMap::value_type newValue; + + size_t i; + + for ( i = 0; kNumberedGenres[i].code != 0; ++i ) { + XMP_Assert ( (long)i == strtol ( kNumberedGenres[i].code, 0, 10 ) ); + ID3GenreMap::value_type code2Name ( kNumberedGenres[i].code, kNumberedGenres[i].name ); + kMapID3GenreCodeToName->insert ( kMapID3GenreCodeToName->end(), code2Name ); + ID3GenreMap::value_type name2Code ( kNumberedGenres[i].name, kNumberedGenres[i].code ); + kMapID3GenreNameToCode->insert ( kMapID3GenreNameToCode->end(), name2Code ); + } + + numberedGenreCount = i; // Used in ID3v1Tag::read and write. + + for ( i = 0; kAbbreviatedGenres[i].code != 0; ++i ) { + ID3GenreMap::value_type code2Name ( kAbbreviatedGenres[i].code, kAbbreviatedGenres[i].name ); + kMapID3GenreCodeToName->insert ( kMapID3GenreCodeToName->end(), code2Name ); + ID3GenreMap::value_type name2Code ( kAbbreviatedGenres[i].name, kAbbreviatedGenres[i].code ); + kMapID3GenreNameToCode->insert ( kMapID3GenreNameToCode->end(), name2Code ); + } + return true; + +} // InitializeGlobals + +// ================================================================================================= + +void TerminateGlobals() +{ + delete kMapID3GenreCodeToName; + delete kMapID3GenreNameToCode; + kMapID3GenreCodeToName = kMapID3GenreNameToCode = 0; +} + +// ================================================================================================= +// GenreUtils +// ================================================================================================= + +const char * GenreUtils::FindGenreName ( const std::string & code ) +{ + // Lookup a genre code and return its name if known, otherwise 0. + + const char * name = 0; + ID3GenreMap::iterator mapPos = kMapID3GenreCodeToName->find ( code.c_str() ); + if ( mapPos != kMapID3GenreCodeToName->end() ) name = mapPos->second; + return name; + +} + +// ================================================================================================= + +const char * GenreUtils::FindGenreCode ( const std::string & name ) +{ + // Lookup a genre name and return its code if known, otherwise 0. + + const char * code = 0; + ID3GenreMap::iterator mapPos = kMapID3GenreNameToCode->find ( name.c_str() ); + if ( mapPos != kMapID3GenreNameToCode->end() ) code = mapPos->second; + return code; + +} + +// ================================================================================================= + +static void StripOutsideSpaces ( std::string * value ) +{ + size_t length = value->size(); + size_t first, last; + + for ( first = 0; ((first < length) && ((*value)[first] == ' ')); ++first ) {} + if ( first == length ) { value->erase(); return; } + XMP_Assert ( (first < length) && ((*value)[first] != ' ') ); + + for ( last = length-1; ((last > first) && ((*value)[last] == ' ')); --last ) {} + if ( (first == 0) && (last == length-1) ) return; + + size_t newLen = last - first + 1; + if ( newLen < length ) *value = value->substr ( first, newLen ); + +} + +// ================================================================================================= + +void GenreUtils::ConvertGenreToXMP ( const char * id3Genre, std::string * xmpGenre ) +{ + // If the first character of TCON is not '(' then the entire TCON value is taken as the genre + // name and the suffix is empty. + // + // If the first character of TCON is '(' then the string up to ')' (or the end) is taken as the + // coded genre name. The rest of the TCON value after ')' is taken as the suffix. + // + // If the coded name is known then the corresponsing full name is used as the genre name, with + // no parens. + // + // If the coded name is not known then the coded name with parens is used as the genre name. + // + // The value of xmpDM:genre begins with the genre name. If the suffix is not empty we append + // "; " and the suffix. The known coded genre names currently do not use semicolon. + // + // Keeping the parens when importing unknown coded names might seem odd. But it preserves the + // ID3 syntax when exporting. Otherwise we would import "(XX)" and export "XX". We don't add + // parens all the time on export, that would import "Blues/R&B" and export "(Blues/R&B)". + + xmpGenre->erase(); + size_t id3Length = strlen ( id3Genre ); + if ( id3Length == 0 ) return; + + if ( id3Genre[0] != '(' ) { + // No left paren, take the whole TCON value as the XMP value. + xmpGenre->assign ( id3Genre, id3Length ); + StripOutsideSpaces ( xmpGenre ); + return; + } + + // The first character of TCON is '(', process the coded part and the suffix. + + size_t codeEnd; + std::string genreCode, suffix; + + for ( codeEnd = 1; ((codeEnd < id3Length) && (id3Genre[codeEnd] != ')')); ++codeEnd ) {} + genreCode.assign ( &id3Genre[1], codeEnd-1 ); + if ( codeEnd < id3Length ) suffix.assign ( &id3Genre[codeEnd+1], id3Length-codeEnd-1 ); + + StripOutsideSpaces ( &genreCode ); + StripOutsideSpaces ( &suffix ); + + if ( genreCode.empty() ) { + + (*xmpGenre) = suffix; // Degenerate case of "()suffix", treat as if "suffix". + + } else { + + const char * fullName = FindGenreName ( genreCode ); + + if ( fullName != 0 ) { + (*xmpGenre) = fullName; + } else { + (*xmpGenre) = '('; + (*xmpGenre) += genreCode; + (*xmpGenre) += ')'; + } + + if ( ! suffix.empty() ) { + (*xmpGenre) += "; "; + (*xmpGenre) += suffix; + } + + } + } // ================================================================================================= -void ID3_Support::TerminateGlobals() +void GenreUtils::ConvertGenreToID3 ( const char * xmpGenre, std::string * id3Genre ) { - // nothing yet + // The genre name is the xmpDM:genre value up to ';', with spaces at the front or back removed. + // The suffix is everything after ';', also with spaces at the front or back removed. + // + // If the genre name is known, it is replaced by the coded name in parens. + // + // The TCON value is the genre name plus the suffix. If the genre name does not end in ')' then + // a space is inserted. + + id3Genre->erase(); + size_t xmpLength = strlen ( xmpGenre ); + if ( xmpLength == 0 ) return; + + size_t nameEnd; + std::string genreName, suffix; + + for ( nameEnd = 0; ((nameEnd < xmpLength) && (xmpGenre[nameEnd] != ';')); ++nameEnd ) {} + genreName.assign ( xmpGenre, nameEnd ); + if ( nameEnd < xmpLength ) suffix.assign ( &xmpGenre[nameEnd+1], xmpLength-nameEnd-1 ); + + StripOutsideSpaces ( &genreName ); + StripOutsideSpaces ( &suffix ); + + if ( genreName.empty() ) { + + (*id3Genre) = suffix; // Degenerate case of "; suffix", treat as if "suffix". + + } else { + + const char * codedName = FindGenreCode ( genreName ); + if ( codedName != 0 ) { + genreName = '('; + genreName += codedName; + genreName += ')'; + } + + (*id3Genre) = genreName; + if ( ! suffix.empty() ) { + if ( genreName[genreName.size()-1] != ')' ) (*id3Genre) += ' '; + (*id3Genre) += suffix; + } + + } + } // ================================================================================================= // ID3Header // ================================================================================================= -bool ID3_Support::ID3Header::read ( XMP_IO* file ) +bool ID3Header::read ( XMP_IO* file ) { XMP_Assert ( sizeof(fields) == kID3_TagHeaderSize ); @@ -66,10 +404,10 @@ bool ID3_Support::ID3Header::read ( XMP_IO* file ) // ================================================================================================= -void ID3_Support::ID3Header::write ( XMP_IO* file, XMP_Int64 tagSize ) +void ID3Header::write ( XMP_IO* file, XMP_Int64 tagSize ) { - XMP_Assert ( (kID3_TagHeaderSize <= tagSize) && (tagSize < 256*1024*1024) ); // 256 MB limit due to synching. + XMP_Assert ( ((XMP_Int64)kID3_TagHeaderSize <= tagSize) && (tagSize < 256*1024*1024) ); // 256 MB limit due to synching. XMP_Uns32 synchSize = int32ToSynch ( (XMP_Uns32)tagSize - kID3_TagHeaderSize ); PutUns32BE ( synchSize, &this->fields[ID3Header::o_size] ); @@ -83,7 +421,7 @@ void ID3_Support::ID3Header::write ( XMP_IO* file, XMP_Int64 tagSize ) #define frameDefaults id(0), flags(0), content(0), contentSize(0), active(true), changed(false) -ID3_Support::ID3v2Frame::ID3v2Frame() : frameDefaults +ID3v2Frame::ID3v2Frame() : frameDefaults { XMP_Assert ( sizeof(fields) == kV23_FrameHeaderSize ); // Only need to do this in one place. memset ( this->fields, 0, kV23_FrameHeaderSize ); @@ -91,7 +429,7 @@ ID3_Support::ID3v2Frame::ID3v2Frame() : frameDefaults // ================================================================================================= -ID3_Support::ID3v2Frame::ID3v2Frame ( XMP_Uns32 id ) : frameDefaults +ID3v2Frame::ID3v2Frame ( XMP_Uns32 id ) : frameDefaults { memset ( this->fields, 0, kV23_FrameHeaderSize ); this->id = id; @@ -100,7 +438,7 @@ ID3_Support::ID3v2Frame::ID3v2Frame ( XMP_Uns32 id ) : frameDefaults // ================================================================================================= -void ID3_Support::ID3v2Frame::release() +void ID3v2Frame::release() { if ( this->content != 0 ) delete this->content; this->content = 0; @@ -109,7 +447,7 @@ void ID3_Support::ID3v2Frame::release() // ================================================================================================= -void ID3_Support::ID3v2Frame::setFrameValue ( const std::string& rawvalue, bool needDescriptor, +void ID3v2Frame::setFrameValue ( const std::string& rawvalue, bool needDescriptor, bool utf16, bool isXMPPRIVFrame, bool needEncodingByte ) { @@ -170,7 +508,7 @@ void ID3_Support::ID3v2Frame::setFrameValue ( const std::string& rawvalue, bool // ================================================================================================= -XMP_Int64 ID3_Support::ID3v2Frame::read ( XMP_IO* file, XMP_Uns8 majorVersion ) +XMP_Int64 ID3v2Frame::read ( XMP_IO* file, XMP_Uns8 majorVersion ) { XMP_Assert ( (2 <= majorVersion) && (majorVersion <= 4) ); @@ -213,7 +551,7 @@ XMP_Int64 ID3_Support::ID3v2Frame::read ( XMP_IO* file, XMP_Uns8 majorVersion ) // ================================================================================================= -void ID3_Support::ID3v2Frame::write ( XMP_IO* file, XMP_Uns8 majorVersion ) +void ID3v2Frame::write ( XMP_IO* file, XMP_Uns8 majorVersion ) { XMP_Assert ( (2 <= majorVersion) && (majorVersion <= 4) ); @@ -236,7 +574,7 @@ void ID3_Support::ID3v2Frame::write ( XMP_IO* file, XMP_Uns8 majorVersion ) // ================================================================================================= -bool ID3_Support::ID3v2Frame::advancePastCOMMDescriptor ( XMP_Int32& pos ) +bool ID3v2Frame::advancePastCOMMDescriptor ( XMP_Int32& pos ) { if ( (this->contentSize - pos) <= 3 ) return false; // silent error, no room left behing language tag @@ -267,7 +605,7 @@ bool ID3_Support::ID3v2Frame::advancePastCOMMDescriptor ( XMP_Int32& pos ) // ================================================================================================= -bool ID3_Support::ID3v2Frame::getFrameValue ( XMP_Uns8 majorVersion, XMP_Uns32 logicalID, std::string* utf8string ) +bool ID3v2Frame::getFrameValue ( XMP_Uns8 majorVersion, XMP_Uns32 logicalID, std::string* utf8string ) { XMP_Assert ( (this->content != 0) && (this->contentSize >= 0) && (this->contentSize < 20*1024*1024) ); @@ -349,9 +687,9 @@ bool ID3_Support::ID3v2Frame::getFrameValue ( XMP_Uns8 majorVersion, XMP_Uns32 l // ID3v1Tag // ================================================================================================= -bool ID3_Support::ID3v1Tag::read ( XMP_IO* file, SXMPMeta* meta ) +bool ID3v1Tag::read ( XMP_IO* file, SXMPMeta* meta ) { - // returns returns true, if ID3v1 (or v1.1) exists, otherwise false, sets XMP properties en route + // Returns true if ID3v1 (or v1.1) exists, otherwise false, sets XMP properties en route. if ( file->Length() <= 128 ) return false; // ensure sufficient room file->Seek ( -128, kXMP_SeekFromEnd ); @@ -410,8 +748,13 @@ bool ID3_Support::ID3v1Tag::read ( XMP_IO* file, SXMPMeta* meta ) } XMP_Uns8 genreNo = XIO::ReadUns8 ( file ); - if ( genreNo < 127 ) { - meta->SetProperty ( kXMP_NS_DM, "genre", Genres[genreNo] ); + if ( genreNo < numberedGenreCount ) { + meta->SetProperty ( kXMP_NS_DM, "genre", kNumberedGenres[genreNo].name ); + } else { + char buffer[4]; // AUDIT: Big enough for UInt8. + snprintf ( buffer, 4, "%d", genreNo ); + XMP_Assert ( strlen(buffer) == 3 ); // Should be in the range 126..255. + meta->SetProperty ( kXMP_NS_DM, "genre", buffer ); } return true; // ID3Tag found @@ -420,7 +763,25 @@ bool ID3_Support::ID3v1Tag::read ( XMP_IO* file, SXMPMeta* meta ) // ================================================================================================= -void ID3_Support::ID3v1Tag::write ( XMP_IO* file, SXMPMeta* meta ) +static inline bool GetDecimalUns32 ( const char * str, XMP_Uns32 * bin ) +{ + XMP_Assert ( bin != 0 ); + if ( (str == 0) || (str[0] == 0) ) return false; + + *bin = 0; + for ( size_t i = 0; str[i] != 0; ++i ) { + char ch = str[i]; + if ( (ch < '0') || (ch > '9') ) return false; + *bin = (*bin * 10) + (ch - '0'); + } + + return true; + +} + +// ================================================================================================= + +void ID3v1Tag::write ( XMP_IO* file, SXMPMeta* meta ) { std::string zeros ( 128, '\0' ); @@ -470,21 +831,23 @@ void ID3_Support::ID3v1Tag::write ( XMP_IO* file, SXMPMeta* meta ) if ( meta->GetProperty ( kXMP_NS_DM, "genre", &utf8, 0 ) ) { - XMP_Uns8 genreNo = 0; - - int i; - const char* genreCString = utf8.c_str(); - for ( i = 0; i < 127; ++i ) { - if ( (strlen(genreCString) == strlen(Genres[i])) && //fixing buggy stricmp behaviour on PPC - (stricmp ( genreCString, Genres[i] ) == 0 ) ) { - genreNo = i; // found - break; + // Write the first genre code as a UInt8. + size_t nameEnd; + std::string name; + + for ( nameEnd = 0; ((nameEnd < utf8.size()) && (utf8[nameEnd] != ';')); ++nameEnd ) {} + name.assign ( utf8.c_str(), nameEnd ); + const char * code = GenreUtils::FindGenreCode ( name ); + + if ( code != 0 ) { + XMP_Uns32 value; + bool ok = GetDecimalUns32 ( code, &value ); + if ( ok && (value <= 255) ) { + file->Seek ( (-128 + 127), kXMP_SeekFromEnd ); + XIO::WriteUns8 ( file, (XMP_Uns8)value ); } } - file->Seek ( (-128 + 127), kXMP_SeekFromEnd ); - XIO::WriteUns8 ( file, genreNo ); - } if ( meta->GetProperty ( kXMP_NS_DM, "trackNumber", &utf8, kXMP_NoOptions ) ) { @@ -502,3 +865,7 @@ void ID3_Support::ID3v1Tag::write ( XMP_IO* file, SXMPMeta* meta ) } } // ID3v1Tag::write + +// ================================================================================================= + +}; // namespace ID3_Support diff --git a/XMPFiles/source/FormatSupport/ID3_Support.hpp b/XMPFiles/source/FormatSupport/ID3_Support.hpp index 43b917d..5228fd7 100644 --- a/XMPFiles/source/FormatSupport/ID3_Support.hpp +++ b/XMPFiles/source/FormatSupport/ID3_Support.hpp @@ -42,137 +42,6 @@ namespace ID3_Support { - // Genres - static char Genres[128][32] = { - "Blues", // 0 - "Classic Rock", // 1 - "Country", // 2 - "Dance", - "Disco", - "Funk", - "Grunge", - "Hip-Hop", - "Jazz", // 8 - "Metal", - "New Age", // 10 - "Oldies", - "Other", // 12 - "Pop", - "R&B", - "Rap", - "Reggae", // 16 - "Rock", // 17 - "Techno", - "Industrial", - "Alternative", - "Ska", - "Death Metal", - "Pranks", - "Soundtrack", // 24 - "Euro-Techno", - "Ambient", - "Trip-Hop", - "Vocal", - "Jazz+Funk", - "Fusion", - "Trance", - "Classical", // 32 - "Instrumental", - "Acid", - "House", - "Game", - "Sound Clip", - "Gospel", - "Noise", - "AlternRock", - "Bass", - "Soul", //42 - "Punk", - "Space", - "Meditative", - "Instrumental Pop", - "Instrumental Rock", - "Ethnic", - "Gothic", - "Darkwave", - "Techno-Industrial", - "Electronic", - "Pop-Folk", - "Eurodance", - "Dream", - "Southern Rock", - "Comedy", - "Cult", - "Gangsta", - "Top 40", - "Christian Rap", - "Pop/Funk", - "Jungle", - "Native American", - "Cabaret", - "New Wave", // 66 - "Psychadelic", - "Rave", - "Showtunes", - "Trailer", - "Lo-Fi", - "Tribal", - "Acid Punk", - "Acid Jazz", - "Polka", - "Retro", - "Musical", - "Rock & Roll", - "Hard Rock", - "Folk", // 80 - "Folk-Rock", - "National Folk", - "Swing", - "Fast Fusion", - "Bebob", - "Latin", - "Revival", - "Celtic", - "Bluegrass", // 89 - "Avantgarde", - "Gothic Rock", - "Progressive Rock", - "Psychedelic Rock", - "Symphonic Rock", - "Slow Rock", - "Big Band", - "Chorus", - "Easy Listening", - "Acoustic", - "Humour", // 100 - "Speech", - "Chanson", - "Opera", - "Chamber Music", - "Sonata", - "Symphony", - "Booty Bass", - "Primus", - "Porn Groove", - "Satire", - "Slow Jam", - "Club", - "Tango", - "Samba", - "Folklore", - "Ballad", - "Power Ballad", - "Rhythmic Soul", - "Freestyle", - "Duet", - "Punk Rock", - "Drum Solo", - "A capella", - "Euro-House", - "Dance Hall", - "Unknown" // 126 - }; - // ============================================================================================= inline XMP_Int32 synchToInt32 ( XMP_Uns32 rawDataBE ) { @@ -191,8 +60,21 @@ namespace ID3_Support { // ============================================================================================= - bool InitializeGlobals(); + bool InitializeGlobals(); // Initialize and terminate the known genre maps. void TerminateGlobals(); + + // ============================================================================================= + + namespace GenreUtils { + + void ConvertGenreToXMP ( const char * id3Genre, std::string * xmpGenre ); + void ConvertGenreToID3 ( const char * xmpGenre, std::string * id3Genre ); + + // Internal utilities, exposed for unit testing: + const char * FindGenreName ( const std::string & code ); + const char * FindGenreCode ( const std::string & name ); + + }; // ============================================================================================= diff --git a/XMPFiles/source/FormatSupport/IFF/Chunk.cpp b/XMPFiles/source/FormatSupport/IFF/Chunk.cpp index a2de741..9e1407f 100644 --- a/XMPFiles/source/FormatSupport/IFF/Chunk.cpp +++ b/XMPFiles/source/FormatSupport/IFF/Chunk.cpp @@ -538,6 +538,52 @@ XMP_Uns64 Chunk::calculateSize( bool setOriginal /*= false*/ ) //----------------------------------------------------------------------------- // +// Chunk::calculateWriteSize(...) +// +// Purpose: Calculate the size of the chunks that are dirty including the size +// of its children +// +//----------------------------------------------------------------------------- + +XMP_Int64 Chunk::calculateWriteSize( ) const +{ + XMP_Int64 size=0; + if (hasChanged()) + { + size+=(sizeof(XMP_Uns32)*2); + if (mChunkMode == CHUNK_LEAF) + { + if ( mSize % 2 == 1 ) + { + // for odd file sizes, a pad byte is written + size+=(mSize+1); + } + else + { + size+=mSize; + } + } + else // mChunkMode == CHUNK_NODE + { + // writes type if defined + if (mChunkId.type != kType_NONE) + { + size+=sizeof(XMP_Uns32); + } + + // calls calculateWriteSize recursively on it's children + for( ConstChunkIterator iter = mChildren.begin(); iter != mChildren.end(); iter++ ) + { + size+=(*iter)->calculateWriteSize( ); + } + } + } + + return size; +} + +//----------------------------------------------------------------------------- +// // Chunk::setOffset(...) // // Purpose: Adjust the offset that this chunk has within the file diff --git a/XMPFiles/source/FormatSupport/IFF/Chunk.h b/XMPFiles/source/FormatSupport/IFF/Chunk.h index 2d170ed..ef2ba47 100644 --- a/XMPFiles/source/FormatSupport/IFF/Chunk.h +++ b/XMPFiles/source/FormatSupport/IFF/Chunk.h @@ -195,6 +195,12 @@ class Chunk : public IChunkData, */ inline void setSize( XMP_Uns64 newSize, bool setOriginal = false ) { mDirty = mSize != newSize; mSize = newSize; mOriginalSize = setOriginal ? newSize : mOriginalSize; } + /** + * Calculate the size of the chunks that are dirty including the size + * of its children + */ + XMP_Int64 calculateWriteSize( ) const; + /** * Calculate the size of this chunks based on its children sizes. * If this chunk has no children then no new size will be calculated. diff --git a/XMPFiles/source/FormatSupport/IFF/ChunkController.cpp b/XMPFiles/source/FormatSupport/IFF/ChunkController.cpp index 786cbd1..2a5a322 100644 --- a/XMPFiles/source/FormatSupport/IFF/ChunkController.cpp +++ b/XMPFiles/source/FormatSupport/IFF/ChunkController.cpp @@ -321,7 +321,7 @@ void ChunkController::parseFile( XMP_IO* stream, XMP_OptionBits* options /* = NU // Purpose: Called by the handler to write back the changes to the file. // //----------------------------------------------------------------------------- -void ChunkController::writeFile( XMP_IO* stream ) +void ChunkController::writeFile( XMP_IO* stream ,XMP_ProgressTracker * progressTracker ) { // @@ -344,11 +344,27 @@ void ChunkController::writeFile( XMP_IO* stream ) // NOTE: the padding bytes can be ignored, as the top-level chunk is always a node, not a leaf. Chunk* lastChild = mRoot->getChildAt(mRoot->numChildren() - 1); XMP_Uns64 newFileSize = lastChild->getOffset() + lastChild->getSize(true); + if ( progressTracker != 0 ) + { + float fileWriteSize=0.0f; + for( XMP_Uns32 i = 0; i < mRoot->numChildren(); i++ ) + { + Chunk* child = mRoot->getChildAt(i); + fileWriteSize+=child->calculateWriteSize( ); + } + XMP_Assert ( progressTracker->WorkInProgress() ); + progressTracker->AddTotalWork ( fileWriteSize ); + } // Move garbage tail after last top-level chunk, // BEFORE the chunks are written -- in case the file shrinks if (mTrailingGarbageSize > 0 && newFileSize != mTrailingGarbageOffset) { + if ( progressTracker != 0 ) + { + XMP_Assert ( progressTracker->WorkInProgress() ); + progressTracker->AddTotalWork ( (float)mTrailingGarbageSize ); + } XIO::Move( stream, mTrailingGarbageOffset, stream, newFileSize, mTrailingGarbageSize ); newFileSize += mTrailingGarbageSize; } diff --git a/XMPFiles/source/FormatSupport/IFF/ChunkController.h b/XMPFiles/source/FormatSupport/IFF/ChunkController.h index 933da36..52dea42 100644 --- a/XMPFiles/source/FormatSupport/IFF/ChunkController.h +++ b/XMPFiles/source/FormatSupport/IFF/ChunkController.h @@ -16,6 +16,7 @@ #include "public/include/XMP_IO.hpp" #include "source/XMP_LibUtils.hpp" +#include "source/XMP_ProgressTracker.hpp" #include "XMPFiles/source/FormatSupport/IFF/ChunkPath.h" #include "XMPFiles/source/FormatSupport/IFF/IChunkBehavior.h" @@ -105,8 +106,9 @@ class ChunkController * 2. write the changed chunks to the file * * @param stream the open [file] stream for writing, the file pointer must be at the beginning + * @param progressTracker Progress tracker to track the file write progress and reporting it to client */ - void writeFile( XMP_IO* stream ); + void writeFile( XMP_IO* stream,XMP_ProgressTracker * progressTracker ); /** * Returns the first (or last) Chunk that matches the passed path. diff --git a/XMPFiles/source/FormatSupport/ISOBaseMedia_Support.hpp b/XMPFiles/source/FormatSupport/ISOBaseMedia_Support.hpp index 115a0e8..dd2fbea 100644 --- a/XMPFiles/source/FormatSupport/ISOBaseMedia_Support.hpp +++ b/XMPFiles/source/FormatSupport/ISOBaseMedia_Support.hpp @@ -33,6 +33,7 @@ namespace ISOMedia { k_mp41 = 0x6D703431UL, // Compatible brand codes k_mp42 = 0x6D703432UL, k_f4v = 0x66347620UL, + k_avc1 = 0x61766331UL, k_qt = 0x71742020UL, k_moov = 0x6D6F6F76UL, // Container Box, no version/flags. @@ -58,6 +59,9 @@ namespace ISOMedia { k_stsc = 0x73747363UL, k_stco = 0x7374636FUL, k_co64 = 0x636F3634UL, + k_dinf = 0x64696E66UL, + k_dref = 0x64726566UL, + k_alis = 0x616C6973UL, k_meta = 0x6D657461UL, // Types for the iTunes metadata boxes. k_ilst = 0x696C7374UL, diff --git a/XMPFiles/source/FormatSupport/MOOV_Support.cpp b/XMPFiles/source/FormatSupport/MOOV_Support.cpp index b9b4996..959df44 100644 --- a/XMPFiles/source/FormatSupport/MOOV_Support.cpp +++ b/XMPFiles/source/FormatSupport/MOOV_Support.cpp @@ -272,6 +272,7 @@ void MOOV_Manager::ParseNestedBoxes ( BoxNode * parentNode, const std::string & case ISOMedia::k_edts : pathSuffix = "/edts"; break; case ISOMedia::k_mdia : pathSuffix = "/mdia"; break; case ISOMedia::k_minf : pathSuffix = "/minf"; break; + case ISOMedia::k_dinf : pathSuffix = "/dinf"; break; case ISOMedia::k_stbl : pathSuffix = "/stbl"; break; } if ( pathSuffix != 0 ) { diff --git a/XMPFiles/source/FormatSupport/MOOV_Support.hpp b/XMPFiles/source/FormatSupport/MOOV_Support.hpp index 814a8bb..1dace2a 100644 --- a/XMPFiles/source/FormatSupport/MOOV_Support.hpp +++ b/XMPFiles/source/FormatSupport/MOOV_Support.hpp @@ -104,7 +104,7 @@ public: // --------------------------------------------------------------------------------------------- - #pragma pack (1) // ! These must match the file layout! + #pragma pack (push, 1) // ! These must match the file layout! struct Content_mvhd_0 { XMP_Uns32 vFlags; // 0 @@ -164,6 +164,12 @@ public: XMP_Uns32 sampleDescrID; // 8 }; // 12 + #pragma pack( pop ) + +#if SUNOS_SPARC + #pragma pack( ) +#endif //#if SUNOS_SPARC + // --------------------------------------------------------------------------------------------- MOOV_Manager() : fileMode(0) diff --git a/XMPFiles/source/FormatSupport/PSIR_FileWriter.cpp b/XMPFiles/source/FormatSupport/PSIR_FileWriter.cpp index aecfec1..5d06220 100644 --- a/XMPFiles/source/FormatSupport/PSIR_FileWriter.cpp +++ b/XMPFiles/source/FormatSupport/PSIR_FileWriter.cpp @@ -273,12 +273,6 @@ void PSIR_FileWriter::ParseMemoryResources ( const void* data, XMP_Uns32 length, void PSIR_FileWriter::ParseFileResources ( XMP_IO* fileRef, XMP_Uns32 length ) { - bool ok; - - this->DeleteExistingInfo(); - this->fileParsed = true; - if ( length == 0 ) return; - // Parse the image resource block. We're using a map keyed by ID, so only one resource of each // ID is recognized. Redundant resources are not legit, but have been seen in the field. In // particular, one case has been seen of a duplicate IIM block with one empty. In general we @@ -286,45 +280,54 @@ void PSIR_FileWriter::ParseFileResources ( XMP_IO* fileRef, XMP_Uns32 length ) // taken though if the current one is empty. // ! Don't use map[id] to lookup, that creates a default entry if none exists! + + // PSIR layout: + // - Uns32 type, usually '8BIM' + // - Uns16 ID + // - PString name + // - Uns8 optional pad for even alignment + // - Uns32 data size + // - data + // - Uns8 optional pad for even alignment + + static const size_t kMinPSIRSize = 12; // 4+2+1+1+4 + + this->DeleteExistingInfo(); + this->fileParsed = true; + if ( length == 0 ) return; - IOBuffer ioBuf; - ioBuf.filePos = fileRef->Offset(); - - XMP_Int64 psirOrigin = ioBuf.filePos; // Need this to determine the resource data offsets. - XMP_Int64 fileEnd = ioBuf.filePos + length; + XMP_Int64 psirOrigin = fileRef->Offset(); // Need this to determine the resource data offsets. + XMP_Int64 fileEnd = psirOrigin + length; - std::string rsrcPName; + char nameBuffer [260]; // The name is a PString, at 1+255+1 including length and pad. - while ( (ioBuf.filePos + (ioBuf.ptr - ioBuf.data)) < fileEnd ) { + while ( fileRef->Offset() < fileEnd ) { - ok = CheckFileSpace ( fileRef, &ioBuf, 12 ); // The minimal image resource takes 12 bytes. - if ( ! ok ) break; // Bad image resource. Throw instead? + if ( ! XIO::CheckFileSpace ( fileRef, kMinPSIRSize ) ) break; // Bad image resource. - XMP_Int64 thisRsrcPos = ioBuf.filePos + (ioBuf.ptr - ioBuf.data); + XMP_Int64 thisRsrcPos = fileRef->Offset(); - XMP_Uns32 type = GetUns32BE(ioBuf.ptr); - XMP_Uns16 id = GetUns16BE(ioBuf.ptr+4); - ioBuf.ptr += 6; // Advance to the resource name. + XMP_Uns32 type = XIO::ReadUns32_BE ( fileRef ); + XMP_Uns16 id = XIO::ReadUns16_BE ( fileRef ); - XMP_Uns16 nameLen = ioBuf.ptr[0]; // ! The length for the Pascal string. + XMP_Uns8 nameLen = XIO::ReadUns8 ( fileRef ); // ! The length for the Pascal string. XMP_Uns16 paddedLen = (nameLen + 2) & 0xFFFE; // ! Round up to an even total. Yes, +2! - ok = CheckFileSpace ( fileRef, &ioBuf, paddedLen+4 ); // Get the name text and the data length. - if ( ! ok ) break; // Bad image resource. Throw instead? + if ( ! XIO::CheckFileSpace ( fileRef, paddedLen+4 ) ) break; // Bad image resource. - if ( nameLen > 0 ) rsrcPName.assign ( (char*)(ioBuf.ptr), paddedLen ); // ! Include the length byte and pad. + nameBuffer[0] = nameLen; + fileRef->ReadAll ( &nameBuffer[1], paddedLen-1 ); // Include the pad byte, present for zero nameLen. - ioBuf.ptr += paddedLen; // Move to the data length. - XMP_Uns32 dataLen = GetUns32BE(ioBuf.ptr); + XMP_Uns32 dataLen = XIO::ReadUns32_BE ( fileRef ); XMP_Uns32 dataTotal = ((dataLen + 1) & 0xFFFFFFFEUL); // Round up to an even total. - ioBuf.ptr += 4; // Advance to the resource data. + if ( ! XIO::CheckFileSpace ( fileRef, dataTotal ) ) break; // Bad image resource. - XMP_Int64 thisDataPos = ioBuf.filePos + (ioBuf.ptr - ioBuf.data); + XMP_Int64 thisDataPos = fileRef->Offset(); XMP_Int64 nextRsrcPos = thisDataPos + dataTotal; if ( type != k8BIM ) { XMP_Uns32 fullRsrcLen = (XMP_Uns32) (nextRsrcPos - thisRsrcPos); this->otherRsrcs.push_back ( OtherRsrcInfo ( (XMP_Uns32)thisRsrcPos, fullRsrcLen ) ); - MoveToOffset ( fileRef, nextRsrcPos, &ioBuf ); + fileRef->Seek ( nextRsrcPos, kXMP_SeekFromStart ); continue; } @@ -335,7 +338,7 @@ void PSIR_FileWriter::ParseFileResources ( XMP_IO* fileRef, XMP_Uns32 length ) } else if ( (rsrcPos->second.dataLen == 0) && (newInfo.dataLen != 0) ) { rsrcPos->second = newInfo; } else { - MoveToOffset ( fileRef, nextRsrcPos, &ioBuf ); + fileRef->Seek ( nextRsrcPos, kXMP_SeekFromStart ); continue; } InternalRsrcInfo* rsrcPtr = &rsrcPos->second; @@ -345,29 +348,17 @@ void PSIR_FileWriter::ParseFileResources ( XMP_IO* fileRef, XMP_Uns32 length ) if ( nameLen > 0 ) { rsrcPtr->rsrcName = (XMP_Uns8*) malloc ( paddedLen ); if ( rsrcPtr->rsrcName == 0 ) XMP_Throw ( "Out of memory", kXMPErr_NoMemory ); - memcpy ( (void*)rsrcPtr->rsrcName, rsrcPName.c_str(), paddedLen ); // AUDIT: Safe, allocated enough bytes above. + memcpy ( (void*)rsrcPtr->rsrcName, nameBuffer, paddedLen ); // AUDIT: Safe, allocated enough bytes above. } if ( ! IsMetadataImgRsrc ( id ) ) { - MoveToOffset ( fileRef, nextRsrcPos, &ioBuf ); + fileRef->Seek ( nextRsrcPos, kXMP_SeekFromStart ); continue; } - rsrcPtr->dataPtr = malloc ( dataLen ); // ! Allocate after the IsMetadataImgRsrc check. + rsrcPtr->dataPtr = malloc ( dataTotal ); // ! Allocate after the IsMetadataImgRsrc check. if ( rsrcPtr->dataPtr == 0 ) XMP_Throw ( "Out of memory", kXMPErr_NoMemory ); - - if ( dataTotal <= kIOBufferSize ) { - // The image resource data fits within the I/O buffer. - ok = CheckFileSpace ( fileRef, &ioBuf, dataTotal ); - if ( ! ok ) break; // Bad image resource. Throw instead? - memcpy ( (void*)rsrcPtr->dataPtr, ioBuf.ptr, dataLen ); // AUDIT: Safe, malloc'ed dataLen bytes above. - ioBuf.ptr += dataTotal; // ! Add the rounded length. - } else { - // The image resource data is bigger than the I/O buffer. - fileRef->Seek ( (ioBuf.filePos + (ioBuf.ptr - ioBuf.data)), kXMP_SeekFromStart ); - fileRef->ReadAll ( (void*)rsrcPtr->dataPtr, dataLen ); - FillBuffer ( fileRef, nextRsrcPos, &ioBuf ); - } + fileRef->ReadAll ( (void*)rsrcPtr->dataPtr, dataTotal ); } @@ -492,11 +483,10 @@ XMP_Uns32 PSIR_FileWriter::UpdateMemoryResources ( void** dataPtr ) // ==================================== XMP_Uns32 PSIR_FileWriter::UpdateFileResources ( XMP_IO* sourceRef, XMP_IO* destRef, - void * _ioBuf, XMP_AbortProc abortProc, void * abortArg ) + XMP_AbortProc abortProc, void * abortArg, + XMP_ProgressTracker* progressTracker ) { - IgnoreParam(_ioBuf); const XMP_Uns32 zero32 = 0; - const bool checkAbort = (abortProc != 0); struct RsrcHeader { @@ -507,9 +497,29 @@ XMP_Uns32 PSIR_FileWriter::UpdateFileResources ( XMP_IO* sourceRef, XMP_IO* dest if ( this->memParsed ) XMP_Throw ( "Not file based", kXMPErr_EnforceFailure ); - XMP_Int64 destLenOffset = destRef->Offset(); - XMP_Uns32 destLength = 0; + InternalRsrcMap::const_iterator rsrcPos; + InternalRsrcMap::const_iterator rsrcEnd = this->imgRsrcs.end(); + + if ( progressTracker != 0 ) { + + float totalLength = 8; + for ( rsrcPos = this->imgRsrcs.begin(); rsrcPos != rsrcEnd; ++rsrcPos ) { + const InternalRsrcInfo& currRsrc = rsrcPos->second; + totalLength += (currRsrc.dataLen + 12); + } + + size_t sizeOtherRsrc = this->otherRsrcs.size(); + for ( size_t i = 0; i < sizeOtherRsrc; ++i ) { + totalLength += this->otherRsrcs[i].rsrcLength; + } + + XMP_Assert ( progressTracker->WorkInProgress() ); + progressTracker->AddTotalWork ( totalLength ); + + } + XMP_Uns32 destLength = 0; + XMP_Int64 destLenOffset = destRef->Offset(); destRef->Write ( &destLength, 4 ); // Write a placeholder for the new PSIR section length. #if 0 @@ -532,13 +542,10 @@ XMP_Uns32 PSIR_FileWriter::UpdateFileResources ( XMP_IO* sourceRef, XMP_IO* dest RsrcHeader outHeader; outHeader.type = MakeUns32BE ( k8BIM ); - InternalRsrcMap::iterator rsrcPos = this->imgRsrcs.begin(); - InternalRsrcMap::iterator rsrcEnd = this->imgRsrcs.end(); - // printf ( "\nPSIR_FileWriter::UpdateFileResources - 8BIM resources\n" ); - for ( ; rsrcPos != rsrcEnd; ++rsrcPos ) { + for ( rsrcPos = this->imgRsrcs.begin(); rsrcPos != rsrcEnd; ++rsrcPos ) { - InternalRsrcInfo& currRsrc = rsrcPos->second; + const InternalRsrcInfo& currRsrc = rsrcPos->second; outHeader.id = MakeUns16BE ( currRsrc.id ); destRef->Write ( &outHeader, 6 ); diff --git a/XMPFiles/source/FormatSupport/PSIR_Support.hpp b/XMPFiles/source/FormatSupport/PSIR_Support.hpp index b0ec13a..bd231b3 100644 --- a/XMPFiles/source/FormatSupport/PSIR_Support.hpp +++ b/XMPFiles/source/FormatSupport/PSIR_Support.hpp @@ -19,6 +19,7 @@ #include "source/XMPFiles_IO.hpp" #include "source/EndianUtils.hpp" +#include "source/XMP_ProgressTracker.hpp" #include <map> @@ -140,8 +141,8 @@ public: virtual XMP_Uns32 UpdateMemoryResources ( void** dataPtr ) = 0; virtual XMP_Uns32 UpdateFileResources ( XMP_IO* sourceRef, XMP_IO* destRef, - void * _ioBuf, XMP_AbortProc abortProc, void * abortArg ) = 0; - // *** Temporary hack above, _ioBuf is IOBuffer* but don't want to include XIO.hpp. + XMP_AbortProc abortProc, void * abortArg, + XMP_ProgressTracker* progressTracker ) = 0; // --------------------------------------------------------------------------------------------- @@ -179,8 +180,8 @@ public: XMP_Uns32 UpdateMemoryResources ( void** dataPtr ) { if ( dataPtr != 0 ) *dataPtr = psirContent; return psirLength; }; XMP_Uns32 UpdateFileResources ( XMP_IO* sourceRef, XMP_IO* destRef, - void * _ioBuf, XMP_AbortProc abortProc, void * abortArg ) { NotAppropriate(); return 0; }; - // *** Temporary hack above, _ioBuf is IOBuffer* but don't want to include XIO.hpp. + XMP_AbortProc abortProc, void * abortArg, + XMP_ProgressTracker* progressTracker ) { NotAppropriate(); return 0; }; PSIR_MemoryReader() : ownedContent(false), psirLength(0), psirContent(0) {}; @@ -232,8 +233,8 @@ public: XMP_Uns32 UpdateMemoryResources ( void** dataPtr ); XMP_Uns32 UpdateFileResources ( XMP_IO* sourceRef, XMP_IO* destRef, - void * _ioBuf, XMP_AbortProc abortProc, void * abortArg ); - // *** Temporary hack above, _ioBuf is IOBuffer* but don't want to include XIO.hpp. + XMP_AbortProc abortProc, void * abortArg, + XMP_ProgressTracker* progressTracker ); PSIR_FileWriter() : changed(false), legacyDeleted(false), memParsed(false), fileParsed(false), ownedContent(false), memLength(0), memContent(0) {}; diff --git a/XMPFiles/source/FormatSupport/PackageFormat_Support.cpp b/XMPFiles/source/FormatSupport/PackageFormat_Support.cpp new file mode 100644 index 0000000..9405293 --- /dev/null +++ b/XMPFiles/source/FormatSupport/PackageFormat_Support.cpp @@ -0,0 +1,124 @@ +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2013 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "public/include/XMP_Environment.h" // ! XMP_Environment.h must be the first included header. + +#include "XMPFiles/source/XMPFiles_Impl.hpp" +#include "XMPFiles/source/FormatSupport/PackageFormat_Support.hpp" +#include <algorithm> + +// ================================================================================================= +/// \file PackageFormat_Support.cpp +/// +// ================================================================================================= + + +// ================================================================================================= +// PackageFormat_Support::GetPostfixRange +// ================================ + +#if 0 +XMP_Bool PackageFormat_Support::GetPostfixRange ( XMP_FileFormat format , XMP_StringPtr extension, XMP_Uns32 * range ) +{ + switch ( format ) { + case kXMP_XDCAM_EXFile: + range[0] = 1; range[1] = 99; + break; + case kXMP_CanonXFFile: + range[0] = 1; range[1] = 99; + break; + case kXMP_XDCAM_SAMFile: + case kXMP_XDCAM_FAMFile: + range[0] = 1; range[1] = 99; + break; + case kXMP_P2File: + range[0] = 0; range[1] = 99;// for voice memo files + if(strcmp(extension, ".MXF") == 0) + range[1] = 15;// for audio essence files + break; + default: + return false; + } + return true; +} // PackageFormat_Support::GetPostfixRange +#endif + +// ================================================================================================= +// PackageFormat_Support::AddResourceIfExists +// ================================ + +bool PackageFormat_Support::AddResourceIfExists ( XMP_StringVector * resourceList, const XMP_VarString & file ) +{ + if ( Host_IO::Exists ( file.c_str() ) ) { + resourceList->push_back ( file ); + return true; + } + return false; +} // PackageFormat_Support::AddResourceIfExists + +#if 0 +// ================================================================================================= +// PackageFormat_Support::AddResourceIfExists +// ================================ + +bool PackageFormat_Support::AddResourceIfExists ( XMP_StringVector * resourceList, const XMP_VarString & noExtPath, + XMP_StringPtr extension, XMP_FileFormat format ) +{ + XMP_Uns32 range[2]; + XMP_Bool atLeastOneFileAdded = false, fileAdded = false; + XMP_VarString iStr, filePath; + if ( GetPostfixRange ( format, extension, range ) ) { + for( XMP_Uns32 index = range[0]; index <= range[1] ; ++index ) + { + SXMPUtils::ConvertFromInt ( index, NULL, &iStr ) ; + if ( index < LEAST_TWO_DIGIT_INT ) + iStr = '0' + iStr; + filePath = noExtPath + iStr + extension; + fileAdded = AddResourceIfExists ( resourceList, filePath ); + atLeastOneFileAdded |= fileAdded ; + } + } + return atLeastOneFileAdded; +} // PackageFormat_Support::AddResourceIfExists + +#endif +// ================================================================================================= +// PackageFormat_Support::AddResourceIfExists +// ================================ +bool PackageFormat_Support::AddResourceIfExists ( XMP_StringVector * resourceList, const XMP_VarString & folderPath, + XMP_StringPtr prefix, XMP_StringPtr postfix ) +{ + Host_IO::FolderRef folderHandle = Host_IO::OpenFolder ( folderPath.c_str() ); + if ( folderHandle == Host_IO::noFolderRef || !prefix || !postfix ) + return false;// can't open folder. + XMP_VarString fileName, filePath; + size_t fileNameLength; + size_t prefixLength = strlen ( prefix ); + size_t postfixLength = strlen ( postfix ); + bool atleastOneFileAdded = false; + while ( Host_IO::GetNextChild ( folderHandle, &fileName ) ) + { + fileNameLength = fileName.length(); + // Check if the file name starts with prefix and ends with postfix + if ( fileNameLength >= ( prefixLength + postfixLength ) && + fileName.compare ( fileNameLength-postfixLength, postfixLength, postfix ) == 0 && + fileName.compare ( 0, prefixLength, prefix ) == 0) + { + filePath = folderPath + kDirChar + fileName; + PackageFormat_Support::AddResourceIfExists ( resourceList, filePath ); + atleastOneFileAdded = true; + } + } + // close folder + Host_IO::CloseFolder ( folderHandle ); + return atleastOneFileAdded; +} // PackageFormat_Support::AddResourceIfExists + + +// ================================================================================================= diff --git a/XMPFiles/source/FormatSupport/PackageFormat_Support.hpp b/XMPFiles/source/FormatSupport/PackageFormat_Support.hpp new file mode 100644 index 0000000..8883c9e --- /dev/null +++ b/XMPFiles/source/FormatSupport/PackageFormat_Support.hpp @@ -0,0 +1,39 @@ +#ifndef __PackageFormat_Support_hpp__ +#define __PackageFormat_Support_hpp__ 1 + +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2013 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "public/include/XMP_Environment.h" // ! This must be the first include. + +#include "source/XMP_LibUtils.hpp" + +// ================================================================================================= +/// \file PackageFormat_Support.hpp +/// \brief XMPFiles support for folder based formats. +/// +// ================================================================================================= + +namespace PackageFormat_Support +{ + + // Checks if the file at path "file" exists. + // If it exists then it adds to "resourceList" and returns true. + bool AddResourceIfExists ( XMP_StringVector * resourceList, const XMP_VarString & file ); + + // This function adds all the existing files in the specified folder whose name starts with prefix and ends with postfix. + bool AddResourceIfExists ( XMP_StringVector * resourceList, const XMP_VarString & folderPath, + XMP_StringPtr prefix, XMP_StringPtr postfix); + + +} // namespace PackageFormat_Support + +// ================================================================================================= + +#endif // __PackageFormat_Support_hpp__ diff --git a/XMPFiles/source/FormatSupport/PostScript_Support.cpp b/XMPFiles/source/FormatSupport/PostScript_Support.cpp new file mode 100644 index 0000000..a0c13d2 --- /dev/null +++ b/XMPFiles/source/FormatSupport/PostScript_Support.cpp @@ -0,0 +1,1094 @@ +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2012 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "XMPFiles/source/FormatSupport/PostScript_Support.hpp" +#include "XMP.hpp" +#include <algorithm> +#include <climits> + +// ================================================================================================= +// PostScript_Support::HasCodesGT127 +// ================================= +// +// function to detect character codes greater than 127 in a string +bool PostScript_Support::HasCodesGT127(const std::string & value) +{ + size_t vallen=value.length(); + for (size_t index=0;index<vallen;index++) + { + if ((unsigned char)value[index]>127) + { + return true; + } + } + return false; +} + +// ================================================================================================= +// PostScript_Support::SkipTabsAndSpaces +// ===================================== +// +// function moves the file pointer ahead such that it skips all tabs and spaces +bool PostScript_Support::SkipTabsAndSpaces(XMP_IO* file,IOBuffer& ioBuf) +{ + while ( true ) + { + if ( ! CheckFileSpace ( file, &ioBuf, 1 ) ) return false; + if ( ! IsSpaceOrTab ( *ioBuf.ptr ) ) break; + ++ioBuf.ptr; + } + return true; +} + +// ================================================================================================= +// PostScript_Support::SkipUntilNewline +// ==================================== +// +// function moves the file pointer ahead such that it skips all characters until a newline +bool PostScript_Support::SkipUntilNewline(XMP_IO* file,IOBuffer& ioBuf) +{ + char ch; + do + { + if ( ! CheckFileSpace ( file, &ioBuf, 1 ) ) return false; + ch = *ioBuf.ptr; + ++ioBuf.ptr; + } while ( ! IsNewline ( ch ) ); + if (ch==kCR &&*ioBuf.ptr==kLF) + { + if ( ! CheckFileSpace ( file, &ioBuf, 1 ) ) return false; + ++ioBuf.ptr; + } + return true; +} + + +// ================================================================================================= +// RevRefillBuffer and RevCheckFileSpace +// ====================================== +// +// These helpers are similar to RefillBuffer and CheckFileSpace with the difference that the it traverses +// the file stream in reverse order +void PostScript_Support::RevRefillBuffer ( XMP_IO* fileRef, IOBuffer* ioBuf ) +{ + // Refill including part of the current data, seek back to the new buffer origin and read. + size_t reverseSeek = ioBuf->limit - ioBuf->ptr; + if (ioBuf->filePos>kIOBufferSize) + { + ioBuf->filePos = fileRef->Seek ( -((XMP_Int64)(kIOBufferSize+reverseSeek)), kXMP_SeekFromCurrent ); + ioBuf->len = fileRef->Read ( &ioBuf->data[0], kIOBufferSize ); + ioBuf->ptr = &ioBuf->data[0]+ioBuf->len; + ioBuf->limit = ioBuf->ptr ; + } + else + { + XMP_Int64 rev = (ioBuf->ptr-&ioBuf->data[0]) + ioBuf->filePos; + ioBuf->filePos = fileRef->Seek ( 0, kXMP_SeekFromStart ); + ioBuf->len = fileRef->Read ( &ioBuf->data[0], kIOBufferSize ); + if ( rev > (XMP_Int64)ioBuf->len )throw XMP_Error ( kXMPErr_ExternalFailure, "Seek failure in FillBuffer" ); + ioBuf->ptr = &ioBuf->data[0]+rev; + ioBuf->limit = &ioBuf->data[0]+ioBuf->len; + } + + +} +bool PostScript_Support::RevCheckFileSpace ( XMP_IO* fileRef, IOBuffer* ioBuf, size_t neededLen ) +{ + if ( size_t(ioBuf->ptr - &ioBuf->data[0]) < size_t(neededLen) ) + { // ! Avoid VS.Net compare warnings. + PostScript_Support::RevRefillBuffer ( fileRef, ioBuf ); + } + return (size_t(ioBuf->ptr - &ioBuf->data[0]) >= size_t(neededLen)); +} + +// ================================================================================================= +// SearchBBoxInTrailer +// =================== +// +// Function searches the Bounding Box in the comments after the %Trailer +// this function gets called when the DSC comment BoundingBox: value is +// (atend) +// returns true if atleast one BoundingBox: is found after %Trailer +inline static bool SearchBBoxInTrailer(XMP_IO* fileRef,IOBuffer& ioBuf) +{ + bool bboxfoundintrailer=false; + if ( ! PostScript_Support::SkipTabsAndSpaces( fileRef, ioBuf ) ) return false; + if ( ! IsNewline ( *ioBuf.ptr ) ) return false; + ++ioBuf.ptr; + // Scan for all the %%Trailer outside %%BeginDocument: & %%EndDocument comments + while(true) + { + if ( ! CheckFileSpace ( fileRef, &ioBuf, kPSContainsBeginDocString.length() ) ) return false; + if ( CheckBytes ( ioBuf.ptr, Uns8Ptr(kPSContainsTrailerString.c_str()), kPSContainsTrailerString.length() )) + { + //found %%Trailer now search for proper %%BoundingBox + ioBuf.ptr+=kPSContainsTrailerString.length(); + //skip chars after %%Trailer till newline + if ( ! PostScript_Support::SkipUntilNewline( fileRef, ioBuf ) ) return false; + while(true) + { + if ( ! CheckFileSpace ( fileRef, &ioBuf, kPSContainsBBoxString.length() ) ) return false; + //check for "%%BoundingBox:" + if (CheckBytes ( ioBuf.ptr, Uns8Ptr(kPSContainsBBoxString.c_str()), kPSContainsBBoxString.length() ) ) + { + //found "%%BoundingBox:" + ioBuf.ptr+=kPSContainsBBoxString.length(); + // Skip leading spaces and tabs. + if ( ! PostScript_Support::SkipTabsAndSpaces( fileRef, ioBuf ) ) return false; + if ( IsNewline ( *ioBuf.ptr ) ) return false; // Reached the end of the %%BoundingBox comment. + bboxfoundintrailer=true; + break; + } + //skip chars till newline + if ( ! PostScript_Support::SkipUntilNewline( fileRef, ioBuf ) ) return false; + } + if (!bboxfoundintrailer) + return false; + else + break; + } + else if ( CheckBytes ( ioBuf.ptr, Uns8Ptr(kPSContainsBeginDocString.c_str()), kPSContainsBeginDocString.length() ) ) + { + //"%%BeginDocument:" Found search for "%%EndDocument" + ioBuf.ptr+=kPSContainsBeginDocString.length(); + //skip chars after "%%BeginDocument:" till newline + if ( ! PostScript_Support::SkipUntilNewline( fileRef, ioBuf ) ) return false; + while(true) + { + if ( ! CheckFileSpace ( fileRef, &ioBuf, kPSContainsEndDocString.length() ) ) return false; + //check for "%%EndDocument" + if (CheckBytes ( ioBuf.ptr, Uns8Ptr(kPSContainsEndDocString.c_str()), kPSContainsEndDocString.length() ) ) + { + //found "%%EndDocument" + ioBuf.ptr+=kPSContainsEndDocString.length(); + break; + } + //skip chars till newline + if ( ! PostScript_Support::SkipUntilNewline( fileRef, ioBuf ) ) return false; + }// while to search %%EndDocument + + } //else if to consume a pair of %%BeginDocument: and %%EndDocument + //skip chars till newline + if ( ! PostScript_Support::SkipUntilNewline( fileRef, ioBuf ) ) return false; + }// while for scanning %%BoundingBox after %%Trailer + if (!bboxfoundintrailer) return false; + return true; +} + +// ================================================================================================= +// PostScript_Support::IsValidPSFile +// ================================= +// +// Determines if the file is a valid PostScript or EPS file +// Checks done +// Looks for a valid Poscript header +// For EPS file checks for a valid Bounding Box comment +bool PostScript_Support::IsValidPSFile(XMP_IO* fileRef,XMP_FileFormat &format) +{ + IOBuffer ioBuf; + XMP_Int64 psOffset; + size_t psLength; + XMP_Uns32 fileheader,psMajorVer, psMinorVer,epsMajorVer,epsMinorVer; + char ch; + // Check for the binary EPSF preview header. + + fileRef->Rewind(); + if ( ! CheckFileSpace ( fileRef, &ioBuf, 4 ) ) return false; + fileheader = GetUns32BE ( ioBuf.ptr ); + + if ( fileheader == 0xC5D0D3C6 ) + { + + if ( ! CheckFileSpace ( fileRef, &ioBuf, 30 ) ) return false; + + psOffset = GetUns32LE ( ioBuf.ptr+4 ); // PostScript offset. + psLength = GetUns32LE ( ioBuf.ptr+8 ); // PostScript length. + + FillBuffer ( fileRef, psOffset, &ioBuf ); // Make sure buffer starts at psOffset for length check. + if ( (ioBuf.len < kIOBufferSize) && (ioBuf.len < psLength) ) return false; // Not enough PostScript. + + } + + // Check the start of the PostScript DSC header comment. + + if ( ! CheckFileSpace ( fileRef, &ioBuf, (kPSFileTag.length() + 3 + 1) ) ) return false; + if ( ! CheckBytes ( ioBuf.ptr, Uns8Ptr(kPSFileTag.c_str()), kPSFileTag.length() ) ) return false; + ioBuf.ptr += kPSFileTag.length(); + + // Check the PostScript DSC major version number. + + psMajorVer = 0; + while ( (ioBuf.ptr < ioBuf.limit) && IsNumeric( *ioBuf.ptr ) ) + { + psMajorVer = (psMajorVer * 10) + (*ioBuf.ptr - '0'); + if ( psMajorVer > 1000 ) return false; // Overflow. + ioBuf.ptr += 1; + } + if ( psMajorVer < 3 ) return false; // The version must be at least 3.0. + + if ( ! CheckFileSpace ( fileRef, &ioBuf, 3 ) ) return false; + if ( *ioBuf.ptr != '.' ) return false; // No minor number. + ioBuf.ptr += 1; + + // Check the PostScript DSC minor version number. + + psMinorVer = 0; + while ( (ioBuf.ptr < ioBuf.limit) && IsNumeric( *ioBuf.ptr ) ) + { + psMinorVer = (psMinorVer * 10) + (*ioBuf.ptr - '0'); + if ( psMinorVer > 1000 ) return false; // Overflow. + ioBuf.ptr += 1; + } + + switch( format ) + { + case kXMP_PostScriptFile: + { + // Almost done for plain PostScript, check for whitespace. + + if ( ! CheckFileSpace ( fileRef, &ioBuf, 1 ) ) return false; + if ( ! IsWhitespace(*ioBuf.ptr) ) return false; + ioBuf.ptr += 1; + + break; + } + case kXMP_UnknownFile: + { + if ( ! SkipTabsAndSpaces ( fileRef, ioBuf ) ) return false; + + if ( ! CheckFileSpace ( fileRef, &ioBuf, 5 ) ) return false; + // checked PS header to this point Atkleast a PostScript File + format=kXMP_PostScriptFile; + //return true if no "EPSF-" is found as it is a valid PS atleast + if ( ! CheckBytes ( ioBuf.ptr, Uns8Ptr("EPSF-"), 5 ) ) return true; + } + case kXMP_EPSFile: + { + + if ( ! SkipTabsAndSpaces ( fileRef, ioBuf ) ) return false; + // Check for the EPSF keyword on the header comment. + + if ( ! CheckFileSpace ( fileRef, &ioBuf, 5+3+1 ) ) return false; + if ( ! CheckBytes ( ioBuf.ptr, Uns8Ptr("EPSF-"), 5 ) ) return false; + ioBuf.ptr += 5; + + // Check the EPS major version number. + + epsMajorVer = 0; + while ( (ioBuf.ptr < ioBuf.limit) &&IsNumeric( *ioBuf.ptr ) ) { + epsMajorVer = (epsMajorVer * 10) + (*ioBuf.ptr - '0'); + if ( epsMajorVer > 1000 ) return false; // Overflow. + ioBuf.ptr += 1; + } + if ( epsMajorVer < 3 ) return false; // The version must be at least 3.0. + + if ( ! CheckFileSpace ( fileRef, &ioBuf, 3 ) ) return false; + if ( *ioBuf.ptr != '.' ) return false; // No minor number. + ioBuf.ptr += 1; + + // Check the EPS minor version number. + + epsMinorVer = 0; + while ( (ioBuf.ptr < ioBuf.limit) && IsNumeric( *ioBuf.ptr ) ) { + epsMinorVer = (epsMinorVer * 10) + (*ioBuf.ptr - '0'); + if ( epsMinorVer > 1000 ) return false; // Overflow. + ioBuf.ptr += 1; + } + + if ( ! SkipTabsAndSpaces ( fileRef, ioBuf ) ) return false; + if ( ! CheckFileSpace ( fileRef, &ioBuf, 1 ) ) return false; + if ( ! IsNewline( *ioBuf.ptr ) ) return false; + ch=*ioBuf.ptr; + ioBuf.ptr += 1; + if (ch==kCR &&*ioBuf.ptr==kLF) + { + if ( ! CheckFileSpace ( fileRef, &ioBuf, 1 ) ) return false; + ++ioBuf.ptr; + } + + + while ( true ) + { + if ( ! CheckFileSpace ( fileRef, &ioBuf, kPSContainsBBoxString.length() ) ) return false; + + if ( CheckBytes ( ioBuf.ptr, Uns8Ptr(kPSEndCommentString.c_str()), kPSEndCommentString.length() ) //explicit endcommentcheck + || *ioBuf.ptr!='%' || !(*(ioBuf.ptr+1)>32 && *(ioBuf.ptr+1)<=126 )) // implicit endcomment check + { + // Found "%%EndComments", don't look any further. + return false; + } + else if ( ! CheckBytes ( ioBuf.ptr, Uns8Ptr(kPSContainsBBoxString.c_str()), kPSContainsBBoxString.length() ) ) + { + // Not "%%EndComments" or "%%BoundingBox:", skip past the end of this line. + if ( ! SkipUntilNewline ( fileRef, ioBuf ) ) return true; + } + else + { + + // Found "%%BoundingBox:", look for llx lly urx ury. + ioBuf.ptr += kPSContainsBBoxString.length(); + //Check for atleast a mandatory space b/w "%%BoundingBox:" and llx lly urx ury + if ( ! CheckFileSpace ( fileRef, &ioBuf, 1 ) ) return false; + if ( ! IsSpaceOrTab ( *ioBuf.ptr ) ) return false; + ioBuf.ptr++; + + while ( true ) + { + // Skip leading spaces and tabs. + if ( ! SkipTabsAndSpaces( fileRef, ioBuf ) ) return false; + if ( IsNewline ( *ioBuf.ptr ) ) return false; // Reached the end of the %%BoundingBox comment. + + //if the comment is %%BoundingBox: (atend) go past the %%Trailer to check BBox + bool bboxfoundintrailer=false; + if (*ioBuf.ptr=='(') + { + if ( ! CheckFileSpace ( fileRef, &ioBuf, kPSContainsAtendString.length() ) ) return false; + + if ( CheckBytes ( ioBuf.ptr, Uns8Ptr(kPSContainsAtendString.c_str()), kPSContainsAtendString.length() ) ) + { + // search for Bounding Box Past Trailer + ioBuf.ptr += kPSContainsAtendString.length(); + bboxfoundintrailer=SearchBBoxInTrailer( fileRef, ioBuf ); + } + + if (!bboxfoundintrailer) + return false; + + }//if (*ioBuf.ptr=='(') + + int noOfIntegers=0; + // verifies for llx lly urx ury. + while(true) + { + if ( ! CheckFileSpace ( fileRef, &ioBuf, 1 ) ) return false; + if(IsPlusMinusSign(*ioBuf.ptr )) + ++ioBuf.ptr; + bool atleastOneNumeric=false; + while ( true) + { + if ( ! CheckFileSpace ( fileRef, &ioBuf, 1 ) ) return false; + if (!IsNumeric ( *ioBuf.ptr ) ) break; + ++ioBuf.ptr; + atleastOneNumeric=true; + } + if (!atleastOneNumeric) return false; + + if ( ! SkipTabsAndSpaces( fileRef, ioBuf ) ) return false; + noOfIntegers++; + if ( IsNewline ( *ioBuf.ptr ) ) break; + } + if (noOfIntegers!=4) + return false; + format=kXMP_EPSFile; + return true; + } + + } //Found "%%BoundingBox:" + + } // Outer marker loop. + } + default: + { + return false; + } + } + return true; +} + + +// ================================================================================================= +// PostScript_Support::IsSFDFilterUsed +// ================================= +// +// Determines Whether the metadata is embedded using the Sub-FileDecode Approach or no +// In case of Sub-FileDecode filter approach the metaData can be easily extended without +// the need to inject a new XMP packet before the existing Packet. +// +bool PostScript_Support::IsSFDFilterUsed(XMP_IO* &fileRef, XMP_Int64 xpacketOffset) +{ + IOBuffer ioBuf; + fileRef->Rewind(); + fileRef->Seek((xpacketOffset/kIOBufferSize)*kIOBufferSize,kXMP_SeekFromStart); + if ( ! CheckFileSpace ( fileRef, &ioBuf,xpacketOffset%kIOBufferSize ) ) return false; + ioBuf.ptr+=(xpacketOffset%kIOBufferSize); + //skip white spaces + while(true) + { + if ( ! PostScript_Support::RevCheckFileSpace ( fileRef, &ioBuf, 1 ) ) return false; + if (!IsWhitespace(*ioBuf.ptr)) break; + --ioBuf.ptr; + } + std::string temp; + bool filterFound=false; + while(true) + { + if ( ! PostScript_Support::RevCheckFileSpace ( fileRef, &ioBuf, 1 ) ) return false; + if (*ioBuf.ptr==')') + { + --ioBuf.ptr; + while(true) + { + //get the string till '(' + if ( ! PostScript_Support::RevCheckFileSpace ( fileRef, &ioBuf, 1 ) ) return false ; + temp+=*ioBuf.ptr; + --ioBuf.ptr; + if (*ioBuf.ptr=='(') + { + if(filterFound) + { + reverse(temp.begin(), temp.end()); + if(!temp.compare("SubFileDecode")) + return true; + } + if ( ! PostScript_Support::RevCheckFileSpace ( fileRef, &ioBuf, 1 ) ) return false ; + --ioBuf.ptr; + temp.clear(); + break; + } + } + + filterFound=false; + } + else if(*ioBuf.ptr=='[') + { + //end of SubFileDecode Filter parsing + return false; + } + else if(*ioBuf.ptr=='k' ) + { + if ( ! PostScript_Support::RevCheckFileSpace ( fileRef, &ioBuf, 4 ) ) return false; + if(IsWhitespace(*(ioBuf.ptr-4))&& *(ioBuf.ptr-3)=='m' + && *(ioBuf.ptr-2)=='a' && *(ioBuf.ptr-1)=='r' ) + //end of SubFileDecode Filter parsing + return false; + while(true)//ignore till any special mark + { + if ( ! PostScript_Support::RevCheckFileSpace ( fileRef, &ioBuf, 4 ) ) return false; + if (IsWhitespace(*(ioBuf.ptr))||*(ioBuf.ptr)=='['|| + *(ioBuf.ptr)=='<' ||*(ioBuf.ptr)=='>') break; + --ioBuf.ptr; + } + filterFound=false; + } + else if(*ioBuf.ptr=='<') + { + --ioBuf.ptr; + if ( ! PostScript_Support::RevCheckFileSpace ( fileRef, &ioBuf, 1 ) ) return false; + if (*(ioBuf.ptr)=='<') + { + //end of SubFileDecode Filter parsing + return false; + } + while(true)//ignore till any special mark + { + if ( ! PostScript_Support::RevCheckFileSpace ( fileRef, &ioBuf, 4 ) ) return false; + if (IsWhitespace(*(ioBuf.ptr))||*(ioBuf.ptr)=='['|| + *(ioBuf.ptr)=='<' ||*(ioBuf.ptr)=='>') break; + --ioBuf.ptr; + } + filterFound=false; + } + else if(*ioBuf.ptr=='>') + { + --ioBuf.ptr; + if ( ! PostScript_Support::RevCheckFileSpace ( fileRef, &ioBuf, 1 ) ) return false; + if (*(ioBuf.ptr)=='>')//ignore the dictionary + { + --ioBuf.ptr; + XMP_Int16 count=1; + while(true) + { + if ( ! PostScript_Support::RevCheckFileSpace ( fileRef, &ioBuf, 2 ) ) return false; + if(*(ioBuf.ptr)=='<' && *(ioBuf.ptr-1)=='<') + { + count--; + ioBuf.ptr-=2; + } + else if(*(ioBuf.ptr)=='>' && *(ioBuf.ptr-1)=='>') + { + count++; + ioBuf.ptr-=2; + } + else + { + ioBuf.ptr-=1; + } + if(count==0) + break; + } + } + filterFound=false; + } + else + { + while(true) + { + if ( ! PostScript_Support::RevCheckFileSpace ( fileRef, &ioBuf, 1 ) ) return false; + temp+=(*ioBuf.ptr); + --ioBuf.ptr; + if (*ioBuf.ptr=='/') + { + if(filterFound) + { + reverse(temp.begin(), temp.end()); + if(!temp.compare("SubFileDecode")) + return true; + } + temp.clear(); + filterFound=false; + break; + } + else if(IsWhitespace(*ioBuf.ptr)) + { + reverse(temp.begin(), temp.end()); + if(!temp.compare("filter")&&!filterFound) + filterFound=true; + else + filterFound=false; + temp.clear(); + break; + } + } + if ( ! PostScript_Support::RevCheckFileSpace ( fileRef, &ioBuf, 1 ) ) return false; + --ioBuf.ptr; + } + while(true) + { + if ( ! PostScript_Support::RevCheckFileSpace ( fileRef, &ioBuf, 1 ) ) return false; + if (!IsWhitespace(*ioBuf.ptr)) break; + --ioBuf.ptr; + } + + } + return false; + +} + + +// ================================================================================================= +// constructDateTime +// ================================= +// +// If input string date is of the format D:YYYYMMDDHHmmSSOHH'mm' and valid +// output format YYYY-MM-DDThh:mm:ssTZD is returned +// +static void constructDateTime(const std::string &input,std::string& outDate) +{ + std::string date; + XMP_DateTime datetime; + std::string verdate; + size_t start =0; + if(input[0]=='D' && input[1]==':') + start=2; + if (input.length() >=14+start) + { + for(int x=0;x<4;x++) + { + date+=input[start+x];//YYYY + } + date+='-'; + start+=4; + for(int x=0;x<2;x++) + { + date+=input[start+x];//MM + } + date+='-'; + start+=2; + for(int x=0;x<2;x++) + { + date+=input[start+x];//DD + } + date+='T'; + start+=2; + for(int x=0;x<2;x++) + { + date+=input[start+x];//HH + } + + date+=':'; + start+=2; + for(int x=0;x<2;x++) + { + date+=input[start+x];//MM + } + + date+=':'; + start+=2; + for(int x=0;x<2;x++) + { + date+=input[start+x];//SS + } + start+=2; + if ((input[start]=='+' || input[start]=='-' )&&input.length() ==19+start) + { + date+=input[start]; + start++; + for(int x=0;x<2;x++) + { + date+=input[start+x];//hh + } + date+=':'; + start+=2; + for(int x=0;x<2;x++) + { + date+=input[start+x];//mm + } + } + else + { + date+='Z'; + } + + try + { + SXMPUtils::ConvertToDate ( date.c_str(),&datetime); + SXMPUtils::ConvertFromDate (datetime,&verdate); + } + catch(...) + { + return; + } + outDate=verdate; + } + +} + +// ================================================================================================= +// GetNumber +// ========= +// +// Extracts number from a char string +// +static short GetNumber(const char **inString,short noOfChars=SHRT_MAX) +{ + const char* inStr=*inString; + short number=0; + while(IsNumeric ( *inStr )&& noOfChars--) + { + number=number*10 +inStr[0]-'0'; + inStr++; + } + *inString=inStr; + return number; +} + +// ================================================================================================= +// tokeniseDateString +// ================== +// +// Parses Date string and tokenizes it for extracting different parts of a date +// +static bool tokeniseDateString(const char* inString,std::vector<PostScript_Support::DateTimeTokens> &tokens ) +{ + const char* inStr= inString; + PostScript_Support::DateTimeTokens dttoken; + //skip leading whitespace + while ( inStr[0]!='\0' ) + { + while( IsSpaceOrTab ( *inStr ) || *inStr =='(' + ||*inStr ==')' ||*inStr==',') + { + ++inStr; + } + if (*inStr=='\0') return true; + dttoken=PostScript_Support::DateTimeTokens(); + if (IsNumeric(*inStr)) + { + while(IsNumeric(*inStr)||(IsDelimiter(*inStr)&&dttoken.noOfDelimiter==0)|| + (dttoken.delimiter==*inStr && dttoken.noOfDelimiter!=0)) + { + if (IsDelimiter(*inStr)) + { + dttoken.delimiter=inStr[0]; + dttoken.noOfDelimiter++; + } + dttoken.token+=*(inStr++); + } + tokens.push_back(dttoken); + } + else if (IsAlpha(*inStr)) + { + if(*inStr=='D'&& *(inStr+1)==':') + { + inStr+=2; + while( IsNumeric(*inStr)) + { + dttoken.token+=*(inStr++); + } + } + else + { + while( IsAlpha(*inStr)) + { + dttoken.token+=*(inStr++); + } + + } + tokens.push_back(dttoken); + } + else if (*inStr=='-' ||*inStr=='+') + { + dttoken.token+=*(inStr++); + while( IsNumeric(*inStr)||*inStr==':') + { + if (*inStr==':') + { + dttoken.delimiter=inStr[0]; + dttoken.noOfDelimiter++; + } + dttoken.token+=*(inStr++); + } + tokens.push_back(dttoken); + } + else + { + ++inStr; + } + } + return true; +} + +// ================================================================================================= +// SwapMonthDateIfNeeded +// ===================== +// +// Swaps month and date value if it creates a possible valid date +// +static void SwapMonthDateIfNeeded(short &day, short&month) +{ + if(month>12&& day<13) + { + short temp=month; + month=day; + day=temp; + } +} + +// ================================================================================================= +// AdjustYearIfNeeded +// ===================== +// +// Guess the year for a two digit year in a date +// +static void AdjustYearIfNeeded(short &year) +{ + if (year<100) + { + if (year >40) + { + year=1900+year; + } + else + { + year=2000+year; + } + } +} + +static bool IsLeapYear ( XMP_Int32 year ) +{ + if ( year < 0 ) year = -year + 1; // Fold the negative years, assuming there is a year 0. + if ( (year % 4) != 0 ) return false; // Not a multiple of 4. + if ( (year % 100) != 0 ) return true; // A multiple of 4 but not a multiple of 100. + if ( (year % 400) == 0 ) return true; // A multiple of 400. + return false; // A multiple of 100 but not a multiple of 400. +} +// ================================================================================================= +// PostScript_Support::ConvertToDate +// ================================= +// +// Converts date string from native of Postscript to YYYY-MM-DDThh:mm:ssTZD if a valid date is identified +// +std::string PostScript_Support::ConvertToDate(const char* inString) +{ + PostScript_Support::Date date(0,0,0,0,0,0); + std::string dateTimeString; + short number=0; + std::vector<PostScript_Support::DateTimeTokens> tokenzs; + tokeniseDateString(inString,tokenzs ); + std::vector<PostScript_Support::DateTimeTokens>:: const_iterator itr=tokenzs.begin(); + for(;itr!=tokenzs.end();itr++) + { + if(itr->token[0]=='+' ||itr->token[0]=='-') + { + const char *str=itr->token.c_str(); + date.offsetSign=*(str++); + date.offsetHour=GetNumber(&str,2); + if (*str==':')str++; + date.offsetMin=GetNumber(&str,2); + if (!(date.offsetHour<=12 && date.offsetHour>=0 + &&date.offsetMin>=0 && date.offsetMin<=59)) + { + date.offsetSign='+'; + date.offsetHour=0; + date.offsetMin=0; + } + else + { + date.containsOffset= true; + } + } + else if(itr->noOfDelimiter!=0) + {//either a date or time token + if(itr->noOfDelimiter==2 && itr->delimiter=='/') + { + if(date.day==0&& date.month==0 && date.year==0) + { + const char *str=itr->token.c_str(); + number=GetNumber(&str); + str++; + if (number<32) + { + date.month=number; + date.day=GetNumber(&str); + SwapMonthDateIfNeeded(date.day, date.month); + str++; + date.year=GetNumber(&str); + AdjustYearIfNeeded(date.year); + } + else + { + date.year=number; + AdjustYearIfNeeded(date.year); + date.month=GetNumber(&str); + str++; + date.day=GetNumber(&str); + SwapMonthDateIfNeeded(date.day, date.month); + } + } + } + else if(itr->noOfDelimiter==2 && itr->delimiter==':') + { + const char *str=itr->token.c_str(); + date.hours=GetNumber(&str); + str++; + date.minutes=GetNumber(&str); + str++; + date.seconds=GetNumber(&str); + if (date.hours>23|| date.minutes>59|| date.seconds>59) + { + date.hours=0; + date.minutes=0; + date.seconds=0; + } + } + else if(itr->noOfDelimiter==1 && itr->delimiter==':') + { + const char *str=itr->token.c_str(); + date.hours=GetNumber(&str); + str++; + date.minutes=GetNumber(&str); + if (date.hours>23|| date.minutes>59) + { + date.hours=0; + date.minutes=0; + } + } + else if(itr->noOfDelimiter==2 && itr->delimiter=='-') + { + const char *str=itr->token.c_str(); + number=GetNumber(&str); + str++; + if (number>31) + { + date.year=number; + AdjustYearIfNeeded(date.year); + date.month=GetNumber(&str); + str++; + date.day=GetNumber(&str); + SwapMonthDateIfNeeded(date.day, date.month); + } + else + { + date.month=number; + date.day=GetNumber(&str); + str++; + SwapMonthDateIfNeeded(date.day, date.month); + date.year=GetNumber(&str); + AdjustYearIfNeeded(date.year); + } + } + else if(itr->noOfDelimiter==2 && itr->delimiter=='.') + { + const char *str=itr->token.c_str(); + date.year=GetNumber(&str); + str++; + AdjustYearIfNeeded(date.year); + date.month=GetNumber(&str); + str++; + date.day=GetNumber(&str); + SwapMonthDateIfNeeded(date.day, date.month); + + } + } + else if (IsAlpha(itr->token[0])) + { //this can be a name of the Month + // name of the day is ignored + short month=0; + std::string lowerToken=itr->token; + std::transform(lowerToken.begin(), lowerToken.end(), lowerToken.begin(), ::tolower); + if(!lowerToken.compare("jan")||!lowerToken.compare("january")) + { + month=1; + } + else if (!lowerToken.compare("feb")||!lowerToken.compare("february")) + { + month=2; + } + else if (!lowerToken.compare("mar")||!lowerToken.compare("march")) + { + month=3; + } + else if (!lowerToken.compare("apr")||!lowerToken.compare("april")) + { + month=4; + } + else if (!lowerToken.compare("may")) + { + month=5; + } + else if (!lowerToken.compare("jun")||!lowerToken.compare("june")) + { + month=6; + } + else if (!lowerToken.compare("jul")||!lowerToken.compare("july")) + { + month=7; + } + else if (!lowerToken.compare("aug")||!lowerToken.compare("august")) + { + month=8; + } + else if (!lowerToken.compare("sep")||!lowerToken.compare("september")) + { + month=9; + } + else if (!lowerToken.compare("oct")||!lowerToken.compare("october")) + { + month=10; + } + else if (!lowerToken.compare("nov")||!lowerToken.compare("november")) + { + month=11; + } + else if (!lowerToken.compare("dec")||!lowerToken.compare("december")) + { + month=12; + } + else if (!lowerToken.compare("pm")) + { + if (date.hours<13) + { + date.hours+=12; + } + } + else if (itr->token.length()>14) + { + constructDateTime(itr->token,dateTimeString); + } + if(month!=0 && date.month==0) + { + date.month=month; + if(date.day==0) + { + if(itr!=tokenzs.begin()) + { + --itr; + if (itr->noOfDelimiter==0 && IsNumeric(itr->token[0]) ) + { + const char * str=itr->token.c_str(); + short day= GetNumber(&str); + if (day<=31 &&day >=1) + { + date.day=day; + } + } + ++itr; + } + if(itr!=tokenzs.end()) + { + ++itr; + if (itr!=tokenzs.end()&&itr->noOfDelimiter==0 && IsNumeric(itr->token[0]) ) + { + const char * str=itr->token.c_str(); + short day= GetNumber(&str); + if (day<=31 &&day >=1) + { + date.day=day; + } + } + } + } + } + } + else if (IsNumeric(itr->token[0]) && date.year==0&&itr->token.length()==4) + {//this is the year + const char * str=itr->token.c_str(); + date.year=GetNumber(&str); + } + else if (itr->token.length()>=14) + { + constructDateTime(itr->token,dateTimeString); + } + } + if (dateTimeString.length()==0) + { + char dtstr[100]; + XMP_DateTime datetime; + if ( date.year < 10000 && date.month < 13 && date.month > 0 && date.day > 0 ) + { + bool isValidDate=true; + if ( date.month == 2 ) + { + if ( IsLeapYear ( date.year ) ) + { + if ( date.day > 29 ) isValidDate=false; + } + else + { + if ( date.day > 28 ) isValidDate=false; + } + } + else if ( date.month == 4 || date.month == 6 || date.month == 9 + || date.month == 11 ) + { + if ( date.day > 30 ) isValidDate=false; + } + else + { + if ( date.day > 31 ) isValidDate=false; + } + if( isValidDate && ! ( date == PostScript_Support::Date ( 0, 0, 0, 0, 0, 0 ) ) ) + { + if ( date.containsOffset ) + { + sprintf(dtstr,"%04d-%02d-%02dT%02d:%02d:%02d%c%02d:%02d",date.year,date.month,date.day, + date.hours,date.minutes,date.seconds,date.offsetSign,date.offsetHour,date.offsetMin); + } + else + { + sprintf(dtstr,"%04d-%02d-%02dT%02d:%02d:%02dZ",date.year,date.month,date.day, + date.hours,date.minutes,date.seconds); + } + try + { + + SXMPUtils::ConvertToDate ( dtstr, &datetime ) ; + SXMPUtils::ConvertFromDate ( datetime, &dateTimeString ) ; + } + catch(...) + { + } + } + } + } + + return dateTimeString; +} +// ================================================================================================= diff --git a/XMPFiles/source/FormatSupport/PostScript_Support.hpp b/XMPFiles/source/FormatSupport/PostScript_Support.hpp new file mode 100644 index 0000000..688fa6d --- /dev/null +++ b/XMPFiles/source/FormatSupport/PostScript_Support.hpp @@ -0,0 +1,266 @@ +#ifndef __PostScript_Support_hpp__ +#define __PostScript_Support_hpp__ 1 + +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2012 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "public/include/XMP_Environment.h" + +#include "public/include/XMP_Const.h" +#include "public/include/XMP_IO.hpp" + +#include "XMPFiles/source/XMPFiles_Impl.hpp" +#include "source/XMPFiles_IO.hpp" +#include "source/XIO.hpp" + + +#define MAX_NO_MARK 100 +#define IsNumeric( ch ) ( ch >='0' && ch<='9' ) +#define Uns8Ptr(p) ((XMP_Uns8 *) (p)) +#define IsPlusMinusSign(ch) ( ch =='+' || ch=='-' ) +#define IsDateDelimiter( ch ) ( ((ch) == '/') || ((ch) == '-') || ((ch) == '.') ) +#define IsTimeDelimiter( ch ) ( ((ch) == ':') ) +#define IsDelimiter(ch) (IsDateDelimiter( ch ) || IsTimeDelimiter( ch )) +#define IsAlpha(ch) ((ch>=97 &&ch <=122) || (ch>=65 && ch<=91)) + + +enum { + kPSHint_NoMarker = 0, + kPSHint_NoMain = 1, + kPSHint_MainFirst = 2, + kPSHint_MainLast = 3 +}; + +enum UpdateMethod{ + kPS_None = 0, + kPS_Inplace = 1, + kPS_ExpandSFDFilter = 2, + kPS_InjectNew = 3 +}; + +enum TokenFlag{ + // -------------------- + // Flags for native Metadata and DSC commnents in EPS format + + /// No Native MetaData + kPS_NoData = 0x00000001, + /// Document Creator tool + kPS_CreatorTool = 0x00000002, + /// Document Creation Date + kPS_CreateDate = 0x00000004, + /// Document Modify Date + kPS_ModifyDate = 0x00000008, + /// Document Creator/Author + kPS_Creator = 0x00000010, + /// Document Title + kPS_Title = 0x00000020, + /// Document Desciption + kPS_Description = 0x00000040, + /// Document Subject/Keywords + kPS_Subject = 0x00000080, + /// ADO_ContainsXMP hint + kPS_ADOContainsXMP = 0x00000100, + /// End Comments + kPS_EndComments = 0x00000200, + /// Begin Prolog + kPS_BeginProlog = 0x00000400, + /// End Prolog + kPS_EndProlog = 0x00000800, + /// Begin Setup + kPS_BeginSetup = 0x00001000, + /// End Setup + kPS_EndSetup = 0x00002000, + /// Page + kPS_Page = 0x00004000, + /// End Page Comments + kPS_EndPageComments = 0x00008000, + /// Begin Page SetUp + kPS_BeginPageSetup = 0x00010000, + /// End Page SetUp + kPS_EndPageSetup = 0x00020000, + /// Trailer + kPS_Trailer = 0x00040000, + /// EOF + kPS_EOF = 0x00080000, + /// End PostScript + kPS_EndPostScript = 0x00100000, + /// Max Token + kPS_MaxToken = 0x00200000 +}; + +enum NativeMetadataIndex{ + // -------------------- + // Index native Metadata ina PS file + kPS_dscCreator = 0, + kPS_dscCreateDate = 1, + kPS_dscFor = 2, + kPS_dscTitle = 3, + kPS_docInfoCreator = 4, + kPS_docInfoCreateDate = 5, + kPS_docInfoModDate = 6, + kPS_docInfoAuthor = 7, + kPS_docInfoTitle = 8, + kPS_docInfoSubject = 9, + kPS_docInfoKeywords = 10, + kPS_MaxNativeIndexValue +}; + +static XMP_Uns64 nativeIndextoFlag[]={ kPS_CreatorTool, + kPS_CreateDate, + kPS_Creator, + kPS_Title, + kPS_CreatorTool, + kPS_CreateDate, + kPS_ModifyDate, + kPS_Creator, + kPS_Title, + kPS_Description, + kPS_Subject + }; + +static const std::string kPSFileTag = "%!PS-Adobe-"; +static const std::string kPSContainsXMPString = "%ADO_ContainsXMP:"; +static const std::string kPSContainsBBoxString = "%%BoundingBox:"; +static const std::string kPSContainsBeginDocString = "%%BeginDocument:"; +static const std::string kPSContainsEndDocString = "%%EndDocument"; +static const std::string kPSContainsTrailerString = "%%Trailer"; +static const std::string kPSContainsCreatorString = "%%Creator:"; +static const std::string kPSContainsCreateDateString = "%%CreationDate:"; +static const std::string kPSContainsForString = "%%For:"; +static const std::string kPSContainsTitleString = "%%Title:"; +static const std::string kPSContainsAtendString = "(atend)"; +static const std::string kPSEndCommentString = "%%EndComments"; // ! Assumed shorter than kPSContainsXMPString. +static const std::string kPSContainsDocInfoString = "/DOCINFO"; +static const std::string kPSContainsPdfmarkString = "pdfmark"; +static const std::string kPS_XMPHintMainFirst="%ADO_ContainsXMP: MainFirst\n"; +static const std::string kPS_XMPHintMainLast="%ADO_ContainsXMP: MainLast\n"; + +// For new xpacket injection into the EPS file is done in Postscript using the pdfmark operator +// There are different conventions described for EPS and PS files in XMP Spec part 3. +// The tokens kEPS_Injectdata1, kEPS_Injectdata2 and kEPS_Injectdata3 are used to +// embedd xpacket in EPS files.the xpacket is written inbetween kEPS_Injectdata1 and kEPS_Injectdata2. +// The tokens kPS_Injectdata1 and kPS_Injectdata2 are used to embedd xpacket in DSC compliant PS files +// The code inside the tokens is taken from examples in XMP Spec part 3 +// section 2.6.2 PS, EPS (PostScript® and Encapsulated PostScript) +static const std::string kEPS_Injectdata1="\n/currentdistillerparams where\n" +"{pop currentdistillerparams /CoreDistVersion get 5000 lt} {true} ifelse\n" +"{userdict /EPSHandler1_pdfmark /cleartomark load put\n" +"userdict /EPSHandler1_ReadMetadata_pdfmark {flushfile cleartomark} bind put}\n" +"{ userdict /EPSHandler1_pdfmark /pdfmark load put\n" +"userdict /EPSHandler1_ReadMetadata_pdfmark {/PUT pdfmark} bind put } ifelse\n" +"[/NamespacePush EPSHandler1_pdfmark\n" +"[/_objdef {eps_metadata_stream} /type /stream /OBJ EPSHandler1_pdfmark\n" +"[{eps_metadata_stream} 2 dict begin\n" +"/Type /Metadata def /Subtype /XML def currentdict end /PUT EPSHandler1_pdfmark\n" +"[{eps_metadata_stream}\n" +"currentfile 0 (% &&end EPS XMP packet marker&&)\n" +"/SubFileDecode filter EPSHandler1_ReadMetadata_pdfmark\n"; + +static const std::string kPS_Injectdata1="\n/currentdistillerparams where\n" +"{pop currentdistillerparams /CoreDistVersion get 5000 lt} {true} ifelse\n" +"{userdict /PSHandler1_pdfmark /cleartomark load put\n" +"userdict /PSHandler1_ReadMetadata_pdfmark {flushfile cleartomark} bind put}\n" +"{ userdict /PSHandler1_pdfmark /pdfmark load put\n" +"userdict /PSHandler1_ReadMetadata_pdfmark {/PUT pdfmark} bind put } ifelse\n" +"[/NamespacePush PSHandler1_pdfmark\n" +"[/_objdef {ps_metadata_stream} /type /stream /OBJ PSHandler1_pdfmark\n" +"[{ps_metadata_stream} 2 dict begin\n" +"/Type /Metadata def /Subtype /XML def currentdict end /PUT PSHandler1_pdfmark\n" +"[{ps_metadata_stream}\n" +"currentfile 0 (% &&end PS XMP packet marker&&)\n" +"/SubFileDecode filter PSHandler1_ReadMetadata_pdfmark\n"; + +static const std::string kEPS_Injectdata2="\n% &&end EPS XMP packet marker&&\n" +"[/Document\n" +"1 dict begin /Metadata {eps_metadata_stream} def\n" +"currentdict end /BDC EPSHandler1_pdfmark\n" +"[/NamespacePop EPSHandler1_pdfmark\n"; + + +static const std::string kPS_Injectdata2="\n% &&end PS XMP packet marker&&\n" +"[{Catalog} {ps_metadata_stream} /Metadata PSHandler1_pdfmark\n" +"[/NamespacePop PSHandler1_pdfmark\n"; + +static const std::string kEPS_Injectdata3="\n/currentdistillerparams where\n" + "{pop currentdistillerparams /CoreDistVersion get 5000 lt} {true} ifelse\n" + "{userdict /EPSHandler1_pdfmark /cleartomark load put}\n" + "{ userdict /EPSHandler1_pdfmark /pdfmark load put} ifelse\n" + "[/EMC EPSHandler1_pdfmark\n"; + + +namespace PostScript_Support +{ + struct Date + { + short day; + short month; + short year; + short hours; + short minutes; + short seconds; + bool containsOffset; + char offsetSign; + short offsetHour; + short offsetMin; + Date(short pday=1,short pmonth=1,short pyear=1900,short phours=0, + short pminutes=0,short pseconds=0):day(pday),month(pmonth), + year(pyear),hours(phours),minutes(pminutes),seconds(pseconds), + containsOffset(false),offsetSign('+'),offsetHour(0),offsetMin(0) + { + } + bool operator==(const Date &a) + { + return this->day==a.day && + this->month==a.month && + this->year==a.year && + this->hours==a.hours && + this->minutes==a.minutes && + this->seconds==a.seconds && + this->containsOffset==a.containsOffset && + this->offsetSign==a.offsetSign && + this->offsetHour==a.offsetHour && + this->offsetMin==a.offsetMin; + } + }; + struct DateTimeTokens + { + std::string token; + short noOfDelimiter; + char delimiter; + DateTimeTokens(std::string ptoken="",short pnoOfDelimiter=0,char pdelimiter=0): + token(ptoken),noOfDelimiter(pnoOfDelimiter),delimiter(pdelimiter) + { + } + }; + + //function to parse strings and get date out of it + std::string ConvertToDate(const char* inString); + // These helpers are similar to RefillBuffer and CheckFileSpace with the difference that the it traverses + // the file stream in reverse order + void RevRefillBuffer ( XMP_IO* fileRef, IOBuffer* ioBuf ); + bool RevCheckFileSpace ( XMP_IO* fileRef, IOBuffer* ioBuf, size_t neededLen ); + + // function to detect character codes greater than 127 in a string + bool HasCodesGT127(const std::string & value); + + // function moves the file pointer ahead such that it skips all tabs and spaces + bool SkipTabsAndSpaces(XMP_IO* file,IOBuffer& ioBuf); + + // function moves the file pointer ahead such that it skips all characters until a newline + bool SkipUntilNewline(XMP_IO* file,IOBuffer& ioBuf); + + // function to detect character codes greater than 127 in a string + bool IsValidPSFile(XMP_IO* fileRef,XMP_FileFormat &format); + + // Determines Whether the metadata is embedded using the Sub-FileDecode Approach or no + bool IsSFDFilterUsed(XMP_IO* &fileRef, XMP_Int64 xpacketOffset); + +} // namespace PostScript_Support + +#endif // __PostScript_Support_hpp__ diff --git a/XMPFiles/source/FormatSupport/QuickTime_Support.cpp b/XMPFiles/source/FormatSupport/QuickTime_Support.cpp index 5a33ab4..5dde0c5 100644 --- a/XMPFiles/source/FormatSupport/QuickTime_Support.cpp +++ b/XMPFiles/source/FormatSupport/QuickTime_Support.cpp @@ -22,6 +22,7 @@ #include "source/UnicodeInlines.incl_cpp" #include "XMPFiles/source/FormatSupport/Reconcile_Impl.hpp" #include "source/XIO.hpp" +#include "source/EndianUtils.hpp" // ================================================================================================= diff --git a/XMPFiles/source/FormatSupport/RIFF.hpp b/XMPFiles/source/FormatSupport/RIFF.hpp index 533d9a3..e2451e5 100644 --- a/XMPFiles/source/FormatSupport/RIFF.hpp +++ b/XMPFiles/source/FormatSupport/RIFF.hpp @@ -174,7 +174,11 @@ namespace RIFF { // ================================================================================================= // ImportCr8rItems // =============== +#if SUNOS_SPARC || SUNOS_X86 + #pragma pack ( 1 ) +#else #pragma pack ( push, 1 ) +#endif //#if SUNOS_SPARC || SUNOS_X86 struct PrmLBoxContent { XMP_Uns32 magic; XMP_Uns32 size; @@ -199,7 +203,11 @@ namespace RIFF { char appOptions[16]; char appName[32]; }; +#if SUNOS_SPARC || SUNOS_X86 + #pragma pack ( ) +#else #pragma pack ( pop ) +#endif //#if SUNOS_SPARC || SUNOS_X86 // static getter, determines appropriate chunkType (peeking)and returns // the respective constructor. It's the caller's responsibility to diff --git a/XMPFiles/source/FormatSupport/ReconcileTIFF.cpp b/XMPFiles/source/FormatSupport/ReconcileTIFF.cpp index 225c91f..5aa4435 100644 --- a/XMPFiles/source/FormatSupport/ReconcileTIFF.cpp +++ b/XMPFiles/source/FormatSupport/ReconcileTIFF.cpp @@ -19,6 +19,8 @@ #define snprintf _snprintf #endif +#include "source/EndianUtils.hpp" + #if XMP_WinBuild #pragma warning ( disable : 4146 ) // unary minus operator applied to unsigned type #pragma warning ( disable : 4800 ) // forcing value to bool 'true' or 'false' @@ -387,7 +389,7 @@ ImportSingleTIFF_Short ( const TIFF_Manager::TagInfo & tagInfo, const bool nativ { try { // Don't let errors with one stop the others. - XMP_Uns16 binValue = *((XMP_Uns16*)tagInfo.dataPtr); + XMP_Uns16 binValue = GetUns16AsIs ( tagInfo.dataPtr ); if ( ! nativeEndian ) binValue = Flip2 ( binValue ); char strValue[20]; @@ -412,7 +414,7 @@ ImportSingleTIFF_Long ( const TIFF_Manager::TagInfo & tagInfo, const bool native { try { // Don't let errors with one stop the others. - XMP_Uns32 binValue = *((XMP_Uns32*)tagInfo.dataPtr); + XMP_Uns32 binValue = GetUns32AsIs ( tagInfo.dataPtr ); if ( ! nativeEndian ) binValue = Flip4 ( binValue ); char strValue[20]; @@ -438,8 +440,8 @@ ImportSingleTIFF_Rational ( const TIFF_Manager::TagInfo & tagInfo, const bool na try { // Don't let errors with one stop the others. XMP_Uns32 * binPtr = (XMP_Uns32*)tagInfo.dataPtr; - XMP_Uns32 binNum = binPtr[0]; - XMP_Uns32 binDenom = binPtr[1]; + XMP_Uns32 binNum = GetUns32AsIs ( &binPtr[0] ); + XMP_Uns32 binDenom = GetUns32AsIs ( &binPtr[1] ); if ( ! nativeEndian ) { binNum = Flip4 ( binNum ); binDenom = Flip4 ( binDenom ); @@ -467,9 +469,14 @@ ImportSingleTIFF_SRational ( const TIFF_Manager::TagInfo & tagInfo, const bool n { try { // Don't let errors with one stop the others. - XMP_Int32 * binPtr = (XMP_Int32*)tagInfo.dataPtr; - XMP_Int32 binNum = binPtr[0]; - XMP_Int32 binDenom = binPtr[1]; +#if SUNOS_SPARC + XMP_Uns32 binPtr[2]; + memcpy(&binPtr, tagInfo.dataPtr, sizeof(XMP_Uns32)*2); +#else + XMP_Uns32 * binPtr = (XMP_Uns32*)tagInfo.dataPtr; +#endif //#if SUNOS_SPARC + XMP_Int32 binNum = GetUns32AsIs ( &binPtr[0] ); + XMP_Int32 binDenom = GetUns32AsIs ( &binPtr[1] ); if ( ! nativeEndian ) { Flip4 ( &binNum ); Flip4 ( &binDenom ); @@ -537,7 +544,7 @@ ImportSingleTIFF_Byte ( const TIFF_Manager::TagInfo & tagInfo, XMP_Uns8 binValue = *((XMP_Uns8*)tagInfo.dataPtr); char strValue[20]; - snprintf ( strValue, sizeof(strValue), "%hu", binValue ); // AUDIT: Using sizeof(strValue) is safe. + snprintf ( strValue, sizeof(strValue), "%hu", (XMP_Uns16)binValue ); // AUDIT: Using sizeof(strValue) is safe. xmp->SetProperty ( xmpNS, xmpProp, strValue ); @@ -561,7 +568,7 @@ ImportSingleTIFF_SByte ( const TIFF_Manager::TagInfo & tagInfo, XMP_Int8 binValue = *((XMP_Int8*)tagInfo.dataPtr); char strValue[20]; - snprintf ( strValue, sizeof(strValue), "%hd", binValue ); // AUDIT: Using sizeof(strValue) is safe. + snprintf ( strValue, sizeof(strValue), "%hd", (short)binValue ); // AUDIT: Using sizeof(strValue) is safe. xmp->SetProperty ( xmpNS, xmpProp, strValue ); @@ -815,8 +822,8 @@ ImportArrayTIFF_Rational ( const TIFF_Manager::TagInfo & tagInfo, const bool nat for ( size_t i = 0; i < tagInfo.count; ++i, binPtr += 2 ) { - XMP_Uns32 binNum = binPtr[0]; - XMP_Uns32 binDenom = binPtr[1]; + XMP_Uns32 binNum = GetUns32AsIs ( &binPtr[0] ); + XMP_Uns32 binDenom = GetUns32AsIs ( &binPtr[1] ); if ( ! nativeEndian ) { binNum = Flip4 ( binNum ); binDenom = Flip4 ( binDenom ); @@ -936,7 +943,7 @@ ImportArrayTIFF_Byte ( const TIFF_Manager::TagInfo & tagInfo, XMP_Uns8 binValue = *binPtr; char strValue[20]; - snprintf ( strValue, sizeof(strValue), "%hu", binValue ); // AUDIT: Using sizeof(strValue) is safe. + snprintf ( strValue, sizeof(strValue), "%hu", (XMP_Uns16)binValue ); // AUDIT: Using sizeof(strValue) is safe. xmp->AppendArrayItem ( xmpNS, xmpProp, kXMP_PropArrayIsOrdered, strValue ); @@ -968,7 +975,7 @@ ImportArrayTIFF_SByte ( const TIFF_Manager::TagInfo & tagInfo, XMP_Int8 binValue = *binPtr; char strValue[20]; - snprintf ( strValue, sizeof(strValue), "%hd", binValue ); // AUDIT: Using sizeof(strValue) is safe. + snprintf ( strValue, sizeof(strValue), "%hd", (short)binValue ); // AUDIT: Using sizeof(strValue) is safe. xmp->AppendArrayItem ( xmpNS, xmpProp, kXMP_PropArrayIsOrdered, strValue ); @@ -1420,7 +1427,7 @@ ImportTIFF_Flash ( const TIFF_Manager::TagInfo & tagInfo, bool nativeEndian, { try { // Don't let errors with one stop the others. - XMP_Uns16 binValue = *((XMP_Uns16*)tagInfo.dataPtr); + XMP_Uns16 binValue = GetUns16AsIs ( tagInfo.dataPtr ); if ( ! nativeEndian ) binValue = Flip2 ( binValue ); bool fired = (bool)(binValue & 1); // Avoid implicit 0/1 conversion. @@ -1559,7 +1566,7 @@ ImportTIFF_CFATable ( const TIFF_Manager::TagInfo & tagInfo, bool nativeEndian, SXMPUtils::ComposeStructFieldPath ( xmpNS, xmpProp, kXMP_NS_EXIF, "Values", &arrayPath ); for ( size_t i = (columns * rows); i > 0; --i, ++bytePtr ) { - snprintf ( buffer, sizeof(buffer), "%hu", *bytePtr ); // AUDIT: Use of sizeof(buffer) is safe. + snprintf ( buffer, sizeof(buffer), "%hu", (XMP_Uns16)(*bytePtr) ); // AUDIT: Use of sizeof(buffer) is safe. xmp->AppendArrayItem ( xmpNS, arrayPath.c_str(), kXMP_PropArrayIsOrdered, buffer ); } @@ -1679,14 +1686,15 @@ ImportTIFF_GPSCoordinate ( const TIFF_Manager & tiff, const TIFF_Manager::TagInf secDenom = Flip4 ( secDenom ); } - degNum = binPtr[0]; - degDenom = binPtr[1]; + degNum = GetUns32AsIs ( &binPtr[0] ); + degDenom = GetUns32AsIs ( &binPtr[1] ); + if ( posInfo.count >= 2 ) { - minNum = binPtr[2]; - minDenom = binPtr[3]; + minNum = GetUns32AsIs ( &binPtr[2] ); + minDenom = GetUns32AsIs ( &binPtr[3] ); if ( posInfo.count >= 3 ) { - secNum = binPtr[4]; - secDenom = binPtr[5]; + secNum = GetUns32AsIs ( &binPtr[4] ); + secDenom = GetUns32AsIs ( &binPtr[5] ); } } @@ -1790,12 +1798,12 @@ ImportTIFF_GPSTimeStamp ( const TIFF_Manager & tiff, const TIFF_Manager::TagInfo if ( (dateStr[10] != 0) && (dateStr[10] != ' ') ) return; XMP_Uns32 * binPtr = (XMP_Uns32*)timeInfo.dataPtr; - XMP_Uns32 hourNum = binPtr[0]; - XMP_Uns32 hourDenom = binPtr[1]; - XMP_Uns32 minNum = binPtr[2]; - XMP_Uns32 minDenom = binPtr[3]; - XMP_Uns32 secNum = binPtr[4]; - XMP_Uns32 secDenom = binPtr[5]; + XMP_Uns32 hourNum = GetUns32AsIs ( &binPtr[0] ); + XMP_Uns32 hourDenom = GetUns32AsIs ( &binPtr[1] ); + XMP_Uns32 minNum = GetUns32AsIs ( &binPtr[2] ); + XMP_Uns32 minDenom = GetUns32AsIs ( &binPtr[3] ); + XMP_Uns32 secNum = GetUns32AsIs ( &binPtr[4] ); + XMP_Uns32 secDenom = GetUns32AsIs ( &binPtr[5] ); if ( ! nativeEndian ) { hourNum = Flip4 ( hourNum ); hourDenom = Flip4 ( hourDenom ); @@ -2116,7 +2124,8 @@ PhotoDataUtils::Import2WayExif ( const TIFF_Manager & exif, SXMPMeta * xmp, int found = exif.GetTag ( kTIFF_ExifIFD, kTIFF_ExifVersion, &tagInfo ); if ( found && (tagInfo.type == kTIFF_UndefinedType) && (tagInfo.count == 4) ) { char str[5]; - *((XMP_Uns32*)str) = *((XMP_Uns32*)tagInfo.dataPtr); + + *((XMP_Uns32*)str) = GetUns32AsIs ( tagInfo.dataPtr ); str[4] = 0; xmp->SetProperty ( kXMP_NS_EXIF, "ExifVersion", str ); } @@ -2125,7 +2134,8 @@ PhotoDataUtils::Import2WayExif ( const TIFF_Manager & exif, SXMPMeta * xmp, int found = exif.GetTag ( kTIFF_ExifIFD, kTIFF_FlashpixVersion, &tagInfo ); if ( found && (tagInfo.type == kTIFF_UndefinedType) && (tagInfo.count == 4) ) { char str[5]; - *((XMP_Uns32*)str) = *((XMP_Uns32*)tagInfo.dataPtr); + + *((XMP_Uns32*)str) = GetUns32AsIs ( tagInfo.dataPtr ); str[4] = 0; xmp->SetProperty ( kXMP_NS_EXIF, "FlashpixVersion", str ); } diff --git a/XMPFiles/source/FormatSupport/SWF_Support.cpp b/XMPFiles/source/FormatSupport/SWF_Support.cpp index ec7b9a6..5ff80f1 100644 --- a/XMPFiles/source/FormatSupport/SWF_Support.cpp +++ b/XMPFiles/source/FormatSupport/SWF_Support.cpp @@ -127,7 +127,7 @@ XMP_Int64 SWF_IO::DecompressFileToMemory ( XMP_IO * fileIn, RawDataBlock * dataO XMP_Int32 ioCount; XMP_Int64 offsetIn; const XMP_Int64 lengthIn = fileIn->Length(); - XMP_Enforce ( (SWF_IO::HeaderPrefixSize <= lengthIn) && (lengthIn <= SWF_IO::MaxExpandedSize) ); + XMP_Enforce ( ((XMP_Int64)SWF_IO::HeaderPrefixSize <= lengthIn) && (lengthIn <= SWF_IO::MaxExpandedSize) ); // Set the uncompressed part of the header. Save the expanded size from the file. diff --git a/XMPFiles/source/FormatSupport/TIFF_FileWriter.cpp b/XMPFiles/source/FormatSupport/TIFF_FileWriter.cpp index 40cb9bd..a3bf808 100644 --- a/XMPFiles/source/FormatSupport/TIFF_FileWriter.cpp +++ b/XMPFiles/source/FormatSupport/TIFF_FileWriter.cpp @@ -11,8 +11,11 @@ #include "public/include/XMP_Const.h" #include "XMPFiles/source/FormatSupport/TIFF_Support.hpp" + #include "source/XIO.hpp" +#include "source/EndianUtils.hpp" + // ================================================================================================= /// \file TIFF_FileWriter.cpp /// \brief TIFF_FileWriter is used for memory-based read-write access and all file-based access. @@ -586,15 +589,26 @@ void TIFF_FileWriter::ParseMemoryStream ( const void* data, XMP_Uns32 length, bo memcpy ( this->memStream, data, length ); // AUDIT: Safe, malloc'ed length bytes above. this->ownedStream = true; } - + this->tiffLength = length; XMP_Uns32 ifdLimit = this->tiffLength - 6; // An IFD must start before this offset. - // Find and process the primary, Exif, GPS, and Interoperability IFDs. + // Find and process the primary, thumbnail, Exif, GPS, and Interoperability IFDs. XMP_Uns32 primaryIFDOffset = this->CheckTIFFHeader ( this->memStream, length ); - if ( primaryIFDOffset != 0 ) (void) this->ProcessMemoryIFD ( primaryIFDOffset, kTIFF_PrimaryIFD ); + if ( primaryIFDOffset != 0 ) { + XMP_Uns32 tnailOffset = this->ProcessMemoryIFD ( primaryIFDOffset, kTIFF_PrimaryIFD ); + if ( tnailOffset != 0 ) { + if ( IsOffsetValid ( tnailOffset, 8, ifdLimit ) ) { // Remove a bad Thumbnail IFD Offset + ( void ) this->ProcessMemoryIFD ( tnailOffset, kTIFF_TNailIFD ); + } else { + XMP_Error error ( kXMPErr_BadTIFF, "Bad IFD offset" ); + this->NotifyClient (kXMPErrSev_Recoverable, error ); + this->DeleteTag ( kTIFF_PrimaryIFD, kTIFF_TNailIFD ); + } + } + } const InternalTagInfo* exifIFDTag = this->FindTagInIFD ( kTIFF_PrimaryIFD, kTIFF_ExifIFDPointer ); if ( (exifIFDTag != 0) && (exifIFDTag->type == kTIFF_LongType) && (exifIFDTag->dataLen == 4) ) { @@ -605,9 +619,11 @@ void TIFF_FileWriter::ParseMemoryStream ( const void* data, XMP_Uns32 length, bo const InternalTagInfo* gpsIFDTag = this->FindTagInIFD ( kTIFF_PrimaryIFD, kTIFF_GPSInfoIFDPointer ); if ( (gpsIFDTag != 0) && (gpsIFDTag->type == kTIFF_LongType) && (gpsIFDTag->dataLen == 4) ) { XMP_Uns32 gpsOffset = this->GetUns32 ( gpsIFDTag->dataPtr ); - if ( (8 <= gpsOffset) && (gpsOffset < ifdLimit) ) { // Remove a bad GPS IFD offset. + if ( IsOffsetValid ( gpsOffset, 8, ifdLimit ) ) { // Remove a bad GPS IFD offset. (void) this->ProcessMemoryIFD ( gpsOffset, kTIFF_GPSInfoIFD ); } else { + XMP_Error error ( kXMPErr_BadTIFF, "Bad IFD offset" ); + this->NotifyClient (kXMPErrSev_Recoverable, error ); this->DeleteTag ( kTIFF_PrimaryIFD, kTIFF_GPSInfoIFDPointer ); } } @@ -615,9 +631,11 @@ void TIFF_FileWriter::ParseMemoryStream ( const void* data, XMP_Uns32 length, bo const InternalTagInfo* interopIFDTag = this->FindTagInIFD ( kTIFF_ExifIFD, kTIFF_InteroperabilityIFDPointer ); if ( (interopIFDTag != 0) && (interopIFDTag->type == kTIFF_LongType) && (interopIFDTag->dataLen == 4) ) { XMP_Uns32 interopOffset = this->GetUns32 ( interopIFDTag->dataPtr ); - if ( (8 <= interopOffset) && (interopOffset < ifdLimit) ) { // Remove a bad Interoperability IFD offset. + if ( IsOffsetValid ( interopOffset, 8, ifdLimit ) ) { // Remove a bad Interoperability IFD offset. (void) this->ProcessMemoryIFD ( interopOffset, kTIFF_InteropIFD ); } else { + XMP_Error error ( kXMPErr_BadTIFF, "Bad IFD offset" ); + this->NotifyClient (kXMPErrSev_Recoverable, error ); this->DeleteTag ( kTIFF_ExifIFD, kTIFF_InteroperabilityIFDPointer ); } } @@ -653,16 +671,22 @@ XMP_Uns32 TIFF_FileWriter::ProcessMemoryIFD ( XMP_Uns32 ifdOffset, XMP_Uns8 ifd InternalIFDInfo& ifdInfo ( this->containedIFDs[ifd] ); if ( (ifdOffset < 8) || (ifdOffset > (this->tiffLength - kEmptyIFDLength)) ) { - XMP_Throw ( "Bad IFD offset", kXMPErr_BadTIFF ); + XMP_Error error ( kXMPErr_BadTIFF, "Bad IFD offset" ); + this->NotifyClient ( kXMPErrSev_FileFatal, error ); } XMP_Uns8* ifdPtr = this->memStream + ifdOffset; XMP_Uns16 tagCount = this->GetUns16 ( ifdPtr ); RawIFDEntry* ifdEntries = (RawIFDEntry*)(ifdPtr+2); - if ( tagCount >= 0x8000 ) XMP_Throw ( "Outrageous IFD count", kXMPErr_BadTIFF ); + if ( tagCount >= 0x8000 ) { + XMP_Error error ( kXMPErr_BadTIFF, "Outrageous IFD count" ); + this->NotifyClient ( kXMPErrSev_FileFatal, error ); + } + if ( (XMP_Uns32)(2 + tagCount*12 + 4) > (this->tiffLength - ifdOffset) ) { - XMP_Throw ( "Out of bounds IFD", kXMPErr_BadTIFF ); + XMP_Error error ( kXMPErr_BadTIFF, "Out of bounds IFD" ); + this->NotifyClient ( kXMPErrSev_FileFatal, error ); } ifdInfo.origIFDOffset = ifdOffset; @@ -682,7 +706,11 @@ XMP_Uns32 TIFF_FileWriter::ProcessMemoryIFD ( XMP_Uns32 ifdOffset, XMP_Uns8 ifd InternalTagInfo& mapTag = newPos->second; mapTag.dataLen = mapTag.origDataLen = mapTag.count * (XMP_Uns32)kTIFF_TypeSizes[mapTag.type]; - mapTag.smallValue = rawTag->dataOrOffset; // Keep the value or offset in stream byte ordering. +#if SUNOS_SPARC + mapTag.smallValue = IE.getUns32(&rawTag->dataOrOffset); +#else + mapTag.smallValue = GetUns32AsIs ( &rawTag->dataOrOffset ); // Keep the value or offset in stream byte ordering. +#endif //#if SUNOS_SPARC if ( mapTag.dataLen <= 4 ) { mapTag.origDataOffset = ifdOffset + 2 + (12 * (XMP_Uns32)i) + 8; // Compute the data offset. @@ -720,39 +748,50 @@ XMP_Uns32 TIFF_FileWriter::ProcessMemoryIFD ( XMP_Uns32 ifdOffset, XMP_Uns8 ifd void TIFF_FileWriter::ParseFileStream ( XMP_IO* fileRef ) { - bool ok; - IOBuffer ioBuf; this->DeleteExistingInfo(); this->fileParsed = true; this->tiffLength = (XMP_Uns32) fileRef->Length(); - if ( this->tiffLength == 0 ) return; + if ( this->tiffLength < 8 ) return; // Ignore empty or impossibly short. + fileRef->Rewind ( ); XMP_Uns32 ifdLimit = this->tiffLength - 6; // An IFD must start before this offset. // Find and process the primary, Exif, GPS, and Interoperability IFDs. - ioBuf.filePos = 0; - fileRef->Rewind ( ); - ok = CheckFileSpace ( fileRef, &ioBuf, 8 ); - if ( ! ok ) XMP_Throw ( "TIFF too small", kXMPErr_BadTIFF ); + XMP_Uns8 tiffHeader [8]; + fileRef->ReadAll ( tiffHeader, sizeof(tiffHeader) ); + XMP_Uns32 primaryIFDOffset = this->CheckTIFFHeader ( tiffHeader, this->tiffLength ); - XMP_Uns32 primaryIFDOffset = this->CheckTIFFHeader ( ioBuf.ptr, this->tiffLength ); - - if ( primaryIFDOffset != 0 ) (void) this->ProcessFileIFD ( kTIFF_PrimaryIFD, primaryIFDOffset, fileRef, &ioBuf ); + if ( primaryIFDOffset == 0 ) { + return; + } else { + XMP_Uns32 tnailOffset = this->ProcessFileIFD ( kTIFF_PrimaryIFD, primaryIFDOffset, fileRef ); + if ( tnailOffset != 0 ) { + if ( IsOffsetValid ( tnailOffset, 8, ifdLimit ) ) { + ( void ) this->ProcessFileIFD ( kTIFF_TNailIFD, tnailOffset, fileRef ); + } else { + XMP_Error error ( kXMPErr_BadTIFF, "Bad IFD offset" ); + this->NotifyClient ( kXMPErrSev_Recoverable, error ); + this->DeleteTag( kTIFF_PrimaryIFD, kTIFF_TNailIFD ); + } + } + } const InternalTagInfo* exifIFDTag = this->FindTagInIFD ( kTIFF_PrimaryIFD, kTIFF_ExifIFDPointer ); if ( (exifIFDTag != 0) && (exifIFDTag->type == kTIFF_LongType) && (exifIFDTag->count == 1) ) { XMP_Uns32 exifOffset = this->GetUns32 ( exifIFDTag->dataPtr ); - (void) this->ProcessFileIFD ( kTIFF_ExifIFD, exifOffset, fileRef, &ioBuf ); + (void) this->ProcessFileIFD ( kTIFF_ExifIFD, exifOffset, fileRef ); } const InternalTagInfo* gpsIFDTag = this->FindTagInIFD ( kTIFF_PrimaryIFD, kTIFF_GPSInfoIFDPointer ); if ( (gpsIFDTag != 0) && (gpsIFDTag->type == kTIFF_LongType) && (gpsIFDTag->count == 1) ) { XMP_Uns32 gpsOffset = this->GetUns32 ( gpsIFDTag->dataPtr ); - if ( (8 <= gpsOffset) && (gpsOffset < ifdLimit) ) { // Remove a bad GPS IFD offset. - (void) this->ProcessFileIFD ( kTIFF_GPSInfoIFD, gpsOffset, fileRef, &ioBuf ); + if ( IsOffsetValid (gpsOffset, 8, ifdLimit ) ) { // Remove a bad GPS IFD offset. + (void) this->ProcessFileIFD ( kTIFF_GPSInfoIFD, gpsOffset, fileRef ); } else { + XMP_Error error ( kXMPErr_BadTIFF, "Bad IFD offset" ); + this->NotifyClient ( kXMPErrSev_Recoverable, error ); this->DeleteTag ( kTIFF_PrimaryIFD, kTIFF_GPSInfoIFDPointer ); } } @@ -760,9 +799,11 @@ void TIFF_FileWriter::ParseFileStream ( XMP_IO* fileRef ) const InternalTagInfo* interopIFDTag = this->FindTagInIFD ( kTIFF_ExifIFD, kTIFF_InteroperabilityIFDPointer ); if ( (interopIFDTag != 0) && (interopIFDTag->type == kTIFF_LongType) && (interopIFDTag->dataLen == 4) ) { XMP_Uns32 interopOffset = this->GetUns32 ( interopIFDTag->dataPtr ); - if ( (8 <= interopOffset) && (interopOffset < ifdLimit) ) { // Remove a bad Interoperability IFD offset. - (void) this->ProcessFileIFD ( kTIFF_InteropIFD, interopOffset, fileRef, &ioBuf ); + if ( IsOffsetValid (interopOffset, 8, ifdLimit ) ) { // Remove a bad Interoperability IFD offset. + (void) this->ProcessFileIFD ( kTIFF_InteropIFD, interopOffset, fileRef ); } else { + XMP_Error error ( kXMPErr_BadTIFF, "Bad IFD offset" ); + this->NotifyClient ( kXMPErrSev_Recoverable, error ); this->DeleteTag ( kTIFF_ExifIFD, kTIFF_InteroperabilityIFDPointer ); } } @@ -792,25 +833,36 @@ void TIFF_FileWriter::ParseFileStream ( XMP_IO* fileRef ) // ================================================================================================= // TIFF_FileWriter::ProcessFileIFD // =============================== +// +// Each IFD has a UInt16 count of IFD entries, a sequence of 12 byte IFD entries, then a UInt32 +// offset to the next IFD. The integer byte order is determined by the II or MM at the TIFF start. -XMP_Uns32 TIFF_FileWriter::ProcessFileIFD ( XMP_Uns8 ifd, XMP_Uns32 ifdOffset, XMP_IO* fileRef, void* _ioBuf ) +XMP_Uns32 TIFF_FileWriter::ProcessFileIFD ( XMP_Uns8 ifd, XMP_Uns32 ifdOffset, XMP_IO* fileRef ) { - IOBuffer* ioBuf = (IOBuffer*)_ioBuf; // *** Temporary hack, _ioBuf is IOBuffer* but don't want to include XIO.hpp. + static const size_t ifdBufferSize = 12*65536; // Enough for the largest possible IFD. + std::vector<XMP_Uns8> ifdBuffer(ifdBufferSize); + XMP_Uns8 intBuffer [4]; // For the IFD count and offset to next IFD. + InternalIFDInfo& ifdInfo ( this->containedIFDs[ifd] ); if ( (ifdOffset < 8) || (ifdOffset > (this->tiffLength - kEmptyIFDLength)) ) { XMP_Throw ( "Bad IFD offset", kXMPErr_BadTIFF ); } - MoveToOffset ( fileRef, ifdOffset, ioBuf ); // Move to the start of the IFD. + fileRef->Seek ( ifdOffset, kXMP_SeekFromStart ); + if ( ! XIO::CheckFileSpace ( fileRef, 2 ) ) return 0; // Bail for a truncated file. + fileRef->ReadAll ( intBuffer, 2 ); - bool ok = CheckFileSpace ( fileRef, ioBuf, 2 ); - if ( ! ok ) XMP_Throw ( "IFD count missing", kXMPErr_BadTIFF ); - XMP_Uns16 tagCount = this->GetUns16 ( ioBuf->ptr ); + XMP_Uns16 tagCount = this->GetUns16 ( intBuffer ); + if ( tagCount >= 0x8000 ) return 0; // Maybe wrong byte order. + if ( ! XIO::CheckFileSpace ( fileRef, 12*tagCount ) ) return 0; // Bail for a truncated file. + fileRef->ReadAll ( &ifdBuffer[0], 12*tagCount ); - if ( tagCount >= 0x8000 ) XMP_Throw ( "Outrageous IFD count", kXMPErr_BadTIFF ); - if ( (XMP_Uns32)(2 + tagCount*12 + 4) > (this->tiffLength - ifdOffset) ) { - XMP_Throw ( "Out of bounds IFD", kXMPErr_BadTIFF ); + if ( ! XIO::CheckFileSpace ( fileRef, 4 ) ) { + ifdInfo.origNextIFD = 0; // Tolerate a trncated file, do the remaining processing. + } else { + fileRef->ReadAll ( intBuffer, 4 ); + ifdInfo.origNextIFD = this->GetUns32 ( intBuffer ); } ifdInfo.origIFDOffset = ifdOffset; @@ -822,13 +874,11 @@ XMP_Uns32 TIFF_FileWriter::ProcessFileIFD ( XMP_Uns8 ifd, XMP_Uns32 ifdOffset, X // sorted output. Plus the "map[key] = value" assignment conveniently keeps the last encountered // value, following Photoshop's behavior. - ioBuf->ptr += 2; // Move to the first IFD entry. + XMP_Uns8* ifdPtr = &ifdBuffer[0]; // Move to the first IFD entry. - for ( XMP_Uns16 i = 0; i < tagCount; ++i, ioBuf->ptr += 12 ) { + for ( XMP_Uns16 i = 0; i < tagCount; ++i, ifdPtr += 12 ) { - if ( ! CheckFileSpace ( fileRef, ioBuf, 12 ) ) XMP_Throw ( "EOF within IFD", kXMPErr_BadTIFF ); - - RawIFDEntry* rawTag = (RawIFDEntry*)ioBuf->ptr; + RawIFDEntry* rawTag = (RawIFDEntry*)ifdPtr; XMP_Uns16 tagType = this->GetUns16 ( &rawTag->type ); if ( (tagType < kTIFF_ByteType) || (tagType > kTIFF_LastType) ) continue; // Bad type, skip this tag. @@ -840,7 +890,7 @@ XMP_Uns32 TIFF_FileWriter::ProcessFileIFD ( XMP_Uns8 ifd, XMP_Uns32 ifdOffset, X InternalTagInfo& mapTag = newPos->second; mapTag.dataLen = mapTag.origDataLen = mapTag.count * (XMP_Uns32)kTIFF_TypeSizes[mapTag.type]; - mapTag.smallValue = rawTag->dataOrOffset; // Keep the value or offset in stream byte ordering. + mapTag.smallValue = GetUns32AsIs ( &rawTag->dataOrOffset ); // Keep the value or offset in stream byte ordering. if ( mapTag.dataLen <= 4 ) { mapTag.dataPtr = (XMP_Uns8*) &mapTag.smallValue; @@ -861,25 +911,14 @@ XMP_Uns32 TIFF_FileWriter::ProcessFileIFD ( XMP_Uns8 ifd, XMP_Uns32 ifdOffset, X } - if ( ! CheckFileSpace ( fileRef, ioBuf, 4 ) ) XMP_Throw ( "EOF at next IFD offset", kXMPErr_BadTIFF ); - ifdInfo.origNextIFD = this->GetUns32 ( ioBuf->ptr ); - - // --------------------------------------------------------------------------------------------- - // Go back over the tag map and extract the data for large recognized tags. This is done in 2 - // passes, in order to lessen the typical amount of I/O. On the first pass make sure we have at - // least 32K of data following the IFD in the buffer, and extract all of the values in that - // portion. This should cover an original file, or the appended values with an appended IFD. - - if ( (ioBuf->limit - ioBuf->ptr) < 32*1024 ) RefillBuffer ( fileRef, ioBuf ); + // ------------------------------------------------------------------------ + // Go back over the tag map and extract the data for large recognized tags. InternalTagMap::iterator tagPos = ifdInfo.tagMap.begin(); InternalTagMap::iterator tagEnd = ifdInfo.tagMap.end(); const XMP_Uns16* knownTagPtr = sKnownTags[ifd]; // Points into the ordered recognized tag list. - XMP_Uns32 bufBegin = (XMP_Uns32)ioBuf->filePos; // TIFF stream bounds for the current buffer. - XMP_Uns32 bufEnd = bufBegin + (XMP_Uns32)ioBuf->len; - for ( ; tagPos != tagEnd; ++tagPos ) { InternalTagInfo* currTag = &tagPos->second; @@ -889,46 +928,10 @@ XMP_Uns32 TIFF_FileWriter::ProcessFileIFD ( XMP_Uns8 ifd, XMP_Uns32 ifdOffset, X while ( *knownTagPtr < currTag->id ) ++knownTagPtr; if ( *knownTagPtr != currTag->id ) continue; // Skip unrecognized tags. - if ( (bufBegin <= currTag->origDataOffset) && ((currTag->origDataOffset + currTag->dataLen) <= bufEnd) ) { - // This value is already fully within the current I/O buffer, copy it. - MoveToOffset ( fileRef, currTag->origDataOffset, ioBuf ); - currTag->dataPtr = (XMP_Uns8*) malloc ( currTag->dataLen ); - if ( currTag->dataPtr == 0 ) XMP_Throw ( "No data block", kXMPErr_NoMemory ); - memcpy ( currTag->dataPtr, ioBuf->ptr, currTag->dataLen ); // AUDIT: Safe, malloc'ed currTag->dataLen bytes above. - } - - } - - // --------------------------------------------------------------------------------------------- - // Now the second large value pass. This will reposition the I/O buffer as necessary. Hopefully - // just once, to pick up the span of data not covered in the first pass. - - tagPos = ifdInfo.tagMap.begin(); // Reset both map/array positions. - knownTagPtr = sKnownTags[ifd]; - - for ( ; tagPos != tagEnd; ++tagPos ) { - - InternalTagInfo* currTag = &tagPos->second; - - if ( (currTag->dataLen <= 4) || (currTag->dataPtr != 0) ) continue; // Done this tag? - while ( *knownTagPtr < currTag->id ) ++knownTagPtr; - if ( *knownTagPtr != currTag->id ) continue; // Skip unrecognized tags. - + fileRef->Seek ( currTag->origDataOffset, kXMP_SeekFromStart ); currTag->dataPtr = (XMP_Uns8*) malloc ( currTag->dataLen ); if ( currTag->dataPtr == 0 ) XMP_Throw ( "No data block", kXMPErr_NoMemory ); - - if ( currTag->dataLen > kIOBufferSize ) { - // This value is bigger than the I/O buffer, read it directly and restore the file position. - fileRef->Seek ( currTag->origDataOffset, kXMP_SeekFromStart ); - fileRef->ReadAll ( currTag->dataPtr, currTag->dataLen ); - fileRef->Seek ( (ioBuf->filePos + ioBuf->len), kXMP_SeekFromStart ); - } else { - // This value can fit in the I/O buffer, so use that. - MoveToOffset ( fileRef, currTag->origDataOffset, ioBuf ); - ok = CheckFileSpace ( fileRef, ioBuf, currTag->dataLen ); - if ( ! ok ) XMP_Throw ( "EOF in data block", kXMPErr_BadTIFF ); - memcpy ( currTag->dataPtr, ioBuf->ptr, currTag->dataLen ); // AUDIT: Safe, malloc'ed currTag->dataLen bytes above. - } + fileRef->ReadAll ( currTag->dataPtr, currTag->dataLen ); } @@ -1262,6 +1265,9 @@ XMP_Uns32 TIFF_FileWriter::DetermineVisibleLength() #define Trace_DetermineAppendInfo 0 #endif +// An IFD grows if it has more tags than before. +#define DoesIFDGrow(ifd) (this->containedIFDs[ifd].origCount < this->containedIFDs[ifd].tagMap.size()) + XMP_Uns32 TIFF_FileWriter::DetermineAppendInfo ( XMP_Uns32 appendedOrigin, bool appendedIFDs[kTIFF_KnownIFDCount], XMP_Uns32 newIFDOffsets[kTIFF_KnownIFDCount], @@ -1306,40 +1312,38 @@ XMP_Uns32 TIFF_FileWriter::DetermineAppendInfo ( XMP_Uns32 appendedOrigin, for ( int i = 0; i < kTIFF_KnownIFDCount ;++i ) appendedIFDs[i] = (this->containedIFDs[i].tagMap.size() > 0); } - appendedIFDs[kTIFF_InteropIFD] |= (this->containedIFDs[kTIFF_InteropIFD].origCount < - this->containedIFDs[kTIFF_InteropIFD].tagMap.size()); + appendedIFDs[kTIFF_InteropIFD] |= DoesIFDGrow ( kTIFF_InteropIFD ); if ( appendedIFDs[kTIFF_InteropIFD] ) { this->SetTag_Long ( kTIFF_ExifIFD, kTIFF_InteroperabilityIFDPointer, 0xABADABAD ); } - appendedIFDs[kTIFF_GPSInfoIFD] |= (this->containedIFDs[kTIFF_GPSInfoIFD].origCount < - this->containedIFDs[kTIFF_GPSInfoIFD].tagMap.size()); + appendedIFDs[kTIFF_GPSInfoIFD] |= DoesIFDGrow ( kTIFF_GPSInfoIFD ); if ( appendedIFDs[kTIFF_GPSInfoIFD] ) { this->SetTag_Long ( kTIFF_PrimaryIFD, kTIFF_GPSInfoIFDPointer, 0xABADABAD ); } - appendedIFDs[kTIFF_ExifIFD] |= (this->containedIFDs[kTIFF_ExifIFD].origCount < - this->containedIFDs[kTIFF_ExifIFD].tagMap.size()); + appendedIFDs[kTIFF_ExifIFD] |= DoesIFDGrow ( kTIFF_ExifIFD ); if ( appendedIFDs[kTIFF_ExifIFD] ) { this->SetTag_Long ( kTIFF_PrimaryIFD, kTIFF_ExifIFDPointer, 0xABADABAD ); } - appendedIFDs[kTIFF_PrimaryIFD] |= (this->containedIFDs[kTIFF_PrimaryIFD].origCount < - this->containedIFDs[kTIFF_PrimaryIFD].tagMap.size()); + appendedIFDs[kTIFF_PrimaryIFD] |= DoesIFDGrow ( kTIFF_PrimaryIFD ); // The appended data (if any) will be a sequence of an IFD followed by its large values. // Determine the new offsets for the appended IFDs and tag values, and the total amount of - // appended stuff. + // appended stuff. The final IFD offset is set in newIFDOffsets for all IFDs, changed or not. + // This makes it easier to set the offsets to the primary and thumbnail IFDs when writing. for ( int ifd = 0; ifd < kTIFF_KnownIFDCount ;++ifd ) { InternalIFDInfo& ifdInfo ( this->containedIFDs[ifd] ); size_t tagCount = ifdInfo.tagMap.size(); + newIFDOffsets[ifd] = ifdInfo.origIFDOffset; // Make the new offset valid for unchanged IFDs. + if ( ! (appendAll | ifdInfo.changed) ) continue; if ( tagCount == 0 ) continue; - newIFDOffsets[ifd] = ifdInfo.origIFDOffset; if ( appendedIFDs[ifd] ) { newIFDOffsets[ifd] = appendedOrigin + appendedLength; appendedLength += (XMP_Uns32)( 6 + (12 * tagCount) ); @@ -1488,7 +1492,7 @@ void TIFF_FileWriter::UpdateMemByAppend ( XMP_Uns8** newStream_out, XMP_Uns32* n this->PutUns32 ( currTag.count, ifdPtr ); ifdPtr += 4; - *((XMP_Uns32*)ifdPtr) = currTag.smallValue; + PutUns32AsIs ( currTag.smallValue, ifdPtr ); if ( (appendAll | currTag.changed) && (currTag.dataLen > 4) ) { @@ -1525,6 +1529,14 @@ void TIFF_FileWriter::UpdateMemByAppend ( XMP_Uns8** newStream_out, XMP_Uns32* n this->PutUns32 ( newIFDOffsets[kTIFF_PrimaryIFD], (newStream + 4) ); } + if ( appendedIFDs[kTIFF_TNailIFD] ) { + size_t primaryTagCount = this->containedIFDs[kTIFF_PrimaryIFD].tagMap.size(); + if ( primaryTagCount > 0 ) { + XMP_Uns32 tnailLinkOffset = newIFDOffsets[kTIFF_PrimaryIFD] + 2 + (12 * primaryTagCount); + this->PutUns32 ( newIFDOffsets[kTIFF_TNailIFD], (newStream + tnailLinkOffset) ); + } + } + } catch ( ... ) { free ( newStream ); @@ -1554,7 +1566,11 @@ void TIFF_FileWriter::UpdateMemByAppend ( XMP_Uns8** newStream_out, XMP_Uns32* n // We don't do most of the actual rewrite here. We set things up so that UpdateMemByAppend can be // called to append onto a bare TIFF header. Additional hidden offsets are then handled here. // -// These tags are recognized as being hidden offsets when composing a condensed stream: +// The hidden offsets for the Exif, GPS, and Interoperability IFDs (tags 34665, 34853, and 40965) +// are handled by the code in DetermineAppendInfo, which is called from UpdateMemByAppend, which is +// called from here. +// +// These other tags are recognized as being hidden offsets when composing a condensed stream: // 273 - StripOffsets, lengths in tag 279 // 288 - FreeOffsets, lengths in tag 289 // 324 - TileOffsets, lengths in tag 325 @@ -1563,11 +1579,11 @@ void TIFF_FileWriter::UpdateMemByAppend ( XMP_Uns8** newStream_out, XMP_Uns32* n // 519 - JPEGQTables, each table is 64 bytes // 520 - JPEGDCTables, lengths ??? // 521 - JPEGACTables, lengths ??? -// Some of these will handled and kept, some will be thrown out, some will cause the rewrite to fail. // -// The hidden offsets for the Exif, GPS, and Interoperability IFDs (tags 34665, 34853, and 40965) -// are handled by the code in DetermineAppendInfo, which is called from UpdateMemByAppend, which is -// called from here. +// Some of these will handled and kept, some will be thrown out, some will cause the rewrite to fail. +// At this time only the JPEG thumbnail tags, 513 and 514, contain hidden data that is kept. The +// final stream layout is whatever UpdateMemByAppend does for the visible content, followed by the +// hidden offset data. The Exif, GPS, and Interoperability IFDs are visible to UpdateMemByAppend. // ! So far, a memory-based TIFF rewrite would only be done for the Exif portion of a JPEG file. // ! In which case we're probably OK to handle JPEGInterchangeFormat (used for compressed thumbnails) @@ -1653,7 +1669,7 @@ void TIFF_FileWriter::UpdateMemByRewrite ( XMP_Uns8** newStream_out, XMP_Uns32* } // Determine the offsets and additional size for the hidden offset content. Set the offset tags - // to the new offset. + // to the new offset so that UpdateMemByAppend writes the new offsets. XMP_Uns32 hiddenContentLength = 0; XMP_Uns32 hiddenContentOrigin = this->DetermineVisibleLength(); @@ -1678,6 +1694,7 @@ void TIFF_FileWriter::UpdateMemByRewrite ( XMP_Uns8** newStream_out, XMP_Uns32* // Save any old memory stream for the content behind hidden offsets. Setup a bare TIFF header. XMP_Uns8* oldStream = this->memStream; + bool ownedOldStream = this->ownedStream; XMP_Uns8 bareTIFF [8]; if ( this->bigEndian ) { @@ -1709,6 +1726,10 @@ void TIFF_FileWriter::UpdateMemByRewrite ( XMP_Uns8** newStream_out, XMP_Uns32* memcpy ( destPtr, srcPtr, hiddenLocations[i].length ); // AUDIT: Safe copy, not user data, computed length. } + + // Delete the old stream if appropriate. + + if ( ownedOldStream ) delete ( oldStream ); } // TIFF_FileWriter::UpdateMemByRewrite @@ -1730,6 +1751,7 @@ XMP_Uns32 TIFF_FileWriter::UpdateMemoryStream ( void** dataPtr, bool condenseStr { if ( this->fileParsed ) XMP_Throw ( "Not memory based", kXMPErr_EnforceFailure ); + this->changed |= condenseStream; // Make sure a compaction request takes effect. if ( ! this->changed ) { if ( dataPtr != 0 ) *dataPtr = this->memStream; return this->tiffLength; @@ -1758,8 +1780,6 @@ XMP_Uns32 TIFF_FileWriter::UpdateMemoryStream ( void** dataPtr, bool condenseStr condenseStream = true; // Makes "conjured" TIFF take the full rewrite path. } - if ( condenseStream ) this->changed = true; // A prior regular call would have cleared this->changed. - if ( condenseStream ) { this->UpdateMemByRewrite ( &newStream, &newLength ); } else { @@ -1805,7 +1825,7 @@ XMP_Uns32 TIFF_FileWriter::UpdateMemoryStream ( void** dataPtr, bool condenseStr #define Trace_UpdateFileStream 0 #endif -void TIFF_FileWriter::UpdateFileStream ( XMP_IO* fileRef ) +void TIFF_FileWriter::UpdateFileStream ( XMP_IO* fileRef, XMP_ProgressTracker* progressTracker ) { if ( this->memParsed ) XMP_Throw ( "Not file based", kXMPErr_EnforceFailure ); if ( ! this->changed ) return; @@ -1832,6 +1852,33 @@ void TIFF_FileWriter::UpdateFileStream ( XMP_IO* fileRef ) XMP_Uns32 appendedLength = DetermineAppendInfo ( appendedOrigin, appendedIFDs, newIFDOffsets ); if ( appendedLength > (0xFFFFFFFFUL - appendedOrigin) ) XMP_Throw ( "TIFF files can't exceed 4GB", kXMPErr_BadTIFF ); + if ( progressTracker != 0 ) { + + float filesize=0; + + for ( int ifd = 0; ifd < kTIFF_KnownIFDCount; ++ifd ) { + + InternalIFDInfo & thisIFD = this->containedIFDs[ifd]; + if ( ! thisIFD.changed ) continue; + + filesize += (thisIFD.tagMap.size() * sizeof(RawIFDEntry) + 6); + + InternalTagMap::iterator tagPos; + InternalTagMap::iterator tagEnd = thisIFD.tagMap.end(); + + for ( tagPos = thisIFD.tagMap.begin(); tagPos != tagEnd; ++tagPos ) { + InternalTagInfo & thisTag = tagPos->second; + if ( (! thisTag.changed) || (thisTag.dataLen <= 4) ) continue; + filesize += (thisTag.dataLen) ; + } + } + + if ( appendedIFDs[kTIFF_PrimaryIFD] ) filesize += 4; + XMP_Assert ( progressTracker->WorkInProgress() ); + progressTracker->AddTotalWork ( filesize ); + + } + // Do the in-place update for the IFDs and tag values that fit. This part does separate seeks // and writes for the IFDs and values. Things to be updated can be anywhere in the file. diff --git a/XMPFiles/source/FormatSupport/TIFF_MemoryReader.cpp b/XMPFiles/source/FormatSupport/TIFF_MemoryReader.cpp index 3e96620..05cd87f 100644 --- a/XMPFiles/source/FormatSupport/TIFF_MemoryReader.cpp +++ b/XMPFiles/source/FormatSupport/TIFF_MemoryReader.cpp @@ -12,6 +12,7 @@ #include "XMPFiles/source/FormatSupport/TIFF_Support.hpp" #include "source/XIO.hpp" +#include "source/EndianUtils.hpp" // ================================================================================================= /// \file TIFF_MemoryReader.cpp @@ -55,11 +56,11 @@ void TIFF_MemoryReader::SortIFD ( TweakedIFDInfo* thisIFD ) XMP_Uns16 tagCount = thisIFD->count; TweakedIFDEntry* ifdEntries = thisIFD->entries; - XMP_Uns16 prevTag = ifdEntries[0].id; + XMP_Uns16 prevTag = GetUns16AsIs ( &ifdEntries[0].id ); for ( size_t i = 1; i < tagCount; ++i ) { - XMP_Uns16 thisTag = ifdEntries[i].id; + XMP_Uns16 thisTag = GetUns16AsIs ( &ifdEntries[i].id ); if ( thisTag > prevTag ) { @@ -78,7 +79,7 @@ void TIFF_MemoryReader::SortIFD ( TweakedIFDInfo* thisIFD ) // Out of order, move this tag up, prevTag is unchanged. Might still be a duplicate! XMP_Int32 j; // ! Need a signed value. for ( j = (XMP_Int32)i-1; j >= 0; --j ) { - if ( ifdEntries[j].id <= thisTag ) break; + if ( GetUns16AsIs(&ifdEntries[j].id) <= thisTag ) break; } if ( (j >= 0) && (ifdEntries[j].id == thisTag) ) { @@ -92,10 +93,20 @@ void TIFF_MemoryReader::SortIFD ( TweakedIFDInfo* thisIFD ) } else { // Move the out of order entry to position j+1, move the middle of the array down. - TweakedIFDEntry temp = ifdEntries[i]; - ++j; // ! So the insertion index becomes j. - memcpy ( &ifdEntries[j+1], &ifdEntries[j], 12*(i-j) ); // AUDIT: Safe, moving less than i entries to a location before i. - ifdEntries[j] = temp; + #if ! SUNOS_SPARC + TweakedIFDEntry temp = ifdEntries[i]; + ++j; // ! So the insertion index becomes j. + memcpy ( &ifdEntries[j+1], &ifdEntries[j], 12*(i-j) ); // AUDIT: Safe, moving less than i entries to a location before i. + ifdEntries[j] = temp; + #else + void * tempifdEntries = &ifdEntries[i]; + TweakedIFDEntry temp; + memcpy ( &temp, tempifdEntries, sizeof(TweakedIFDEntry) ); + ++j; // ! So the insertion index becomes j. + memcpy ( &ifdEntries[j+1], &ifdEntries[j], 12*(i-j) ); // AUDIT: Safe, moving less than i entries to a location before i. + tempifdEntries = &ifdEntries[j]; + memcpy ( tempifdEntries, &temp, sizeof(TweakedIFDEntry) ); + #endif } @@ -126,7 +137,7 @@ bool TIFF_MemoryReader::GetIFD ( XMP_Uns8 ifd, TagInfoMap* ifdMap ) const TweakedIFDEntry* thisTag = &(thisIFD->entries[i]); if ( (thisTag->type < kTIFF_ByteType) || (thisTag->type > kTIFF_LastType) ) continue; // Bad type, skip this tag. - TagInfo info ( thisTag->id, thisTag->type, 0, 0, thisTag->bytes ); + TagInfo info ( thisTag->id, thisTag->type, 0, 0, GetUns32AsIs(&thisTag->bytes) ); info.count = info.dataLen / (XMP_Uns32)kTIFF_TypeSizes[info.type]; info.dataPtr = this->GetDataPtr ( thisTag ); @@ -166,10 +177,11 @@ const TIFF_MemoryReader::TweakedIFDEntry* TIFF_MemoryReader::FindTagInIFD ( XMP_ // There are halfLength entries below spanMiddle, then the spanMiddle entry, then // spanLength-halfLength-1 entries above spanMiddle (which can be none). - if ( spanMiddle->id == id ) { + XMP_Uns16 middleID = GetUns16AsIs ( &spanMiddle->id ); + if ( middleID == id ) { spanBegin = spanMiddle; break; - } else if ( spanMiddle->id > id ) { + } else if ( middleID > id ) { spanLength = halfLength; // Discard the middle. } else { spanBegin = spanMiddle; // Keep a valid spanBegin for the return check, don't use spanMiddle+1. @@ -178,7 +190,7 @@ const TIFF_MemoryReader::TweakedIFDEntry* TIFF_MemoryReader::FindTagInIFD ( XMP_ } - if ( spanBegin->id != id ) spanBegin = 0; + if ( GetUns16AsIs(&spanBegin->id) != id ) spanBegin = 0; return spanBegin; } // TIFF_MemoryReader::FindTagInIFD @@ -206,14 +218,18 @@ bool TIFF_MemoryReader::GetTag ( XMP_Uns8 ifd, XMP_Uns16 id, TagInfo* info ) con { const TweakedIFDEntry* thisTag = this->FindTagInIFD ( ifd, id ); if ( thisTag == 0 ) return false; - if ( (thisTag->type < kTIFF_ByteType) || (thisTag->type > kTIFF_LastType) ) return false; // Bad type, skip this tag. + + XMP_Uns16 thisType = GetUns16AsIs ( &thisTag->type ); + XMP_Uns32 thisBytes = GetUns32AsIs ( &thisTag->bytes ); + + if ( (thisType < kTIFF_ByteType) || (thisType > kTIFF_LastType) ) return false; // Bad type, skip this tag. if ( info != 0 ) { - info->id = thisTag->id; - info->type = thisTag->type; - info->count = thisTag->bytes / (XMP_Uns32)kTIFF_TypeSizes[thisTag->type]; - info->dataLen = thisTag->bytes; + info->id = GetUns16AsIs ( &thisTag->id ); + info->type = thisType; + info->count = thisBytes / (XMP_Uns32)kTIFF_TypeSizes[thisType]; + info->dataLen = thisBytes; info->dataPtr = this->GetDataPtr ( thisTag ); @@ -242,7 +258,7 @@ bool TIFF_MemoryReader::GetTag_Integer ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns32* if ( thisTag == 0 ) return false; if ( thisTag->type > kTIFF_LastType ) return false; // Unknown type. - if ( thisTag->bytes != kTIFF_TypeSizes[thisTag->type] ) return false; // Wrong count. + if ( GetUns32AsIs(&thisTag->bytes) != kTIFF_TypeSizes[thisTag->type] ) return false; // Wrong count. XMP_Uns32 uns32; XMP_Int32 int32; @@ -553,27 +569,40 @@ void TIFF_MemoryReader::ParseMemoryStream ( const void* data, XMP_Uns32 length, if ( primaryIFDOffset != 0 ) tnailIFDOffset = this->ProcessOneIFD ( primaryIFDOffset, kTIFF_PrimaryIFD ); // ! Need the thumbnail IFD for checking full Exif APP1 in some JPEG files! - if ( tnailIFDOffset != 0 ) (void) this->ProcessOneIFD ( tnailIFDOffset, kTIFF_TNailIFD ); + if ( tnailIFDOffset != 0 ) { + if ( IsOffsetValid(tnailIFDOffset, 8, ifdLimit ) ) { // Ignore a bad Thumbnail IFD offset. + (void) this->ProcessOneIFD ( tnailIFDOffset, kTIFF_TNailIFD ); + } else { + XMP_Error error ( kXMPErr_BadTIFF, "Bad IFD offset" ); + this->NotifyClient (kXMPErrSev_Recoverable, error ); + } + } const TweakedIFDEntry* exifIFDTag = this->FindTagInIFD ( kTIFF_PrimaryIFD, kTIFF_ExifIFDPointer ); - if ( (exifIFDTag != 0) && (exifIFDTag->type == kTIFF_LongType) && (exifIFDTag->bytes == 4) ) { + if ( (exifIFDTag != 0) && (exifIFDTag->type == kTIFF_LongType) && (GetUns32AsIs(&exifIFDTag->bytes) == 4) ) { XMP_Uns32 exifOffset = this->GetUns32 ( &exifIFDTag->dataOrPos ); (void) this->ProcessOneIFD ( exifOffset, kTIFF_ExifIFD ); } const TweakedIFDEntry* gpsIFDTag = this->FindTagInIFD ( kTIFF_PrimaryIFD, kTIFF_GPSInfoIFDPointer ); - if ( (gpsIFDTag != 0) && (gpsIFDTag->type == kTIFF_LongType) && (gpsIFDTag->bytes == 4) ) { + if ( (gpsIFDTag != 0) && (gpsIFDTag->type == kTIFF_LongType) && (GetUns32AsIs(&gpsIFDTag->bytes) == 4) ) { XMP_Uns32 gpsOffset = this->GetUns32 ( &gpsIFDTag->dataOrPos ); - if ( (8 <= gpsOffset) && (gpsOffset < ifdLimit) ) { // Ignore a bad GPS IFD offset. + if ( IsOffsetValid ( gpsOffset, 8, ifdLimit ) ) { // Ignore a bad GPS IFD offset. (void) this->ProcessOneIFD ( gpsOffset, kTIFF_GPSInfoIFD ); + } else { + XMP_Error error ( kXMPErr_BadTIFF, "Bad IFD offset" ); + this->NotifyClient (kXMPErrSev_Recoverable, error ); } } const TweakedIFDEntry* interopIFDTag = this->FindTagInIFD ( kTIFF_ExifIFD, kTIFF_InteroperabilityIFDPointer ); - if ( (interopIFDTag != 0) && (interopIFDTag->type == kTIFF_LongType) && (interopIFDTag->bytes == 4) ) { + if ( (interopIFDTag != 0) && (interopIFDTag->type == kTIFF_LongType) && (GetUns32AsIs(&interopIFDTag->bytes) == 4) ) { XMP_Uns32 interopOffset = this->GetUns32 ( &interopIFDTag->dataOrPos ); - if ( (8 <= interopOffset) && (interopOffset < ifdLimit) ) { // Ignore a bad Interoperability IFD offset. + if ( IsOffsetValid ( interopOffset, 8, ifdLimit ) ) { // Ignore a bad Interoperability IFD offset. (void) this->ProcessOneIFD ( interopOffset, kTIFF_InteropIFD ); + } else { + XMP_Error error ( kXMPErr_BadTIFF, "Bad IFD offset" ); + this->NotifyClient (kXMPErrSev_Recoverable, error ); } } @@ -588,16 +617,22 @@ XMP_Uns32 TIFF_MemoryReader::ProcessOneIFD ( XMP_Uns32 ifdOffset, XMP_Uns8 ifd ) TweakedIFDInfo& ifdInfo = this->containedIFDs[ifd]; if ( (ifdOffset < 8) || (ifdOffset > (this->tiffLength - kEmptyIFDLength)) ) { - XMP_Throw ( "Bad IFD offset", kXMPErr_BadTIFF ); + XMP_Error error(kXMPErr_BadTIFF, "Bad IFD offset" ); + this->NotifyClient ( kXMPErrSev_FileFatal, error ); } XMP_Uns8* ifdPtr = this->tiffStream + ifdOffset; XMP_Uns16 ifdCount = this->GetUns16 ( ifdPtr ); TweakedIFDEntry* ifdEntries = (TweakedIFDEntry*)(ifdPtr+2); - if ( ifdCount >= 0x8000 ) XMP_Throw ( "Outrageous IFD count", kXMPErr_BadTIFF ); + if ( ifdCount >= 0x8000 ) { + XMP_Error error(kXMPErr_BadTIFF, "Outrageous IFD count" ); + this->NotifyClient ( kXMPErrSev_FileFatal, error ); + } + if ( (XMP_Uns32)(2 + ifdCount*12 + 4) > (this->tiffLength - ifdOffset) ) { - XMP_Throw ( "Out of bounds IFD", kXMPErr_BadTIFF ); + XMP_Error error(kXMPErr_BadTIFF, "Out of bounds IFD" ); + this->NotifyClient ( kXMPErrSev_FileFatal, error ); } ifdInfo.count = ifdCount; @@ -615,21 +650,48 @@ XMP_Uns32 TIFF_MemoryReader::ProcessOneIFD ( XMP_Uns32 ifdOffset, XMP_Uns8 ifd ) Flip4 ( &thisEntry->bytes ); } - if ( thisEntry->id <= prevTag ) needsSorting = true; - prevTag = thisEntry->id; + if ( GetUns16AsIs(&thisEntry->id) <= prevTag ) needsSorting = true; + prevTag = GetUns16AsIs ( &thisEntry->id ); - if ( (thisEntry->type < kTIFF_ByteType) || (thisEntry->type > kTIFF_LastType) ) continue; // Bad type, skip this tag. + if ( (GetUns16AsIs(&thisEntry->type) < kTIFF_ByteType) || (GetUns16AsIs(&thisEntry->type) > kTIFF_LastType) ) continue; // Bad type, skip this tag. - thisEntry->bytes *= (XMP_Uns32)kTIFF_TypeSizes[thisEntry->type]; - if ( thisEntry->bytes > 4 ) { - if ( ! this->nativeEndian ) Flip4 ( &thisEntry->dataOrPos ); - if ( (thisEntry->dataOrPos < 8) || (thisEntry->dataOrPos >= this->tiffLength) ) { - thisEntry->bytes = thisEntry->dataOrPos = 0; // Make this bad tag look empty. + #if ! SUNOS_SPARC + + thisEntry->bytes *= (XMP_Uns32)kTIFF_TypeSizes[thisEntry->type]; + if ( thisEntry->bytes > 4 ) { + if ( ! this->nativeEndian ) Flip4 ( &thisEntry->dataOrPos ); + if ( (thisEntry->dataOrPos < 8) || (thisEntry->dataOrPos >= this->tiffLength) ) { + thisEntry->bytes = thisEntry->dataOrPos = 0; // Make this bad tag look empty. + } + if ( thisEntry->bytes > (this->tiffLength - thisEntry->dataOrPos) ) { + thisEntry->bytes = thisEntry->dataOrPos = 0; // Make this bad tag look empty. + } } - if ( thisEntry->bytes > (this->tiffLength - thisEntry->dataOrPos) ) { - thisEntry->bytes = thisEntry->dataOrPos = 0; // Make this bad tag look empty. + + #else + + void *tempEntryByte = &thisEntry->bytes; + XMP_Uns32 temp = GetUns32AsIs(&thisEntry->bytes); + temp = temp * (XMP_Uns32)kTIFF_TypeSizes[GetUns16AsIs(&thisEntry->type)]; + memcpy ( tempEntryByte, &temp, sizeof(thisEntry->bytes) ); + + // thisEntry->bytes *= (XMP_Uns32)kTIFF_TypeSizes[thisEntry->type]; + if ( GetUns32AsIs(&thisEntry->bytes) > 4 ) { + void *tempEntryDataOrPos = &thisEntry->dataOrPos; + if ( ! this->nativeEndian ) Flip4 ( &thisEntry->dataOrPos ); + if ( (GetUns32AsIs(&thisEntry->dataOrPos) < 8) || (GetUns32AsIs(&thisEntry->dataOrPos) >= this->tiffLength) ) { + // thisEntry->bytes = thisEntry->dataOrPos = 0; // Make this bad tag look empty. + memset ( tempEntryByte, 0, sizeof(XMP_Uns32) ); + memset ( tempEntryDataOrPos, 0, sizeof(XMP_Uns32) ); + } + if ( GetUns32AsIs(&thisEntry->bytes) > (this->tiffLength - GetUns32AsIs(&thisEntry->dataOrPos)) ) { + // thisEntry->bytes = thisEntry->dataOrPos = 0; // Make this bad tag look empty. + memset ( tempEntryByte, 0, sizeof(XMP_Uns32) ); + memset ( tempEntryDataOrPos, 0, sizeof(XMP_Uns32) ); + } } - } + + #endif } diff --git a/XMPFiles/source/FormatSupport/TIFF_Support.cpp b/XMPFiles/source/FormatSupport/TIFF_Support.cpp index 9da9b52..da2f9b6 100644 --- a/XMPFiles/source/FormatSupport/TIFF_Support.cpp +++ b/XMPFiles/source/FormatSupport/TIFF_Support.cpp @@ -29,7 +29,7 @@ static bool sFirstCTor = true; TIFF_Manager::TIFF_Manager() - : bigEndian(false), nativeEndian(false), + : bigEndian(false), nativeEndian(false), errorCallbackPtr( NULL ), GetUns16(0), GetUns32(0), GetFloat(0), GetDouble(0), PutUns16(0), PutUns32(0), PutFloat(0), PutDouble(0) { @@ -379,6 +379,13 @@ static void UTF8_to_UTF16 ( const UTF8Unit * utf8Ptr, size_t utf8Len, bool bigEn } // UTF8_to_UTF16 +XMP_Bool IsOffsetValid( XMP_Uns32 offset, XMP_Uns32 lowerBound, XMP_Uns32 upperBound ) +{ + if ( (lowerBound <= offset) && (offset < upperBound) ) + return true; + return false; +} + // ================================================================================================= // TIFF_Manager::EncodeString // ========================== @@ -436,4 +443,14 @@ bool TIFF_Manager::EncodeString ( const std::string& utf8Str, XMP_Uns8 encoding, } // TIFF_Manager::EncodeString +void TIFF_Manager::NotifyClient( XMP_ErrorSeverity severity, XMP_Error & error ) +{ + if (this->errorCallbackPtr) + this->errorCallbackPtr->NotifyClient( severity, error ); + else { + if ( severity != kXMPErrSev_Recoverable ) + throw error; + } +} + // ================================================================================================= diff --git a/XMPFiles/source/FormatSupport/TIFF_Support.hpp b/XMPFiles/source/FormatSupport/TIFF_Support.hpp index 447ab79..002376b 100644 --- a/XMPFiles/source/FormatSupport/TIFF_Support.hpp +++ b/XMPFiles/source/FormatSupport/TIFF_Support.hpp @@ -23,6 +23,14 @@ #include "source/EndianUtils.hpp" +#include "source/Endian.h" + +#if SUNOS_SPARC + static const IEndian &IE = BigEndian::getInstance(); +#else + static const IEndian &IE = LittleEndian::getInstance(); +#endif //#if SUNOS_SPARC + // ================================================================================================= /// \file TIFF_Support.hpp /// \brief XMPFiles support for TIFF streams. @@ -642,7 +650,7 @@ public: virtual void IntegrateFromPShop6 ( const void * buriedPtr, size_t buriedLen ) = 0; virtual XMP_Uns32 UpdateMemoryStream ( void** dataPtr, bool condenseStream = false ) = 0; - virtual void UpdateFileStream ( XMP_IO* fileRef ) = 0; + virtual void UpdateFileStream ( XMP_IO* fileRef, XMP_ProgressTracker* progressTracker ) = 0; // --------------------------------------------------------------------------------------------- @@ -658,11 +666,17 @@ public: virtual ~TIFF_Manager() {}; + virtual void SetErrorCallback ( GenericErrorCallback * ec ) { this->errorCallbackPtr = ec; }; + + virtual void NotifyClient ( XMP_ErrorSeverity severity, XMP_Error & error ); + protected: bool bigEndian, nativeEndian; XMP_Uns32 CheckTIFFHeader ( const XMP_Uns8* tiffPtr, XMP_Uns32 length ); + // The pointer is to a buffer of the first 8 bytes. The length is the overall length, used + // to check the primary IFD offset. TIFF_Manager(); // Force clients to use the reader or writer derived classes. @@ -673,6 +687,8 @@ protected: XMP_Uns32 dataOrOffset; }; + GenericErrorCallback *errorCallbackPtr; + }; // TIFF_Manager @@ -730,7 +746,7 @@ public: void IntegrateFromPShop6 ( const void * buriedPtr, size_t buriedLen ) { NotAppropriate(); }; XMP_Uns32 UpdateMemoryStream ( void** dataPtr, bool condenseStream = false ) { if ( dataPtr != 0 ) *dataPtr = tiffStream; return tiffLength; }; - void UpdateFileStream ( XMP_IO* fileRef ) { NotAppropriate(); }; + void UpdateFileStream ( XMP_IO* fileRef, XMP_ProgressTracker* progressTracker ) { NotAppropriate(); }; TIFF_MemoryReader() : ownedStream(false), tiffStream(0), tiffLength(0) {}; @@ -771,7 +787,12 @@ private: const TweakedIFDEntry* FindTagInIFD ( XMP_Uns8 ifd, XMP_Uns16 id ) const; const inline void* GetDataPtr ( const TweakedIFDEntry* tifdEntry ) const - { if ( tifdEntry->bytes <= 4 ) return &tifdEntry->dataOrPos; else return (this->tiffStream + tifdEntry->dataOrPos); }; + { if ( GetUns32AsIs(&tifdEntry->bytes) <= 4 ) { + return &tifdEntry->dataOrPos; + } else { + return (this->tiffStream + GetUns32AsIs(&tifdEntry->dataOrPos)); + } + } static inline void NotAppropriate() { XMP_Throw ( "Not appropriate for TIFF_Reader", kXMPErr_InternalFailure ); }; @@ -835,7 +856,7 @@ public: void IntegrateFromPShop6 ( const void * buriedPtr, size_t buriedLen ); XMP_Uns32 UpdateMemoryStream ( void** dataPtr, bool condenseStream = false ); - void UpdateFileStream ( XMP_IO* fileRef ); + void UpdateFileStream ( XMP_IO* fileRef, XMP_ProgressTracker* progressTracker ); TIFF_FileWriter(); @@ -934,8 +955,7 @@ private: void DeleteExistingInfo(); XMP_Uns32 ProcessMemoryIFD ( XMP_Uns32 ifdOffset, XMP_Uns8 ifd ); - XMP_Uns32 ProcessFileIFD ( XMP_Uns8 ifd, XMP_Uns32 ifdOffset, XMP_IO* fileRef, void* _ioBuf ); - // *** Temporary hack above, _ioBuf is IOBuffer* but don't want to include XIO.hpp. + XMP_Uns32 ProcessFileIFD ( XMP_Uns8 ifd, XMP_Uns32 ifdOffset, XMP_IO* fileRef ); void ProcessPShop6IFD ( const TIFF_MemoryReader& buriedExif, XMP_Uns8 ifd ); @@ -958,6 +978,7 @@ private: }; // TIFF_FileWriter +XMP_Bool IsOffsetValid( XMP_Uns32 offset, XMP_Uns32 lowerBound, XMP_Uns32 upperBound ); // ================================================================================================= diff --git a/XMPFiles/source/FormatSupport/WAVE/BEXTMetadata.cpp b/XMPFiles/source/FormatSupport/WAVE/BEXTMetadata.cpp index a11e481..5b98ba0 100644 --- a/XMPFiles/source/FormatSupport/WAVE/BEXTMetadata.cpp +++ b/XMPFiles/source/FormatSupport/WAVE/BEXTMetadata.cpp @@ -26,7 +26,11 @@ static const XMP_Uns32 kSizeOriginationDate = 10; static const XMP_Uns32 kSizeOriginationTime = 8; // Needed to be able to memcpy directly to this struct. +#if SUNOS_SPARC || SUNOS_X86 +#pragma pack ( 1 ) +#else #pragma pack ( push, 1 ) +#endif //#if SUNOS_SPARC || SUNOS_X86 struct BEXT { char mDescription[256]; @@ -40,7 +44,11 @@ static const XMP_Uns32 kSizeOriginationTime = 8; XMP_Uns8 mUMID[64]; XMP_Uns8 mReserved[190]; }; +#if SUNOS_SPARC || SUNOS_X86 +#pragma pack ( ) +#else #pragma pack ( pop ) +#endif //#if SUNOS_SPARC || SUNOS_X86 //----------------------------------------------------------------------------- // diff --git a/XMPFiles/source/FormatSupport/WAVE/CartMetadata.cpp b/XMPFiles/source/FormatSupport/WAVE/CartMetadata.cpp index 07c8969..73165e4 100644 --- a/XMPFiles/source/FormatSupport/WAVE/CartMetadata.cpp +++ b/XMPFiles/source/FormatSupport/WAVE/CartMetadata.cpp @@ -22,7 +22,11 @@ using namespace IFF_RIFF; // Types and globals for the stored form of the cart chunk. +#if SUNOS_SPARC || SUNOS_X86 +#pragma pack ( 1 ) +#else #pragma pack ( push, 1 ) +#endif //#if SUNOS_SPARC || SUNOS_X86 struct StoredCartChunk { char Version[4]; // All of the fixed size text fields are null-filled local text, @@ -49,7 +53,11 @@ struct StoredCartChunk { static const size_t kMinimumCartChunkSize = sizeof(StoredCartChunk); +#if SUNOS_SPARC || SUNOS_X86 +#pragma pack ( ) +#else #pragma pack ( pop ) +#endif //#if SUNOS_SPARC || SUNOS_X86 static const size_t kFixedTextCount = (CartMetadata::kLastFixedTextField - CartMetadata::kFirstFixedTextField + 1); diff --git a/XMPFiles/source/FormatSupport/WAVE/Cr8rMetadata.cpp b/XMPFiles/source/FormatSupport/WAVE/Cr8rMetadata.cpp index c9cbc23..277924f 100644 --- a/XMPFiles/source/FormatSupport/WAVE/Cr8rMetadata.cpp +++ b/XMPFiles/source/FormatSupport/WAVE/Cr8rMetadata.cpp @@ -24,7 +24,11 @@ static const XMP_Uns32 kSizeAppOtions = 16; static const XMP_Uns32 kSizeAppName = 32; // Needed to be able to memcpy directly to this struct. +#if SUNOS_SPARC || SUNOS_X86 +#pragma pack ( 1 ) +#else #pragma pack ( push, 1 ) +#endif //#if SUNOS_SPARC || SUNOS_X86 struct Cr8rBoxContent { XMP_Uns32 mMagic; @@ -37,7 +41,11 @@ static const XMP_Uns32 kSizeAppName = 32; char mAppOptions[kSizeAppOtions]; char mAppName[kSizeAppName]; }; +#if SUNOS_SPARC || SUNOS_X86 +#pragma pack ( ) +#else #pragma pack ( pop ) +#endif //#if SUNOS_SPARC || SUNOS_X86 //----------------------------------------------------------------------------- // diff --git a/XMPFiles/source/FormatSupport/WAVE/INFOMetadata.cpp b/XMPFiles/source/FormatSupport/WAVE/INFOMetadata.cpp index 76faaa3..6597f2a 100644 --- a/XMPFiles/source/FormatSupport/WAVE/INFOMetadata.cpp +++ b/XMPFiles/source/FormatSupport/WAVE/INFOMetadata.cpp @@ -217,6 +217,8 @@ XMP_Uns64 INFOMetadata::serialize( XMP_Uns8** outBuffer ) // memcpy( buffer+offset, &id, kSizeChunkID ); memcpy( buffer+offset+kSizeChunkID, &size, kSizeChunkSize ); + //size has been changed in little endian format. Change it back to bigendina + size = LE.getUns32( &size ); memcpy( buffer+offset+kChunkHeaderSize, value.c_str(), size ); // diff --git a/XMPFiles/source/FormatSupport/WAVE/PrmLMetadata.cpp b/XMPFiles/source/FormatSupport/WAVE/PrmLMetadata.cpp index c5dc42e..477c31e 100644 --- a/XMPFiles/source/FormatSupport/WAVE/PrmLMetadata.cpp +++ b/XMPFiles/source/FormatSupport/WAVE/PrmLMetadata.cpp @@ -22,7 +22,11 @@ static const XMP_Uns32 kPrmlSizeFix = 282; // always 282 bytes static const XMP_Uns32 kSizeFilePath = 260; // Needed to be able to memcpy directly to this struct. +#if SUNOS_SPARC || SUNOS_X86 +#pragma pack ( 1 ) +#else #pragma pack ( push, 1 ) +#endif //#if SUNOS_SPARC || SUNOS_X86 struct PrmlBoxContent { XMP_Uns32 mMagic; @@ -34,7 +38,11 @@ static const XMP_Uns32 kSizeFilePath = 260; XMP_Uns32 mMacParID; char mFilePath[kSizeFilePath]; }; +#if SUNOS_SPARC || SUNOS_X86 +#pragma pack ( ) +#else #pragma pack ( pop ) +#endif //#if SUNOS_SPARC || SUNOS_X86 //----------------------------------------------------------------------------- // diff --git a/XMPFiles/source/FormatSupport/WAVE/WAVEBehavior.h b/XMPFiles/source/FormatSupport/WAVE/WAVEBehavior.h index 1395cd1..b5a2e3f 100644 --- a/XMPFiles/source/FormatSupport/WAVE/WAVEBehavior.h +++ b/XMPFiles/source/FormatSupport/WAVE/WAVEBehavior.h @@ -33,7 +33,11 @@ class WAVEBehavior : public IChunkBehavior { // Internal structure to hold RF64 related data public: +#if SUNOS_SPARC || SUNOS_X86 +#pragma pack ( 1 ) +#else #pragma pack ( push, 1 ) +#endif //#if SUNOS_SPARC || SUNOS_X86 struct ChunkSize64 { XMP_Uns64 size; @@ -55,7 +59,11 @@ public: // ctor DS64(): riffSize(0), dataSize(0), sampleCount(0), tableLength(0), trailingBytes(0) {} }; +#if SUNOS_SPARC || SUNOS_X86 +#pragma pack ( ) +#else #pragma pack ( pop ) +#endif //#if SUNOS_SPARC || SUNOS_X86 /** diff --git a/XMPFiles/source/FormatSupport/XDCAM_Support.cpp b/XMPFiles/source/FormatSupport/XDCAM_Support.cpp index b9704b9..cb65bf0 100644 --- a/XMPFiles/source/FormatSupport/XDCAM_Support.cpp +++ b/XMPFiles/source/FormatSupport/XDCAM_Support.cpp @@ -194,7 +194,7 @@ bool XDCAM_Support::GetLegacyMetadata ( SXMPMeta * xmpObjPtr, bool containsXMP = false; XML_NodePtr legacyContext = 0, legacyProp = 0; - XMP_StringPtr formatFPS; + XMP_StringPtr formatFPS = 0; // UMID if ( digestFound || (! xmpObjPtr->DoesPropertyExist ( kXMP_NS_DC, "identifier" )) ) { @@ -307,18 +307,20 @@ bool XDCAM_Support::GetLegacyMetadata ( SXMPMeta * xmpObjPtr, } } - // Frame rate + // Frame rate (always read, because its used later for the Duration + legacyProp = legacyContext->GetNamedElement ( legacyNS, "VideoFrame" ); + if ( (legacyProp != 0) && legacyProp->IsEmptyLeafNode() ) { + formatFPS = legacyProp->GetAttrValue ( "formatFps" ); + } + + // only write back to XMP if framerate is not set in XMP yet if ( digestFound || (! xmpObjPtr->DoesPropertyExist ( kXMP_NS_DM, "videoFrameRate" )) ) { - legacyProp = legacyContext->GetNamedElement ( legacyNS, "VideoFrame" ); - if ( (legacyProp != 0) && legacyProp->IsEmptyLeafNode() ) { - formatFPS = legacyProp->GetAttrValue ( "formatFps" ); - if ( formatFPS != 0 ) { - xmpObjPtr->SetProperty ( kXMP_NS_DM, "videoFrameRate", formatFPS, kXMP_DeleteExisting ); - containsXMP = true; - } + if ( formatFPS != 0 ) { + xmpObjPtr->SetProperty ( kXMP_NS_DM, "videoFrameRate", formatFPS, kXMP_DeleteExisting ); + containsXMP = true; } } - + // Video codec if ( digestFound || (! xmpObjPtr->DoesPropertyExist ( kXMP_NS_DM, "videoCompressor" )) ) { legacyProp = legacyContext->GetNamedElement ( legacyNS, "VideoFrame" ); @@ -361,8 +363,12 @@ bool XDCAM_Support::GetLegacyMetadata ( SXMPMeta * xmpObjPtr, if ( durationValue != 0 ) durationFrames = durationValue; } - std::string timeScale = GetTimeScale ( formatFPS ); - + std::string timeScale; + if ( formatFPS ) { + + timeScale = GetTimeScale ( formatFPS ); + } + if ( (! timeScale.empty()) && (! durationFrames.empty()) ) { xmpObjPtr->DeleteProperty ( kXMP_NS_DM, "duration" ); xmpObjPtr->SetStructField ( kXMP_NS_DM, "duration", kXMP_NS_DM, "value", durationFrames ); |