summaryrefslogtreecommitdiff
path: root/XMPFiles/source/FormatSupport
diff options
context:
space:
mode:
Diffstat (limited to 'XMPFiles/source/FormatSupport')
-rw-r--r--XMPFiles/source/FormatSupport/ASF_Support.cpp14
-rw-r--r--XMPFiles/source/FormatSupport/ASF_Support.hpp4
-rw-r--r--XMPFiles/source/FormatSupport/ID3_Support.cpp433
-rw-r--r--XMPFiles/source/FormatSupport/ID3_Support.hpp146
-rw-r--r--XMPFiles/source/FormatSupport/IFF/Chunk.cpp46
-rw-r--r--XMPFiles/source/FormatSupport/IFF/Chunk.h6
-rw-r--r--XMPFiles/source/FormatSupport/IFF/ChunkController.cpp18
-rw-r--r--XMPFiles/source/FormatSupport/IFF/ChunkController.h4
-rw-r--r--XMPFiles/source/FormatSupport/ISOBaseMedia_Support.hpp4
-rw-r--r--XMPFiles/source/FormatSupport/MOOV_Support.cpp1
-rw-r--r--XMPFiles/source/FormatSupport/MOOV_Support.hpp8
-rw-r--r--XMPFiles/source/FormatSupport/PSIR_FileWriter.cpp117
-rw-r--r--XMPFiles/source/FormatSupport/PSIR_Support.hpp13
-rw-r--r--XMPFiles/source/FormatSupport/PackageFormat_Support.cpp124
-rw-r--r--XMPFiles/source/FormatSupport/PackageFormat_Support.hpp39
-rw-r--r--XMPFiles/source/FormatSupport/PostScript_Support.cpp1094
-rw-r--r--XMPFiles/source/FormatSupport/PostScript_Support.hpp266
-rw-r--r--XMPFiles/source/FormatSupport/QuickTime_Support.cpp1
-rw-r--r--XMPFiles/source/FormatSupport/RIFF.hpp8
-rw-r--r--XMPFiles/source/FormatSupport/ReconcileTIFF.cpp68
-rw-r--r--XMPFiles/source/FormatSupport/SWF_Support.cpp2
-rw-r--r--XMPFiles/source/FormatSupport/TIFF_FileWriter.cpp267
-rw-r--r--XMPFiles/source/FormatSupport/TIFF_MemoryReader.cpp136
-rw-r--r--XMPFiles/source/FormatSupport/TIFF_Support.cpp19
-rw-r--r--XMPFiles/source/FormatSupport/TIFF_Support.hpp33
-rw-r--r--XMPFiles/source/FormatSupport/WAVE/BEXTMetadata.cpp8
-rw-r--r--XMPFiles/source/FormatSupport/WAVE/CartMetadata.cpp8
-rw-r--r--XMPFiles/source/FormatSupport/WAVE/Cr8rMetadata.cpp8
-rw-r--r--XMPFiles/source/FormatSupport/WAVE/INFOMetadata.cpp2
-rw-r--r--XMPFiles/source/FormatSupport/WAVE/PrmLMetadata.cpp8
-rw-r--r--XMPFiles/source/FormatSupport/WAVE/WAVEBehavior.h8
-rw-r--r--XMPFiles/source/FormatSupport/XDCAM_Support.cpp30
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 );