summaryrefslogtreecommitdiff
path: root/source
diff options
context:
space:
mode:
Diffstat (limited to 'source')
-rw-r--r--source/XMPCore/ExpatAdapter.cpp33
-rw-r--r--source/XMPCore/ParseRDF.cpp77
-rw-r--r--source/XMPCore/WXMPIterator.cpp120
-rw-r--r--source/XMPCore/WXMPMeta.cpp782
-rw-r--r--source/XMPCore/WXMPUtils.cpp434
-rw-r--r--source/XMPCore/XMPCore_Impl.cpp133
-rw-r--r--source/XMPCore/XMPCore_Impl.hpp204
-rw-r--r--source/XMPCore/XMPIterator.cpp103
-rw-r--r--source/XMPCore/XMPIterator.hpp10
-rw-r--r--source/XMPCore/XMPMeta-GetSet.cpp276
-rw-r--r--source/XMPCore/XMPMeta-Parse.cpp39
-rw-r--r--source/XMPCore/XMPMeta-Serialize.cpp160
-rw-r--r--source/XMPCore/XMPMeta.cpp927
-rw-r--r--source/XMPCore/XMPMeta.hpp56
-rw-r--r--source/XMPCore/XMPUtils-FileInfo.cpp444
-rw-r--r--source/XMPCore/XMPUtils.cpp767
-rw-r--r--source/XMPCore/XMPUtils.hpp79
-rw-r--r--source/XMPFiles/FileHandlers/ASF_Handler.cpp14
-rw-r--r--source/XMPFiles/FileHandlers/ASF_Handler.hpp12
-rw-r--r--source/XMPFiles/FileHandlers/AVCHD_Handler.cpp1786
-rw-r--r--source/XMPFiles/FileHandlers/AVCHD_Handler.hpp10
-rw-r--r--source/XMPFiles/FileHandlers/AVI_Handler.cpp498
-rw-r--r--source/XMPFiles/FileHandlers/AVI_Handler.hpp53
-rw-r--r--source/XMPFiles/FileHandlers/Basic_Handler.cpp2
-rw-r--r--source/XMPFiles/FileHandlers/Basic_Handler.hpp2
-rw-r--r--source/XMPFiles/FileHandlers/FLV_Handler.cpp5
-rw-r--r--source/XMPFiles/FileHandlers/FLV_Handler.hpp2
-rw-r--r--source/XMPFiles/FileHandlers/InDesign_Handler.cpp2
-rw-r--r--source/XMPFiles/FileHandlers/InDesign_Handler.hpp2
-rw-r--r--source/XMPFiles/FileHandlers/JPEG_Handler.cpp503
-rw-r--r--source/XMPFiles/FileHandlers/JPEG_Handler.hpp4
-rw-r--r--source/XMPFiles/FileHandlers/MOV_Handler.cpp996
-rw-r--r--source/XMPFiles/FileHandlers/MOV_Handler.hpp94
-rw-r--r--source/XMPFiles/FileHandlers/MP3_Handler.cpp898
-rw-r--r--source/XMPFiles/FileHandlers/MP3_Handler.hpp62
-rw-r--r--source/XMPFiles/FileHandlers/MPEG2_Handler.cpp2
-rw-r--r--source/XMPFiles/FileHandlers/MPEG2_Handler.hpp2
-rw-r--r--source/XMPFiles/FileHandlers/MPEG4_Handler.cpp2651
-rw-r--r--source/XMPFiles/FileHandlers/MPEG4_Handler.hpp45
-rw-r--r--source/XMPFiles/FileHandlers/P2_Handler.cpp276
-rw-r--r--source/XMPFiles/FileHandlers/P2_Handler.hpp6
-rw-r--r--source/XMPFiles/FileHandlers/PNG_Handler.cpp14
-rw-r--r--source/XMPFiles/FileHandlers/PNG_Handler.hpp12
-rw-r--r--source/XMPFiles/FileHandlers/PSD_Handler.cpp238
-rw-r--r--source/XMPFiles/FileHandlers/PSD_Handler.hpp4
-rw-r--r--source/XMPFiles/FileHandlers/PostScript_Handler.cpp38
-rw-r--r--source/XMPFiles/FileHandlers/PostScript_Handler.hpp2
-rw-r--r--source/XMPFiles/FileHandlers/RIFF_Handler.cpp347
-rw-r--r--source/XMPFiles/FileHandlers/RIFF_Handler.hpp70
-rw-r--r--source/XMPFiles/FileHandlers/SWF_Handler.cpp13
-rw-r--r--source/XMPFiles/FileHandlers/SWF_Handler.hpp14
-rw-r--r--source/XMPFiles/FileHandlers/Scanner_Handler.cpp5
-rw-r--r--source/XMPFiles/FileHandlers/Scanner_Handler.hpp2
-rw-r--r--source/XMPFiles/FileHandlers/SonyHDV_Handler.cpp26
-rw-r--r--source/XMPFiles/FileHandlers/SonyHDV_Handler.hpp2
-rw-r--r--source/XMPFiles/FileHandlers/TIFF_Handler.cpp220
-rw-r--r--source/XMPFiles/FileHandlers/TIFF_Handler.hpp4
-rw-r--r--source/XMPFiles/FileHandlers/Trivial_Handler.cpp2
-rw-r--r--source/XMPFiles/FileHandlers/Trivial_Handler.hpp2
-rw-r--r--source/XMPFiles/FileHandlers/UCF_Handler.cpp10
-rw-r--r--source/XMPFiles/FileHandlers/UCF_Handler.hpp2
-rw-r--r--source/XMPFiles/FileHandlers/WAV_Handler.cpp707
-rw-r--r--source/XMPFiles/FileHandlers/WAV_Handler.hpp75
-rw-r--r--source/XMPFiles/FileHandlers/XDCAMEX_Handler.cpp47
-rw-r--r--source/XMPFiles/FileHandlers/XDCAMEX_Handler.hpp4
-rw-r--r--source/XMPFiles/FileHandlers/XDCAM_Handler.cpp68
-rw-r--r--source/XMPFiles/FileHandlers/XDCAM_Handler.hpp4
-rw-r--r--source/XMPFiles/FormatSupport/ASF_Support.cpp18
-rw-r--r--source/XMPFiles/FormatSupport/ASF_Support.hpp2
-rw-r--r--source/XMPFiles/FormatSupport/ID3_Support.cpp1156
-rw-r--r--source/XMPFiles/FormatSupport/ID3_Support.hpp775
-rw-r--r--source/XMPFiles/FormatSupport/IPTC_Support.cpp234
-rw-r--r--source/XMPFiles/FormatSupport/IPTC_Support.hpp36
-rw-r--r--source/XMPFiles/FormatSupport/ISOBaseMedia_Support.cpp149
-rw-r--r--source/XMPFiles/FormatSupport/ISOBaseMedia_Support.hpp100
-rw-r--r--source/XMPFiles/FormatSupport/MOOV_Support.cpp542
-rw-r--r--source/XMPFiles/FormatSupport/MOOV_Support.hpp215
-rw-r--r--source/XMPFiles/FormatSupport/MacScriptExtracts.h244
-rw-r--r--source/XMPFiles/FormatSupport/PNG_Support.cpp2
-rw-r--r--source/XMPFiles/FormatSupport/PNG_Support.hpp2
-rw-r--r--source/XMPFiles/FormatSupport/PSIR_FileWriter.cpp145
-rw-r--r--source/XMPFiles/FormatSupport/PSIR_MemoryReader.cpp2
-rw-r--r--source/XMPFiles/FormatSupport/PSIR_Support.hpp63
-rw-r--r--source/XMPFiles/FormatSupport/QuickTime_Support.cpp1157
-rw-r--r--source/XMPFiles/FormatSupport/QuickTime_Support.hpp98
-rw-r--r--source/XMPFiles/FormatSupport/RIFF.cpp879
-rw-r--r--source/XMPFiles/FormatSupport/RIFF.hpp316
-rw-r--r--source/XMPFiles/FormatSupport/RIFF_Support.cpp1467
-rw-r--r--source/XMPFiles/FormatSupport/RIFF_Support.hpp189
-rw-r--r--source/XMPFiles/FormatSupport/ReconcileIPTC.cpp721
-rw-r--r--source/XMPFiles/FormatSupport/ReconcileLegacy.cpp273
-rw-r--r--source/XMPFiles/FormatSupport/ReconcileLegacy.hpp118
-rw-r--r--source/XMPFiles/FormatSupport/ReconcileTIFF.cpp2073
-rw-r--r--source/XMPFiles/FormatSupport/Reconcile_Impl.cpp223
-rw-r--r--source/XMPFiles/FormatSupport/Reconcile_Impl.hpp79
-rw-r--r--source/XMPFiles/FormatSupport/SWF_Support.cpp6
-rw-r--r--source/XMPFiles/FormatSupport/SWF_Support.hpp2
-rw-r--r--source/XMPFiles/FormatSupport/TIFF_FileWriter.cpp667
-rw-r--r--source/XMPFiles/FormatSupport/TIFF_MemoryReader.cpp157
-rw-r--r--source/XMPFiles/FormatSupport/TIFF_Support.cpp220
-rw-r--r--source/XMPFiles/FormatSupport/TIFF_Support.hpp140
-rw-r--r--source/XMPFiles/FormatSupport/XDCAM_Support.cpp66
-rw-r--r--source/XMPFiles/FormatSupport/XDCAM_Support.hpp2
-rw-r--r--source/XMPFiles/FormatSupport/XMPScanner.cpp2
-rw-r--r--source/XMPFiles/FormatSupport/XMPScanner.hpp2
-rw-r--r--source/XMPFiles/WXMPFiles.cpp180
-rw-r--r--source/XMPFiles/XMPFiles.cpp287
-rw-r--r--source/XMPFiles/XMPFiles.hpp21
-rw-r--r--source/XMPFiles/XMPFiles_Impl.cpp67
-rw-r--r--source/XMPFiles/XMPFiles_Impl.hpp215
-rw-r--r--source/common/EndianUtils.hpp2
-rw-r--r--source/common/ExpatAdapter.hpp14
-rw-r--r--source/common/LargeFileAccess.cpp47
-rw-r--r--source/common/LargeFileAccess.hpp197
-rw-r--r--source/common/UnicodeConversions.cpp54
-rw-r--r--source/common/UnicodeConversions.hpp9
-rw-r--r--source/common/UnicodeInlines.incl_cpp2
-rw-r--r--source/common/XMLParserAdapter.hpp2
-rw-r--r--source/common/XML_Node.cpp4
-rw-r--r--source/common/XMP_LibUtils.cpp592
-rw-r--r--source/common/XMP_LibUtils.hpp520
121 files changed, 17043 insertions, 12215 deletions
diff --git a/source/XMPCore/ExpatAdapter.cpp b/source/XMPCore/ExpatAdapter.cpp
index 3dff828..48f6763 100644
--- a/source/XMPCore/ExpatAdapter.cpp
+++ b/source/XMPCore/ExpatAdapter.cpp
@@ -1,5 +1,5 @@
// =================================================================================================
-// Copyright 2005-2008 Adobe Systems Incorporated
+// Copyright 2005 Adobe Systems Incorporated
// All Rights Reserved.
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -62,14 +62,16 @@ static void CommentHandler ( void * userData, XMP_StringPtr commen
// =================================================================================================
-extern "C" ExpatAdapter * XMP_NewExpatAdapter()
+extern "C" ExpatAdapter * XMP_NewExpatAdapter ( bool useGlobalNamespaces )
{
- return new ExpatAdapter;
+
+ return new ExpatAdapter ( useGlobalNamespaces );
+
} // XMP_NewExpatAdapter
// =================================================================================================
-ExpatAdapter::ExpatAdapter() : parser(0)
+ExpatAdapter::ExpatAdapter ( bool useGlobalNamespaces ) : parser(0), registeredNamespaces(0)
{
#if XMP_DebugBuild
@@ -82,6 +84,12 @@ ExpatAdapter::ExpatAdapter() : parser(0)
this->parser = XML_ParserCreateNS ( 0, FullNameSeparator );
if ( this->parser == 0 ) XMP_Throw ( "Failure creating Expat parser", kXMPErr_ExternalFailure );
+ if ( useGlobalNamespaces ) {
+ this->registeredNamespaces = sRegisteredNamespaces;
+ } else {
+ this->registeredNamespaces = new XMP_NamespaceTable ( *sRegisteredNamespaces );
+ }
+
XML_SetUserData ( this->parser, this );
XML_SetNamespaceDeclHandler ( this->parser, StartNamespaceDeclHandler, EndNamespaceDeclHandler );
@@ -109,6 +117,9 @@ ExpatAdapter::~ExpatAdapter()
if ( this->parser != 0 ) XML_ParserFree ( this->parser );
this->parser = 0;
+
+ if ( this->registeredNamespaces != sRegisteredNamespaces ) delete ( this->registeredNamespaces );
+ this->registeredNamespaces = 0;
} // ExpatAdapter::~ExpatAdapter
@@ -181,7 +192,7 @@ void ExpatAdapter::ParseBuffer ( const void * buffer, size_t length, bool last /
// =================================================================================================
-static void SetQualName ( XMP_StringPtr fullName, XML_Node * node )
+static void SetQualName ( ExpatAdapter * thiz, XMP_StringPtr fullName, XML_Node * node )
{
// Expat delivers the full name as a catenation of namespace URI, separator, and local name.
@@ -207,7 +218,7 @@ static void SetQualName ( XMP_StringPtr fullName, XML_Node * node )
node->ns.assign ( fullName, sepPos );
if ( node->ns == "http://purl.org/dc/1.1/" ) node->ns = "http://purl.org/dc/elements/1.1/";
- bool found = XMPMeta::GetNamespacePrefix ( node->ns.c_str(), &prefix, &prefixLen );
+ bool found = thiz->registeredNamespaces->GetPrefix ( node->ns.c_str(), &prefix, &prefixLen );
if ( ! found ) XMP_Throw ( "Unknown URI in Expat full name", kXMPErr_ExternalFailure );
node->nsPrefixLen = prefixLen; // ! Includes the ':'.
@@ -243,9 +254,7 @@ static void StartNamespaceDeclHandler ( void * userData, XMP_StringPtr prefix, X
// As a bug fix hack, change a URI of "http://purl.org/dc/1.1/" to ""http://purl.org/dc/elements/1.1/.
// Early versions of Flash that put XMP in SWF used a bad URI for the dc: namespace.
- #if XMP_DebugBuild & DumpXMLParseEvents // Avoid unused variable warning.
- ExpatAdapter * thiz = (ExpatAdapter*)userData;
- #endif
+ ExpatAdapter * thiz = (ExpatAdapter*)userData;
if ( prefix == 0 ) prefix = "_dflt_"; // Have default namespace.
if ( uri == 0 ) return; // Ignore, have xmlns:pre="", no URI to register.
@@ -258,7 +267,7 @@ static void StartNamespaceDeclHandler ( void * userData, XMP_StringPtr prefix, X
#endif
if ( XMP_LitMatch ( uri, "http://purl.org/dc/1.1/" ) ) uri = "http://purl.org/dc/elements/1.1/";
- (void) XMPMeta::RegisterNamespace ( uri, prefix, &voidStringPtr, &voidStringLen );
+ (void) thiz->registeredNamespaces->Define ( uri, prefix, 0, 0 );
} // StartNamespaceDeclHandler
@@ -313,7 +322,7 @@ static void StartElementHandler ( void * userData, XMP_StringPtr name, XMP_Strin
XML_Node * parentNode = thiz->parseStack.back();
XML_Node * elemNode = new XML_Node ( parentNode, "", kElemNode );
- SetQualName ( name, elemNode );
+ SetQualName ( thiz, name, elemNode );
for ( XMP_StringPtr* attr = attrs; *attr != 0; attr += 2 ) {
@@ -321,7 +330,7 @@ static void StartElementHandler ( void * userData, XMP_StringPtr name, XMP_Strin
XMP_StringPtr attrValue = *(attr+1);
XML_Node * attrNode = new XML_Node ( elemNode, "", kAttrNode );
- SetQualName ( attrName, attrNode );
+ SetQualName ( thiz, attrName, attrNode );
attrNode->value = attrValue;
if ( attrNode->name == "xml:lang" ) NormalizeLangValue ( &attrNode->value );
elemNode->attrs.push_back ( attrNode );
diff --git a/source/XMPCore/ParseRDF.cpp b/source/XMPCore/ParseRDF.cpp
index 31fb2b9..9931194 100644
--- a/source/XMPCore/ParseRDF.cpp
+++ b/source/XMPCore/ParseRDF.cpp
@@ -1,5 +1,5 @@
// =================================================================================================
-// Copyright 2002-2008 Adobe Systems Incorporated
+// Copyright 2004 Adobe Systems Incorporated
// All Rights Reserved.
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -490,30 +490,30 @@ AddQualifierNode ( XMP_Node * xmpParent, const XMP_VarString & name, const XMP_V
XMP_Node * newQual = 0;
- newQual = new XMP_Node ( xmpParent, name, value, kXMP_PropIsQualifier );
+ newQual = new XMP_Node ( xmpParent, name, value, kXMP_PropIsQualifier );
- if ( ! (isLang | isType) ) {
+ if ( ! (isLang | isType) ) {
+ xmpParent->qualifiers.push_back ( newQual );
+ } else if ( isLang ) {
+ if ( xmpParent->qualifiers.empty() ) {
xmpParent->qualifiers.push_back ( newQual );
- } else if ( isLang ) {
- if ( xmpParent->qualifiers.empty() ) {
- xmpParent->qualifiers.push_back ( newQual );
- } else {
- xmpParent->qualifiers.insert ( xmpParent->qualifiers.begin(), newQual );
- }
- xmpParent->options |= kXMP_PropHasLang;
} else {
- XMP_Assert ( isType );
- if ( xmpParent->qualifiers.empty() ) {
- xmpParent->qualifiers.push_back ( newQual );
- } else {
- size_t offset = 0;
- if ( XMP_PropHasLang ( xmpParent->options ) ) offset = 1;
- xmpParent->qualifiers.insert ( xmpParent->qualifiers.begin()+offset, newQual );
- }
- xmpParent->options |= kXMP_PropHasType;
+ xmpParent->qualifiers.insert ( xmpParent->qualifiers.begin(), newQual );
}
+ xmpParent->options |= kXMP_PropHasLang;
+ } else {
+ XMP_Assert ( isType );
+ if ( xmpParent->qualifiers.empty() ) {
+ xmpParent->qualifiers.push_back ( newQual );
+ } else {
+ size_t offset = 0;
+ if ( XMP_PropHasLang ( xmpParent->options ) ) offset = 1;
+ xmpParent->qualifiers.insert ( xmpParent->qualifiers.begin()+offset, newQual );
+ }
+ xmpParent->options |= kXMP_PropHasType;
+ }
- xmpParent->options |= kXMP_PropHasQualifiers;
+ xmpParent->options |= kXMP_PropHasQualifiers;
return newQual;
@@ -608,26 +608,25 @@ FixupQualifiedNode ( XMP_Node * xmpParent )
for ( childNum = 1, childLim = xmpParent->children.size(); childNum != childLim; ++childNum ) {
XMP_Node * currQual = xmpParent->children[childNum];
-
- bool isLang = (currQual->name == "xml:lang");
-
- currQual->options |= kXMP_PropIsQualifier;
- currQual->parent = xmpParent;
-
- if ( isLang ) {
- if ( xmpParent->options & kXMP_PropHasLang ) XMP_Throw ( "Duplicate xml:lang qualifier", kXMPErr_BadXMP );
- xmpParent->options |= kXMP_PropHasLang;
- } else if ( currQual->name == "rdf:type" ) {
- xmpParent->options |= kXMP_PropHasType;
- }
-
- if ( (! isLang) || xmpParent->qualifiers.empty() ) {
- xmpParent->qualifiers.push_back ( currQual );
- } else {
- xmpParent->qualifiers.insert ( xmpParent->qualifiers.begin(), currQual );
- }
- xmpParent->children[childNum] = 0; // We just moved it to the qualifers.
+ bool isLang = (currQual->name == "xml:lang");
+ currQual->options |= kXMP_PropIsQualifier;
+ currQual->parent = xmpParent;
+
+ if ( isLang ) {
+ if ( xmpParent->options & kXMP_PropHasLang ) XMP_Throw ( "Duplicate xml:lang qualifier", kXMPErr_BadXMP );
+ xmpParent->options |= kXMP_PropHasLang;
+ } else if ( currQual->name == "rdf:type" ) {
+ xmpParent->options |= kXMP_PropHasType;
+ }
+
+ if ( (! isLang) || xmpParent->qualifiers.empty() ) {
+ xmpParent->qualifiers.push_back ( currQual );
+ } else {
+ xmpParent->qualifiers.insert ( xmpParent->qualifiers.begin(), currQual );
+ }
+ xmpParent->children[childNum] = 0; // We just moved it to the qualifers.
+
}
if ( ! xmpParent->qualifiers.empty() ) xmpParent->options |= kXMP_PropHasQualifiers;
diff --git a/source/XMPCore/WXMPIterator.cpp b/source/XMPCore/WXMPIterator.cpp
index b52fa0d..6b0e69e 100644
--- a/source/XMPCore/WXMPIterator.cpp
+++ b/source/XMPCore/WXMPIterator.cpp
@@ -1,5 +1,5 @@
// =================================================================================================
-// Copyright 2002-2007 Adobe Systems Incorporated
+// Copyright 2004 Adobe Systems Incorporated
// All Rights Reserved.
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -7,11 +7,13 @@
// =================================================================================================
#include "XMP_Environment.h" // ! This must be the first include!
-#include "XMPCore_Impl.hpp"
+#include "XMP_Const.h"
-#include "XMPIterator.hpp"
#include "client-glue/WXMPIterator.hpp"
+#include "XMPCore_Impl.hpp"
+#include "XMPIterator.hpp"
+
#if XMP_WinBuild
#pragma warning ( disable : 4101 ) // unreferenced local variable
#pragma warning ( disable : 4189 ) // local variable is initialized but not referenced
@@ -36,18 +38,20 @@ WXMPIterator_PropCTor_1 ( XMPMetaRef xmpRef,
XMP_OptionBits options,
WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER ( "WXMPIterator_PropCTor_1" )
+ XMP_ENTER_Static ( "WXMPIterator_PropCTor_1" ) // No lib object yet, use the static entry.
if ( schemaNS == 0 ) schemaNS = "";
if ( propName == 0 ) propName = "";
const XMPMeta & xmpObj = WtoXMPMeta_Ref ( xmpRef );
- XMPIterator * iter = new XMPIterator ( xmpObj, schemaNS, propName, options );
+ XMP_AutoLock metaLock ( &xmpObj.lock, kXMP_ReadLock );
+
+ XMPIterator * iter = new XMPIterator ( xmpObj, schemaNS, propName, options );
++iter->clientRefs;
XMP_Assert ( iter->clientRefs == 1 );
wResult->ptrResult = XMPIteratorRef ( iter );
- XMP_EXIT_WRAPPER
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
@@ -58,7 +62,7 @@ WXMPIterator_TableCTor_1 ( XMP_StringPtr schemaNS,
XMP_OptionBits options,
WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER ( "WXMPIterator_TableCTor_1" )
+ XMP_ENTER_Static ( "WXMPIterator_TableCTor_1" ) // No lib object yet, use the static entry.
if ( schemaNS == 0 ) schemaNS = "";
if ( propName == 0 ) propName = "";
@@ -68,53 +72,39 @@ WXMPIterator_TableCTor_1 ( XMP_StringPtr schemaNS,
XMP_Assert ( iter->clientRefs == 1 );
wResult->ptrResult = XMPIteratorRef ( iter );
- XMP_EXIT_WRAPPER
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
void
-WXMPIterator_IncrementRefCount_1 ( XMPIteratorRef iterRef )
+WXMPIterator_IncrementRefCount_1 ( XMPIteratorRef xmpObjRef )
{
WXMP_Result * wResult = &void_wResult; // ! Needed to "fool" the EnterWrapper macro.
- XMP_ENTER_WRAPPER ( "WXMPIterator_IncrementRefCount_1" )
+ XMP_ENTER_ObjWrite ( XMPIterator, "WXMPIterator_IncrementRefCount_1" )
- XMPIterator * thiz = (XMPIterator*)iterRef;
-
++thiz->clientRefs;
XMP_Assert ( thiz->clientRefs > 1 );
- XMP_EXIT_WRAPPER_NO_THROW
+ XMP_EXIT_NoThrow
}
// -------------------------------------------------------------------------------------------------
void
-WXMPIterator_DecrementRefCount_1 ( XMPIteratorRef iterRef )
+WXMPIterator_DecrementRefCount_1 ( XMPIteratorRef xmpObjRef )
{
WXMP_Result * wResult = &void_wResult; // ! Needed to "fool" the EnterWrapper macro.
- XMP_ENTER_WRAPPER ( "WXMPIterator_DecrementRefCount_1" )
+ XMP_ENTER_ObjWrite ( XMPIterator, "WXMPIterator_DecrementRefCount_1" )
- XMPIterator * thiz = (XMPIterator*)iterRef;
-
XMP_Assert ( thiz->clientRefs > 0 );
--thiz->clientRefs;
- if ( thiz->clientRefs <= 0 ) delete ( thiz );
-
- XMP_EXIT_WRAPPER_NO_THROW
-}
-
-// -------------------------------------------------------------------------------------------------
-
-void
-WXMPIterator_Unlock_1 ( XMP_OptionBits options )
-{
- WXMP_Result * wResult = &void_wResult; // ! Needed to "fool" the EnterWrapper macro.
- XMP_ENTER_WRAPPER_NO_LOCK ( "WXMPIterator_Unlock_1" )
+ if ( thiz->clientRefs <= 0 ) {
+ objLock.Release();
+ delete ( thiz );
+ }
- XMPIterator::Unlock ( options );
-
- XMP_EXIT_WRAPPER_NO_THROW
+ XMP_EXIT_NoThrow
}
// =================================================================================================
@@ -122,61 +112,53 @@ WXMPIterator_Unlock_1 ( XMP_OptionBits options )
// =====================
void
-WXMPIterator_Next_1 ( XMPIteratorRef iterRef,
- XMP_StringPtr * schemaNS,
- XMP_StringLen * nsSize,
- XMP_StringPtr * propPath,
- XMP_StringLen * pathSize,
- XMP_StringPtr * propValue,
- XMP_StringLen * valueSize,
+WXMPIterator_Next_1 ( XMPIteratorRef xmpObjRef,
+ void * schemaNS,
+ void * propPath,
+ void * propValue,
XMP_OptionBits * propOptions,
+ SetClientStringProc SetClientString,
WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER ( "WXMPIterator_Next_1" )
-
- if ( schemaNS == 0 ) schemaNS = &voidStringPtr;
- if ( nsSize == 0 ) nsSize = &voidStringLen;
- if ( propPath == 0 ) propPath = &voidStringPtr;
- if ( pathSize == 0 ) pathSize = &voidStringLen;
- if ( propValue == 0 ) propValue = &voidStringPtr;
- if ( valueSize == 0 ) valueSize = &voidStringLen;
+ XMP_ENTER_ObjWrite ( XMPIterator, "WXMPIterator_Next_1" )
+
+ XMP_StringPtr schemaPtr = 0;
+ XMP_StringLen schemaLen = 0;
+ XMP_StringPtr pathPtr = 0;
+ XMP_StringLen pathLen = 0;
+ XMP_StringPtr valuePtr = 0;
+ XMP_StringLen valueLen = 0;
+
if ( propOptions == 0 ) propOptions = &voidOptionBits;
- XMPIterator * iter = WtoXMPIterator_Ptr ( iterRef );
- XMP_Bool found = iter->Next ( schemaNS, nsSize, propPath, pathSize, propValue, valueSize, propOptions );
+ XMP_AutoLock metaLock ( &thiz->info.xmpObj->lock, kXMP_ReadLock, (thiz->info.xmpObj != 0) );
+
+ XMP_Bool found = thiz->Next ( &schemaPtr, &schemaLen, &pathPtr, &pathLen, &valuePtr, &valueLen, propOptions );
wResult->int32Result = found;
+
+ if ( found ) {
+ if ( schemaNS != 0 ) (*SetClientString) ( schemaNS, schemaPtr, schemaLen );
+ if ( propPath != 0 ) (*SetClientString) ( propPath, pathPtr, pathLen );
+ if ( propValue != 0 ) (*SetClientString) ( propValue, valuePtr, valueLen );
+ }
- XMP_EXIT_WRAPPER_KEEP_LOCK ( found )
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
void
-WXMPIterator_Skip_1 ( XMPIteratorRef iterRef,
+WXMPIterator_Skip_1 ( XMPIteratorRef xmpObjRef,
XMP_OptionBits options,
WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER ( "WXMPIterator_Skip_1" )
-
- XMPIterator * iter = WtoXMPIterator_Ptr ( iterRef );
- iter->Skip ( options );
+ XMP_ENTER_ObjWrite ( XMPIterator, "WXMPIterator_Skip_1" )
- XMP_EXIT_WRAPPER
-}
-
-// -------------------------------------------------------------------------------------------------
-
-void
-WXMPUtils_UnlockIter_1 ( XMPIteratorRef iterRef,
- XMP_OptionBits options )
-{
- WXMP_Result * wResult = &void_wResult; // ! Needed to "fool" the EnterWrapper macro.
- XMP_ENTER_WRAPPER_NO_LOCK ( "WXMPUtils_UnlockIter_1" )
+ XMP_AutoLock metaLock ( &thiz->info.xmpObj->lock, kXMP_ReadLock, (thiz->info.xmpObj != 0) );
- XMPIterator * iter = WtoXMPIterator_Ptr ( iterRef );
- iter->UnlockIter ( options );
+ thiz->Skip ( options );
- XMP_EXIT_WRAPPER_NO_THROW
+ XMP_EXIT
}
// =================================================================================================
diff --git a/source/XMPCore/WXMPMeta.cpp b/source/XMPCore/WXMPMeta.cpp
index 4c95c5d..e7138e4 100644
--- a/source/XMPCore/WXMPMeta.cpp
+++ b/source/XMPCore/WXMPMeta.cpp
@@ -1,5 +1,5 @@
// =================================================================================================
-// Copyright 2002-2008 Adobe Systems Incorporated
+// Copyright 2004 Adobe Systems Incorporated
// All Rights Reserved.
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -7,11 +7,13 @@
// =================================================================================================
#include "XMP_Environment.h" // ! This must be the first include!
-#include "XMPCore_Impl.hpp"
+#include "XMP_Const.h"
-#include "XMPMeta.hpp"
#include "client-glue/WXMPMeta.hpp"
+#include "XMPCore_Impl.hpp"
+#include "XMPMeta.hpp"
+
#if XMP_WinBuild
#pragma warning ( disable : 4101 ) // unreferenced local variable
#pragma warning ( disable : 4189 ) // local variable is initialized but not referenced
@@ -34,11 +36,11 @@ extern "C" {
WXMPMeta_GetVersionInfo_1 ( XMP_VersionInfo * info )
{
WXMP_Result * wResult = &void_wResult; // ! Needed to "fool" the EnterWrapper macro.
- XMP_ENTER_WRAPPER_NO_LOCK ( "WXMPMeta_GetVersionInfo_1" )
+ XMP_ENTER_NoLock ( "WXMPMeta_GetVersionInfo_1" )
XMPMeta::GetVersionInfo ( info );
- XMP_EXIT_WRAPPER_NO_THROW
+ XMP_EXIT_NoThrow
}
// -------------------------------------------------------------------------------------------------
@@ -46,12 +48,11 @@ WXMPMeta_GetVersionInfo_1 ( XMP_VersionInfo * info )
/* class static */ void
WXMPMeta_Initialize_1 ( WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER_NO_LOCK ( "WXMPMeta_Initialize_1" )
+ XMP_ENTER_NoLock ( "WXMPMeta_Initialize_1" )
- bool ok = XMPMeta::Initialize();
- wResult->int32Result = ok;
+ wResult->int32Result = XMPMeta::Initialize();
- XMP_EXIT_WRAPPER
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
@@ -59,11 +60,11 @@ WXMPMeta_Initialize_1 ( WXMP_Result * wResult )
WXMPMeta_Terminate_1()
{
WXMP_Result * wResult = &void_wResult; // ! Needed to "fool" the EnterWrapper macro.
- XMP_ENTER_WRAPPER_NO_LOCK ( "WXMPMeta_Terminate_1" )
+ XMP_ENTER_NoLock ( "WXMPMeta_Terminate_1" )
XMPMeta::Terminate();
- XMP_EXIT_WRAPPER_NO_THROW
+ XMP_EXIT_NoThrow
}
// =================================================================================================
@@ -73,83 +74,61 @@ WXMPMeta_Terminate_1()
void
WXMPMeta_CTor_1 ( WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER ( "WXMPMeta_CTor_1" )
+ XMP_ENTER_Static ( "WXMPMeta_CTor_1" ) // No lib object yet, use the static entry.
XMPMeta * xmpObj = new XMPMeta();
++xmpObj->clientRefs;
XMP_Assert ( xmpObj->clientRefs == 1 );
wResult->ptrResult = XMPMetaRef ( xmpObj );
- XMP_EXIT_WRAPPER
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
void
-WXMPMeta_IncrementRefCount_1 ( XMPMetaRef xmpRef )
+WXMPMeta_IncrementRefCount_1 ( XMPMetaRef xmpObjRef )
{
WXMP_Result * wResult = &void_wResult; // ! Needed to "fool" the EnterWrapper macro.
- XMP_ENTER_WRAPPER ( "WXMPMeta_IncrementRefCount_1" )
+ XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_IncrementRefCount_1" )
- XMPMeta * thiz = (XMPMeta*)xmpRef;
-
++thiz->clientRefs;
XMP_Assert ( thiz->clientRefs > 0 );
- XMP_EXIT_WRAPPER_NO_THROW
+ XMP_EXIT_NoThrow
}
// -------------------------------------------------------------------------------------------------
void
-WXMPMeta_DecrementRefCount_1 ( XMPMetaRef xmpRef )
+WXMPMeta_DecrementRefCount_1 ( XMPMetaRef xmpObjRef )
{
WXMP_Result * wResult = &void_wResult; // ! Needed to "fool" the EnterWrapper macro.
- XMP_ENTER_WRAPPER ( "WXMPMeta_DecrementRefCount_1" )
+ XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_DecrementRefCount_1" )
- XMPMeta * thiz = (XMPMeta*)xmpRef;
-
XMP_Assert ( thiz->clientRefs > 0 );
--thiz->clientRefs;
- if ( thiz->clientRefs <= 0 ) delete ( thiz );
+ if ( thiz->clientRefs <= 0 ) {
+ objLock.Release();
+ delete ( thiz );
+ }
- XMP_EXIT_WRAPPER_NO_THROW
+ XMP_EXIT_NoThrow
}
// =================================================================================================
// Class Static Wrappers
// =====================
-//
-// These are DLL-entry wrappers for class-static functions. They all follow a simple pattern:
-//
-// try
-// acquire toolbox lock
-// validate parameters
-// call through to the implementation
-// retain toolbox lock if necessary
-// catch anything and return an appropriate XMP_Error object
-// return null (no error if we get to here)
-//
-// The toolbox lock is acquired through a local wrapper object that automatically unlocks when the
-// try-block is exited. The lock must be retained if the function is returning a string result. The
-// output string is owned by the toolkit, the client must copy the string then release the lock.
-// The lock used here is the overall toolkit lock. For simplicity at this time the lock is a simple
-// mutual exclusion lock, we do not allow multiple concurrent readers.
-//
-// The one exception to this model is UnlockToolkit. It does not acquire the toolkit lock since this
-// is the function the client calls to release the lock after copying an output string!
-//
-// =================================================================================================
/* class static */ void
WXMPMeta_GetGlobalOptions_1 ( WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER ( "WXMPMeta_GetGlobalOptions_1" )
+ XMP_ENTER_Static ( "WXMPMeta_GetGlobalOptions_1" )
XMP_OptionBits options = XMPMeta::GetGlobalOptions();
wResult->int32Result = options;
- XMP_EXIT_WRAPPER
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
@@ -158,11 +137,11 @@ WXMPMeta_GetGlobalOptions_1 ( WXMP_Result * wResult )
WXMPMeta_SetGlobalOptions_1 ( XMP_OptionBits options,
WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER ( "WXMPMeta_SetGlobalOptions_1" )
+ XMP_ENTER_Static ( "WXMPMeta_SetGlobalOptions_1" )
XMPMeta::SetGlobalOptions ( options );
- XMP_EXIT_WRAPPER
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
@@ -171,109 +150,85 @@ WXMPMeta_DumpNamespaces_1 ( XMP_TextOutputProc outProc,
void * refCon,
WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER ( "WXMPMeta_DumpNamespaces_1" )
+ XMP_ENTER_Static ( "WXMPMeta_DumpNamespaces_1" )
if ( outProc == 0 ) XMP_Throw ( "Null client output routine", kXMPErr_BadParam );
XMP_Status status = XMPMeta::DumpNamespaces ( outProc, refCon );
wResult->int32Result = status;
- XMP_EXIT_WRAPPER
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
/* class static */ void
-WXMPMeta_DumpAliases_1 ( XMP_TextOutputProc outProc,
- void * refCon,
- WXMP_Result * wResult )
-{
- XMP_ENTER_WRAPPER ( "WXMPMeta_DumpAliases_1" )
-
- if ( outProc == 0 ) XMP_Throw ( "Null client output routine", kXMPErr_BadParam );
-
- XMP_Status status = XMPMeta::DumpAliases ( outProc, refCon );
- wResult->int32Result = status;
-
- XMP_EXIT_WRAPPER
-}
-
-// -------------------------------------------------------------------------------------------------
-
-/* class static */ void
-WXMPMeta_Unlock_1 ( XMP_OptionBits options )
-{
- WXMP_Result * wResult = &void_wResult; // ! Needed to "fool" the EnterWrapper macro.
- XMP_ENTER_WRAPPER_NO_LOCK ( "WXMPMeta_Unlock_1" )
-
- XMPMeta::Unlock ( options );
-
- XMP_EXIT_WRAPPER_NO_THROW
-}
-
-// -------------------------------------------------------------------------------------------------
-
-/* class static */ void
-WXMPMeta_RegisterNamespace_1 ( XMP_StringPtr namespaceURI,
- XMP_StringPtr suggestedPrefix,
- XMP_StringPtr * registeredPrefix,
- XMP_StringLen * prefixSize,
- WXMP_Result * wResult )
+WXMPMeta_RegisterNamespace_1 ( XMP_StringPtr namespaceURI,
+ XMP_StringPtr suggestedPrefix,
+ void * actualPrefix,
+ SetClientStringProc SetClientString,
+ WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER ( "WXMPMeta_RegisterNamespace_1" )
+ XMP_ENTER_Static ( "WXMPMeta_RegisterNamespace_1" )
if ( (namespaceURI == 0) || (*namespaceURI == 0) ) XMP_Throw ( "Empty namespace URI", kXMPErr_BadSchema );
if ( (suggestedPrefix == 0) || (*suggestedPrefix == 0) ) XMP_Throw ( "Empty suggested prefix", kXMPErr_BadSchema );
- if ( registeredPrefix == 0 ) registeredPrefix = &voidStringPtr;
- if ( prefixSize == 0 ) prefixSize = &voidStringLen;
+ XMP_StringPtr prefixPtr = 0;
+ XMP_StringLen prefixSize = 0;
- bool prefixMatch = XMPMeta::RegisterNamespace ( namespaceURI, suggestedPrefix, registeredPrefix, prefixSize );
+ bool prefixMatch = XMPMeta::RegisterNamespace ( namespaceURI, suggestedPrefix, &prefixPtr, &prefixSize );
wResult->int32Result = prefixMatch;
+
+ if ( actualPrefix != 0 ) (*SetClientString) ( actualPrefix, prefixPtr, prefixSize );
- XMP_EXIT_WRAPPER_KEEP_LOCK ( true ) // ! Always keep the lock, a string is always returned!
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
/* class static */ void
-WXMPMeta_GetNamespacePrefix_1 ( XMP_StringPtr namespaceURI,
- XMP_StringPtr * namespacePrefix,
- XMP_StringLen * prefixSize,
- WXMP_Result * wResult )
+WXMPMeta_GetNamespacePrefix_1 ( XMP_StringPtr namespaceURI,
+ void * namespacePrefix,
+ SetClientStringProc SetClientString,
+ WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER ( "WXMPMeta_GetNamespacePrefix_1" )
+ XMP_ENTER_Static ( "WXMPMeta_GetNamespacePrefix_1" )
if ( (namespaceURI == 0) || (*namespaceURI == 0) ) XMP_Throw ( "Empty namespace URI", kXMPErr_BadSchema );
- if ( namespacePrefix == 0 ) namespacePrefix = &voidStringPtr;
- if ( prefixSize == 0 ) prefixSize = &voidStringLen;
+ XMP_StringPtr prefixPtr = 0;
+ XMP_StringLen prefixSize = 0;
- bool found = XMPMeta::GetNamespacePrefix ( namespaceURI, namespacePrefix, prefixSize );
+ bool found = XMPMeta::GetNamespacePrefix ( namespaceURI, &prefixPtr, &prefixSize );
wResult->int32Result = found;
- XMP_EXIT_WRAPPER_KEEP_LOCK ( found )
+ if ( found && (namespacePrefix != 0) ) (*SetClientString) ( namespacePrefix, prefixPtr, prefixSize );
+
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
/* class static */ void
-WXMPMeta_GetNamespaceURI_1 ( XMP_StringPtr namespacePrefix,
- XMP_StringPtr * namespaceURI,
- XMP_StringLen * uriSize,
- WXMP_Result * wResult )
+WXMPMeta_GetNamespaceURI_1 ( XMP_StringPtr namespacePrefix,
+ void * namespaceURI,
+ SetClientStringProc SetClientString,
+ WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER ( "WXMPMeta_GetNamespaceURI_1" )
+ XMP_ENTER_Static ( "WXMPMeta_GetNamespaceURI_1" )
if ( (namespacePrefix == 0) || (*namespacePrefix == 0) ) XMP_Throw ( "Empty namespace prefix", kXMPErr_BadSchema );
- if ( namespaceURI == 0 ) namespaceURI = &voidStringPtr;
- if ( uriSize == 0 ) uriSize = &voidStringLen;
+ XMP_StringPtr uriPtr = 0;
+ XMP_StringLen uriSize = 0;
- bool found = XMPMeta::GetNamespaceURI ( namespacePrefix, namespaceURI, uriSize );
+ bool found = XMPMeta::GetNamespaceURI ( namespacePrefix, &uriPtr, &uriSize );
wResult->int32Result = found;
+
+ if ( found && (namespaceURI != 0) ) (*SetClientString) ( namespaceURI, uriPtr, uriSize );
- XMP_EXIT_WRAPPER_KEEP_LOCK ( found )
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
@@ -282,263 +237,162 @@ WXMPMeta_GetNamespaceURI_1 ( XMP_StringPtr namespacePrefix,
WXMPMeta_DeleteNamespace_1 ( XMP_StringPtr namespaceURI,
WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER ( "WXMPMeta_DeleteNamespace_1" )
+ XMP_ENTER_Static ( "WXMPMeta_DeleteNamespace_1" )
if ( (namespaceURI == 0) || (*namespaceURI == 0) ) XMP_Throw ( "Empty namespace URI", kXMPErr_BadSchema );
XMPMeta::DeleteNamespace ( namespaceURI );
- XMP_EXIT_WRAPPER
-}
-
-// -------------------------------------------------------------------------------------------------
-
-/* class static */ void
-WXMPMeta_RegisterAlias_1 ( XMP_StringPtr aliasNS,
- XMP_StringPtr aliasProp,
- XMP_StringPtr actualNS,
- XMP_StringPtr actualProp,
- XMP_OptionBits arrayForm,
- WXMP_Result * wResult )
-{
- XMP_ENTER_WRAPPER ( "WXMPMeta_RegisterAlias_1" )
-
- if ( (aliasNS == 0) || (*aliasNS == 0) ) XMP_Throw ( "Empty alias namespace URI", kXMPErr_BadSchema );
- if ( (aliasProp == 0) || (*aliasProp == 0) ) XMP_Throw ( "Empty alias property name", kXMPErr_BadXPath );
- if ( (actualNS == 0) || (*actualNS == 0) ) XMP_Throw ( "Empty actual namespace URI", kXMPErr_BadSchema );
- if ( (actualProp == 0) || (*actualProp == 0) ) XMP_Throw ( "Empty actual property name", kXMPErr_BadXPath );
-
- XMPMeta::RegisterAlias ( aliasNS, aliasProp, actualNS, actualProp, arrayForm );
-
- XMP_EXIT_WRAPPER
-}
-
-// -------------------------------------------------------------------------------------------------
-
-/* class static */ void
-WXMPMeta_ResolveAlias_1 ( XMP_StringPtr aliasNS,
- XMP_StringPtr aliasProp,
- XMP_StringPtr * actualNS,
- XMP_StringLen * nsSize,
- XMP_StringPtr * actualProp,
- XMP_StringLen * propSize,
- XMP_OptionBits * arrayForm,
- WXMP_Result * wResult )
-{
- XMP_ENTER_WRAPPER ( "WXMPMeta_ResolveAlias_1" )
-
- if ( (aliasNS == 0) || (*aliasNS == 0) ) XMP_Throw ( "Empty alias namespace URI", kXMPErr_BadSchema );
- if ( (aliasProp == 0) || (*aliasProp == 0) ) XMP_Throw ( "Empty alias property name", kXMPErr_BadXPath );
-
- if ( actualNS == 0 ) actualNS = &voidStringPtr;
- if ( nsSize == 0 ) nsSize = &voidStringLen;
- if ( actualProp == 0 ) actualProp = &voidStringPtr;
- if ( propSize == 0 ) propSize = &voidStringLen;
- if ( arrayForm == 0 ) arrayForm = &voidOptionBits;
-
- bool found = XMPMeta::ResolveAlias ( aliasNS, aliasProp, actualNS, nsSize, actualProp, propSize, arrayForm );
- wResult->int32Result = found;
-
- XMP_EXIT_WRAPPER_KEEP_LOCK ( found )
-}
-
-// -------------------------------------------------------------------------------------------------
-
-/* class static */ void
-WXMPMeta_DeleteAlias_1 ( XMP_StringPtr aliasNS,
- XMP_StringPtr aliasProp,
- WXMP_Result * wResult )
-{
- XMP_ENTER_WRAPPER ( "WXMPMeta_DeleteAlias_1" )
-
- if ( (aliasNS == 0) || (*aliasNS == 0) ) XMP_Throw ( "Empty alias namespace URI", kXMPErr_BadSchema );
- if ( (aliasProp == 0) || (*aliasProp == 0) ) XMP_Throw ( "Empty alias property name", kXMPErr_BadXPath );
-
- XMPMeta::DeleteAlias ( aliasNS, aliasProp );
-
- XMP_EXIT_WRAPPER
-}
-
-// -------------------------------------------------------------------------------------------------
-
-/* class static */ void
-WXMPMeta_RegisterStandardAliases_1 ( XMP_StringPtr schemaNS,
- WXMP_Result * wResult )
-{
- XMP_ENTER_WRAPPER ( "WXMPMeta_RegisterStandardAliases_1" )
-
- if ( schemaNS == 0 ) schemaNS = "";
-
- XMPMeta::RegisterStandardAliases ( schemaNS );
-
- XMP_EXIT_WRAPPER
+ XMP_EXIT
}
// =================================================================================================
// Class Method Wrappers
// =====================
-//
-// These are DLL-entry wrappers for the methods. They all follow a simple pattern:
-//
-// validate parameters
-// try
-// acquire object lock
-// call through to the implementation
-// retain object lock if necessary
-// catch anything and return an appropriate XMP_Error object
-// return null (no error if we get to here)
-//
-// The object lock is acquired through a local wrapper object that automatically unlocks when the
-// try-block is exited. The lock must be retained if the function is returning a string result. The
-// output string is owned by the object, the client must copy the string then release the lock. The
-// lock used here is the per-object lock. For simplicity at this time the lock is a simple mutual
-// exclusion lock, we do not allow multiple concurrent readers.
-//
-// The one exception to this model is UnlockObject. It does not acquire the object lock since this
-// is the function the client calls to release the lock after copying an output string!
-//
-// =================================================================================================
void
-WXMPMeta_GetProperty_1 ( XMPMetaRef xmpRef,
+WXMPMeta_GetProperty_1 ( XMPMetaRef xmpObjRef,
XMP_StringPtr schemaNS,
XMP_StringPtr propName,
- XMP_StringPtr * propValue,
- XMP_StringLen * valueSize,
+ void * propValue,
XMP_OptionBits * options,
+ SetClientStringProc SetClientString,
WXMP_Result * wResult ) /* const */
{
- XMP_ENTER_WRAPPER ( "WXMPMeta_GetProperty_1" )
+ XMP_ENTER_ObjRead ( XMPMeta, "WXMPMeta_GetProperty_1" )
if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
if ( (propName == 0) || (*propName == 0) ) XMP_Throw ( "Empty property name", kXMPErr_BadXPath );
- if ( propValue == 0 ) propValue = &voidStringPtr;
- if ( valueSize == 0 ) valueSize = &voidStringLen;
+ XMP_StringPtr valuePtr = 0;
+ XMP_StringLen valueSize = 0;
if ( options == 0 ) options = &voidOptionBits;
- const XMPMeta & meta = WtoXMPMeta_Ref ( xmpRef );
- bool found = meta.GetProperty ( schemaNS, propName, propValue, valueSize, options );
+ bool found = thiz.GetProperty ( schemaNS, propName, &valuePtr, &valueSize, options );
wResult->int32Result = found;
+
+ if ( found && (propValue != 0) ) (*SetClientString) ( propValue, valuePtr, valueSize );
- XMP_EXIT_WRAPPER_KEEP_LOCK ( found )
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
void
-WXMPMeta_GetArrayItem_1 ( XMPMetaRef xmpRef,
+WXMPMeta_GetArrayItem_1 ( XMPMetaRef xmpObjRef,
XMP_StringPtr schemaNS,
XMP_StringPtr arrayName,
XMP_Index itemIndex,
- XMP_StringPtr * itemValue,
- XMP_StringLen * valueSize,
+ void * itemValue,
XMP_OptionBits * options,
+ SetClientStringProc SetClientString,
WXMP_Result * wResult ) /* const */
{
- XMP_ENTER_WRAPPER ( "WXMPMeta_GetArrayItem_1" )
+ XMP_ENTER_ObjRead ( XMPMeta, "WXMPMeta_GetArrayItem_1" )
if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
if ( (arrayName == 0) || (*arrayName == 0) ) XMP_Throw ( "Empty array name", kXMPErr_BadXPath );
- if ( itemValue == 0 ) itemValue = &voidStringPtr;
- if ( valueSize == 0 ) valueSize = &voidStringLen;
+ XMP_StringPtr valuePtr = 0;
+ XMP_StringLen valueSize = 0;
if ( options == 0 ) options = &voidOptionBits;
- const XMPMeta & meta = WtoXMPMeta_Ref ( xmpRef );
- bool found = meta.GetArrayItem ( schemaNS, arrayName, itemIndex, itemValue, valueSize, options );
+ bool found = thiz.GetArrayItem ( schemaNS, arrayName, itemIndex, &valuePtr, &valueSize, options );
wResult->int32Result = found;
+
+ if ( found && (itemValue != 0) ) (*SetClientString) ( itemValue, valuePtr, valueSize );
- XMP_EXIT_WRAPPER_KEEP_LOCK ( found )
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
void
-WXMPMeta_GetStructField_1 ( XMPMetaRef xmpRef,
+WXMPMeta_GetStructField_1 ( XMPMetaRef xmpObjRef,
XMP_StringPtr schemaNS,
XMP_StringPtr structName,
XMP_StringPtr fieldNS,
XMP_StringPtr fieldName,
- XMP_StringPtr * fieldValue,
- XMP_StringLen * valueSize,
+ void * fieldValue,
XMP_OptionBits * options,
+ SetClientStringProc SetClientString,
WXMP_Result * wResult ) /* const */
{
- XMP_ENTER_WRAPPER ( "WXMPMeta_GetStructField_1" )
+ XMP_ENTER_ObjRead ( XMPMeta, "WXMPMeta_GetStructField_1" )
if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
if ( (structName == 0) || (*structName == 0) ) XMP_Throw ( "Empty struct name", kXMPErr_BadXPath );
if ( (fieldNS == 0) || (*fieldNS == 0) ) XMP_Throw ( "Empty field namespace URI", kXMPErr_BadSchema );
if ( (fieldName == 0) || (*fieldName == 0) ) XMP_Throw ( "Empty field name", kXMPErr_BadXPath );
- if ( fieldValue == 0 ) fieldValue = &voidStringPtr;
- if ( valueSize == 0 ) valueSize = &voidStringLen;
+ XMP_StringPtr valuePtr = 0;
+ XMP_StringLen valueSize = 0;
if ( options == 0 ) options = &voidOptionBits;
- const XMPMeta & meta = WtoXMPMeta_Ref ( xmpRef );
- bool found = meta.GetStructField ( schemaNS, structName, fieldNS, fieldName, fieldValue, valueSize, options );
+ bool found = thiz.GetStructField ( schemaNS, structName, fieldNS, fieldName, &valuePtr, &valueSize, options );
wResult->int32Result = found;
+
+ if ( found && (fieldValue != 0) ) (*SetClientString) ( fieldValue, valuePtr, valueSize );
- XMP_EXIT_WRAPPER_KEEP_LOCK ( found )
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
void
-WXMPMeta_GetQualifier_1 ( XMPMetaRef xmpRef,
+WXMPMeta_GetQualifier_1 ( XMPMetaRef xmpObjRef,
XMP_StringPtr schemaNS,
XMP_StringPtr propName,
XMP_StringPtr qualNS,
XMP_StringPtr qualName,
- XMP_StringPtr * qualValue,
- XMP_StringLen * valueSize,
+ void * qualValue,
XMP_OptionBits * options,
+ SetClientStringProc SetClientString,
WXMP_Result * wResult ) /* const */
{
- XMP_ENTER_WRAPPER ( "WXMPMeta_GetQualifier_1" )
+ XMP_ENTER_ObjRead ( XMPMeta, "WXMPMeta_GetQualifier_1" )
if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
if ( (propName == 0) || (*propName == 0) ) XMP_Throw ( "Empty property name", kXMPErr_BadXPath );
if ( (qualNS == 0) || (*qualNS == 0) ) XMP_Throw ( "Empty qualifier namespace URI", kXMPErr_BadSchema );
if ( (qualName == 0) || (*qualName == 0) ) XMP_Throw ( "Empty qualifier name", kXMPErr_BadXPath );
- if ( qualValue == 0 ) qualValue = &voidStringPtr;
- if ( valueSize == 0 ) valueSize = &voidStringLen;
+ XMP_StringPtr valuePtr = 0;
+ XMP_StringLen valueSize = 0;
if ( options == 0 ) options = &voidOptionBits;
- const XMPMeta & meta = WtoXMPMeta_Ref ( xmpRef );
- bool found = meta.GetQualifier ( schemaNS, propName, qualNS, qualName, qualValue, valueSize, options );
+ bool found = thiz.GetQualifier ( schemaNS, propName, qualNS, qualName, &valuePtr, &valueSize, options );
wResult->int32Result = found;
+
+ if ( found && (qualValue != 0) ) (*SetClientString) ( qualValue, valuePtr, valueSize );
- XMP_EXIT_WRAPPER_KEEP_LOCK ( found )
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
void
-WXMPMeta_SetProperty_1 ( XMPMetaRef xmpRef,
+WXMPMeta_SetProperty_1 ( XMPMetaRef xmpObjRef,
XMP_StringPtr schemaNS,
XMP_StringPtr propName,
XMP_StringPtr propValue,
XMP_OptionBits options,
WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER ( "WXMPMeta_SetProperty_1" )
+ XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_SetProperty_1" )
if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
if ( (propName == 0) || (*propName == 0) ) XMP_Throw ( "Empty property name", kXMPErr_BadXPath );
- XMPMeta * meta = WtoXMPMeta_Ptr ( xmpRef );
- meta->SetProperty ( schemaNS, propName, propValue, options );
+ thiz->SetProperty ( schemaNS, propName, propValue, options );
- XMP_EXIT_WRAPPER
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
void
-WXMPMeta_SetArrayItem_1 ( XMPMetaRef xmpRef,
+WXMPMeta_SetArrayItem_1 ( XMPMetaRef xmpObjRef,
XMP_StringPtr schemaNS,
XMP_StringPtr arrayName,
XMP_Index itemIndex,
@@ -546,21 +400,20 @@ WXMPMeta_SetArrayItem_1 ( XMPMetaRef xmpRef,
XMP_OptionBits options,
WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER ( "WXMPMeta_SetArrayItem_1" )
+ XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_SetArrayItem_1" )
if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
if ( (arrayName == 0) || (*arrayName == 0) ) XMP_Throw ( "Empty array name", kXMPErr_BadXPath );
- XMPMeta * meta = WtoXMPMeta_Ptr ( xmpRef );
- meta->SetArrayItem ( schemaNS, arrayName, itemIndex, itemValue, options );
+ thiz->SetArrayItem ( schemaNS, arrayName, itemIndex, itemValue, options );
- XMP_EXIT_WRAPPER
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
void
-WXMPMeta_AppendArrayItem_1 ( XMPMetaRef xmpRef,
+WXMPMeta_AppendArrayItem_1 ( XMPMetaRef xmpObjRef,
XMP_StringPtr schemaNS,
XMP_StringPtr arrayName,
XMP_OptionBits arrayOptions,
@@ -568,21 +421,20 @@ WXMPMeta_AppendArrayItem_1 ( XMPMetaRef xmpRef,
XMP_OptionBits options,
WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER ( "WXMPMeta_AppendArrayItem_1" )
+ XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_AppendArrayItem_1" )
if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
if ( (arrayName == 0) || (*arrayName == 0) ) XMP_Throw ( "Empty array name", kXMPErr_BadXPath );
- XMPMeta * meta = WtoXMPMeta_Ptr ( xmpRef );
- meta->AppendArrayItem ( schemaNS, arrayName, arrayOptions, itemValue, options );
+ thiz->AppendArrayItem ( schemaNS, arrayName, arrayOptions, itemValue, options );
- XMP_EXIT_WRAPPER
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
void
-WXMPMeta_SetStructField_1 ( XMPMetaRef xmpRef,
+WXMPMeta_SetStructField_1 ( XMPMetaRef xmpObjRef,
XMP_StringPtr schemaNS,
XMP_StringPtr structName,
XMP_StringPtr fieldNS,
@@ -591,23 +443,22 @@ WXMPMeta_SetStructField_1 ( XMPMetaRef xmpRef,
XMP_OptionBits options,
WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER ( "WXMPMeta_SetStructField_1" )
+ XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_SetStructField_1" )
if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
if ( (structName == 0) || (*structName == 0) ) XMP_Throw ( "Empty struct name", kXMPErr_BadXPath );
if ( (fieldNS == 0) || (*fieldNS == 0) ) XMP_Throw ( "Empty field namespace URI", kXMPErr_BadSchema );
if ( (fieldName == 0) || (*fieldName == 0) ) XMP_Throw ( "Empty field name", kXMPErr_BadXPath );
- XMPMeta * meta = WtoXMPMeta_Ptr ( xmpRef );
- meta->SetStructField ( schemaNS, structName, fieldNS, fieldName, fieldValue, options );
+ thiz->SetStructField ( schemaNS, structName, fieldNS, fieldName, fieldValue, options );
- XMP_EXIT_WRAPPER
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
void
-WXMPMeta_SetQualifier_1 ( XMPMetaRef xmpRef,
+WXMPMeta_SetQualifier_1 ( XMPMetaRef xmpObjRef,
XMP_StringPtr schemaNS,
XMP_StringPtr propName,
XMP_StringPtr qualNS,
@@ -616,233 +467,227 @@ WXMPMeta_SetQualifier_1 ( XMPMetaRef xmpRef,
XMP_OptionBits options,
WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER ( "WXMPMeta_SetQualifier_1" )
+ XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_SetQualifier_1" )
if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
if ( (propName == 0) || (*propName == 0) ) XMP_Throw ( "Empty property name", kXMPErr_BadXPath );
if ( (qualNS == 0) || (*qualNS == 0) ) XMP_Throw ( "Empty qualifier namespace URI", kXMPErr_BadSchema );
if ( (qualName == 0) || (*qualName == 0) ) XMP_Throw ( "Empty qualifier name", kXMPErr_BadXPath );
- XMPMeta * meta = WtoXMPMeta_Ptr ( xmpRef );
- meta->SetQualifier ( schemaNS, propName, qualNS, qualName, qualValue, options );
+ thiz->SetQualifier ( schemaNS, propName, qualNS, qualName, qualValue, options );
- XMP_EXIT_WRAPPER
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
void
-WXMPMeta_DeleteProperty_1 ( XMPMetaRef xmpRef,
+WXMPMeta_DeleteProperty_1 ( XMPMetaRef xmpObjRef,
XMP_StringPtr schemaNS,
XMP_StringPtr propName,
WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER ( "WXMPMeta_DeleteProperty_1" )
+ XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_DeleteProperty_1" )
if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
if ( (propName == 0) || (*propName == 0) ) XMP_Throw ( "Empty property name", kXMPErr_BadXPath );
- XMPMeta * meta = WtoXMPMeta_Ptr ( xmpRef );
- meta->DeleteProperty ( schemaNS, propName );
+ thiz->DeleteProperty ( schemaNS, propName );
- XMP_EXIT_WRAPPER
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
void
-WXMPMeta_DeleteArrayItem_1 ( XMPMetaRef xmpRef,
+WXMPMeta_DeleteArrayItem_1 ( XMPMetaRef xmpObjRef,
XMP_StringPtr schemaNS,
XMP_StringPtr arrayName,
XMP_Index itemIndex,
WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER ( "WXMPMeta_DeleteArrayItem_1" )
+ XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_DeleteArrayItem_1" )
if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
if ( (arrayName == 0) || (*arrayName == 0) ) XMP_Throw ( "Empty array name", kXMPErr_BadXPath );
- XMPMeta * meta = WtoXMPMeta_Ptr ( xmpRef );
- meta->DeleteArrayItem ( schemaNS, arrayName, itemIndex );
+ thiz->DeleteArrayItem ( schemaNS, arrayName, itemIndex );
- XMP_EXIT_WRAPPER
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
void
-WXMPMeta_DeleteStructField_1 ( XMPMetaRef xmpRef,
+WXMPMeta_DeleteStructField_1 ( XMPMetaRef xmpObjRef,
XMP_StringPtr schemaNS,
XMP_StringPtr structName,
XMP_StringPtr fieldNS,
XMP_StringPtr fieldName,
WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER ( "WXMPMeta_DeleteStructField_1" )
+ XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_DeleteStructField_1" )
if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
if ( (structName == 0) || (*structName == 0) ) XMP_Throw ( "Empty struct name", kXMPErr_BadXPath );
if ( (fieldNS == 0) || (*fieldNS == 0) ) XMP_Throw ( "Empty field namespace URI", kXMPErr_BadSchema );
if ( (fieldName == 0) || (*fieldName == 0) ) XMP_Throw ( "Empty field name", kXMPErr_BadXPath );
- XMPMeta * meta = WtoXMPMeta_Ptr ( xmpRef );
- meta->DeleteStructField ( schemaNS, structName, fieldNS, fieldName );
+ thiz->DeleteStructField ( schemaNS, structName, fieldNS, fieldName );
- XMP_EXIT_WRAPPER
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
void
-WXMPMeta_DeleteQualifier_1 ( XMPMetaRef xmpRef,
+WXMPMeta_DeleteQualifier_1 ( XMPMetaRef xmpObjRef,
XMP_StringPtr schemaNS,
XMP_StringPtr propName,
XMP_StringPtr qualNS,
XMP_StringPtr qualName,
WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER ( "WXMPMeta_DeleteQualifier_1" )
+ XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_DeleteQualifier_1" )
if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
if ( (propName == 0) || (*propName == 0) ) XMP_Throw ( "Empty property name", kXMPErr_BadXPath );
if ( (qualNS == 0) || (*qualNS == 0) ) XMP_Throw ( "Empty qualifier namespace URI", kXMPErr_BadSchema );
if ( (qualName == 0) || (*qualName == 0) ) XMP_Throw ( "Empty qualifier name", kXMPErr_BadXPath );
- XMPMeta * meta = WtoXMPMeta_Ptr ( xmpRef );
- meta->DeleteQualifier ( schemaNS, propName, qualNS, qualName );
+ thiz->DeleteQualifier ( schemaNS, propName, qualNS, qualName );
- XMP_EXIT_WRAPPER
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
void
-WXMPMeta_DoesPropertyExist_1 ( XMPMetaRef xmpRef,
+WXMPMeta_DoesPropertyExist_1 ( XMPMetaRef xmpObjRef,
XMP_StringPtr schemaNS,
XMP_StringPtr propName,
WXMP_Result * wResult ) /* const */
{
- XMP_ENTER_WRAPPER ( "WXMPMeta_DoesPropertyExist_1" )
+ XMP_ENTER_ObjRead ( XMPMeta, "WXMPMeta_DoesPropertyExist_1" )
if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
if ( (propName == 0) || (*propName == 0) ) XMP_Throw ( "Empty property name", kXMPErr_BadXPath );
- const XMPMeta & meta = WtoXMPMeta_Ref ( xmpRef );
- bool found = meta.DoesPropertyExist ( schemaNS, propName );
+ bool found = thiz.DoesPropertyExist ( schemaNS, propName );
wResult->int32Result = found;
- XMP_EXIT_WRAPPER
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
void
-WXMPMeta_DoesArrayItemExist_1 ( XMPMetaRef xmpRef,
+WXMPMeta_DoesArrayItemExist_1 ( XMPMetaRef xmpObjRef,
XMP_StringPtr schemaNS,
XMP_StringPtr arrayName,
XMP_Index itemIndex,
WXMP_Result * wResult ) /* const */
{
- XMP_ENTER_WRAPPER ( "WXMPMeta_DoesArrayItemExist_1" )
+ XMP_ENTER_ObjRead ( XMPMeta, "WXMPMeta_DoesArrayItemExist_1" )
if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
if ( (arrayName == 0) || (*arrayName == 0) ) XMP_Throw ( "Empty array name", kXMPErr_BadXPath );
- const XMPMeta & meta = WtoXMPMeta_Ref ( xmpRef );
- bool found = meta.DoesArrayItemExist ( schemaNS, arrayName, itemIndex );
+ bool found = thiz.DoesArrayItemExist ( schemaNS, arrayName, itemIndex );
wResult->int32Result = found;
- XMP_EXIT_WRAPPER
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
void
-WXMPMeta_DoesStructFieldExist_1 ( XMPMetaRef xmpRef,
+WXMPMeta_DoesStructFieldExist_1 ( XMPMetaRef xmpObjRef,
XMP_StringPtr schemaNS,
XMP_StringPtr structName,
XMP_StringPtr fieldNS,
XMP_StringPtr fieldName,
WXMP_Result * wResult ) /* const */
{
- XMP_ENTER_WRAPPER ( "WXMPMeta_DoesStructFieldExist_1" )
+ XMP_ENTER_ObjRead ( XMPMeta, "WXMPMeta_DoesStructFieldExist_1" )
if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
if ( (structName == 0) || (*structName == 0) ) XMP_Throw ( "Empty struct name", kXMPErr_BadXPath );
if ( (fieldNS == 0) || (*fieldNS == 0) ) XMP_Throw ( "Empty field namespace URI", kXMPErr_BadSchema );
if ( (fieldName == 0) || (*fieldName == 0) ) XMP_Throw ( "Empty field name", kXMPErr_BadXPath );
- const XMPMeta & meta = WtoXMPMeta_Ref ( xmpRef );
- bool found = meta.DoesStructFieldExist ( schemaNS, structName, fieldNS, fieldName );
+ bool found = thiz.DoesStructFieldExist ( schemaNS, structName, fieldNS, fieldName );
wResult->int32Result = found;
- XMP_EXIT_WRAPPER
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
void
-WXMPMeta_DoesQualifierExist_1 ( XMPMetaRef xmpRef,
+WXMPMeta_DoesQualifierExist_1 ( XMPMetaRef xmpObjRef,
XMP_StringPtr schemaNS,
XMP_StringPtr propName,
XMP_StringPtr qualNS,
XMP_StringPtr qualName,
WXMP_Result * wResult ) /* const */
{
- XMP_ENTER_WRAPPER ( "WXMPMeta_DoesQualifierExist_1" )
+ XMP_ENTER_ObjRead ( XMPMeta, "WXMPMeta_DoesQualifierExist_1" )
if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
if ( (propName == 0) || (*propName == 0) ) XMP_Throw ( "Empty property name", kXMPErr_BadXPath );
if ( (qualNS == 0) || (*qualNS == 0) ) XMP_Throw ( "Empty qualifier namespace URI", kXMPErr_BadSchema );
if ( (qualName == 0) || (*qualName == 0) ) XMP_Throw ( "Empty qualifier name", kXMPErr_BadXPath );
- const XMPMeta & meta = WtoXMPMeta_Ref ( xmpRef );
- bool found = meta.DoesQualifierExist ( schemaNS, propName, qualNS, qualName );
+ bool found = thiz.DoesQualifierExist ( schemaNS, propName, qualNS, qualName );
wResult->int32Result = found;
- XMP_EXIT_WRAPPER
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
void
-WXMPMeta_GetLocalizedText_1 ( XMPMetaRef xmpRef,
+WXMPMeta_GetLocalizedText_1 ( XMPMetaRef xmpObjRef,
XMP_StringPtr schemaNS,
XMP_StringPtr arrayName,
XMP_StringPtr genericLang,
XMP_StringPtr specificLang,
- XMP_StringPtr * actualLang,
- XMP_StringLen * langSize,
- XMP_StringPtr * itemValue,
- XMP_StringLen * valueSize,
+ void * actualLang,
+ void * itemValue,
XMP_OptionBits * options,
+ SetClientStringProc SetClientString,
WXMP_Result * wResult ) /* const */
{
- XMP_ENTER_WRAPPER ( "WXMPMeta_GetLocalizedText_1" )
+ XMP_ENTER_ObjRead ( XMPMeta, "WXMPMeta_GetLocalizedText_1" )
if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
if ( (arrayName == 0) || (*arrayName == 0) ) XMP_Throw ( "Empty array name", kXMPErr_BadXPath );
if ( genericLang == 0 ) genericLang = "";
if ( (specificLang == 0) ||(*specificLang == 0) ) XMP_Throw ( "Empty specific language", kXMPErr_BadParam );
- if ( actualLang == 0 ) actualLang = &voidStringPtr;
- if ( langSize == 0 ) langSize = &voidStringLen;
- if ( itemValue == 0 ) itemValue = &voidStringPtr;
- if ( valueSize == 0 ) valueSize = &voidStringLen;
+ XMP_StringPtr langPtr = 0;
+ XMP_StringLen langSize = 0;
+ XMP_StringPtr valuePtr = 0;
+ XMP_StringLen valueSize = 0;
if ( options == 0 ) options = &voidOptionBits;
- const XMPMeta & meta = WtoXMPMeta_Ref ( xmpRef );
- bool found = meta.GetLocalizedText ( schemaNS, arrayName, genericLang, specificLang,
- actualLang, langSize, itemValue, valueSize, options );
+ bool found = thiz.GetLocalizedText ( schemaNS, arrayName, genericLang, specificLang,
+ &langPtr, &langSize, &valuePtr, &valueSize, options );
wResult->int32Result = found;
+
+ if ( found ) {
+ if ( actualLang != 0 ) (*SetClientString) ( actualLang, langPtr, langSize );
+ if ( itemValue != 0 ) (*SetClientString) ( itemValue, valuePtr, valueSize );
+ }
- XMP_EXIT_WRAPPER_KEEP_LOCK ( found )
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
void
-WXMPMeta_SetLocalizedText_1 ( XMPMetaRef xmpRef,
+WXMPMeta_SetLocalizedText_1 ( XMPMetaRef xmpObjRef,
XMP_StringPtr schemaNS,
XMP_StringPtr arrayName,
XMP_StringPtr genericLang,
@@ -851,7 +696,7 @@ WXMPMeta_SetLocalizedText_1 ( XMPMetaRef xmpRef,
XMP_OptionBits options,
WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER ( "WXMPMeta_SetLocalizedText_1" )
+ XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_SetLocalizedText_1" )
if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
if ( (arrayName == 0) || (*arrayName == 0) ) XMP_Throw ( "Empty array name", kXMPErr_BadXPath );
@@ -859,23 +704,43 @@ WXMPMeta_SetLocalizedText_1 ( XMPMetaRef xmpRef,
if ( (specificLang == 0) ||(*specificLang == 0) ) XMP_Throw ( "Empty specific language", kXMPErr_BadParam );
if ( itemValue == 0 ) itemValue = "";
- XMPMeta * meta = WtoXMPMeta_Ptr ( xmpRef );
- meta->SetLocalizedText ( schemaNS, arrayName, genericLang, specificLang, itemValue, options );
+ thiz->SetLocalizedText ( schemaNS, arrayName, genericLang, specificLang, itemValue, options );
+
+ XMP_EXIT
+}
+
+void
+WXMPMeta_DeleteLocalizedText_1 ( XMPMetaRef xmpObjRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_StringPtr genericLang,
+ XMP_StringPtr specificLang,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_DeleteLocalizedText_1" )
- XMP_EXIT_WRAPPER
+ if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
+ if ( (arrayName == 0) || (*arrayName == 0) ) XMP_Throw ( "Empty array name", kXMPErr_BadXPath );
+ if ( genericLang == 0 ) genericLang = "";
+ if ( (specificLang == 0) ||(*specificLang == 0) ) XMP_Throw ( "Empty specific language", kXMPErr_BadParam );
+
+ thiz->DeleteLocalizedText ( schemaNS, arrayName, genericLang, specificLang );
+
+ XMP_EXIT
}
+
// -------------------------------------------------------------------------------------------------
void
-WXMPMeta_GetProperty_Bool_1 ( XMPMetaRef xmpRef,
+WXMPMeta_GetProperty_Bool_1 ( XMPMetaRef xmpObjRef,
XMP_StringPtr schemaNS,
XMP_StringPtr propName,
XMP_Bool * propValue,
XMP_OptionBits * options,
WXMP_Result * wResult ) /* const */
{
- XMP_ENTER_WRAPPER ( "WXMPMeta_GetProperty_Bool_1" )
+ XMP_ENTER_ObjRead ( XMPMeta, "WXMPMeta_GetProperty_Bool_1" )
if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
if ( (propName == 0) || (*propName == 0) ) XMP_Throw ( "Empty property name", kXMPErr_BadXPath );
@@ -883,26 +748,25 @@ WXMPMeta_GetProperty_Bool_1 ( XMPMetaRef xmpRef,
if ( propValue == 0 ) propValue = &voidByte;
if ( options == 0 ) options = &voidOptionBits;
- const XMPMeta & meta = WtoXMPMeta_Ref ( xmpRef );
bool value;
- bool found = meta.GetProperty_Bool ( schemaNS, propName, &value, options );
+ bool found = thiz.GetProperty_Bool ( schemaNS, propName, &value, options );
if ( propValue != 0 ) *propValue = value;
wResult->int32Result = found;
- XMP_EXIT_WRAPPER
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
void
-WXMPMeta_GetProperty_Int_1 ( XMPMetaRef xmpRef,
+WXMPMeta_GetProperty_Int_1 ( XMPMetaRef xmpObjRef,
XMP_StringPtr schemaNS,
XMP_StringPtr propName,
XMP_Int32 * propValue,
XMP_OptionBits * options,
WXMP_Result * wResult ) /* const */
{
- XMP_ENTER_WRAPPER ( "WXMPMeta_GetProperty_Int_1" )
+ XMP_ENTER_ObjRead ( XMPMeta, "WXMPMeta_GetProperty_Int_1" )
if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
if ( (propName == 0) || (*propName == 0) ) XMP_Throw ( "Empty property name", kXMPErr_BadXPath );
@@ -910,24 +774,23 @@ WXMPMeta_GetProperty_Int_1 ( XMPMetaRef xmpRef,
if ( propValue == 0 ) propValue = &voidInt32;
if ( options == 0 ) options = &voidOptionBits;
- const XMPMeta & meta = WtoXMPMeta_Ref ( xmpRef );
- bool found = meta.GetProperty_Int ( schemaNS, propName, propValue, options );
+ bool found = thiz.GetProperty_Int ( schemaNS, propName, propValue, options );
wResult->int32Result = found;
- XMP_EXIT_WRAPPER
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
void
-WXMPMeta_GetProperty_Int64_1 ( XMPMetaRef xmpRef,
- XMP_StringPtr schemaNS,
- XMP_StringPtr propName,
- XMP_Int64 * propValue,
+WXMPMeta_GetProperty_Int64_1 ( XMPMetaRef xmpObjRef,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr propName,
+ XMP_Int64 * propValue,
XMP_OptionBits * options,
- WXMP_Result * wResult ) /* const */
+ WXMP_Result * wResult ) /* const */
{
- XMP_ENTER_WRAPPER ( "WXMPMeta_GetProperty_Int64_1" )
+ XMP_ENTER_ObjRead ( XMPMeta, "WXMPMeta_GetProperty_Int64_1" )
if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
if ( (propName == 0) || (*propName == 0) ) XMP_Throw ( "Empty property name", kXMPErr_BadXPath );
@@ -935,24 +798,23 @@ WXMPMeta_GetProperty_Int64_1 ( XMPMetaRef xmpRef,
if ( propValue == 0 ) propValue = &voidInt64;
if ( options == 0 ) options = &voidOptionBits;
- const XMPMeta & meta = WtoXMPMeta_Ref ( xmpRef );
- bool found = meta.GetProperty_Int64 ( schemaNS, propName, propValue, options );
+ bool found = thiz.GetProperty_Int64 ( schemaNS, propName, propValue, options );
wResult->int32Result = found;
- XMP_EXIT_WRAPPER
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
void
-WXMPMeta_GetProperty_Float_1 ( XMPMetaRef xmpRef,
+WXMPMeta_GetProperty_Float_1 ( XMPMetaRef xmpObjRef,
XMP_StringPtr schemaNS,
XMP_StringPtr propName,
double * propValue,
XMP_OptionBits * options,
WXMP_Result * wResult ) /* const */
{
- XMP_ENTER_WRAPPER ( "WXMPMeta_GetProperty_Float_1" )
+ XMP_ENTER_ObjRead ( XMPMeta, "WXMPMeta_GetProperty_Float_1" )
if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
if ( (propName == 0) || (*propName == 0) ) XMP_Throw ( "Empty property name", kXMPErr_BadXPath );
@@ -960,24 +822,23 @@ WXMPMeta_GetProperty_Float_1 ( XMPMetaRef xmpRef,
if ( propValue == 0 ) propValue = &voidDouble;
if ( options == 0 ) options = &voidOptionBits;
- const XMPMeta & meta = WtoXMPMeta_Ref ( xmpRef );
- bool found = meta.GetProperty_Float ( schemaNS, propName, propValue, options );
+ bool found = thiz.GetProperty_Float ( schemaNS, propName, propValue, options );
wResult->int32Result = found;
- XMP_EXIT_WRAPPER
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
void
-WXMPMeta_GetProperty_Date_1 ( XMPMetaRef xmpRef,
+WXMPMeta_GetProperty_Date_1 ( XMPMetaRef xmpObjRef,
XMP_StringPtr schemaNS,
XMP_StringPtr propName,
XMP_DateTime * propValue,
XMP_OptionBits * options,
WXMP_Result * wResult ) /* const */
{
- XMP_ENTER_WRAPPER ( "WXMPMeta_GetProperty_Date_1" )
+ XMP_ENTER_ObjRead ( XMPMeta, "WXMPMeta_GetProperty_Date_1" )
if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
if ( (propName == 0) || (*propName == 0) ) XMP_Throw ( "Empty property name", kXMPErr_BadXPath );
@@ -985,326 +846,295 @@ WXMPMeta_GetProperty_Date_1 ( XMPMetaRef xmpRef,
if ( propValue == 0 ) propValue = &voidDateTime;
if ( options == 0 ) options = &voidOptionBits;
- const XMPMeta & meta = WtoXMPMeta_Ref ( xmpRef );
- bool found = meta.GetProperty_Date ( schemaNS, propName, propValue, options );
+ bool found = thiz.GetProperty_Date ( schemaNS, propName, propValue, options );
wResult->int32Result = found;
- XMP_EXIT_WRAPPER
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
void
-WXMPMeta_SetProperty_Bool_1 ( XMPMetaRef xmpRef,
+WXMPMeta_SetProperty_Bool_1 ( XMPMetaRef xmpObjRef,
XMP_StringPtr schemaNS,
XMP_StringPtr propName,
XMP_Bool propValue,
XMP_OptionBits options,
WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER ( "WXMPMeta_SetProperty_Bool_1" )
+ XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_SetProperty_Bool_1" )
if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
if ( (propName == 0) || (*propName == 0) ) XMP_Throw ( "Empty property name", kXMPErr_BadXPath );
- XMPMeta * meta = WtoXMPMeta_Ptr ( xmpRef );
- meta->SetProperty_Bool ( schemaNS, propName, propValue, options );
+ thiz->SetProperty_Bool ( schemaNS, propName, propValue, options );
- XMP_EXIT_WRAPPER
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
void
-WXMPMeta_SetProperty_Int_1 ( XMPMetaRef xmpRef,
+WXMPMeta_SetProperty_Int_1 ( XMPMetaRef xmpObjRef,
XMP_StringPtr schemaNS,
XMP_StringPtr propName,
XMP_Int32 propValue,
XMP_OptionBits options,
WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER ( "WXMPMeta_SetProperty_Int_1" )
+ XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_SetProperty_Int_1" )
if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
if ( (propName == 0) || (*propName == 0) ) XMP_Throw ( "Empty property name", kXMPErr_BadXPath );
- XMPMeta * meta = WtoXMPMeta_Ptr ( xmpRef );
- meta->SetProperty_Int ( schemaNS, propName, propValue, options );
+ thiz->SetProperty_Int ( schemaNS, propName, propValue, options );
- XMP_EXIT_WRAPPER
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
void
-WXMPMeta_SetProperty_Int64_1 ( XMPMetaRef xmpRef,
+WXMPMeta_SetProperty_Int64_1 ( XMPMetaRef xmpObjRef,
XMP_StringPtr schemaNS,
XMP_StringPtr propName,
XMP_Int64 propValue,
XMP_OptionBits options,
WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER ( "WXMPMeta_SetProperty_Int64_1" )
+ XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_SetProperty_Int64_1" )
if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
if ( (propName == 0) || (*propName == 0) ) XMP_Throw ( "Empty property name", kXMPErr_BadXPath );
- XMPMeta * meta = WtoXMPMeta_Ptr ( xmpRef );
- meta->SetProperty_Int64 ( schemaNS, propName, propValue, options );
+ thiz->SetProperty_Int64 ( schemaNS, propName, propValue, options );
- XMP_EXIT_WRAPPER
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
void
-WXMPMeta_SetProperty_Float_1 ( XMPMetaRef xmpRef,
+WXMPMeta_SetProperty_Float_1 ( XMPMetaRef xmpObjRef,
XMP_StringPtr schemaNS,
XMP_StringPtr propName,
double propValue,
XMP_OptionBits options,
WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER ( "WXMPMeta_SetProperty_Float_1" )
+ XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_SetProperty_Float_1" )
if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
if ( (propName == 0) || (*propName == 0) ) XMP_Throw ( "Empty property name", kXMPErr_BadXPath );
- XMPMeta * meta = WtoXMPMeta_Ptr ( xmpRef );
- meta->SetProperty_Float ( schemaNS, propName, propValue, options );
+ thiz->SetProperty_Float ( schemaNS, propName, propValue, options );
- XMP_EXIT_WRAPPER
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
void
-WXMPMeta_SetProperty_Date_1 ( XMPMetaRef xmpRef,
+WXMPMeta_SetProperty_Date_1 ( XMPMetaRef xmpObjRef,
XMP_StringPtr schemaNS,
XMP_StringPtr propName,
const XMP_DateTime & propValue,
XMP_OptionBits options,
WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER ( "WXMPMeta_SetProperty_Date_1" )
+ XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_SetProperty_Date_1" )
if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
if ( (propName == 0) || (*propName == 0) ) XMP_Throw ( "Empty property name", kXMPErr_BadXPath );
- XMPMeta * meta = WtoXMPMeta_Ptr ( xmpRef );
- meta->SetProperty_Date ( schemaNS, propName, propValue, options );
+ thiz->SetProperty_Date ( schemaNS, propName, propValue, options );
- XMP_EXIT_WRAPPER
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
void
-WXMPMeta_DumpObject_1 ( XMPMetaRef xmpRef,
+WXMPMeta_DumpObject_1 ( XMPMetaRef xmpObjRef,
XMP_TextOutputProc outProc,
void * refCon,
WXMP_Result * wResult ) /* const */
{
- XMP_ENTER_WRAPPER ( "WXMPMeta_DumpObject_1" )
+ XMP_ENTER_ObjRead ( XMPMeta, "WXMPMeta_DumpObject_1" )
if ( outProc == 0 ) XMP_Throw ( "Null client output routine", kXMPErr_BadParam );
- const XMPMeta & meta = WtoXMPMeta_Ref ( xmpRef );
- XMP_Status status = meta.DumpObject ( outProc, refCon );
- wResult->int32Result = status;
+ thiz.DumpObject ( outProc, refCon );
+ wResult->int32Result = 0;
- XMP_EXIT_WRAPPER
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
void
-WXMPMeta_Sort_1 ( XMPMetaRef xmpRef,
+WXMPMeta_Sort_1 ( XMPMetaRef xmpObjRef,
WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER ( "WXMPMeta_Sort_1" )
+ XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_Sort_1" )
- XMPMeta * meta = WtoXMPMeta_Ptr ( xmpRef );
- meta->Sort();
+ thiz->Sort();
- XMP_EXIT_WRAPPER
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
void
-WXMPMeta_Erase_1 ( XMPMetaRef xmpRef,
+WXMPMeta_Erase_1 ( XMPMetaRef xmpObjRef,
WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER ( "WXMPMeta_Erase_1" )
+ XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_Erase_1" )
- XMPMeta * meta = WtoXMPMeta_Ptr ( xmpRef );
- meta->Erase();
+ thiz->Erase();
- XMP_EXIT_WRAPPER
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
void
-WXMPMeta_Clone_1 ( XMPMetaRef xmpRef,
+WXMPMeta_Clone_1 ( XMPMetaRef xmpObjRef,
XMP_OptionBits options,
WXMP_Result * wResult ) /* const */
{
- XMP_ENTER_WRAPPER ( "WXMPMeta_Clone_1" )
+ XMP_ENTER_ObjRead ( XMPMeta, "WXMPMeta_Clone_1" )
- const XMPMeta & xOriginal = WtoXMPMeta_Ref ( xmpRef );
- XMPMeta * xClone = new XMPMeta;
- xOriginal.Clone ( xClone, options );
+ XMPMeta * xClone = new XMPMeta; // ! Don't need an output lock, final ref assignment in client glue.
+ thiz.Clone ( xClone, options );
XMP_Assert ( xClone->clientRefs == 0 ); // ! Gets incremented in TXMPMeta::Clone.
wResult->ptrResult = xClone;
- XMP_EXIT_WRAPPER
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
void
-WXMPMeta_CountArrayItems_1 ( XMPMetaRef xmpRef,
+WXMPMeta_CountArrayItems_1 ( XMPMetaRef xmpObjRef,
XMP_StringPtr schemaNS,
XMP_StringPtr arrayName,
WXMP_Result * wResult ) /* const */
{
- XMP_ENTER_WRAPPER ( "WXMPMeta_CountArrayItems_1" )
+ XMP_ENTER_ObjRead ( XMPMeta, "WXMPMeta_CountArrayItems_1" )
if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
if ( (arrayName == 0) || (*arrayName == 0) ) XMP_Throw ( "Empty array name", kXMPErr_BadXPath );
- const XMPMeta & meta = WtoXMPMeta_Ref ( xmpRef );
- XMP_Index count = meta.CountArrayItems ( schemaNS, arrayName );
+ XMP_Index count = thiz.CountArrayItems ( schemaNS, arrayName );
wResult->int32Result = count;
- XMP_EXIT_WRAPPER
-}
-
-// -------------------------------------------------------------------------------------------------
-
-void
-WXMPMeta_UnlockObject_1 ( XMPMetaRef xmpRef,
- XMP_OptionBits options ) /* const */
-{
- WXMP_Result * wResult = &void_wResult; // ! Needed to "fool" the EnterWrapper macro.
- XMP_ENTER_WRAPPER_NO_LOCK ( "WXMPMeta_UnlockObject_1" )
-
- const XMPMeta & meta = WtoXMPMeta_Ref ( xmpRef );
- meta.UnlockObject ( options );
-
- XMP_EXIT_WRAPPER_NO_THROW
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
void
-WXMPMeta_GetObjectName_1 ( XMPMetaRef xmpRef,
- XMP_StringPtr * namePtr,
- XMP_StringLen * nameLen,
- WXMP_Result * wResult ) /* const */
+WXMPMeta_GetObjectName_1 ( XMPMetaRef xmpObjRef,
+ void * objName,
+ SetClientStringProc SetClientString,
+ WXMP_Result * wResult ) /* const */
{
- XMP_ENTER_WRAPPER ( "WXMPMeta_GetObjectName_1" )
+ XMP_ENTER_ObjRead ( XMPMeta, "WXMPMeta_GetObjectName_1" )
- if ( namePtr == 0 ) namePtr = &voidStringPtr;
- if ( nameLen == 0 ) nameLen = &voidStringLen;
+ XMP_StringPtr namePtr = 0;
+ XMP_StringLen nameSize = 0;
- const XMPMeta & meta = WtoXMPMeta_Ref ( xmpRef );
- meta.GetObjectName ( namePtr, nameLen );
+ thiz.GetObjectName ( &namePtr, &nameSize );
+ if ( objName != 0 ) (*SetClientString) ( objName, namePtr, nameSize );
- XMP_EXIT_WRAPPER_KEEP_LOCK ( true ) // ! Always keep the lock, a string is always returned!
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
void
-WXMPMeta_SetObjectName_1 ( XMPMetaRef xmpRef,
+WXMPMeta_SetObjectName_1 ( XMPMetaRef xmpObjRef,
XMP_StringPtr name,
WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER ( "WXMPMeta_SetObjectName_1" )
+ XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_SetObjectName_1" )
if ( name == 0 ) name = "";
- XMPMeta * meta = WtoXMPMeta_Ptr ( xmpRef );
- meta->SetObjectName ( name );
+ thiz->SetObjectName ( name );
- XMP_EXIT_WRAPPER
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
void
-WXMPMeta_GetObjectOptions_1 ( XMPMetaRef xmpRef,
+WXMPMeta_GetObjectOptions_1 ( XMPMetaRef xmpObjRef,
WXMP_Result * wResult ) /* const */
{
- XMP_ENTER_WRAPPER ( "WXMPMeta_GetObjectOptions_1" )
+ XMP_ENTER_ObjRead ( XMPMeta, "WXMPMeta_GetObjectOptions_1" )
- const XMPMeta & meta = WtoXMPMeta_Ref ( xmpRef );
- XMP_OptionBits options = meta.GetObjectOptions();
+ XMP_OptionBits options = thiz.GetObjectOptions();
wResult->int32Result = options;
- XMP_EXIT_WRAPPER
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
void
-WXMPMeta_SetObjectOptions_1 ( XMPMetaRef xmpRef,
+WXMPMeta_SetObjectOptions_1 ( XMPMetaRef xmpObjRef,
XMP_OptionBits options,
WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER ( "WXMPMeta_SetObjectOptions_1" )
+ XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_SetObjectOptions_1" )
- XMPMeta * meta = WtoXMPMeta_Ptr ( xmpRef );
- meta->SetObjectOptions ( options );
+ thiz->SetObjectOptions ( options );
- XMP_EXIT_WRAPPER
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
void
-WXMPMeta_ParseFromBuffer_1 ( XMPMetaRef xmpRef,
+WXMPMeta_ParseFromBuffer_1 ( XMPMetaRef xmpObjRef,
XMP_StringPtr buffer,
XMP_StringLen bufferSize,
XMP_OptionBits options,
WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER ( "WXMPMeta_ParseFromBuffer_1" )
+ XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_ParseFromBuffer_1" )
- XMPMeta * meta = WtoXMPMeta_Ptr ( xmpRef );
- meta->ParseFromBuffer ( buffer, bufferSize, options );
+ thiz->ParseFromBuffer ( buffer, bufferSize, options );
- XMP_EXIT_WRAPPER
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
void
-WXMPMeta_SerializeToBuffer_1 ( XMPMetaRef xmpRef,
- XMP_StringPtr * rdfString,
- XMP_StringLen * rdfSize,
- XMP_OptionBits options,
- XMP_StringLen padding,
- XMP_StringPtr newline,
- XMP_StringPtr indent,
- XMP_Index baseIndent,
- WXMP_Result * wResult ) /* const */
+WXMPMeta_SerializeToBuffer_1 ( XMPMetaRef xmpObjRef,
+ void * pktString,
+ XMP_OptionBits options,
+ XMP_StringLen padding,
+ XMP_StringPtr newline,
+ XMP_StringPtr indent,
+ XMP_Index baseIndent,
+ SetClientStringProc SetClientString,
+ WXMP_Result * wResult ) /* const */
{
- XMP_ENTER_WRAPPER ( "WXMPMeta_SerializeToBuffer_1" )
+ XMP_ENTER_ObjRead ( XMPMeta, "WXMPMeta_SerializeToBuffer_1" )
- if ( rdfString == 0 ) rdfString = &voidStringPtr;
- if ( rdfSize == 0 ) rdfSize = &voidStringLen;
+ XMP_VarString localStr;
if ( newline == 0 ) newline = "";
if ( indent == 0 ) indent = "";
- const XMPMeta & meta = WtoXMPMeta_Ref ( xmpRef );
- meta.SerializeToBuffer ( rdfString, rdfSize, options, padding, newline, indent, baseIndent );
+ thiz.SerializeToBuffer ( &localStr, options, padding, newline, indent, baseIndent );
+ if ( pktString != 0 ) (*SetClientString) ( pktString, localStr.c_str(), localStr.size() );
- XMP_EXIT_WRAPPER_KEEP_LOCK ( true ) // ! Always keep the lock, a string is always returned!
+ XMP_EXIT
}
// =================================================================================================
diff --git a/source/XMPCore/WXMPUtils.cpp b/source/XMPCore/WXMPUtils.cpp
index dac093a..b8a96ff 100644
--- a/source/XMPCore/WXMPUtils.cpp
+++ b/source/XMPCore/WXMPUtils.cpp
@@ -1,5 +1,5 @@
// =================================================================================================
-// Copyright 2002-2008 Adobe Systems Incorporated
+// Copyright 2004 Adobe Systems Incorporated
// All Rights Reserved.
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -9,11 +9,13 @@
// *** Should change "type * inParam" to "type & inParam"
#include "XMP_Environment.h" // ! This must be the first include!
-#include "XMPCore_Impl.hpp"
+#include "XMP_Const.h"
-#include "XMPUtils.hpp"
#include "client-glue/WXMPUtils.hpp"
+#include "XMPCore_Impl.hpp"
+#include "XMPUtils.hpp"
+
#if XMP_WinBuild
#pragma warning ( disable : 4101 ) // unreferenced local variable
#pragma warning ( disable : 4189 ) // local variable is initialized but not referenced
@@ -32,37 +34,24 @@ extern "C" {
// =====================
void
-WXMPUtils_Unlock_1 ( XMP_OptionBits options )
-{
- WXMP_Result * wResult = &void_wResult; // ! Needed to "fool" the EnterWrapper macro.
- XMP_ENTER_WRAPPER_NO_LOCK ( "WXMPUtils_Unlock_1" )
-
- XMPUtils::Unlock ( options );
-
- XMP_EXIT_WRAPPER_NO_THROW
-}
-
-// =================================================================================================
-
-void
WXMPUtils_ComposeArrayItemPath_1 ( XMP_StringPtr schemaNS,
XMP_StringPtr arrayName,
XMP_Index itemIndex,
- XMP_StringPtr * fullPath,
- XMP_StringLen * pathSize,
+ void * itemPath,
+ SetClientStringProc SetClientString,
WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER ( "WXMPUtils_ComposeArrayItemPath_1" )
-
+ XMP_ENTER_Static ( "WXMPUtils_ComposeArrayItemPath_1" )
+
if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
if ( (arrayName == 0) || (*arrayName == 0) ) XMP_Throw ( "Empty array name", kXMPErr_BadXPath );
-
- if ( fullPath == 0 ) fullPath = &voidStringPtr;
- if ( pathSize == 0 ) pathSize = &voidStringLen;
- XMPUtils::ComposeArrayItemPath ( schemaNS, arrayName, itemIndex, fullPath, pathSize );
+ XMP_VarString localStr;
- XMP_EXIT_WRAPPER_KEEP_LOCK ( true )
+ XMPUtils::ComposeArrayItemPath ( schemaNS, arrayName, itemIndex, &localStr );
+ if ( itemPath != 0 ) (*SetClientString) ( itemPath, localStr.c_str(), localStr.size() );
+
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
@@ -72,23 +61,23 @@ WXMPUtils_ComposeStructFieldPath_1 ( XMP_StringPtr schemaNS,
XMP_StringPtr structName,
XMP_StringPtr fieldNS,
XMP_StringPtr fieldName,
- XMP_StringPtr * fullPath,
- XMP_StringLen * pathSize,
+ void * fieldPath,
+ SetClientStringProc SetClientString,
WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER ( "WXMPUtils_ComposeStructFieldPath_1" )
+ XMP_ENTER_Static ( "WXMPUtils_ComposeStructFieldPath_1" )
if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
if ( (structName == 0) || (*structName == 0) ) XMP_Throw ( "Empty struct name", kXMPErr_BadXPath );
if ( (fieldNS == 0) || (*fieldNS == 0) ) XMP_Throw ( "Empty field namespace URI", kXMPErr_BadSchema );
if ( (fieldName == 0) || (*fieldName == 0) ) XMP_Throw ( "Empty field name", kXMPErr_BadXPath );
-
- if ( fullPath == 0 ) fullPath = &voidStringPtr;
- if ( pathSize == 0 ) pathSize = &voidStringLen;
- XMPUtils::ComposeStructFieldPath ( schemaNS, structName, fieldNS, fieldName, fullPath, pathSize );
+ XMP_VarString localStr;
+
+ XMPUtils::ComposeStructFieldPath ( schemaNS, structName, fieldNS, fieldName, &localStr );
+ if ( fieldPath != 0 ) (*SetClientString) ( fieldPath, localStr.c_str(), localStr.size() );
- XMP_EXIT_WRAPPER_KEEP_LOCK ( true )
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
@@ -98,23 +87,23 @@ WXMPUtils_ComposeQualifierPath_1 ( XMP_StringPtr schemaNS,
XMP_StringPtr propName,
XMP_StringPtr qualNS,
XMP_StringPtr qualName,
- XMP_StringPtr * fullPath,
- XMP_StringLen * pathSize,
+ void * qualPath,
+ SetClientStringProc SetClientString,
WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER ( "WXMPUtils_ComposeQualifierPath_1" )
-
+ XMP_ENTER_Static ( "WXMPUtils_ComposeQualifierPath_1" )
+
if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
if ( (propName == 0) || (*propName == 0) ) XMP_Throw ( "Empty property name", kXMPErr_BadXPath );
if ( (qualNS == 0) || (*qualNS == 0) ) XMP_Throw ( "Empty qualifier namespace URI", kXMPErr_BadSchema );
if ( (qualName == 0) || (*qualName == 0) ) XMP_Throw ( "Empty qualifier name", kXMPErr_BadXPath );
-
- if ( fullPath == 0 ) fullPath = &voidStringPtr;
- if ( pathSize == 0 ) pathSize = &voidStringLen;
- XMPUtils::ComposeQualifierPath ( schemaNS, propName, qualNS, qualName, fullPath, pathSize );
+ XMP_VarString localStr;
+
+ XMPUtils::ComposeQualifierPath ( schemaNS, propName, qualNS, qualName, &localStr );
+ if ( qualPath != 0 ) (*SetClientString) ( qualPath, localStr.c_str(), localStr.size() );
- XMP_EXIT_WRAPPER_KEEP_LOCK ( true )
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
@@ -123,22 +112,22 @@ void
WXMPUtils_ComposeLangSelector_1 ( XMP_StringPtr schemaNS,
XMP_StringPtr arrayName,
XMP_StringPtr langName,
- XMP_StringPtr * fullPath,
- XMP_StringLen * pathSize,
+ void * selPath,
+ SetClientStringProc SetClientString,
WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER ( "WXMPUtils_ComposeLangSelector_1" )
-
+ XMP_ENTER_Static ( "WXMPUtils_ComposeLangSelector_1" )
+
if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
if ( (arrayName == 0) || (*arrayName == 0) ) XMP_Throw ( "Empty array name", kXMPErr_BadXPath );
if ( (langName == 0) || (*langName == 0) ) XMP_Throw ( "Empty language name", kXMPErr_BadParam );
-
- if ( fullPath == 0 ) fullPath = &voidStringPtr;
- if ( pathSize == 0 ) pathSize = &voidStringLen;
- XMPUtils::ComposeLangSelector ( schemaNS, arrayName, langName, fullPath, pathSize );
+ XMP_VarString localStr;
+
+ XMPUtils::ComposeLangSelector ( schemaNS, arrayName, langName, &localStr );
+ if ( selPath != 0 ) (*SetClientString) ( selPath, localStr.c_str(), localStr.size() );
- XMP_EXIT_WRAPPER_KEEP_LOCK ( true )
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
@@ -149,123 +138,123 @@ WXMPUtils_ComposeFieldSelector_1 ( XMP_StringPtr schemaNS,
XMP_StringPtr fieldNS,
XMP_StringPtr fieldName,
XMP_StringPtr fieldValue,
- XMP_StringPtr * fullPath,
- XMP_StringLen * pathSize,
+ void * selPath,
+ SetClientStringProc SetClientString,
WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER ( "WXMPUtils_ComposeFieldSelector_1" )
-
+ XMP_ENTER_Static ( "WXMPUtils_ComposeFieldSelector_1" )
+
if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
if ( (arrayName == 0) || (*arrayName == 0) ) XMP_Throw ( "Empty array name", kXMPErr_BadXPath );
if ( (fieldNS == 0) || (*fieldNS == 0) ) XMP_Throw ( "Empty field namespace URI", kXMPErr_BadSchema );
if ( (fieldName == 0) || (*fieldName == 0) ) XMP_Throw ( "Empty field name", kXMPErr_BadXPath );
if ( fieldValue == 0 ) fieldValue = "";
-
- if ( fullPath == 0 ) fullPath = &voidStringPtr;
- if ( pathSize == 0 ) pathSize = &voidStringLen;
- XMPUtils::ComposeFieldSelector ( schemaNS, arrayName, fieldNS, fieldName, fieldValue, fullPath, pathSize );
+ XMP_VarString localStr;
- XMP_EXIT_WRAPPER_KEEP_LOCK ( true )
+ XMPUtils::ComposeFieldSelector ( schemaNS, arrayName, fieldNS, fieldName, fieldValue, &localStr );
+ if ( selPath != 0 ) (*SetClientString) ( selPath, localStr.c_str(), localStr.size() );
+
+ XMP_EXIT
}
// =================================================================================================
void
-WXMPUtils_ConvertFromBool_1 ( XMP_Bool binValue,
- XMP_StringPtr * strValue,
- XMP_StringLen * strSize,
- WXMP_Result * wResult )
+WXMPUtils_ConvertFromBool_1 ( XMP_Bool binValue,
+ void * strValue,
+ SetClientStringProc SetClientString,
+ WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER ( "WXMPUtils_ConvertFromBool_1" )
+ XMP_ENTER_Static ( "WXMPUtils_ConvertFromBool_1" )
- if ( strValue == 0 ) strValue = &voidStringPtr;
- if ( strSize == 0 ) strSize = &voidStringLen;
+ XMP_VarString localStr;
- XMPUtils::ConvertFromBool ( binValue, strValue, strSize );
+ XMPUtils::ConvertFromBool ( binValue, &localStr );
+ if ( strValue != 0 ) (*SetClientString) ( strValue, localStr.c_str(), localStr.size() );
- XMP_EXIT_WRAPPER_KEEP_LOCK ( true )
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
void
-WXMPUtils_ConvertFromInt_1 ( XMP_Int32 binValue,
- XMP_StringPtr format,
- XMP_StringPtr * strValue,
- XMP_StringLen * strSize,
- WXMP_Result * wResult )
+WXMPUtils_ConvertFromInt_1 ( XMP_Int32 binValue,
+ XMP_StringPtr format,
+ void * strValue,
+ SetClientStringProc SetClientString,
+ WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER ( "WXMPUtils_ConvertFromInt_1" )
+ XMP_ENTER_Static ( "WXMPUtils_ConvertFromInt_1" )
if ( format == 0 ) format = "";
-
- if ( strValue == 0 ) strValue = &voidStringPtr;
- if ( strSize == 0 ) strSize = &voidStringLen;
- XMPUtils::ConvertFromInt ( binValue, format, strValue, strSize );
+ XMP_VarString localStr;
- XMP_EXIT_WRAPPER_KEEP_LOCK ( true )
+ XMPUtils::ConvertFromInt ( binValue, format, &localStr );
+ if ( strValue != 0 ) (*SetClientString) ( strValue, localStr.c_str(), localStr.size() );
+
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
void
-WXMPUtils_ConvertFromInt64_1 ( XMP_Int64 binValue,
- XMP_StringPtr format,
- XMP_StringPtr * strValue,
- XMP_StringLen * strSize,
- WXMP_Result * wResult )
+WXMPUtils_ConvertFromInt64_1 ( XMP_Int64 binValue,
+ XMP_StringPtr format,
+ void * strValue,
+ SetClientStringProc SetClientString,
+ WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER ( "WXMPUtils_ConvertFromInt64_1" )
+ XMP_ENTER_Static ( "WXMPUtils_ConvertFromInt64_1" )
if ( format == 0 ) format = "";
-
- if ( strValue == 0 ) strValue = &voidStringPtr;
- if ( strSize == 0 ) strSize = &voidStringLen;
- XMPUtils::ConvertFromInt64 ( binValue, format, strValue, strSize );
+ XMP_VarString localStr;
+
+ XMPUtils::ConvertFromInt64 ( binValue, format, &localStr );
+ if ( strValue != 0 ) (*SetClientString) ( strValue, localStr.c_str(), localStr.size() );
- XMP_EXIT_WRAPPER_KEEP_LOCK ( true )
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
void
-WXMPUtils_ConvertFromFloat_1 ( double binValue,
- XMP_StringPtr format,
- XMP_StringPtr * strValue,
- XMP_StringLen * strSize,
- WXMP_Result * wResult )
+WXMPUtils_ConvertFromFloat_1 ( double binValue,
+ XMP_StringPtr format,
+ void * strValue,
+ SetClientStringProc SetClientString,
+ WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER ( "WXMPUtils_ConvertFromFloat_1" )
+ XMP_ENTER_Static ( "WXMPUtils_ConvertFromFloat_1" )
if ( format == 0 ) format = "";
-
- if ( strValue == 0 ) strValue = &voidStringPtr;
- if ( strSize == 0 ) strSize = &voidStringLen;
- XMPUtils::ConvertFromFloat ( binValue, format, strValue, strSize );
+ XMP_VarString localStr;
- XMP_EXIT_WRAPPER_KEEP_LOCK ( true )
+ XMPUtils::ConvertFromFloat ( binValue, format, &localStr );
+ if ( strValue != 0 ) (*SetClientString) ( strValue, localStr.c_str(), localStr.size() );
+
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
void
WXMPUtils_ConvertFromDate_1 ( const XMP_DateTime & binValue,
- XMP_StringPtr * strValue,
- XMP_StringLen * strSize,
+ void * strValue,
+ SetClientStringProc SetClientString,
WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER ( "WXMPUtils_ConvertFromDate_1" )
+ XMP_ENTER_Static ( "WXMPUtils_ConvertFromDate_1" )
- if ( strValue == 0 ) strValue = &voidStringPtr;
- if ( strSize == 0 ) strSize = &voidStringLen;
+ XMP_VarString localStr;
- XMPUtils::ConvertFromDate( binValue, strValue, strSize );
+ XMPUtils::ConvertFromDate( binValue, &localStr );
+ if ( strValue != 0 ) (*SetClientString) ( strValue, localStr.c_str(), localStr.size() );
- XMP_EXIT_WRAPPER_KEEP_LOCK ( true )
+ XMP_EXIT
}
// =================================================================================================
@@ -274,13 +263,13 @@ void
WXMPUtils_ConvertToBool_1 ( XMP_StringPtr strValue,
WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER_NO_LOCK ( "WXMPUtils_ConvertToBool_1" )
+ XMP_ENTER_Static ( "WXMPUtils_ConvertToBool_1" )
if ( (strValue == 0) || (*strValue == 0) ) XMP_Throw ( "Empty string value", kXMPErr_BadParam);
XMP_Bool result = XMPUtils::ConvertToBool ( strValue );
wResult->int32Result = result;
- XMP_EXIT_WRAPPER
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
@@ -289,13 +278,13 @@ void
WXMPUtils_ConvertToInt_1 ( XMP_StringPtr strValue,
WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER_NO_LOCK ( "WXMPUtils_ConvertToInt_1" )
+ XMP_ENTER_Static ( "WXMPUtils_ConvertToInt_1" )
if ( (strValue == 0) || (*strValue == 0) ) XMP_Throw ( "Empty string value", kXMPErr_BadParam);
XMP_Int32 result = XMPUtils::ConvertToInt ( strValue );
wResult->int32Result = result;
- XMP_EXIT_WRAPPER
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
@@ -304,13 +293,13 @@ void
WXMPUtils_ConvertToInt64_1 ( XMP_StringPtr strValue,
WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER_NO_LOCK ( "WXMPUtils_ConvertToInt64_1" )
+ XMP_ENTER_Static ( "WXMPUtils_ConvertToInt64_1" )
if ( (strValue == 0) || (*strValue == 0) ) XMP_Throw ( "Empty string value", kXMPErr_BadParam);
XMP_Int64 result = XMPUtils::ConvertToInt64 ( strValue );
wResult->int64Result = result;
- XMP_EXIT_WRAPPER
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
@@ -319,13 +308,13 @@ void
WXMPUtils_ConvertToFloat_1 ( XMP_StringPtr strValue,
WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER_NO_LOCK ( "WXMPUtils_ConvertToFloat_1")
+ XMP_ENTER_Static ( "WXMPUtils_ConvertToFloat_1")
if ( (strValue == 0) || (*strValue == 0) ) XMP_Throw ( "Empty string value", kXMPErr_BadParam);
double result = XMPUtils::ConvertToFloat ( strValue );
wResult->floatResult = result;
- XMP_EXIT_WRAPPER
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
@@ -335,12 +324,12 @@ WXMPUtils_ConvertToDate_1 ( XMP_StringPtr strValue,
XMP_DateTime * binValue,
WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER_NO_LOCK ( "WXMPUtils_ConvertToDate_1" )
+ XMP_ENTER_Static ( "WXMPUtils_ConvertToDate_1" )
if ( binValue == 0 ) XMP_Throw ( "Null output date", kXMPErr_BadParam); // ! Pointer is from the client.
XMPUtils::ConvertToDate ( strValue, binValue );
- XMP_EXIT_WRAPPER
+ XMP_EXIT
}
// =================================================================================================
@@ -349,12 +338,12 @@ void
WXMPUtils_CurrentDateTime_1 ( XMP_DateTime * time,
WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER_NO_LOCK ( "WXMPUtils_CurrentDateTime_1" )
-
+ XMP_ENTER_Static ( "WXMPUtils_CurrentDateTime_1" )
+
if ( time == 0 ) XMP_Throw ( "Null output date", kXMPErr_BadParam);
XMPUtils::CurrentDateTime ( time );
- XMP_EXIT_WRAPPER
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
@@ -363,12 +352,12 @@ void
WXMPUtils_SetTimeZone_1 ( XMP_DateTime * time,
WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER_NO_LOCK ( "WXMPUtils_SetTimeZone_1" )
+ XMP_ENTER_Static ( "WXMPUtils_SetTimeZone_1" )
if ( time == 0 ) XMP_Throw ( "Null output date", kXMPErr_BadParam);
XMPUtils::SetTimeZone ( time );
- XMP_EXIT_WRAPPER
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
@@ -377,12 +366,12 @@ void
WXMPUtils_ConvertToUTCTime_1 ( XMP_DateTime * time,
WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER_NO_LOCK ( "WXMPUtils_ConvertToUTCTime_1" )
+ XMP_ENTER_Static ( "WXMPUtils_ConvertToUTCTime_1" )
if ( time == 0 ) XMP_Throw ( "Null output date", kXMPErr_BadParam);
XMPUtils::ConvertToUTCTime ( time );
- XMP_EXIT_WRAPPER
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
@@ -391,12 +380,12 @@ void
WXMPUtils_ConvertToLocalTime_1 ( XMP_DateTime * time,
WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER_NO_LOCK ( "WXMPUtils_ConvertToLocalTime_1" )
+ XMP_ENTER_Static ( "WXMPUtils_ConvertToLocalTime_1" )
if ( time == 0 ) XMP_Throw ( "Null output date", kXMPErr_BadParam);
XMPUtils::ConvertToLocalTime ( time );
- XMP_EXIT_WRAPPER
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
@@ -406,77 +395,77 @@ WXMPUtils_CompareDateTime_1 ( const XMP_DateTime & left,
const XMP_DateTime & right,
WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER_NO_LOCK ( "WXMPUtils_CompareDateTime_1" )
+ XMP_ENTER_Static ( "WXMPUtils_CompareDateTime_1" )
int result = XMPUtils::CompareDateTime ( left, right );
wResult->int32Result = result;
- XMP_EXIT_WRAPPER
+ XMP_EXIT
}
// =================================================================================================
void
-WXMPUtils_EncodeToBase64_1 ( XMP_StringPtr rawStr,
- XMP_StringLen rawLen,
- XMP_StringPtr * encodedStr,
- XMP_StringLen * encodedLen,
- WXMP_Result * wResult )
+WXMPUtils_EncodeToBase64_1 ( XMP_StringPtr rawStr,
+ XMP_StringLen rawLen,
+ void * encodedStr,
+ SetClientStringProc SetClientString,
+ WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER ( "WXMPUtils_EncodeToBase64_1" )
-
- if ( encodedStr == 0 ) encodedStr = &voidStringPtr;
- if ( encodedLen == 0 ) encodedLen = &voidStringLen;
+ XMP_ENTER_Static ( "WXMPUtils_EncodeToBase64_1" )
+
+ XMP_VarString localStr;
- XMPUtils::EncodeToBase64 ( rawStr, rawLen, encodedStr, encodedLen );
+ XMPUtils::EncodeToBase64 ( rawStr, rawLen, &localStr );
+ if ( encodedStr != 0 ) (*SetClientString) ( encodedStr, localStr.c_str(), localStr.size() );
- XMP_EXIT_WRAPPER_KEEP_LOCK ( true )
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
void
-WXMPUtils_DecodeFromBase64_1 ( XMP_StringPtr encodedStr,
- XMP_StringLen encodedLen,
- XMP_StringPtr * rawStr,
- XMP_StringLen * rawLen,
- WXMP_Result * wResult )
+WXMPUtils_DecodeFromBase64_1 ( XMP_StringPtr encodedStr,
+ XMP_StringLen encodedLen,
+ void * rawStr,
+ SetClientStringProc SetClientString,
+ WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER ( "WXMPUtils_DecodeFromBase64_1" )
+ XMP_ENTER_Static ( "WXMPUtils_DecodeFromBase64_1" )
- if ( rawStr == 0 ) rawStr = &voidStringPtr;
- if ( rawLen == 0 ) rawLen = &voidStringLen;
+ XMP_VarString localStr;
- XMPUtils::DecodeFromBase64 ( encodedStr, encodedLen, rawStr, rawLen );
+ XMPUtils::DecodeFromBase64 ( encodedStr, encodedLen, &localStr );
+ if ( rawStr != 0 ) (*SetClientString) ( rawStr, localStr.c_str(), localStr.size() );
- XMP_EXIT_WRAPPER_KEEP_LOCK ( true )
+ XMP_EXIT
}
// =================================================================================================
void
-WXMPUtils_PackageForJPEG_1 ( XMPMetaRef wxmpObj,
- XMP_StringPtr * stdStr,
- XMP_StringLen * stdLen,
- XMP_StringPtr * extStr,
- XMP_StringLen * extLen,
- XMP_StringPtr * digestStr,
- XMP_StringLen * digestLen,
- WXMP_Result * wResult )
+WXMPUtils_PackageForJPEG_1 ( XMPMetaRef wxmpObj,
+ void * stdStr,
+ void * extStr,
+ void * digestStr,
+ SetClientStringProc SetClientString,
+ WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER ( "WXMPUtils_PackageForJPEG_1" )
+ XMP_ENTER_Static ( "WXMPUtils_PackageForJPEG_1" )
- if ( stdStr == 0 ) stdStr = &voidStringPtr;
- if ( stdLen == 0 ) stdLen = &voidStringLen;
- if ( extStr == 0 ) extStr = &voidStringPtr;
- if ( extLen == 0 ) extLen = &voidStringLen;
- if ( digestStr == 0 ) digestStr = &voidStringPtr;
- if ( digestLen == 0 ) digestLen = &voidStringLen;
+ XMP_VarString localStdStr;
+ XMP_VarString localExtStr;
+ XMP_VarString localDigestStr;
const XMPMeta & xmpObj = WtoXMPMeta_Ref ( wxmpObj );
- XMPUtils::PackageForJPEG ( xmpObj, stdStr, stdLen, extStr, extLen, digestStr, digestLen );
+ XMP_AutoLock metaLock ( &xmpObj.lock, kXMP_ReadLock );
- XMP_EXIT_WRAPPER_KEEP_LOCK ( true )
+ XMPUtils::PackageForJPEG ( xmpObj, &localStdStr, &localExtStr, &localDigestStr );
+ if ( stdStr != 0 ) (*SetClientString) ( stdStr, localStdStr.c_str(), localStdStr.size() );
+ if ( extStr != 0 ) (*SetClientString) ( extStr, localExtStr.c_str(), localExtStr.size() );
+ if ( digestStr != 0 ) (*SetClientString) ( digestStr, localDigestStr.c_str(), localDigestStr.size() );
+
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
@@ -486,45 +475,52 @@ WXMPUtils_MergeFromJPEG_1 ( XMPMetaRef wfullXMP,
XMPMetaRef wextendedXMP,
WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER ( "WXMPUtils_MergeFromJPEG_1" )
+ XMP_ENTER_Static ( "WXMPUtils_MergeFromJPEG_1" )
if ( wfullXMP == 0 ) XMP_Throw ( "Output XMP pointer is null", kXMPErr_BadParam );
+ if ( wfullXMP == wextendedXMP ) XMP_Throw ( "Full and extended XMP pointers match", kXMPErr_BadParam );
XMPMeta * fullXMP = WtoXMPMeta_Ptr ( wfullXMP );
+ XMP_AutoLock fullXMPLock ( &fullXMP->lock, kXMP_WriteLock );
+
const XMPMeta & extendedXMP = WtoXMPMeta_Ref ( wextendedXMP );
+ XMP_AutoLock extendedXMPLock ( &extendedXMP.lock, kXMP_ReadLock );
+
XMPUtils::MergeFromJPEG ( fullXMP, extendedXMP );
- XMP_EXIT_WRAPPER
+ XMP_EXIT
}
// =================================================================================================
void
-WXMPUtils_CatenateArrayItems_1 ( XMPMetaRef wxmpObj,
- XMP_StringPtr schemaNS,
- XMP_StringPtr arrayName,
- XMP_StringPtr separator,
- XMP_StringPtr quotes,
- XMP_OptionBits options,
- XMP_StringPtr * catedStr,
- XMP_StringLen * catedLen,
- WXMP_Result * wResult )
+WXMPUtils_CatenateArrayItems_1 ( XMPMetaRef wxmpObj,
+ XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_StringPtr separator,
+ XMP_StringPtr quotes,
+ XMP_OptionBits options,
+ void * catedStr,
+ SetClientStringProc SetClientString,
+ WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER ( "WXMPUtils_CatenateArrayItems_1" )
+ XMP_ENTER_Static ( "WXMPUtils_CatenateArrayItems_1" )
if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
if ( (arrayName == 0) || (*arrayName == 0) ) XMP_Throw ( "Empty array name", kXMPErr_BadXPath );
-
+
if ( separator == 0 ) separator = "; ";
if ( quotes == 0 ) quotes = "\"";
-
- if ( catedStr == 0 ) catedStr = &voidStringPtr;
- if ( catedLen == 0 ) catedLen = &voidStringLen;
+
+ XMP_VarString localStr;
const XMPMeta & xmpObj = WtoXMPMeta_Ref ( wxmpObj );
- XMPUtils::CatenateArrayItems ( xmpObj, schemaNS, arrayName, separator, quotes, options, catedStr, catedLen );
+ XMP_AutoLock metaLock ( &xmpObj.lock, kXMP_ReadLock );
+
+ XMPUtils::CatenateArrayItems ( xmpObj, schemaNS, arrayName, separator, quotes, options, &localStr );
+ if ( catedStr != 0 ) (*SetClientString) ( catedStr, localStr.c_str(), localStr.size() );
- XMP_EXIT_WRAPPER_KEEP_LOCK ( true )
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
@@ -537,17 +533,42 @@ WXMPUtils_SeparateArrayItems_1 ( XMPMetaRef wxmpObj,
XMP_StringPtr catedStr,
WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER ( "WXMPUtils_SeparateArrayItems_1" )
+ XMP_ENTER_Static ( "WXMPUtils_SeparateArrayItems_1" )
if ( wxmpObj == 0 ) XMP_Throw ( "Output XMP pointer is null", kXMPErr_BadParam );
if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema );
if ( (arrayName == 0) || (*arrayName == 0) ) XMP_Throw ( "Empty array name", kXMPErr_BadXPath );
if ( catedStr == 0 ) catedStr = "";
-
+
XMPMeta * xmpObj = WtoXMPMeta_Ptr ( wxmpObj );
+ XMP_AutoLock metaLock ( &xmpObj->lock, kXMP_WriteLock );
+
XMPUtils::SeparateArrayItems ( xmpObj, schemaNS, arrayName, options, catedStr );
- XMP_EXIT_WRAPPER
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPUtils_ApplyTemplate_1 ( XMPMetaRef wWorkingXMP,
+ XMPMetaRef wTemplateXMP,
+ XMP_OptionBits actions,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_Static ( "WXMPUtils_ApplyTemplate_1" )
+
+ XMP_Assert ( (wWorkingXMP != 0) && (wTemplateXMP != 0) ); // Client glue enforced.
+
+ XMPMeta * workingXMP = WtoXMPMeta_Ptr ( wWorkingXMP );
+ XMP_AutoLock workingLock ( &workingXMP->lock, kXMP_WriteLock );
+
+ const XMPMeta & templateXMP = WtoXMPMeta_Ref ( wTemplateXMP );
+ XMP_AutoLock templateLock ( &templateXMP.lock, kXMP_ReadLock );
+
+ XMPUtils::ApplyTemplate ( workingXMP, templateXMP, actions );
+
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
@@ -559,37 +580,22 @@ WXMPUtils_RemoveProperties_1 ( XMPMetaRef wxmpObj,
XMP_OptionBits options,
WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER ( "WXMPUtils_RemoveProperties_1" )
+ XMP_ENTER_Static ( "WXMPUtils_RemoveProperties_1" )
if ( wxmpObj == 0 ) XMP_Throw ( "Output XMP pointer is null", kXMPErr_BadParam );
if ( schemaNS == 0 ) schemaNS = "";
if ( propName == 0 ) propName = "";
-
+
XMPMeta * xmpObj = WtoXMPMeta_Ptr ( wxmpObj );
+ XMP_AutoLock metaLock ( &xmpObj->lock, kXMP_WriteLock );
+
XMPUtils::RemoveProperties ( xmpObj, schemaNS, propName, options );
- XMP_EXIT_WRAPPER
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
-void
-WXMPUtils_AppendProperties_1 ( XMPMetaRef wSource,
- XMPMetaRef wDest,
- XMP_OptionBits options,
- WXMP_Result * wResult )
-{
- XMP_ENTER_WRAPPER ( "WXMPUtils_AppendProperties_1" )
-
- if ( wDest == 0 ) XMP_Throw ( "Output XMP pointer is null", kXMPErr_BadParam );
-
- const XMPMeta & source = WtoXMPMeta_Ref ( wSource );
- XMPMeta * dest = WtoXMPMeta_Ptr ( wDest );
- XMPUtils::AppendProperties ( source, dest, options );
-
- XMP_EXIT_WRAPPER
-}
-
// -------------------------------------------------------------------------------------------------
void
@@ -602,8 +608,8 @@ WXMPUtils_DuplicateSubtree_1 ( XMPMetaRef wSource,
XMP_OptionBits options,
WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER ( "WXMPUtils_DuplicateSubtree_1" )
-
+ XMP_ENTER_Static ( "WXMPUtils_DuplicateSubtree_1" )
+
if ( wDest == 0 ) XMP_Throw ( "Output XMP pointer is null", kXMPErr_BadParam );
if ( (sourceNS == 0) || (*sourceNS == 0) ) XMP_Throw ( "Empty source schema URI", kXMPErr_BadSchema );
if ( (sourceRoot == 0) || (*sourceRoot == 0) ) XMP_Throw ( "Empty source root name", kXMPErr_BadXPath );
@@ -611,10 +617,14 @@ WXMPUtils_DuplicateSubtree_1 ( XMPMetaRef wSource,
if ( destRoot == 0 ) destRoot = sourceRoot;
const XMPMeta & source = WtoXMPMeta_Ref ( wSource );
+ XMP_AutoLock sourceLock ( &source.lock, kXMP_ReadLock, (wSource != wDest) );
+
XMPMeta * dest = WtoXMPMeta_Ptr ( wDest );
+ XMP_AutoLock destLock ( &dest->lock, kXMP_WriteLock );
+
XMPUtils::DuplicateSubtree ( source, dest, sourceNS, sourceRoot, destNS, destRoot, options );
- XMP_EXIT_WRAPPER
+ XMP_EXIT
}
// =================================================================================================
diff --git a/source/XMPCore/XMPCore_Impl.cpp b/source/XMPCore/XMPCore_Impl.cpp
index 1cf92c6..757fe72 100644
--- a/source/XMPCore/XMPCore_Impl.cpp
+++ b/source/XMPCore/XMPCore_Impl.cpp
@@ -1,5 +1,5 @@
// =================================================================================================
-// Copyright 2002-2007 Adobe Systems Incorporated
+// Copyright 2004 Adobe Systems Incorporated
// All Rights Reserved.
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -48,21 +48,9 @@ using namespace std;
XMP_Int32 sXMP_InitCount = 0;
-XMP_StringMap * sNamespaceURIToPrefixMap = 0;
-XMP_StringMap * sNamespacePrefixToURIMap = 0;
+XMP_NamespaceTable * sRegisteredNamespaces = 0;
-XMP_AliasMap * sRegisteredAliasMap = 0; // Needed by XMPIterator.
-
-XMP_VarString * sOutputNS = 0;
-XMP_VarString * sOutputStr = 0;
-XMP_VarString * sExceptionMessage = 0;
-
-XMP_Mutex sXMPCoreLock;
-int sLockCount = 0;
-
-#if TraceXMPCalls
- FILE * xmpOut = stderr;
-#endif
+XMP_AliasMap * sRegisteredAliasMap = 0;
void * voidVoidPtr = 0; // Used to backfill null output parameters.
XMP_StringPtr voidStringPtr = 0;
@@ -77,60 +65,6 @@ XMP_DateTime voidDateTime;
WXMP_Result void_wResult;
// =================================================================================================
-// Mutex Utilities
-// ===============
-
-// ! Note that the mutex need not be "recursive", allowing the same thread to acquire it multiple
-// ! times. There is a single XMP lock which is acquired in the wrapper classes. Internal calls
-// ! never go back out to the wrappers.
-
-#if XMP_WinBuild
-
- bool XMP_InitMutex ( XMP_Mutex * mutex ) {
- InitializeCriticalSection ( mutex );
- return true;
- }
-
- void XMP_TermMutex ( XMP_Mutex & mutex ) {
- DeleteCriticalSection ( &mutex );
- }
-
- void XMP_EnterCriticalRegion ( XMP_Mutex & mutex ) {
- EnterCriticalSection ( &mutex );
- }
-
- void XMP_ExitCriticalRegion ( XMP_Mutex & mutex ) {
- LeaveCriticalSection ( &mutex );
- }
-
-#else
-
- // Use pthread for both Mac and generic UNIX.
- // ! Would be nice to specify PTHREAD_MUTEX_ERRORCHECK, but the POSIX documentation is useless.
- // ! Would be OK but overkill to specify PTHREAD_MUTEX_RECURSIVE.
-
- bool XMP_InitMutex ( XMP_Mutex * mutex ) {
- int err = pthread_mutex_init ( mutex, 0 );
- return (err == 0 );
- }
-
- void XMP_TermMutex ( XMP_Mutex & mutex ) {
- (void) pthread_mutex_destroy ( &mutex );
- }
-
- void XMP_EnterCriticalRegion ( XMP_Mutex & mutex ) {
- int err = pthread_mutex_lock ( &mutex );
- if ( err != 0 ) XMP_Throw ( "XMP_EnterCriticalRegion - pthread_mutex_lock failure", kXMPErr_ExternalFailure );
- }
-
- void XMP_ExitCriticalRegion ( XMP_Mutex & mutex ) {
- int err = pthread_mutex_unlock ( &mutex );
- if ( err != 0 ) XMP_Throw ( "XMP_ExitCriticalRegion - pthread_mutex_unlock failure", kXMPErr_ExternalFailure );
- }
-
-#endif
-
-// =================================================================================================
// Local Utilities
// ===============
@@ -166,10 +100,9 @@ VerifyXPathRoot ( XMP_StringPtr schemaURI,
}
}
- XMP_StringMapPos uriPos = sNamespaceURIToPrefixMap->find ( XMP_VarString ( schemaURI ) );
- if ( uriPos == sNamespaceURIToPrefixMap->end() ) {
- XMP_Throw ( "Unregistered schema namespace URI", kXMPErr_BadSchema );
- }
+ XMP_StringPtr schemaPrefix;
+ bool nsFound = sRegisteredNamespaces->GetPrefix ( schemaURI, &schemaPrefix, 0 );
+ if ( ! nsFound ) XMP_Throw ( "Unregistered schema namespace URI", kXMPErr_BadSchema );
XMP_StringPtr colonPos = propName;
while ( (*colonPos != 0) && (*colonPos != ':') ) ++colonPos;
@@ -182,7 +115,7 @@ VerifyXPathRoot ( XMP_StringPtr schemaURI,
// The propName is unqualified, use the schemaURI and associated prefix.
expandedXPath->push_back ( XPathStepInfo ( schemaURI, kXMP_SchemaNode ) );
- expandedXPath->push_back ( XPathStepInfo ( uriPos->second, 0 ) );
+ expandedXPath->push_back ( XPathStepInfo ( schemaPrefix, 0 ) );
(*expandedXPath)[kRootPropStep].step += propName;
} else {
@@ -193,13 +126,7 @@ VerifyXPathRoot ( XMP_StringPtr schemaURI,
VerifySimpleXMLName ( colonPos+1, colonPos+strlen(colonPos) );
XMP_VarString prefix ( propName, prefixLen );
- XMP_StringMapPos prefixPos = sNamespacePrefixToURIMap->find ( prefix );
- if ( prefixPos == sNamespacePrefixToURIMap->end() ) {
- XMP_Throw ( "Unknown schema namespace prefix", kXMPErr_BadSchema );
- }
- if ( prefix != uriPos->second ) {
- XMP_Throw ( "Schema namespace URI and prefix mismatch", kXMPErr_BadSchema );
- }
+ if ( prefix != schemaPrefix ) XMP_Throw ( "Schema namespace URI and prefix mismatch", kXMPErr_BadSchema );
expandedXPath->push_back ( XPathStepInfo ( schemaURI, kXMP_SchemaNode ) );
expandedXPath->push_back ( XPathStepInfo ( propName, 0 ) );
@@ -226,10 +153,8 @@ VerifyQualName ( XMP_StringPtr qualName, XMP_StringPtr nameEnd )
size_t prefixLen = colonPos - qualName + 1; // ! Include the colon.
XMP_VarString prefix ( qualName, prefixLen );
- XMP_StringMapPos prefixPos = sNamespacePrefixToURIMap->find ( prefix );
- if ( prefixPos == sNamespacePrefixToURIMap->end() ) {
- XMP_Throw ( "Unknown namespace prefix for qualified name", kXMPErr_BadXPath );
- }
+ bool nsFound = sRegisteredNamespaces->GetURI ( prefix.c_str(), 0, 0 );
+ if ( ! nsFound ) XMP_Throw ( "Unknown namespace prefix for qualified name", kXMPErr_BadXPath );
} // VerifyQualName
@@ -498,9 +423,7 @@ CheckImplicitStruct ( XMP_Node * node,
// DeleteSubtree
// -------------
-// *** Might be useful elsewhere?
-
-static void
+void
DeleteSubtree ( XMP_NodePtrPos rootNodePos )
{
XMP_Node * rootNode = *rootNodePos;
@@ -1171,7 +1094,7 @@ EXIT:
// ==============
void
-CloneOffspring ( const XMP_Node * origParent, XMP_Node * cloneParent )
+CloneOffspring ( const XMP_Node * origParent, XMP_Node * cloneParent, bool skipEmpty /* = false */ )
{
size_t qualCount = origParent->qualifiers.size();
size_t childCount = origParent->children.size();
@@ -1182,8 +1105,14 @@ CloneOffspring ( const XMP_Node * origParent, XMP_Node * cloneParent )
for ( size_t qualNum = 0, qualLim = qualCount; qualNum != qualLim; ++qualNum ) {
const XMP_Node * origQual = origParent->qualifiers[qualNum];
- XMP_Node * cloneQual = new XMP_Node ( cloneParent, origQual->name, origQual->value, origQual->options );
- CloneOffspring ( origQual, cloneQual );
+ if ( skipEmpty && origQual->value.empty() && origQual->children.empty() ) continue;
+ XMP_Node * cloneQual = new XMP_Node ( cloneParent, origQual->name, origQual->value, origQual->options );
+ CloneOffspring ( origQual, cloneQual, skipEmpty );
+ if ( skipEmpty && cloneQual->value.empty() && cloneQual->children.empty() ) {
+ // Check again, might have had an array or struct with all empty children.
+ delete cloneQual;
+ continue;
+ }
cloneParent->qualifiers.push_back ( cloneQual );
}
@@ -1195,8 +1124,14 @@ CloneOffspring ( const XMP_Node * origParent, XMP_Node * cloneParent )
for ( size_t childNum = 0, childLim = childCount; childNum != childLim; ++childNum ) {
const XMP_Node * origChild = origParent->children[childNum];
- XMP_Node * cloneChild = new XMP_Node ( cloneParent, origChild->name, origChild->value, origChild->options );
- CloneOffspring ( origChild, cloneChild );
+ if ( skipEmpty && origChild->value.empty() && origChild->children.empty() ) continue;
+ XMP_Node * cloneChild = new XMP_Node ( cloneParent, origChild->name, origChild->value, origChild->options );
+ CloneOffspring ( origChild, cloneChild, skipEmpty );
+ if ( skipEmpty && cloneChild->value.empty() && cloneChild->children.empty() ) {
+ // Check again, might have had an array or struct with all empty children.
+ delete cloneChild;
+ continue;
+ }
cloneParent->children.push_back ( cloneChild );
}
@@ -1209,7 +1144,7 @@ CloneOffspring ( const XMP_Node * origParent, XMP_Node * cloneParent )
// ============
XMP_Node *
-CloneSubtree ( const XMP_Node * origRoot, XMP_Node * cloneParent )
+CloneSubtree ( const XMP_Node * origRoot, XMP_Node * cloneParent, bool skipEmpty /* = false */ )
{
#if XMP_DebugBuild
if ( cloneParent->parent == 0 ) {
@@ -1224,9 +1159,15 @@ CloneSubtree ( const XMP_Node * origRoot, XMP_Node * cloneParent )
#endif
XMP_Node * cloneRoot = new XMP_Node ( cloneParent, origRoot->name, origRoot->value, origRoot->options );
- CloneOffspring ( origRoot, cloneRoot ) ;
+ CloneOffspring ( origRoot, cloneRoot, skipEmpty ) ;
+
+ if ( skipEmpty && cloneRoot->value.empty() && cloneRoot->children.empty() ) {
+ // ! Can't do earlier, CloneOffspring might be skipping empty children.
+ delete cloneRoot;
+ return 0;
+ }
+
cloneParent->children.push_back ( cloneRoot );
-
return cloneRoot;
} // CloneSubtree
diff --git a/source/XMPCore/XMPCore_Impl.hpp b/source/XMPCore/XMPCore_Impl.hpp
index d1be1c3..9a5314a 100644
--- a/source/XMPCore/XMPCore_Impl.hpp
+++ b/source/XMPCore/XMPCore_Impl.hpp
@@ -1,8 +1,8 @@
#ifndef __XMPCore_Impl_hpp__
-#define __XMPCore_Impl_hpp__
+#define __XMPCore_Impl_hpp__ 1
// =================================================================================================
-// Copyright 2002-2008 Adobe Systems Incorporated
+// Copyright 2004 Adobe Systems Incorporated
// All Rights Reserved.
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -12,8 +12,9 @@
#include "XMP_Environment.h" // ! Must be the first #include!
#include "XMP_Const.h"
#include "XMP_BuildInfo.h"
+#include "XMP_LibUtils.hpp"
-#include "client-glue/WXMPMeta.hpp"
+#include "client-glue/WXMP_Common.hpp"
#include <vector>
#include <string>
@@ -22,13 +23,6 @@
#include <cassert>
#if XMP_WinBuild
- #include <Windows.h>
-#else
- // Use pthread for both Mac and generic UNIX.
- #include <pthread.h>
-#endif
-
-#if XMP_WinBuild
#pragma warning ( disable : 4244 ) // possible loss of data (temporary for 64 bit builds)
#pragma warning ( disable : 4267 ) // possible loss of data (temporary for 64 bit builds)
#endif
@@ -45,16 +39,9 @@ typedef XMP_Node * XMP_NodePtr;
typedef std::vector<XMP_Node*> XMP_NodeOffspring;
typedef XMP_NodeOffspring::iterator XMP_NodePtrPos;
-typedef std::string XMP_VarString;
typedef XMP_VarString::iterator XMP_VarStringPos;
typedef XMP_VarString::const_iterator XMP_cVarStringPos;
-typedef std::pair < XMP_VarString, XMP_VarString > XMP_StringPair;
-
-typedef std::map < XMP_VarString, XMP_VarString > XMP_StringMap;
-typedef XMP_StringMap::iterator XMP_StringMapPos;
-typedef XMP_StringMap::const_iterator XMP_cStringMapPos;
-
typedef std::vector < XPathStepInfo > XMP_ExpandedXPath;
typedef XMP_ExpandedXPath::iterator XMP_ExpandedXPathPos;
typedef XMP_ExpandedXPath::const_iterator XMP_cExpandedXPathPos;
@@ -68,13 +55,14 @@ typedef XMP_AliasMap::const_iterator XMP_cAliasMapPos;
extern XMP_Int32 sXMP_InitCount;
-extern XMP_AliasMap * sRegisteredAliasMap;
+extern XMP_NamespaceTable * sRegisteredNamespaces;
-extern XMP_StringMap * sNamespaceURIToPrefixMap;
-extern XMP_StringMap * sNamespacePrefixToURIMap;
+extern XMP_AliasMap * sRegisteredAliasMap;
-extern XMP_VarString * sOutputNS;
-extern XMP_VarString * sOutputStr;
+#define WtoXMPMeta_Ref(xmpRef) (const XMPMeta &) (*((XMPMeta*)(xmpRef)))
+#define WtoXMPMeta_Ptr(xmpRef) ((XMPMeta*)(xmpRef))
+
+#define WtoXMPDocOps_Ptr(docRef) ((XMPDocOps*)(docRef))
extern void * voidVoidPtr; // Used to backfill null output parameters.
extern XMP_StringPtr voidStringPtr;
@@ -94,25 +82,10 @@ extern WXMP_Result void_wResult;
#define XMP_LitNMatch(s,l,n) (std::strncmp((s),(l),(n)) == 0)
// *** Use the above macros!
-#define kTab ((char)0x09)
-#define kLF ((char)0x0A)
-#define kCR ((char)0x0D)
-
#if XMP_WinBuild
#define snprintf _snprintf
#endif
-#define WtoXMPMeta_Ref(xmpRef) *((const XMPMeta *)(xmpRef))
-#define WtoXMPMeta_Ptr(xmpRef) (((xmpRef) == 0) ? 0 : (XMPMeta *)(xmpRef))
-
-#define WtoXMPIterator_Ref(iterRef) *((const XMPIterator *)(iterRef))
-#define WtoXMPIterator_Ptr(iterRef) (((iterRef) == 0) ? 0 : (XMPIterator *)(iterRef))
-
-#define WtoXMPDocOps_Ref(docRef) *((const XMPDocOps *)(docRef))
-#define WtoXMPDocOps_Ptr(docRef) (((docRef) == 0) ? 0 : (XMPDocOps *)(docRef))
-
-#define IgnoreParam(p) voidVoidPtr = (void*)&p
-
// =================================================================================================
// Version info
@@ -130,158 +103,40 @@ extern WXMP_Result void_wResult;
#define kXMPCoreName "XMP Core"
#define kXMPCore_VersionMessage kXMPCoreName " " XMP_API_VERSION_STRING
// =================================================================================================
-// Support for asserts
+// Support for call tracing
-#define _MakeStr(p) #p
-#define _NotifyMsg(n,c,f,l) #n " failed: " #c " in " f " at line " _MakeStr(l)
-
-#if ! XMP_DebugBuild
- #define XMP_Assert(c) ((void) 0)
-#else
- #define XMP_Assert(c) assert ( c )
+#ifndef XMP_TraceCoreCalls
+ #define XMP_TraceCoreCalls 0
+ #define XMP_TraceCoreCallsToFile 0
#endif
- #define XMP_Enforce(c) \
- if ( ! (c) ) { \
- const char * assert_msg = _NotifyMsg ( XMP_Enforce, (c), __FILE__, __LINE__ ); \
- XMP_Throw ( assert_msg , kXMPErr_EnforceFailure ); \
- }
-// =================================================================================================
-// Support for exceptions and thread locking
-
-#ifndef TraceXMPCalls
- #define TraceXMPCalls 0
-#endif
+#if XMP_TraceCoreCalls
-#if ! TraceXMPCalls
+ #undef AnnounceThrow
+ #undef AnnounceCatch
- #define AnnounceThrow(msg) /* Do nothing. */
- #define AnnounceCatch(msg) /* Do nothing. */
+ #undef AnnounceEntry
+ #undef AnnounceNoLock
+ #undef AnnounceExit
- #define AnnounceEntry(proc) /* Do nothing. */
- #define AnnounceNoLock(proc) /* Do nothing. */
- #define AnnounceExit() /* Do nothing. */
-
- #define ReportLock() ++sLockCount
- #define ReportUnlock() --sLockCount
- #define ReportKeepLock() /* Do nothing. */
-
-#else
-
- extern FILE * xmpCoreOut;
+ extern FILE * xmpCoreLog;
#define AnnounceThrow(msg) \
- fprintf ( xmpCoreOut, "XMP_Throw: %s\n", msg ); fflush ( xmpOut )
+ fprintf ( xmpCoreLog, "XMP_Throw: %s\n", msg ); fflush ( xmpCoreLog )
#define AnnounceCatch(msg) \
- fprintf ( xmpCoreOut, "Catch in %s: %s\n", procName, msg ); fflush ( xmpOut )
+ fprintf ( xmpCoreLog, "Catch in %s: %s\n", procName, msg ); fflush ( xmpCoreLog )
#define AnnounceEntry(proc) \
const char * procName = proc; \
- fprintf ( xmpCoreOut, "Entering %s\n", procName ); fflush ( xmpOut )
+ fprintf ( xmpCoreLog, "Entering %s\n", procName ); fflush ( xmpCoreLog )
#define AnnounceNoLock(proc) \
const char * procName = proc; \
- fprintf ( xmpCoreOut, "Entering %s (no lock)\n", procName ); fflush ( xmpOut )
+ fprintf ( xmpCoreLog, "Entering %s (no lock)\n", procName ); fflush ( xmpCoreLog )
#define AnnounceExit() \
- fprintf ( xmpCoreOut, "Exiting %s\n", procName ); fflush ( xmpOut )
-
- #define ReportLock() \
- ++sLockCount; fprintf ( xmpCoreOut, " Auto lock, count = %d\n", sLockCount ); fflush ( xmpOut )
- #define ReportUnlock() \
- --sLockCount; fprintf ( xmpCoreOut, " Auto unlock, count = %d\n", sLockCount ); fflush ( xmpOut )
- #define ReportKeepLock() \
- fprintf ( xmpCoreOut, " Keeping lock, count = %d\n", sLockCount ); fflush ( xmpOut )
+ fprintf ( xmpCoreLog, "Exiting %s\n", procName ); fflush ( xmpCoreLog )
#endif
-#define XMP_Throw(msg,id) { AnnounceThrow ( msg ); throw XMP_Error ( id, msg ); }
-
-// -------------------------------------------------------------------------------------------------
-
-#if XMP_WinBuild
- typedef CRITICAL_SECTION XMP_Mutex;
-#else
- // Use pthread for both Mac and generic UNIX.
- typedef pthread_mutex_t XMP_Mutex;
-#endif
-
-extern XMP_Mutex sXMPCoreLock;
-extern int sLockCount; // Keep signed to catch unlock errors.
-extern XMP_VarString * sExceptionMessage;
-
-extern bool XMP_InitMutex ( XMP_Mutex * mutex );
-extern void XMP_TermMutex ( XMP_Mutex & mutex );
-
-extern void XMP_EnterCriticalRegion ( XMP_Mutex & mutex );
-extern void XMP_ExitCriticalRegion ( XMP_Mutex & mutex );
-
-class XMP_AutoMutex {
-public:
- XMP_AutoMutex() : mutex(&sXMPCoreLock) { XMP_EnterCriticalRegion ( *mutex ); ReportLock(); };
- ~XMP_AutoMutex() { if ( mutex != 0 ) { ReportUnlock(); XMP_ExitCriticalRegion ( *mutex ); mutex = 0; } };
- void KeepLock() { ReportKeepLock(); mutex = 0; };
-private:
- XMP_Mutex * mutex;
-};
-
-// *** Switch to XMPEnterObjectWrapper & XMPEnterStaticWrapper, to allow for per-object locks.
-
-// ! Don't do the initialization check (sXMP_InitCount > 0) for the no-lock case. That macro is used
-// ! by WXMPMeta_Initialize_1.
-
-#define XMP_ENTER_WRAPPER_NO_LOCK(proc) \
- AnnounceNoLock ( proc ); \
- XMP_Assert ( (0 <= sLockCount) && (sLockCount <= 1) ); \
- try { \
- wResult->errMessage = 0;
-
-#define XMP_ENTER_WRAPPER(proc) \
- AnnounceEntry ( proc ); \
- XMP_Assert ( sXMP_InitCount > 0 ); \
- XMP_Assert ( (0 <= sLockCount) && (sLockCount <= 1) ); \
- try { \
- XMP_AutoMutex mutex; \
- wResult->errMessage = 0;
-
-#define XMP_EXIT_WRAPPER \
- XMP_CATCH_EXCEPTIONS \
- AnnounceExit();
-
-#define XMP_EXIT_WRAPPER_KEEP_LOCK(keep) \
- if ( keep ) mutex.KeepLock(); \
- XMP_CATCH_EXCEPTIONS \
- AnnounceExit();
-
-#define XMP_EXIT_WRAPPER_NO_THROW \
- } catch ( ... ) { \
- AnnounceCatch ( "no-throw catch-all" ); \
- /* Do nothing. */ \
- } \
- AnnounceExit();
-
-#define XMP_CATCH_EXCEPTIONS \
- } catch ( XMP_Error & xmpErr ) { \
- wResult->int32Result = xmpErr.GetID(); \
- wResult->ptrResult = (void*)"XMP"; \
- wResult->errMessage = xmpErr.GetErrMsg(); \
- if ( wResult->errMessage == 0 ) wResult->errMessage = ""; \
- AnnounceCatch ( wResult->errMessage ); \
- } catch ( std::exception & stdErr ) { \
- wResult->int32Result = kXMPErr_StdException; \
- wResult->errMessage = stdErr.what(); \
- if ( wResult->errMessage == 0 ) wResult->errMessage = ""; \
- AnnounceCatch ( wResult->errMessage ); \
- } catch ( ... ) { \
- wResult->int32Result = kXMPErr_UnknownException; \
- wResult->errMessage = "Caught unknown exception"; \
- AnnounceCatch ( wResult->errMessage ); \
- }
-
-#if XMP_DebugBuild
- #define RELEASE_NO_THROW /* empty */
-#else
- #define RELEASE_NO_THROW throw()
-#endif
-
// =================================================================================================
// ExpandXPath, FindNode, and related support
@@ -341,15 +196,18 @@ extern XMP_Index
LookupFieldSelector ( const XMP_Node * arrayNode, XMP_StringPtr fieldName, XMP_StringPtr fieldValue );
extern void
-CloneOffspring ( const XMP_Node * origParent, XMP_Node * cloneParent );
+CloneOffspring ( const XMP_Node * origParent, XMP_Node * cloneParent, bool skipEmpty = false );
extern XMP_Node *
-CloneSubtree ( const XMP_Node * origRoot, XMP_Node * cloneParent );
+CloneSubtree ( const XMP_Node * origRoot, XMP_Node * cloneParent, bool skipEmpty = false );
extern bool
CompareSubtrees ( const XMP_Node & leftNode, const XMP_Node & rightNode );
extern void
+DeleteSubtree ( XMP_NodePtrPos rootNodePos );
+
+extern void
DeleteEmptySchema ( XMP_Node * schemaNode );
extern void
diff --git a/source/XMPCore/XMPIterator.cpp b/source/XMPCore/XMPIterator.cpp
index 8032bfa..08e03fa 100644
--- a/source/XMPCore/XMPIterator.cpp
+++ b/source/XMPCore/XMPIterator.cpp
@@ -1,5 +1,5 @@
// =================================================================================================
-// Copyright 2002-2007 Adobe Systems Incorporated
+// Copyright 2003 Adobe Systems Incorporated
// All Rights Reserved.
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -61,47 +61,6 @@ AddSchemaProps ( IterInfo & info, IterNode & iterSchema, const XMP_Node * xmpSch
} // AddSchemaProps
// -------------------------------------------------------------------------------------------------
-// AddSchemaAliases
-// ----------------
-//
-// Add the aliases to the IterNode for a schema, if the corresponding actual exists.
-
-static void
-AddSchemaAliases ( IterInfo & info, IterNode & iterSchema, XMP_StringPtr schemaURI )
-{
-
- // We're showing the aliases also. Look them up by their namespace prefix. Yes, the alias map is
- // sorted so we could process just that portion. But that takes more code and the extra speed
- // isn't worth it. (Plus this way we avoid a dependence on the map implementation.) Lookup the
- // XMP node from the alias, to make sure the actual exists.
-
- #if TraceIterators
- printf ( " Adding aliases\n", schemaURI );
- #endif
-
- XMP_StringPtr nsPrefix;
- XMP_StringLen nsLen;
- bool found = XMPMeta::GetNamespacePrefix ( schemaURI, &nsPrefix, &nsLen );
- if ( ! found ) XMP_Throw ( "Unknown iteration namespace", kXMPErr_BadSchema );
-
- XMP_AliasMapPos currAlias = sRegisteredAliasMap->begin();
- XMP_AliasMapPos endAlias = sRegisteredAliasMap->end();
-
- for ( ; currAlias != endAlias; ++currAlias ) {
- if ( XMP_LitNMatch ( currAlias->first.c_str(), nsPrefix, nsLen ) ) {
- const XMP_Node * actualProp = FindConstNode ( &info.xmpObj->tree, currAlias->second );
- if ( actualProp != 0 ) {
- iterSchema.children.push_back ( IterNode ( (actualProp->options | kXMP_PropIsAlias), currAlias->first, 0 ) );
- #if TraceIterators
- printf ( " %s => %s\n", currAlias->first.c_str(), actualProp->name.c_str() );
- #endif
- }
- }
- }
-
-} // AddSchemaAliases
-
-// -------------------------------------------------------------------------------------------------
// AddNodeOffspring
// ----------------
//
@@ -156,7 +115,7 @@ AddNodeOffspring ( IterInfo & info, IterNode & iterParent, const XMP_Node * xmpP
currPath += xmpChild->name;
} else {
char buffer [32]; // AUDIT: Using sizeof(buffer) below for snprintf length is safe.
- snprintf ( buffer, sizeof(buffer), "[%d]", childNum+1 ); // ! XPath indices are one-based.
+ snprintf ( buffer, sizeof(buffer), "[%lu]", childNum+1 ); // ! XPath indices are one-based.
currPath += buffer;
}
iterParent.children.push_back ( IterNode ( xmpChild->options, currPath, leafOffset ) );
@@ -390,19 +349,6 @@ XMPIterator::Terminate() RELEASE_NO_THROW
} // Terminate
-// -------------------------------------------------------------------------------------------------
-// Unlock
-// ------
-
-void
-XMPIterator::Unlock ( XMP_OptionBits options )
-{
- options = options; // Avoid unused parameter warning.
-
- XMPMeta::Unlock ( 0 );
-
-} // Unlock
-
// =================================================================================================
// Constructors
// =================================================================================================
@@ -480,8 +426,6 @@ XMPIterator::XMPIterator ( const XMPMeta & xmpObj,
XMP_Node * xmpSchema = FindConstSchema ( &xmpObj.tree, schemaNS );
if ( xmpSchema != 0 ) AddSchemaProps ( info, iterSchema, xmpSchema );
- if ( info.options & kXMP_IterIncludeAliases ) AddSchemaAliases ( info, iterSchema, schemaNS );
-
if ( iterSchema.children.empty() ) {
info.tree.children.pop_back(); // No properties, remove the schema node.
} else {
@@ -509,40 +453,10 @@ XMPIterator::XMPIterator ( const XMPMeta & xmpObj,
if ( ! (info.options & kXMP_IterJustChildren) ) {
AddSchemaProps ( info, iterSchema, xmpSchema );
- if ( info.options & kXMP_IterIncludeAliases ) AddSchemaAliases ( info, iterSchema, xmpSchema->name.c_str() );
if ( iterSchema.children.empty() ) info.tree.children.pop_back(); // No properties, remove the schema node.
}
}
-
- if ( info.options & kXMP_IterIncludeAliases ) {
-
- // Add the schema that only have aliases. The most convenient, and safest way, is to go
- // through the registered namespaces, see if it exists, and let AddSchemaAliases do its
- // thing if not. Don't combine with the above loop, it is nicer to have the "real" stuff
- // be in storage order (not subject to the namespace map order).
-
- // ! We don't do the kXMP_IterJustChildren handing in the same way here as above. The
- // ! existing schema (presumably) have actual children. We need to call AddSchemaAliases
- // ! here to determine if the namespace has any aliases to existing properties. We then
- // ! strip the children if necessary.
-
- XMP_cStringMapPos currNS = sNamespaceURIToPrefixMap->begin();
- XMP_cStringMapPos endNS = sNamespaceURIToPrefixMap->end();
- for ( ; currNS != endNS; ++currNS ) {
- XMP_StringPtr schemaName = currNS->first.c_str();
- if ( FindConstSchema ( &xmpObj.tree, schemaName ) != 0 ) continue;
- info.tree.children.push_back ( IterNode ( kXMP_SchemaNode, schemaName, 0 ) );
- IterNode & iterSchema = info.tree.children.back();
- AddSchemaAliases ( info, iterSchema, schemaName );
- if ( iterSchema.children.empty() ) {
- info.tree.children.pop_back(); // No aliases, remove the schema node.
- } else if ( info.options & kXMP_IterJustChildren ) {
- iterSchema.children.clear(); // Get rid of the children.
- }
- }
-
- }
}
@@ -719,17 +633,4 @@ XMPIterator::Skip ( XMP_OptionBits iterOptions )
} // Skip
-// -------------------------------------------------------------------------------------------------
-// UnlockIter
-// ----------
-
-void
-XMPIterator::UnlockIter ( XMP_OptionBits options )
-{
- options = options; // Avoid unused parameter warning.
-
- XMPMeta::Unlock ( 0 );
-
-} // UnlockIter
-
// =================================================================================================
diff --git a/source/XMPCore/XMPIterator.hpp b/source/XMPCore/XMPIterator.hpp
index b72b975..d55f721 100644
--- a/source/XMPCore/XMPIterator.hpp
+++ b/source/XMPCore/XMPIterator.hpp
@@ -2,7 +2,7 @@
#define __XMPIterator_hpp__
// =================================================================================================
-// Copyright 2002-2007 Adobe Systems Incorporated
+// Copyright 2003 Adobe Systems Incorporated
// All Rights Reserved.
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -97,9 +97,6 @@ public:
static void
Terminate() RELEASE_NO_THROW; // ! For internal use only!
- static void
- Unlock ( XMP_OptionBits options );
-
XMPIterator ( const XMPMeta & xmpObj, // Construct a property iterator.
XMP_StringPtr schemaNS,
XMP_StringPtr propName,
@@ -123,12 +120,11 @@ public:
void
Skip ( XMP_OptionBits options );
- void
- UnlockIter ( XMP_OptionBits options );
-
// ! Expose so that wrappers and file static functions can see the data.
XMP_Int32 clientRefs; // ! Must be signed to allow decrement from 0.
+ XMP_ReadWriteLock lock;
+
IterInfo info;
private:
diff --git a/source/XMPCore/XMPMeta-GetSet.cpp b/source/XMPCore/XMPMeta-GetSet.cpp
index 41b01d5..3ec0997 100644
--- a/source/XMPCore/XMPMeta-GetSet.cpp
+++ b/source/XMPCore/XMPMeta-GetSet.cpp
@@ -1,5 +1,5 @@
// =================================================================================================
-// Copyright 2002-2008 Adobe Systems Incorporated
+// Copyright 2003 Adobe Systems Incorporated
// All Rights Reserved.
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -79,10 +79,11 @@ SetNodeValue ( XMP_Node * node, XMP_StringPtr value )
}
#endif
- node->value = value;
+ std::string newValue = value; // Need a local copy to tweak and not change node.value for errors.
- XMP_Uns8* chPtr = (XMP_Uns8*) node->value.c_str(); // Check for valid UTF-8, replace ASCII controls with a space.
+ XMP_Uns8* chPtr = (XMP_Uns8*) newValue.c_str(); // Check for valid UTF-8, replace ASCII controls with a space.
while ( *chPtr != 0 ) {
+
while ( (*chPtr != 0) && (*chPtr < 0x80) ) {
if ( *chPtr < 0x20 ) {
if ( (*chPtr != kTab) && (*chPtr != kLF) && (*chPtr != kCR) ) *chPtr = 0x20;
@@ -91,11 +92,21 @@ SetNodeValue ( XMP_Node * node, XMP_StringPtr value )
}
++chPtr;
}
+
XMP_Assert ( (*chPtr == 0) || (*chPtr >= 0x80) );
- if ( *chPtr != 0 ) (void) GetCodePoint ( (const XMP_Uns8 **) &chPtr ); // Throws for bad UTF-8.
+
+ if ( *chPtr != 0 ) {
+ XMP_Uns32 cp = GetCodePoint ( (const XMP_Uns8 **) &chPtr ); // Throws for bad UTF-8.
+ if ( (cp == 0xFFFE) || (cp == 0xFFFF) ) {
+ XMP_Throw ( "U+FFFE and U+FFFF are not allowed in XML", kXMPErr_BadXML );
+ }
+ }
+
}
- if ( XMP_PropIsQualifier(node->options) && (node->name == "xml:lang") ) NormalizeLangValue ( &node->value );
+ if ( XMP_PropIsQualifier(node->options) && (node->name == "xml:lang") ) NormalizeLangValue ( &newValue );
+
+ node->value.swap ( newValue );
#if 0 // *** XMP_DebugBuild
node->_valuePtr = node->value.c_str();
@@ -314,8 +325,18 @@ ChooseLocalizedText ( const XMP_Node * arrayNode,
static void
AppendLangItem ( XMP_Node * arrayNode, XMP_StringPtr itemLang, XMP_StringPtr itemValue )
{
- XMP_Node * newItem = new XMP_Node ( arrayNode, kXMP_ArrayItemName, itemValue, (kXMP_PropHasQualifiers | kXMP_PropHasLang) );
- XMP_Node * langQual = new XMP_Node ( newItem, "xml:lang", itemLang, kXMP_PropIsQualifier );
+ XMP_Node * newItem = new XMP_Node ( arrayNode, kXMP_ArrayItemName, (kXMP_PropHasQualifiers | kXMP_PropHasLang) );
+ XMP_Node * langQual = new XMP_Node ( newItem, "xml:lang", kXMP_PropIsQualifier );
+
+ try { // ! Use SetNodeValue, not constructors above, to get the character checks.
+ SetNodeValue ( newItem, itemValue );
+ SetNodeValue ( langQual, itemLang );
+ } catch (...) {
+ delete newItem;
+ delete langQual;
+ throw;
+ }
+
newItem->qualifiers.push_back ( langQual );
if ( (arrayNode->children.empty()) || (langQual->value != "x-default") ) {
@@ -373,17 +394,19 @@ XMPMeta::GetArrayItem ( XMP_StringPtr schemaNS,
XMP_StringPtr arrayName,
XMP_Index itemIndex,
XMP_StringPtr * itemValue,
- XMP_StringLen * valueSize,
+ XMP_StringLen * valueSize,
XMP_OptionBits * options ) const
{
XMP_Assert ( (schemaNS != 0) && (arrayName != 0) ); // Enforced by wrapper.
- XMP_Assert ( (itemValue != 0) && (valueSize != 0) && (options != 0) ); // Enforced by wrapper.
+ XMP_Assert ( (itemValue != 0) && (options != 0) ); // Enforced by wrapper.
- XMP_StringPtr itemPath;
- XMP_StringLen pathLen;
+ // ! Special case check to make errors consistent if the array does not exist. The other array
+ // ! functions and existing array here (empty or not) already throw.
+ if ( (itemIndex <= 0) && (itemIndex != kXMP_ArrayLastItem) ) XMP_Throw ( "Array index must be larger than zero", kXMPErr_BadXPath );
- XMPUtils::ComposeArrayItemPath ( schemaNS, arrayName, itemIndex, &itemPath, &pathLen );
- return GetProperty ( schemaNS, itemPath, itemValue, valueSize, options );
+ XMP_VarString itemPath;
+ XMPUtils::ComposeArrayItemPath ( schemaNS, arrayName, itemIndex, &itemPath );
+ return GetProperty ( schemaNS, itemPath.c_str(), itemValue, valueSize, options );
} // GetArrayItem
@@ -402,13 +425,11 @@ XMPMeta::GetStructField ( XMP_StringPtr schemaNS,
XMP_OptionBits * options ) const
{
XMP_Assert ( (schemaNS != 0) && (structName != 0) && (fieldNS != 0) && (fieldName != 0) ); // Enforced by wrapper.
- XMP_Assert ( (fieldValue != 0) && (valueSize != 0) && (options != 0) ); // Enforced by wrapper.
-
- XMP_StringPtr fieldPath;
- XMP_StringLen pathLen;
+ XMP_Assert ( (fieldValue != 0) && (options != 0) ); // Enforced by wrapper.
- XMPUtils::ComposeStructFieldPath ( schemaNS, structName, fieldNS, fieldName, &fieldPath, &pathLen );
- return GetProperty ( schemaNS, fieldPath, fieldValue, valueSize, options );
+ XMP_VarString fieldPath;
+ XMPUtils::ComposeStructFieldPath ( schemaNS, structName, fieldNS, fieldName, &fieldPath );
+ return GetProperty ( schemaNS, fieldPath.c_str(), fieldValue, valueSize, options );
} // GetStructField
@@ -423,17 +444,15 @@ XMPMeta::GetQualifier ( XMP_StringPtr schemaNS,
XMP_StringPtr qualNS,
XMP_StringPtr qualName,
XMP_StringPtr * qualValue,
- XMP_StringLen * valueSize,
+ XMP_StringLen * valueSize,
XMP_OptionBits * options ) const
{
XMP_Assert ( (schemaNS != 0) && (propName != 0) && (qualNS != 0) && (qualName != 0) ); // Enforced by wrapper.
- XMP_Assert ( (qualValue != 0) && (valueSize != 0) && (options != 0) ); // Enforced by wrapper.
+ XMP_Assert ( (qualValue != 0) && (options != 0) ); // Enforced by wrapper.
- XMP_StringPtr qualPath;
- XMP_StringLen pathLen;
-
- XMPUtils::ComposeQualifierPath ( schemaNS, propName, qualNS, qualName, &qualPath, &pathLen );
- return GetProperty ( schemaNS, qualPath, qualValue, valueSize, options );
+ XMP_VarString qualPath;
+ XMPUtils::ComposeQualifierPath ( schemaNS, propName, qualNS, qualName, &qualPath );
+ return GetProperty ( schemaNS, qualPath.c_str(), qualValue, valueSize, options );
} // GetQualifier
@@ -550,11 +569,9 @@ XMPMeta::SetStructField ( XMP_StringPtr schemaNS,
{
XMP_Assert ( (schemaNS != 0) && (structName != 0) && (fieldNS != 0) && (fieldName != 0) ); // Enforced by wrapper.
- XMP_StringPtr fieldPath;
- XMP_StringLen pathLen;
-
- XMPUtils::ComposeStructFieldPath ( schemaNS, structName, fieldNS, fieldName, &fieldPath, &pathLen );
- SetProperty ( schemaNS, fieldPath, fieldValue, options );
+ XMP_VarString fieldPath;
+ XMPUtils::ComposeStructFieldPath ( schemaNS, structName, fieldNS, fieldName, &fieldPath );
+ SetProperty ( schemaNS, fieldPath.c_str(), fieldValue, options );
} // SetStructField
@@ -573,16 +590,14 @@ XMPMeta::SetQualifier ( XMP_StringPtr schemaNS,
{
XMP_Assert ( (schemaNS != 0) && (propName != 0) && (qualNS != 0) && (qualName != 0) ); // Enforced by wrapper.
- XMP_StringPtr qualPath;
- XMP_StringLen pathLen;
-
XMP_ExpandedXPath expPath;
ExpandXPath ( schemaNS, propName, &expPath );
XMP_Node * propNode = FindNode ( &tree, expPath, kXMP_ExistingOnly );
if ( propNode == 0 ) XMP_Throw ( "Specified property does not exist", kXMPErr_BadXPath );
- XMPUtils::ComposeQualifierPath ( schemaNS, propName, qualNS, qualName, &qualPath, &pathLen );
- SetProperty ( schemaNS, qualPath, qualValue, options );
+ XMP_VarString qualPath;
+ XMPUtils::ComposeQualifierPath ( schemaNS, propName, qualNS, qualName, &qualPath );
+ SetProperty ( schemaNS, qualPath.c_str(), qualValue, options );
} // SetQualifier
@@ -644,11 +659,9 @@ XMPMeta::DeleteArrayItem ( XMP_StringPtr schemaNS,
{
XMP_Assert ( (schemaNS != 0) && (arrayName != 0) ); // Enforced by wrapper.
- XMP_StringPtr itemPath;
- XMP_StringLen pathLen;
-
- XMPUtils::ComposeArrayItemPath ( schemaNS, arrayName, itemIndex, &itemPath, &pathLen );
- DeleteProperty ( schemaNS, itemPath );
+ XMP_VarString itemPath;
+ XMPUtils::ComposeArrayItemPath ( schemaNS, arrayName, itemIndex, &itemPath );
+ DeleteProperty ( schemaNS, itemPath.c_str() );
} // DeleteArrayItem
@@ -665,11 +678,9 @@ XMPMeta::DeleteStructField ( XMP_StringPtr schemaNS,
{
XMP_Assert ( (schemaNS != 0) && (structName != 0) && (fieldNS != 0) && (fieldName != 0) ); // Enforced by wrapper.
- XMP_StringPtr fieldPath;
- XMP_StringLen pathLen;
-
- XMPUtils::ComposeStructFieldPath ( schemaNS, structName, fieldNS, fieldName, &fieldPath, &pathLen );
- DeleteProperty ( schemaNS, fieldPath );
+ XMP_VarString fieldPath;
+ XMPUtils::ComposeStructFieldPath ( schemaNS, structName, fieldNS, fieldName, &fieldPath );
+ DeleteProperty ( schemaNS, fieldPath.c_str() );
} // DeleteStructField
@@ -686,11 +697,9 @@ XMPMeta::DeleteQualifier ( XMP_StringPtr schemaNS,
{
XMP_Assert ( (schemaNS != 0) && (propName != 0) && (qualNS != 0) && (qualName != 0) ); // Enforced by wrapper.
- XMP_StringPtr qualPath;
- XMP_StringLen pathLen;
-
- XMPUtils::ComposeQualifierPath ( schemaNS, propName, qualNS, qualName, &qualPath, &pathLen );
- DeleteProperty ( schemaNS, qualPath );
+ XMP_VarString qualPath;
+ XMPUtils::ComposeQualifierPath ( schemaNS, propName, qualNS, qualName, &qualPath );
+ DeleteProperty ( schemaNS, qualPath.c_str() );
} // DeleteQualifier
@@ -725,11 +734,9 @@ XMPMeta::DoesArrayItemExist ( XMP_StringPtr schemaNS,
{
XMP_Assert ( (schemaNS != 0) && (arrayName != 0) ); // Enforced by wrapper.
- XMP_StringPtr itemPath;
- XMP_StringLen pathLen;
-
- XMPUtils::ComposeArrayItemPath ( schemaNS, arrayName, itemIndex, &itemPath, &pathLen );
- return DoesPropertyExist ( schemaNS, itemPath );
+ XMP_VarString itemPath;
+ XMPUtils::ComposeArrayItemPath ( schemaNS, arrayName, itemIndex, &itemPath );
+ return DoesPropertyExist ( schemaNS, itemPath.c_str() );
} // DoesArrayItemExist
@@ -746,11 +753,9 @@ XMPMeta::DoesStructFieldExist ( XMP_StringPtr schemaNS,
{
XMP_Assert ( (schemaNS != 0) && (structName != 0) && (fieldNS != 0) && (fieldName != 0) ); // Enforced by wrapper.
- XMP_StringPtr fieldPath;
- XMP_StringLen pathLen;
-
- XMPUtils::ComposeStructFieldPath ( schemaNS, structName, fieldNS, fieldName, &fieldPath, &pathLen );
- return DoesPropertyExist ( schemaNS, fieldPath );
+ XMP_VarString fieldPath;
+ XMPUtils::ComposeStructFieldPath ( schemaNS, structName, fieldNS, fieldName, &fieldPath );
+ return DoesPropertyExist ( schemaNS, fieldPath.c_str() );
} // DoesStructFieldExist
@@ -767,11 +772,9 @@ XMPMeta::DoesQualifierExist ( XMP_StringPtr schemaNS,
{
XMP_Assert ( (schemaNS != 0) && (propName != 0) && (qualNS != 0) && (qualName != 0) ); // Enforced by wrapper.
- XMP_StringPtr qualPath;
- XMP_StringLen pathLen;
-
- XMPUtils::ComposeQualifierPath ( schemaNS, propName, qualNS, qualName, &qualPath, &pathLen );
- return DoesPropertyExist ( schemaNS, qualPath );
+ XMP_VarString qualPath;
+ XMPUtils::ComposeQualifierPath ( schemaNS, propName, qualNS, qualName, &qualPath );
+ return DoesPropertyExist ( schemaNS, qualPath.c_str() );
} // DoesQualifierExist
@@ -971,6 +974,107 @@ XMPMeta::SetLocalizedText ( XMP_StringPtr schemaNS,
} // SetLocalizedText
+// -------------------------------------------------------------------------------------------------
+// DeleteLocalizedText
+// -------------------
+
+void
+XMPMeta::DeleteLocalizedText ( XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_StringPtr _genericLang,
+ XMP_StringPtr _specificLang )
+{
+ XMP_Assert ( (schemaNS != 0) && (arrayName != 0) && (_genericLang != 0) && (_specificLang != 0) ); // Enforced by wrapper.
+
+ XMP_VarString zGenericLang ( _genericLang );
+ XMP_VarString zSpecificLang ( _specificLang );
+ NormalizeLangValue ( &zGenericLang );
+ NormalizeLangValue ( &zSpecificLang );
+
+ XMP_StringPtr genericLang = zGenericLang.c_str();
+ XMP_StringPtr specificLang = zSpecificLang.c_str();
+
+ XMP_ExpandedXPath arrayPath;
+ ExpandXPath ( schemaNS, arrayName, &arrayPath );
+
+ // Find the LangAlt array and the selected array item.
+
+ XMP_Node * arrayNode = FindNode ( &tree, arrayPath, kXMP_ExistingOnly );
+ if ( arrayNode == 0 ) return;
+ size_t arraySize = arrayNode->children.size();
+
+ XMP_CLTMatch match;
+ XMP_Node * itemNode;
+
+ match = ChooseLocalizedText ( arrayNode, genericLang, specificLang, (const XMP_Node **) &itemNode );
+ if ( match != kXMP_CLT_SpecificMatch ) return;
+
+ size_t itemIndex = 0;
+ for ( ; itemIndex < arraySize; ++itemIndex ) {
+ if ( arrayNode->children[itemIndex] == itemNode ) break;
+ }
+ XMP_Enforce ( itemIndex < arraySize );
+
+ // Decide if the selected item is x-default or not, find relevant matching item.
+
+ bool itemIsXDefault = false;
+ if ( ! itemNode->qualifiers.empty() ) {
+ XMP_Node * qualNode = itemNode->qualifiers[0];
+ if ( (qualNode->name == "xml:lang") && (qualNode->value == "x-default") ) itemIsXDefault = true;
+ }
+
+ if ( itemIsXDefault && (itemIndex != 0) ) { // Enforce the x-default is first policy.
+ XMP_Node * temp = arrayNode->children[0];
+ arrayNode->children[0] = arrayNode->children[itemIndex];
+ arrayNode->children[itemIndex] = temp;
+ itemIndex = 0;
+ }
+
+ XMP_Node * assocNode = 0;
+ size_t assocIndex;
+ size_t assocIsXDefault = false;
+
+ if ( itemIsXDefault ) {
+
+ for ( assocIndex = 1; assocIndex < arraySize; ++assocIndex ) {
+ if ( arrayNode->children[assocIndex]->value == itemNode->value ) {
+ assocNode = arrayNode->children[assocIndex];
+ break;
+ }
+ }
+
+ } else if ( itemIndex > 0 ) {
+
+ XMP_Node * itemZero = arrayNode->children[0];
+ if ( itemZero->value == itemNode->value ) {
+ XMP_Node * qualNode = itemZero->qualifiers[0];
+ if ( (qualNode->name == "xml:lang") && (qualNode->value == "x-default") ) {
+ assocNode = arrayNode->children[0];
+ assocIndex = 0;
+ assocIsXDefault = true;
+ }
+ }
+
+ }
+
+ // Delete the appropriate nodes.
+
+ XMP_NodePtrPos arrayBegin = arrayNode->children.begin();
+
+ if ( assocNode == 0 ) {
+ arrayNode->children.erase ( arrayBegin + itemIndex );
+ } else if ( itemIndex < assocIndex ) {
+ arrayNode->children.erase ( arrayBegin + assocIndex );
+ arrayNode->children.erase ( arrayBegin + itemIndex );
+ } else {
+ arrayNode->children.erase ( arrayBegin + itemIndex );
+ arrayNode->children.erase ( arrayBegin + assocIndex );
+ }
+
+ delete itemNode;
+ if ( assocNode != 0 ) delete assocNode;
+
+} // DeleteLocalizedText
// -------------------------------------------------------------------------------------------------
// GetProperty_Bool
@@ -1114,11 +1218,9 @@ XMPMeta::SetProperty_Bool ( XMP_StringPtr schemaNS,
{
XMP_Assert ( (schemaNS != 0) && (propName != 0) ); // Enforced by wrapper.
- XMP_StringPtr valueStr;
- XMP_StringLen valueLen;
-
- XMPUtils::ConvertFromBool ( propValue, &valueStr, &valueLen );
- SetProperty ( schemaNS, propName, valueStr, options );
+ XMP_VarString valueStr;
+ XMPUtils::ConvertFromBool ( propValue, &valueStr );
+ SetProperty ( schemaNS, propName, valueStr.c_str(), options );
} // SetProperty_Bool
@@ -1135,11 +1237,9 @@ XMPMeta::SetProperty_Int ( XMP_StringPtr schemaNS,
{
XMP_Assert ( (schemaNS != 0) && (propName != 0) ); // Enforced by wrapper.
- XMP_StringPtr valueStr;
- XMP_StringLen valueLen;
-
- XMPUtils::ConvertFromInt ( propValue, "", &valueStr, &valueLen );
- SetProperty ( schemaNS, propName, valueStr, options );
+ XMP_VarString valueStr;
+ XMPUtils::ConvertFromInt ( propValue, "", &valueStr );
+ SetProperty ( schemaNS, propName, valueStr.c_str(), options );
} // SetProperty_Int
@@ -1156,11 +1256,9 @@ XMPMeta::SetProperty_Int64 ( XMP_StringPtr schemaNS,
{
XMP_Assert ( (schemaNS != 0) && (propName != 0) ); // Enforced by wrapper.
- XMP_StringPtr valueStr;
- XMP_StringLen valueLen;
-
- XMPUtils::ConvertFromInt64 ( propValue, "", &valueStr, &valueLen );
- SetProperty ( schemaNS, propName, valueStr, options );
+ XMP_VarString valueStr;
+ XMPUtils::ConvertFromInt64 ( propValue, "", &valueStr );
+ SetProperty ( schemaNS, propName, valueStr.c_str(), options );
} // SetProperty_Int64
@@ -1177,11 +1275,9 @@ XMPMeta::SetProperty_Float ( XMP_StringPtr schemaNS,
{
XMP_Assert ( (schemaNS != 0) && (propName != 0) ); // Enforced by wrapper.
- XMP_StringPtr valueStr;
- XMP_StringLen valueLen;
-
- XMPUtils::ConvertFromFloat ( propValue, "", &valueStr, &valueLen );
- SetProperty ( schemaNS, propName, valueStr, options );
+ XMP_VarString valueStr;
+ XMPUtils::ConvertFromFloat ( propValue, "", &valueStr );
+ SetProperty ( schemaNS, propName, valueStr.c_str(), options );
} // SetProperty_Float
@@ -1198,11 +1294,9 @@ XMPMeta::SetProperty_Date ( XMP_StringPtr schemaNS,
{
XMP_Assert ( (schemaNS != 0) && (propName != 0) ); // Enforced by wrapper.
- XMP_StringPtr valueStr;
- XMP_StringLen valueLen;
-
- XMPUtils::ConvertFromDate ( propValue, &valueStr, &valueLen );
- SetProperty ( schemaNS, propName, valueStr, options );
+ XMP_VarString valueStr;
+ XMPUtils::ConvertFromDate ( propValue, &valueStr );
+ SetProperty ( schemaNS, propName, valueStr.c_str(), options );
} // SetProperty_Date
diff --git a/source/XMPCore/XMPMeta-Parse.cpp b/source/XMPCore/XMPMeta-Parse.cpp
index 5ff64f4..a70efff 100644
--- a/source/XMPCore/XMPMeta-Parse.cpp
+++ b/source/XMPCore/XMPMeta-Parse.cpp
@@ -1,5 +1,5 @@
// =================================================================================================
-// Copyright 2002-2008 Adobe Systems Incorporated
+// Copyright 2003 Adobe Systems Incorporated
// All Rights Reserved.
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -258,18 +258,27 @@ NormalizeDCArrays ( XMP_Node * xmpTree )
arrayForm = VerifySetOptions ( arrayForm, 0 ); // Set the implicit array bits.
XMP_Node * newArray = new XMP_Node ( dcSchema, currProp->name.c_str(), arrayForm );
dcSchema->children[propNum] = newArray;
- newArray->children.push_back ( currProp );
- currProp->parent = newArray;
- currProp->name = kXMP_ArrayItemName;
- if ( XMP_ArrayIsAltText ( arrayForm ) && (! (currProp->options & kXMP_PropHasLang)) ) {
- XMP_Node * newLang = new XMP_Node ( currProp, "xml:lang", "x-default", kXMP_PropIsQualifier );
- currProp->options |= (kXMP_PropHasQualifiers | kXMP_PropHasLang);
- if ( currProp->qualifiers.empty() ) { // *** Need a util?
- currProp->qualifiers.push_back ( newLang );
- } else {
- currProp->qualifiers.insert ( currProp->qualifiers.begin(), newLang );
+ if ( currProp->value.empty() ) { // Don't add an empty item, leave the array empty.
+
+ delete ( currProp );
+
+ } else {
+
+ newArray->children.push_back ( currProp );
+ currProp->parent = newArray;
+ currProp->name = kXMP_ArrayItemName;
+
+ if ( XMP_ArrayIsAltText ( arrayForm ) && (! (currProp->options & kXMP_PropHasLang)) ) {
+ XMP_Node * newLang = new XMP_Node ( currProp, "xml:lang", "x-default", kXMP_PropIsQualifier );
+ currProp->options |= (kXMP_PropHasQualifiers | kXMP_PropHasLang);
+ if ( currProp->qualifiers.empty() ) { // *** Need a util?
+ currProp->qualifiers.push_back ( newLang );
+ } else {
+ currProp->qualifiers.insert ( currProp->qualifiers.begin(), newLang );
+ }
}
+
}
}
@@ -496,11 +505,7 @@ FixGPSTimeStamp ( XMP_Node * exifSchema, XMP_Node * gpsDateTime )
binGPSStamp.month = binOtherDate.month;
binGPSStamp.day = binOtherDate.day;
- XMP_StringPtr goodStr;
- XMP_StringLen goodLen;
- XMPUtils::ConvertFromDate ( binGPSStamp, &goodStr, &goodLen );
-
- gpsDateTime->value.assign ( goodStr, goodLen );
+ XMPUtils::ConvertFromDate ( binGPSStamp, &gpsDateTime->value );
} // FixGPSTimeStamp
@@ -1086,7 +1091,7 @@ XMPMeta::ParseFromBuffer ( XMP_StringPtr buffer,
if ( this->xmlParser == 0 ) {
if ( (xmpSize == 0) && lastClientCall ) return; // Tolerate empty parse. Expat complains if there are no XML elements.
- this->xmlParser = XMP_NewExpatAdapter();
+ this->xmlParser = XMP_NewExpatAdapter ( ExpatAdapter::kUseGlobalNamespaces );
}
XMLParserAdapter& parser = *this->xmlParser;
diff --git a/source/XMPCore/XMPMeta-Serialize.cpp b/source/XMPCore/XMPMeta-Serialize.cpp
index 0aa75ae..0ae2d12 100644
--- a/source/XMPCore/XMPMeta-Serialize.cpp
+++ b/source/XMPCore/XMPMeta-Serialize.cpp
@@ -1,5 +1,5 @@
// =================================================================================================
-// Copyright 2002-2008 Adobe Systems Incorporated
+// Copyright 2003-2009 Adobe Systems Incorporated
// All Rights Reserved.
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -43,7 +43,7 @@ using namespace std;
static const char * kPacketHeader = "<?xpacket begin=\"\xEF\xBB\xBF\" id=\"W5M0MpCehiHzreSzNTczkc9d\"?>";
static const char * kPacketTrailer = "<?xpacket end=\"w\"?>"; // ! The w/r is at [size-4].
-static const char * kPXMP_SchemaGroup = "XMP_SchemaGroup";
+static const char * kTXMP_SchemaGroup = "XMP_SchemaGroup";
static const char * kRDF_XMPMetaStart = "<x:xmpmeta xmlns:x=\"adobe:ns:meta/\" x:xmptk=\"";
static const char * kRDF_XMPMetaEnd = "</x:xmpmeta>";
@@ -134,13 +134,13 @@ EstimateRDFSize ( const XMP_Node * currNode, XMP_Index indent, size_t indentLen
// -------------------
static void
-DeclareOneNamespace ( const XMP_VarString & nsPrefix,
- const XMP_VarString & nsURI,
- XMP_VarString & usedNS, // ! A catenation of the prefixes with colons.
- XMP_VarString & outputStr,
- XMP_StringPtr newline,
- XMP_StringPtr indentStr,
- XMP_Index indent )
+DeclareOneNamespace ( XMP_StringPtr nsPrefix,
+ XMP_StringPtr nsURI,
+ XMP_VarString & usedNS, // ! A catenation of the prefixes with colons.
+ XMP_VarString & outputStr,
+ XMP_StringPtr newline,
+ XMP_StringPtr indentStr,
+ XMP_Index indent )
{
size_t nsPos = usedNS.find ( nsPrefix );
@@ -178,9 +178,10 @@ DeclareElemNamespace ( const XMP_VarString & elemName,
if ( colonPos != XMP_VarString::npos ) {
XMP_VarString nsPrefix ( elemName.substr ( 0, colonPos+1 ) );
- XMP_StringMapPos prefixPos = sNamespacePrefixToURIMap->find ( nsPrefix );
- XMP_Enforce ( prefixPos != sNamespacePrefixToURIMap->end() );
- DeclareOneNamespace ( nsPrefix, prefixPos->second, usedNS, outputStr, newline, indentStr, indent );
+ XMP_StringPtr nsURI;
+ bool nsFound = sRegisteredNamespaces->GetURI ( nsPrefix.c_str(), &nsURI, 0 );
+ XMP_Enforce ( nsFound );
+ DeclareOneNamespace ( nsPrefix.c_str(), nsURI, usedNS, outputStr, newline, indentStr, indent );
}
} // DeclareElemNamespace
@@ -190,8 +191,6 @@ DeclareElemNamespace ( const XMP_VarString & elemName,
// DeclareUsedNamespaces
// ---------------------
-// ??? Should iterators be passed by reference to avoid temp copies?
-
static void
DeclareUsedNamespaces ( const XMP_Node * currNode,
XMP_VarString & usedNS,
@@ -203,7 +202,7 @@ DeclareUsedNamespaces ( const XMP_Node * currNode,
if ( currNode->options & kXMP_SchemaNode ) {
// The schema node name is the URI, the value is the prefix.
- DeclareOneNamespace ( currNode->value, currNode->name, usedNS, outputStr, newline, indentStr, indent );
+ DeclareOneNamespace ( currNode->value.c_str(), currNode->name.c_str(), usedNS, outputStr, newline, indentStr, indent );
} else if ( currNode->options & kXMP_PropValueIsStruct ) {
for ( size_t fieldNum = 0, fieldLim = currNode->children.size(); fieldNum < fieldLim; ++fieldNum ) {
const XMP_Node * currField = currNode->children[fieldNum];
@@ -228,8 +227,6 @@ DeclareUsedNamespaces ( const XMP_Node * currNode,
// EmitRDFArrayTag
// ---------------
-// ??? Should iterators be passed by reference to avoid temp copies?
-
enum {
kIsStartTag = true,
kIsEndTag = false
@@ -438,9 +435,8 @@ SerializePrettyRDFProperty ( const XMP_Node * propNode,
for ( level = indent; level > 0; --level ) outputStr += indentStr;
outputStr += '<';
outputStr += elemName;
-
- #define isCompact false
- bool hasGeneralQualifiers = isCompact; // Might also become true later.
+
+ bool hasGeneralQualifiers = false;
bool hasRDFResourceQual = false;
for ( size_t qualNum = 0, qualLim = propNode->qualifiers.size(); qualNum < qualLim; ++qualNum ) {
@@ -476,7 +472,7 @@ SerializePrettyRDFProperty ( const XMP_Node * propNode,
outputStr += newline;
SerializePrettyRDFProperty ( propNode, outputStr, newline, indentStr, indent+1, true );
-
+
for ( size_t qualNum = 0, qualLim = propNode->qualifiers.size(); qualNum < qualLim; ++qualNum ) {
const XMP_Node * currQual = propNode->qualifiers[qualNum];
if ( IsRDFAttrQualifier ( currQual->name ) ) continue;
@@ -618,13 +614,8 @@ SerializePrettyRDFSchema ( const XMP_VarString & treeName,
outputStr += treeName;
outputStr += '"';
- size_t totalLen = 8; // Start at 8 for "xml:rdf:".
- XMP_cStringMapPos currPos = sNamespacePrefixToURIMap->begin();
- XMP_cStringMapPos endPos = sNamespacePrefixToURIMap->end();
- for ( ; currPos != endPos; ++currPos ) totalLen += currPos->first.size();
-
XMP_VarString usedNS;
- usedNS.reserve ( totalLen );
+ usedNS.reserve ( 400 ); // The predefined prefixes add up to about 320 bytes.
usedNS = "xml:rdf:";
DeclareUsedNamespaces ( schemaNode, usedNS, outputStr, newline, indentStr, baseIndent+4 );
@@ -632,36 +623,6 @@ SerializePrettyRDFSchema ( const XMP_VarString & treeName,
outputStr += newline;
// Write alias comments, if wanted.
-
- if ( options & kXMP_WriteAliasComments ) { // *** Hoist into a routine, used for Plain XMP also.
-
- #if 0 // *** Buggy, disable for now.
-
- XMP_cAliasMapPos aliasPos = sRegisteredAliasMap->begin();
- XMP_cAliasMapPos aliasEnd = sRegisteredAliasMap->end();
-
- for ( ; aliasPos != aliasEnd; ++aliasPos ) {
-
- size_t nsPos = aliasPos->first.find ( schemaNode->value );
- if ( nsPos == XMP_VarString::npos ) continue;
- XMP_Assert ( nsPos == 0 );
-
- for ( level = baseIndent+3; level > 0; --level ) outputStr += indentStr;
-
- outputStr += "<!-- ";
- outputStr += aliasPos->first;
- outputStr += " is aliased to ";
- for ( size_t step = 1, stepLim = aliasPos->second.size(); step != stepLim; ++step ) {
- outputStr += aliasPos->second[step].step;
- }
- outputStr += " -->";
- outputStr += newline;
-
- }
-
- #endif
-
- }
// Write each of the schema's actual properties.
for ( size_t propNum = 0, propLim = schemaNode->children.size(); propNum < propLim; ++propNum ) {
@@ -785,9 +746,8 @@ SerializeCompactRDFElemProps ( const XMP_Node * parentNode,
for ( level = indent; level > 0; --level ) outputStr += indentStr;
outputStr += '<';
outputStr += elemName;
-
- #define isCompact false
- bool hasGeneralQualifiers = isCompact; // Might also become true later.
+
+ bool hasGeneralQualifiers = false;
bool hasRDFResourceQual = false;
for ( size_t qualNum = 0, qualLim = propNode->qualifiers.size(); qualNum < qualLim; ++qualNum ) {
@@ -821,7 +781,7 @@ SerializeCompactRDFElemProps ( const XMP_Node * parentNode,
outputStr += newline;
SerializePrettyRDFProperty ( propNode, outputStr, newline, indentStr, indent+1, true );
-
+
size_t qualNum = 0;
size_t qualLim = propNode->qualifiers.size();
if ( propNode->options & kXMP_PropHasLang ) ++qualNum;
@@ -993,14 +953,9 @@ SerializeCompactRDFSchemas ( const XMP_Node & xmpTree,
outputStr += '"';
// Write all necessary xmlns attributes.
-
- size_t totalLen = 8; // Start at 8 for "xml:rdf:".
- XMP_cStringMapPos currPos = sNamespacePrefixToURIMap->begin();
- XMP_cStringMapPos endPos = sNamespacePrefixToURIMap->end();
- for ( ; currPos != endPos; ++currPos ) totalLen += currPos->first.size();
XMP_VarString usedNS;
- usedNS.reserve ( totalLen );
+ usedNS.reserve ( 400 ); // The predefined prefixes add up to about 320 bytes.
usedNS = "xml:rdf:";
for ( schema = 0, schemaLim = xmpTree.children.size(); schema != schemaLim; ++schema ) {
@@ -1166,16 +1121,16 @@ SerializeAsRDF ( const XMPMeta & xmpObj,
// -----------------
void
-XMPMeta::SerializeToBuffer ( XMP_StringPtr * rdfString,
- XMP_StringLen * rdfSize,
+XMPMeta::SerializeToBuffer ( XMP_VarString * rdfString,
XMP_OptionBits options,
XMP_StringLen padding,
XMP_StringPtr newline,
XMP_StringPtr indentStr,
XMP_Index baseIndent ) const
{
- XMP_Assert ( (rdfString != 0) && (rdfSize != 0) && (newline != 0) && (indentStr != 0) );
-
+ XMP_Assert ( (rdfString != 0) && (newline != 0) && (indentStr != 0) );
+ rdfString->erase();
+
// Fix up some default parameters.
enum { kDefaultPad = 2048 };
@@ -1222,7 +1177,11 @@ XMPMeta::SerializeToBuffer ( XMP_StringPtr * rdfString,
}
padding = 0;
} else {
- if ( padding == 0 ) padding = kDefaultPad * unicodeUnitSize;
+ if ( padding == 0 ) {
+ padding = kDefaultPad * unicodeUnitSize;
+ } else if ( (padding >> 28) != 0 ) {
+ XMP_Throw ( "Outrageously large padding size", kXMPErr_BadOptions ); // Bigger than 256 MB.
+ }
if ( options & kXMP_IncludeThumbnailPad ) {
if ( ! this->DoesPropertyExist ( kXMP_NS_XMP, "Thumbnails" ) ) padding += (10000 * unicodeUnitSize); // *** Need a better estimate.
}
@@ -1232,11 +1191,11 @@ XMPMeta::SerializeToBuffer ( XMP_StringPtr * rdfString,
std::string tailStr;
- SerializeAsRDF ( *this, *sOutputStr, tailStr, options, newline, indentStr, baseIndent );
+ SerializeAsRDF ( *this, *rdfString, tailStr, options, newline, indentStr, baseIndent );
if ( charEncoding == kXMP_EncodeUTF8 ) {
if ( options & kXMP_ExactPacketLength ) {
- size_t minSize = sOutputStr->size() + tailStr.size();
+ size_t minSize = rdfString->size() + tailStr.size();
if ( minSize > padding ) XMP_Throw ( "Can't fit into specified packet size", kXMPErr_BadSerialize );
padding -= minSize; // Now the actual amount of padding to add.
}
@@ -1244,19 +1203,19 @@ XMPMeta::SerializeToBuffer ( XMP_StringPtr * rdfString,
size_t newlineLen = strlen ( newline );
if ( padding < newlineLen ) {
- sOutputStr->append ( padding, ' ' );
+ rdfString->append ( padding, ' ' );
} else {
padding -= newlineLen; // Write this newline last.
while ( padding >= (100 + newlineLen) ) {
- sOutputStr->append ( 100, ' ' );
- *sOutputStr += newline;
+ rdfString->append ( 100, ' ' );
+ *rdfString += newline;
padding -= (100 + newlineLen);
}
- sOutputStr->append ( padding, ' ' );
- *sOutputStr += newline;
+ rdfString->append ( padding, ' ' );
+ *rdfString += newline;
}
- *sOutputStr += tailStr;
+ *rdfString += tailStr;
} else {
@@ -1269,13 +1228,13 @@ XMPMeta::SerializeToBuffer ( XMP_StringPtr * rdfString,
std::string padStr ( " " ); padStr[0] = 0; // Assume big endian.
- utf8Str.swap ( *sOutputStr );
- ToUTF16 ( (UTF8Unit*)utf8Str.c_str(), utf8Str.size(), sOutputStr, bigEndian );
+ utf8Str.swap ( *rdfString );
+ ToUTF16 ( (UTF8Unit*)utf8Str.c_str(), utf8Str.size(), rdfString, bigEndian );
utf8Str.swap ( tailStr );
ToUTF16 ( (UTF8Unit*)utf8Str.c_str(), utf8Str.size(), &tailStr, bigEndian );
if ( options & kXMP_ExactPacketLength ) {
- size_t minSize = sOutputStr->size() + tailStr.size();
+ size_t minSize = rdfString->size() + tailStr.size();
if ( minSize > padding ) XMP_Throw ( "Can't fit into specified packet size", kXMPErr_BadSerialize );
padding -= minSize; // Now the actual amount of padding to add (in bytes).
}
@@ -1285,19 +1244,19 @@ XMPMeta::SerializeToBuffer ( XMP_StringPtr * rdfString,
size_t newlineLen = newlineStr.size();
if ( padding < newlineLen ) {
- for ( int i = padding/2; i > 0; --i ) *sOutputStr += padStr;
+ for ( int i = padding/2; i > 0; --i ) *rdfString += padStr;
} else {
padding -= newlineLen; // Write this newline last.
while ( padding >= (200 + newlineLen) ) {
- for ( int i = 100; i > 0; --i ) *sOutputStr += padStr;
- *sOutputStr += newlineStr;
+ for ( int i = 100; i > 0; --i ) *rdfString += padStr;
+ *rdfString += newlineStr;
padding -= (200 + newlineLen);
}
- for ( int i = padding/2; i > 0; --i ) *sOutputStr += padStr;
- *sOutputStr += newlineStr;
+ for ( int i = padding/2; i > 0; --i ) *rdfString += padStr;
+ *rdfString += newlineStr;
}
- *sOutputStr += tailStr;
+ *rdfString += tailStr;
} else {
@@ -1309,13 +1268,13 @@ XMPMeta::SerializeToBuffer ( XMP_StringPtr * rdfString,
Converter = UTF8_to_UTF32LE;
}
- utf8Str.swap ( *sOutputStr );
- ToUTF32 ( (UTF8Unit*)utf8Str.c_str(), utf8Str.size(), sOutputStr, bigEndian );
+ utf8Str.swap ( *rdfString );
+ ToUTF32 ( (UTF8Unit*)utf8Str.c_str(), utf8Str.size(), rdfString, bigEndian );
utf8Str.swap ( tailStr );
ToUTF32 ( (UTF8Unit*)utf8Str.c_str(), utf8Str.size(), &tailStr, bigEndian );
if ( options & kXMP_ExactPacketLength ) {
- size_t minSize = sOutputStr->size() + tailStr.size();
+ size_t minSize = rdfString->size() + tailStr.size();
if ( minSize > padding ) XMP_Throw ( "Can't fit into specified packet size", kXMPErr_BadSerialize );
padding -= minSize; // Now the actual amount of padding to add (in bytes).
}
@@ -1325,29 +1284,24 @@ XMPMeta::SerializeToBuffer ( XMP_StringPtr * rdfString,
size_t newlineLen = newlineStr.size();
if ( padding < newlineLen ) {
- for ( int i = padding/4; i > 0; --i ) *sOutputStr += padStr;
+ for ( int i = padding/4; i > 0; --i ) *rdfString += padStr;
} else {
padding -= newlineLen; // Write this newline last.
while ( padding >= (400 + newlineLen) ) {
- for ( int i = 100; i > 0; --i ) *sOutputStr += padStr;
- *sOutputStr += newlineStr;
+ for ( int i = 100; i > 0; --i ) *rdfString += padStr;
+ *rdfString += newlineStr;
padding -= (400 + newlineLen);
}
- for ( int i = padding/4; i > 0; --i ) *sOutputStr += padStr;
- *sOutputStr += newlineStr;
+ for ( int i = padding/4; i > 0; --i ) *rdfString += padStr;
+ *rdfString += newlineStr;
}
- *sOutputStr += tailStr;
+ *rdfString += tailStr;
}
}
- // Return the finished string.
-
- *rdfString = sOutputStr->c_str();
- *rdfSize = sOutputStr->size();
-
} // SerializeToBuffer
// =================================================================================================
diff --git a/source/XMPCore/XMPMeta.cpp b/source/XMPCore/XMPMeta.cpp
index c5def31..44bee16 100644
--- a/source/XMPCore/XMPMeta.cpp
+++ b/source/XMPCore/XMPMeta.cpp
@@ -1,5 +1,5 @@
// =================================================================================================
-// Copyright 2002-2008 Adobe Systems Incorporated
+// Copyright 2003 Adobe Systems Incorporated
// All Rights Reserved.
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -52,7 +52,7 @@ using namespace std;
// Static Variables
// ================
-XMP_VarString * xdefaultName = 0;
+XMP_VarString * xdefaultName = 0; // Needed in XMPMeta-Parse.cpp, MoveExplicitAliases.
// These are embedded version strings.
@@ -63,134 +63,16 @@ const char * kXMPCore_EmbeddedCopyright = kXMPCoreName " " kXMP_CopyrightStr;
// Local Utilities
// ===============
-#define IsHexDigit(ch) ( (('0' <= (ch)) && ((ch) <= '9')) || (('A' <= (ch)) && ((ch) <= 'F')) )
-#define HexDigitValue(ch) ( (((ch) - '0') < 10) ? ((ch) - '0') : ((ch) - 'A' + 10) )
-
-static const char * kTenSpaces = " ";
-#define OutProcPadding(pad) { size_t padLen = (pad); \
- for ( ; padLen >= 10; padLen -= 10 ) OutProcNChars ( kTenSpaces, 10 ); \
- for ( ; padLen > 0; padLen -= 1 ) OutProcNChars ( " ", 1 ); }
-
-
-#define OutProcNewline() { status = (*outProc) ( refCon, "\n", 1 ); if ( status != 0 ) goto EXIT; }
-
-#define OutProcNChars(p,n) { status = (*outProc) ( refCon, (p), (n) ); if ( status != 0 ) goto EXIT; }
-
-#define OutProcLiteral(lit) { status = (*outProc) ( refCon, (lit), strlen(lit) ); if ( status != 0 ) goto EXIT; }
-
-#define OutProcString(str) { status = (*outProc) ( refCon, (str).c_str(), (str).size() ); if ( status != 0 ) goto EXIT; }
-
-#define OutProcDecInt(num) { snprintf ( buffer, sizeof(buffer), "%d", (num) ); /* AUDIT: Using sizeof for snprintf length is safe */ \
- status = (*outProc) ( refCon, buffer, strlen(buffer) ); if ( status != 0 ) goto EXIT; }
-
-#define OutProcHexInt(num) { snprintf ( buffer, sizeof(buffer), "%X", (num) ); /* AUDIT: Using sizeof for snprintf length is safe */ \
- status = (*outProc) ( refCon, buffer, strlen(buffer) ); if ( status != 0 ) goto EXIT; }
-
-#define OutProcHexByte(num) { snprintf ( buffer, sizeof(buffer), "%.2X", (num) ); /* AUDIT: Using sizeof for snprintf length is safe */ \
- status = (*outProc) ( refCon, buffer, strlen(buffer) ); if ( status != 0 ) goto EXIT; }
-
-static const char * kIndent = " ";
-#define OutProcIndent(lev) { for ( size_t i = 0; i < (lev); ++i ) OutProcNChars ( kIndent, 3 ); }
-
-
-// -------------------------------------------------------------------------------------------------
-// DumpClearString
-// ---------------
-
-static XMP_Status
-DumpClearString ( const XMP_VarString & value, XMP_TextOutputProc outProc, void * refCon )
-{
-
- char buffer [20];
- bool prevNormal;
- XMP_Status status = 0;
-
- XMP_StringPtr spanStart, spanEnd;
- XMP_StringPtr valueEnd = &value[0] + value.size();
-
- spanStart = &value[0];
- while ( spanStart < valueEnd ) {
-
- // Output the next span of regular characters.
- for ( spanEnd = spanStart; spanEnd < valueEnd; ++spanEnd ) {
- if ( *spanEnd > 0x7F ) break;
- if ( (*spanEnd < 0x20) && (*spanEnd != kTab) && (*spanEnd != kLF) ) break;
- }
- if ( spanStart != spanEnd ) status = (*outProc) ( refCon, spanStart, (spanEnd-spanStart) );
- if ( status != 0 ) break;
- spanStart = spanEnd;
-
- // Output the next span of irregular characters.
- prevNormal = true;
- for ( spanEnd = spanStart; spanEnd < valueEnd; ++spanEnd ) {
- if ( ((0x20 <= *spanEnd) && (*spanEnd <= 0x7F)) || (*spanEnd == kTab) || (*spanEnd == kLF) ) break;
- char space = ' ';
- if ( prevNormal ) space = '<';
- status = (*outProc) ( refCon, &space, 1 );
- if ( status != 0 ) break;
- OutProcHexByte ( *spanEnd );
- prevNormal = false;
- }
- if ( ! prevNormal ) {
- status = (*outProc) ( refCon, ">", 1 );
- if ( status != 0 ) return status;
- }
- spanStart = spanEnd;
-
- }
-
-EXIT:
- return status;
-
-} // DumpClearString
-
-
-// -------------------------------------------------------------------------------------------------
-// DumpStringMap
-// -------------
-
-static XMP_Status
-DumpStringMap ( const XMP_StringMap & map, XMP_StringPtr label, XMP_TextOutputProc outProc, void * refCon )
-{
- XMP_Status status;
- XMP_cStringMapPos currPos;
- XMP_cStringMapPos endPos = map.end();
-
- size_t maxLen = 0;
- for ( currPos = map.begin(); currPos != endPos; ++currPos ) {
- size_t currLen = currPos->first.size();
- if ( currLen > maxLen ) maxLen = currLen;
- }
-
- OutProcNewline();
- OutProcLiteral ( label );
- OutProcNewline();
-
- for ( currPos = map.begin(); currPos != endPos; ++currPos ) {
- OutProcNChars ( " ", 2 );
- DumpClearString ( currPos->first, outProc, refCon );
- OutProcPadding ( maxLen - currPos->first.size() );
- OutProcNChars ( " => ", 4 );
- DumpClearString ( currPos->second, outProc, refCon );
- OutProcNewline();
- }
-
-EXIT:
- return status;
-
-} // DumpStringMap
-
// -------------------------------------------------------------------------------------------------
// DumpNodeOptions
// ---------------
-static XMP_Status
+static void
DumpNodeOptions ( XMP_OptionBits options,
XMP_TextOutputProc outProc,
void * refCon )
{
- XMP_Status status;
char buffer [32]; // Decimal of a 64 bit int is at most about 20 digits.
static const char * optNames[] = { " schema", // 0x8000_0000
@@ -244,9 +126,6 @@ DumpNodeOptions ( XMP_OptionBits options,
OutProcNChars ( ")", 1 );
}
-
-EXIT:
- return status;
} // DumpNodeOptions
@@ -257,15 +136,14 @@ EXIT:
// *** Extract the validation code into a separate routine to call on exit in debug builds.
-static XMP_Status
+static void
DumpPropertyTree ( const XMP_Node * currNode,
int indent,
size_t itemIndex,
XMP_TextOutputProc outProc,
void * refCon )
{
- XMP_Status status;
- char buffer [32]; // Decimal of a 64 bit int is at most about 20 digits.
+ char buffer [32]; // Decimal of a 64 bit int is at most about 20 digits.
OutProcIndent ( (size_t)indent );
if ( itemIndex == 0 ) {
@@ -285,8 +163,7 @@ DumpPropertyTree ( const XMP_Node * currNode,
if ( currNode->options != 0 ) {
OutProcNChars ( " ", 2 );
- status = DumpNodeOptions ( currNode->options, outProc, refCon );
- if ( status != 0 ) goto EXIT;
+ DumpNodeOptions ( currNode->options, outProc, refCon );
}
if ( currNode->options & kXMP_PropHasLang ) {
@@ -322,8 +199,7 @@ DumpPropertyTree ( const XMP_Node * currNode,
if ( (qualNum != 0) || (! (currNode->options & kXMP_PropHasLang)) ) OutProcLiteral ( "** bad lang qual => " );
}
- status = DumpPropertyTree ( currQual, indent+2, 0, outProc, refCon );
- if ( status != 0 ) goto EXIT;
+ DumpPropertyTree ( currQual, indent+2, 0, outProc, refCon );
}
@@ -342,13 +218,9 @@ DumpPropertyTree ( const XMP_Node * currNode,
if ( currChild->name == kXMP_ArrayItemName ) OutProcLiteral ( "** bad field name => " );
}
- status = DumpPropertyTree ( currChild, indent+1, itemIndex, outProc, refCon );
- if ( status != 0 ) goto EXIT;
+ DumpPropertyTree ( currChild, indent+1, itemIndex, outProc, refCon );
}
-
-EXIT:
- return status;
} // DumpPropertyTree
@@ -575,6 +447,191 @@ SortWithinOffspring ( XMP_NodeOffspring & nodeVec )
} // SortWithinOffspring
+// -------------------------------------------------------------------------------------------------
+// RegisterAlias
+// -------------
+//
+// Allow 3 kinds of alias:
+// TopProp => TopProp
+// TopProp => TopArray[1]
+// TopProp => TopArray[@xml:lang='x-default']
+//
+// A new alias can be made to something that is already aliased, as long as the net result is one of
+// the legitimate forms. The new alias can already have aliases to it, also as long as result of
+// adjusting all of the exiting aliases leaves them legal.
+//
+// ! The caller assumes all risk that new aliases do not invalidate existing XMPMeta objects. Any
+// ! conflicts will result in later references throwing bad XPath exceptions.
+
+static void
+RegisterAlias ( XMP_StringPtr aliasNS,
+ XMP_StringPtr aliasProp,
+ XMP_StringPtr actualNS,
+ XMP_StringPtr actualProp,
+ XMP_OptionBits arrayForm )
+{
+ XMP_ExpandedXPath expAlias, expActual;
+ XMP_AliasMapPos mapPos;
+ XMP_ExpandedXPath * regActual = 0;
+
+ XMP_Assert ( (aliasNS != 0) && (aliasProp != 0) && (actualNS != 0) && (actualProp != 0) ); // Enforced by wrapper.
+
+ // Expand the alias and actual names, make sure they are one of the basic 3 forms. When counting
+ // the expanded XPath size remember that the schema URI is the first component. We don't have to
+ // compare the schema URIs though, the (unique) prefix is part of the top property name.
+
+ ExpandXPath ( aliasNS, aliasProp, &expAlias );
+ ExpandXPath ( actualNS, actualProp, &expActual );
+ if ( (expAlias.size() != 2) || (expActual.size() != 2) ) {
+ XMP_Throw ( "Alias and actual property names must be simple", kXMPErr_BadXPath );
+ }
+
+ arrayForm = VerifySetOptions ( arrayForm, 0 );
+ if ( arrayForm != 0 ) {
+ if ( (arrayForm & ~kXMP_PropArrayFormMask) != 0 ) XMP_Throw ( "Only array form flags are allowed", kXMPErr_BadOptions );
+ expActual[1].options |= arrayForm; // Set the array form for the top level step.
+ if ( ! (arrayForm & kXMP_PropArrayIsAltText) ) {
+ expActual.push_back ( XPathStepInfo ( "[1]", kXMP_ArrayIndexStep ) );
+ } else {
+ expActual.push_back ( XPathStepInfo ( "[?xml:lang=\"x-default\"]", kXMP_QualSelectorStep ) );
+ }
+ }
+
+ // See if there are any conflicts with existing aliases. A couple of the checks are easy. If the
+ // alias is already aliased it is only OK to reregister an identical alias. If the actual is
+ // already aliased to something else and the new chain is legal, just swap in the old base.
+
+ mapPos = sRegisteredAliasMap->find ( expAlias[kRootPropStep].step );
+ if ( mapPos != sRegisteredAliasMap->end() ) {
+
+ // This alias is already registered to something, make sure it is the same something.
+
+ regActual = &mapPos->second;
+ if ( arrayForm != (mapPos->second[1].options & kXMP_PropArrayFormMask) ) {
+ XMP_Throw ( "Mismatch with existing alias array form", kXMPErr_BadParam );
+ }
+ if ( expActual.size() != regActual->size() ) {
+ XMP_Throw ( "Mismatch with existing actual path", kXMPErr_BadParam );
+ }
+ if ( expActual[kRootPropStep].step != (*regActual)[kRootPropStep].step ) {
+ XMP_Throw ( "Mismatch with existing actual name", kXMPErr_BadParam );
+ }
+ if ( (expActual.size() == 3) && (expActual[kAliasIndexStep].step != (*regActual)[kAliasIndexStep].step) ) {
+ XMP_Throw ( "Mismatch with existing actual array item", kXMPErr_BadParam );
+ }
+ return;
+
+ }
+
+ mapPos = sRegisteredAliasMap->find ( expActual[kRootPropStep].step );
+ if ( mapPos != sRegisteredAliasMap->end() ) {
+
+ // The actual is already aliased to something else.
+
+ regActual = &mapPos->second;
+ if ( expActual.size() == 2 ) {
+ expActual = *regActual; // TopProp => TopProp => anything : substitute the entire old base.
+ } else if ( regActual->size() != 2 ) {
+ XMP_Throw ( "Can't alias an array item to an array item", kXMPErr_BadParam ); // TopProp => TopArray[] => TopArray[] : nope.
+ } else {
+ expActual[kSchemaStep].step = (*regActual)[kSchemaStep].step; // TopProp => TopArray[] => TopProp :
+ expActual[kRootPropStep].step = (*regActual)[kRootPropStep].step; // substitute the old base name.
+ }
+
+ }
+
+ // Checking for existing aliases to this one is touchier. This involves updating the alias map,
+ // which must not be done unless all of the changes are legal. So we need 2 loops, one to verify
+ // that everything is OK, and one to make the changes. The bad case is:
+ // TopProp => TopArray[] => TopArray[]
+ // In the valid cases we back substitute the new base.
+
+ for ( mapPos = sRegisteredAliasMap->begin(); mapPos != sRegisteredAliasMap->end(); ++mapPos ) {
+ regActual = &mapPos->second;
+ if ( expAlias[kRootPropStep].step == (*regActual)[kRootPropStep].step ) {
+ if ( (regActual->size() == 2) && (expAlias.size() == 2) ) {
+ XMP_Throw ( "Can't alias an array item to an array item", kXMPErr_BadParam );
+ }
+ }
+ }
+
+ for ( mapPos = sRegisteredAliasMap->begin(); mapPos != sRegisteredAliasMap->end(); ++mapPos ) {
+ regActual = &mapPos->second;
+ if ( expAlias[kRootPropStep].step == (*regActual)[kRootPropStep].step ) {
+
+ if ( regActual->size() == 1 ) {
+ *regActual = expActual; // TopProp => TopProp => anything : substitute the entire new base.
+ } else {
+ (*regActual)[kSchemaStep].step = expActual[kSchemaStep].step; // TopProp => TopArray[] => TopProp :
+ (*regActual)[kRootPropStep].step = expActual[kRootPropStep].step; // substitute the new base name.
+ }
+
+ }
+ }
+
+ // Finally, all is OK to register the new alias.
+
+ (void) sRegisteredAliasMap->insert ( XMP_AliasMap::value_type ( expAlias[kRootPropStep].step, expActual ) );
+
+} // RegisterAlias
+
+
+// -------------------------------------------------------------------------------------------------
+// RegisterStandardAliases
+// -----------------------
+
+static void
+RegisterStandardAliases()
+{
+
+ // Aliases from XMP to DC.
+ RegisterAlias ( kXMP_NS_XMP, "Author", kXMP_NS_DC, "creator", kXMP_PropArrayIsOrdered );
+ RegisterAlias ( kXMP_NS_XMP, "Authors", kXMP_NS_DC, "creator", 0 );
+ RegisterAlias ( kXMP_NS_XMP, "Description", kXMP_NS_DC, "description", 0 );
+ RegisterAlias ( kXMP_NS_XMP, "Format", kXMP_NS_DC, "format", 0 );
+ RegisterAlias ( kXMP_NS_XMP, "Keywords", kXMP_NS_DC, "subject", 0 );
+ RegisterAlias ( kXMP_NS_XMP, "Locale", kXMP_NS_DC, "language", 0 );
+ RegisterAlias ( kXMP_NS_XMP, "Title", kXMP_NS_DC, "title", 0 );
+ RegisterAlias ( kXMP_NS_XMP_Rights, "Copyright", kXMP_NS_DC, "rights", 0 );
+
+ // Aliases from PDF to DC and XMP.
+ RegisterAlias ( kXMP_NS_PDF, "Author", kXMP_NS_DC, "creator", kXMP_PropArrayIsOrdered );
+ RegisterAlias ( kXMP_NS_PDF, "BaseURL", kXMP_NS_XMP, "BaseURL", 0 );
+ RegisterAlias ( kXMP_NS_PDF, "CreationDate", kXMP_NS_XMP, "CreateDate", 0 );
+ RegisterAlias ( kXMP_NS_PDF, "Creator", kXMP_NS_XMP, "CreatorTool", 0 );
+ RegisterAlias ( kXMP_NS_PDF, "ModDate", kXMP_NS_XMP, "ModifyDate", 0 );
+ RegisterAlias ( kXMP_NS_PDF, "Subject", kXMP_NS_DC, "description", kXMP_PropArrayIsAltText );
+ RegisterAlias ( kXMP_NS_PDF, "Title", kXMP_NS_DC, "title", kXMP_PropArrayIsAltText );
+
+ // Aliases from Photoshop to DC and XMP.
+ RegisterAlias ( kXMP_NS_Photoshop, "Author", kXMP_NS_DC, "creator", kXMP_PropArrayIsOrdered );
+ RegisterAlias ( kXMP_NS_Photoshop, "Caption", kXMP_NS_DC, "description", kXMP_PropArrayIsAltText );
+ RegisterAlias ( kXMP_NS_Photoshop, "Copyright", kXMP_NS_DC, "rights", kXMP_PropArrayIsAltText );
+ RegisterAlias ( kXMP_NS_Photoshop, "Keywords", kXMP_NS_DC, "subject", 0 );
+ RegisterAlias ( kXMP_NS_Photoshop, "Marked", kXMP_NS_XMP_Rights, "Marked", 0 );
+ RegisterAlias ( kXMP_NS_Photoshop, "Title", kXMP_NS_DC, "title", kXMP_PropArrayIsAltText );
+ RegisterAlias ( kXMP_NS_Photoshop, "WebStatement", kXMP_NS_XMP_Rights, "WebStatement", 0 );
+
+ // Aliases from TIFF and EXIF to DC and XMP.
+ RegisterAlias ( kXMP_NS_TIFF, "Artist", kXMP_NS_DC, "creator", kXMP_PropArrayIsOrdered);
+ RegisterAlias ( kXMP_NS_TIFF, "Copyright", kXMP_NS_DC, "rights", 0 );
+ RegisterAlias ( kXMP_NS_TIFF, "DateTime", kXMP_NS_XMP, "ModifyDate", 0 );
+ RegisterAlias ( kXMP_NS_EXIF, "DateTimeDigitized", kXMP_NS_XMP, "CreateDate", 0 );
+ RegisterAlias ( kXMP_NS_TIFF, "ImageDescription", kXMP_NS_DC, "description", 0 );
+ RegisterAlias ( kXMP_NS_TIFF, "Software", kXMP_NS_XMP, "CreatorTool", 0 );
+
+ // Aliases from PNG to DC and XMP.
+ RegisterAlias ( kXMP_NS_PNG, "Author", kXMP_NS_DC, "creator", kXMP_PropArrayIsOrdered);
+ RegisterAlias ( kXMP_NS_PNG, "Copyright", kXMP_NS_DC, "rights", kXMP_PropArrayIsAltText);
+ RegisterAlias ( kXMP_NS_PNG, "CreationTime", kXMP_NS_XMP, "CreateDate", 0 );
+ RegisterAlias ( kXMP_NS_PNG, "Description", kXMP_NS_DC, "description", kXMP_PropArrayIsAltText);
+ RegisterAlias ( kXMP_NS_PNG, "ModificationTime", kXMP_NS_XMP, "ModifyDate", 0 );
+ RegisterAlias ( kXMP_NS_PNG, "Software", kXMP_NS_XMP, "CreatorTool", 0 );
+ RegisterAlias ( kXMP_NS_PNG, "Title", kXMP_NS_DC, "title", kXMP_PropArrayIsAltText);
+
+} // RegisterStandardAliases
+
+
// =================================================================================================
// Constructors
// ============
@@ -634,6 +691,14 @@ XMPMeta::GetVersionInfo ( XMP_VersionInfo * info )
// Initialize
// ----------
+#if XMP_TraceCoreCalls
+ FILE * xmpCoreLog = stderr;
+#endif
+
+#if UseGlobalLibraryLock
+ XMP_BasicMutex sLibraryLock;
+#endif
+
/* class-static */ bool
XMPMeta::Initialize()
{
@@ -642,21 +707,21 @@ XMPMeta::Initialize()
++sXMP_InitCount;
if ( sXMP_InitCount > 1 ) return true;
- #if TraceXMPCalls
- // xmpOut = fopen ( "xmp.out", "w" ); // Coordinate with client glue in WXMP_Common.hpp
- fprintf ( xmpOut, "XMP initializing\n" ); fflush ( xmpOut );
+ #if XMP_TraceCoreCallsToFile
+ xmpCoreLog = fopen ( "XMPCoreLog.txt", "w" );
+ if ( xmpCoreLog == 0 ) xmpCoreLog = stderr;
#endif
- sExceptionMessage = new XMP_VarString();
- XMP_InitMutex ( &sXMPCoreLock );
- sOutputNS = new XMP_VarString;
- sOutputStr = new XMP_VarString;
+ #if UseGlobalLibraryLock
+ InitializeBasicMutex ( sLibraryLock );
+ #endif
+
+ if ( ! Initialize_LibUtils() ) return false;
xdefaultName = new XMP_VarString ( "x-default" );
- sNamespaceURIToPrefixMap = new XMP_StringMap;
- sNamespacePrefixToURIMap = new XMP_StringMap;
- sRegisteredAliasMap = new XMP_AliasMap;
+ sRegisteredNamespaces = new XMP_NamespaceTable;
+ sRegisteredAliasMap = new XMP_AliasMap;
InitializeUnicodeConversions();
@@ -692,6 +757,8 @@ XMPMeta::Initialize()
(void) RegisterNamespace ( kXMP_NS_XMP_Note, "xmpNote", &voidPtr, &voidLen );
(void) RegisterNamespace ( kXMP_NS_DM, "xmpDM", &voidPtr, &voidLen );
+ (void) RegisterNamespace ( kXMP_NS_Script, "xmpScript", &voidPtr, &voidLen );
+ (void) RegisterNamespace ( kXMP_NS_BWF, "bext", &voidPtr, &voidLen );
(void) RegisterNamespace ( kXMP_NS_XMP_Text, "xmpT", &voidPtr, &voidLen );
(void) RegisterNamespace ( kXMP_NS_XMP_PagedFile, "xmpTPg", &voidPtr, &voidLen );
(void) RegisterNamespace ( kXMP_NS_XMP_Graphics, "xmpG", &voidPtr, &voidLen );
@@ -723,7 +790,7 @@ XMPMeta::Initialize()
(void) RegisterNamespace ( "adobe:ns:meta/", "x", &voidPtr, &voidLen );
(void) RegisterNamespace ( "http://ns.adobe.com/iX/1.0/", "iX", &voidPtr, &voidLen );
- XMPMeta::RegisterStandardAliases ( "" );
+ RegisterStandardAliases();
// Initialize the other core classes.
@@ -741,13 +808,13 @@ XMPMeta::Initialize()
XMP_Assert ( sizeof(XMP_Uns64) == 8 );
XMP_Assert ( sizeof(XMP_OptionBits) == 4 ); // Check that option masking work on all 32 bits.
- XMP_OptionBits flag = ~0UL;
+ XMP_OptionBits flag = (XMP_OptionBits) (~0UL);
XMP_Assert ( flag == (XMP_OptionBits)(-1L) );
XMP_Assert ( (flag ^ kXMP_PropHasLang) == 0xFFFFFFBFUL );
XMP_Assert ( (flag & ~kXMP_PropHasLang) == 0xFFFFFFBFUL );
XMP_OptionBits opt1 = 0; // Check the general option bit macros.
- XMP_OptionBits opt2 = ~0UL;
+ XMP_OptionBits opt2 = (XMP_OptionBits)~0UL;
XMP_SetOption ( opt1, kXMP_PropValueIsArray );
XMP_ClearOption ( opt2, kXMP_PropValueIsArray );
XMP_Assert ( opt1 == ~opt2 );
@@ -803,66 +870,31 @@ XMPMeta::Initialize()
// Terminate
// ---------
-#define EliminateGlobal(g) delete ( g ); g = 0
-
/* class-static */ void
XMPMeta::Terminate() RELEASE_NO_THROW
{
--sXMP_InitCount;
- if ( sXMP_InitCount > 0 ) return;
+ if ( sXMP_InitCount != 0 ) return; // Not ready to terminate, or already terminated.
- #if TraceXMPCalls
- fprintf ( xmpOut, "XMP terminating\n" ); fflush ( xmpOut );
- // fclose ( xmpOut ); // Coordinate with fopen in XMPMeta::Initialize.
- #endif
-
XMPIterator::Terminate();
XMPUtils::Terminate();
- EliminateGlobal ( sNamespaceURIToPrefixMap );
- EliminateGlobal ( sNamespacePrefixToURIMap );
+ EliminateGlobal ( sRegisteredNamespaces );
EliminateGlobal ( sRegisteredAliasMap );
EliminateGlobal ( xdefaultName );
- EliminateGlobal ( sOutputNS );
- EliminateGlobal ( sOutputStr );
- EliminateGlobal ( sExceptionMessage );
-
- XMP_TermMutex ( sXMPCoreLock );
-
-} // Terminate
-
-// -------------------------------------------------------------------------------------------------
-// Unlock
-// ------
+ Terminate_LibUtils();
-/* class-static */ void
-XMPMeta::Unlock ( XMP_OptionBits options )
-{
- options = options; // Avoid unused parameter warning. // *** Need IgnoreParam macro.
-
- #if TraceXMPLocking
- fprintf ( xmpOut, " Unlocking XMP toolkit, count = %d\n", sLockCount ); fflush ( xmpOut );
+ #if UseGlobalLibraryLock
+ TerminateBasicMutex ( sLibraryLock );
#endif
- --sLockCount;
- XMP_Assert ( sLockCount == 0 );
- XMP_ExitCriticalRegion ( sXMPCoreLock );
-
-} // Unlock
-
-
-// -------------------------------------------------------------------------------------------------
-// UnlockObject
-// ------------
-
-void
-XMPMeta::UnlockObject ( XMP_OptionBits options ) const
-{
- options = options; // Avoid unused parameter warning.
-
- XMPMeta::Unlock ( 0 );
-
-} // UnlockObject
+
+ #if XMP_TraceCoreCallsToFile
+ if ( xmpCoreLog != stderr ) fclose ( xmpCoreLog );
+ xmpCoreLog = stderr;
+ #endif
+
+} // Terminate
// -------------------------------------------------------------------------------------------------
@@ -877,128 +909,11 @@ XMPMeta::UnlockObject ( XMP_OptionBits options ) const
XMPMeta::DumpNamespaces ( XMP_TextOutputProc outProc,
void * refCon )
{
- XMP_Assert ( outProc != 0 ); // ! Enforced by wrapper.
- XMP_Status status = 0;
-
- XMP_StringMapPos p2uEnd = sNamespacePrefixToURIMap->end(); // ! Move up to avoid gcc complaints.
- XMP_StringMapPos u2pEnd = sNamespaceURIToPrefixMap->end();
-
- status = DumpStringMap ( *sNamespacePrefixToURIMap, "Dumping namespace prefix to URI map", outProc, refCon );
- if ( status != 0 ) goto EXIT;
-
- if ( sNamespacePrefixToURIMap->size() != sNamespaceURIToPrefixMap->size() ) {
- OutProcLiteral ( "** bad namespace map sizes **" );
- XMP_Throw ( "Fatal namespace map problem", kXMPErr_InternalFailure );
- }
-
- for ( XMP_StringMapPos nsLeft = sNamespacePrefixToURIMap->begin(); nsLeft != p2uEnd; ++nsLeft ) {
-
- XMP_StringMapPos nsOther = sNamespaceURIToPrefixMap->find ( nsLeft->second );
- if ( (nsOther == u2pEnd) || (nsLeft != sNamespacePrefixToURIMap->find ( nsOther->second )) ) {
- OutProcLiteral ( " ** bad namespace URI ** " );
- DumpClearString ( nsLeft->second, outProc, refCon );
- goto FAILURE;
- }
-
- for ( XMP_StringMapPos nsRight = nsLeft; nsRight != p2uEnd; ++nsRight ) {
- if ( nsRight == nsLeft ) continue; // ! Can't start at nsLeft+1, no operator+!
- if ( nsLeft->second == nsRight->second ) {
- OutProcLiteral ( " ** duplicate namespace URI ** " );
- DumpClearString ( nsLeft->second, outProc, refCon );
- goto FAILURE;
- }
- }
-
- }
-
- for ( XMP_StringMapPos nsLeft = sNamespaceURIToPrefixMap->begin(); nsLeft != u2pEnd; ++nsLeft ) {
-
- XMP_StringMapPos nsOther = sNamespacePrefixToURIMap->find ( nsLeft->second );
- if ( (nsOther == p2uEnd) || (nsLeft != sNamespaceURIToPrefixMap->find ( nsOther->second )) ) {
- OutProcLiteral ( " ** bad namespace prefix ** " );
- DumpClearString ( nsLeft->second, outProc, refCon );
- goto FAILURE;
- }
-
- for ( XMP_StringMapPos nsRight = nsLeft; nsRight != u2pEnd; ++nsRight ) {
- if ( nsRight == nsLeft ) continue; // ! Can't start at nsLeft+1, no operator+!
- if ( nsLeft->second == nsRight->second ) {
- OutProcLiteral ( " ** duplicate namespace prefix ** " );
- DumpClearString ( nsLeft->second, outProc, refCon );
- goto FAILURE;
- }
- }
-
- }
-EXIT:
- return status;
-
-FAILURE:
- OutProcNewline();
- (void) DumpStringMap ( *sNamespaceURIToPrefixMap, "Dumping namespace URI to prefix map", outProc, refCon );
- XMP_Throw ( "Fatal namespace map problem", kXMPErr_InternalFailure );
+ sRegisteredNamespaces->Dump ( outProc, refCon );
return 0;
-
-} // DumpNamespaces
-
-
-// -------------------------------------------------------------------------------------------------
-// DumpAliases
-// -----------
-
-/* class-static */ XMP_Status
-XMPMeta::DumpAliases ( XMP_TextOutputProc outProc,
- void * refCon )
-{
- XMP_Assert ( outProc != 0 ); // ! Enforced by wrapper.
- XMP_Status status = 0;
-
- XMP_Assert ( sRegisteredAliasMap != 0 );
-
- XMP_cAliasMapPos aliasPos;
- XMP_cAliasMapPos aliasEnd = sRegisteredAliasMap->end();
-
- size_t maxLen = 0;
- for ( aliasPos = sRegisteredAliasMap->begin(); aliasPos != aliasEnd; ++aliasPos ) {
- size_t currLen = aliasPos->first.size();
- if ( currLen > maxLen ) maxLen = currLen;
- }
-
- OutProcLiteral ( "Dumping alias name to actual path map" );
- OutProcNewline();
-
- for ( aliasPos = sRegisteredAliasMap->begin(); aliasPos != aliasEnd; ++aliasPos ) {
- OutProcNChars ( " ", 3 );
- DumpClearString ( aliasPos->first, outProc, refCon );
- OutProcPadding ( maxLen - aliasPos->first.size() );
- OutProcNChars ( " => ", 4 );
-
- size_t actualPathSize = aliasPos->second.size();
- for ( size_t stepNum = 1; stepNum < actualPathSize; ++stepNum ) OutProcString ( aliasPos->second[stepNum].step );
-
- XMP_OptionBits arrayForm = aliasPos->second[1].options & kXMP_PropArrayFormMask;
-
- if ( arrayForm == 0 ) {
- if ( actualPathSize != 2 ) OutProcLiteral ( " ** bad actual path **" );
- } else {
- OutProcNChars ( " ", 2 );
- DumpNodeOptions ( arrayForm, outProc, refCon );
- if ( ! (arrayForm & kXMP_PropValueIsArray) ) OutProcLiteral ( " ** bad array form **" );
- if ( actualPathSize != 3 ) OutProcLiteral ( " ** bad actual path **" );
- }
-
- if ( aliasPos->second[0].options != kXMP_SchemaNode ) OutProcLiteral ( " ** bad schema form **" );
-
- OutProcNewline();
-
- }
-
-EXIT:
- return status;
-
-} // DumpAliases
+} // DumpNamespaces
// -------------------------------------------------------------------------------------------------
@@ -1039,55 +954,9 @@ XMPMeta::RegisterNamespace ( XMP_StringPtr namespaceURI,
XMP_StringPtr * registeredPrefix,
XMP_StringLen * prefixSize )
{
- bool prefixMatches = false;
-
- XMP_Assert ( (registeredPrefix != 0) && (prefixSize != 0) ); // ! Enforced by wrapper.
- if ( (*namespaceURI == 0) || (*suggestedPrefix == 0) ) {
- XMP_Throw ( "Empty namespace URI or prefix", kXMPErr_BadParam );
- }
-
- XMP_VarString nsURI ( namespaceURI );
- XMP_VarString suggPrefix ( suggestedPrefix );
- if ( suggPrefix[suggPrefix.size()-1] != ':' ) suggPrefix += ':';
- VerifySimpleXMLName ( suggestedPrefix, suggestedPrefix+suggPrefix.size()-1 ); // Exclude the colon.
-
- XMP_StringMapPos uriPos = sNamespaceURIToPrefixMap->find ( nsURI );
-
- if ( uriPos == sNamespaceURIToPrefixMap->end() ) {
-
- // The URI is not yet registered, make sure we use a unique prefix.
-
- XMP_VarString uniqPrefix ( suggPrefix );
- int suffix = 0;
- char buffer [32];
-
- while ( true ) {
- if ( sNamespacePrefixToURIMap->find ( uniqPrefix ) == sNamespacePrefixToURIMap->end() ) break;
- ++suffix;
- snprintf ( buffer, sizeof(buffer), "_%d_:", suffix ); // AUDIT: Using sizeof for snprintf length is safe.
- uniqPrefix = suggPrefix;
- uniqPrefix.erase ( uniqPrefix.size()-1 ); // ! Remove the trailing ':'.
- uniqPrefix += buffer;
- }
-
- // Add the new namespace to both maps.
-
- XMP_StringPair newNS ( nsURI, uniqPrefix );
- uriPos = sNamespaceURIToPrefixMap->insert ( sNamespaceURIToPrefixMap->end(), newNS );
-
- newNS.first.swap ( newNS.second );
- (void) sNamespacePrefixToURIMap->insert ( sNamespacePrefixToURIMap->end(), newNS );
- }
-
- // Return the actual prefix and see if it matches the suggested prefix.
-
- *registeredPrefix = uriPos->second.c_str();
- *prefixSize = uriPos->second.size();
-
- prefixMatches = ( uriPos->second == suggPrefix );
- return prefixMatches;
-
+ return sRegisteredNamespaces->Define ( namespaceURI, suggestedPrefix, registeredPrefix, prefixSize );
+
} // RegisterNamespace
@@ -1100,22 +969,9 @@ XMPMeta::GetNamespacePrefix ( XMP_StringPtr namespaceURI,
XMP_StringPtr * namespacePrefix,
XMP_StringLen * prefixSize )
{
- bool found = false;
-
- XMP_Assert ( *namespaceURI != 0 ); // ! Enforced by wrapper.
- XMP_Assert ( (namespacePrefix != 0) && (prefixSize != 0) ); // ! Enforced by wrapper.
- XMP_VarString nsURI ( namespaceURI );
- XMP_StringMapPos uriPos = sNamespaceURIToPrefixMap->find ( nsURI );
-
- if ( uriPos != sNamespaceURIToPrefixMap->end() ) {
- *namespacePrefix = uriPos->second.c_str();
- *prefixSize = uriPos->second.size();
- found = true;
- }
-
- return found;
-
+ return sRegisteredNamespaces->GetPrefix ( namespaceURI, namespacePrefix, prefixSize );
+
} // GetNamespacePrefix
@@ -1128,24 +984,9 @@ XMPMeta::GetNamespaceURI ( XMP_StringPtr namespacePrefix,
XMP_StringPtr * namespaceURI,
XMP_StringLen * uriSize )
{
- bool found = false;
-
- XMP_Assert ( *namespacePrefix != 0 ); // ! Enforced by wrapper.
- XMP_Assert ( (namespacePrefix != 0) && (namespaceURI != 0) ); // ! Enforced by wrapper.
- XMP_VarString nsPrefix ( namespacePrefix );
- if ( nsPrefix[nsPrefix.size()-1] != ':' ) nsPrefix += ':';
-
- XMP_StringMapPos prefixPos = sNamespacePrefixToURIMap->find ( nsPrefix );
-
- if ( prefixPos != sNamespacePrefixToURIMap->end() ) {
- *namespaceURI = prefixPos->second.c_str();
- *uriSize = prefixPos->second.size();
- found = true;
- }
-
- return found;
-
+ return sRegisteredNamespaces->GetURI ( namespacePrefix, namespaceURI, uriSize );
+
} // GetNamespaceURI
@@ -1161,291 +1002,11 @@ XMPMeta::GetNamespaceURI ( XMP_StringPtr namespacePrefix,
XMPMeta::DeleteNamespace ( XMP_StringPtr namespaceURI )
{
- XMP_StringMapPos uriPos = sNamespaceURIToPrefixMap->find ( namespaceURI );
- if ( uriPos == sNamespaceURIToPrefixMap->end() ) return;
-
- XMP_StringMapPos prefixPos = sNamespacePrefixToURIMap->find ( uriPos->second );
- XMP_Assert ( prefixPos != sNamespacePrefixToURIMap->end() );
-
- sNamespaceURIToPrefixMap->erase ( uriPos );
- sNamespacePrefixToURIMap->erase ( prefixPos );
+ XMP_Throw ( "Unimplemented method XMPMeta::DeleteNamespace", kXMPErr_Unimplemented );
} // DeleteNamespace
-// -------------------------------------------------------------------------------------------------
-// RegisterAlias
-// -------------
-//
-// Allow 3 kinds of alias:
-// TopProp => TopProp
-// TopProp => TopArray[1]
-// TopProp => TopArray[@xml:lang='x-default']
-//
-// A new alias can be made to something that is already aliased, as long as the net result is one of
-// the legitimate forms. The new alias can already have aliases to it, also as long as result of
-// adjusting all of the exiting aliases leaves them legal.
-//
-// ! The caller assumes all risk that new aliases do not invalidate existing XMPMeta objects. Any
-// ! conflicts will result in later references throwing bad XPath exceptions.
-
-/* class-static */ void
-XMPMeta::RegisterAlias ( XMP_StringPtr aliasNS,
- XMP_StringPtr aliasProp,
- XMP_StringPtr actualNS,
- XMP_StringPtr actualProp,
- XMP_OptionBits arrayForm )
-{
- XMP_ExpandedXPath expAlias, expActual;
- XMP_AliasMapPos mapPos;
- XMP_ExpandedXPath * regActual = 0;
-
- XMP_Assert ( (aliasNS != 0) && (aliasProp != 0) && (actualNS != 0) && (actualProp != 0) ); // Enforced by wrapper.
-
- // Expand the alias and actual names, make sure they are one of the basic 3 forms. When counting
- // the expanded XPath size remember that the schema URI is the first component. We don't have to
- // compare the schema URIs though, the (unique) prefix is part of the top property name.
-
- ExpandXPath ( aliasNS, aliasProp, &expAlias );
- ExpandXPath ( actualNS, actualProp, &expActual );
- if ( (expAlias.size() != 2) || (expActual.size() != 2) ) {
- XMP_Throw ( "Alias and actual property names must be simple", kXMPErr_BadXPath );
- }
-
- arrayForm = VerifySetOptions ( arrayForm, 0 );
- if ( arrayForm != 0 ) {
- if ( (arrayForm & ~kXMP_PropArrayFormMask) != 0 ) XMP_Throw ( "Only array form flags are allowed", kXMPErr_BadOptions );
- expActual[1].options |= arrayForm; // Set the array form for the top level step.
- if ( ! (arrayForm & kXMP_PropArrayIsAltText) ) {
- expActual.push_back ( XPathStepInfo ( "[1]", kXMP_ArrayIndexStep ) );
- } else {
- expActual.push_back ( XPathStepInfo ( "[?xml:lang=\"x-default\"]", kXMP_QualSelectorStep ) );
- }
- }
-
- // See if there are any conflicts with existing aliases. A couple of the checks are easy. If the
- // alias is already aliased it is only OK to reregister an identical alias. If the actual is
- // already aliased to something else and the new chain is legal, just swap in the old base.
-
- mapPos = sRegisteredAliasMap->find ( expAlias[kRootPropStep].step );
- if ( mapPos != sRegisteredAliasMap->end() ) {
-
- // This alias is already registered to something, make sure it is the same something.
-
- regActual = &mapPos->second;
- if ( arrayForm != (mapPos->second[1].options & kXMP_PropArrayFormMask) ) {
- XMP_Throw ( "Mismatch with existing alias array form", kXMPErr_BadParam );
- }
- if ( expActual.size() != regActual->size() ) {
- XMP_Throw ( "Mismatch with existing actual path", kXMPErr_BadParam );
- }
- if ( expActual[kRootPropStep].step != (*regActual)[kRootPropStep].step ) {
- XMP_Throw ( "Mismatch with existing actual name", kXMPErr_BadParam );
- }
- if ( (expActual.size() == 3) && (expActual[kAliasIndexStep].step != (*regActual)[kAliasIndexStep].step) ) {
- XMP_Throw ( "Mismatch with existing actual array item", kXMPErr_BadParam );
- }
- return;
-
- }
-
- mapPos = sRegisteredAliasMap->find ( expActual[kRootPropStep].step );
- if ( mapPos != sRegisteredAliasMap->end() ) {
-
- // The actual is already aliased to something else.
-
- regActual = &mapPos->second;
- if ( expActual.size() == 2 ) {
- expActual = *regActual; // TopProp => TopProp => anything : substitute the entire old base.
- } else if ( regActual->size() != 2 ) {
- XMP_Throw ( "Can't alias an array item to an array item", kXMPErr_BadParam ); // TopProp => TopArray[] => TopArray[] : nope.
- } else {
- expActual[kSchemaStep].step = (*regActual)[kSchemaStep].step; // TopProp => TopArray[] => TopProp :
- expActual[kRootPropStep].step = (*regActual)[kRootPropStep].step; // substitute the old base name.
- }
-
- }
-
- // Checking for existing aliases to this one is touchier. This involves updating the alias map,
- // which must not be done unless all of the changes are legal. So we need 2 loops, one to verify
- // that everything is OK, and one to make the changes. The bad case is:
- // TopProp => TopArray[] => TopArray[]
- // In the valid cases we back substitute the new base.
-
- for ( mapPos = sRegisteredAliasMap->begin(); mapPos != sRegisteredAliasMap->end(); ++mapPos ) {
- regActual = &mapPos->second;
- if ( expAlias[kRootPropStep].step == (*regActual)[kRootPropStep].step ) {
- if ( (regActual->size() == 2) && (expAlias.size() == 2) ) {
- XMP_Throw ( "Can't alias an array item to an array item", kXMPErr_BadParam );
- }
- }
- }
-
- for ( mapPos = sRegisteredAliasMap->begin(); mapPos != sRegisteredAliasMap->end(); ++mapPos ) {
- regActual = &mapPos->second;
- if ( expAlias[kRootPropStep].step == (*regActual)[kRootPropStep].step ) {
-
- if ( regActual->size() == 1 ) {
- *regActual = expActual; // TopProp => TopProp => anything : substitute the entire new base.
- } else {
- (*regActual)[kSchemaStep].step = expActual[kSchemaStep].step; // TopProp => TopArray[] => TopProp :
- (*regActual)[kRootPropStep].step = expActual[kRootPropStep].step; // substitute the new base name.
- }
-
- }
- }
-
- // Finally, all is OK to register the new alias.
-
- (void) sRegisteredAliasMap->insert ( XMP_AliasMap::value_type ( expAlias[kRootPropStep].step, expActual ) );
-
-} // RegisterAlias
-
-
-// -------------------------------------------------------------------------------------------------
-// ResolveAlias
-// ------------
-
-/* class-static */ bool
-XMPMeta::ResolveAlias ( XMP_StringPtr aliasNS,
- XMP_StringPtr aliasProp,
- XMP_StringPtr * actualNS,
- XMP_StringLen * nsSize,
- XMP_StringPtr * actualProp,
- XMP_StringLen * propSize,
- XMP_OptionBits * arrayForm )
-{
- XMP_Assert ( (aliasNS != 0) && (aliasProp != 0) ); // Enforced by wrapper.
- XMP_Assert ( (actualNS != 0) && (nsSize != 0) && (actualProp != 0) && (propSize != 0) && (arrayForm != 0) ); // Enforced by wrapper.
-
- // Expand the input path and look up the first component in the alias table. Return if not an alias.
-
- XMP_ExpandedXPath fullPath, minPath;
- ExpandXPath ( aliasNS, aliasProp, &fullPath );
- XMP_Assert ( fullPath.size() >= 2 );
-
- minPath.push_back ( fullPath[kSchemaStep] );
- minPath.push_back ( fullPath[kRootPropStep] );
- XMP_AliasMapPos mapPos = sRegisteredAliasMap->find ( minPath[kRootPropStep].step );
- if ( mapPos == sRegisteredAliasMap->end() ) return false;
-
- // Replace the alias portion of the full expanded path. Compose the output path string.
-
- const XMP_ExpandedXPath & actualPath = mapPos->second;
-
- fullPath[kSchemaStep] = actualPath[kSchemaStep];
- fullPath[kRootPropStep] = actualPath[kRootPropStep];
- if ( actualPath.size() > 2 ) { // This is an alias to an array item.
- XMP_ExpandedXPathPos insertPos = fullPath.begin() + kAliasIndexStep;
- fullPath.insert ( insertPos, actualPath[kAliasIndexStep] );
- }
-
- *sOutputNS = fullPath[kSchemaStep].step;
- *actualNS = sOutputNS->c_str();
- *nsSize = sOutputNS->size();
-
- ComposeXPath ( fullPath, sOutputStr );
- *actualProp = sOutputStr->c_str();
- *propSize = sOutputStr->size();
-
- *arrayForm = actualPath[kRootPropStep].options & kXMP_PropArrayFormMask;
-
- #if XMP_DebugBuild // Test that the output string is valid and unchanged by round trip expand/compose.
- XMP_ExpandedXPath rtPath;
- ExpandXPath ( *actualNS, *actualProp, &rtPath );
- std::string rtString;
- ComposeXPath ( rtPath, &rtString );
- XMP_Assert ( rtString == *sOutputStr );
- #endif
-
- return true;
-
-} // ResolveAlias
-
-
-// -------------------------------------------------------------------------------------------------
-// DeleteAlias
-// -----------
-
-/* class-static */ void
-XMPMeta::DeleteAlias ( XMP_StringPtr aliasNS,
- XMP_StringPtr aliasProp )
-{
-
- XMP_Assert ( (aliasNS != 0) && (aliasProp != 0) ); // Enforced by wrapper.
- XMP_Throw ( "Unimplemented method XMPMeta::DeleteAlias", kXMPErr_Unimplemented ); // *** #error "write me"
- void * p; p = &aliasNS; p = &aliasProp; // Avoid unused param warnings.
-
-} // DeleteAlias
-
-
-// -------------------------------------------------------------------------------------------------
-// RegisterStandardAliases
-// -----------------------
-
-/* class-static */ void
-XMPMeta::RegisterStandardAliases ( XMP_StringPtr schemaNS )
-{
- XMP_Assert ( schemaNS != 0 ); // Enforced by wrapper.
-
- const bool doAll = (*schemaNS == 0);
-
- if ( doAll || XMP_LitMatch ( schemaNS, kXMP_NS_XMP ) ) {
- // Aliases from XMP to DC.
- XMPMeta::RegisterAlias ( kXMP_NS_XMP, "Author", kXMP_NS_DC, "creator", kXMP_PropArrayIsOrdered );
- XMPMeta::RegisterAlias ( kXMP_NS_XMP, "Authors", kXMP_NS_DC, "creator", 0 );
- XMPMeta::RegisterAlias ( kXMP_NS_XMP, "Description", kXMP_NS_DC, "description", 0 );
- XMPMeta::RegisterAlias ( kXMP_NS_XMP, "Format", kXMP_NS_DC, "format", 0 );
- XMPMeta::RegisterAlias ( kXMP_NS_XMP, "Keywords", kXMP_NS_DC, "subject", 0 );
- XMPMeta::RegisterAlias ( kXMP_NS_XMP, "Locale", kXMP_NS_DC, "language", 0 );
- XMPMeta::RegisterAlias ( kXMP_NS_XMP, "Title", kXMP_NS_DC, "title", 0 );
- XMPMeta::RegisterAlias ( kXMP_NS_XMP_Rights, "Copyright", kXMP_NS_DC, "rights", 0 );
- }
-
- if ( doAll || XMP_LitMatch ( schemaNS, kXMP_NS_PDF ) ) {
- // Aliases from PDF to DC and XMP.
- XMPMeta::RegisterAlias ( kXMP_NS_PDF, "Author", kXMP_NS_DC, "creator", kXMP_PropArrayIsOrdered );
- XMPMeta::RegisterAlias ( kXMP_NS_PDF, "BaseURL", kXMP_NS_XMP, "BaseURL", 0 );
- XMPMeta::RegisterAlias ( kXMP_NS_PDF, "CreationDate", kXMP_NS_XMP, "CreateDate", 0 );
- XMPMeta::RegisterAlias ( kXMP_NS_PDF, "Creator", kXMP_NS_XMP, "CreatorTool", 0 );
- XMPMeta::RegisterAlias ( kXMP_NS_PDF, "ModDate", kXMP_NS_XMP, "ModifyDate", 0 );
- XMPMeta::RegisterAlias ( kXMP_NS_PDF, "Subject", kXMP_NS_DC, "description", kXMP_PropArrayIsAltText );
- XMPMeta::RegisterAlias ( kXMP_NS_PDF, "Title", kXMP_NS_DC, "title", kXMP_PropArrayIsAltText );
- }
-
- if ( doAll || XMP_LitMatch ( schemaNS, kXMP_NS_Photoshop ) ) {
- // Aliases from PHOTOSHOP to DC and XMP.
- XMPMeta::RegisterAlias ( kXMP_NS_Photoshop, "Author", kXMP_NS_DC, "creator", kXMP_PropArrayIsOrdered );
- XMPMeta::RegisterAlias ( kXMP_NS_Photoshop, "Caption", kXMP_NS_DC, "description", kXMP_PropArrayIsAltText );
- XMPMeta::RegisterAlias ( kXMP_NS_Photoshop, "Copyright", kXMP_NS_DC, "rights", kXMP_PropArrayIsAltText );
- XMPMeta::RegisterAlias ( kXMP_NS_Photoshop, "Keywords", kXMP_NS_DC, "subject", 0 );
- XMPMeta::RegisterAlias ( kXMP_NS_Photoshop, "Marked", kXMP_NS_XMP_Rights, "Marked", 0 );
- XMPMeta::RegisterAlias ( kXMP_NS_Photoshop, "Title", kXMP_NS_DC, "title", kXMP_PropArrayIsAltText );
- XMPMeta::RegisterAlias ( kXMP_NS_Photoshop, "WebStatement", kXMP_NS_XMP_Rights, "WebStatement", 0 );
- }
-
- if ( doAll || XMP_LitMatch ( schemaNS, kXMP_NS_TIFF ) || XMP_LitMatch ( schemaNS, kXMP_NS_EXIF ) ) {
- // Aliases from TIFF and EXIF to DC and XMP.
- XMPMeta::RegisterAlias ( kXMP_NS_TIFF, "Artist", kXMP_NS_DC, "creator", kXMP_PropArrayIsOrdered);
- XMPMeta::RegisterAlias ( kXMP_NS_TIFF, "Copyright", kXMP_NS_DC, "rights", 0 );
- XMPMeta::RegisterAlias ( kXMP_NS_TIFF, "DateTime", kXMP_NS_XMP, "ModifyDate", 0 );
- XMPMeta::RegisterAlias ( kXMP_NS_TIFF, "ImageDescription", kXMP_NS_DC, "description", 0 );
- XMPMeta::RegisterAlias ( kXMP_NS_TIFF, "Software", kXMP_NS_XMP, "CreatorTool", 0 );
- }
-
- if ( doAll || XMP_LitMatch ( schemaNS, kXMP_NS_PNG ) ) { // ! From Acrobat ImageCapture:
- XMPMeta::RegisterAlias ( kXMP_NS_PNG, "Author", kXMP_NS_DC, "creator", kXMP_PropArrayIsOrdered);
- XMPMeta::RegisterAlias ( kXMP_NS_PNG, "Copyright", kXMP_NS_DC, "rights", kXMP_PropArrayIsAltText);
- XMPMeta::RegisterAlias ( kXMP_NS_PNG, "CreationTime", kXMP_NS_XMP, "CreateDate", 0 );
- XMPMeta::RegisterAlias ( kXMP_NS_PNG, "Description", kXMP_NS_DC, "description", kXMP_PropArrayIsAltText);
- XMPMeta::RegisterAlias ( kXMP_NS_PNG, "ModificationTime", kXMP_NS_XMP, "ModifyDate", 0 );
- XMPMeta::RegisterAlias ( kXMP_NS_PNG, "Software", kXMP_NS_XMP, "CreatorTool", 0 );
- XMPMeta::RegisterAlias ( kXMP_NS_PNG, "Title", kXMP_NS_DC, "title", kXMP_PropArrayIsAltText);
- }
-
-} // RegisterStandardAliases
-
-
// =================================================================================================
// Class Methods
// =============
@@ -1458,18 +1019,16 @@ XMPMeta::RegisterStandardAliases ( XMP_StringPtr schemaNS )
// DumpObject
// ----------
-XMP_Status
+void
XMPMeta::DumpObject ( XMP_TextOutputProc outProc,
void * refCon ) const
{
XMP_Assert ( outProc != 0 ); // ! Enforced by wrapper.
- XMP_Status status = 0;
OutProcLiteral ( "Dumping XMPMeta object \"" );
DumpClearString ( tree.name, outProc, refCon );
OutProcNChars ( "\" ", 3 );
- status = DumpNodeOptions ( tree.options, outProc, refCon );
- if ( status != 0 ) goto EXIT;
+ DumpNodeOptions ( tree.options, outProc, refCon );
#if 0 // *** XMP_DebugBuild
if ( (tree._namePtr != tree.name.c_str()) ||
(tree._valuePtr != tree.value.c_str()) ) OutProcLiteral ( " ** bad debug string **" );
@@ -1487,7 +1046,7 @@ XMPMeta::DumpObject ( XMP_TextOutputProc outProc,
OutProcLiteral ( "** bad root qualifiers **" );
OutProcNewline();
for ( size_t qualNum = 0, qualLim = tree.qualifiers.size(); qualNum < qualLim; ++qualNum ) {
- status = DumpPropertyTree ( tree.qualifiers[qualNum], 3, 0, outProc, refCon );
+ DumpPropertyTree ( tree.qualifiers[qualNum], 3, 0, outProc, refCon );
}
}
@@ -1503,8 +1062,7 @@ XMPMeta::DumpObject ( XMP_TextOutputProc outProc,
OutProcNChars ( " ", 2 );
DumpClearString ( currSchema->name, outProc, refCon );
OutProcNChars ( " ", 2 );
- status = DumpNodeOptions ( currSchema->options, outProc, refCon );
- if ( status != 0 ) goto EXIT;
+ DumpNodeOptions ( currSchema->options, outProc, refCon );
#if 0 // *** XMP_DebugBuild
if ( (currSchema->_namePtr != currSchema->name.c_str()) ||
(currSchema->_valuePtr != currSchema->value.c_str()) ) OutProcLiteral ( " ** bad debug string **" );
@@ -1531,9 +1089,6 @@ XMPMeta::DumpObject ( XMP_TextOutputProc outProc,
}
}
-
-EXIT:
- return status;
} // DumpObject
diff --git a/source/XMPCore/XMPMeta.hpp b/source/XMPCore/XMPMeta.hpp
index dca2c2a..e2d1693 100644
--- a/source/XMPCore/XMPMeta.hpp
+++ b/source/XMPCore/XMPMeta.hpp
@@ -2,7 +2,7 @@
#define __XMPMeta_hpp__
// =================================================================================================
-// Copyright 2002-2008 Adobe Systems Incorporated
+// Copyright 2003 Adobe Systems Incorporated
// All Rights Reserved.
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -20,7 +20,7 @@
#define DumpXMLParseTree 0
#endif
-extern XMP_VarString * xdefaultName;
+extern XMP_VarString * xdefaultName; // Needed in XMPMeta-Parse.cpp, MoveExplicitAliases.
class XMPIterator;
class XMPUtils;
@@ -37,9 +37,6 @@ public:
Initialize();
static void
Terminate() RELEASE_NO_THROW;
-
- static void
- Unlock ( XMP_OptionBits options );
// ---------------------------------------------------------------------------------------------
@@ -61,10 +58,6 @@ public:
DumpNamespaces ( XMP_TextOutputProc outProc,
void * refCon );
- static XMP_Status
- DumpAliases ( XMP_TextOutputProc outProc,
- void * refCon );
-
// ---------------------------------------------------------------------------------------------
static bool
@@ -88,36 +81,6 @@ public:
// ---------------------------------------------------------------------------------------------
- static void
- RegisterAlias ( XMP_StringPtr aliasNS,
- XMP_StringPtr aliasProp,
- XMP_StringPtr actualNS,
- XMP_StringPtr actualProp,
- XMP_OptionBits arrayForm );
-
- static bool
- ResolveAlias ( XMP_StringPtr aliasNS,
- XMP_StringPtr aliasProp,
- XMP_StringPtr * actualNS,
- XMP_StringLen * nsSize,
- XMP_StringPtr * actualProp,
- XMP_StringLen * propSize,
- XMP_OptionBits * arrayForm );
-
- static void
- DeleteAlias ( XMP_StringPtr aliasNS,
- XMP_StringPtr aliasProp );
-
- static void
- RegisterStandardAliases ( XMP_StringPtr schemaNS );
-
- // ---------------------------------------------------------------------------------------------
-
- void
- UnlockObject ( XMP_OptionBits options ) const;
-
- // ---------------------------------------------------------------------------------------------
-
bool
GetProperty ( XMP_StringPtr schemaNS,
XMP_StringPtr propName,
@@ -256,6 +219,12 @@ public:
XMP_StringPtr itemValue,
XMP_OptionBits options );
+ void
+ DeleteLocalizedText ( XMP_StringPtr schemaNS,
+ XMP_StringPtr altTextName,
+ XMP_StringPtr genericLang,
+ XMP_StringPtr specificLang);
+
// ---------------------------------------------------------------------------------------------
bool
@@ -348,7 +317,7 @@ public:
CountArrayItems ( XMP_StringPtr schemaNS,
XMP_StringPtr arrayName ) const;
- XMP_Status
+ void
DumpObject ( XMP_TextOutputProc outProc,
void * refCon ) const;
@@ -360,8 +329,7 @@ public:
XMP_OptionBits options );
void
- SerializeToBuffer ( XMP_StringPtr * rdfString,
- XMP_StringLen * rdfSize,
+ SerializeToBuffer ( XMP_VarString * rdfString,
XMP_OptionBits options,
XMP_StringLen padding,
XMP_StringPtr newline,
@@ -396,6 +364,10 @@ public:
// ! Expose the implementation so that file static functions can see the data.
XMP_Int32 clientRefs; // ! Must be signed to allow decrement from 0.
+ XMP_ReadWriteLock lock;
+
+ // ! Any data member changes must be propagted to the Clone function!
+
XMP_Int32 prevTkVer; // Previous toolkit version as MMmmuubbb (major, minor, micro, build).
XMP_Node tree;
diff --git a/source/XMPCore/XMPUtils-FileInfo.cpp b/source/XMPCore/XMPUtils-FileInfo.cpp
index ebfb9a5..c8d1e90 100644
--- a/source/XMPCore/XMPUtils-FileInfo.cpp
+++ b/source/XMPCore/XMPUtils-FileInfo.cpp
@@ -1,5 +1,5 @@
// =================================================================================================
-// Copyright 2002-2008 Adobe Systems Incorporated
+// Copyright 2003 Adobe Systems Incorporated
// All Rights Reserved.
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -11,6 +11,8 @@
#include "XMPUtils.hpp"
+#include <algorithm> // For binary_search.
+
#include <time.h>
#include <string.h>
#include <stdlib.h>
@@ -431,6 +433,47 @@ ApplyQuotes ( XMP_VarString * item, UniCodePoint openQuote, UniCodePoint closeQu
// *** Need static checks of the schema prefixes!
+static const char * kExternalxmpDM[] =
+ { "xmpDM:album",
+ "xmpDM:altTapeName",
+ "xmpDM:altTimecode",
+ "xmpDM:artist",
+ "xmpDM:cameraAngle",
+ "xmpDM:cameraLabel",
+ "xmpDM:cameraModel",
+ "xmpDM:cameraMove",
+ "xmpDM:client",
+ "xmpDM:comment",
+ "xmpDM:composer",
+ "xmpDM:director",
+ "xmpDM:directorPhotography",
+ "xmpDM:engineer",
+ "xmpDM:genre",
+ "xmpDM:good",
+ "xmpDM:instrument",
+ "xmpDM:logComment",
+ "xmpDM:projectName",
+ "xmpDM:releaseDate",
+ "xmpDM:scene",
+ "xmpDM:shotDate",
+ "xmpDM:shotDay",
+ "xmpDM:shotLocation",
+ "xmpDM:shotName",
+ "xmpDM:shotNumber",
+ "xmpDM:shotSize",
+ "xmpDM:speakerPlacement",
+ "xmpDM:takeNumber",
+ "xmpDM:tapeName",
+ "xmpDM:trackNumber",
+ "xmpDM:videoAlphaMode",
+ "xmpDM:videoAlphaPremultipleColor",
+ 0 }; // ! Must have zero sentinel!
+
+typedef const char ** CharStarIterator; // Used for binary search of kExternalxmpDM;
+static const char ** kLastExternalxmpDM = 0; // Set on first use.
+static int CharStarLess (const char * left, const char * right )
+ { return (strcmp ( left, right ) < 0); }
+
#define IsExternalProperty(s,p) (! IsInternalProperty ( s, p ))
static bool
@@ -490,12 +533,28 @@ IsInternalProperty ( const XMP_VarString & schema, const XMP_VarString & prop )
} else if ( schema == kXMP_NS_CameraRaw ) {
- if ( (prop == "crs:Version") ||
- (prop == "crs:RawFileName") ||
- (prop == "crs:ToneCurveName") ) {
- isInternal = true;
+ isInternal = true; // All of crs: is internal, they are processing settings.
+
+ } else if ( schema == kXMP_NS_DM ) {
+
+ // ! Most of the xmpDM schema is internal, and unknown properties default to internal.
+ if ( kLastExternalxmpDM == 0 ) {
+ for ( kLastExternalxmpDM = &kExternalxmpDM[0]; *kLastExternalxmpDM != 0; ++kLastExternalxmpDM ) {}
}
+ isInternal = (! std::binary_search ( &kExternalxmpDM[0], kLastExternalxmpDM, prop.c_str(), CharStarLess ));
+
+ } else if ( schema == kXMP_NS_Script ) {
+
+ isInternal = true; // ! Most of the xmpScript schema is internal, and unknown properties default to internal.
+ if ( (prop == "xmpScript:action") || (prop == "xmpScript:character") || (prop == "xmpScript:dialog") ||
+ (prop == "xmpScript:sceneSetting") || (prop == "xmpScript:sceneTimeOfDay") ) {
+ isInternal = false;
+ }
+
+ } else if ( schema == kXMP_NS_BWF ) {
+ if ( prop == "bext:version" ) isInternal = true;
+
} else if ( schema == kXMP_NS_AdobeStockPhoto ) {
isInternal = true; // ! The bmsp schema has only internal properties.
@@ -634,127 +693,153 @@ ItemValuesMatch ( const XMP_Node * leftNode, const XMP_Node * rightNode )
// The main implementation of XMPUtils::AppendProperties. See the description in TXMPMeta.hpp.
static void
-AppendSubtree ( const XMP_Node * sourceNode, XMP_Node * destParent, const bool replaceOld, const bool deleteEmpty )
+AppendSubtree ( const XMP_Node * sourceNode, XMP_Node * destParent,
+ const bool mergeCompound, const bool replaceOld, const bool deleteEmpty )
{
XMP_NodePtrPos destPos;
XMP_Node * destNode = FindChildNode ( destParent, sourceNode->name.c_str(), kXMP_ExistingOnly, &destPos );
bool valueIsEmpty = false;
- if ( deleteEmpty ) {
- if ( XMP_PropIsSimple ( sourceNode->options ) ) {
- valueIsEmpty = sourceNode->value.empty();
- } else {
- valueIsEmpty = sourceNode->children.empty();
- }
+ if ( XMP_PropIsSimple ( sourceNode->options ) ) {
+ valueIsEmpty = sourceNode->value.empty();
+ } else {
+ valueIsEmpty = sourceNode->children.empty();
}
-
- if ( deleteEmpty & valueIsEmpty ) {
-
- if ( destNode != 0 ) {
+
+ if ( valueIsEmpty ) {
+ if ( deleteEmpty && (destNode != 0) ) {
delete ( destNode );
destParent->children.erase ( destPos );
}
+ return; // ! Done, empty values are either ignored or cause deletions.
+ }
- } else if ( destNode == 0 ) {
-
+ if ( destNode == 0 ) {
// The one easy case, the destination does not exist.
- CloneSubtree ( sourceNode, destParent );
+ destNode = CloneSubtree ( sourceNode, destParent, true /* skipEmpty */ );
+ XMP_Assert ( (destNode == 0) || (! destNode->value.empty()) || (! destNode->children.empty()) );
+ return;
+ }
+
+ // If we get here we're going to modify an existing property, either replacing or merging.
+
+ XMP_Assert ( (! valueIsEmpty) && (destNode != 0) );
- } else if ( replaceOld ) {
+ XMP_OptionBits sourceForm = sourceNode->options & kXMP_PropCompositeMask;
+ XMP_OptionBits destForm = destNode->options & kXMP_PropCompositeMask;
- // The destination exists and should be replaced.
+ bool replaceThis = replaceOld; // ! Don't modify replaceOld, it gets passed to inner calls.
+ if ( mergeCompound && (! XMP_PropIsSimple ( sourceForm )) ) replaceThis = false;
+
+ if ( replaceThis ) {
destNode->value = sourceNode->value; // *** Should use SetNode.
destNode->options = sourceNode->options;
destNode->RemoveChildren();
destNode->RemoveQualifiers();
- CloneOffspring ( sourceNode, destNode );
+ CloneOffspring ( sourceNode, destNode, true /* skipEmpty */ );
+
+ if ( (! XMP_PropIsSimple ( destNode->options )) && destNode->children.empty() ) {
+ // Don't keep an empty array or struct. The source might be implicitly empty due to
+ // all children being empty. In this case CloneOffspring should skip them.
+ DeleteSubtree ( destPos );
+ }
- #if 0 // *** XMP_DebugBuild
- destNode->_valuePtr = destNode->value.c_str();
- #endif
-
- } else {
+ return;
- // The destination exists and is not totally replaced. Structs and arrays are merged.
+ }
+
+ // From here on are cases for merging arrays or structs.
+
+ if ( XMP_PropIsSimple ( sourceForm ) || (sourceForm != destForm) ) return;
+
+ if ( sourceForm == kXMP_PropValueIsStruct ) {
+
+ // To merge a struct process the fields recursively. E.g. add simple missing fields. The
+ // recursive call to AppendSubtree will handle deletion for fields with empty values.
- XMP_OptionBits sourceForm = sourceNode->options & kXMP_PropCompositeMask;
- XMP_OptionBits destForm = destNode->options & kXMP_PropCompositeMask;
- if ( sourceForm != destForm ) return;
-
- if ( sourceForm == kXMP_PropValueIsStruct ) {
-
- // To merge a struct process the fields recursively. E.g. add simple missing fields. The
- // recursive call to AppendSubtree will handle deletion for fields with empty values.
-
- for ( size_t sourceNum = 0, sourceLim = sourceNode->children.size(); sourceNum != sourceLim; ++sourceNum ) {
- const XMP_Node * sourceField = sourceNode->children[sourceNum];
- AppendSubtree ( sourceField, destNode, replaceOld, deleteEmpty );
- if ( deleteEmpty && destNode->children.empty() ) {
- delete ( destNode );
- destParent->children.erase ( destPos );
- }
+ for ( size_t sourceNum = 0, sourceLim = sourceNode->children.size(); sourceNum != sourceLim; ++sourceNum ) {
+ const XMP_Node * sourceField = sourceNode->children[sourceNum];
+ AppendSubtree ( sourceField, destNode, mergeCompound, replaceOld, deleteEmpty );
+ if ( deleteEmpty && destNode->children.empty() ) {
+ delete ( destNode );
+ destParent->children.erase ( destPos );
}
-
- } else if ( sourceForm & kXMP_PropArrayIsAltText ) {
+ }
- // Merge AltText arrays by the xml:lang qualifiers. Make sure x-default is first. Make a
- // special check for deletion of empty values. Meaningful in AltText arrays because the
- // xml:lang qualifier provides unambiguous source/dest correspondence.
+ } else if ( sourceForm & kXMP_PropArrayIsAltText ) {
+
+ // Merge AltText arrays by the xml:lang qualifiers. Make sure x-default is first. Make a
+ // special check for deletion of empty values. Meaningful in AltText arrays because the
+ // xml:lang qualifier provides unambiguous source/dest correspondence.
+
+ XMP_Assert ( mergeCompound );
- for ( size_t sourceNum = 0, sourceLim = sourceNode->children.size(); sourceNum != sourceLim; ++sourceNum ) {
+ for ( size_t sourceNum = 0, sourceLim = sourceNode->children.size(); sourceNum != sourceLim; ++sourceNum ) {
- const XMP_Node * sourceItem = sourceNode->children[sourceNum];
- if ( sourceItem->qualifiers.empty() || (sourceItem->qualifiers[0]->name != "xml:lang") ) continue;
-
- XMP_Index destIndex = LookupLangItem ( destNode, sourceItem->qualifiers[0]->value );
-
- if ( deleteEmpty && sourceItem->value.empty() ) {
-
- if ( destIndex != -1 ) {
- delete ( destNode->children[destIndex] );
- destNode->children.erase ( destNode->children.begin() + destIndex );
- if ( destNode->children.empty() ) {
- delete ( destNode );
- destParent->children.erase ( destPos );
- }
+ const XMP_Node * sourceItem = sourceNode->children[sourceNum];
+ if ( sourceItem->qualifiers.empty() || (sourceItem->qualifiers[0]->name != "xml:lang") ) continue;
+
+ XMP_Index destIndex = LookupLangItem ( destNode, sourceItem->qualifiers[0]->value );
+
+ if ( sourceItem->value.empty() ) {
+
+ if ( deleteEmpty && (destIndex != -1) ) {
+ delete ( destNode->children[destIndex] );
+ destNode->children.erase ( destNode->children.begin() + destIndex );
+ if ( destNode->children.empty() ) {
+ delete ( destNode );
+ destParent->children.erase ( destPos );
}
+ }
- } else {
+ } else {
+
+ if ( destIndex != -1 ) {
- if ( destIndex != -1 ) continue; // Not replacing, keep the existing item.
+ // The source and dest arrays both have this language item.
+
+ if ( replaceOld ) { // ! Yes, check replaceOld not replaceThis!
+ destNode->children[destIndex]->value = sourceItem->value;
+ }
+
+ } else {
+ // The dest array does not have this language item, add it.
+
if ( (sourceItem->qualifiers[0]->value != "x-default") || destNode->children.empty() ) {
- CloneSubtree ( sourceItem, destNode );
+ // Typical case, empty dest array or not "x-default". Non-empty should always have "x-default".
+ CloneSubtree ( sourceItem, destNode, true /* skipEmpty */ );
} else {
+ // Edge case, non-empty dest array had no "x-default", insert that at the beginning.
XMP_Node * destItem = new XMP_Node ( destNode, sourceItem->name, sourceItem->value, sourceItem->options );
- CloneOffspring ( sourceItem, destItem );
+ CloneOffspring ( sourceItem, destItem, true /* skipEmpty */ );
destNode->children.insert ( destNode->children.begin(), destItem );
- }
+ }
}
-
+
}
-
- } else if ( sourceForm & kXMP_PropValueIsArray ) {
-
- // Merge other arrays by item values. Don't worry about order or duplicates. Source
- // items with empty values do not cause deletion, that conflicts horribly with merging.
- for ( size_t sourceNum = 0, sourceLim = sourceNode->children.size(); sourceNum != sourceLim; ++sourceNum ) {
- const XMP_Node * sourceItem = sourceNode->children[sourceNum];
+ }
+
+ } else if ( sourceForm & kXMP_PropValueIsArray ) {
+
+ // Merge other arrays by item values. Don't worry about order or duplicates. Source
+ // items with empty values do not cause deletion, that conflicts horribly with merging.
- size_t destNum, destLim;
- for ( destNum = 0, destLim = destNode->children.size(); destNum != destLim; ++destNum ) {
- const XMP_Node * destItem = destNode->children[destNum];
- if ( ItemValuesMatch ( sourceItem, destItem ) ) break;
- }
- if ( destNum == destLim ) CloneSubtree ( sourceItem, destNode );
+ for ( size_t sourceNum = 0, sourceLim = sourceNode->children.size(); sourceNum != sourceLim; ++sourceNum ) {
+ const XMP_Node * sourceItem = sourceNode->children[sourceNum];
+ size_t destNum, destLim;
+ for ( destNum = 0, destLim = destNode->children.size(); destNum != destLim; ++destNum ) {
+ const XMP_Node * destItem = destNode->children[destNum];
+ if ( ItemValuesMatch ( sourceItem, destItem ) ) break;
}
-
- }
+ if ( destNum == destLim ) CloneSubtree ( sourceItem, destNode, true /* skipEmpty */ );
+ }
+
}
} // AppendSubtree
@@ -775,11 +860,10 @@ XMPUtils::CatenateArrayItems ( const XMPMeta & xmpObj,
XMP_StringPtr separator,
XMP_StringPtr quotes,
XMP_OptionBits options,
- XMP_StringPtr * catedStr,
- XMP_StringLen * catedLen )
+ XMP_VarString * catedStr )
{
XMP_Assert ( (schemaNS != 0) && (arrayName != 0) ); // ! Enforced by wrapper.
- XMP_Assert ( (separator != 0) && (quotes != 0) && (catedStr != 0) && (catedLen != 0) ); // ! Enforced by wrapper.
+ XMP_Assert ( (separator != 0) && (quotes != 0) && (catedStr != 0) ); // ! Enforced by wrapper.
size_t strLen, strPos, charLen;
UniCharKind charKind;
@@ -828,19 +912,19 @@ XMPUtils::CatenateArrayItems ( const XMPMeta & xmpObj,
// Return an empty result if the array does not exist, hurl if it isn't the right form.
- sCatenatedItems->erase();
+ catedStr->erase();
XMP_ExpandedXPath arrayPath;
ExpandXPath ( schemaNS, arrayName, &arrayPath );
arrayNode = FindConstNode ( &xmpObj.tree, arrayPath );
- if ( arrayNode == 0 ) goto EXIT; // ! Need to set the output pointer and length.
+ if ( arrayNode == 0 ) return;
arrayForm = arrayNode->options & kXMP_PropCompositeMask;
if ( (! (arrayForm & kXMP_PropValueIsArray)) || (arrayForm & kXMP_PropArrayIsAlternate) ) {
XMP_Throw ( "Named property must be non-alternate array", kXMPErr_BadParam );
}
- if ( arrayNode->children.empty() ) goto EXIT; // ! Need to set the output pointer and length.
+ if ( arrayNode->children.empty() ) return;
// Build the result, quoting the array items, adding separators. Hurl if any item isn't simple.
// Start the result with the first value, then add the rest with a preceeding separator.
@@ -848,21 +932,17 @@ XMPUtils::CatenateArrayItems ( const XMPMeta & xmpObj,
currItem = arrayNode->children[0];
if ( (currItem->options & kXMP_PropCompositeMask) != 0 ) XMP_Throw ( "Array items must be simple", kXMPErr_BadParam );
- *sCatenatedItems = currItem->value;
- ApplyQuotes ( sCatenatedItems, openQuote, closeQuote, allowCommas );
+ *catedStr = currItem->value;
+ ApplyQuotes ( catedStr, openQuote, closeQuote, allowCommas );
for ( size_t itemNum = 1, itemLim = arrayNode->children.size(); itemNum != itemLim; ++itemNum ) {
const XMP_Node * currItem = arrayNode->children[itemNum];
if ( (currItem->options & kXMP_PropCompositeMask) != 0 ) XMP_Throw ( "Array items must be simple", kXMPErr_BadParam );
XMP_VarString tempStr ( currItem->value );
ApplyQuotes ( &tempStr, openQuote, closeQuote, allowCommas );
- *sCatenatedItems += separator;
- *sCatenatedItems += tempStr;
+ *catedStr += separator;
+ *catedStr += tempStr;
}
-
-EXIT:
- *catedStr = sCatenatedItems->c_str();
- *catedLen = sCatenatedItems->size();
} // CatenateArrayItems
@@ -1037,6 +1117,120 @@ XMPUtils::SeparateArrayItems ( XMPMeta * xmpObj,
// -------------------------------------------------------------------------------------------------
+// ApplyTemplate
+// -------------
+
+/* class static */ void
+XMPUtils::ApplyTemplate ( XMPMeta * workingXMP,
+ const XMPMeta & templateXMP,
+ XMP_OptionBits actions )
+{
+ bool doClear = XMP_OptionIsSet ( actions, kXMPTemplate_ClearUnnamedProperties );
+ bool doAdd = XMP_OptionIsSet ( actions, kXMPTemplate_AddNewProperties );
+ bool doReplace = XMP_OptionIsSet ( actions, kXMPTemplate_ReplaceExistingProperties );
+
+ bool deleteEmpty = XMP_OptionIsSet ( actions, kXMPTemplate_ReplaceWithDeleteEmpty );
+ doReplace |= deleteEmpty; // Delete-empty implies Replace.
+ deleteEmpty &= (! doClear); // Clear implies not delete-empty, but keep the implicit Replace.
+
+ bool doAll = XMP_OptionIsSet ( actions, kXMPTemplate_IncludeInternalProperties );
+
+ // ! In several places we do loops backwards so that deletions do not perturb the remaining indices.
+ // ! These loops use ordinals (size .. 1), we must use a zero based index inside the loop.
+
+ if ( doClear ) {
+
+ // Visit the top level working properties, delete if not in the template.
+
+ for ( size_t schemaOrdinal = workingXMP->tree.children.size(); schemaOrdinal > 0; --schemaOrdinal ) {
+
+ size_t schemaNum = schemaOrdinal-1; // ! Convert ordinal to index!
+ XMP_Node * workingSchema = workingXMP->tree.children[schemaNum];
+ const XMP_Node * templateSchema = FindConstSchema ( &templateXMP.tree, workingSchema->name.c_str() );
+
+ if ( templateSchema == 0 ) {
+
+ // The schema is not in the template, delete all properties or just all external ones.
+
+ if ( doAll ) {
+
+ workingSchema->RemoveChildren(); // Remove the properties here, delete the schema below.
+
+ } else {
+
+ for ( size_t propOrdinal = workingSchema->children.size(); propOrdinal > 0; --propOrdinal ) {
+ size_t propNum = propOrdinal-1; // ! Convert ordinal to index!
+ XMP_Node * workingProp = workingSchema->children[propNum];
+ if ( IsExternalProperty ( workingSchema->name, workingProp->name ) ) {
+ delete ( workingProp );
+ workingSchema->children.erase ( workingSchema->children.begin() + propNum );
+ }
+ }
+
+ }
+
+ } else {
+
+ // Check each of the working XMP's properties to see if it is in the template.
+
+ for ( size_t propOrdinal = workingSchema->children.size(); propOrdinal > 0; --propOrdinal ) {
+ size_t propNum = propOrdinal-1; // ! Convert ordinal to index!
+ XMP_Node * workingProp = workingSchema->children[propNum];
+ if ( (doAll || IsExternalProperty ( workingSchema->name, workingProp->name )) &&
+ (FindConstChild ( templateSchema, workingProp->name.c_str() ) == 0) ) {
+ delete ( workingProp );
+ workingSchema->children.erase ( workingSchema->children.begin() + propNum );
+ }
+ }
+
+ }
+
+ if ( workingSchema->children.empty() ) {
+ delete ( workingSchema );
+ workingXMP->tree.children.erase ( workingXMP->tree.children.begin() + schemaNum );
+ }
+
+ }
+
+ }
+
+ if ( doAdd | doReplace ) {
+
+ for ( size_t schemaNum = 0, schemaLim = templateXMP.tree.children.size(); schemaNum < schemaLim; ++schemaNum ) {
+
+ const XMP_Node * templateSchema = templateXMP.tree.children[schemaNum];
+
+ // Make sure we have an output schema node, then process the top level template properties.
+
+ XMP_NodePtrPos workingSchemaPos;
+ XMP_Node * workingSchema = FindSchemaNode ( &workingXMP->tree, templateSchema->name.c_str(),
+ kXMP_ExistingOnly, &workingSchemaPos );
+ if ( workingSchema == 0 ) {
+ workingSchema = new XMP_Node ( &workingXMP->tree, templateSchema->name, templateSchema->value, kXMP_SchemaNode );
+ workingXMP->tree.children.push_back ( workingSchema );
+ workingSchemaPos = workingXMP->tree.children.end() - 1;
+ }
+
+ for ( size_t propNum = 0, propLim = templateSchema->children.size(); propNum < propLim; ++propNum ) {
+ const XMP_Node * templateProp = templateSchema->children[propNum];
+ if ( doAll || IsExternalProperty ( templateSchema->name, templateProp->name ) ) {
+ AppendSubtree ( templateProp, workingSchema, doAdd, doReplace, deleteEmpty );
+ }
+ }
+
+ if ( workingSchema->children.empty() ) {
+ delete ( workingSchema );
+ workingXMP->tree.children.erase ( workingSchemaPos );
+ }
+
+ }
+
+ }
+
+} // ApplyTemplate
+
+
+// -------------------------------------------------------------------------------------------------
// RemoveProperties
// ----------------
@@ -1136,58 +1330,6 @@ XMPUtils::RemoveProperties ( XMPMeta * xmpObj,
// -------------------------------------------------------------------------------------------------
-// AppendProperties
-// ----------------
-
-/* class static */ void
-XMPUtils::AppendProperties ( const XMPMeta & source,
- XMPMeta * dest,
- XMP_OptionBits options )
-{
- XMP_Assert ( dest != 0 ); // ! Enforced by wrapper.
-
- const bool doAll = ((options & kXMPUtil_DoAllProperties) != 0);
- const bool replaceOld = ((options & kXMPUtil_ReplaceOldValues) != 0);
- const bool deleteEmpty = ((options & kXMPUtil_DeleteEmptyValues) != 0);
-
- for ( size_t schemaNum = 0, schemaLim = source.tree.children.size(); schemaNum != schemaLim; ++schemaNum ) {
-
- const XMP_Node * sourceSchema = source.tree.children[schemaNum];
-
- // Make sure we have a destination schema node. Remember if it is newly created.
-
- XMP_Node * destSchema = FindSchemaNode ( &dest->tree, sourceSchema->name.c_str(), kXMP_ExistingOnly );
- const bool newDestSchema = (destSchema == 0);
- if ( newDestSchema ) {
- destSchema = new XMP_Node ( &dest->tree, sourceSchema->name, sourceSchema->value, kXMP_SchemaNode );
- dest->tree.children.push_back ( destSchema );
- }
-
- // Process the source schema's children. Do this backwards in case deleteEmpty is set.
-
- for ( long propNum = ((long)sourceSchema->children.size() - 1); propNum >= 0; --propNum ) {
- const XMP_Node * sourceProp = sourceSchema->children[propNum];
- if ( doAll || IsExternalProperty ( sourceSchema->name, sourceProp->name ) ) {
- AppendSubtree ( sourceProp, destSchema, replaceOld, deleteEmpty );
-// *** RemoveMultiValueInfo ( dest, sourceSchema->name.c_str(), sourceProp->name.c_str() );
- }
- }
-
- if ( destSchema->children.empty() ) {
- if ( newDestSchema ) {
- delete ( destSchema );
- dest->tree.children.pop_back();
- } else if ( deleteEmpty ) {
- DeleteEmptySchema ( destSchema );
- }
- }
-
- }
-
-} // AppendProperties
-
-
-// -------------------------------------------------------------------------------------------------
// DuplicateSubtree
// ----------------
diff --git a/source/XMPCore/XMPUtils.cpp b/source/XMPCore/XMPUtils.cpp
index 0cfe497..3729ed1 100644
--- a/source/XMPCore/XMPUtils.cpp
+++ b/source/XMPCore/XMPUtils.cpp
@@ -1,5 +1,5 @@
// =================================================================================================
-// Copyright 2002-2007 Adobe Systems Incorporated
+// Copyright 2003 Adobe Systems Incorporated
// All Rights Reserved.
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -28,7 +28,6 @@
#pragma warning ( disable : 4996 ) // '...' was declared deprecated
#endif
-
// =================================================================================================
// Local Types and Constants
// =========================
@@ -36,30 +35,17 @@
static const char * sBase64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
// =================================================================================================
-// Static Variables
-// ================
-
-XMP_VarString * sComposedPath = 0; // *** Only really need 1 string. Shrink periodically?
-XMP_VarString * sConvertedValue = 0;
-XMP_VarString * sBase64Str = 0;
-XMP_VarString * sCatenatedItems = 0;
-XMP_VarString * sStandardXMP = 0;
-XMP_VarString * sExtendedXMP = 0;
-XMP_VarString * sExtendedDigest = 0;
-
-// =================================================================================================
// Local Utilities
// ===============
-
// -------------------------------------------------------------------------------------------------
// ANSI Time Functions
// -------------------
//
// A bit of hackery to use the best available time functions. Mac and UNIX have thread safe versions
-// of gmtime and localtime. On Mac the CodeWarrior functions are buggy, use Apple's.
+// of gmtime and localtime.
-#if XMP_UNIXBuild
+#if XMP_MacBuild | XMP_UNIXBuild
typedef time_t ansi_tt;
typedef struct tm ansi_tm;
@@ -101,69 +87,22 @@ XMP_VarString * sExtendedDigest = 0;
}
#endif
-#elif XMP_MacBuild
-
- #if ! __MWERKS__
-
- typedef time_t ansi_tt;
- typedef struct tm ansi_tm;
-
- #define ansi_time time
- #define ansi_mktime mktime
- #define ansi_difftime difftime
-
- #define ansi_gmtime gmtime_r
- #define ansi_localtime localtime_r
-
- #else
-
- // ! The CW versions are buggy. Use Apple's code, time_t, and "struct tm".
-
- #include <mach-o/dyld.h>
-
- typedef _BSD_TIME_T_ ansi_tt;
-
- typedef struct apple_tm {
- int tm_sec; /* seconds after the minute [0-60] */
- int tm_min; /* minutes after the hour [0-59] */
- int tm_hour; /* hours since midnight [0-23] */
- int tm_mday; /* day of the month [1-31] */
- int tm_mon; /* months since January [0-11] */
- int tm_year; /* years since 1900 */
- int tm_wday; /* days since Sunday [0-6] */
- int tm_yday; /* days since January 1 [0-365] */
- int tm_isdst; /* Daylight Savings Time flag */
- long tm_gmtoff; /* offset from CUT in seconds */
- char *tm_zone; /* timezone abbreviation */
- } ansi_tm;
-
-
- typedef ansi_tt (* GetTimeProc) ( ansi_tt * ttTime );
- typedef ansi_tt (* MakeTimeProc) ( ansi_tm * tmTime );
- typedef double (* DiffTimeProc) ( ansi_tt t1, ansi_tt t0 );
-
- typedef void (* ConvertTimeProc) ( const ansi_tt * ttTime, ansi_tm * tmTime );
+#endif
- static GetTimeProc ansi_time = 0;
- static MakeTimeProc ansi_mktime = 0;
- static DiffTimeProc ansi_difftime = 0;
+// -------------------------------------------------------------------------------------------------
+// VerifyDateTimeFlags
+// -------------------
- static ConvertTimeProc ansi_gmtime = 0;
- static ConvertTimeProc ansi_localtime = 0;
-
- static void LookupTimeProcs()
- {
- _dyld_lookup_and_bind_with_hint ( "_time", "libSystem", (XMP_Uns32*)&ansi_time, 0 );
- _dyld_lookup_and_bind_with_hint ( "_mktime", "libSystem", (XMP_Uns32*)&ansi_mktime, 0 );
- _dyld_lookup_and_bind_with_hint ( "_difftime", "libSystem", (XMP_Uns32*)&ansi_difftime, 0 );
- _dyld_lookup_and_bind_with_hint ( "_gmtime_r", "libSystem", (XMP_Uns32*)&ansi_gmtime, 0 );
- _dyld_lookup_and_bind_with_hint ( "_localtime_r", "libSystem", (XMP_Uns32*)&ansi_localtime, 0 );
- }
-
- #endif
+static void
+VerifyDateTimeFlags ( XMP_DateTime * dt )
+{
-#endif
+ if ( (dt->year != 0) || (dt->month != 0) || (dt->day != 0) ) dt->hasDate = true;
+ if ( (dt->hour != 0) || (dt->minute != 0) || (dt->second != 0) || (dt->nanoSecond != 0) ) dt->hasTime = true;
+ if ( (dt->tzSign != 0) || (dt->tzHour != 0) || (dt->tzMinute != 0) ) dt->hasTimeZone = true;
+ if ( dt->hasTimeZone ) dt->hasTime = true; // ! Don't combine with above line, UTC has zero values.
+} // VerifyDateTimeFlags
// -------------------------------------------------------------------------------------------------
// IsLeapYear
@@ -183,7 +122,6 @@ IsLeapYear ( long year )
} // IsLeapYear
-
// -------------------------------------------------------------------------------------------------
// DaysInMonth
// -----------
@@ -202,7 +140,6 @@ DaysInMonth ( XMP_Int32 year, XMP_Int32 month )
} // DaysInMonth
-
// -------------------------------------------------------------------------------------------------
// AdjustTimeOverflow
// ------------------
@@ -356,7 +293,6 @@ AdjustTimeOverflow ( XMP_DateTime * time )
} // AdjustTimeOverflow
-
// -------------------------------------------------------------------------------------------------
// GatherInt
// ---------
@@ -369,6 +305,7 @@ GatherInt ( XMP_StringPtr strValue, size_t * _pos, const char * errMsg )
for ( char ch = strValue[pos]; ('0' <= ch) && (ch <= '9'); ++pos, ch = strValue[pos] ) {
value = (value * 10) + (ch - '0');
+ if ( value < 0 ) XMP_Throw ( errMsg, kXMPErr_BadValue );
}
if ( pos == *_pos ) XMP_Throw ( errMsg, kXMPErr_BadParam );
@@ -377,7 +314,6 @@ GatherInt ( XMP_StringPtr strValue, size_t * _pos, const char * errMsg )
} // GatherInt
-
// -------------------------------------------------------------------------------------------------
static void FormatFullDateTime ( XMP_DateTime & tempDate, char * buffer, size_t bufferLen )
@@ -410,7 +346,6 @@ static void FormatFullDateTime ( XMP_DateTime & tempDate, char * buffer, size_t
} // FormatFullDateTime
-
// -------------------------------------------------------------------------------------------------
// DecodeBase64Char
// ----------------
@@ -450,7 +385,6 @@ DecodeBase64Char ( XMP_Uns8 ch )
} // DecodeBase64Char ();
-
// -------------------------------------------------------------------------------------------------
// EstimateSizeForJPEG
// -------------------
@@ -497,7 +431,6 @@ EstimateSizeForJPEG ( const XMP_Node * xmpNode )
} // EstimateSizeForJPEG
-
// -------------------------------------------------------------------------------------------------
// MoveOneProperty
// ---------------
@@ -529,7 +462,6 @@ static bool MoveOneProperty ( XMPMeta & stdXMP, XMPMeta * extXMP,
} // MoveOneProperty
-
// -------------------------------------------------------------------------------------------------
// CreateEstimatedSizeMap
// ----------------------
@@ -572,7 +504,6 @@ static void CreateEstimatedSizeMap ( XMPMeta & stdXMP, PropSizeMap * propSizes )
} // CreateEstimatedSizeMap
-
// -------------------------------------------------------------------------------------------------
// MoveLargestProperty
// -------------------
@@ -608,12 +539,10 @@ static size_t MoveLargestProperty ( XMPMeta & stdXMP, XMPMeta * extXMP, PropSize
} // MoveLargestProperty
-
// =================================================================================================
// Class Static Functions
// ======================
-
// -------------------------------------------------------------------------------------------------
// Initialize
// ----------
@@ -621,58 +550,25 @@ static size_t MoveLargestProperty ( XMPMeta & stdXMP, XMPMeta * extXMP, PropSize
/* class static */ bool
XMPUtils::Initialize()
{
- sComposedPath = new XMP_VarString();
- sConvertedValue = new XMP_VarString();
- sBase64Str = new XMP_VarString();
- sCatenatedItems = new XMP_VarString();
- sStandardXMP = new XMP_VarString();
- sExtendedXMP = new XMP_VarString();
- sExtendedDigest = new XMP_VarString();
-
- #if XMP_MacBuild && __MWERKS__
- LookupTimeProcs();
- #endif
+ // Nothing at present.
return true;
} // Initialize
-
// -------------------------------------------------------------------------------------------------
// Terminate
// ---------
-#define EliminateGlobal(g) delete ( g ); g = 0
-
/* class static */ void
XMPUtils::Terminate() RELEASE_NO_THROW
{
- EliminateGlobal ( sComposedPath );
- EliminateGlobal ( sConvertedValue );
- EliminateGlobal ( sBase64Str );
- EliminateGlobal ( sCatenatedItems );
- EliminateGlobal ( sStandardXMP );
- EliminateGlobal ( sExtendedXMP );
- EliminateGlobal ( sExtendedDigest );
+ // Nothing at present.
return;
} // Terminate
-
-// -------------------------------------------------------------------------------------------------
-// Unlock
-// ------
-
-/* class static */ void
-XMPUtils::Unlock ( XMP_OptionBits options )
-{
- options = options; // Avoid unused parameter warning.
-
- XMPMeta::Unlock ( 0 );
-
-} // Unlock
-
// -------------------------------------------------------------------------------------------------
// ComposeArrayItemPath
// --------------------
@@ -683,12 +579,11 @@ XMPUtils::Unlock ( XMP_OptionBits options )
XMPUtils::ComposeArrayItemPath ( XMP_StringPtr schemaNS,
XMP_StringPtr arrayName,
XMP_Index itemIndex,
- XMP_StringPtr * fullPath,
- XMP_StringLen * pathSize )
+ XMP_VarString * _fullPath )
{
XMP_Assert ( schemaNS != 0 ); // Enforced by wrapper.
- XMP_Assert ( *arrayName != 0 ); // Enforced by wrapper.
- XMP_Assert ( (fullPath != 0) && (pathSize != 0) ); // Enforced by wrapper.
+ XMP_Assert ( (arrayName != 0) && (*arrayName != 0) ); // Enforced by wrapper.
+ XMP_Assert ( _fullPath != 0 ); // Enforced by wrapper.
XMP_ExpandedXPath expPath; // Just for side effects to check namespace and basic path.
ExpandXPath ( schemaNS, arrayName, &expPath );
@@ -697,27 +592,23 @@ XMPUtils::ComposeArrayItemPath ( XMP_StringPtr schemaNS,
XMP_StringLen reserveLen = strlen(arrayName) + 2 + 32; // Room plus padding.
- sComposedPath->erase();
- sComposedPath->reserve ( reserveLen );
- sComposedPath->append ( reserveLen, ' ' );
+ XMP_VarString fullPath; // ! Allow for arrayName to be the incoming _fullPath.c_str().
+ fullPath.reserve ( reserveLen );
+ fullPath = arrayName;
- if ( itemIndex != kXMP_ArrayLastItem ) {
- // AUDIT: Using string->size() for the snprintf length is safe.
- snprintf ( const_cast<char*>(sComposedPath->c_str()), sComposedPath->size(), "%s[%d]", arrayName, itemIndex );
+ if ( itemIndex == kXMP_ArrayLastItem ) {
+ fullPath += "[last()]";
} else {
- *sComposedPath = arrayName;
- *sComposedPath += "[last()] ";
- (*sComposedPath)[sComposedPath->size()-1] = 0; // ! Final null is for the strlen at exit.
+ // AUDIT: Using sizeof(buffer) for the snprintf length is safe.
+ char buffer [32]; // A 32 byte buffer is plenty, even for a 64-bit integer.
+ snprintf ( buffer, sizeof(buffer), "[%d]", itemIndex );
+ fullPath += buffer;
}
- *fullPath = sComposedPath->c_str();
- *pathSize = strlen ( *fullPath ); // ! Don't use sComposedPath->size()!
-
- XMP_Enforce ( *pathSize < sComposedPath->size() ); // Rather late, but complain about buffer overflow.
+ *_fullPath = fullPath;
} // ComposeArrayItemPath
-
// -------------------------------------------------------------------------------------------------
// ComposeStructFieldPath
// ----------------------
@@ -729,12 +620,12 @@ XMPUtils::ComposeStructFieldPath ( XMP_StringPtr schemaNS,
XMP_StringPtr structName,
XMP_StringPtr fieldNS,
XMP_StringPtr fieldName,
- XMP_StringPtr * fullPath,
- XMP_StringLen * pathSize )
+ XMP_VarString * _fullPath )
{
XMP_Assert ( (schemaNS != 0) && (fieldNS != 0) ); // Enforced by wrapper.
- XMP_Assert ( (*structName != 0) && (*fieldName != 0) ); // Enforced by wrapper.
- XMP_Assert ( (fullPath != 0) && (pathSize != 0) ); // Enforced by wrapper.
+ XMP_Assert ( (structName != 0) && (*structName != 0) ); // Enforced by wrapper.
+ XMP_Assert ( (fieldName != 0) && (*fieldName != 0) ); // Enforced by wrapper.
+ XMP_Assert ( _fullPath != 0 ); // Enforced by wrapper.
XMP_ExpandedXPath expPath; // Just for side effects to check namespace and basic path.
ExpandXPath ( schemaNS, structName, &expPath );
@@ -745,18 +636,16 @@ XMPUtils::ComposeStructFieldPath ( XMP_StringPtr schemaNS,
XMP_StringLen reserveLen = strlen(structName) + fieldPath[kRootPropStep].step.size() + 1;
- sComposedPath->erase();
- sComposedPath->reserve ( reserveLen );
- *sComposedPath = structName;
- *sComposedPath += '/';
- *sComposedPath += fieldPath[kRootPropStep].step;
+ XMP_VarString fullPath; // ! Allow for arrayName to be the incoming _fullPath.c_str().
+ fullPath.reserve ( reserveLen );
+ fullPath = structName;
+ fullPath += '/';
+ fullPath += fieldPath[kRootPropStep].step;
- *fullPath = sComposedPath->c_str();
- *pathSize = sComposedPath->size();
+ *_fullPath = fullPath;
} // ComposeStructFieldPath
-
// -------------------------------------------------------------------------------------------------
// ComposeQualifierPath
// --------------------
@@ -768,12 +657,12 @@ XMPUtils::ComposeQualifierPath ( XMP_StringPtr schemaNS,
XMP_StringPtr propName,
XMP_StringPtr qualNS,
XMP_StringPtr qualName,
- XMP_StringPtr * fullPath,
- XMP_StringLen * pathSize )
+ XMP_VarString * _fullPath )
{
- XMP_Assert ( (schemaNS != 0) && (qualNS != 0) ); // Enforced by wrapper.
- XMP_Assert ( (*propName != 0) && (*qualName != 0) ); // Enforced by wrapper.
- XMP_Assert ( (fullPath != 0) && (pathSize != 0) ); // Enforced by wrapper.
+ XMP_Assert ( (schemaNS != 0) && (qualNS != 0) ); // Enforced by wrapper.
+ XMP_Assert ( (propName != 0) && (*propName != 0) ); // Enforced by wrapper.
+ XMP_Assert ( (qualName != 0) && (*qualName != 0) ); // Enforced by wrapper.
+ XMP_Assert ( _fullPath != 0 ); // Enforced by wrapper.
XMP_ExpandedXPath expPath; // Just for side effects to check namespace and basic path.
ExpandXPath ( schemaNS, propName, &expPath );
@@ -784,18 +673,16 @@ XMPUtils::ComposeQualifierPath ( XMP_StringPtr schemaNS,
XMP_StringLen reserveLen = strlen(propName) + qualPath[kRootPropStep].step.size() + 2;
- sComposedPath->erase();
- sComposedPath->reserve ( reserveLen );
- *sComposedPath = propName;
- *sComposedPath += "/?";
- *sComposedPath += qualPath[kRootPropStep].step;
+ XMP_VarString fullPath; // ! Allow for arrayName to be the incoming _fullPath.c_str().
+ fullPath.reserve ( reserveLen );
+ fullPath = propName;
+ fullPath += "/?";
+ fullPath += qualPath[kRootPropStep].step;
- *fullPath = sComposedPath->c_str();
- *pathSize = sComposedPath->size();
+ *_fullPath = fullPath;
} // ComposeQualifierPath
-
// -------------------------------------------------------------------------------------------------
// ComposeLangSelector
// -------------------
@@ -808,12 +695,12 @@ XMPUtils::ComposeQualifierPath ( XMP_StringPtr schemaNS,
XMPUtils::ComposeLangSelector ( XMP_StringPtr schemaNS,
XMP_StringPtr arrayName,
XMP_StringPtr _langName,
- XMP_StringPtr * fullPath,
- XMP_StringLen * pathSize )
+ XMP_VarString * _fullPath )
{
XMP_Assert ( schemaNS != 0 ); // Enforced by wrapper.
- XMP_Assert ( (*arrayName != 0) && (*_langName != 0) ); // Enforced by wrapper.
- XMP_Assert ( (fullPath != 0) && (pathSize != 0) ); // Enforced by wrapper.
+ XMP_Assert ( (arrayName != 0) && (*arrayName != 0) ); // Enforced by wrapper.
+ XMP_Assert ( (_langName != 0) && (*_langName != 0) ); // Enforced by wrapper.
+ XMP_Assert ( _fullPath != 0 ); // Enforced by wrapper.
XMP_ExpandedXPath expPath; // Just for side effects to check namespace and basic path.
ExpandXPath ( schemaNS, arrayName, &expPath );
@@ -823,19 +710,17 @@ XMPUtils::ComposeLangSelector ( XMP_StringPtr schemaNS,
XMP_StringLen reserveLen = strlen(arrayName) + langName.size() + 14;
- sComposedPath->erase();
- sComposedPath->reserve ( reserveLen );
- *sComposedPath = arrayName;
- *sComposedPath += "[?xml:lang=\"";
- *sComposedPath += langName;
- *sComposedPath += "\"]";
+ XMP_VarString fullPath; // ! Allow for arrayName to be the incoming _fullPath.c_str().
+ fullPath.reserve ( reserveLen );
+ fullPath = arrayName;
+ fullPath += "[?xml:lang=\"";
+ fullPath += langName;
+ fullPath += "\"]";
- *fullPath = sComposedPath->c_str();
- *pathSize = sComposedPath->size();
+ *_fullPath = fullPath;
} // ComposeLangSelector
-
// -------------------------------------------------------------------------------------------------
// ComposeFieldSelector
// --------------------
@@ -850,12 +735,11 @@ XMPUtils::ComposeFieldSelector ( XMP_StringPtr schemaNS,
XMP_StringPtr fieldNS,
XMP_StringPtr fieldName,
XMP_StringPtr fieldValue,
- XMP_StringPtr * fullPath,
- XMP_StringLen * pathSize )
+ XMP_VarString * _fullPath )
{
XMP_Assert ( (schemaNS != 0) && (fieldNS != 0) && (fieldValue != 0) ); // Enforced by wrapper.
XMP_Assert ( (*arrayName != 0) && (*fieldName != 0) ); // Enforced by wrapper.
- XMP_Assert ( (fullPath != 0) && (pathSize != 0) ); // Enforced by wrapper.
+ XMP_Assert ( _fullPath != 0 ); // Enforced by wrapper.
XMP_ExpandedXPath expPath; // Just for side effects to check namespace and basic path.
ExpandXPath ( schemaNS, arrayName, &expPath );
@@ -866,43 +750,37 @@ XMPUtils::ComposeFieldSelector ( XMP_StringPtr schemaNS,
XMP_StringLen reserveLen = strlen(arrayName) + fieldPath[kRootPropStep].step.size() + strlen(fieldValue) + 5;
- sComposedPath->erase();
- sComposedPath->reserve ( reserveLen );
- *sComposedPath = arrayName;
- *sComposedPath += '[';
- *sComposedPath += fieldPath[kRootPropStep].step;
- *sComposedPath += "=\"";
- *sComposedPath += fieldValue;
- *sComposedPath += "\"]";
+ XMP_VarString fullPath; // ! Allow for arrayName to be the incoming _fullPath.c_str().
+ fullPath.reserve ( reserveLen );
+ fullPath = arrayName;
+ fullPath += '[';
+ fullPath += fieldPath[kRootPropStep].step;
+ fullPath += "=\"";
+ fullPath += fieldValue;
+ fullPath += "\"]";
- *fullPath = sComposedPath->c_str();
- *pathSize = sComposedPath->size();
+ *_fullPath = fullPath;
} // ComposeFieldSelector
-
// -------------------------------------------------------------------------------------------------
// ConvertFromBool
// ---------------
/* class static */ void
XMPUtils::ConvertFromBool ( bool binValue,
- XMP_StringPtr * strValue,
- XMP_StringLen * strSize )
+ XMP_VarString * strValue )
{
- XMP_Assert ( (strValue != 0) && (strSize != 0) ); // Enforced by wrapper.
+ XMP_Assert ( strValue != 0 ); // Enforced by wrapper.
if ( binValue ) {
*strValue = kXMP_TrueStr;
- *strSize = strlen ( kXMP_TrueStr );
} else {
*strValue = kXMP_FalseStr;
- *strSize = strlen ( kXMP_FalseStr );
}
} // ConvertFromBool
-
// -------------------------------------------------------------------------------------------------
// ConvertFromInt
// --------------
@@ -910,28 +788,21 @@ XMPUtils::ConvertFromBool ( bool binValue,
/* class static */ void
XMPUtils::ConvertFromInt ( XMP_Int32 binValue,
XMP_StringPtr format,
- XMP_StringPtr * strValue,
- XMP_StringLen * strSize )
+ XMP_VarString * strValue )
{
- XMP_Assert ( (format != 0) && (strValue != 0) && (strSize != 0) ); // Enforced by wrapper.
+ XMP_Assert ( (format != 0) && (strValue != 0) ); // Enforced by wrapper.
+ strValue->erase();
if ( *format == 0 ) format = "%d";
- sConvertedValue->erase();
- sConvertedValue->reserve ( 100 ); // More than enough for any reasonable format and value.
- sConvertedValue->append ( 100, ' ' );
+ // AUDIT: Using sizeof(buffer) for the snprintf length is safe.
+ char buffer [32]; // Big enough for a 64-bit integer;
+ snprintf ( buffer, sizeof(buffer), format, binValue );
- // AUDIT: Using string->size() for the snprintf length is safe.
- snprintf ( const_cast<char*>(sConvertedValue->c_str()), sConvertedValue->size(), format, binValue );
-
- *strValue = sConvertedValue->c_str();
- *strSize = strlen ( *strValue ); // ! Don't use sConvertedValue->size()!
-
- XMP_Enforce ( *strSize < sConvertedValue->size() ); // Rather late, but complain about buffer overflow.
+ *strValue = buffer;
} // ConvertFromInt
-
// -------------------------------------------------------------------------------------------------
// ConvertFromInt64
// ----------------
@@ -939,28 +810,21 @@ XMPUtils::ConvertFromInt ( XMP_Int32 binValue,
/* class static */ void
XMPUtils::ConvertFromInt64 ( XMP_Int64 binValue,
XMP_StringPtr format,
- XMP_StringPtr * strValue,
- XMP_StringLen * strSize )
+ XMP_VarString * strValue )
{
- XMP_Assert ( (format != 0) && (strValue != 0) && (strSize != 0) ); // Enforced by wrapper.
+ XMP_Assert ( (format != 0) && (strValue != 0) ); // Enforced by wrapper.
+ strValue->erase();
if ( *format == 0 ) format = "%lld";
- sConvertedValue->erase();
- sConvertedValue->reserve ( 100 ); // More than enough for any reasonable format and value.
- sConvertedValue->append ( 100, ' ' );
-
- // AUDIT: Using string->size() for the snprintf length is safe.
- snprintf ( const_cast<char*>(sConvertedValue->c_str()), sConvertedValue->size(), format, binValue );
+ // AUDIT: Using sizeof(buffer) for the snprintf length is safe.
+ char buffer [32]; // Big enough for a 64-bit integer;
+ snprintf ( buffer, sizeof(buffer), format, binValue );
- *strValue = sConvertedValue->c_str();
- *strSize = strlen ( *strValue ); // ! Don't use sConvertedValue->size()!
-
- XMP_Enforce ( *strSize < sConvertedValue->size() ); // Rather late, but complain about buffer overflow.
+ *strValue = buffer;
} // ConvertFromInt64
-
// -------------------------------------------------------------------------------------------------
// ConvertFromFloat
// ----------------
@@ -968,33 +832,26 @@ XMPUtils::ConvertFromInt64 ( XMP_Int64 binValue,
/* class static */ void
XMPUtils::ConvertFromFloat ( double binValue,
XMP_StringPtr format,
- XMP_StringPtr * strValue,
- XMP_StringLen * strSize )
+ XMP_VarString * strValue )
{
- XMP_Assert ( (format != 0) && (strValue != 0) && (strSize != 0) ); // Enforced by wrapper.
+ XMP_Assert ( (format != 0) && (strValue != 0) ); // Enforced by wrapper.
+ strValue->erase();
if ( *format == 0 ) format = "%f";
- sConvertedValue->erase();
- sConvertedValue->reserve ( 1000 ); // More than enough for any reasonable format and value.
- sConvertedValue->append ( 1000, ' ' );
+ // AUDIT: Using sizeof(buffer) for the snprintf length is safe.
+ char buffer [64]; // Ought to be plenty big enough.
+ snprintf ( buffer, sizeof(buffer), format, binValue );
- // AUDIT: Using string->size() for the snprintf length is safe.
- snprintf ( const_cast<char*>(sConvertedValue->c_str()), sConvertedValue->size(), format, binValue );
-
- *strValue = sConvertedValue->c_str();
- *strSize = strlen ( *strValue ); // ! Don't use sConvertedValue->size()!
-
- XMP_Enforce ( *strSize < sConvertedValue->size() ); // Rather late, but complain about buffer overflow.
+ *strValue = buffer;
} // ConvertFromFloat
-
// -------------------------------------------------------------------------------------------------
// ConvertFromDate
// ---------------
//
-// Format a date according to ISO 8601 and http://www.w3.org/TR/NOTE-datetime:
+// Format a date-time string according to ISO 8601 and http://www.w3.org/TR/NOTE-datetime:
// YYYY
// YYYY-MM
// YYYY-MM-DD
@@ -1012,18 +869,16 @@ XMPUtils::ConvertFromFloat ( double binValue,
// TZD = time zone designator (Z or +hh:mm or -hh:mm)
//
// Note that ISO 8601 does not seem to allow years less than 1000 or greater than 9999. We allow
-// any year, even negative ones. The year is formatted as "%.4d".
-
-// *** Need to check backward compatibility for partial forms!
+// any year, even negative ones. The year is formatted as "%.4d". The TZD is also optional in XMP,
+// even though required in the W3C profile. Finally, Photoshop 8 (CS) sometimes created time-only
+// values so we tolerate that.
/* class static */ void
-XMPUtils::ConvertFromDate ( const XMP_DateTime & binValue,
- XMP_StringPtr * strValue,
- XMP_StringLen * strSize )
+XMPUtils::ConvertFromDate ( const XMP_DateTime & _inValue,
+ XMP_VarString * strValue )
{
- XMP_Assert ( (strValue != 0) && (strSize != 0) ); // Enforced by wrapper.
+ XMP_Assert ( strValue != 0 ); // Enforced by wrapper.
- bool addTimeZone = false;
char buffer [100]; // Plenty long enough.
// Pick the format, use snprintf to format into a local buffer, assign to static output string.
@@ -1031,101 +886,82 @@ XMPUtils::ConvertFromDate ( const XMP_DateTime & binValue,
// ! Photoshop 8 creates "time only" values with zeros for year, month, and day.
- XMP_DateTime tempDate = binValue;
+ XMP_DateTime binValue = _inValue;
+ VerifyDateTimeFlags ( &binValue );
// Temporary fix for bug 1269463, silently fix out of range month or day.
- bool haveDay = (tempDate.day != 0);
- bool haveTime = ( (tempDate.hour != 0) || (tempDate.minute != 0) ||
- (tempDate.second != 0) || (tempDate.nanoSecond != 0) ||
- (tempDate.tzSign != 0) || (tempDate.tzHour != 0) || (tempDate.tzMinute != 0) );
-
- if ( tempDate.month == 0 ) {
- if ( haveDay || haveTime ) tempDate.month = 1;
+ if ( binValue.month == 0 ) {
+ if ( (binValue.day != 0) || binValue.hasTime ) binValue.month = 1;
} else {
- if ( tempDate.month < 1 ) tempDate.month = 1;
- if ( tempDate.month > 12 ) tempDate.month = 12;
+ if ( binValue.month < 1 ) binValue.month = 1;
+ if ( binValue.month > 12 ) binValue.month = 12;
}
- if ( tempDate.day == 0 ) {
- if ( haveTime ) tempDate.day = 1;
+ if ( binValue.day == 0 ) {
+ if ( binValue.hasTime ) binValue.day = 1;
} else {
- if ( tempDate.day < 1 ) tempDate.day = 1;
- if ( tempDate.day > 31 ) tempDate.day = 31;
+ if ( binValue.day < 1 ) binValue.day = 1;
+ if ( binValue.day > 31 ) binValue.day = 31;
}
// Now carry on with the original logic.
- if ( tempDate.month == 0 ) {
+ if ( binValue.month == 0 ) {
// Output YYYY if all else is zero, otherwise output a full string for the quasi-bogus
// "time only" values from Photoshop CS.
- if ( (tempDate.day == 0) && (tempDate.hour == 0) && (tempDate.minute == 0) &&
- (tempDate.second == 0) && (tempDate.nanoSecond == 0) &&
- (tempDate.tzSign == 0) && (tempDate.tzHour == 0) && (tempDate.tzMinute == 0) ) {
- snprintf ( buffer, sizeof(buffer), "%.4d", tempDate.year ); // AUDIT: Using sizeof for snprintf length is safe.
- } else if ( (tempDate.year == 0) && (tempDate.day == 0) ) {
- FormatFullDateTime ( tempDate, buffer, sizeof(buffer) );
- addTimeZone = true;
+ if ( (binValue.day == 0) && (! binValue.hasTime) ) {
+ snprintf ( buffer, sizeof(buffer), "%.4d", binValue.year ); // AUDIT: Using sizeof for snprintf length is safe.
+ } else if ( (binValue.year == 0) && (binValue.day == 0) ) {
+ FormatFullDateTime ( binValue, buffer, sizeof(buffer) );
} else {
XMP_Throw ( "Invalid partial date", kXMPErr_BadParam);
}
- } else if ( tempDate.day == 0 ) {
+ } else if ( binValue.day == 0 ) {
// Output YYYY-MM.
- if ( (tempDate.month < 1) || (tempDate.month > 12) ) XMP_Throw ( "Month is out of range", kXMPErr_BadParam);
- if ( (tempDate.hour != 0) || (tempDate.minute != 0) ||
- (tempDate.second != 0) || (tempDate.nanoSecond != 0) ||
- (tempDate.tzSign != 0) || (tempDate.tzHour != 0) || (tempDate.tzMinute != 0) ) {
- XMP_Throw ( "Invalid partial date, non-zeros after zero month and day", kXMPErr_BadParam);
- }
- snprintf ( buffer, sizeof(buffer), "%.4d-%02d", tempDate.year, tempDate.month ); // AUDIT: Using sizeof for snprintf length is safe.
+ if ( (binValue.month < 1) || (binValue.month > 12) ) XMP_Throw ( "Month is out of range", kXMPErr_BadParam);
+ if ( binValue.hasTime ) XMP_Throw ( "Invalid partial date, non-zeros after zero month and day", kXMPErr_BadParam);
+ snprintf ( buffer, sizeof(buffer), "%.4d-%02d", binValue.year, binValue.month ); // AUDIT: Using sizeof for snprintf length is safe.
- } else if ( (tempDate.hour == 0) && (tempDate.minute == 0) &&
- (tempDate.second == 0) && (tempDate.nanoSecond == 0) &&
- (tempDate.tzSign == 0) && (tempDate.tzHour == 0) && (tempDate.tzMinute == 0) ) {
+ } else if ( ! binValue.hasTime ) {
// Output YYYY-MM-DD.
- if ( (tempDate.month < 1) || (tempDate.month > 12) ) XMP_Throw ( "Month is out of range", kXMPErr_BadParam);
- if ( (tempDate.day < 1) || (tempDate.day > 31) ) XMP_Throw ( "Day is out of range", kXMPErr_BadParam);
- snprintf ( buffer, sizeof(buffer), "%.4d-%02d-%02d", tempDate.year, tempDate.month, tempDate.day ); // AUDIT: Using sizeof for snprintf length is safe.
+ if ( (binValue.month < 1) || (binValue.month > 12) ) XMP_Throw ( "Month is out of range", kXMPErr_BadParam);
+ if ( (binValue.day < 1) || (binValue.day > 31) ) XMP_Throw ( "Day is out of range", kXMPErr_BadParam);
+ snprintf ( buffer, sizeof(buffer), "%.4d-%02d-%02d", binValue.year, binValue.month, binValue.day ); // AUDIT: Using sizeof for snprintf length is safe.
} else {
- FormatFullDateTime ( tempDate, buffer, sizeof(buffer) );
- addTimeZone = true;
+ FormatFullDateTime ( binValue, buffer, sizeof(buffer) );
}
- sConvertedValue->assign ( buffer );
+ strValue->assign ( buffer );
- if ( addTimeZone ) {
+ if ( binValue.hasTimeZone ) {
- if ( (tempDate.tzHour < 0) || (tempDate.tzHour > 23) ||
- (tempDate.tzMinute < 0 ) || (tempDate.tzMinute > 59) ||
- (tempDate.tzSign < -1) || (tempDate.tzSign > +1) ||
- ((tempDate.tzSign != 0) && (tempDate.tzHour == 0) && (tempDate.tzMinute == 0)) ||
- ((tempDate.tzSign == 0) && ((tempDate.tzHour != 0) || (tempDate.tzMinute != 0))) ) {
+ if ( (binValue.tzHour < 0) || (binValue.tzHour > 23) ||
+ (binValue.tzMinute < 0 ) || (binValue.tzMinute > 59) ||
+ (binValue.tzSign < -1) || (binValue.tzSign > +1) ||
+ ((binValue.tzSign == 0) && ((binValue.tzHour != 0) || (binValue.tzMinute != 0))) ) {
XMP_Throw ( "Invalid time zone values", kXMPErr_BadParam );
}
- if ( tempDate.tzSign == 0 ) {
- *sConvertedValue += 'Z';
+ if ( binValue.tzSign == 0 ) {
+ *strValue += 'Z';
} else {
- snprintf ( buffer, sizeof(buffer), "+%02d:%02d", tempDate.tzHour, tempDate.tzMinute ); // AUDIT: Using sizeof for snprintf length is safe.
- if ( tempDate.tzSign < 0 ) buffer[0] = '-';
- *sConvertedValue += buffer;
+ snprintf ( buffer, sizeof(buffer), "+%02d:%02d", binValue.tzHour, binValue.tzMinute ); // AUDIT: Using sizeof for snprintf length is safe.
+ if ( binValue.tzSign < 0 ) buffer[0] = '-';
+ *strValue += buffer;
}
}
- *strValue = sConvertedValue->c_str();
- *strSize = sConvertedValue->size();
-
} // ConvertFromDate
-
// -------------------------------------------------------------------------------------------------
// ConvertToBool
// -------------
@@ -1157,7 +993,6 @@ XMPUtils::ConvertToBool ( XMP_StringPtr strValue )
} // ConvertToBool
-
// -------------------------------------------------------------------------------------------------
// ConvertToInt
// ------------
@@ -1183,7 +1018,6 @@ XMPUtils::ConvertToInt ( XMP_StringPtr strValue )
} // ConvertToInt
-
// -------------------------------------------------------------------------------------------------
// ConvertToInt64
// --------------
@@ -1209,7 +1043,6 @@ XMPUtils::ConvertToInt64 ( XMP_StringPtr strValue )
} // ConvertToInt64
-
// -------------------------------------------------------------------------------------------------
// ConvertToFloat
// --------------
@@ -1237,12 +1070,11 @@ XMPUtils::ConvertToFloat ( XMP_StringPtr strValue )
} // ConvertToFloat
-
// -------------------------------------------------------------------------------------------------
// ConvertToDate
// -------------
//
-// Parse a date according to ISO 8601 and http://www.w3.org/TR/NOTE-datetime:
+// Parse a date-time string according to ISO 8601 and http://www.w3.org/TR/NOTE-datetime:
// YYYY
// YYYY-MM
// YYYY-MM-DD
@@ -1260,10 +1092,9 @@ XMPUtils::ConvertToFloat ( XMP_StringPtr strValue )
// TZD = time zone designator (Z or +hh:mm or -hh:mm)
//
// Note that ISO 8601 does not seem to allow years less than 1000 or greater than 9999. We allow
-// any year, even negative ones. The year is formatted as "%.4d".
-
-// ! Tolerate missing TZD, assume is UTC. Photoshop 8 writes dates like this for exif:GPSTimeStamp.
-// ! Tolerate missing date portion, in case someone foolishly writes a time-only value that way.
+// any year, even negative ones. The year is formatted as "%.4d". The TZD is also optional in XMP,
+// even though required in the W3C profile. Finally, Photoshop 8 (CS) sometimes created time-only
+// values so we tolerate that.
// *** Put the ISO format comments in the header documentation.
@@ -1273,18 +1104,21 @@ XMPUtils::ConvertToDate ( XMP_StringPtr strValue,
{
if ( (strValue == 0) || (*strValue == 0) ) XMP_Throw ( "Empty convert-from string", kXMPErr_BadValue);
- size_t pos = 0;
+ size_t pos = 0;
XMP_Int32 temp;
XMP_Assert ( sizeof(*binValue) == sizeof(XMP_DateTime) );
(void) memset ( binValue, 0, sizeof(*binValue) ); // AUDIT: Safe, using sizeof destination.
+ size_t strSize = strlen ( strValue );
bool timeOnly = ( (strValue[0] == 'T') ||
- ((strlen(strValue) >= 2) && (strValue[1] == ':')) ||
- ((strlen(strValue) >= 3) && (strValue[2] == ':')) );
+ ((strSize >= 2) && (strValue[1] == ':')) ||
+ ((strSize >= 3) && (strValue[2] == ':')) );
if ( ! timeOnly ) {
-
+
+ binValue->hasDate = true;
+
if ( strValue[0] == '-' ) pos = 1;
temp = GatherInt ( strValue, &pos, "Invalid year in date string" ); // Extract the year.
@@ -1318,12 +1152,16 @@ XMPUtils::ConvertToDate ( XMP_StringPtr strValue,
}
+ // If we get here there is more of the string, otherwise we would have returned above.
+
if ( strValue[pos] == 'T' ) {
++pos;
} else if ( ! timeOnly ) {
XMP_Throw ( "Invalid date string, missing 'T' after date", kXMPErr_BadParam );
}
+ binValue->hasTime = true;
+
temp = GatherInt ( strValue, &pos, "Invalid hour in date string" ); // Extract the hour.
if ( strValue[pos] != ':' ) XMP_Throw ( "Invalid date string, after hour", kXMPErr_BadParam );
if ( temp > 23 ) temp = 23; // *** 1269463: XMP_Throw ( "Hour is out of range", kXMPErr_BadParam );
@@ -1372,11 +1210,15 @@ XMPUtils::ConvertToDate ( XMP_StringPtr strValue,
}
+ if ( strValue[pos] == 0 ) return;
+
+ binValue->hasTimeZone = true;
+
if ( strValue[pos] == 'Z' ) {
++pos;
- } else if ( strValue[pos] != 0 ) {
+ } else {
if ( strValue[pos] == '+' ) {
binValue->tzSign = kXMP_TimeEastOfUTC;
@@ -1403,7 +1245,6 @@ XMPUtils::ConvertToDate ( XMP_StringPtr strValue,
} // ConvertToDate
-
// -------------------------------------------------------------------------------------------------
// EncodeToBase64
// --------------
@@ -1415,15 +1256,13 @@ XMPUtils::ConvertToDate ( XMP_StringPtr strValue,
/* class static */ void
XMPUtils::EncodeToBase64 ( XMP_StringPtr rawStr,
XMP_StringLen rawLen,
- XMP_StringPtr * encodedStr,
- XMP_StringLen * encodedLen )
+ XMP_VarString * encodedStr )
{
if ( (rawStr == 0) && (rawLen != 0) ) XMP_Throw ( "Null raw data buffer", kXMPErr_BadParam );
- if ( rawLen == 0 ) {
- *encodedStr = 0;
- *encodedLen = 0;
- return;
- }
+ XMP_Assert ( encodedStr != 0 ); // Enforced by wrapper.
+
+ encodedStr->erase();
+ if ( rawLen == 0 ) return;
char encChunk[4];
@@ -1433,8 +1272,7 @@ XMPUtils::EncodeToBase64 ( XMP_StringPtr rawStr,
const size_t outputSize = (rawLen / 3) * 4; // Approximate, might be small.
- sBase64Str->erase();
- sBase64Str->reserve ( outputSize );
+ encodedStr->reserve ( outputSize );
// ----------------------------------------------------------------------------------------
// Each 6 bits of input produces 8 bits of output, so 3 input bytes become 4 output bytes.
@@ -1455,10 +1293,10 @@ XMPUtils::EncodeToBase64 ( XMP_StringPtr rawStr,
encChunk[3] = sBase64Chars [ merge & 0x3F ];
if ( out >= 76 ) {
- sBase64Str->append ( 1, kLF );
+ encodedStr->append ( 1, kLF );
out = 0;
}
- sBase64Str->append ( encChunk, 4 );
+ encodedStr->append ( encChunk, 4 );
}
@@ -1481,8 +1319,8 @@ XMPUtils::EncodeToBase64 ( XMP_StringPtr rawStr,
encChunk[1] = sBase64Chars [ (merge >> 12) & 0x3F ];
encChunk[2] = encChunk[3] = '=';
- if ( out >= 76 ) sBase64Str->append ( 1, kLF );
- sBase64Str->append ( encChunk, 4 );
+ if ( out >= 76 ) encodedStr->append ( 1, kLF );
+ encodedStr->append ( encChunk, 4 );
break;
case 2: // Two input bytes remain.
@@ -1496,21 +1334,14 @@ XMPUtils::EncodeToBase64 ( XMP_StringPtr rawStr,
encChunk[2] = sBase64Chars [ (merge >> 6) & 0x3F ];
encChunk[3] = '=';
- if ( out >= 76 ) sBase64Str->append ( 1, kLF );
- sBase64Str->append ( encChunk, 4 );
+ if ( out >= 76 ) encodedStr->append ( 1, kLF );
+ encodedStr->append ( encChunk, 4 );
break;
}
-
- // -------------------------
- // Assign the output values.
-
- *encodedStr = sBase64Str->c_str();
- *encodedLen = sBase64Str->size();
} // EncodeToBase64
-
// -------------------------------------------------------------------------------------------------
// DecodeFromBase64
// ----------------
@@ -1523,23 +1354,20 @@ XMPUtils::EncodeToBase64 ( XMP_StringPtr rawStr,
/* class static */ void
XMPUtils::DecodeFromBase64 ( XMP_StringPtr encodedStr,
XMP_StringLen encodedLen,
- XMP_StringPtr * rawStr,
- XMP_StringLen * rawLen )
+ XMP_VarString * rawStr )
{
if ( (encodedStr == 0) && (encodedLen != 0) ) XMP_Throw ( "Null encoded data buffer", kXMPErr_BadParam );
- if ( encodedLen == 0 ) {
- *rawStr = 0;
- *rawLen = 0;
- return;
- }
+ XMP_Assert ( rawStr != 0 ); // Enforced by wrapper.
+
+ rawStr->erase();
+ if ( encodedLen == 0 ) return;
unsigned char ch, rawChunk[3];
unsigned long inStr, inChunk, inLimit, merge, padding;
XMP_StringLen outputSize = (encodedLen / 4) * 3; // Only a close approximation.
- sBase64Str->erase();
- sBase64Str->reserve ( outputSize );
+ rawStr->reserve ( outputSize );
// ----------------------------------------------------------------------------------------
@@ -1589,7 +1417,7 @@ XMPUtils::DecodeFromBase64 ( XMP_StringPtr encodedStr,
rawChunk[1] = (unsigned char) ((merge >> 8) & 0xFF);
rawChunk[2] = (unsigned char) (merge & 0xFF);
- sBase64Str->append ( (char*)rawChunk, 3 );
+ rawStr->append ( (char*)rawChunk, 3 );
}
@@ -1611,69 +1439,58 @@ XMPUtils::DecodeFromBase64 ( XMP_StringPtr encodedStr,
if ( padding == 2 ) {
rawChunk[0] = (unsigned char) (merge >> 4);
- sBase64Str->append ( (char*)rawChunk, 1 );
+ rawStr->append ( (char*)rawChunk, 1 );
} else if ( padding == 1 ) {
rawChunk[0] = (unsigned char) (merge >> 10);
rawChunk[1] = (unsigned char) ((merge >> 2) & 0xFF);
- sBase64Str->append ( (char*)rawChunk, 2 );
+ rawStr->append ( (char*)rawChunk, 2 );
} else {
rawChunk[0] = (unsigned char) (merge >> 16);
rawChunk[1] = (unsigned char) ((merge >> 8) & 0xFF);
rawChunk[2] = (unsigned char) (merge & 0xFF);
- sBase64Str->append ( (char*)rawChunk, 3 );
+ rawStr->append ( (char*)rawChunk, 3 );
}
-
- // -------------------------
- // Assign the output values.
-
- *rawStr = sBase64Str->c_str();
- *rawLen = sBase64Str->size();
} // DecodeFromBase64
-
// -------------------------------------------------------------------------------------------------
// PackageForJPEG
// --------------
/* class static */ void
XMPUtils::PackageForJPEG ( const XMPMeta & origXMP,
- XMP_StringPtr * stdStr,
- XMP_StringLen * stdLen,
- XMP_StringPtr * extStr,
- XMP_StringLen * extLen,
- XMP_StringPtr * digestStr,
- XMP_StringLen * digestLen )
+ XMP_VarString * stdStr,
+ XMP_VarString * extStr,
+ XMP_VarString * digestStr )
{
+ XMP_Assert ( (stdStr != 0) && (extStr != 0) && (digestStr != 0) ); // ! Enforced by wrapper.
+
enum { kStdXMPLimit = 65000 };
static const char * kPacketTrailer = "<?xpacket end=\"w\"?>";
static size_t kTrailerLen = strlen ( kPacketTrailer );
- XMP_StringPtr tempStr;
- XMP_StringLen tempLen;
-
+ XMP_VarString tempStr;
XMPMeta stdXMP, extXMP;
-
- sStandardXMP->clear(); // Clear the static strings that get returned to the client.
- sExtendedXMP->clear();
- sExtendedDigest->clear();
-
XMP_OptionBits keepItSmall = kXMP_UseCompactFormat | kXMP_OmitAllFormatting;
+ stdStr->erase();
+ extStr->erase();
+ digestStr->erase();
+
// Try to serialize everything. Note that we're making internal calls to SerializeToBuffer, so
// we'll be getting back the pointer and length for its internal string.
- origXMP.SerializeToBuffer ( &tempStr, &tempLen, keepItSmall, 1, "", "", 0 );
+ origXMP.SerializeToBuffer ( &tempStr, keepItSmall, 1, "", "", 0 );
#if Trace_PackageForJPEG
- printf ( "\nXMPUtils::PackageForJPEG - Full serialize %d bytes\n", tempLen );
+ printf ( "\nXMPUtils::PackageForJPEG - Full serialize %d bytes\n", tempStr.size() );
#endif
- if ( tempLen > kStdXMPLimit ) {
+ if ( tempStr.size() > kStdXMPLimit ) {
// Couldn't fit everything, make a copy of the input XMP and make sure there is no xmp:Thumbnails property.
@@ -1684,15 +1501,15 @@ XMPUtils::PackageForJPEG ( const XMPMeta & origXMP,
if ( stdXMP.DoesPropertyExist ( kXMP_NS_XMP, "Thumbnails" ) ) {
stdXMP.DeleteProperty ( kXMP_NS_XMP, "Thumbnails" );
- stdXMP.SerializeToBuffer ( &tempStr, &tempLen, keepItSmall, 1, "", "", 0 );
+ stdXMP.SerializeToBuffer ( &tempStr, keepItSmall, 1, "", "", 0 );
#if Trace_PackageForJPEG
- printf ( " Delete xmp:Thumbnails, %d bytes left\n", tempLen );
+ printf ( " Delete xmp:Thumbnails, %d bytes left\n", tempStr.size() );
#endif
}
}
- if ( tempLen > kStdXMPLimit ) {
+ if ( tempStr.size() > kStdXMPLimit ) {
// Still doesn't fit, move all of the Camera Raw namespace. Add a dummy value for xmpNote:HasExtendedXMP.
@@ -1705,30 +1522,30 @@ XMPUtils::PackageForJPEG ( const XMPMeta & origXMP,
crSchema->parent = &extXMP.tree;
extXMP.tree.children.push_back ( crSchema );
stdXMP.tree.children.erase ( crSchemaPos );
- stdXMP.SerializeToBuffer ( &tempStr, &tempLen, keepItSmall, 1, "", "", 0 );
+ stdXMP.SerializeToBuffer ( &tempStr, keepItSmall, 1, "", "", 0 );
#if Trace_PackageForJPEG
- printf ( " Move Camera Raw schema, %d bytes left\n", tempLen );
+ printf ( " Move Camera Raw schema, %d bytes left\n", tempStr.size() );
#endif
}
}
- if ( tempLen > kStdXMPLimit ) {
+ if ( tempStr.size() > kStdXMPLimit ) {
// Still doesn't fit, move photoshop:History.
bool moved = MoveOneProperty ( stdXMP, &extXMP, kXMP_NS_Photoshop, "photoshop:History" );
if ( moved ) {
- stdXMP.SerializeToBuffer ( &tempStr, &tempLen, keepItSmall, 1, "", "", 0 );
+ stdXMP.SerializeToBuffer ( &tempStr, keepItSmall, 1, "", "", 0 );
#if Trace_PackageForJPEG
- printf ( " Move photoshop:History, %d bytes left\n", tempLen );
+ printf ( " Move photoshop:History, %d bytes left\n", tempStr.size() );
#endif
}
}
- if ( tempLen > kStdXMPLimit ) {
+ if ( tempStr.size() > kStdXMPLimit ) {
// Still doesn't fit, move top level properties in order of estimated size. This is done by
// creating a multi-map that maps the serialized size to the string pair for the schema URI
@@ -1770,10 +1587,11 @@ XMPUtils::PackageForJPEG ( const XMPMeta & origXMP,
// Outer loop to make sure enough is actually moved.
- while ( (tempLen > kStdXMPLimit) && (! propSizes.empty()) ) {
+ while ( (tempStr.size() > kStdXMPLimit) && (! propSizes.empty()) ) {
// Inner loop, move what seems to be enough according to the estimates.
+ size_t tempLen = tempStr.size();
while ( (tempLen > kStdXMPLimit) && (! propSizes.empty()) ) {
size_t propSize = MoveLargestProperty ( stdXMP, &extXMP, propSizes );
@@ -1786,13 +1604,13 @@ XMPUtils::PackageForJPEG ( const XMPMeta & origXMP,
// Reserialize the remaining standard XMP.
- stdXMP.SerializeToBuffer ( &tempStr, &tempLen, keepItSmall, 1, "", "", 0 );
+ stdXMP.SerializeToBuffer ( &tempStr, keepItSmall, 1, "", "", 0 );
}
}
- if ( tempLen > kStdXMPLimit ) {
+ if ( tempStr.size() > kStdXMPLimit ) {
// Still doesn't fit, throw an exception and let the client decide what to do.
// ! This should never happen with the policy of moving any and all top level properties.
XMP_Throw ( "Can't reduce XMP enough for JPEG file", kXMPErr_TooLargeForJPEG );
@@ -1803,59 +1621,49 @@ XMPUtils::PackageForJPEG ( const XMPMeta & origXMP,
if ( extXMP.tree.children.empty() ) {
// Just have the standard XMP.
- sStandardXMP->assign ( tempStr, tempLen );
+ *stdStr = tempStr;
} else {
// Have extended XMP. Serialize it, compute the digest, reset xmpNote:HasExtendedXMP, and
// reserialize the standard XMP.
- extXMP.SerializeToBuffer ( &tempStr, &tempLen, (keepItSmall | kXMP_OmitPacketWrapper), 0, "", "", 0 );
- sExtendedXMP->assign ( tempStr, tempLen );
+ extXMP.SerializeToBuffer ( &tempStr, (keepItSmall | kXMP_OmitPacketWrapper), 0, "", "", 0 );
+ *extStr = tempStr;
MD5_CTX context;
XMP_Uns8 digest [16];
MD5Init ( &context );
- MD5Update ( &context, (XMP_Uns8*)tempStr, tempLen );
+ MD5Update ( &context, (XMP_Uns8*)tempStr.c_str(), (XMP_Uns32)tempStr.size() );
MD5Final ( digest, &context );
- sExtendedDigest->reserve ( 32 );
+ digestStr->reserve ( 32 );
for ( size_t i = 0; i < 16; ++i ) {
XMP_Uns8 byte = digest[i];
- sExtendedDigest->push_back ( kHexDigits [ byte>>4 ] );
- sExtendedDigest->push_back ( kHexDigits [ byte&0xF ] );
+ digestStr->push_back ( kHexDigits [ byte>>4 ] );
+ digestStr->push_back ( kHexDigits [ byte&0xF ] );
}
- stdXMP.SetProperty ( kXMP_NS_XMP_Note, "HasExtendedXMP", sExtendedDigest->c_str(), 0 );
- stdXMP.SerializeToBuffer ( &tempStr, &tempLen, keepItSmall, 1, "", "", 0 );
- sStandardXMP->assign ( tempStr, tempLen );
+ stdXMP.SetProperty ( kXMP_NS_XMP_Note, "HasExtendedXMP", digestStr->c_str(), 0 );
+ stdXMP.SerializeToBuffer ( &tempStr, keepItSmall, 1, "", "", 0 );
+ *stdStr = tempStr;
}
// Adjust the standard XMP padding to be up to 2KB.
- XMP_Assert ( (sStandardXMP->size() > kTrailerLen) && (sStandardXMP->size() <= kStdXMPLimit) );
- const char * packetEnd = sStandardXMP->c_str() + sStandardXMP->size() - kTrailerLen;
+ XMP_Assert ( (stdStr->size() > kTrailerLen) && (stdStr->size() <= kStdXMPLimit) );
+ const char * packetEnd = stdStr->c_str() + stdStr->size() - kTrailerLen;
XMP_Assert ( XMP_LitMatch ( packetEnd, kPacketTrailer ) );
- size_t extraPadding = kStdXMPLimit - sStandardXMP->size(); // ! Do this before erasing the trailer.
+ size_t extraPadding = kStdXMPLimit - stdStr->size(); // ! Do this before erasing the trailer.
if ( extraPadding > 2047 ) extraPadding = 2047;
- sStandardXMP->erase ( sStandardXMP->size() - kTrailerLen );
- sStandardXMP->append ( extraPadding, ' ' );
- sStandardXMP->append ( kPacketTrailer );
-
- // Assign the output pointer and sizes.
-
- *stdStr = sStandardXMP->c_str();
- *stdLen = sStandardXMP->size();
- *extStr = sExtendedXMP->c_str();
- *extLen = sExtendedXMP->size();
- *digestStr = sExtendedDigest->c_str();
- *digestLen = sExtendedDigest->size();
+ stdStr->erase ( stdStr->size() - kTrailerLen );
+ stdStr->append ( extraPadding, ' ' );
+ stdStr->append ( kPacketTrailer );
} // PackageForJPEG
-
// -------------------------------------------------------------------------------------------------
// MergeFromJPEG
// -------------
@@ -1868,12 +1676,12 @@ XMPUtils::MergeFromJPEG ( XMPMeta * fullXMP,
const XMPMeta & extendedXMP )
{
- XMPUtils::AppendProperties ( extendedXMP, fullXMP, kXMPUtil_DoAllProperties );
+ XMP_OptionBits apFlags = (kXMPTemplate_ReplaceExistingProperties | kXMPTemplate_IncludeInternalProperties);
+ XMPUtils::ApplyTemplate ( fullXMP, extendedXMP, apFlags );
fullXMP->DeleteProperty ( kXMP_NS_XMP_Note, "HasExtendedXMP" );
} // MergeFromJPEG
-
// -------------------------------------------------------------------------------------------------
// CurrentDateTime
// ---------------
@@ -1891,20 +1699,22 @@ XMPUtils::CurrentDateTime ( XMP_DateTime * xmpTime )
xmpTime->year = currTime.tm_year + 1900;
xmpTime->month = currTime.tm_mon + 1;
xmpTime->day = currTime.tm_mday;
+ xmpTime->hasDate = true;
+
xmpTime->hour = currTime.tm_hour;
xmpTime->minute = currTime.tm_min;
xmpTime->second = currTime.tm_sec;
-
xmpTime->nanoSecond = 0;
+ xmpTime->hasTime = true;
+
xmpTime->tzSign = 0;
xmpTime->tzHour = 0;
xmpTime->tzMinute = 0;
-
+ xmpTime->hasTimeZone = false; // ! Needed for SetTimeZone.
XMPUtils::SetTimeZone ( xmpTime );
} // CurrentDateTime
-
// -------------------------------------------------------------------------------------------------
// SetTimeZone
// -----------
@@ -1918,8 +1728,10 @@ XMPUtils::SetTimeZone ( XMP_DateTime * xmpTime )
{
XMP_Assert ( xmpTime != 0 ); // ! Enforced by wrapper.
- if ( (xmpTime->tzSign != 0) || (xmpTime->tzHour != 0) || (xmpTime->tzMinute != 0) ) {
- XMP_Throw ( "SetTimeZone can only be used on \"zoneless\" times", kXMPErr_BadParam );
+ VerifyDateTimeFlags ( xmpTime );
+
+ if ( xmpTime->hasTimeZone ) {
+ XMP_Throw ( "SetTimeZone can only be used on zone-less times", kXMPErr_BadParam );
}
// Create ansi_tt form of the input time. Need the ansi_tm form to make the ansi_tt form.
@@ -1991,6 +1803,8 @@ XMPUtils::SetTimeZone ( XMP_DateTime * xmpTime )
}
xmpTime->tzHour = XMP_Int32 ( diffSecs / 3600.0 );
xmpTime->tzMinute = XMP_Int32 ( (diffSecs / 60.0) - (xmpTime->tzHour * 60.0) );
+
+ xmpTime->hasTimeZone = xmpTime->hasTime = true;
// *** Save the tm_isdst flag in a qualifier?
@@ -2002,7 +1816,6 @@ XMPUtils::SetTimeZone ( XMP_DateTime * xmpTime )
} // SetTimeZone
-
// -------------------------------------------------------------------------------------------------
// ConvertToUTCTime
// ----------------
@@ -2012,12 +1825,16 @@ XMPUtils::ConvertToUTCTime ( XMP_DateTime * time )
{
XMP_Assert ( time != 0 ); // ! Enforced by wrapper.
+ VerifyDateTimeFlags ( time );
+
+ if ( ! time->hasTimeZone ) return; // Do nothing if there is no current time zone.
+
XMP_Assert ( (0 <= time->tzHour) && (time->tzHour <= 23) );
XMP_Assert ( (0 <= time->tzMinute) && (time->tzMinute <= 59) );
XMP_Assert ( (-1 <= time->tzSign) && (time->tzSign <= +1) );
XMP_Assert ( (time->tzSign == 0) ? ((time->tzHour == 0) && (time->tzMinute == 0)) :
((time->tzHour != 0) || (time->tzMinute != 0)) );
-
+
if ( time->tzSign == kXMP_TimeEastOfUTC ) {
// We are before (east of) GMT, subtract the offset from the time.
time->hour -= time->tzHour;
@@ -2033,7 +1850,6 @@ XMPUtils::ConvertToUTCTime ( XMP_DateTime * time )
} // ConvertToUTCTime
-
// -------------------------------------------------------------------------------------------------
// ConvertToLocalTime
// ------------------
@@ -2043,6 +1859,10 @@ XMPUtils::ConvertToLocalTime ( XMP_DateTime * time )
{
XMP_Assert ( time != 0 ); // ! Enforced by wrapper.
+ VerifyDateTimeFlags ( time );
+
+ if ( ! time->hasTimeZone ) return; // Do nothing if there is no current time zone.
+
XMP_Assert ( (0 <= time->tzHour) && (time->tzHour <= 23) );
XMP_Assert ( (0 <= time->tzMinute) && (time->tzMinute <= 59) );
XMP_Assert ( (-1 <= time->tzSign) && (time->tzSign <= +1) );
@@ -2050,6 +1870,7 @@ XMPUtils::ConvertToLocalTime ( XMP_DateTime * time )
((time->tzHour != 0) || (time->tzMinute != 0)) );
ConvertToUTCTime ( time ); // The existing time zone might not be the local one.
+ time->hasTimeZone = false; // ! Needed for SetTimeZone.
SetTimeZone ( time ); // Fill in the local timezone offset, then adjust the time.
if ( time->tzSign > 0 ) {
@@ -2066,7 +1887,6 @@ XMPUtils::ConvertToLocalTime ( XMP_DateTime * time )
} // ConvertToLocalTime
-
// -------------------------------------------------------------------------------------------------
// CompareDateTime
// ---------------
@@ -2075,46 +1895,69 @@ XMPUtils::ConvertToLocalTime ( XMP_DateTime * time )
XMPUtils::CompareDateTime ( const XMP_DateTime & _in_left,
const XMP_DateTime & _in_right )
{
- int result;
+ int result = 0;
XMP_DateTime left = _in_left;
XMP_DateTime right = _in_right;
+
+ VerifyDateTimeFlags ( &left );
+ VerifyDateTimeFlags ( &right );
+
+ // Can't compare if one has a date and the other does not.
+ if ( left.hasDate != right.hasDate ) return 0; // Throw?
+
+ if ( left.hasTimeZone & right.hasTimeZone ) {
+ // If both times have zones then convert them to UTC, otherwise assume the same zone.
+ ConvertToUTCTime ( &left );
+ ConvertToUTCTime ( &right );
+ }
+
+ if ( left.hasDate ) {
+
+ XMP_Assert ( right.hasDate );
+
+ if ( left.year < right.year ) {
+ result = -1;
+ } else if ( left.year > right.year ) {
+ result = +1;
+ } else if ( left.month < right.month ) {
+ result = -1;
+ } else if ( left.month > right.month ) {
+ result = +1;
+ } else if ( left.day < right.day ) {
+ result = -1;
+ } else if ( left.day > right.day ) {
+ result = +1;
+ }
+
+ if ( result != 0 ) return result;
+
+ }
+
+ if ( left.hasTime & right.hasTime ) {
+
+ // Ignore the time parts if either value is date-only.
+
+ if ( left.hour < right.hour ) {
+ result = -1;
+ } else if ( left.hour > right.hour ) {
+ result = +1;
+ } else if ( left.minute < right.minute ) {
+ result = -1;
+ } else if ( left.minute > right.minute ) {
+ result = +1;
+ } else if ( left.second < right.second ) {
+ result = -1;
+ } else if ( left.second > right.second ) {
+ result = +1;
+ } else if ( left.nanoSecond < right.nanoSecond ) {
+ result = -1;
+ } else if ( left.nanoSecond > right.nanoSecond ) {
+ result = +1;
+ } else {
+ result = 0;
+ }
- ConvertToUTCTime ( &left );
- ConvertToUTCTime ( &right );
-
- // *** We could use memcmp if the XMP_DateTime stuct has no holes.
-
- if ( left.year < right.year ) {
- result = -1;
- } else if ( left.year > right.year ) {
- result = +1;
- } else if ( left.month < right.month ) {
- result = -1;
- } else if ( left.month > right.month ) {
- result = +1;
- } else if ( left.day < right.day ) {
- result = -1;
- } else if ( left.day > right.day ) {
- result = +1;
- } else if ( left.hour < right.hour ) {
- result = -1;
- } else if ( left.hour > right.hour ) {
- result = +1;
- } else if ( left.minute < right.minute ) {
- result = -1;
- } else if ( left.minute > right.minute ) {
- result = +1;
- } else if ( left.second < right.second ) {
- result = -1;
- } else if ( left.second > right.second ) {
- result = +1;
- } else if ( left.nanoSecond < right.nanoSecond ) {
- result = -1;
- } else if ( left.nanoSecond > right.nanoSecond ) {
- result = +1;
- } else {
- result = 0;
}
return result;
diff --git a/source/XMPCore/XMPUtils.hpp b/source/XMPCore/XMPUtils.hpp
index e1c7e78..a7d0153 100644
--- a/source/XMPCore/XMPUtils.hpp
+++ b/source/XMPCore/XMPUtils.hpp
@@ -2,7 +2,7 @@
#define __XMPUtils_hpp__
// =================================================================================================
-// Copyright 2002-2007 Adobe Systems Incorporated
+// Copyright 2003 Adobe Systems Incorporated
// All Rights Reserved.
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -18,16 +18,6 @@
// -------------------------------------------------------------------------------------------------
-extern XMP_VarString * sComposedPath; // *** Only really need 1 string. Shrink periodically?
-extern XMP_VarString * sConvertedValue;
-extern XMP_VarString * sBase64Str;
-extern XMP_VarString * sCatenatedItems;
-extern XMP_VarString * sStandardXMP;
-extern XMP_VarString * sExtendedXMP;
-extern XMP_VarString * sExtendedDigest;
-
-// -------------------------------------------------------------------------------------------------
-
class XMPUtils {
public:
@@ -37,40 +27,33 @@ public:
static void
Terminate() RELEASE_NO_THROW; // ! For internal use only!
- static void
- Unlock ( XMP_OptionBits options );
-
// ---------------------------------------------------------------------------------------------
static void
ComposeArrayItemPath ( XMP_StringPtr schemaNS,
XMP_StringPtr arrayName,
XMP_Index itemIndex,
- XMP_StringPtr * fullPath,
- XMP_StringLen * pathSize );
+ XMP_VarString * fullPath );
static void
ComposeStructFieldPath ( XMP_StringPtr schemaNS,
XMP_StringPtr structName,
XMP_StringPtr fieldNS,
XMP_StringPtr fieldName,
- XMP_StringPtr * fullPath,
- XMP_StringLen * pathSize );
+ XMP_VarString * fullPath );
static void
ComposeQualifierPath ( XMP_StringPtr schemaNS,
XMP_StringPtr propName,
XMP_StringPtr qualNS,
XMP_StringPtr qualName,
- XMP_StringPtr * fullPath,
- XMP_StringLen * pathSize );
+ XMP_VarString * fullPath );
static void
- ComposeLangSelector ( XMP_StringPtr schemaNS,
- XMP_StringPtr arrayName,
- XMP_StringPtr langName,
- XMP_StringPtr * fullPath,
- XMP_StringLen * pathSize );
+ ComposeLangSelector ( XMP_StringPtr schemaNS,
+ XMP_StringPtr arrayName,
+ XMP_StringPtr langName,
+ XMP_VarString * fullPath );
static void
ComposeFieldSelector ( XMP_StringPtr schemaNS,
@@ -78,38 +61,32 @@ public:
XMP_StringPtr fieldNS,
XMP_StringPtr fieldName,
XMP_StringPtr fieldValue,
- XMP_StringPtr * fullPath,
- XMP_StringLen * pathSize );
+ XMP_VarString * fullPath );
// ---------------------------------------------------------------------------------------------
static void
ConvertFromBool ( bool binValue,
- XMP_StringPtr * strValue,
- XMP_StringLen * strSize );
+ XMP_VarString * strValue );
static void
ConvertFromInt ( XMP_Int32 binValue,
XMP_StringPtr format,
- XMP_StringPtr * strValue,
- XMP_StringLen * strSize );
+ XMP_VarString * strValue );
static void
ConvertFromInt64 ( XMP_Int64 binValue,
XMP_StringPtr format,
- XMP_StringPtr * strValue,
- XMP_StringLen * strSize );
+ XMP_VarString * strValue );
static void
ConvertFromFloat ( double binValue,
XMP_StringPtr format,
- XMP_StringPtr * strValue,
- XMP_StringLen * strSize );
+ XMP_VarString * strValue );
static void
ConvertFromDate ( const XMP_DateTime & binValue,
- XMP_StringPtr * strValue,
- XMP_StringLen * strSize );
+ XMP_VarString * strValue );
// ---------------------------------------------------------------------------------------------
@@ -151,25 +128,20 @@ public:
static void
EncodeToBase64 ( XMP_StringPtr rawStr,
XMP_StringLen rawLen,
- XMP_StringPtr * encodedStr,
- XMP_StringLen * encodedLen );
+ XMP_VarString * encodedStr );
static void
DecodeFromBase64 ( XMP_StringPtr encodedStr,
XMP_StringLen encodedLen,
- XMP_StringPtr * rawStr,
- XMP_StringLen * rawLen );
+ XMP_VarString * rawStr );
// ---------------------------------------------------------------------------------------------
static void
PackageForJPEG ( const XMPMeta & xmpObj,
- XMP_StringPtr * stdStr,
- XMP_StringLen * stdLen,
- XMP_StringPtr * extStr,
- XMP_StringLen * extLen,
- XMP_StringPtr * digestStr,
- XMP_StringLen * digestLen );
+ XMP_VarString * stdStr,
+ XMP_VarString * extStr,
+ XMP_VarString * digestStr );
static void
MergeFromJPEG ( XMPMeta * fullXMP,
@@ -184,8 +156,7 @@ public:
XMP_StringPtr separator,
XMP_StringPtr quotes,
XMP_OptionBits options,
- XMP_StringPtr * catedStr,
- XMP_StringLen * catedLen );
+ XMP_VarString * catedStr );
static void
SeparateArrayItems ( XMPMeta * xmpObj,
@@ -195,17 +166,17 @@ public:
XMP_StringPtr catedStr );
static void
+ ApplyTemplate ( XMPMeta * workingXMP,
+ const XMPMeta & templateXMP,
+ XMP_OptionBits actions );
+
+ static void
RemoveProperties ( XMPMeta * xmpObj,
XMP_StringPtr schemaNS,
XMP_StringPtr propName,
XMP_OptionBits options );
static void
- AppendProperties ( const XMPMeta & source,
- XMPMeta * dest,
- XMP_OptionBits options );
-
- static void
DuplicateSubtree ( const XMPMeta & source,
XMPMeta * dest,
XMP_StringPtr sourceNS,
diff --git a/source/XMPFiles/FileHandlers/ASF_Handler.cpp b/source/XMPFiles/FileHandlers/ASF_Handler.cpp
index 5726533..ac6aad7 100644
--- a/source/XMPFiles/FileHandlers/ASF_Handler.cpp
+++ b/source/XMPFiles/FileHandlers/ASF_Handler.cpp
@@ -1,6 +1,6 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2008 Adobe Systems Incorporated
+// Copyright 2006 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -114,17 +114,6 @@ void ASF_MetaHandler::CacheFileData()
} // ASF_MetaHandler::CacheFileData
// =================================================================================================
-// ASF_MetaHandler::ProcessTNail
-// ==============================
-
-void ASF_MetaHandler::ProcessTNail()
-{
-
- XMP_Throw ( "ASF_MetaHandler::ProcessTNail isn't implemented yet", kXMPErr_Unimplemented );
-
-} // ASF_MetaHandler::ProcessTNail
-
-// =================================================================================================
// ASF_MetaHandler::ProcessXMP
// ============================
//
@@ -341,6 +330,7 @@ bool ASF_MetaHandler::SafeWriteFile ()
ret = true;
} catch ( ... ) {
LFA_Close ( updateRef );
+ LFA_Delete ( updatePath.c_str() );
this->parent->filePath = origPath;
this->parent->fileRef = origRef;
throw;
diff --git a/source/XMPFiles/FileHandlers/ASF_Handler.hpp b/source/XMPFiles/FileHandlers/ASF_Handler.hpp
index 9f54b15..e5fec1c 100644
--- a/source/XMPFiles/FileHandlers/ASF_Handler.hpp
+++ b/source/XMPFiles/FileHandlers/ASF_Handler.hpp
@@ -3,7 +3,7 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2007 Adobe Systems Incorporated
+// Copyright 2006 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -26,9 +26,9 @@
extern XMPFileHandler* ASF_MetaHandlerCTor ( XMPFiles* parent );
extern bool ASF_CheckFormat ( XMP_FileFormat format,
- XMP_StringPtr filePath,
- LFA_FileRef fileRef,
- XMPFiles* parent );
+ XMP_StringPtr filePath,
+ LFA_FileRef fileRef,
+ XMPFiles * parent );
static const XMP_OptionBits kASF_HandlerFlags = ( kXMPFiles_CanInjectXMP |
kXMPFiles_CanExpand |
@@ -36,15 +36,13 @@ static const XMP_OptionBits kASF_HandlerFlags = ( kXMPFiles_CanInjectXMP |
kXMPFiles_CanReconcile |
kXMPFiles_AllowsOnlyXMP |
kXMPFiles_ReturnsRawPacket |
- kXMPFiles_NeedsReadOnlyPacket
- );
+ kXMPFiles_NeedsReadOnlyPacket );
class ASF_MetaHandler : public XMPFileHandler
{
public:
void CacheFileData();
- void ProcessTNail();
void ProcessXMP();
void UpdateFile ( bool doSafeUpdate );
diff --git a/source/XMPFiles/FileHandlers/AVCHD_Handler.cpp b/source/XMPFiles/FileHandlers/AVCHD_Handler.cpp
index 63b8f29..f842a73 100644
--- a/source/XMPFiles/FileHandlers/AVCHD_Handler.cpp
+++ b/source/XMPFiles/FileHandlers/AVCHD_Handler.cpp
@@ -1,6 +1,6 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2008 Adobe Systems Incorporated
+// Copyright 2008 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -10,9 +10,16 @@
#include "AVCHD_Handler.hpp"
#include "MD5.h"
+#include "UnicodeConversions.hpp"
using namespace std;
+// AVCHD maker ID values. Panasonic has confirmed their Maker ID with us, the others come from examining
+// sample data files.
+#define kMakerIDPanasonic 0x103
+#define kMakerIDSony 0x108
+#define kMakerIDCanon 0x1011
+
// =================================================================================================
/// \file AVCHD_Handler.cpp
/// \brief Folder format handler for AVCHD.
@@ -85,6 +92,319 @@ struct AVCHD_blkProgramInfo
};
+// AVCHD Format, Panasonic proprietary PRO_PlayListMark block
+
+struct AVCCAM_blkProPlayListMark
+{
+ XMP_Uns8 mPresent;
+ XMP_Uns8 mProTagID;
+ XMP_Uns8 mFillItem1;
+ XMP_Uns16 mLength;
+ XMP_Uns8 mMarkType;
+
+ // Entry mark
+ struct
+ {
+ XMP_Uns8 mGlobalClipID[32];
+ XMP_Uns8 mStartTimeCode[4];
+ XMP_Uns8 mStreamTimecodeInfo;
+ XMP_Uns8 mStartBinaryGroup[4];
+ XMP_Uns8 mLastUpdateTimeZone;
+ XMP_Uns8 mLastUpdateDate[7];
+ XMP_Uns16 mFillItem;
+ } mEntryMark;
+
+ // Shot Mark
+ struct
+ {
+ XMP_Uns8 mPresent;
+ XMP_Uns8 mShotMark;
+ XMP_Uns8 mFillItem[3];
+ } mShotMark;
+
+ // Access
+ struct
+ {
+ XMP_Uns8 mPresent;
+ XMP_Uns8 mCreatorCharacterSet;
+ XMP_Uns8 mCreatorLength;
+ XMP_Uns8 mCreator[32];
+ XMP_Uns8 mLastUpdatePersonCharacterSet;
+ XMP_Uns8 mLastUpdatePersonLength;
+ XMP_Uns8 mLastUpdatePerson[32];
+ } mAccess;
+
+ // Device
+ struct
+ {
+ XMP_Uns8 mPresent;
+ XMP_Uns16 mMakerID;
+ XMP_Uns16 mMakerModelCode;
+ XMP_Uns8 mSerialNoCharacterCode;
+ XMP_Uns8 mSerialNoLength;
+ XMP_Uns8 mSerialNo[24];
+ XMP_Uns16 mFillItem;
+ } mDevice;
+
+ // Shoot
+ struct
+ {
+ XMP_Uns8 mPresent;
+ XMP_Uns8 mShooterCharacterSet;
+ XMP_Uns8 mShooterLength;
+ XMP_Uns8 mShooter[32];
+ XMP_Uns8 mStartDateTimeZone;
+ XMP_Uns8 mStartDate[7];
+ XMP_Uns8 mEndDateTimeZone;
+ XMP_Uns8 mEndDate[7];
+ XMP_Uns16 mFillItem;
+ } mShoot;
+
+ // Location
+ struct
+ {
+ XMP_Uns8 mPresent;
+ XMP_Uns8 mSource;
+ XMP_Uns32 mGPSLatitudeRef;
+ XMP_Uns32 mGPSLatitude1;
+ XMP_Uns32 mGPSLatitude2;
+ XMP_Uns32 mGPSLatitude3;
+ XMP_Uns32 mGPSLongitudeRef;
+ XMP_Uns32 mGPSLongitude1;
+ XMP_Uns32 mGPSLongitude2;
+ XMP_Uns32 mGPSLongitude3;
+ XMP_Uns32 mGPSAltitudeRef;
+ XMP_Uns32 mGPSAltitude;
+ XMP_Uns8 mPlaceNameCharacterSet;
+ XMP_Uns8 mPlaceNameLength;
+ XMP_Uns8 mPlaceName[64];
+ XMP_Uns8 mFillItem;
+ } mLocation;
+};
+
+// AVCHD Format, Panasonic proprietary extension data (AVCCAM)
+
+struct AVCCAM_Pro_PlayListInfo
+{
+ XMP_Uns8 mPresent;
+ XMP_Uns8 mTagID;
+ XMP_Uns8 mTagVersion;
+ XMP_Uns16 mFillItem1;
+ XMP_Uns32 mLength;
+ XMP_Uns16 mNumberOfPlayListMarks;
+ XMP_Uns16 mFillItem2;
+
+ // Although a playlist may contain multiple marks, we only store the one that corresponds to
+ // the clip/shot of interest.
+ AVCCAM_blkProPlayListMark mPlayListMark;
+};
+
+// AVCHD Format, Panasonic proprietary extension data (AVCCAM)
+
+struct AVCHD_blkPanasonicPrivateData
+{
+ XMP_Uns8 mPresent;
+ XMP_Uns16 mNumberOfData;
+ XMP_Uns16 mReserved;
+
+ struct
+ {
+ XMP_Uns8 mPresent;
+ XMP_Uns8 mTagID;
+ XMP_Uns8 mTagVersion;
+ XMP_Uns16 mTagLength;
+ XMP_Uns8 mProfessionalMetaID[16];
+ } mProMetaIDBlock;
+
+ struct
+ {
+ XMP_Uns8 mPresent;
+ XMP_Uns8 mTagID;
+ XMP_Uns8 mTagVersion;
+ XMP_Uns16 mTagLength;
+ XMP_Uns8 mGlobalClipID[32];
+ XMP_Uns8 mStartTimecode[4];
+ XMP_Uns32 mStartBinaryGroup;
+ } mProClipIDBlock;
+
+ AVCCAM_Pro_PlayListInfo mProPlaylistInfoBlock;
+};
+
+// AVCHD Format. Book 2: Recording Extension Specifications, section 4.2.4.2. plus Panasonic extensions
+
+struct AVCHD_blkMakersPrivateData
+{
+ XMP_Uns8 mPresent;
+ XMP_Uns32 mLength;
+ XMP_Uns32 mDataBlockStartAddress;
+ XMP_Uns8 mReserved[3];
+ XMP_Uns8 mNumberOfMakerEntries;
+ XMP_Uns16 mMakerID;
+ XMP_Uns16 mMakerModelCode;
+ AVCHD_blkPanasonicPrivateData mPanasonicPrivateData;
+};
+
+// AVCHD Format. Book 2: Recording Extension Specifications, section 4.4.2.1
+
+struct AVCHD_blkClipInfoExt
+{
+ XMP_Uns32 mLength;
+ XMP_Uns16 mMakerID;
+ XMP_Uns16 mMakerModelCode;
+};
+
+// AVCHD Format. Book 2: Recording Extension Specifications, section 4.4.1.2
+
+struct AVCHD_blkClipExtensionData
+{
+ XMP_Uns8 mPresent;
+ XMP_Uns8 mTypeIndicator[4];
+ XMP_Uns8 mReserved1[4];
+ XMP_Uns32 mProgramInfoExtStartAddress;
+ XMP_Uns32 mMakersPrivateDataStartAddress;
+
+ AVCHD_blkClipInfoExt mClipInfoExt;
+ AVCHD_blkMakersPrivateData mMakersPrivateData;
+};
+
+// AVCHD Format. Book 2: Recording Extension Specifications, section 4.3.3.1 -- although each playlist
+// may contain a list of these, we only record the one that matches our target shot/clip.
+
+struct AVCHD_blkPlayListMarkExt
+{
+ XMP_Uns32 mLength;
+ XMP_Uns16 mNumberOfPlaylistMarks;
+ bool mPresent;
+ XMP_Uns16 mMakerID;
+ XMP_Uns16 mMakerModelCode;
+ XMP_Uns8 mReserved1[3];
+ XMP_Uns8 mFlags; // bit 0: MarkWriteProtectFlag, bits 1-2: pulldown
+ XMP_Uns16 mRefToMarkThumbnailIndex;
+ XMP_Uns8 mBlkTimezone;
+ XMP_Uns8 mRecordDataAndTime[7];
+ XMP_Uns8 mMarkCharacterSet;
+ XMP_Uns8 mMarkNameLength;
+ XMP_Uns8 mMarkName[24];
+ XMP_Uns8 mMakersInformation[16];
+ XMP_Uns8 mBlkTimecode[4];
+ XMP_Uns16 mReserved2;
+};
+
+// AVCHD Format. Book 2: Recording Extension Specifications, section 4.3.2.1
+
+struct AVCHD_blkPlaylistMeta
+{
+ XMP_Uns32 mLength;
+ XMP_Uns16 mMakerID;
+ XMP_Uns16 mMakerModelCode;
+ XMP_Uns32 mReserved1;
+ XMP_Uns16 mRefToMenuThumbnailIndex;
+ XMP_Uns8 mBlkTimezone;
+ XMP_Uns8 mRecordDataAndTime[7];
+ XMP_Uns8 mReserved2;
+ XMP_Uns8 mPlaylistCharacterSet;
+ XMP_Uns8 mPlaylistNameLength;
+ XMP_Uns8 mPlaylistName[255];
+};
+
+// AVCHD Format. Book 2: Recording Extension Specifications, section 4.3.1.2
+
+struct AVCHD_blkPlayListExtensionData
+{
+ XMP_Uns8 mPresent;
+ char mTypeIndicator[4];
+ XMP_Uns8 mReserved[4];
+ XMP_Uns32 mPlayListMarkExtStartAddress;
+ XMP_Uns32 mMakersPrivateDataStartAddress;
+
+ AVCHD_blkPlaylistMeta mPlaylistMeta;
+ AVCHD_blkPlayListMarkExt mPlaylistMarkExt;
+ AVCHD_blkMakersPrivateData mMakersPrivateData;
+};
+
+// AVCHD Format. Book 1: Playback System Basic Specifications V 1.01. p. 38
+struct AVCHD_blkExtensionData
+{
+ XMP_Uns32 mLength;
+ XMP_Uns32 mDataBlockStartAddress;
+ XMP_Uns8 mReserved[3];
+ XMP_Uns8 mNumberOfDataEntries;
+
+ struct AVCHD_blkExtDataEntry
+ {
+ XMP_Uns16 mExtDataType;
+ XMP_Uns16 mExtDataVersion;
+ XMP_Uns32 mExtDataStartAddress;
+ XMP_Uns32 mExtDataLength;
+ } mExtDataEntry;
+};
+
+// Simple container for the various AVCHD legacy metadata structures we care about for an AVCHD clip
+
+struct AVCHD_LegacyMetadata
+{
+ AVCHD_blkProgramInfo mProgramInfo;
+ AVCHD_blkClipExtensionData mClipExtensionData;
+ AVCHD_blkPlayListExtensionData mPlaylistExtensionData;
+};
+
+// =================================================================================================
+// MakeLeafPath
+// ============
+
+static bool MakeLeafPath ( std::string * path, XMP_StringPtr root, XMP_StringPtr group,
+ XMP_StringPtr clip, XMP_StringPtr suffix, bool checkFile = false )
+{
+ size_t partialLen;
+
+ *path = root;
+ *path += kDirChar;
+ *path += "BDMV";
+ *path += kDirChar;
+ *path += group;
+ *path += kDirChar;
+ *path += clip;
+ partialLen = path->size();
+ *path += suffix;
+
+ if ( ! checkFile ) return true;
+ if ( GetFileMode ( path->c_str() ) == kFMode_IsFile ) return true;
+
+ // Convert the suffix to uppercase and try again. Even on Mac/Win, in case a remote file system is sensitive.
+ for ( char* chPtr = ((char*)path->c_str() + partialLen); *chPtr != 0; ++chPtr ) {
+ if ( (0x61 <= *chPtr) && (*chPtr <= 0x7A) ) *chPtr -= 0x20;
+ }
+ if ( GetFileMode ( path->c_str() ) == kFMode_IsFile ) return true;
+
+ if ( XMP_LitMatch ( suffix, ".clpi" ) ) { // Special case of ".cpi" for the clip file.
+
+ path->erase ( partialLen );
+ *path += ".cpi";
+ if ( GetFileMode ( path->c_str() ) == kFMode_IsFile ) return true;
+
+ path->erase ( partialLen );
+ *path += ".CPI";
+ if ( GetFileMode ( path->c_str() ) == kFMode_IsFile ) return true;
+
+ } else if ( XMP_LitMatch ( suffix, ".mpls" ) ) { // Special case of ".mpl" for the playlist file.
+
+ path->erase ( partialLen );
+ *path += ".mpl";
+ if ( GetFileMode ( path->c_str() ) == kFMode_IsFile ) return true;
+
+ path->erase ( partialLen );
+ *path += ".MPL";
+ if ( GetFileMode ( path->c_str() ) == kFMode_IsFile ) return true;
+
+ }
+
+ // Still not found, revert to the original suffix.
+ path->erase ( partialLen );
+ *path += suffix;
+ return false;
+
+} // MakeLeafPath
+
// =================================================================================================
// AVCHD_CheckFormat
// =================
@@ -138,36 +458,29 @@ bool AVCHD_CheckFormat ( XMP_FileFormat format,
if ( GetChildMode ( bdmvPath, "STREAM" ) != kFMode_IsFolder ) return false;
if ( (GetChildMode ( bdmvPath, "index.bdmv" ) != kFMode_IsFile) &&
- (GetChildMode ( bdmvPath, "index.bdm" ) != kFMode_IsFile) ) return false;
+ (GetChildMode ( bdmvPath, "index.bdm" ) != kFMode_IsFile) &&
+ (GetChildMode ( bdmvPath, "INDEX.BDMV" ) != kFMode_IsFile) && // Some usage is all caps.
+ (GetChildMode ( bdmvPath, "INDEX.BDM" ) != kFMode_IsFile) ) return false;
if ( (GetChildMode ( bdmvPath, "MovieObject.bdmv" ) != kFMode_IsFile) &&
- (GetChildMode ( bdmvPath, "MovieObj.bdm" ) != kFMode_IsFile) ) return false;
+ (GetChildMode ( bdmvPath, "MovieObj.bdm" ) != kFMode_IsFile) &&
+ (GetChildMode ( bdmvPath, "MOVIEOBJECT.BDMV" ) != kFMode_IsFile) && // Some usage is all caps.
+ (GetChildMode ( bdmvPath, "MOVIEOBJ.BDM" ) != kFMode_IsFile) ) return false;
// Make sure the .clpi file exists.
- std::string tempPath ( bdmvPath );
- tempPath += kDirChar;
- tempPath += "CLIPINF";
- tempPath += kDirChar;
- tempPath += leafName;
- tempPath += ".clpi";
- const bool foundClpi = ( GetFileMode ( tempPath.c_str() ) == kFMode_IsFile );
-
- // No .clpi -- check for .cpi instead.
- if ( ! foundClpi ) {
- tempPath.erase ( tempPath.size() - 5 ); // Remove the ".clpi" part.
- tempPath += ".cpi";
- if ( GetFileMode ( tempPath.c_str() ) != kFMode_IsFile ) return false;
- }
+ std::string tempPath;
+ bool foundClpi = MakeLeafPath ( &tempPath, rootPath.c_str(), "CLIPINF", leafName.c_str(), ".clpi", true /* checkFile */ );
+ if ( ! foundClpi ) return false;
// And now save the pseudo path for the handler object.
tempPath = rootPath;
tempPath += kDirChar;
tempPath += leafName;
size_t pathLen = tempPath.size() + 1; // Include a terminating nul.
- parent->handlerTemp = malloc ( pathLen );
- if ( parent->handlerTemp == 0 ) XMP_Throw ( "No memory for AVCHD clip info", kXMPErr_NoMemory );
- memcpy ( parent->handlerTemp, tempPath.c_str(), pathLen );
+ parent->tempPtr = malloc ( pathLen );
+ if ( parent->tempPtr == 0 ) XMP_Throw ( "No memory for AVCHD clip info", kXMPErr_NoMemory );
+ memcpy ( parent->tempPtr, tempPath.c_str(), pathLen );
return true;
@@ -175,130 +488,1114 @@ bool AVCHD_CheckFormat ( XMP_FileFormat format,
// =================================================================================================
// ReadAVCHDProgramInfo
+// ============
+
+static bool ReadAVCHDProgramInfo ( LFA_FileRef& cpiFileRef, AVCHD_blkProgramInfo& avchdProgramInfo )
+{
+ avchdProgramInfo.mLength = LFA_ReadUns32_BE ( cpiFileRef );
+ LFA_Read ( cpiFileRef, avchdProgramInfo.mReserved1, 2 );
+ avchdProgramInfo.mSPNProgramSequenceStart = LFA_ReadUns32_BE ( cpiFileRef );
+ avchdProgramInfo.mProgramMapPID = LFA_ReadUns16_BE ( cpiFileRef );
+ LFA_Read ( cpiFileRef, &avchdProgramInfo.mNumberOfStreamsInPS, 1 );
+ LFA_Read ( cpiFileRef, &avchdProgramInfo.mReserved2, 1 );
+
+ XMP_Uns16 streamPID = 0;
+ for ( int i=0; i<avchdProgramInfo.mNumberOfStreamsInPS; ++i ) {
+
+ XMP_Uns8 length = 0;
+ XMP_Uns8 streamCodingType = 0;
+
+ streamPID = LFA_ReadUns16_BE ( cpiFileRef );
+ LFA_Read ( cpiFileRef, &length, 1 );
+
+ XMP_Int64 pos = LFA_Tell ( cpiFileRef );
+
+ LFA_Read ( cpiFileRef, &streamCodingType, 1 );
+
+ switch ( streamCodingType ) {
+
+ case 0x1B : // Video stream case.
+ {
+ XMP_Uns8 videoFormatAndFrameRate;
+ LFA_Read ( cpiFileRef, &videoFormatAndFrameRate, 1 );
+ avchdProgramInfo.mVideoStream.mVideoFormat = videoFormatAndFrameRate >> 4; // hi 4 bits
+ avchdProgramInfo.mVideoStream.mFrameRate = videoFormatAndFrameRate & 0x0f; // lo 4 bits
+
+ XMP_Uns8 aspectRatioAndReserved = 0;
+ LFA_Read ( cpiFileRef, &aspectRatioAndReserved, 1 );
+ avchdProgramInfo.mVideoStream.mAspectRatio = aspectRatioAndReserved >> 4; // hi 4 bits
+
+ XMP_Uns8 ccFlag = 0;
+ LFA_Read ( cpiFileRef, &ccFlag, 1 );
+ avchdProgramInfo.mVideoStream.mCCFlag = ccFlag;
+
+ avchdProgramInfo.mVideoStream.mPresent = 1;
+ }
+ break;
+
+ case 0x80 : // Fall through.
+ case 0x81 : // Audio stream case.
+ {
+ XMP_Uns8 audioPresentationTypeAndFrequency = 0;
+ LFA_Read ( cpiFileRef, &audioPresentationTypeAndFrequency, 1 );
+
+ avchdProgramInfo.mAudioStream.mAudioPresentationType = audioPresentationTypeAndFrequency >> 4; // hi 4 bits
+ avchdProgramInfo.mAudioStream.mSamplingFrequency = audioPresentationTypeAndFrequency & 0x0f; // lo 4 bits
+
+ LFA_Read ( cpiFileRef, avchdProgramInfo.mAudioStream.mAudioLanguageCode, 3 );
+ avchdProgramInfo.mAudioStream.mAudioLanguageCode[3] = 0;
+
+ avchdProgramInfo.mAudioStream.mPresent = 1;
+ }
+ break;
+
+ case 0x90 : // Overlay bitmap stream case.
+ LFA_Read ( cpiFileRef, &avchdProgramInfo.mOverlayBitmapStream.mOBLanguageCode, 3 );
+ avchdProgramInfo.mOverlayBitmapStream.mOBLanguageCode[3] = 0;
+ avchdProgramInfo.mOverlayBitmapStream.mPresent = 1;
+ break;
+
+ case 0x91 : // Menu bitmap stream.
+ LFA_Read ( cpiFileRef, &avchdProgramInfo.mMenuBitmapStream.mBMLanguageCode, 3 );
+ avchdProgramInfo.mMenuBitmapStream.mBMLanguageCode[3] = 0;
+ avchdProgramInfo.mMenuBitmapStream.mPresent = 1;
+ break;
+
+ default :
+ break;
+
+ }
+
+ LFA_Seek ( cpiFileRef, pos + length, SEEK_SET );
+
+ }
+
+ return true;
+}
+
+// =================================================================================================
+// ReadAVCHDExtensionData
+// ============
+
+static bool ReadAVCHDExtensionData ( LFA_FileRef& cpiFileRef, AVCHD_blkExtensionData& extensionDataHeader )
+{
+ extensionDataHeader.mLength = LFA_ReadUns32_BE ( cpiFileRef );
+
+ if ( extensionDataHeader.mLength == 0 ) {
+ // Nothing to read
+ return true;
+ }
+
+ extensionDataHeader.mDataBlockStartAddress = LFA_ReadUns32_BE ( cpiFileRef );
+ LFA_Read ( cpiFileRef, extensionDataHeader.mReserved, 3 );
+ LFA_Read ( cpiFileRef, &extensionDataHeader.mNumberOfDataEntries, 1 );
+
+ if ( extensionDataHeader.mNumberOfDataEntries != 1 ) {
+ // According to AVCHD Format. Book1. v. 1.01. p 38, "This field shall be set to 1 in this format."
+ return false;
+ }
+
+ extensionDataHeader.mExtDataEntry.mExtDataType = LFA_ReadUns16_BE ( cpiFileRef );
+ extensionDataHeader.mExtDataEntry.mExtDataVersion = LFA_ReadUns16_BE ( cpiFileRef );
+ extensionDataHeader.mExtDataEntry.mExtDataStartAddress = LFA_ReadUns32_BE ( cpiFileRef );
+ extensionDataHeader.mExtDataEntry.mExtDataLength = LFA_ReadUns32_BE ( cpiFileRef );
+
+ if ( extensionDataHeader.mExtDataEntry.mExtDataType != 0x1000 ) {
+ // According to AVCHD Format. Book1. v. 1.01. p 38, "If the metadata is for an AVCHD application,
+ // this value shall be set to 'Ox1OOO'."
+ return false;
+ }
+
+ return true;
+}
+
+// =================================================================================================
+// ReadAVCCAMProMetaID-- read Panasonic's proprietary PRO_MetaID block
+// ============
+
+static bool ReadAVCCAMProMetaID ( LFA_FileRef& cpiFileRef, XMP_Uns8 tagID, AVCHD_blkPanasonicPrivateData& extensionDataHeader )
+{
+ extensionDataHeader.mPresent = 1;
+ extensionDataHeader.mProMetaIDBlock.mPresent = 1;
+ extensionDataHeader.mProMetaIDBlock.mTagID = tagID;
+ LFA_Read ( cpiFileRef, &extensionDataHeader.mProMetaIDBlock.mTagVersion, 1);
+ extensionDataHeader.mProMetaIDBlock.mTagLength = LFA_ReadUns16_BE ( cpiFileRef );
+ LFA_Read ( cpiFileRef, &extensionDataHeader.mProMetaIDBlock.mProfessionalMetaID, 16);
+
+ return true;
+}
+
+// =================================================================================================
+// ReadAVCCAMProClipInfo-- read Panasonic's proprietary PRO_ClipInfo block
+// ============
+
+static bool ReadAVCCAMProClipInfo ( LFA_FileRef& cpiFileRef, XMP_Uns8 tagID, AVCHD_blkPanasonicPrivateData& extensionDataHeader )
+{
+ extensionDataHeader.mPresent = 1;
+ extensionDataHeader.mProClipIDBlock.mPresent = 1;
+ extensionDataHeader.mProClipIDBlock.mTagID = tagID;
+ LFA_Read ( cpiFileRef, &extensionDataHeader.mProClipIDBlock.mTagVersion, 1);
+ extensionDataHeader.mProClipIDBlock.mTagLength = LFA_ReadUns16_BE ( cpiFileRef );
+ LFA_Read ( cpiFileRef, &extensionDataHeader.mProClipIDBlock.mGlobalClipID, 32);
+ LFA_Read ( cpiFileRef, &extensionDataHeader.mProClipIDBlock.mStartTimecode, 4 );
+ extensionDataHeader.mProClipIDBlock.mStartBinaryGroup = LFA_ReadUns32_BE ( cpiFileRef );
+
+ return true;
+}
+
+// =================================================================================================
+// ReadAVCCAM_blkPRO_ShotMark -- read Panasonic's proprietary PRO_ShotMark block
+// ============
+
+static bool ReadAVCCAM_blkPRO_ShotMark ( LFA_FileRef& mplFileRef, AVCCAM_blkProPlayListMark& proMark )
+{
+ proMark.mShotMark.mPresent = 1;
+ LFA_Read ( mplFileRef, &proMark.mShotMark.mShotMark, 1);
+ LFA_Read ( mplFileRef, &proMark.mShotMark.mFillItem, 3);
+
+ return true;
+}
+
+// =================================================================================================
+// ReadAVCCAM_blkPRO_Access -- read Panasonic's proprietary PRO_Access block
+// ============
+
+static bool ReadAVCCAM_blkPRO_Access ( LFA_FileRef& mplFileRef, AVCCAM_blkProPlayListMark& proMark )
+{
+ proMark.mAccess.mPresent = 1;
+ LFA_Read ( mplFileRef, &proMark.mAccess.mCreatorCharacterSet, 1 );
+ LFA_Read ( mplFileRef, &proMark.mAccess.mCreatorLength, 1 );
+ LFA_Read ( mplFileRef, &proMark.mAccess.mCreator, 32 );
+ LFA_Read ( mplFileRef, &proMark.mAccess.mLastUpdatePersonCharacterSet, 1 );
+ LFA_Read ( mplFileRef, &proMark.mAccess.mLastUpdatePersonLength, 1 );
+ LFA_Read ( mplFileRef, &proMark.mAccess.mLastUpdatePerson, 32 );
+
+ return true;
+}
+
+// =================================================================================================
+// ReadAVCCAM_blkPRO_Device -- read Panasonic's proprietary PRO_Device block
+// ============
+
+static bool ReadAVCCAM_blkPRO_Device ( LFA_FileRef& mplFileRef, AVCCAM_blkProPlayListMark& proMark )
+{
+ proMark.mDevice.mPresent = 1;
+ proMark.mDevice.mMakerID = LFA_ReadUns16_BE ( mplFileRef );
+ proMark.mDevice.mMakerModelCode = LFA_ReadUns16_BE ( mplFileRef );
+ LFA_Read ( mplFileRef, &proMark.mDevice.mSerialNoCharacterCode, 1 );
+ LFA_Read ( mplFileRef, &proMark.mDevice.mSerialNoLength, 1 );
+ LFA_Read ( mplFileRef, &proMark.mDevice.mSerialNo, 24 );
+ LFA_Read ( mplFileRef, &proMark.mDevice.mFillItem, 2 );
+
+ return true;
+}
+
+// =================================================================================================
+// ReadAVCCAM_blkPRO_Shoot -- read Panasonic's proprietary PRO_Shoot block
+// ============
+
+static bool ReadAVCCAM_blkPRO_Shoot ( LFA_FileRef& mplFileRef, AVCCAM_blkProPlayListMark& proMark )
+{
+ proMark.mShoot.mPresent = 1;
+ LFA_Read ( mplFileRef, &proMark.mShoot.mShooterCharacterSet, 1 );
+ LFA_Read ( mplFileRef, &proMark.mShoot.mShooterLength, 1 );
+ LFA_Read ( mplFileRef, &proMark.mShoot.mShooter, 32 );
+ LFA_Read ( mplFileRef, &proMark.mShoot.mStartDateTimeZone, 1 );
+ LFA_Read ( mplFileRef, &proMark.mShoot.mStartDate, 7 );
+ LFA_Read ( mplFileRef, &proMark.mShoot.mEndDateTimeZone, 1 );
+ LFA_Read ( mplFileRef, &proMark.mShoot.mEndDate, 7 );
+ LFA_Read ( mplFileRef, &proMark.mShoot.mFillItem, 2 );
+
+ return true;
+}
+
+// =================================================================================================
+// ReadAVCCAM_blkPRO_Location -- read Panasonic's proprietary PRO_Location block
+// ============
+
+static bool ReadAVCCAM_blkPRO_Location ( LFA_FileRef& mplFileRef, AVCCAM_blkProPlayListMark& proMark )
+{
+ proMark.mLocation.mPresent = 1;
+ LFA_Read ( mplFileRef, &proMark.mLocation.mSource, 1 );
+ proMark.mLocation.mGPSLatitudeRef = LFA_ReadUns32_BE ( mplFileRef );
+ proMark.mLocation.mGPSLatitude1 = LFA_ReadUns32_BE ( mplFileRef );
+ proMark.mLocation.mGPSLatitude2 = LFA_ReadUns32_BE ( mplFileRef );
+ proMark.mLocation.mGPSLatitude3 = LFA_ReadUns32_BE ( mplFileRef );
+ proMark.mLocation.mGPSLongitudeRef = LFA_ReadUns32_BE ( mplFileRef );
+ proMark.mLocation.mGPSLongitude1 = LFA_ReadUns32_BE ( mplFileRef );
+ proMark.mLocation.mGPSLongitude2 = LFA_ReadUns32_BE ( mplFileRef );
+ proMark.mLocation.mGPSLongitude3 = LFA_ReadUns32_BE ( mplFileRef );
+ proMark.mLocation.mGPSAltitudeRef = LFA_ReadUns32_BE ( mplFileRef );
+ proMark.mLocation.mGPSAltitude = LFA_ReadUns32_BE ( mplFileRef );
+ LFA_Read ( mplFileRef, &proMark.mLocation.mPlaceNameCharacterSet, 1 );
+ LFA_Read ( mplFileRef, &proMark.mLocation.mPlaceNameLength, 1 );
+ LFA_Read ( mplFileRef, &proMark.mLocation.mPlaceName, 64 );
+ LFA_Read ( mplFileRef, &proMark.mLocation.mFillItem, 1 );
+
+ return true;
+}
+
+// =================================================================================================
+// ReadAVCCAMProPlaylistInfo -- read Panasonic's proprietary PRO_PlayListInfo block
+// ============
+
+static bool ReadAVCCAMProPlaylistInfo ( LFA_FileRef& mplFileRef,
+ XMP_Uns8 tagID,
+ XMP_Uns16 playlistMarkID,
+ AVCHD_blkPanasonicPrivateData& extensionDataHeader )
+{
+ AVCCAM_Pro_PlayListInfo& playlistBlock = extensionDataHeader.mProPlaylistInfoBlock;
+
+ playlistBlock.mTagID = tagID;
+ LFA_Read ( mplFileRef, &playlistBlock.mTagVersion, 1);
+ LFA_Read ( mplFileRef, &playlistBlock.mFillItem1, 2);
+ playlistBlock.mLength = LFA_ReadUns32_BE ( mplFileRef );
+ playlistBlock.mNumberOfPlayListMarks = LFA_ReadUns16_BE ( mplFileRef );
+ LFA_Read ( mplFileRef, &playlistBlock.mFillItem2, 2);
+
+ if ( playlistBlock.mNumberOfPlayListMarks == 0 ) return true;
+
+ extensionDataHeader.mPresent = 1;
+
+ XMP_Uns64 blockStart = 0;
+
+ for ( int i = 0; i < playlistBlock.mNumberOfPlayListMarks; ++i ) {
+ AVCCAM_blkProPlayListMark& currMark = playlistBlock.mPlayListMark;
+
+ LFA_Read ( mplFileRef, &currMark.mProTagID, 1);
+ LFA_Read ( mplFileRef, &currMark.mFillItem1, 1);
+ currMark.mLength = LFA_ReadUns16_BE ( mplFileRef );
+ blockStart = LFA_Tell ( mplFileRef );
+ LFA_Read ( mplFileRef, &currMark.mMarkType, 1 );
+
+ if ( ( currMark.mProTagID == 0x40 ) && ( currMark.mMarkType == 0x01 ) ) {
+ LFA_Read ( mplFileRef, &currMark.mEntryMark.mGlobalClipID, 32);
+
+ // skip marks for different clips
+ if ( i == playlistMarkID ) {
+ playlistBlock.mPresent = 1;
+ currMark.mPresent = 1;
+ LFA_Read ( mplFileRef, &currMark.mEntryMark.mStartTimeCode, 4);
+ LFA_Read ( mplFileRef, &currMark.mEntryMark.mStreamTimecodeInfo, 1);
+ LFA_Read ( mplFileRef, &currMark.mEntryMark.mStartBinaryGroup, 4);
+ LFA_Read ( mplFileRef, &currMark.mEntryMark.mLastUpdateTimeZone, 1);
+ LFA_Read ( mplFileRef, &currMark.mEntryMark.mLastUpdateDate, 7);
+ LFA_Read ( mplFileRef, &currMark.mEntryMark.mFillItem, 2);
+
+ XMP_Uns64 currPos = LFA_Tell ( mplFileRef );
+ XMP_Uns8 blockTag = 0;
+ XMP_Uns8 blockFill;
+ XMP_Uns16 blockLength = 0;
+
+ while ( currPos < ( blockStart + currMark.mLength ) ) {
+ LFA_Read ( mplFileRef, &blockTag, 1);
+ LFA_Read ( mplFileRef, &blockFill, 1);
+ blockLength = LFA_ReadUns16_BE ( mplFileRef );
+ currPos += 4;
+
+ switch ( blockTag ) {
+ case 0x20:
+ if ( ! ReadAVCCAM_blkPRO_ShotMark ( mplFileRef, currMark ) ) return false;
+ break;
+
+
+ case 0x21:
+ if ( ! ReadAVCCAM_blkPRO_Access ( mplFileRef, currMark ) ) return false;
+ break;
+
+ case 0x22:
+ if ( ! ReadAVCCAM_blkPRO_Device ( mplFileRef, currMark ) ) return false;
+ break;
+
+ case 0x23:
+ if ( ! ReadAVCCAM_blkPRO_Shoot ( mplFileRef, currMark ) ) return false;
+ break;
+
+ case 0x24:
+ if (! ReadAVCCAM_blkPRO_Location ( mplFileRef, currMark ) ) return false;
+ break;
+
+ default : break;
+ }
+
+ currPos += blockLength;
+ LFA_Seek ( mplFileRef, currPos, SEEK_SET );
+ }
+ }
+ }
+
+ LFA_Seek ( mplFileRef, blockStart + currMark.mLength, SEEK_SET );
+ }
+
+ return true;
+}
+
+// =================================================================================================
+// ReadAVCCAMMakersPrivateData -- read Panasonic's implementation of an AVCCAM "Maker's Private Data"
+// block. Panasonic calls their extensions "AVCCAM."
+// ============
+
+static bool ReadAVCCAMMakersPrivateData ( LFA_FileRef& fileRef,
+ XMP_Uns16 playlistMarkID,
+ AVCHD_blkPanasonicPrivateData& avccamPrivateData )
+{
+ const XMP_Uns64 blockStart = LFA_Tell(fileRef);
+
+ avccamPrivateData.mNumberOfData = LFA_ReadUns16_BE ( fileRef );
+ LFA_Read ( fileRef, &avccamPrivateData.mReserved, 2 );
+
+ for (int i = 0; i < avccamPrivateData.mNumberOfData; ++i) {
+ const XMP_Uns8 tagID = LFA_ReadUns8 ( fileRef );
+
+ switch ( tagID ) {
+ case 0xe0: ReadAVCCAMProMetaID ( fileRef, tagID, avccamPrivateData );
+ break;
+ case 0xe2: ReadAVCCAMProClipInfo( fileRef, tagID, avccamPrivateData );
+ break;
+ case 0xf0: ReadAVCCAMProPlaylistInfo( fileRef, tagID, playlistMarkID, avccamPrivateData );
+ break;
+
+ default:
+ // Ignore any blocks we don't now or care about
+ break;
+ }
+ }
+
+ return true;
+}
+
+// =================================================================================================
+// ReadAVCHDMakersPrivateData (AVCHD Format. Book 2: Recording Extension Specifications, section 4.2.4.2)
+// ============
+
+static bool ReadAVCHDMakersPrivateData ( LFA_FileRef& mplFileRef,
+ XMP_Uns16 playlistMarkID,
+ AVCHD_blkMakersPrivateData& avchdLegacyData )
+{
+ const XMP_Uns64 blockStart = LFA_Tell ( mplFileRef );
+
+ avchdLegacyData.mLength = LFA_ReadUns32_BE ( mplFileRef );
+
+ if ( avchdLegacyData.mLength == 0 ) return false;
+
+ avchdLegacyData.mPresent = 1;
+ avchdLegacyData.mDataBlockStartAddress = LFA_ReadUns32_BE ( mplFileRef );
+ LFA_Read ( mplFileRef, &avchdLegacyData.mReserved, 3 );
+ LFA_Read ( mplFileRef, &avchdLegacyData.mNumberOfMakerEntries, 1 );
+
+ if ( avchdLegacyData.mNumberOfMakerEntries == 0 ) return true;
+
+ XMP_Uns16 makerID;
+ XMP_Uns16 makerModelCode;
+ XMP_Uns32 mpdStartAddress;
+ XMP_Uns32 mpdLength;
+
+ for ( int i = 0; i < avchdLegacyData.mNumberOfMakerEntries; ++i ) {
+ makerID = LFA_ReadUns16_BE ( mplFileRef );
+ makerModelCode = LFA_ReadUns16_BE ( mplFileRef );
+ mpdStartAddress = LFA_ReadUns32_BE ( mplFileRef );
+ mpdLength = LFA_ReadUns32_BE ( mplFileRef );
+
+ // We only have documentation for Panasonic's Maker's Private Data blocks, so we'll ignore everyone else's
+ if ( makerID == kMakerIDPanasonic ) {
+ avchdLegacyData.mMakerID = makerID;
+ avchdLegacyData.mMakerModelCode = makerModelCode;
+ LFA_Seek ( mplFileRef, blockStart + mpdStartAddress, SEEK_SET );
+
+ if (! ReadAVCCAMMakersPrivateData ( mplFileRef, playlistMarkID, avchdLegacyData.mPanasonicPrivateData ) ) return false;
+ }
+ }
+
+ return true;
+}
+
+// =================================================================================================
+// ReadAVCHDClipExtensionData
+// ============
+
+static bool ReadAVCHDClipExtensionData ( LFA_FileRef& cpiFileRef, AVCHD_blkClipExtensionData& avchdExtensionData )
+{
+ const XMP_Int64 extensionBlockStart = LFA_Tell ( cpiFileRef );
+ AVCHD_blkExtensionData extensionDataHeader;
+
+ if ( ! ReadAVCHDExtensionData ( cpiFileRef, extensionDataHeader ) ) {
+ return false;
+ }
+
+ if ( extensionDataHeader.mLength == 0 ) {
+ return true;
+ }
+
+ const XMP_Int64 dataBlockStart = extensionBlockStart + extensionDataHeader.mDataBlockStartAddress;
+
+ LFA_Seek ( cpiFileRef, dataBlockStart, SEEK_SET );
+ LFA_Read ( cpiFileRef, avchdExtensionData.mTypeIndicator, 4 );
+
+ if ( strncmp ( reinterpret_cast<const char*>( avchdExtensionData.mTypeIndicator ), "CLEX", 4 ) != 0 ) return false;
+
+ avchdExtensionData.mPresent = 1;
+ LFA_Read ( cpiFileRef, avchdExtensionData.mReserved1, 4 );
+ avchdExtensionData.mProgramInfoExtStartAddress = LFA_ReadUns32_BE ( cpiFileRef );
+ avchdExtensionData.mMakersPrivateDataStartAddress = LFA_ReadUns32_BE ( cpiFileRef );
+
+ // read Clip info extension
+ LFA_Seek ( cpiFileRef, dataBlockStart + 40, SEEK_SET );
+ avchdExtensionData.mClipInfoExt.mLength = LFA_ReadUns32_BE ( cpiFileRef );
+ avchdExtensionData.mClipInfoExt.mMakerID = LFA_ReadUns16_BE ( cpiFileRef );
+ avchdExtensionData.mClipInfoExt.mMakerModelCode = LFA_ReadUns16_BE ( cpiFileRef );
+
+ if ( avchdExtensionData.mMakersPrivateDataStartAddress == 0 ) return true;
+
+ if ( avchdExtensionData.mClipInfoExt.mMakerID == kMakerIDPanasonic ) {
+ // Read Maker's Private Data block -- we only have Panasonic's definition for their AVCCAM models
+ // at this point, so we'll ignore the block if its from a different manufacturer.
+ LFA_Seek ( cpiFileRef, dataBlockStart + avchdExtensionData.mMakersPrivateDataStartAddress, SEEK_SET );
+
+ if ( ! ReadAVCHDMakersPrivateData ( cpiFileRef, 0, avchdExtensionData.mMakersPrivateData ) ) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+// =================================================================================================
+// AVCHD_PlaylistContainsClip -- returns true of the specified AVCHD playlist block references the
+// specified clip, or false if not.
+// ====================
+
+static bool AVCHD_PlaylistContainsClip ( LFA_FileRef& mplFileRef, XMP_Uns16& playItemID, const std::string& strClipName )
+{
+ // Read clip header. ( AVCHD Format. Book1. v. 1.01. p 45 )
+ struct AVCHD_blkPlayList
+ {
+ XMP_Uns32 mLength;
+ XMP_Uns16 mReserved;
+ XMP_Uns16 mNumberOfPlayItems;
+ XMP_Uns16 mNumberOfSubPaths;
+ };
+
+ AVCHD_blkPlayList blkPlayList;
+ blkPlayList.mLength = LFA_ReadUns32_BE ( mplFileRef );
+ LFA_Read ( mplFileRef, &blkPlayList.mReserved, 2 );
+ blkPlayList.mNumberOfPlayItems = LFA_ReadUns16_BE ( mplFileRef );
+ blkPlayList.mNumberOfSubPaths = LFA_ReadUns16_BE ( mplFileRef );
+
+ // Search the play items. ( AVCHD Format. Book1. v. 1.01. p 47 )
+ struct AVCHD_blkPlayItem
+ {
+ XMP_Uns16 mLength;
+ char mClipInformationFileName[5];
+ // Note: remaining fields omitted because we don't care about them
+ };
+
+ AVCHD_blkPlayItem currPlayItem;
+ XMP_Uns64 blockStart = 0;
+ for ( playItemID = 0; playItemID < blkPlayList.mNumberOfPlayItems; ++playItemID ) {
+ currPlayItem.mLength = LFA_ReadUns16_BE ( mplFileRef );
+
+ // mLength is measured from the end of mLength, not the start of the block ( AVCHD Format. Book1. v. 1.01. p 47 )
+ blockStart = LFA_Tell ( mplFileRef );
+ LFA_Read ( mplFileRef, currPlayItem.mClipInformationFileName, 5 );
+
+ if ( strncmp ( strClipName.c_str(), currPlayItem.mClipInformationFileName, 5 ) == 0 ) return true;
+
+ LFA_Seek ( mplFileRef, blockStart + currPlayItem.mLength, SEEK_SET );
+ }
+
+ return false;
+}
+
+// =================================================================================================
+// ReadAVCHDPlaylistMetadataBlock
+// ============
+
+static bool ReadAVCHDPlaylistMetadataBlock ( LFA_FileRef& mplFileRef,
+ AVCHD_blkPlaylistMeta& avchdLegacyData )
+{
+ avchdLegacyData.mLength = LFA_ReadUns32_BE ( mplFileRef );
+
+ if ( avchdLegacyData.mLength < sizeof ( AVCHD_blkPlaylistMeta ) ) return false;
+
+ avchdLegacyData.mMakerID = LFA_ReadUns16_BE ( mplFileRef );
+ avchdLegacyData.mMakerModelCode = LFA_ReadUns16_BE ( mplFileRef );
+ LFA_Read ( mplFileRef, &avchdLegacyData.mReserved1, 4 );
+ avchdLegacyData.mRefToMenuThumbnailIndex = LFA_ReadUns16_BE ( mplFileRef );
+ LFA_Read ( mplFileRef, &avchdLegacyData.mBlkTimezone, 1 );
+ LFA_Read ( mplFileRef, &avchdLegacyData.mRecordDataAndTime, 7 );
+ LFA_Read ( mplFileRef, &avchdLegacyData.mReserved2, 1 );
+ LFA_Read ( mplFileRef, &avchdLegacyData.mPlaylistCharacterSet, 1 );
+ LFA_Read ( mplFileRef, &avchdLegacyData.mPlaylistNameLength, 1 );
+ LFA_Read ( mplFileRef, &avchdLegacyData.mPlaylistName, avchdLegacyData.mPlaylistNameLength );
+
+ return true;
+}
+
+// =================================================================================================
+// ReadAVCHDPlaylistMarkExtension
+// ============
+
+static bool ReadAVCHDPlaylistMarkExtension ( LFA_FileRef& mplFileRef,
+ XMP_Uns16 playlistMarkID,
+ AVCHD_blkPlayListMarkExt& avchdLegacyData )
+{
+ avchdLegacyData.mLength = LFA_ReadUns32_BE ( mplFileRef );
+
+ if ( avchdLegacyData.mLength == 0 ) return false;
+
+ avchdLegacyData.mNumberOfPlaylistMarks = LFA_ReadUns16_BE ( mplFileRef );
+
+ if ( avchdLegacyData.mNumberOfPlaylistMarks <= playlistMarkID ) return true;
+
+ // Number of bytes in blkMarkExtension, AVCHD Book 2, section 4.3.3.1
+ const XMP_Uns64 markExtensionSize = 66;
+
+ // Entries in the mark extension block correspond one-to-one with entries in
+ // blkPlaylistMark, so we'll only read the one that corresponds to the
+ // chosen clip.
+ const XMP_Uns64 markOffset = markExtensionSize * playlistMarkID;
+
+ avchdLegacyData.mPresent = 1;
+ LFA_Seek ( mplFileRef, markOffset, SEEK_CUR );
+ avchdLegacyData.mMakerID = LFA_ReadUns16_BE ( mplFileRef );
+ avchdLegacyData.mMakerModelCode = LFA_ReadUns16_BE ( mplFileRef );
+ LFA_Read ( mplFileRef, &avchdLegacyData.mReserved1, 3 );
+ LFA_Read ( mplFileRef, &avchdLegacyData.mFlags, 1 );
+ avchdLegacyData.mRefToMarkThumbnailIndex = LFA_ReadUns16_BE ( mplFileRef );
+ LFA_Read ( mplFileRef, &avchdLegacyData.mBlkTimezone, 1 );
+ LFA_Read ( mplFileRef, &avchdLegacyData.mRecordDataAndTime, 7 );
+ LFA_Read ( mplFileRef, &avchdLegacyData.mMarkCharacterSet, 1 );
+ LFA_Read ( mplFileRef, &avchdLegacyData.mMarkNameLength, 1 );
+ LFA_Read ( mplFileRef, &avchdLegacyData.mMarkName, 24 );
+ LFA_Read ( mplFileRef, &avchdLegacyData.mMakersInformation, 16 );
+ LFA_Read ( mplFileRef, &avchdLegacyData.mBlkTimecode, 4 );
+ LFA_Read ( mplFileRef, &avchdLegacyData.mReserved2, 2 );
+
+ return true;
+}
+
+// =================================================================================================
+// ReadAVCHDPlaylistMarkID -- read the playlist mark block to find the ID of the playlist mark that
+// matches the specified playlist item.
+// ============
+
+static bool ReadAVCHDPlaylistMarkID ( LFA_FileRef& mplFileRef,
+ XMP_Uns16 playItemID,
+ XMP_Uns16& markID )
+{
+ XMP_Uns32 length = LFA_ReadUns32_BE ( mplFileRef );
+ XMP_Uns16 numberOfPlayListMarks = LFA_ReadUns16_BE ( mplFileRef );
+
+ if ( length == 0 ) return false;
+
+ XMP_Uns8 reserved;
+ XMP_Uns8 markType;
+ XMP_Uns16 refToPlayItemID;
+
+ for ( int i = 0; i < numberOfPlayListMarks; ++i ) {
+ LFA_Read ( mplFileRef, &reserved, 1 );
+ LFA_Read ( mplFileRef, &markType, 1 );
+ refToPlayItemID = LFA_ReadUns16_BE ( mplFileRef );
+
+ if ( ( markType == 0x01 ) && ( refToPlayItemID == playItemID ) ) {
+ markID = i;
+ return true;
+ }
+
+ LFA_Seek ( mplFileRef, 10, SEEK_CUR );
+ }
+
+ return false;
+}
+
+// =================================================================================================
+// ReadAVCHDPlaylistExtensionData
+// ============
+
+static bool ReadAVCHDPlaylistExtensionData ( LFA_FileRef& mplFileRef,
+ AVCHD_LegacyMetadata& avchdLegacyData,
+ XMP_Uns16 playlistMarkID )
+{
+ const XMP_Int64 extensionBlockStart = LFA_Tell ( mplFileRef );
+ AVCHD_blkExtensionData extensionDataHeader;
+
+ if ( ! ReadAVCHDExtensionData ( mplFileRef, extensionDataHeader ) ) {
+ return false;
+ }
+
+ if ( extensionDataHeader.mLength == 0 ) {
+ return true;
+ }
+
+ const XMP_Int64 dataBlockStart = extensionBlockStart + extensionDataHeader.mDataBlockStartAddress;
+ AVCHD_blkPlayListExtensionData& extensionData = avchdLegacyData.mPlaylistExtensionData;
+ const int reserved2Len = 24;
+
+ LFA_Seek ( mplFileRef, dataBlockStart, SEEK_SET );
+ LFA_Read ( mplFileRef, extensionData.mTypeIndicator, 4 );
+
+ if ( strncmp ( extensionData.mTypeIndicator, "PLEX", 4 ) != 0 ) return false;
+
+ extensionData.mPresent = true;
+ LFA_Read ( mplFileRef, extensionData.mReserved, 4 );
+ extensionData.mPlayListMarkExtStartAddress = LFA_ReadUns32_BE ( mplFileRef );
+ extensionData.mMakersPrivateDataStartAddress = LFA_ReadUns32_BE ( mplFileRef );
+ LFA_Seek ( mplFileRef, reserved2Len, SEEK_CUR );
+
+ if ( ! ReadAVCHDPlaylistMetadataBlock ( mplFileRef, extensionData.mPlaylistMeta ) ) return false;
+
+ LFA_Seek ( mplFileRef, dataBlockStart + extensionData.mPlayListMarkExtStartAddress, SEEK_SET );
+
+ if ( ! ReadAVCHDPlaylistMarkExtension ( mplFileRef, playlistMarkID, extensionData.mPlaylistMarkExt ) ) return false;
+
+ if ( extensionData.mMakersPrivateDataStartAddress > 0 ) {
+
+ if ( ! avchdLegacyData.mClipExtensionData.mMakersPrivateData.mPanasonicPrivateData.mPresent ) return false;
+
+ LFA_Seek ( mplFileRef, dataBlockStart + extensionData.mMakersPrivateDataStartAddress, SEEK_SET );
+
+ if ( ! ReadAVCHDMakersPrivateData ( mplFileRef, playlistMarkID, extensionData.mMakersPrivateData ) ) return false;
+
+ }
+
+ return true;
+}
+
+// =================================================================================================
+// ReadAVCHDLegacyClipFile -- read the legacy metadata stored in an AVCHD .CPI file
// ====================
-static bool ReadAVCHDProgramInfo ( const std::string& strPath, AVCHD_blkProgramInfo& avchdProgramInfo )
+static bool ReadAVCHDLegacyClipFile ( const std::string& strPath, AVCHD_LegacyMetadata& avchdLegacyData )
{
+ bool success = false;
+
try {
- AutoFile idxFile;
- idxFile.fileRef = LFA_Open ( strPath.c_str(), 'r' );
- if ( idxFile.fileRef == 0 ) return false; // The open failed.
+ AutoFile cpiFile;
+ cpiFile.fileRef = LFA_Open ( strPath.c_str(), 'r' );
+ if ( cpiFile.fileRef == 0 ) return false; // The open failed.
- memset ( &avchdProgramInfo, 0, sizeof(AVCHD_blkProgramInfo) );
+ memset ( &avchdLegacyData, 0, sizeof(AVCHD_LegacyMetadata) );
- // Read clip header. (AVCHD Format. Book1. v. 1.01. p 64 )
+ // Read clip header. ( AVCHD Format. Book1. v. 1.01. p 64 )
struct AVCHD_ClipInfoHeader
{
- XMP_Uns8 mTypeIndicator[4];
- XMP_Uns8 mTypeIndicator2[4];
+ char mTypeIndicator[4];
+ char mTypeIndicator2[4];
XMP_Uns32 mSequenceInfoStartAddress;
XMP_Uns32 mProgramInfoStartAddress;
- XMP_Uns32 mCPIStartAddress;
+ XMP_Uns32 mCPIStartAddress;
+ XMP_Uns32 mClipMarkStartAddress;
XMP_Uns32 mExtensionDataStartAddress;
XMP_Uns8 mReserved[12];
};
// Read the AVCHD header.
AVCHD_ClipInfoHeader avchdHeader;
- LFA_Read ( idxFile.fileRef, avchdHeader.mTypeIndicator, 4 );
- LFA_Read ( idxFile.fileRef, avchdHeader.mTypeIndicator2, 4 );
- avchdHeader.mSequenceInfoStartAddress = LFA_ReadUns32_BE ( idxFile.fileRef );
- avchdHeader.mProgramInfoStartAddress = LFA_ReadUns32_BE ( idxFile.fileRef );
- avchdHeader.mCPIStartAddress = LFA_ReadUns32_BE ( idxFile.fileRef );
- avchdHeader.mExtensionDataStartAddress = LFA_ReadUns32_BE ( idxFile.fileRef );
- LFA_Read ( idxFile.fileRef, avchdHeader.mReserved, 12 );
+ LFA_Read ( cpiFile.fileRef, avchdHeader.mTypeIndicator, 4 );
+ LFA_Read ( cpiFile.fileRef, avchdHeader.mTypeIndicator2, 4 );
+
+ if ( strncmp ( avchdHeader.mTypeIndicator, "HDMV", 4 ) != 0 ) return false;
+ if ( strncmp ( avchdHeader.mTypeIndicator2, "0100", 4 ) != 0 ) return false;
+
+ avchdHeader.mSequenceInfoStartAddress = LFA_ReadUns32_BE ( cpiFile.fileRef );
+ avchdHeader.mProgramInfoStartAddress = LFA_ReadUns32_BE ( cpiFile.fileRef );
+ avchdHeader.mCPIStartAddress = LFA_ReadUns32_BE ( cpiFile.fileRef );
+ avchdHeader.mClipMarkStartAddress = LFA_ReadUns32_BE ( cpiFile.fileRef );
+ avchdHeader.mExtensionDataStartAddress = LFA_ReadUns32_BE ( cpiFile.fileRef );
+ LFA_Read ( cpiFile.fileRef, avchdHeader.mReserved, 12 );
// Seek to the program header. (AVCHD Format. Book1. v. 1.01. p 77 )
- LFA_Seek ( idxFile.fileRef, avchdHeader.mProgramInfoStartAddress, SEEK_SET );
+ LFA_Seek ( cpiFile.fileRef, avchdHeader.mProgramInfoStartAddress, SEEK_SET );
- avchdProgramInfo.mLength = LFA_ReadUns32_BE ( idxFile.fileRef );
- LFA_Read ( idxFile.fileRef, avchdProgramInfo.mReserved1, 2 );
- avchdProgramInfo.mSPNProgramSequenceStart = LFA_ReadUns32_BE ( idxFile.fileRef );
- avchdProgramInfo.mProgramMapPID = LFA_ReadUns16_BE ( idxFile.fileRef );
- LFA_Read ( idxFile.fileRef, &avchdProgramInfo.mNumberOfStreamsInPS, 1 );
- LFA_Read ( idxFile.fileRef, &avchdProgramInfo.mReserved2, 1 );
+ // Read the program info block
+ success = ReadAVCHDProgramInfo ( cpiFile.fileRef, avchdLegacyData.mProgramInfo );
+
+ if ( success && ( avchdHeader.mExtensionDataStartAddress != 0 ) ) {
+ // Seek to the program header. (AVCHD Format. Book1. v. 1.01. p 77 )
+ LFA_Seek ( cpiFile.fileRef, avchdHeader.mExtensionDataStartAddress, SEEK_SET );
+ success = ReadAVCHDClipExtensionData ( cpiFile.fileRef, avchdLegacyData.mClipExtensionData );
+ }
+
+ } catch ( ... ) {
- XMP_Uns16 streamPID = 0;
- for ( int i=0; i<avchdProgramInfo.mNumberOfStreamsInPS; ++i ) {
+ return false;
- XMP_Uns8 length = 0;
- XMP_Uns8 streamCodingType = 0;
+ }
- streamPID = LFA_ReadUns16_BE ( idxFile.fileRef );
- LFA_Read ( idxFile.fileRef, &length, 1 );
+ return success;
+}
- XMP_Int64 pos = LFA_Tell ( idxFile.fileRef );
+// =================================================================================================
+// ReadAVCHDLegacyPlaylistFile -- read the legacy metadata stored in an AVCHD .MPL file
+// ====================
- LFA_Read ( idxFile.fileRef, &streamCodingType, 1 );
+static bool ReadAVCHDLegacyPlaylistFile ( const std::string& strRootPath,
+ const std::string& strClipName,
+ AVCHD_LegacyMetadata& avchdLegacyData )
+{
+ bool success = false;
+ std::string mplPath;
+ char playlistName [10];
+ const int rootPlaylistNum = atoi(strClipName.c_str());
+
+ // Find the corresponding .MPL file -- because of clip spanning the .MPL name may not match the .CPI name for
+ // a given clip -- we need to open .MPL files and look for one that contains a reference to the clip name. To speed
+ // up the search we'll start with the playlist with the same number/name as the clip and search backwards. Assuming
+ // this directory was generated by a camera, the clip numbers will increase sequentially across the playlist files,
+ // though one playlist file may reference more than one clip.
+ for ( int i = rootPlaylistNum; i >= 0; --i ) {
+
+ sprintf ( playlistName, "%05d", i );
+
+ if ( MakeLeafPath ( &mplPath, strRootPath.c_str(), "PLAYLIST", playlistName, ".mpl", true /* checkFile */ ) ) {
+
+ try {
+
+ AutoFile mplFile;
+ mplFile.fileRef = LFA_Open ( mplPath.c_str(), 'r' );
+ if ( mplFile.fileRef == 0 ) return false; // The open failed.
+
+ // Read playlist header. ( AVCHD Format. Book1. v. 1.01. p 43 )
+ struct AVCHD_PlaylistFileHeader
+ {
+ char mTypeIndicator[4];
+ char mTypeIndicator2[4];
+ XMP_Uns32 mPlaylistStartAddress;
+ XMP_Uns32 mPlaylistMarkStartAddress;
+ XMP_Uns32 mExtensionDataStartAddress;
+ };
+
+ // Read the AVCHD playlist file header.
+ AVCHD_PlaylistFileHeader avchdHeader;
+ LFA_Read ( mplFile.fileRef, avchdHeader.mTypeIndicator, 4 );
+ LFA_Read ( mplFile.fileRef, avchdHeader.mTypeIndicator2, 4 );
+
+ if ( strncmp ( avchdHeader.mTypeIndicator, "MPLS", 4 ) != 0 ) return false;
+ if ( strncmp ( avchdHeader.mTypeIndicator2, "0100", 4 ) != 0 ) return false;
+
+ avchdHeader.mPlaylistStartAddress = LFA_ReadUns32_BE ( mplFile.fileRef );
+ avchdHeader.mPlaylistMarkStartAddress = LFA_ReadUns32_BE ( mplFile.fileRef );
+ avchdHeader.mExtensionDataStartAddress = LFA_ReadUns32_BE ( mplFile.fileRef );
+
+ if ( avchdHeader.mExtensionDataStartAddress == 0 ) return false;
+
+ // Seek to the start of the Playlist block. (AVCHD Format. Book1. v. 1.01. p 45 )
+ LFA_Seek ( mplFile.fileRef, avchdHeader.mPlaylistStartAddress, SEEK_SET );
+
+ XMP_Uns16 playItemID = 0xFFFF;
+ XMP_Uns16 playlistMarkID = 0xFFFF;
+
+ if ( AVCHD_PlaylistContainsClip ( mplFile.fileRef, playItemID, strClipName ) ) {
+ LFA_Seek ( mplFile.fileRef, avchdHeader.mPlaylistMarkStartAddress, SEEK_SET );
+
+ if ( ! ReadAVCHDPlaylistMarkID ( mplFile.fileRef, playItemID, playlistMarkID ) ) return false;
+
+ LFA_Seek ( mplFile.fileRef, avchdHeader.mExtensionDataStartAddress, SEEK_SET );
+ success = ReadAVCHDPlaylistExtensionData ( mplFile.fileRef, avchdLegacyData, playlistMarkID );
+ }
+ } catch ( ... ) {
+
+ return false;
- switch ( streamCodingType ) {
+ }
+ }
+
+ }
- case 0x1B : // Video stream case.
- {
- XMP_Uns8 videoFormatAndFrameRate;
- LFA_Read ( idxFile.fileRef, &videoFormatAndFrameRate, 1 );
- avchdProgramInfo.mVideoStream.mVideoFormat = videoFormatAndFrameRate >> 4; // hi 4 bits
- avchdProgramInfo.mVideoStream.mFrameRate = videoFormatAndFrameRate & 0x0f; // lo 4 bits
-
- XMP_Uns8 aspectRatioAndReserved = 0;
- LFA_Read ( idxFile.fileRef, &aspectRatioAndReserved, 1 );
- avchdProgramInfo.mVideoStream.mAspectRatio = aspectRatioAndReserved >> 4; // hi 4 bits
-
- XMP_Uns8 ccFlag = 0;
- LFA_Read ( idxFile.fileRef, &ccFlag, 1 );
- avchdProgramInfo.mVideoStream.mCCFlag = ccFlag;
-
- avchdProgramInfo.mVideoStream.mPresent = 1;
- }
- break;
-
- case 0x80 : // Fall through.
- case 0x81 : // Audio stream case.
- {
- XMP_Uns8 audioPresentationTypeAndFrequency = 0;
- LFA_Read ( idxFile.fileRef, &audioPresentationTypeAndFrequency, 1 );
-
- avchdProgramInfo.mAudioStream.mAudioPresentationType = audioPresentationTypeAndFrequency >> 4; // hi 4 bits
- avchdProgramInfo.mAudioStream.mSamplingFrequency = audioPresentationTypeAndFrequency & 0x0f; // lo 4 bits
+ return success;
+}
+
+// =================================================================================================
+// ReadAVCHDLegacyMetadata -- read the legacy metadata stored in an AVCHD .CPI file
+// ====================
+
+static bool ReadAVCHDLegacyMetadata ( const std::string& strPath,
+ const std::string& strRootPath,
+ const std::string& strClipName,
+ AVCHD_LegacyMetadata& avchdLegacyData )
+{
+ bool success = ReadAVCHDLegacyClipFile ( strPath, avchdLegacyData );
- LFA_Read ( idxFile.fileRef, avchdProgramInfo.mAudioStream.mAudioLanguageCode, 3 );
- avchdProgramInfo.mAudioStream.mAudioLanguageCode[3] = 0;
+ if ( success && avchdLegacyData.mClipExtensionData.mPresent ) {
+ success = ReadAVCHDLegacyPlaylistFile ( strRootPath, strClipName, avchdLegacyData );
+ }
+
+ return success;
+
+} // ReadAVCHDLegacyMetadata
+
+// =================================================================================================
+// AVCCAM_SetXMPStartTimecode
+// =============================
+
+static void AVCCAM_SetXMPStartTimecode ( SXMPMeta& xmpObj, const XMP_Uns8* avccamTimecode, XMP_Uns8 avchdFrameRate )
+{
+ // Timecode in SMPTE 12M format, according to Panasonic's documentation
+ if ( *reinterpret_cast<const XMP_Uns32*>( avccamTimecode ) == 0xFFFFFFFF ) {
+ // 0xFFFFFFFF means timecode not specified
+ return;
+ }
- avchdProgramInfo.mAudioStream.mPresent = 1;
- }
- break;
+ const XMP_Uns8 isColor = ( avccamTimecode[0] >> 7 ) & 0x01;
+ const XMP_Uns8 isDropFrame = ( avccamTimecode[0] >> 6 ) & 0x01;
+ const XMP_Uns8 frameTens = ( avccamTimecode[0] >> 4 ) & 0x03;
+ const XMP_Uns8 frameUnits = avccamTimecode[0] & 0x0f;
+ const XMP_Uns8 secondTens = ( avccamTimecode[1] >> 4 ) & 0x07;
+ const XMP_Uns8 secondUnits = avccamTimecode[1] & 0x0f;
+ const XMP_Uns8 minuteTens = ( avccamTimecode[2] >> 4 ) & 0x07;
+ const XMP_Uns8 minuteUnits = avccamTimecode[2] & 0x0f;
+ const XMP_Uns8 hourTens = ( avccamTimecode[3] >> 4 ) & 0x03;
+ const XMP_Uns8 hourUnits = avccamTimecode[3] & 0x0f;
+ char tcSeparator = ':';
+ const char* dmTimeFormat = NULL;
+ const char* dmTimeScale = NULL;
+ const char* dmTimeSampleSize = NULL;
+
+ switch ( avchdFrameRate ) {
+ case 1 :
+ // 23.976i
+ dmTimeFormat = "23976Timecode";
+ dmTimeScale = "24000";
+ dmTimeSampleSize = "1001";
+ break;
+
+ case 2 :
+ // 24p
+ dmTimeFormat = "24Timecode";
+ dmTimeScale = "24";
+ dmTimeSampleSize = "1";
+ break;
+
+ case 3 :
+ case 6 :
+ // 50i or 25p
+ dmTimeFormat = "25Timecode";
+ dmTimeScale = "25";
+ dmTimeSampleSize = "1";
+ break;
+
+ case 4 :
+ case 7 :
+ // 29.97p or 59.94i
+ if ( isDropFrame ) {
+ dmTimeFormat = "2997DropTimecode";
+ tcSeparator = ';';
+ } else {
+ dmTimeFormat = "2997NonDropTimecode";
+ }
+
+ dmTimeScale = "30000";
+ dmTimeSampleSize = "1001";
+
+ break;
+ }
- case 0x90 : // Overlay bitmap stream case.
- LFA_Read ( idxFile.fileRef, &avchdProgramInfo.mOverlayBitmapStream.mOBLanguageCode, 3 );
- avchdProgramInfo.mOverlayBitmapStream.mOBLanguageCode[3] = 0;
- avchdProgramInfo.mOverlayBitmapStream.mPresent = 1;
- break;
+ if ( dmTimeFormat != NULL ) {
+ char timecodeBuff [12];
+
+ sprintf ( timecodeBuff, "%d%d%c%d%d%c%d%d%c%d%d", hourTens, hourUnits, tcSeparator,
+ minuteTens, minuteUnits, tcSeparator, secondTens, secondUnits, tcSeparator, frameTens, frameUnits);
+
+ xmpObj.SetProperty( kXMP_NS_DM, "startTimeScale", dmTimeScale, kXMP_DeleteExisting );
+ xmpObj.SetProperty( kXMP_NS_DM, "startTimeSampleSize", dmTimeSampleSize, kXMP_DeleteExisting );
+ xmpObj.SetStructField ( kXMP_NS_DM, "startTimecode", kXMP_NS_DM, "timeValue", timecodeBuff, 0 );
+ xmpObj.SetStructField ( kXMP_NS_DM, "startTimecode", kXMP_NS_DM, "timeFormat", dmTimeFormat, 0 );
+ }
+}
+
+// =================================================================================================
+// AVCHD_SetXMPMakeAndModel
+// =============================
+
+static bool AVCHD_SetXMPMakeAndModel ( SXMPMeta& xmpObj, const AVCHD_blkClipExtensionData& clipExtData )
+{
+ if ( ! clipExtData.mPresent ) return false;
+
+ XMP_StringPtr xmpValue = 0;
+
+ // Set the Make. Use a hex string for unknown makes.
+ {
+ char hexMakeNumber [7];
+
+ switch ( clipExtData.mClipInfoExt.mMakerID ) {
+ case kMakerIDCanon : xmpValue = "Canon"; break;
+ case kMakerIDPanasonic : xmpValue = "Panasonic"; break;
+ case kMakerIDSony : xmpValue = "Sony"; break;
+ default :
+ std::sprintf ( hexMakeNumber, "0x%04x", clipExtData.mClipInfoExt.mMakerID );
+ xmpValue = hexMakeNumber;
+
+ break;
+ }
+
+ xmpObj.SetProperty ( kXMP_NS_TIFF, "Make", xmpValue, kXMP_DeleteExisting );
+ }
- case 0x91 : // Menu bitmap stream.
- LFA_Read ( idxFile.fileRef, &avchdProgramInfo.mMenuBitmapStream.mBMLanguageCode, 3 );
- avchdProgramInfo.mMenuBitmapStream.mBMLanguageCode[3] = 0;
- avchdProgramInfo.mMenuBitmapStream.mPresent = 1;
- break;
+ // Set the Model number. Use a hex string for unknown model numbers so they can still be distinguished.
+ {
+ char hexModelNumber [7];
+
+ xmpValue = 0;
+
+ switch ( clipExtData.mClipInfoExt.mMakerID ) {
+ case kMakerIDCanon :
+ switch ( clipExtData.mClipInfoExt.mMakerModelCode ) {
+ case 0x1000 : xmpValue = "HR10"; break;
+ case 0x2000 : xmpValue = "HG10"; break;
+ case 0x2001 : xmpValue = "HG21"; break;
+ case 0x3000 : xmpValue = "HF100"; break;
+ case 0x3003 : xmpValue = "HF S10"; break;
+ default : break;
+ }
+ break;
+
+ case kMakerIDPanasonic :
+ switch ( clipExtData.mClipInfoExt.mMakerModelCode ) {
+ case 0x0202 : xmpValue = "HD-writer"; break;
+ case 0x0400 : xmpValue = "AG-HSC1U"; break;
+ case 0x0401 : xmpValue = "AG-HMC70"; break;
+ case 0x0410 : xmpValue = "AG-HMC150"; break;
+ case 0x0411 : xmpValue = "AG-HMC40"; break;
+ case 0x0412 : xmpValue = "AG-HMC80"; break;
+ case 0x0413 : xmpValue = "AG-3DA1"; break;
+ case 0x0414 : xmpValue = "AG-AF100"; break;
+ case 0x0450 : xmpValue = "AG-HMR10"; break;
+ case 0x0451 : xmpValue = "AJ-YCX250"; break;
+ case 0x0452 : xmpValue = "AG-MDR15"; break;
+ case 0x0490 : xmpValue = "AVCCAM Restorer"; break;
+ case 0x0491 : xmpValue = "AVCCAM Viewer"; break;
+ case 0x0492 : xmpValue = "AVCCAM Viewer for Mac"; break;
+ default : break;
+ }
+
+ break;
+
+ default : break;
+ }
+
+ if ( ( xmpValue == 0 ) && ( clipExtData.mClipInfoExt.mMakerID != kMakerIDSony ) ) {
+ // Panasonic has said that if we don't have a string for the model number, they'd like to see the code
+ // anyway. We'll do the same for every manufacturer except Sony, who have said that they use
+ // the same model number for multiple cameras.
+ std::sprintf ( hexModelNumber, "0x%04x", clipExtData.mClipInfoExt.mMakerModelCode );
+ xmpValue = hexModelNumber;
+ }
+
+ if ( xmpValue != 0 ) xmpObj.SetProperty ( kXMP_NS_TIFF, "Model", xmpValue, kXMP_DeleteExisting );
+ }
- default :
- break;
+ return true;
+}
- }
+// =================================================================================================
+// AVCHD_StringFieldToXMP
+// =============================
- LFA_Seek ( idxFile.fileRef, pos + length, SEEK_SET );
+static std::string AVCHD_StringFieldToXMP ( XMP_Uns8 avchdLength,
+ XMP_Uns8 avchdCharacterSet,
+ const XMP_Uns8* avchdField,
+ XMP_Uns8 avchdFieldSize )
+{
+ std::string xmpString;
+
+ if ( avchdCharacterSet == 0x02 ) {
+ // UTF-16, Big Endian
+ UTF8Unit utf8Name [512];
+ const XMP_Uns8 avchdMaxChars = ( avchdFieldSize / 2);
+ size_t utf16Read;
+ size_t utf8Written;
+
+ // The spec doesn't say whether AVCHD length fields count bytes or characters, so we'll
+ // clamp to the max number of UTF-16 characters just in case.
+ const int stringLength = ( avchdLength > avchdMaxChars ) ? avchdMaxChars : avchdLength;
+
+ UTF16BE_to_UTF8 ( reinterpret_cast<const UTF16Unit*> ( avchdField ), stringLength,
+ utf8Name, 512, &utf16Read, &utf8Written );
+ xmpString.assign ( reinterpret_cast<const char*> ( utf8Name ), utf8Written );
+ } else {
+ // AVCHD supports many character encodings, but UTF-8 (0x01) and ASCII (0x90) are the only ones I've
+ // seen in the wild at this point. We'll treat the other character sets as UTF-8 on the assumption that
+ // at least a few characters will come across, and something is better than nothing.
+ const int stringLength = ( avchdLength > avchdFieldSize ) ? avchdFieldSize : avchdLength;
- }
+ xmpString.assign ( reinterpret_cast<const char*> ( avchdField ), stringLength );
+ }
+
+ return xmpString;
+}
- } catch ( ... ) {
+// =================================================================================================
+// AVCHD_SetXMPShotName
+// =============================
- return false;
+static void AVCHD_SetXMPShotName ( SXMPMeta& xmpObj, const AVCHD_blkPlayListMarkExt& markExt, const std::string& strClipName )
+{
+ if ( markExt.mPresent ) {
+ const std::string shotName = AVCHD_StringFieldToXMP ( markExt.mMarkNameLength, markExt.mMarkCharacterSet, markExt.mMarkName, 24 );
+
+ if ( ! shotName.empty() ) xmpObj.SetProperty ( kXMP_NS_DC, "shotName", shotName.c_str(), kXMP_DeleteExisting );
+ }
+}
+
+// =================================================================================================
+// BytesToHex
+// =============================
+
+#define kHexDigits "0123456789ABCDEF"
+static std::string BytesToHex ( const XMP_Uns8* inClipIDBytes, int inNumBytes )
+{
+ const int numChars = ( inNumBytes * 2 );
+ std::string hexStr;
+
+ hexStr.reserve(numChars);
+
+ for ( int i = 0; i < inNumBytes; ++i ) {
+ const XMP_Uns8 byte = inClipIDBytes[i];
+ hexStr.push_back ( kHexDigits [byte >> 4] );
+ hexStr.push_back ( kHexDigits [byte & 0xF] );
}
+
+ return hexStr;
+}
- return true;
+// =================================================================================================
+// AVCHD_DateFieldToXMP (AVCHD Format Book 2, section 4.2.2.2)
+// =============================
-} // ReadAVCHDProgramInfo
+static std::string AVCHD_DateFieldToXMP ( XMP_Uns8 avchdTimeZone, const XMP_Uns8* avchdDateTime )
+{
+ const XMP_Uns8 daylightSavingsTime = ( avchdTimeZone >> 6 ) & 0x01;
+ const XMP_Uns8 timezoneSign = ( avchdTimeZone >> 5 ) & 0x01;
+ const XMP_Uns8 timezoneValue = ( avchdTimeZone >> 1 ) & 0x0F;
+ const XMP_Uns8 halfHourFlag = avchdTimeZone & 0x01;
+ int utcOffsetHours = 0;
+ unsigned int utcOffsetMinutes = 0;
+
+ // It's not entirely clear how to interpret the daylightSavingsTime flag from the documentation -- my best
+ // guess is that it should only be used if trying to display local time, not the UTC-relative time that
+ // XMP specifies.
+ if ( timezoneValue != 0xF ) {
+ utcOffsetHours = timezoneSign ? -timezoneValue : timezoneValue;
+ utcOffsetMinutes = 30 * halfHourFlag;
+ }
+
+ char dateBuff [26];
+
+ sprintf ( dateBuff,
+ "%01d%01d%01d%01d-%01d%01d-%01d%01dT%01d%01d:%01d%01d:%01d%01d%+02d:%02d",
+ (avchdDateTime[0] >> 4), (avchdDateTime[0] & 0x0F),
+ (avchdDateTime[1] >> 4), (avchdDateTime[1] & 0x0F),
+ (avchdDateTime[2] >> 4), (avchdDateTime[2] & 0x0F),
+ (avchdDateTime[3] >> 4), (avchdDateTime[3] & 0x0F),
+ (avchdDateTime[4] >> 4), (avchdDateTime[4] & 0x0F),
+ (avchdDateTime[5] >> 4), (avchdDateTime[5] & 0x0F),
+ (avchdDateTime[6] >> 4), (avchdDateTime[6] & 0x0F),
+ utcOffsetHours, utcOffsetMinutes );
+
+ return std::string(dateBuff);
+}
// =================================================================================================
// AVCHD_MetaHandlerCTor
@@ -322,11 +1619,11 @@ AVCHD_MetaHandler::AVCHD_MetaHandler ( XMPFiles * _parent )
// Extract the root path and clip name.
- XMP_Assert ( this->parent->handlerTemp != 0 );
+ XMP_Assert ( this->parent->tempPtr != 0 );
- this->rootPath.assign ( (char*) this->parent->handlerTemp );
- free ( this->parent->handlerTemp );
- this->parent->handlerTemp = 0;
+ this->rootPath.assign ( (char*) this->parent->tempPtr );
+ free ( this->parent->tempPtr );
+ this->parent->tempPtr = 0;
SplitLeafName ( &this->rootPath, &this->clipName );
@@ -339,9 +1636,9 @@ AVCHD_MetaHandler::AVCHD_MetaHandler ( XMPFiles * _parent )
AVCHD_MetaHandler::~AVCHD_MetaHandler()
{
- if ( this->parent->handlerTemp != 0 ) {
- free ( this->parent->handlerTemp );
- this->parent->handlerTemp = 0;
+ if ( this->parent->tempPtr != 0 ) {
+ free ( this->parent->tempPtr );
+ this->parent->tempPtr = 0;
}
} // AVCHD_MetaHandler::~AVCHD_MetaHandler
@@ -350,72 +1647,86 @@ AVCHD_MetaHandler::~AVCHD_MetaHandler()
// AVCHD_MetaHandler::MakeClipInfoPath
// ===================================
-void AVCHD_MetaHandler::MakeClipInfoPath ( std::string * path, XMP_StringPtr suffix )
+bool AVCHD_MetaHandler::MakeClipInfoPath ( std::string * path, XMP_StringPtr suffix, bool checkFile /* = false */ ) const
{
-
- *path = this->rootPath;
- *path += kDirChar;
- *path += "BDMV";
- *path += kDirChar;
- *path += "CLIPINF";
- *path += kDirChar;
- *path += this->clipName;
- *path += suffix;
-
+ return MakeLeafPath ( path, this->rootPath.c_str(), "CLIPINF", this->clipName.c_str(), suffix, checkFile );
} // AVCHD_MetaHandler::MakeClipInfoPath
// =================================================================================================
// AVCHD_MetaHandler::MakeClipStreamPath
// =====================================
-void AVCHD_MetaHandler::MakeClipStreamPath ( std::string * path, XMP_StringPtr suffix )
+bool AVCHD_MetaHandler::MakeClipStreamPath ( std::string * path, XMP_StringPtr suffix, bool checkFile /* = false */ ) const
{
+ return MakeLeafPath ( path, this->rootPath.c_str(), "STREAM", this->clipName.c_str(), suffix, checkFile );
+} // AVCHD_MetaHandler::MakeClipStreamPath
- *path = this->rootPath;
- *path += kDirChar;
- *path += "BDMV";
- *path += kDirChar;
- *path += "STREAM";
- *path += kDirChar;
- *path += this->clipName;
- *path += suffix;
+// =================================================================================================
+// AVCHD_MetaHandler::MakePlaylistPath
+// =====================================
-} // AVCHD_MetaHandler::MakeClipStreamPath
+bool AVCHD_MetaHandler::MakePlaylistPath ( std::string * path, XMP_StringPtr suffix, bool checkFile /* = false */ ) const
+{
+ return MakeLeafPath ( path, this->rootPath.c_str(), "PLAYLIST", this->clipName.c_str(), suffix, checkFile );
+} // AVCHD_MetaHandler::MakePlaylistPath
// =================================================================================================
// AVCHD_MetaHandler::MakeLegacyDigest
// ===================================
-#define kHexDigits "0123456789ABCDEF"
-
void AVCHD_MetaHandler::MakeLegacyDigest ( std::string * digestStr )
{
- AVCHD_blkProgramInfo avchdProgramInfo;
- std::string strPath;
- this->MakeClipInfoPath ( &strPath, ".clpi" );
+ std::string strClipPath;
+ std::string strPlaylistPath;
+ std::vector<XMP_Uns8> legacyBuff;
- if ( ! ReadAVCHDProgramInfo ( strPath, avchdProgramInfo ) ) {
- this->MakeClipInfoPath ( &strPath, ".cpi" );
- if ( ! ReadAVCHDProgramInfo ( strPath, avchdProgramInfo ) ) return;
- }
+ bool ok = this->MakeClipInfoPath ( &strClipPath, ".clpi", true /* checkFile */ );
+ if ( ! ok ) return;
+ ok = this->MakePlaylistPath ( &strPlaylistPath, ".mpls", true /* checkFile */ );
+ if ( ! ok ) return;
+
+ try {
+ {
+ AutoFile cpiFile;
+ cpiFile.fileRef = LFA_Open ( strClipPath.c_str(), 'r' );
+ if ( cpiFile.fileRef == 0 ) return; // The open failed.
+
+ // Read at most the first 2k of data from the cpi file to use in the digest
+ // (every CPI file I've seen is less than 1k).
+ const XMP_Int64 cpiLen = LFA_Measure ( cpiFile.fileRef );
+ const XMP_Int64 buffLen = (cpiLen <= 2048) ? cpiLen : 2048;
+
+ legacyBuff.resize ( (unsigned int) buffLen );
+ LFA_Read ( cpiFile.fileRef, &(legacyBuff[0]), static_cast<XMP_Int32> ( buffLen ) );
+ }
+
+ {
+ AutoFile mplFile;
+ mplFile.fileRef = LFA_Open ( strPlaylistPath.c_str(), 'r' );
+ if ( mplFile.fileRef == 0 ) return; // The open failed.
+
+ // Read at most the first 2k of data from the cpi file to use in the digest
+ // (every playlist file I've seen is less than 1k).
+ const XMP_Int64 mplLen = LFA_Measure ( mplFile.fileRef );
+ const XMP_Int64 buffLen = (mplLen <= 2048) ? mplLen : 2048;
+ const XMP_Int64 clipBuffLen = legacyBuff.size();
+
+ legacyBuff.resize ( (unsigned int) (clipBuffLen + buffLen) );
+ LFA_Read ( mplFile.fileRef, &( legacyBuff [(unsigned int)clipBuffLen] ), (XMP_Int32)buffLen );
+ }
+ } catch (...) {
+ return;
+ }
+
MD5_CTX context;
unsigned char digestBin [16];
MD5Init ( &context );
- MD5Update ( &context, (XMP_Uns8*)&avchdProgramInfo, (unsigned int) sizeof(avchdProgramInfo) );
+ MD5Update ( &context, (XMP_Uns8*)&(legacyBuff[0]), (unsigned int) legacyBuff.size() );
MD5Final ( digestBin, &context );
- char buffer [40];
- for ( int in = 0, out = 0; in < 16; in += 1, out += 2 ) {
- XMP_Uns8 byte = digestBin[in];
- buffer[out] = kHexDigits [ byte >> 4 ];
- buffer[out+1] = kHexDigits [ byte & 0xF ];
- }
- buffer[32] = 0;
- digestStr->erase();
- digestStr->append ( buffer, 32 );
-
+ *digestStr = BytesToHex ( digestBin, 16 );
} // AVCHD_MetaHandler::MakeLegacyDigest
// =================================================================================================
@@ -424,13 +1735,13 @@ void AVCHD_MetaHandler::MakeLegacyDigest ( std::string * digestStr )
void AVCHD_MetaHandler::CacheFileData()
{
- XMP_Assert ( (! this->containsXMP) && (! this->containsTNail) );
+ XMP_Assert ( ! this->containsXMP );
// See if the clip's .XMP file exists.
std::string xmpPath;
- this->MakeClipStreamPath ( &xmpPath, ".xmp" );
- if ( GetFileMode ( xmpPath.c_str() ) != kFMode_IsFile ) return; // No XMP.
+ bool found = this->MakeClipStreamPath ( &xmpPath, ".xmp", true /* checkFile */ );
+ if ( ! found ) return;
// Read the entire .XMP file.
@@ -481,50 +1792,50 @@ void AVCHD_MetaHandler::ProcessXMP()
}
// read clip info
- AVCHD_blkProgramInfo avchdProgramInfo;
+ AVCHD_LegacyMetadata avchdLegacyData;
std::string strPath;
- this->MakeClipInfoPath ( &strPath, ".clpi" );
- if ( ! ReadAVCHDProgramInfo ( strPath, avchdProgramInfo ) ) {
- this->MakeClipInfoPath ( &strPath, ".cpi" );
- if ( ! ReadAVCHDProgramInfo ( strPath, avchdProgramInfo ) ) return;
+ bool ok = this->MakeClipInfoPath ( &strPath, ".clpi", true /* checkFile */ );
+ if ( ok ) ReadAVCHDLegacyMetadata ( strPath, this->rootPath, this->clipName, avchdLegacyData );
+ if ( ! ok ) return;
+
+ const AVCHD_blkPlayListMarkExt& markExt = avchdLegacyData.mPlaylistExtensionData.mPlaylistMarkExt;
+ XMP_Uns8 pulldownFlag = 0;
+
+ if ( markExt.mPresent ) {
+ const std::string dateString = AVCHD_DateFieldToXMP ( markExt.mBlkTimezone, markExt.mRecordDataAndTime );
+
+ if ( ! dateString.empty() ) this->xmpObj.SetProperty ( kXMP_NS_DM, "shotDate", dateString.c_str(), kXMP_DeleteExisting );
+ AVCHD_SetXMPShotName ( this->xmpObj, markExt, this->clipName );
+ AVCCAM_SetXMPStartTimecode ( this->xmpObj, markExt.mBlkTimecode, avchdLegacyData.mProgramInfo.mVideoStream.mFrameRate );
+ pulldownFlag = (markExt.mFlags >> 1) & 0x03; // bits 1 and 2
}
// Video Stream. AVCHD Format v. 1.01 p. 78
+ const bool has2_2pulldown = (pulldownFlag == 0x01);
+ const bool has3_2pulldown = (pulldownFlag == 0x10);
XMP_StringPtr xmpValue = 0;
- if ( avchdProgramInfo.mVideoStream.mPresent ) {
-
- // XMP videoFrameRate.
- xmpValue = 0;
- switch ( avchdProgramInfo.mVideoStream.mFrameRate ) {
- case 1 : xmpValue = "23.98p"; break; // "23.976"
- case 2 : xmpValue = "24p"; break; // "24"
- case 3 : xmpValue = "25p"; break; // "25"
- case 4 : xmpValue = "29.97p"; break; // "29.97"
- case 6 : xmpValue = "50i"; break; // "50"
- case 7 : xmpValue = "59.94i"; break; // "59.94"
- default: break;
- }
- if ( xmpValue != 0 ) {
- this->xmpObj.SetProperty ( kXMP_NS_DM, "videoFrameRate", xmpValue, kXMP_DeleteExisting );
- }
-
+ if ( avchdLegacyData.mProgramInfo.mVideoStream.mPresent ) {
+
// XMP videoFrameSize.
xmpValue = 0;
int frameIndex = -1;
+ bool isProgressiveHD = false;
const char* frameWidth[4] = { "720", "720", "1280", "1920" };
const char* frameHeight[4] = { "480", "576", "720", "1080" };
- switch ( avchdProgramInfo.mVideoStream.mVideoFormat ) {
- case 1 : frameIndex = 0; break; // 480i
- case 2 : frameIndex = 1; break; // 576i
- case 3 : frameIndex = 0; break; // 480i
- case 4 : frameIndex = 3; break; // 1080i
- case 5 : frameIndex = 2; break; // 720p
- case 6 : frameIndex = 3; break; // 1080p
+
+ switch ( avchdLegacyData.mProgramInfo.mVideoStream.mVideoFormat ) {
+ case 1 : frameIndex = 0; break; // 480i
+ case 2 : frameIndex = 1; break; // 576i
+ case 3 : frameIndex = 0; break; // 480p
+ case 4 : frameIndex = 3; break; // 1080i
+ case 5 : frameIndex = 2; isProgressiveHD = true; break; // 720p
+ case 6 : frameIndex = 3; isProgressiveHD = true; break; // 1080p
default: break;
}
+
if ( frameIndex != -1 ) {
xmpValue = frameWidth[frameIndex];
this->xmpObj.SetStructField ( kXMP_NS_DM, "videoFrameSize", kXMP_NS_XMP_Dimensions, "w", xmpValue, 0 );
@@ -533,16 +1844,59 @@ void AVCHD_MetaHandler::ProcessXMP()
xmpValue = "pixels";
this->xmpObj.SetStructField ( kXMP_NS_DM, "videoFrameSize", kXMP_NS_XMP_Dimensions, "unit", xmpValue, 0 );
}
+
+ // XMP videoFrameRate. The logic below seems pretty tortured, but matches "Table 4-4 pulldown" on page 31 of Book 2 of the AVCHD
+ // spec, if you interepret "frame_mbs_only_flag" as "isProgressiveHD", "frame-rate [Hz]" as the frame rate encoded in
+ // mVideoStream.mFrameRate, and "Video Scan Type" as the desired xmp output value. The algorithm produces correct results for
+ // all the AVCHD media I've tested.
+ xmpValue = 0;
+ if ( isProgressiveHD ) {
+
+ switch ( avchdLegacyData.mProgramInfo.mVideoStream.mFrameRate ) {
+ case 1 : xmpValue = "23.98p"; break; // "23.976"
+ case 2 : xmpValue = "24p"; break; // "24"
+ case 3 : xmpValue = "25p"; break; // "25"
+ case 4 : xmpValue = has2_2pulldown ? "29.97p" : "59.94p"; break; // "29.97"
+ case 6 : xmpValue = has2_2pulldown ? "25p" : "50p"; break; // "50"
+ case 7 : // "59.94"
+ if ( has2_2pulldown )
+ xmpValue = "29.97p";
+ else
+ xmpValue = has3_2pulldown ? "23.98p" : "59.94p";
+
+ break;
+ default: break;
+ }
+
+ } else {
+
+ switch ( avchdLegacyData.mProgramInfo.mVideoStream.mFrameRate ) {
+ case 3 : xmpValue = has2_2pulldown ? "25p" : "50i"; break; // "25" (but 1080p25 is reported as 1080i25 with 2:2 pulldown...)
+ case 4 : // "29.97"
+ if ( has2_2pulldown )
+ xmpValue = "29.97p";
+ else
+ xmpValue = has3_2pulldown ? "23.98p" : "59.94i";
+
+ break;
+ default: break;
+ }
+
+ }
+
+ if ( xmpValue != 0 ) {
+ this->xmpObj.SetProperty ( kXMP_NS_DM, "videoFrameRate", xmpValue, kXMP_DeleteExisting );
+ }
this->containsXMP = true;
}
// Audio Stream.
- if ( avchdProgramInfo.mAudioStream.mPresent ) {
+ if ( avchdLegacyData.mProgramInfo.mAudioStream.mPresent ) {
xmpValue = 0;
- switch ( avchdProgramInfo.mAudioStream.mAudioPresentationType ) {
+ switch ( avchdLegacyData.mProgramInfo.mAudioStream.mAudioPresentationType ) {
case 1 : xmpValue = "Mono"; break;
case 3 : xmpValue = "Stereo"; break;
default : break;
@@ -552,7 +1906,7 @@ void AVCHD_MetaHandler::ProcessXMP()
}
xmpValue = 0;
- switch ( avchdProgramInfo.mAudioStream.mSamplingFrequency ) {
+ switch ( avchdLegacyData.mProgramInfo.mAudioStream.mSamplingFrequency ) {
case 1 : xmpValue = "48000"; break;
case 4 : xmpValue = "96000"; break;
case 5 : xmpValue = "192000"; break;
@@ -563,7 +1917,64 @@ void AVCHD_MetaHandler::ProcessXMP()
}
this->containsXMP = true;
+ }
+
+ // Proprietary vendor extensions
+ if ( AVCHD_SetXMPMakeAndModel ( this->xmpObj, avchdLegacyData.mClipExtensionData ) ) this->containsXMP = true;
+
+ this->xmpObj.SetProperty ( kXMP_NS_DM, "title", this->clipName.c_str(), kXMP_DeleteExisting );
+ this->containsXMP = true;
+ if ( avchdLegacyData.mClipExtensionData.mMakersPrivateData.mPresent &&
+ ( avchdLegacyData.mClipExtensionData.mClipInfoExt.mMakerID == kMakerIDPanasonic ) ) {
+
+ const AVCHD_blkPanasonicPrivateData& panasonicClipData = avchdLegacyData.mClipExtensionData.mMakersPrivateData.mPanasonicPrivateData;
+
+ if ( panasonicClipData.mProClipIDBlock.mPresent ) {
+ const std::string globalClipIDString = BytesToHex ( panasonicClipData.mProClipIDBlock.mGlobalClipID, 32 );
+
+ this->xmpObj.SetProperty ( kXMP_NS_DC, "identifier", globalClipIDString.c_str(), kXMP_DeleteExisting );
+ }
+
+ const AVCHD_blkPanasonicPrivateData& panasonicPlaylistData =
+ avchdLegacyData.mPlaylistExtensionData.mMakersPrivateData.mPanasonicPrivateData;
+
+ if ( panasonicPlaylistData.mProPlaylistInfoBlock.mPlayListMark.mPresent ) {
+ const AVCCAM_blkProPlayListMark& playlistMark = panasonicPlaylistData.mProPlaylistInfoBlock.mPlayListMark;
+
+ if ( playlistMark.mShotMark.mPresent ) {
+ // Unlike P2, where "shotmark" is a boolean, Panasonic treats their AVCCAM shotmark as a bit field with
+ // 8 user-definable bits. For now we're going to treat any bit being set as xmpDM::good == true, and all
+ // bits being clear as xmpDM::good == false.
+ const bool isGood = ( playlistMark.mShotMark.mShotMark != 0 );
+
+ xmpObj.SetProperty_Bool ( kXMP_NS_DM, "good", isGood, kXMP_DeleteExisting );
+ }
+
+ if ( playlistMark.mAccess.mPresent && ( playlistMark.mAccess.mCreatorLength > 0 ) ) {
+ const std::string creatorString = AVCHD_StringFieldToXMP (
+ playlistMark.mAccess.mCreatorLength, playlistMark.mAccess.mCreatorCharacterSet, playlistMark.mAccess.mCreator, 32 ) ;
+
+ if ( ! creatorString.empty() ) {
+ xmpObj.DeleteProperty ( kXMP_NS_DC, "creator" );
+ xmpObj.AppendArrayItem ( kXMP_NS_DC, "creator", kXMP_PropArrayIsOrdered, creatorString.c_str() );
+ }
+ }
+
+ if ( playlistMark.mDevice.mPresent && ( playlistMark.mDevice.mSerialNoLength > 0 ) ) {
+ const std::string serialNoString = AVCHD_StringFieldToXMP (
+ playlistMark.mDevice.mSerialNoLength, playlistMark.mDevice.mSerialNoCharacterCode, playlistMark.mDevice.mSerialNo, 24 ) ;
+
+ if ( ! serialNoString.empty() ) xmpObj.SetProperty ( kXMP_NS_EXIF_Aux, "SerialNumber", serialNoString.c_str(), kXMP_DeleteExisting );
+ }
+
+ if ( playlistMark.mLocation.mPresent && ( playlistMark.mLocation.mPlaceNameLength > 0 ) ) {
+ const std::string placeNameString = AVCHD_StringFieldToXMP (
+ playlistMark.mLocation.mPlaceNameLength, playlistMark.mLocation.mPlaceNameCharacterSet, playlistMark.mLocation.mPlaceName, 64 ) ;
+
+ if ( ! placeNameString.empty() ) xmpObj.SetProperty ( kXMP_NS_DM, "shotLocation", placeNameString.c_str(), kXMP_DeleteExisting );
+ }
+ }
}
} // AVCHD_MetaHandler::ProcessXMP
@@ -616,7 +2027,8 @@ void AVCHD_MetaHandler::UpdateFile ( bool doSafeUpdate )
std::string xmpPath, tempPath;
- this->MakeClipStreamPath ( &xmpPath, ".xmp" );
+ bool found = this->MakeClipStreamPath ( &xmpPath, ".xmp", true /* checkFile */ );
+ if ( ! found ) XMP_Throw ( "AVCHD_MetaHandler::UpdateFile - XMP is supposed to exist", kXMPErr_InternalFailure );
CreateTempFile ( xmpPath, &tempPath );
LFA_FileRef tempFile = LFA_Open ( tempPath.c_str(), 'w' );
diff --git a/source/XMPFiles/FileHandlers/AVCHD_Handler.hpp b/source/XMPFiles/FileHandlers/AVCHD_Handler.hpp
index 45e9cf9..4123089 100644
--- a/source/XMPFiles/FileHandlers/AVCHD_Handler.hpp
+++ b/source/XMPFiles/FileHandlers/AVCHD_Handler.hpp
@@ -3,7 +3,7 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2008 Adobe Systems Incorporated
+// Copyright 2008 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -63,9 +63,11 @@ public:
private:
AVCHD_MetaHandler() {}; // Hidden on purpose.
-
- void MakeClipInfoPath ( std::string * path, XMP_StringPtr suffix );
- void MakeClipStreamPath ( std::string * path, XMP_StringPtr suffix );
+
+ bool MakeClipInfoPath ( std::string * path, XMP_StringPtr suffix, bool checkFile = false ) const;
+ bool MakeClipStreamPath ( std::string * path, XMP_StringPtr suffix, bool checkFile = false ) const;
+ bool MakePlaylistPath ( std::string * path, XMP_StringPtr suffix, bool checkFile = false ) const;
+
void MakeLegacyDigest ( std::string * digestStr );
std::string rootPath, clipName;
diff --git a/source/XMPFiles/FileHandlers/AVI_Handler.cpp b/source/XMPFiles/FileHandlers/AVI_Handler.cpp
deleted file mode 100644
index 96a11ec..0000000
--- a/source/XMPFiles/FileHandlers/AVI_Handler.cpp
+++ /dev/null
@@ -1,498 +0,0 @@
-// =================================================================================================
-// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2008 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 "AVI_Handler.hpp"
-#include "RIFF_Support.hpp"
-
-#if XMP_WinBuild
- #include <vfw.h>
-#else
- #ifndef formtypeAVI
- #define formtypeAVI MakeFourCC('A', 'V', 'I', ' ')
- #else
- #error "formtypeAVI already defined"
- #endif
-#endif
-
-using namespace std;
-
-#define kXMPUserDataType MakeFourCC ( '_', 'P', 'M', 'X' ) /* Yes, backwards! */
-
-// FourCC codes for the RIFF chunks
-#define aviTimeChunk MakeFourCC('I','S','M','T')
-#define avihdrlChunk MakeFourCC('h','d','r','l')
-#define myOrgTimeChunk MakeFourCC('t','c','_','O') /* 0x4f5f6374 */
-#define myAltTimeChunk MakeFourCC('t','c','_','A')
-#define myOrgReelChunk MakeFourCC('r','n','_','O') /* 0x4f5f6e72 */
-#define myAltReelChunk MakeFourCC('r','n','_','A')
-#define myCommentChunk MakeFourCC('c','m','n','t')
-#define myTimeList MakeFourCC('T','d','a','t') /* 0x74616454 */
-#define myCommentList MakeFourCC('C','d','a','t')
-
-#define TIMELEN 18
-#define REELLEN 40
-#define REALTIMELEN 11
-#define COMMENTLEN 256
-
-/* list id (4 bytes) + four tags hdrs (8 each) + 2 TIMEs + 2 REELs */
-#define PR_AVI_TIMELEN (12 + 2 * (8 + TIMELEN) + 2 * (8 + REELLEN))
-
-#define PR_AVI_COMMENTLEN (12 + 8 + COMMENTLEN)
-
-#define kStartTimecode "startTimecode"
-#define kTimeValue "timeValue"
-#define kAltTimecode "altTimecode"
-#define kTapeName "tapeName"
-#define kAltTapeName "altTapeName"
-#define kLogComment "logComment"
-
-// =================================================================================================
-/// \file AVI_Handler.cpp
-/// \brief File format handler for AVI.
-///
-/// This header ...
-///
-// =================================================================================================
-
-// =================================================================================================
-// AVI_MetaHandlerCTor
-// ====================
-
-XMPFileHandler * AVI_MetaHandlerCTor ( XMPFiles * parent )
-{
- return new AVI_MetaHandler ( parent );
-
-} // AVI_MetaHandlerCTor
-
-// =================================================================================================
-// AVI_CheckFormat
-// ===============
-//
-// An AVI file must begin with "RIFF", a 4 byte little endian length, then "AVI ". The length should
-// be fileSize-8, but we don't bother checking this here.
-
-bool AVI_CheckFormat ( XMP_FileFormat format,
- XMP_StringPtr filePath,
- LFA_FileRef fileRef,
- XMPFiles * parent )
-{
- IgnoreParam(format); IgnoreParam(parent);
- XMP_Assert ( format == kXMP_AVIFile );
-
- if ( fileRef == 0 ) return false;
-
- enum { kBufferSize = 12 };
- XMP_Uns8 buffer [kBufferSize];
-
- LFA_Seek ( fileRef, 0, SEEK_SET );
- LFA_Read ( fileRef, buffer, kBufferSize );
-
- // "RIFF" is 52 49 46 46, "AVI " is 41 56 49 20
- if ( (! CheckBytes ( &buffer[0], "\x52\x49\x46\x46", 4 )) ||
- (! CheckBytes ( &buffer[8], "\x41\x56\x49\x20", 4 )) ) return false;
-
- return true;
-
-} // AVI_CheckFormat
-
-// =================================================================================================
-// AVI_MetaHandler::AVI_MetaHandler
-// ================================
-
-AVI_MetaHandler::AVI_MetaHandler ( XMPFiles * _parent )
-{
- this->parent = _parent;
- this->handlerFlags = kAVI_HandlerFlags;
- this->stdCharForm = kXMP_Char8Bit;
-
-} // AVI_MetaHandler::AVI_MetaHandler
-
-// =================================================================================================
-// AVI_MetaHandler::~AVI_MetaHandler
-// =================================
-
-AVI_MetaHandler::~AVI_MetaHandler()
-{
- // Nothing to do.
-
-} // AVI_MetaHandler::~AVI_MetaHandler
-
-// =================================================================================================
-// AVI_MetaHandler::UpdateFile
-// ===========================
-
-void AVI_MetaHandler::UpdateFile ( bool doSafeUpdate )
-{
- bool ok;
-
- if ( ! this->needsUpdate ) return;
- if ( doSafeUpdate ) XMP_Throw ( "AVI_MetaHandler::UpdateFile: Safe update not supported", kXMPErr_Unavailable );
-
- XMP_StringPtr packetStr = xmpPacket.c_str();
- XMP_StringLen packetLen = (XMP_StringLen)xmpPacket.size();
- if ( packetLen == 0 ) return;
-
- // Make sure we're writing an even number of bytes as required by the RIFF specification.
- if ( (xmpPacket.size() & 1) == 1 ) xmpPacket.push_back ( ' ' );
- XMP_Assert ( (xmpPacket.size() & 1) == 0 );
- packetStr = xmpPacket.c_str(); // ! Make sure they are current.
- packetLen = (XMP_StringLen)xmpPacket.size();
-
- LFA_FileRef fileRef(this->parent->fileRef);
- if ( fileRef == 0 ) return;
-
- RIFF_Support::RiffState riffState;
- long numTags = RIFF_Support::OpenRIFF ( fileRef, riffState );
- if ( numTags == 0 ) return;
-
- ok = RIFF_Support::PutChunk ( fileRef, riffState, formtypeAVI, kXMPUserDataType, (char*)packetStr, packetLen );
- if ( ! ok ) return; // If there's an error writing the chunk, bail.
-
- ok = CreatorAtom::Update ( this->xmpObj, fileRef, formtypeAVI, riffState );
- if ( ! ok ) return;
-
- // Update legacy metadata
-
- std::string startTimecodeString, altTimecodeString, orgReelString, altReelString, logCommentString;
-
- this->xmpObj.GetStructField ( kXMP_NS_DM, kStartTimecode, kXMP_NS_DM, kTimeValue, &startTimecodeString, 0 );
- this->xmpObj.GetStructField ( kXMP_NS_DM, kAltTimecode, kXMP_NS_DM, kTimeValue, &altTimecodeString, 0 );
- this->xmpObj.GetProperty ( kXMP_NS_DM, kTapeName, &orgReelString, 0 );
- this->xmpObj.GetProperty ( kXMP_NS_DM, kAltTapeName, &altReelString, 0 );
- this->xmpObj.GetProperty ( kXMP_NS_DM, kLogComment, &logCommentString, 0 );
-
- if ( startTimecodeString.size() != 0 ) {
- // I'm not sure why we copy into this 12 char buffer, but this is what Premiere code does.
- char aviTime [12];
- memset ( aviTime, 0, 12 );
- memcpy ( aviTime, startTimecodeString.data(), 11 ); // AUDIT: 11 is less than 12
- RewriteChunk ( fileRef, riffState, aviTimeChunk, avihdrlChunk, aviTime );
- }
-
- if ( logCommentString.size() != 0 ) {
-
- ok = FindChunk ( riffState, myCommentChunk, myCommentList, 0, 0, 0, 0 );
-
- if ( ok ) {
-
- // Always rewrite the comment string, even if empty, so the user can erase it.
- RIFF_Support::RewriteChunk ( fileRef, riffState, myCommentChunk, myCommentList, logCommentString.c_str() );
-
- } else {
-
- ok = MakeChunk ( fileRef, riffState, formtypeAVI, PR_AVI_COMMENTLEN );
- if ( ! ok ) return; // If there's an error making a chunk, bail
-
- RIFF_Support::ltag listtag;
- listtag.id = MakeUns32LE ( FOURCC_LIST );
- listtag.len = MakeUns32LE ( PR_AVI_COMMENTLEN - 8 );
- listtag.subid = MakeUns32LE ( myCommentList );
- LFA_Write ( fileRef, &listtag, 12 );
-
- RIFF_Support::WriteChunk ( fileRef, myCommentChunk, logCommentString.c_str(), COMMENTLEN );
-
- }
-
- }
-
- ok = RIFF_Support::FindChunk ( riffState, myOrgTimeChunk, myTimeList, 0, 0, 0, 0 );
-
- if ( ok ) {
-
- if ( startTimecodeString.size() != 0 ) {
- RewriteChunk ( fileRef, riffState, myOrgTimeChunk, myTimeList, startTimecodeString.c_str() );
- }
-
- if ( altTimecodeString.size() != 0 ) {
- RewriteChunk ( fileRef, riffState, myAltTimeChunk, myTimeList, altTimecodeString.c_str() );
- }
-
- // Always rewrite the reel strings, even if empty, so the user can erase them.
- RewriteChunk ( fileRef, riffState, myOrgReelChunk, myTimeList, orgReelString.c_str() );
- RewriteChunk ( fileRef, riffState, myAltReelChunk, myTimeList, altReelString.c_str() );
-
- } else {
-
- // We don't have the legacy part yet. If none of the XMP items exist then don't do anything.
- // Otherwise, add all 4 even if empty. This is the original logic from way back.
-
- bool haveAnyXMP = ( (! startTimecodeString.empty()) || (! altTimecodeString.empty()) ||
- (! orgReelString.empty()) || (! altReelString.empty()) );
-
- if ( haveAnyXMP ) {
-
- ok = MakeChunk ( fileRef, riffState, formtypeAVI, PR_AVI_TIMELEN );
- if ( ! ok ) return; // If there's an error making a chunk, bail
-
- RIFF_Support::ltag listtag;
- listtag.id = MakeUns32LE ( FOURCC_LIST );
- listtag.len = MakeUns32LE ( PR_AVI_TIMELEN - 8 );
- listtag.subid = MakeUns32LE ( myTimeList );
- LFA_Write(fileRef, &listtag, 12);
-
- RIFF_Support::WriteChunk ( fileRef, myOrgTimeChunk, startTimecodeString.c_str(), TIMELEN );
- RIFF_Support::WriteChunk ( fileRef, myAltTimeChunk, altTimecodeString.c_str(), TIMELEN );
- RIFF_Support::WriteChunk ( fileRef, myOrgReelChunk, orgReelString.c_str(), REELLEN );
- RIFF_Support::WriteChunk ( fileRef, myAltReelChunk, altReelString.c_str(), REELLEN );
-
- }
-
- }
-
- this->needsUpdate = false;
-
-} // AVI_MetaHandler::UpdateFile
-
-// =================================================================================================
-// AVI_MetaHandler::WriteFile
-// ==========================
-
-void AVI_MetaHandler::WriteFile ( LFA_FileRef sourceRef,
- const std::string & sourcePath )
-{
- IgnoreParam(sourceRef); IgnoreParam(sourcePath);
-
- XMP_Throw ( "AVI_MetaHandler::WriteFile: Not supported", kXMPErr_Unavailable );
-
-} // AVI_MetaHandler::WriteFile
-
-// =================================================================================================
-
-static void StripSimpleEmpty ( SXMPMeta * xmp, XMP_StringPtr ns, XMP_StringPtr prop )
-{
- // Small hack to clean up bad data. There are cases of code writing xmpDM:startTimecode and
- // xmpDM:altTimecode as simple properties with empty values. They are supposed to be structs.
-
- std::string value;
- XMP_OptionBits flags;
-
- bool found = xmp->GetProperty ( ns, prop, &value, &flags );
-
- if ( found && XMP_PropIsSimple(flags) && value.empty() ) {
- xmp->DeleteProperty ( ns, prop );
- }
-
-}
-
-// =================================================================================================
-// AVI_MetaHandler::CacheFileData
-// ==============================
-
-void AVI_MetaHandler::CacheFileData()
-{
- bool ok;
-
- this->containsXMP = false;
-
- LFA_FileRef fileRef ( this->parent->fileRef ); //*** simplify to assignment
- if ( fileRef == 0 ) return;
-
- bool updateFile = XMP_OptionIsSet ( this->parent->openFlags, kXMPFiles_OpenForUpdate );
- if ( updateFile ) {
-
- // Workaround for bad files in the field that have a bad size in the outermost RIFF chunk.
- // Repair the cases where the length is too long (beyond EOF). Don't repair a length that is
- // less than EOF, we don't know if there actually are multiple top level chunks. There is
- // also a check and "runtime repair" inside ReadTag, needed for read-only file access.
-
- XMP_Int64 fileLen = LFA_Measure ( fileRef );
- XMP_Uns32 riffLen;
-
- LFA_Seek ( fileRef, 4, SEEK_SET );
- LFA_Read ( fileRef, &riffLen, 4 );
- riffLen = GetUns32LE ( &riffLen );
-
- if ( (fileLen >= 8) && ((XMP_Int64)riffLen > (fileLen - 8)) ) { // Is the initial chunk too long?
-
- bool repairFile = XMP_OptionIsSet ( this->parent->openFlags, kXMPFiles_OpenRepairFile );
- if ( ! repairFile ) {
- XMP_Throw ( "Initial RIFF tag exceeds file length", kXMPErr_BadValue );
- } else {
- riffLen = MakeUns32LE ( (XMP_Uns32)fileLen - 8 );
- LFA_Seek ( fileRef, 4, SEEK_SET );
- LFA_Write ( fileRef, &riffLen, 4 );
- }
-
- }
-
- }
-
- // Contnue with normal processing.
-
- RIFF_Support::RiffState riffState;
- long numTags = RIFF_Support::OpenRIFF ( fileRef, riffState );
- if ( numTags == 0 ) return; //*** shouldn't we throw ? XMP_Throw("invalid file format") or such?
-
- // Determine the size of the metadata
- unsigned long bufferSize(0);
- ok = RIFF_Support::GetRIFFChunk ( fileRef, riffState, kXMPUserDataType /* _PMX, the xmp packet */, 0, 0, 0, &bufferSize);
-
- if ( ! ok ) {
-
- packetInfo.writeable = true; // If no packet found, created packets will be writeable.
-
- } else if ( bufferSize > 0 ) {
-
- // Size and clear the buffer
- this->xmpPacket.reserve ( bufferSize );
- this->xmpPacket.assign ( bufferSize, ' ' );
-
- // Get the metadata
- XMP_Uns64 xmpPacketPosition;
- ok = RIFF_Support::GetRIFFChunk ( fileRef, riffState, kXMPUserDataType /* _PMX, the xmp packet */, 0, 0,
- (char*)this->xmpPacket.c_str(), &bufferSize, &xmpPacketPosition );
- if ( ok ) {
- this->packetInfo.offset = xmpPacketPosition;
- this->packetInfo.length = bufferSize;
- this->xmpObj.ParseFromBuffer ( this->xmpPacket.c_str(), (XMP_StringLen)this->xmpPacket.size() );
- this->containsXMP = true;
- }
-
- }
-
- // Reconcile legacy metadata.
-
- std::string aviTimeString, orgTimeString, altTimeString, projectPathString;
- unsigned long aviTimeSize, orgTimeSize, altTimeSize;
-
- StripSimpleEmpty ( &this->xmpObj, kXMP_NS_DM, kStartTimecode );
- StripSimpleEmpty ( &this->xmpObj, kXMP_NS_DM, kAltTimecode );
-
- ok = RIFF_Support::GetRIFFChunk ( fileRef, riffState, aviTimeChunk, avihdrlChunk, 0, 0, &aviTimeSize );
- if ( ok ) {
- aviTimeString.reserve ( aviTimeSize );
- aviTimeString.assign ( aviTimeSize, ' ' );
- RIFF_Support::GetRIFFChunk ( fileRef, riffState, aviTimeChunk, avihdrlChunk, 0, (char*)aviTimeString.c_str(), &aviTimeSize );
- }
-
- ok = RIFF_Support::GetRIFFChunk ( fileRef, riffState, myOrgTimeChunk, myTimeList, 0, 0, &orgTimeSize );
- if ( ok ) {
- orgTimeString.reserve ( orgTimeSize );
- orgTimeString.assign ( orgTimeSize, ' ' );
- RIFF_Support::GetRIFFChunk ( fileRef, riffState, myOrgTimeChunk, myTimeList, 0, (char*)orgTimeString.c_str(), &orgTimeSize );
- }
-
- ok = RIFF_Support::GetRIFFChunk ( fileRef, riffState, myAltTimeChunk, myTimeList, 0, 0, &altTimeSize );
- if ( ok ) {
- altTimeString.reserve ( altTimeSize );
- altTimeString.assign ( altTimeSize, ' ' );
- RIFF_Support::GetRIFFChunk ( fileRef, riffState, myAltTimeChunk, myTimeList, 0, (char*)altTimeString.c_str(), &altTimeSize );
- }
-
- if ( (! aviTimeString.empty()) && orgTimeString.empty() && (altTimeString.empty()) ) {
-
- // If we have an avi time, and not the org or alt, use the avi. I suspect this is for some earlier backwards compatibility.
-
- std::string xmpString;
- this->xmpObj.GetStructField ( kXMP_NS_DM, kStartTimecode, kXMP_NS_DM, kTimeValue, &xmpString, 0 );
- if ( xmpString.compare ( 0, REALTIMELEN, aviTimeString, 0, REALTIMELEN ) ) {
- this->xmpObj.SetStructField ( kXMP_NS_DM, kStartTimecode, kXMP_NS_DM, kTimeValue, aviTimeString, 0 );
- this->containsXMP = true;
- }
-
- } else {
-
- // Otherwise, check the original and alt timecodes.
-
- if ( ! orgTimeString.empty() ) {
- std::string xmpString;
- this->xmpObj.GetStructField ( kXMP_NS_DM, kStartTimecode, kXMP_NS_DM, kTimeValue, &xmpString, 0 );
- if (xmpString.compare ( 0, REALTIMELEN, orgTimeString, 0, REALTIMELEN ) ) {
- this->xmpObj.SetStructField ( kXMP_NS_DM, kStartTimecode, kXMP_NS_DM, kTimeValue, orgTimeString, 0 );
- this->containsXMP = true;
- }
- }
-
- if ( ! altTimeString.empty() ) {
- std::string xmpString;
- this->xmpObj.GetStructField ( kXMP_NS_DM, kAltTimecode, kXMP_NS_DM, kTimeValue, &xmpString, 0 );
- if ( xmpString.compare ( 0, REALTIMELEN, altTimeString, 0, REALTIMELEN ) ) {
- this->xmpObj.SetStructField ( kXMP_NS_DM, kAltTimecode, kXMP_NS_DM, kTimeValue, altTimeString, 0 );
- this->containsXMP = true;
- }
- }
-
- }
-
- unsigned long orgReelSize;
- ok = RIFF_Support::GetRIFFChunk ( fileRef, riffState, myOrgReelChunk, myTimeList, 0, 0, &orgReelSize );
- if ( ok ) {
-
- std::string orgReelString;
- orgReelString.reserve ( orgReelSize );
- orgReelString.assign ( orgReelSize, ' ' );
- RIFF_Support::GetRIFFChunk ( fileRef, riffState, myOrgReelChunk, myTimeList, 0, (char*)orgReelString.c_str(), &orgReelSize );
-
- if ( ! orgReelString.empty() ) {
- std::string xmpString;
- this->xmpObj.GetProperty ( kXMP_NS_DM, kTapeName, &xmpString, 0 );
- if ( xmpString.compare ( 0, REELLEN, orgReelString, 0, REELLEN ) ) {
- this->xmpObj.SetProperty ( kXMP_NS_DM, kTapeName, orgReelString, 0 );
- this->containsXMP = true;
- }
- }
-
- }
-
- unsigned long altReelSize;
- ok = RIFF_Support::GetRIFFChunk ( fileRef, riffState, myAltReelChunk, myTimeList, 0, 0, &altReelSize );
- if ( ok ) {
-
- std::string altReelString;
- altReelString.reserve ( altReelSize );
- altReelString.assign ( altReelSize, ' ' );
- RIFF_Support::GetRIFFChunk ( fileRef, riffState, myAltReelChunk, myTimeList, 0, (char*)altReelString.c_str(), &altReelSize );
-
- if ( ! altReelString.empty() ) {
- std::string xmpString;
- this->xmpObj.GetProperty ( kXMP_NS_DM, kAltTapeName, &xmpString, 0 );
- if ( xmpString.compare ( 0, REELLEN, altReelString, 0, REELLEN ) ) {
- this->xmpObj.SetProperty ( kXMP_NS_DM, kAltTapeName, altReelString, 0 );
- this->containsXMP = true;
- }
- }
-
- }
-
- unsigned long logCommentSize;
- ok = RIFF_Support::GetRIFFChunk ( fileRef, riffState, myCommentChunk, myCommentList, 0, 0, &logCommentSize );
- if ( ok ) {
-
- std::string logCommentString;
- logCommentString.reserve ( logCommentSize );
- logCommentString.assign ( logCommentSize, ' ' );
- RIFF_Support::GetRIFFChunk ( fileRef, riffState, myCommentChunk, myCommentList, 0, (char*)logCommentString.c_str(), &logCommentSize );
-
- if ( ! logCommentString.empty() ) {
- std::string xmpString;
- this->xmpObj.GetProperty ( kXMP_NS_DM, kLogComment, &xmpString, 0 );
- if ( xmpString.compare ( logCommentString ) ) {
- this->xmpObj.SetProperty ( kXMP_NS_DM, kLogComment, logCommentString, 0 );
- this->containsXMP = true;
- }
- }
-
- }
-
- CreatorAtom::Import ( this->xmpObj, fileRef, riffState );
-
- //// Update the xmpPacket, as the xmpObj might have been updated with legacy info.
- //// Produce packet of same size [1781657]
- //try {
- // this->xmpObj.SerializeToBuffer ( &this->xmpPacket,
- // (kXMP_UseCompactFormat | kXMP_ExactPacketLength) , packetInfo.length );
- //} catch ( XMP_Error ) {
- // this->xmpObj.SerializeToBuffer ( &this->xmpPacket, (kXMP_UseCompactFormat ) );
- //}
-
- // removed for [1781657] this->packetInfo.offset = kXMPFiles_UnknownOffset;
- // removed for [1781657] this->packetInfo.length = (XMP_StringLen)this->xmpPacket.size();
- this->processedXMP = this->containsXMP;
-
-} // AVI_MetaHandler::CacheFileData
diff --git a/source/XMPFiles/FileHandlers/AVI_Handler.hpp b/source/XMPFiles/FileHandlers/AVI_Handler.hpp
deleted file mode 100644
index 9db656c..0000000
--- a/source/XMPFiles/FileHandlers/AVI_Handler.hpp
+++ /dev/null
@@ -1,53 +0,0 @@
-#ifndef __AVI_Handler_hpp__
-#define __AVI_Handler_hpp__ 1
-
-// =================================================================================================
-// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2007 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_Impl.hpp"
-
-// =================================================================================================
-/// \file AVI_Handler.hpp
-/// \brief File format handler for AVI.
-///
-/// This header ...
-///
-// =================================================================================================
-
-extern XMPFileHandler * AVI_MetaHandlerCTor ( XMPFiles * parent );
-
-extern bool AVI_CheckFormat ( XMP_FileFormat format,
- XMP_StringPtr filePath,
- LFA_FileRef fileRef,
- XMPFiles * parent );
-
-static const XMP_OptionBits kAVI_HandlerFlags = (kXMPFiles_CanInjectXMP |
- kXMPFiles_CanExpand |
- kXMPFiles_PrefersInPlace |
- kXMPFiles_AllowsOnlyXMP |
- kXMPFiles_ReturnsRawPacket);
- // In the future, we'll add kXMPFiles_CanReconcile
-
-class AVI_MetaHandler : public XMPFileHandler
-{
-public:
-
- AVI_MetaHandler ( XMPFiles * parent );
- ~AVI_MetaHandler();
-
- void CacheFileData();
-
- void UpdateFile ( bool doSafeUpdate );
- void WriteFile ( LFA_FileRef sourceRef, const std::string & sourcePath );
-
-}; // AVI_MetaHandler
-
-// =================================================================================================
-
-#endif /* __AVI_Handler_hpp__ */
diff --git a/source/XMPFiles/FileHandlers/Basic_Handler.cpp b/source/XMPFiles/FileHandlers/Basic_Handler.cpp
index f3b471e..3efd739 100644
--- a/source/XMPFiles/FileHandlers/Basic_Handler.cpp
+++ b/source/XMPFiles/FileHandlers/Basic_Handler.cpp
@@ -1,6 +1,6 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2007 Adobe Systems Incorporated
+// Copyright 2004 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
diff --git a/source/XMPFiles/FileHandlers/Basic_Handler.hpp b/source/XMPFiles/FileHandlers/Basic_Handler.hpp
index 45eef9f..17b9775 100644
--- a/source/XMPFiles/FileHandlers/Basic_Handler.hpp
+++ b/source/XMPFiles/FileHandlers/Basic_Handler.hpp
@@ -3,7 +3,7 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2007 Adobe Systems Incorporated
+// Copyright 2004 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
diff --git a/source/XMPFiles/FileHandlers/FLV_Handler.cpp b/source/XMPFiles/FileHandlers/FLV_Handler.cpp
index 2472870..f9624ed 100644
--- a/source/XMPFiles/FileHandlers/FLV_Handler.cpp
+++ b/source/XMPFiles/FileHandlers/FLV_Handler.cpp
@@ -1,6 +1,6 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2008 Adobe Systems Incorporated
+// Copyright 2007 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -316,7 +316,7 @@ static inline bool CheckName ( XMP_StringPtr inputName, XMP_Uns16 inputLen,
void FLV_MetaHandler::CacheFileData()
{
- XMP_Assert ( (! this->containsXMP) && (! this->containsTNail) );
+ XMP_Assert ( ! this->containsXMP );
XMP_AbortProc abortProc = this->parent->abortProc;
void * abortArg = this->parent->abortArg;
@@ -569,6 +569,7 @@ void FLV_MetaHandler::UpdateFile ( bool doSafeUpdate )
this->WriteFile ( origRef, origPath );
} catch ( ... ) {
LFA_Close ( updateRef );
+ LFA_Delete ( updatePath.c_str() );
this->parent->filePath = origPath;
this->parent->fileRef = origRef;
throw;
diff --git a/source/XMPFiles/FileHandlers/FLV_Handler.hpp b/source/XMPFiles/FileHandlers/FLV_Handler.hpp
index 1fea76d..80ed1a0 100644
--- a/source/XMPFiles/FileHandlers/FLV_Handler.hpp
+++ b/source/XMPFiles/FileHandlers/FLV_Handler.hpp
@@ -3,7 +3,7 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2007 Adobe Systems Incorporated
+// Copyright 2007 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
diff --git a/source/XMPFiles/FileHandlers/InDesign_Handler.cpp b/source/XMPFiles/FileHandlers/InDesign_Handler.cpp
index c26db20..2f9845e 100644
--- a/source/XMPFiles/FileHandlers/InDesign_Handler.cpp
+++ b/source/XMPFiles/FileHandlers/InDesign_Handler.cpp
@@ -1,6 +1,6 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2008 Adobe Systems Incorporated
+// Copyright 2004 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
diff --git a/source/XMPFiles/FileHandlers/InDesign_Handler.hpp b/source/XMPFiles/FileHandlers/InDesign_Handler.hpp
index bed1851..aded52e 100644
--- a/source/XMPFiles/FileHandlers/InDesign_Handler.hpp
+++ b/source/XMPFiles/FileHandlers/InDesign_Handler.hpp
@@ -3,7 +3,7 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2007 Adobe Systems Incorporated
+// Copyright 2004 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
diff --git a/source/XMPFiles/FileHandlers/JPEG_Handler.cpp b/source/XMPFiles/FileHandlers/JPEG_Handler.cpp
index 6917355..a3842f8 100644
--- a/source/XMPFiles/FileHandlers/JPEG_Handler.cpp
+++ b/source/XMPFiles/FileHandlers/JPEG_Handler.cpp
@@ -1,6 +1,6 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2008 Adobe Systems Incorporated
+// Copyright 2004 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -13,6 +13,7 @@
#include "PSIR_Support.hpp"
#include "IPTC_Support.hpp"
#include "ReconcileLegacy.hpp"
+#include "Reconcile_Impl.hpp"
#include "MD5.h"
@@ -88,23 +89,23 @@ bool JPEG_CheckFormat ( XMP_FileFormat format,
XMP_Assert ( format == kXMP_JPEGFile );
IOBuffer ioBuf;
-
+
LFA_Seek ( fileRef, 0, SEEK_SET );
if ( ! CheckFileSpace ( fileRef, &ioBuf, 4 ) ) return false; // We need at least 4, the buffer is filled anyway.
-
+
// First look for the SOI standalone marker. Then skip all 0xFF bytes, padding plus the high
// order byte of the next marker. Finally see if the next marker is legit.
-
+
if ( ! CheckBytes ( ioBuf.ptr, "\xFF\xD8", 2 ) ) return false;
ioBuf.ptr += 2; // Move past the SOI.
while ( (ioBuf.ptr < ioBuf.limit) && (*ioBuf.ptr == 0xFF) ) ++ioBuf.ptr;
if ( ioBuf.ptr == ioBuf.limit ) return false;
-
+
XMP_Uns8 id = *ioBuf.ptr;
if ( id >= 0xDD ) return true; // The most probable cases.
if ( (id < 0xC0) || ((id & 0xF8) == 0xD0) || (id == 0xD8) || (id == 0xDA) || (id == 0xDC) ) return false;
return true;
-
+
} // JPEG_CheckFormat
// =================================================================================================
@@ -130,44 +131,8 @@ JPEG_MetaHandler::~JPEG_MetaHandler()
if ( exifMgr != 0 ) delete ( exifMgr );
if ( psirMgr != 0 ) delete ( psirMgr );
if ( iptcMgr != 0 ) delete ( iptcMgr );
-
-} // JPEG_MetaHandler::~JPEG_MetaHandler
-
-// =================================================================================================
-// TableOrDataMarker
-// =================
-//
-// Returns true if the marker is for a table or data marker segment:
-// FFC4 - DHT
-// FFCC - DAC
-// FFDB - DQT
-// FFDC - DNL
-// FFDD - DRI
-// FFDE - DHP
-// FFDF - EXP
-// FFEn - APPn
-// FFFE - COM
-
-static inline bool TableOrDataMarker ( XMP_Uns16 marker )
-{
-
- if ( (marker & 0xFFF0) == 0xFFE0 ) return true; // APPn is probably the most common case.
-
- if ( marker < 0xFFC4 ) return false;
- if ( marker == 0xFFC4 ) return true;
- if ( marker < 0xFFCC ) return false;
- if ( marker == 0xFFCC ) return true;
-
- if ( marker < 0xFFDB ) return false;
- if ( marker <= 0xFFDF ) return true;
-
- if ( marker < 0xFFFE ) return false;
- if ( marker == 0xFFFE ) return true;
-
- return false;
-
-} // TableOrDataMarker
+} // JPEG_MetaHandler::~JPEG_MetaHandler
// =================================================================================================
// JPEG_MetaHandler::CacheFileData
@@ -182,7 +147,7 @@ static inline bool TableOrDataMarker ( XMP_Uns16 marker )
// EOI marker, 2 bytes, 0xFFD9
//
// Each marker segment begins with a 2 byte big endian marker and a 2 byte big endian length. The
-// length includes the 2 bytes of the length field but not the marker. The high order byte of a
+// length includes the 2 bytes of the length field but not the marker. The high order byte of a
// marker is 0xFF, the low order byte tells what kind of marker. A marker can be preceeded by any
// number of 0xFF fill bytes, however there are no alignment constraints.
//
@@ -221,20 +186,20 @@ void JPEG_MetaHandler::CacheFileData()
size_t segLen;
bool ok;
IOBuffer ioBuf;
-
+
XMP_AbortProc abortProc = this->parent->abortProc;
void * abortArg = this->parent->abortArg;
const bool checkAbort = (abortProc != 0);
-
+
ExtendedXMPInfo extXMP;
-
- XMP_Assert ( (! this->containsXMP) && (! this->containsTNail) );
+
+ XMP_Assert ( ! this->containsXMP );
// Set containsXMP to true here only if the standard XMP packet is found.
-
+
XMP_Assert ( kPSIRSignatureLength == (strlen(kPSIRSignatureString) + 1) );
XMP_Assert ( kMainXMPSignatureLength == (strlen(kMainXMPSignatureString) + 1) );
XMP_Assert ( kExtXMPSignatureLength == (strlen(kExtXMPSignatureString) + 1) );
-
+
// -------------------------------------------------------------------------------------------
// Look for any of the Exif, PSIR, main XMP, or extended XMP marker segments. Quit when we hit
// an SOFn, EOI, or invalid/unexpected marker.
@@ -242,13 +207,13 @@ void JPEG_MetaHandler::CacheFileData()
LFA_Seek ( fileRef, 2, SEEK_SET ); // Skip the SOI. The JPEG header has already been verified.
ioBuf.filePos = 2;
RefillBuffer ( fileRef, &ioBuf );
-
+
while ( true ) {
if ( checkAbort && abortProc(abortArg) ) {
XMP_Throw ( "JPEG_MetaHandler::CacheFileData - User abort", kXMPErr_UserAbort );
}
-
+
if ( ! CheckFileSpace ( fileRef, &ioBuf, 2 ) ) return;
if ( *ioBuf.ptr != 0xFF ) return; // All valid markers have a high byte of 0xFF.
@@ -256,26 +221,31 @@ void JPEG_MetaHandler::CacheFileData()
++ioBuf.ptr;
if ( ! CheckFileSpace ( fileRef, &ioBuf, 1 ) ) return;
}
-
+
XMP_Uns16 marker = 0xFF00 + *ioBuf.ptr;
+
+ if ( (marker == 0xFFDA) || (marker == 0xFFD9) ) break; // Quit reading at the first SOS marker or at EOI.
+ if ( (marker == 0xFF01) || // Ill-formed file if we encounter a TEM or RSTn marker.
+ ((0xFFD0 <= marker) && (marker <= 0xFFD7)) ) return;
+
if ( marker == 0xFFED ) {
-
+
// This is an APP13 marker, is it the Photoshop image resources?
-
+
++ioBuf.ptr; // Move ioBuf.ptr to the marker segment length field.
if ( ! CheckFileSpace ( fileRef, &ioBuf, 2 ) ) return;
-
+
segLen = GetUns16BE ( ioBuf.ptr );
if ( segLen < 2 ) return; // Invalid JPEG.
ioBuf.ptr += 2; // Move ioBuf.ptr to the marker segment content.
segLen -= 2; // Adjust segLen to count just the content portion.
-
+
ok = CheckFileSpace ( fileRef, &ioBuf, kPSIRSignatureLength );
if ( ok && (segLen >= kPSIRSignatureLength) &&
CheckBytes ( ioBuf.ptr, kPSIRSignatureString, kPSIRSignatureLength ) ) {
-
+
// This is the Photoshop image resources, cache the contents.
ioBuf.ptr += kPSIRSignatureLength; // Move ioBuf.ptr to the image resources.
@@ -285,9 +255,9 @@ void JPEG_MetaHandler::CacheFileData()
this->psirContents.assign ( (XMP_StringPtr)ioBuf.ptr, segLen );
ioBuf.ptr += segLen;
-
+
} else {
-
+
// This is the not Photoshop image resources, skip the marker segment's content.
if ( segLen <= size_t(ioBuf.limit - ioBuf.ptr) ) {
@@ -300,30 +270,30 @@ void JPEG_MetaHandler::CacheFileData()
}
}
-
+
continue; // Move on to the next marker.
-
+
} else if ( marker == 0xFFE1 ) {
-
+
// This is an APP1 marker, is it the Exif, main XMP, or extended XMP?
// ! Check in that order, which happens to be increasing signature string length.
-
+
++ioBuf.ptr; // Move ioBuf.ptr to the marker segment length field.
if ( ! CheckFileSpace ( fileRef, &ioBuf, 2 ) ) return;
-
+
segLen = GetUns16BE ( ioBuf.ptr );
if ( segLen < 2 ) return; // Invalid JPEG.
ioBuf.ptr += 2; // Move ioBuf.ptr to the marker segment content.
segLen -= 2; // Adjust segLen to count just the content portion.
-
+
// Check for the Exif APP1 marker segment.
-
+
ok = CheckFileSpace ( fileRef, &ioBuf, kExifSignatureLength );
if ( ok && (segLen >= kExifSignatureLength) &&
(CheckBytes ( ioBuf.ptr, kExifSignatureString, kExifSignatureLength ) ||
CheckBytes ( ioBuf.ptr, kExifSignatureAltStr, kExifSignatureLength )) ) {
-
+
// This is the Exif metadata, cache the contents.
ioBuf.ptr += kExifSignatureLength; // Move ioBuf.ptr to the TIFF stream.
@@ -333,24 +303,24 @@ void JPEG_MetaHandler::CacheFileData()
this->exifContents.assign ( (XMP_StringPtr)ioBuf.ptr, segLen );
ioBuf.ptr += segLen;
-
+
continue; // Move on to the next marker.
-
+
}
-
+
// Check for the main XMP APP1 marker segment.
-
+
ok = CheckFileSpace ( fileRef, &ioBuf, kMainXMPSignatureLength );
if ( ok && (segLen >= kMainXMPSignatureLength) &&
CheckBytes ( ioBuf.ptr, kMainXMPSignatureString, kMainXMPSignatureLength ) ) {
-
+
// This is the main XMP, cache the contents.
ioBuf.ptr += kMainXMPSignatureLength; // Move ioBuf.ptr to the XMP Packet.
segLen -= kMainXMPSignatureLength; // Adjust segLen to count just the XMP Packet.
ok = CheckFileSpace ( fileRef, &ioBuf, segLen ); // Buffer the full content portion.
if ( ! ok ) return; // Must be a truncated file.
-
+
this->packetInfo.offset = ioBuf.filePos + (ioBuf.ptr - &ioBuf.data[0]);
this->packetInfo.length = (XMP_Int32)segLen;
this->packetInfo.padSize = 0; // Assume for now, set these properly in ProcessXMP.
@@ -359,50 +329,50 @@ void JPEG_MetaHandler::CacheFileData()
this->xmpPacket.assign ( (XMP_StringPtr)ioBuf.ptr, segLen );
ioBuf.ptr += segLen; // ! Set this->packetInfo.offset first!
-
+
this->containsXMP = true; // Found the standard XMP packet.
continue; // Move on to the next marker.
-
+
}
-
+
// Check for an extension XMP APP1 marker segment.
-
+
ok = CheckFileSpace ( fileRef, &ioBuf, kExtXMPPrefixLength ); // ! The signature, GUID, length, and offset.
if ( ok && (segLen >= kExtXMPPrefixLength) &&
CheckBytes ( ioBuf.ptr, kExtXMPSignatureString, kExtXMPSignatureLength ) ) {
-
+
// This is a portion of the extended XMP, cache the contents. This is complicated by
// the need to tolerate files where the extension portions are not in order. The
// local ExtendedXMPInfo map uses the GUID as the key and maps that to a struct that
// has the full length and a map of the known portions. This known portion map uses
// the offset of the portion as the key and maps that to a string. Only fully seen
// extended XMP streams are kept, the right one gets picked in ProcessXMP.
-
+
segLen -= kExtXMPPrefixLength; // Adjust segLen to count just the XMP stream portion.
ioBuf.ptr += kExtXMPSignatureLength; // Move ioBuf.ptr to the GUID.
GUID_32 guid;
XMP_Assert ( sizeof(guid.data) == 32 );
memcpy ( &guid.data[0], ioBuf.ptr, sizeof(guid.data) ); // AUDIT: Use of sizeof(guid.data) is safe.
-
+
ioBuf.ptr += 32; // Move ioBuf.ptr to the length and offset.
XMP_Uns32 fullLen = GetUns32BE ( ioBuf.ptr );
XMP_Uns32 offset = GetUns32BE ( ioBuf.ptr+4 );
-
+
ioBuf.ptr += 8; // Move ioBuf.ptr to the XMP stream portion.
-
+
#if Trace_UnlimitedJPEG
printf ( "New extended XMP portion: fullLen %d, offset %d, GUID %.32s\n", fullLen, offset, guid.data );
#endif
-
+
// Find the ExtXMPContent for this GUID, and the string for this portion's offset.
-
+
ExtendedXMPInfo::iterator guidPos = extXMP.find ( guid );
if ( guidPos == extXMP.end() ) {
ExtXMPContent newExtContent ( fullLen );
guidPos = extXMP.insert ( extXMP.begin(), ExtendedXMPInfo::value_type ( guid, newExtContent ) );
}
-
+
ExtXMPPortions::iterator offsetPos;
ExtXMPContent & extContent = guidPos->second;
@@ -427,21 +397,21 @@ void JPEG_MetaHandler::CacheFileData()
ExtXMPPortions::value_type ( offset, std::string() ) );
}
}
-
+
// Cache this portion of the extended XMP.
-
+
std::string & extPortion = offsetPos->second;
ok = CheckFileSpace ( fileRef, &ioBuf, segLen ); // Buffer the full content portion.
if ( ! ok ) return; // Must be a truncated file.
extPortion.append ( (XMP_StringPtr)ioBuf.ptr, segLen );
ioBuf.ptr += segLen;
-
+
continue; // Move on to the next marker.
-
+
}
-
+
// If we get here this is some other uninteresting APP1 marker segment, skip it.
-
+
if ( segLen <= size_t(ioBuf.limit - ioBuf.ptr) ) {
ioBuf.ptr += segLen; // The next marker is in this buffer.
} else {
@@ -450,14 +420,14 @@ void JPEG_MetaHandler::CacheFileData()
ioBuf.filePos = LFA_Seek ( fileRef, skipCount, SEEK_CUR );
ioBuf.ptr = ioBuf.limit; // No data left in the buffer.
}
-
- } else if ( TableOrDataMarker ( marker ) ) {
-
+
+ } else {
+
// This is a non-terminating but uninteresting marker segment. Skip it.
-
+
++ioBuf.ptr; // Move ioBuf.ptr to the marker segment length field.
if ( ! CheckFileSpace ( fileRef, &ioBuf, 2 ) ) return;
-
+
segLen = GetUns16BE ( ioBuf.ptr ); // Remember that the length includes itself.
if ( segLen < 2 ) return; // Invalid JPEG.
@@ -469,39 +439,35 @@ void JPEG_MetaHandler::CacheFileData()
ioBuf.filePos = LFA_Seek ( fileRef, skipCount, SEEK_CUR );
ioBuf.ptr = ioBuf.limit; // No data left in the buffer.
}
-
- continue; // Move on to the next marker.
- } else {
+ continue; // Move on to the next marker.
- break; // This is a terminating marker of some sort.
-
}
-
+
}
if ( ! extXMP.empty() ) {
-
+
// We have extended XMP. Find out which ones are complete, collapse them into a single
// string, and save them for ProcessXMP.
-
+
ExtendedXMPInfo::iterator guidPos = extXMP.begin();
ExtendedXMPInfo::iterator guidEnd = extXMP.end();
-
+
for ( ; guidPos != guidEnd; ++guidPos ) {
-
+
ExtXMPContent & thisContent = guidPos->second;
ExtXMPPortions::iterator partZero = thisContent.portions.begin();
ExtXMPPortions::iterator partEnd = thisContent.portions.end();
ExtXMPPortions::iterator partPos = partZero;
-
+
#if Trace_UnlimitedJPEG
printf ( "Extended XMP portions for GUID %.32s, full length %d\n",
guidPos->first.data, guidPos->second.length );
printf ( " Offset %d, length %d, next offset %d\n",
partZero->first, partZero->second.size(), (partZero->first + partZero->second.size()) );
#endif
-
+
for ( ++partPos; partPos != partEnd; ++partPos ) {
#if Trace_UnlimitedJPEG
printf ( " Offset %d, length %d, next offset %d\n",
@@ -510,7 +476,7 @@ void JPEG_MetaHandler::CacheFileData()
if ( partPos->first != partZero->second.size() ) break; // Quit if not contiguous.
partZero->second.append ( partPos->second );
}
-
+
if ( (partPos == partEnd) && (partZero->first == 0) && (partZero->second.size() == thisContent.length) ) {
// This is a complete extended XMP stream.
this->extendedXMP.insert ( ExtendedXMPMap::value_type ( guidPos->first, partZero->second ) );
@@ -519,38 +485,78 @@ void JPEG_MetaHandler::CacheFileData()
guidPos->first.data, partZero->second.size() );
#endif
}
-
+
}
-
+
}
-
+
} // JPEG_MetaHandler::CacheFileData
// =================================================================================================
-// JPEG_MetaHandler::ProcessTNail
-// ==============================
+// TrimFullExifAPP1
+// ================
+//
+// Try to trim trailing padding from full Exif APP1 segment written by some Nikon cameras. Do a
+// temporary read-only parse of the Exif APP1 contents, determine the highest used offset, trim the
+// padding if all zero bytes.
-void JPEG_MetaHandler::ProcessTNail()
+static const char * IFDNames[] = { "Primary", "TNail", "Exif", "GPS", "Interop", };
+
+static void TrimFullExifAPP1 ( std::string * exifContents )
{
+ TIFF_MemoryReader tempMgr;
+ TIFF_MemoryReader::TagInfo tagInfo;
+ bool tagFound, isNikon;
+
+ // ! Make a copy of the data to parse! The RO memory TIFF manager will flip bytes in-place!
+ tempMgr.ParseMemoryStream ( exifContents->data(), (XMP_Uns32)exifContents->size(), true /* copy data */ );
+
+ // Only trim the Exif APP1 from Nikon cameras.
+ tagFound = tempMgr.GetTag ( kTIFF_PrimaryIFD, kTIFF_Make, &tagInfo );
+ isNikon = tagFound && (tagInfo.type == kTIFF_ASCIIType) && (tagInfo.count >= 5) &&
+ (memcmp ( tagInfo.dataPtr, "NIKON", 5) == 0);
+ if ( ! isNikon ) return;
- XMP_Assert ( ! this->processedTNail );
- this->processedTNail = true; // Make sure we only come through here once.
- this->containsTNail = false; // Set it to true after all of the info is gathered.
+ // Determine the highest used offset (actually 1 beyond that). Look at the IFD structure, and
+ // the thumbnail info. Ignore the MakerNote tag, Nikon says they are self-contained.
- if ( this->exifMgr == 0 ) { // Thumbnails only need the Exif, not the PSIR or IPTC.
- bool readOnly = ((this->parent->openFlags & kXMPFiles_OpenForUpdate) == 0);
- if ( readOnly ) { // *** Could reduce heap usage by not copying in TIFF_MemoryReader.
- this->exifMgr = new TIFF_MemoryReader();
- } else {
- this->exifMgr = new TIFF_FileWriter();
+ XMP_Uns32 maxOffset = 0;
+
+ for ( XMP_Uns8 ifd = 0; ifd < kTIFF_KnownIFDCount; ++ifd ) {
+
+ TIFF_MemoryReader::TagInfoMap tagMap;
+ bool ifdFound = tempMgr.GetIFD ( ifd, &tagMap );
+ if ( ! ifdFound ) continue;
+
+ TIFF_MemoryReader::TagInfoMap::const_iterator mapPos = tagMap.begin();
+ TIFF_MemoryReader::TagInfoMap::const_iterator mapEnd = tagMap.end();
+
+ for ( ; mapPos != mapEnd; ++mapPos ) {
+ const TIFF_MemoryReader::TagInfo & tagInfo = mapPos->second;
+ XMP_Uns32 tagEnd = tempMgr.GetValueOffset ( ifd, tagInfo.id ) + tagInfo.dataLen;
+ if ( tagEnd > maxOffset ) maxOffset = tagEnd;
}
- this->exifMgr->ParseMemoryStream ( this->exifContents.c_str(), (XMP_Uns32)this->exifContents.size() );
+
}
- this->containsTNail = this->exifMgr->GetTNailInfo ( &this->tnailInfo );
- if ( this->containsTNail ) this->tnailInfo.fileFormat = this->parent->format;
+ tagFound = tempMgr.GetTag ( kTIFF_TNailIFD, kTIFF_JPEGInterchangeFormat, &tagInfo );
+ if ( tagFound ) {
+ XMP_Uns32 tnailOffset = tempMgr.GetUns32 ( tagInfo.dataPtr );
+ tagFound = tempMgr.GetTag ( kTIFF_TNailIFD, kTIFF_JPEGInterchangeFormatLength, &tagInfo );
+ if ( ! tagFound ) return; // Don't trim if there is a TNail offset but no length.
+ tnailOffset += tempMgr.GetUns32 ( tagInfo.dataPtr );
+ if ( tnailOffset > maxOffset ) maxOffset = tnailOffset;
+ }
-} // JPEG_MetaHandler::ProcessTNail
+ if ( maxOffset >= exifContents->size() ) return; // Sanity check for in bounds maximum offset.
+
+ for ( size_t i = maxOffset, limit = exifContents->size(); i < limit; ++i ) {
+ if ( (*exifContents)[i] != 0 ) return; // Don't trim if unless the trailer is all zero.
+ }
+
+ exifContents->erase ( maxOffset );
+
+} // TrimFullExifAPP1
// =================================================================================================
// JPEG_MetaHandler::ProcessXMP
@@ -560,12 +566,12 @@ void JPEG_MetaHandler::ProcessTNail()
void JPEG_MetaHandler::ProcessXMP()
{
-
+
XMP_Assert ( ! this->processedXMP );
this->processedXMP = true; // Make sure we only come through here once.
-
+
// Create the PSIR and IPTC handlers, even if there is no legacy. They might be needed for updates.
-
+
XMP_Assert ( (this->psirMgr == 0) && (this->iptcMgr == 0) ); // ProcessTNail might create the exifMgr.
bool readOnly = ((this->parent->openFlags & kXMPFiles_OpenForUpdate) == 0);
@@ -575,75 +581,60 @@ void JPEG_MetaHandler::ProcessXMP()
this->psirMgr = new PSIR_MemoryReader();
this->iptcMgr = new IPTC_Reader(); // ! Parse it later.
} else {
+ if ( this->exifContents.size() == (65534 - 2 - 6) ) TrimFullExifAPP1 ( &this->exifContents );
if ( this->exifMgr == 0 ) this->exifMgr = new TIFF_FileWriter();
this->psirMgr = new PSIR_FileWriter();
- #if ! XMP_UNIXBuild
- this->iptcMgr = new IPTC_Writer(); // ! Parse it later.
- #else
- // ! Hack until the legacy-as-local issues are resolved for generic UNIX.
- this->iptcMgr = new IPTC_Reader(); // ! Import IPTC but don't export it.
- #endif
+ this->iptcMgr = new IPTC_Writer(); // ! Parse it later.
}
// Set up everything for the legacy import, but don't do it yet. This lets us do a forced legacy
// import if the XMP packet gets parsing errors.
- bool found;
- bool haveExif = (! this->exifContents.empty());
- bool haveIPTC = false;
-
- RecJTP_LegacyPriority lastLegacy = kLegacyJTP_None;
-
TIFF_Manager & exif = *this->exifMgr; // Give the compiler help in recognizing non-aliases.
PSIR_Manager & psir = *this->psirMgr;
IPTC_Manager & iptc = *this->iptcMgr;
+ bool haveExif = (! this->exifContents.empty());
if ( haveExif ) {
exif.ParseMemoryStream ( this->exifContents.c_str(), (XMP_Uns32)this->exifContents.size() );
}
-
- if ( ! this->psirContents.empty() ) {
+
+ bool havePSIR = (! this->psirContents.empty());
+ if ( havePSIR ) {
psir.ParseMemoryResources ( this->psirContents.c_str(), (XMP_Uns32)this->psirContents.size() );
}
-
- // Determine the last-legacy priority and do the reconciliation. For JPEG files, the relevant
- // legacy priorities (ignoring Mac pnot and ANPA resources) are:
- // kLegacyJTP_PSIR_OldCaption - highest
- // kLegacyJTP_PSIR_IPTC
- // kLegacyJTP_JPEG_TIFF_Tags
- // kLegacyJTP_None - lowest
-
- found = psir.GetImgRsrc ( kPSIR_OldCaption, 0 );
- if ( ! found ) found = psir.GetImgRsrc ( kPSIR_OldCaptionPStr, 0 );
- if ( found ) {
- haveIPTC = true;
- lastLegacy = kLegacyJTP_PSIR_OldCaption;
- }
PSIR_Manager::ImgRsrcInfo iptcInfo;
- found = psir.GetImgRsrc ( kPSIR_IPTC, &iptcInfo );
- if ( found ) {
- haveIPTC = true;
- iptc.ParseMemoryDataSets ( iptcInfo.dataPtr, iptcInfo.dataLen );
- if ( lastLegacy < kLegacyJTP_PSIR_IPTC ) lastLegacy = kLegacyJTP_PSIR_IPTC;
- }
+ bool haveIPTC = false;
+ if ( havePSIR ) haveIPTC = psir.GetImgRsrc ( kPSIR_IPTC, &iptcInfo );;
+ int iptcDigestState = kDigestMatches;
- if ( lastLegacy < kLegacyJTP_JPEG_TIFF_Tags ) {
- found = exif.GetTag ( kTIFF_PrimaryIFD, kTIFF_ImageDescription, 0 );
- if ( ! found ) found = exif.GetTag ( kTIFF_PrimaryIFD, kTIFF_Artist, 0 );
- if ( ! found ) found = exif.GetTag ( kTIFF_PrimaryIFD, kTIFF_Copyright, 0 );
- if ( found ) lastLegacy = kLegacyJTP_JPEG_TIFF_Tags;
+ if ( haveIPTC ) {
+
+ bool haveDigest = false;
+ PSIR_Manager::ImgRsrcInfo digestInfo;
+ if ( havePSIR ) haveDigest = psir.GetImgRsrc ( kPSIR_IPTCDigest, &digestInfo );
+ if ( digestInfo.dataLen != 16 ) haveDigest = false;
+
+ if ( ! haveDigest ) {
+ iptcDigestState = kDigestMissing;
+ } else {
+ iptcDigestState = PhotoDataUtils::CheckIPTCDigest ( iptcInfo.dataPtr, iptcInfo.dataLen, digestInfo.dataPtr );
+ }
+
}
-
+
XMP_OptionBits options = 0;
if ( this->containsXMP ) options |= k2XMP_FileHadXMP;
if ( haveExif ) options |= k2XMP_FileHadExif;
if ( haveIPTC ) options |= k2XMP_FileHadIPTC;
-
+
// Process the main XMP packet. If it fails to parse, do a forced legacy import but still throw
// an exception. This tells the caller that an error happened, but gives them recovered legacy
// should they want to proceed with that.
+ bool haveXMP = false;
+
if ( ! this->xmpPacket.empty() ) {
XMP_Assert ( this->containsXMP );
// Common code takes care of packetInfo.charForm, .padSize, and .writeable.
@@ -651,9 +642,12 @@ void JPEG_MetaHandler::ProcessXMP()
XMP_StringLen packetLen = (XMP_StringLen)this->xmpPacket.size();
try {
this->xmpObj.ParseFromBuffer ( packetStr, packetLen );
+ haveXMP = true;
} catch ( ... ) {
XMP_ClearOption ( options, k2XMP_FileHadXMP );
- ImportJTPtoXMP ( kXMP_JPEGFile, lastLegacy, &exif, psir, &iptc, &this->xmpObj, options );
+ if ( haveIPTC ) iptc.ParseMemoryDataSets ( iptcInfo.dataPtr, iptcInfo.dataLen );
+ if ( iptcDigestState == kDigestMatches ) iptcDigestState = kDigestMissing;
+ ImportPhotoData ( exif, iptc, psir, iptcDigestState, &this->xmpObj, options );
throw; // ! Rethrow the exception, don't absorb it.
}
}
@@ -661,7 +655,7 @@ void JPEG_MetaHandler::ProcessXMP()
// Process the extended XMP if it has a matching GUID.
if ( ! this->extendedXMP.empty() ) {
-
+
bool found;
GUID_32 g32;
std::string extGUID, extPacket;
@@ -678,7 +672,7 @@ void JPEG_MetaHandler::ProcessXMP()
((guidPos != this->extendedXMP.end()) ? "Found" : "Missing"), extGUID.c_str() );
#endif
}
-
+
if ( guidPos != this->extendedXMP.end() ) {
try {
XMP_StringPtr extStr = guidPos->second.c_str();
@@ -691,12 +685,16 @@ void JPEG_MetaHandler::ProcessXMP()
}
}
-
+
// Process the legacy metadata.
- ImportJTPtoXMP ( kXMP_JPEGFile, lastLegacy, &exif, psir, &iptc, &this->xmpObj, options );
- if ( haveExif | haveIPTC ) this->containsXMP = true; // Assume we had something for the XMP.
-
+ if ( haveIPTC && (! haveXMP) && (iptcDigestState == kDigestMatches) ) iptcDigestState = kDigestMissing;
+ bool parseIPTC = (iptcDigestState != kDigestMatches) || (! readOnly);
+ if ( parseIPTC ) iptc.ParseMemoryDataSets ( iptcInfo.dataPtr, iptcInfo.dataLen );
+ ImportPhotoData ( exif, iptc, psir, iptcDigestState, &this->xmpObj, options );
+
+ this->containsXMP = true; // Assume we had something for the XMP.
+
} // JPEG_MetaHandler::ProcessXMP
// =================================================================================================
@@ -706,25 +704,38 @@ void JPEG_MetaHandler::ProcessXMP()
void JPEG_MetaHandler::UpdateFile ( bool doSafeUpdate )
{
XMP_Assert ( ! doSafeUpdate ); // This should only be called for "unsafe" updates.
-
- // Decide whether to do an in-place update. This can only happen if all of the following are true:
- // - There is a standard packet in the file.
- // - There is no extended XMP in the file.
- // - The are no changes to the legacy Exif or PSIR portions. (The IPTC is in the PSIR.)
- // - The new XMP can fit in the old space, without extensions.
- ExportXMPtoJTP ( kXMP_JPEGFile, &this->xmpObj, this->exifMgr, this->psirMgr, this->iptcMgr );
-
XMP_Int64 oldPacketOffset = this->packetInfo.offset;
XMP_Int32 oldPacketLength = this->packetInfo.length;
-
+
if ( oldPacketOffset == kXMPFiles_UnknownOffset ) oldPacketOffset = 0; // ! Simplify checks.
if ( oldPacketLength == kXMPFiles_UnknownLength ) oldPacketLength = 0;
+
+ bool fileHadXMP = ((oldPacketOffset != 0) && (oldPacketLength != 0));
+
+ // Update the IPTC-IIM and native TIFF/Exif metadata. ExportPhotoData also trips the tiff: and
+ // exif: copies from the XMP, so reserialize the now final XMP packet.
- bool doInPlace = (this->xmpPacket.size() <= (size_t)this->packetInfo.length);
+ ExportPhotoData ( kXMP_JPEGFile, &this->xmpObj, this->exifMgr, this->iptcMgr, this->psirMgr );
+ try {
+ XMP_OptionBits options = kXMP_UseCompactFormat;
+ if ( fileHadXMP ) options |= kXMP_ExactPacketLength;
+ this->xmpObj.SerializeToBuffer ( &this->xmpPacket, options, oldPacketLength );
+ } catch ( ... ) {
+ this->xmpObj.SerializeToBuffer ( &this->xmpPacket, kXMP_UseCompactFormat );
+ }
+
+ // Decide whether to do an in-place update. This can only happen if all of the following are true:
+ // - There is a standard packet in the file.
+ // - There is no extended XMP in the file.
+ // - The are no changes to the legacy Exif or PSIR portions. (The IPTC is in the PSIR.)
+ // - The new XMP can fit in the old space, without extensions.
+
+ bool doInPlace = (fileHadXMP && (this->xmpPacket.size() <= (size_t)oldPacketLength));
+
if ( ! this->extendedXMP.empty() ) doInPlace = false;
-
+
if ( (this->exifMgr != 0) && (this->exifMgr->IsLegacyChanged()) ) doInPlace = false;
if ( (this->psirMgr != 0) && (this->psirMgr->IsLegacyChanged()) ) doInPlace = false;
@@ -733,39 +744,39 @@ void JPEG_MetaHandler::UpdateFile ( bool doSafeUpdate )
#if GatherPerformanceData
sAPIPerf->back().extraInfo += ", JPEG in-place update";
#endif
-
+
if ( this->xmpPacket.size() < (size_t)this->packetInfo.length ) {
// They ought to match, cheap to be sure.
size_t extraSpace = (size_t)this->packetInfo.length - this->xmpPacket.size();
this->xmpPacket.append ( extraSpace, ' ' );
}
-
+
LFA_FileRef liveFile = this->parent->fileRef;
std::string & newPacket = this->xmpPacket;
-
+
XMP_Assert ( newPacket.size() == (size_t)oldPacketLength ); // ! Done by common PutXMP logic.
-
+
LFA_Seek ( liveFile, oldPacketOffset, SEEK_SET );
LFA_Write ( liveFile, newPacket.c_str(), (XMP_Int32)newPacket.size() );
-
+
} else {
#if GatherPerformanceData
sAPIPerf->back().extraInfo += ", JPEG copy update";
#endif
-
+
std::string origPath = this->parent->filePath;
LFA_FileRef origRef = this->parent->fileRef;
-
+
std::string updatePath;
LFA_FileRef updateRef = 0;
-
+
CreateTempFile ( origPath, &updatePath, kCopyMacRsrc );
updateRef = LFA_Open ( updatePath.c_str(), 'w' );
-
+
this->parent->filePath = updatePath;
this->parent->fileRef = updateRef;
-
+
try {
XMP_Assert ( ! this->skipReconcile );
this->skipReconcile = true;
@@ -774,21 +785,22 @@ void JPEG_MetaHandler::UpdateFile ( bool doSafeUpdate )
} catch ( ... ) {
this->skipReconcile = false;
LFA_Close ( updateRef );
+ LFA_Delete ( updatePath.c_str() );
this->parent->filePath = origPath;
this->parent->fileRef = origRef;
throw;
}
-
+
LFA_Close ( origRef );
LFA_Delete ( origPath.c_str() );
-
+
LFA_Close ( updateRef );
LFA_Rename ( updatePath.c_str(), origPath.c_str() );
this->parent->filePath = origPath;
this->parent->fileRef = 0;
}
-
+
this->needsUpdate = false;
} // JPEG_MetaHandler::UpdateFile
@@ -809,7 +821,7 @@ void JPEG_MetaHandler::UpdateFile ( bool doSafeUpdate )
void JPEG_MetaHandler::WriteFile ( LFA_FileRef sourceRef, const std::string & sourcePath )
{
LFA_FileRef destRef = this->parent->fileRef;
-
+
XMP_AbortProc abortProc = this->parent->abortProc;
void * abortArg = this->parent->abortArg;
const bool checkAbort = (abortProc != 0);
@@ -818,7 +830,7 @@ void JPEG_MetaHandler::WriteFile ( LFA_FileRef sourceRef, const std::string & so
size_t segLen; // ! Must be a size to hold at least 64k+2.
IOBuffer ioBuf;
XMP_Uns32 first4;
-
+
XMP_Assert ( kIOBufferSize >= (2 + 64*1024) ); // Enough for a marker plus maximum contents.
if ( LFA_Measure ( sourceRef ) == 0 ) return; // Tolerate empty files.
@@ -826,27 +838,29 @@ void JPEG_MetaHandler::WriteFile ( LFA_FileRef sourceRef, const std::string & so
LFA_Truncate (destRef, 0 );
if ( ! skipReconcile ) {
- ExportXMPtoJTP ( kXMP_JPEGFile, &this->xmpObj, this->exifMgr, this->psirMgr, this->iptcMgr );
+ // Update the IPTC-IIM and native TIFF/Exif metadata, and reserialize the now final XMP packet.
+ ExportPhotoData ( kXMP_JPEGFile, &this->xmpObj, this->exifMgr, this->iptcMgr, this->psirMgr );
+ this->xmpObj.SerializeToBuffer ( &this->xmpPacket, kXMP_UseCompactFormat );
}
-
+
RefillBuffer ( sourceRef, &ioBuf );
if ( ! CheckFileSpace ( sourceRef, &ioBuf, 4 ) ) {
XMP_Throw ( "JPEG must have at least SOI and EOI markers", kXMPErr_BadJPEG );
}
-
+
marker = GetUns16BE ( ioBuf.ptr );
if ( marker != 0xFFD8 ) XMP_Throw ( "Missing SOI marker", kXMPErr_BadJPEG );
LFA_Write ( destRef, ioBuf.ptr, 2 );
ioBuf.ptr += 2;
-
+
// Copy the leading APP0 marker segments.
-
+
while ( true ) {
if ( checkAbort && abortProc(abortArg) ) {
XMP_Throw ( "JPEG_MetaHandler::WriteFile - User abort", kXMPErr_UserAbort );
}
-
+
if ( ! CheckFileSpace ( sourceRef, &ioBuf, 2 ) ) XMP_Throw ( "Unexpected end to JPEG", kXMPErr_BadJPEG );
marker = GetUns16BE ( ioBuf.ptr );
if ( marker == 0xFFFF ) {
@@ -856,49 +870,49 @@ void JPEG_MetaHandler::WriteFile ( LFA_FileRef sourceRef, const std::string & so
}
if ( marker != 0xFFE0 ) break;
-
+
if ( ! CheckFileSpace ( sourceRef, &ioBuf, 4 ) ) XMP_Throw ( "Unexpected end to JPEG", kXMPErr_BadJPEG );
segLen = GetUns16BE ( ioBuf.ptr+2 );
segLen += 2; // ! Don't do above in case machine does 16 bit "+".
-
+
if ( ! CheckFileSpace ( sourceRef, &ioBuf, segLen ) ) XMP_Throw ( "Unexpected end to JPEG", kXMPErr_BadJPEG );
LFA_Write ( destRef, ioBuf.ptr, (XMP_Int32)segLen );
ioBuf.ptr += segLen;
-
+
}
// Write the new Exif APP1 marker segment.
-
+
if ( this->exifMgr != 0 ) {
void* exifPtr;
XMP_Uns32 exifLen = this->exifMgr->UpdateMemoryStream ( &exifPtr );
- if ( exifLen > kExifMaxDataLength ) exifLen = this->exifMgr->UpdateMemoryStream ( &exifPtr, true );
+ if ( exifLen > kExifMaxDataLength ) exifLen = this->exifMgr->UpdateMemoryStream ( &exifPtr, true /* compact */ );
if ( exifLen > kExifMaxDataLength ) XMP_Throw ( "Overflow of Exif APP1 data", kXMPErr_BadJPEG );
-
+
if ( exifLen > 0 ) {
first4 = MakeUns32BE ( 0xFFE10000 + 2 + kExifSignatureLength + exifLen );
LFA_Write ( destRef, &first4, 4 );
LFA_Write ( destRef, kExifSignatureString, kExifSignatureLength );
LFA_Write ( destRef, exifPtr, exifLen );
}
-
+
}
// Write the new XMP APP1 marker segment, with possible extension marker segments.
-
+
std::string mainXMP, extXMP, extDigest;
SXMPUtils::PackageForJPEG ( this->xmpObj, &mainXMP, &extXMP, &extDigest );
XMP_Assert ( (extXMP.size() == 0) || (extDigest.size() == 32) );
-
+
first4 = MakeUns32BE ( 0xFFE10000 + 2 + kMainXMPSignatureLength + (XMP_Uns32)mainXMP.size() );
LFA_Write ( destRef, &first4, 4 );
LFA_Write ( destRef, kMainXMPSignatureString, kMainXMPSignatureLength );
LFA_Write ( destRef, mainXMP.c_str(), (XMP_Int32)mainXMP.size() );
-
+
size_t extPos = 0;
size_t extLen = extXMP.size();
-
+
while ( extLen > 0 ) {
size_t partLen = extLen;
@@ -906,7 +920,7 @@ void JPEG_MetaHandler::WriteFile ( LFA_FileRef sourceRef, const std::string & so
first4 = MakeUns32BE ( 0xFFE10000 + 2 + kExtXMPPrefixLength + (XMP_Uns32)partLen );
LFA_Write ( destRef, &first4, 4 );
-
+
LFA_Write ( destRef, kExtXMPSignatureString, kExtXMPSignatureLength );
LFA_Write ( destRef, extDigest.c_str(), (XMP_Int32)extDigest.size() );
@@ -914,39 +928,39 @@ void JPEG_MetaHandler::WriteFile ( LFA_FileRef sourceRef, const std::string & so
LFA_Write ( destRef, &first4, 4 );
first4 = MakeUns32BE ( (XMP_Int32)extPos );
LFA_Write ( destRef, &first4, 4 );
-
+
LFA_Write ( destRef, &extXMP[extPos], (XMP_Int32)partLen );
-
+
extPos += partLen;
extLen -= partLen;
-
+
}
// Write the new PSIR APP13 marker segment.
-
+
if ( this->psirMgr != 0 ) {
void* psirPtr;
XMP_Uns32 psirLen = this->psirMgr->UpdateMemoryResources ( &psirPtr );
if ( psirLen > kPSIRMaxDataLength ) XMP_Throw ( "Overflow of PSIR APP13 data", kXMPErr_BadJPEG );
-
+
if ( psirLen > 0 ) {
first4 = MakeUns32BE ( 0xFFED0000 + 2 + kPSIRSignatureLength + psirLen );
LFA_Write ( destRef, &first4, 4 );
LFA_Write ( destRef, kPSIRSignatureString, kPSIRSignatureLength );
LFA_Write ( destRef, psirPtr, psirLen );
}
-
+
}
-
- // Copy remaining marker segments, skipping old metadata, to the first SOFn marker.
-
+
+ // Copy remaining marker segments, skipping old metadata, to the first SOS marker or to EOI.
+
while ( true ) {
if ( checkAbort && abortProc(abortArg) ) {
XMP_Throw ( "JPEG_MetaHandler::WriteFile - User abort", kXMPErr_UserAbort );
}
-
+
if ( ! CheckFileSpace ( sourceRef, &ioBuf, 2 ) ) XMP_Throw ( "Unexpected end to JPEG", kXMPErr_BadJPEG );
marker = GetUns16BE ( ioBuf.ptr );
if ( marker == 0xFFFF ) {
@@ -954,17 +968,22 @@ void JPEG_MetaHandler::WriteFile ( LFA_FileRef sourceRef, const std::string & so
++ioBuf.ptr;
continue;
}
+
+ if ( (marker == 0xFFDA) || (marker == 0xFFD9) ) break; // Quit at the first SOS marker or at EOI.
- if ( ! TableOrDataMarker ( marker ) ) break;
+ if ( (marker == 0xFF01) || // Ill-formed file if we encounter a TEM or RSTn marker.
+ ((0xFFD0 <= marker) && (marker <= 0xFFD7)) ) {
+ XMP_Throw ( "Unexpected TEM or RSTn marker", kXMPErr_BadJPEG );
+ }
if ( ! CheckFileSpace ( sourceRef, &ioBuf, 4 ) ) XMP_Throw ( "Unexpected end to JPEG", kXMPErr_BadJPEG );
segLen = GetUns16BE ( ioBuf.ptr+2 );
-
+
if ( ! CheckFileSpace ( sourceRef, &ioBuf, 2+segLen ) ) XMP_Throw ( "Unexpected end to JPEG", kXMPErr_BadJPEG );
bool copySegment = true;
XMP_Uns8* signaturePtr = ioBuf.ptr + 4;
-
+
if ( marker == 0xFFED ) {
if ( (segLen >= kPSIRSignatureLength) &&
CheckBytes ( signaturePtr, kPSIRSignatureString, kPSIRSignatureLength ) ) {
@@ -987,22 +1006,22 @@ void JPEG_MetaHandler::WriteFile ( LFA_FileRef sourceRef, const std::string & so
if ( copySegment ) LFA_Write ( destRef, ioBuf.ptr, (XMP_Int32)(2+segLen) );
ioBuf.ptr += 2+segLen;
-
+
}
-
+
// Copy the remainder of the source file.
-
+
size_t bufTail = ioBuf.len - (ioBuf.ptr - &ioBuf.data[0]);
LFA_Write ( destRef, ioBuf.ptr, (XMP_Int32)bufTail );
ioBuf.ptr += bufTail;
-
+
while ( true ) {
RefillBuffer ( sourceRef, &ioBuf );
if ( ioBuf.len == 0 ) break;
LFA_Write ( destRef, ioBuf.ptr, (XMP_Int32)ioBuf.len );
ioBuf.ptr += ioBuf.len;
}
-
+
this->needsUpdate = false;
} // JPEG_MetaHandler::WriteFile
diff --git a/source/XMPFiles/FileHandlers/JPEG_Handler.hpp b/source/XMPFiles/FileHandlers/JPEG_Handler.hpp
index 8a5c0f1..650a5b5 100644
--- a/source/XMPFiles/FileHandlers/JPEG_Handler.hpp
+++ b/source/XMPFiles/FileHandlers/JPEG_Handler.hpp
@@ -3,7 +3,7 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2007 Adobe Systems Incorporated
+// Copyright 2004 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -38,7 +38,6 @@ static const XMP_OptionBits kJPEG_HandlerFlags = (kXMPFiles_CanInjectXMP |
kXMPFiles_CanReconcile |
kXMPFiles_AllowsOnlyXMP |
kXMPFiles_ReturnsRawPacket |
- kXMPFiles_ReturnsTNail |
kXMPFiles_AllowsSafeUpdate);
class JPEG_MetaHandler : public XMPFileHandler
@@ -46,7 +45,6 @@ class JPEG_MetaHandler : public XMPFileHandler
public:
void CacheFileData();
- void ProcessTNail();
void ProcessXMP();
void UpdateFile ( bool doSafeUpdate );
diff --git a/source/XMPFiles/FileHandlers/MOV_Handler.cpp b/source/XMPFiles/FileHandlers/MOV_Handler.cpp
deleted file mode 100644
index e2ea26c..0000000
--- a/source/XMPFiles/FileHandlers/MOV_Handler.cpp
+++ /dev/null
@@ -1,996 +0,0 @@
-// =================================================================================================
-// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2008 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 "XMP_Environment.h" // ! This must be the first include.
-#if ! ( XMP_64 || XMP_UNIXBuild) // Closes at very bottom.
-
-#if XMP_WinBuild
- #pragma warning ( disable : 4996 ) // '...' was declared deprecated
-#endif
-
-#include "MOV_Handler.hpp"
-#include "QuickTime_Support.hpp"
-
-#include "QuickTimeComponents.h"
-
-#if XMP_WinBuild
- #include "QTML.h"
- #include "Movies.h"
-#endif
-
-#if XMP_MacBuild
- #include <Movies.h>
-#endif
-
-using namespace std;
-
-static OSType kXMPUserDataType = 'XMP_';
-static long kXMPUserDataTypeIndex = 1;
-
-static bool CreatorAtom_SetProperties ( SXMPMeta& xmpObj,
- const MOV_MetaHandler::CreatorAtomStrings& creatorAtomStrings );
-
-static bool CreatorAtom_Update ( SXMPMeta& xmpObj, UserData& movieUserData );
-
-static bool CreatorAtom_ReadStrings ( MOV_MetaHandler::CreatorAtomStrings& creatorAtomStrings,
- UserData& movieUserData );
-
-// =================================================================================================
-/// \file MOV_Handler.cpp
-/// \brief File format handler for MOV.
-///
-/// This header ...
-///
-// =================================================================================================
-
-// =================================================================================================
-// MOV_MetaHandlerCTor
-// ===================
-
-XMPFileHandler * MOV_MetaHandlerCTor ( XMPFiles * parent )
-{
- return new MOV_MetaHandler ( parent );
-
-} // MOV_MetaHandlerCTor
-
-// =================================================================================================
-// MOV_CheckFormat
-// ===============
-
-bool MOV_CheckFormat ( XMP_FileFormat format,
- XMP_StringPtr filePath,
- LFA_FileRef fileRef,
- XMPFiles * parent )
-{
- IgnoreParam(format); IgnoreParam(fileRef);
-
- XMP_Assert ( format == kXMP_MOVFile );
- XMP_Assert ( fileRef == 0 );
-
- bool inBackground = XMP_OptionIsSet ( parent->openFlags, kXMPFiles_OpenInBackground );
-
- if ( parent->format != kXMP_MOVFile ) return false; // Check the first call hint, QT is promiscuous.
- if ( ! QuickTime_Support::sMainInitOK ) return false;
-
- if ( inBackground ) {
- if ( ! QuickTime_Support::ThreadInitialize() ) return false;
- }
-
- bool isMov = false;
- OSErr err = noErr;
-
- Handle qtDataRef = 0;
- OSType qtRefType;
- Movie tempMovie = 0;
- short flags;
-
- CFStringRef cfsPath = CFStringCreateWithCString ( 0, filePath, kCFStringEncodingUTF8 );
- if ( cfsPath == 0 ) return false; // ? Throw?
-
- err = QTNewDataReferenceFromFullPathCFString ( cfsPath, kQTNativeDefaultPathStyle, 0, &qtDataRef, &qtRefType );
- if ( err != noErr ) goto EXIT;
-
- flags = newMovieDontResolveDataRefs | newMovieDontAskUnresolvedDataRefs;
- err = NewMovieFromDataRef ( &tempMovie, flags, 0, qtDataRef, qtRefType );
- if ( err != noErr ) goto EXIT;
-
- isMov = true;
-
-EXIT:
-
- if ( tempMovie != 0 ) DisposeMovie ( tempMovie );
- if ( qtDataRef != 0 ) DisposeHandle ( qtDataRef );
- if ( cfsPath != 0 ) CFRelease ( cfsPath );
-
- if ( inBackground && (! isMov) ) QuickTime_Support::ThreadTerminate();
- return isMov;
-
-} // MOV_CheckFormat
-
-// =================================================================================================
-// MOV_MetaHandler::MOV_MetaHandler
-// ================================
-
-MOV_MetaHandler::MOV_MetaHandler ( XMPFiles * _parent )
- : mQTInit(false), mMovieDataRef(0), mMovieDataHandler(0), mMovie(0), mMovieResourceID(0), mFilePermission(0)
-{
-
- this->parent = _parent;
- this->handlerFlags = kMOV_HandlerFlags;
- this->stdCharForm = kXMP_Char8Bit;
-
-} // MOV_MetaHandler::MOV_MetaHandler
-
-// =================================================================================================
-// MOV_MetaHandler::~MOV_MetaHandler
-// =================================
-
-MOV_MetaHandler::~MOV_MetaHandler()
-{
- bool inBackground = XMP_OptionIsSet ( this->parent->openFlags, kXMPFiles_OpenInBackground );
-
- this->CloseMovie();
- if ( inBackground ) QuickTime_Support::ThreadTerminate();
-
-} // MOV_MetaHandler::~MOV_MetaHandler
-
-// =================================================================================================
-// MOV_MetaHandler::ProcessXMP
-// ===========================
-
-void MOV_MetaHandler::ProcessXMP()
-{
- if ( (!this->containsXMP) || this->processedXMP ) return;
-
- if ( this->handlerFlags & kXMPFiles_CanReconcile ) {
- XMP_Throw ( "Reconciling file handlers must implement ProcessXMP", kXMPErr_InternalFailure );
- }
-
- SXMPUtils::RemoveProperties ( &this->xmpObj, 0, 0, kXMPUtil_DoAllProperties );
- this->xmpObj.ParseFromBuffer ( this->xmpPacket.c_str(), (XMP_StringLen)this->xmpPacket.size() );
- this->processedXMP = true;
-
- CreatorAtom_SetProperties ( this->xmpObj, mCreatorAtomStrings );
-
-}
-
-// =================================================================================================
-// MOV_MetaHandler::UpdateFile
-// ===========================
-
-void MOV_MetaHandler::UpdateFile ( bool doSafeUpdate )
-{
- if ( ! this->needsUpdate ) return;
- if ( doSafeUpdate ) XMP_Throw ( "MOV_MetaHandler::UpdateFile: Safe update not supported", kXMPErr_Unavailable );
-
- XMP_StringPtr packetStr = this->xmpPacket.c_str();
- XMP_StringLen packetLen = (XMP_StringLen)this->xmpPacket.size();
-
- if ( packetLen == 0 ) return; // Bail if no XMP packet
-
- if ( this->OpenMovie ( (kDataHCanRead | kDataHCanWrite) ) ) {
-
- UserData movieUserData ( GetMovieUserData ( this->mMovie ) );
- if ( movieUserData != 0 ) {
-
- OSErr err;
-
- // Remove previous versions
- err = GetUserData ( movieUserData, 0, kXMPUserDataType, kXMPUserDataTypeIndex );
- if ( err == noErr ) {
- RemoveUserData(movieUserData, kXMPUserDataType, kXMPUserDataTypeIndex);
- }
-
- // Add the new one
- Handle XMPdata ( NewHandle(packetLen) );
- if ( XMPdata != 0 ) {
- HLock ( XMPdata );
- strncpy ( *XMPdata, packetStr, packetLen ); // AUDIT: Handle created above using packetLen.
- HUnlock ( XMPdata );
- err = AddUserData ( movieUserData, XMPdata, kXMPUserDataType );
- DisposeHandle ( XMPdata );
- }
-
- CreatorAtom_Update ( this->xmpObj, movieUserData );
-
- }
-
- }
-
- this->needsUpdate = false;
-
-} // MOV_MetaHandler::UpdateFile
-
-// =================================================================================================
-// MOV_MetaHandler::WriteFile
-// ==========================
-
-void MOV_MetaHandler::WriteFile ( LFA_FileRef sourceRef,
- const std::string & sourcePath )
-{
- IgnoreParam(sourceRef); IgnoreParam(sourcePath);
-
- XMP_Throw ( "MOV_MetaHandler::WriteFile: Not supported", kXMPErr_Unavailable );
-
-} // MOV_MetaHandler::WriteFile
-
-// =================================================================================================
-// MOV_MetaHandler::OpenMovie
-// ==========================
-
-bool MOV_MetaHandler::OpenMovie ( long inPermission )
-{
- // If the file is already opened with the correct permission, bail
- if ( (inPermission == this->mFilePermission) && (this->mMovie != 0) ) return true;
-
- // If already open, close to open with new permissions
- if ( (this->mMovieDataRef != 0) || (this->mMovieDataHandler != 0) || (this->mMovie != 0) ) this->CloseMovie();
- XMP_Assert ( (this->mMovieDataRef == 0) && (this->mMovieDataHandler == 0) && (this->mMovie == 0) );
-
- OSErr err = noErr;
- OSType qtRefType;
- short flags;
-
- CFStringRef cfsPath = CFStringCreateWithCString ( 0, this->parent->filePath.c_str(), kCFStringEncodingUTF8 );
- if ( cfsPath == 0 ) return false; // ? Throw?
-
- err = QTNewDataReferenceFromFullPathCFString ( cfsPath, kQTNativeDefaultPathStyle, 0,
- &this->mMovieDataRef, &qtRefType );
- if ( err != noErr ) goto FAILURE;
-
- this->mFilePermission = inPermission;
- err = OpenMovieStorage ( this->mMovieDataRef, qtRefType, this->mFilePermission, &this->mMovieDataHandler );
- if ( err != noErr ) goto FAILURE;
-
- flags = newMovieDontAskUnresolvedDataRefs;
- this->mMovieResourceID = 0; // *** Is this the right input value?
- err = NewMovieFromDataRef ( &this->mMovie, flags, &this->mMovieResourceID, this->mMovieDataRef, qtRefType );
- if ( err != noErr ) goto FAILURE;
-
- if ( cfsPath != 0 ) CFRelease ( cfsPath );
- return true;
-
-FAILURE:
-
- if ( this->mMovie != 0 ) DisposeMovie ( this->mMovie );
- if ( this->mMovieDataHandler != 0 ) CloseMovieStorage ( this->mMovieDataHandler );
- if ( this->mMovieDataRef != 0 ) DisposeHandle ( this->mMovieDataRef );
-
- this->mMovie = 0;
- this->mMovieDataHandler = 0;
- this->mMovieDataRef = 0;
-
- if ( cfsPath != 0 ) CFRelease ( cfsPath );
- return false;
-
-} // MOV_MetaHandler::OpenMovie
-
-// =================================================================================================
-// MOV_MetaHandler::CloseMovie
-// ===========================
-
-void MOV_MetaHandler::CloseMovie()
-{
-
- if ( this->mMovie != 0 ) {
- if ( HasMovieChanged ( this->mMovie ) ) { // If the movie has been modified, write it to disk.
- (void) UpdateMovieInStorage ( this->mMovie, this->mMovieDataHandler );
- }
- DisposeMovie ( this->mMovie );
- }
-
- if ( this->mMovieDataHandler != 0 ) CloseMovieStorage ( this->mMovieDataHandler );
- if ( this->mMovieDataRef != 0 ) DisposeHandle ( this->mMovieDataRef );
-
- this->mMovie = 0;
- this->mMovieDataHandler = 0;
- this->mMovieDataRef = 0;
-
-} // MOV_MetaHandler::CloseMovie
-
-// =================================================================================================
-// GetAtomInfo
-// ===========
-
-struct AtomInfo {
- XMP_Int64 atomSize;
- XMP_Uns32 atomType;
- bool hasLargeSize;
-};
-
-enum { // ! Do not rearrange, code depends on this order.
- kBadQT_NoError = 0, // No errors.
- kBadQT_SmallInner = 1, // An extra 1..7 bytes at the end of an inner span.
- kBadQT_LargeInner = 2, // More serious inner garbage, found as invalid atom length.
- kBadQT_SmallOuter = 3, // An extra 1..7 bytes at the end of the file.
- kBadQT_LargeOuter = 4 // More serious EOF garbage, found as invalid atom length.
-};
-typedef XMP_Uns8 QTErrorMode;
-
-static QTErrorMode GetAtomInfo ( const LFA_FileRef qtFile, XMP_Int64 spanSize, int nesting, AtomInfo * info )
-{
- QTErrorMode status = kBadQT_NoError;
- XMP_Uns8 buffer [8];
-
- info->hasLargeSize = false;
-
- LFA_Read ( qtFile, buffer, 8, kLFA_RequireAll ); // Will throw if 8 bytes aren't available.
- info->atomSize = GetUns32BE ( &buffer[0] ); // ! Yes, the initial size is big endian UInt32.
- info->atomType = GetUns32BE ( &buffer[4] );
-
- if ( info->atomSize == 0 ) { // Does the atom extend to EOF?
-
- if ( nesting != 0 ) return kBadQT_LargeInner;
- info->atomSize = spanSize; // This outer atom goes to EOF.
-
- } else if ( info->atomSize == 1 ) { // Does the atom have a 64-bit size?
-
- if ( spanSize < 16 ) { // Is there room in the span for the 16 byte header?
- status = kBadQT_LargeInner;
- if ( nesting == 0 ) status += 2; // Convert to "outer".
- return status;
- }
-
- LFA_Read ( qtFile, buffer, 8, kLFA_RequireAll );
- info->atomSize = (XMP_Int64) GetUns64BE ( &buffer[0] );
- info->hasLargeSize = true;
-
- }
-
- XMP_Assert ( status == kBadQT_NoError );
- return status;
-
-} // GetAtomInfo
-
-// =================================================================================================
-// CheckAtomList
-// =============
-//
-// Check that a sequence of atoms fills a given span. The I/O position must be at the start of the
-// span, it is left just past the span on success. Recursive checks are done for top level 'moov'
-// atoms, and second level 'udta' atoms ('udta' inside 'moov').
-//
-// Checking continues for "small inner" errors. They will be reported if no other kinds of errors
-// are found, otherwise the other error is reported. Checking is immediately aborted for any "large"
-// error. The rationale is that QuickTime can apparently handle small inner errors. They might be
-// arise from updates that shorten an atom by less than 8 bytes. Larger shrinkage should introduce a
-// 'free' atom.
-
-static QTErrorMode CheckAtomList ( const LFA_FileRef qtFile, XMP_Int64 spanSize, int nesting )
-{
- QTErrorMode status = kBadQT_NoError;
- AtomInfo info;
-
- const static XMP_Uns32 moovAtomType = 0x6D6F6F76; // ! Don't use MakeUns32BE, already big endian.
- const static XMP_Uns32 udtaAtomType = 0x75647461;
-
- for ( ; spanSize >= 8; spanSize -= info.atomSize ) {
-
- QTErrorMode atomStatus = GetAtomInfo ( qtFile, spanSize, nesting, &info );
- if ( atomStatus != kBadQT_NoError ) return atomStatus;
-
- XMP_Int64 headerSize = 8;
- if ( info.hasLargeSize ) headerSize = 16;
-
- if ( (info.atomSize < headerSize) || (info.atomSize > spanSize) ) {
- status = kBadQT_LargeInner;
- if ( nesting == 0 ) status += 2; // Convert to "outer".
- return status;
- }
-
- bool doChildren = false;
- if ( (nesting == 0) && (info.atomType == moovAtomType) ) doChildren = true;
- if ( (nesting == 1) && (info.atomType == udtaAtomType) ) doChildren = true;
-
- XMP_Int64 dataSize = info.atomSize - headerSize;
-
- if ( ! doChildren ) {
- LFA_Seek ( qtFile, dataSize, SEEK_CUR );
- } else {
- QTErrorMode innerStatus = CheckAtomList ( qtFile, dataSize, nesting+1 );
- if ( innerStatus > kBadQT_SmallInner ) return innerStatus; // Quit for serious errors.
- if ( status == kBadQT_NoError ) status = innerStatus; // Remember small inner errors.
- }
-
- }
-
- XMP_Assert ( status <= kBadQT_SmallInner ); // Else already returned.
- // ! Make sure inner kBadQT_SmallInner is propagated if this span is OK.
-
- if ( spanSize != 0 ) {
- LFA_Seek ( qtFile, spanSize, SEEK_CUR ); // ! Skip the trailing garbage of this span.
- status = kBadQT_SmallInner;
- if ( spanSize >= 8 ) status = kBadQT_LargeInner;
- if ( nesting == 0 ) status += 2; // Convert to "outer".
- }
-
- return status;
-
-} // CheckAtomList
-
-// =================================================================================================
-// AttemptFileRepair
-// =================
-
-static void AttemptFileRepair ( LFA_FileRef qtFile, XMP_Int64 fileSpace, QTErrorMode status )
-{
-
- switch ( status ) {
- case kBadQT_NoError : return; // Sanity check.
- case kBadQT_SmallInner : return; // Ignore these, QT seems to be able to handle them.
- case kBadQT_LargeInner : XMP_Throw ( "Can't repair QuickTime file", kXMPErr_BadFileFormat );
- case kBadQT_SmallOuter : break; // Truncate file below.
- case kBadQT_LargeOuter : break; // Truncate file below.
- default : XMP_Throw ( "Invalid QuickTime error mode", kXMPErr_InternalFailure );
- }
-
- AtomInfo info;
- XMP_Int64 headerSize;
-
- // Process the top level atoms until an error is found.
-
- LFA_Seek ( qtFile, 0, SEEK_SET );
-
- for ( ; fileSpace >= 8; fileSpace -= info.atomSize ) {
-
- QTErrorMode atomStatus = GetAtomInfo ( qtFile, fileSpace, 0, &info );
-
- headerSize = 8; // ! Set this before checking atomStatus, used after the loop.
- if ( info.hasLargeSize ) headerSize = 16;
-
- if ( atomStatus != kBadQT_NoError ) break;
- if ( (info.atomSize < headerSize) || (info.atomSize > fileSpace) ) break;
-
- XMP_Int64 dataSize = info.atomSize - headerSize;
- LFA_Seek ( qtFile, dataSize, SEEK_CUR );
-
- }
-
- // Truncate the file. If fileSpace >= 8 then the loop exited early due to a bad atom, seek back
- // to the atom's start. Otherwise, the loop exited because no mmore atoms are possible, no seek.
-
- if ( fileSpace >= 8 ) LFA_Seek ( qtFile, -headerSize, SEEK_CUR );
- XMP_Int64 currPos = LFA_Tell ( qtFile );
- LFA_Truncate ( qtFile, currPos );
-
-} // AttemptFileRepair
-
-// =================================================================================================
-// CheckFileStructure
-// ==================
-
-static void CheckFileStructure ( XMPFileHandler * thiz, bool doRepair )
-{
- XMPFiles * parent = thiz->parent;
-
- // Open the disk file so we can look inside and maybe repair.
-
- AutoFile localFile; // ! Don't use parent->fileRef keep this usage private.
- XMP_Assert ( parent->fileRef == 0 ); // The file should not be open yet.
-
- char openMode = 'r';
- if ( doRepair ) openMode = 'w';
- localFile.fileRef = LFA_Open ( parent->filePath.c_str(), openMode );
- if ( localFile.fileRef == 0 ) XMP_Throw ( "Can't open QuickTime file for update checks", kXMPErr_ExternalFailure );
- XMP_Int64 fileSize = LFA_Measure ( localFile.fileRef );
-
- // Check the basic file structure and try to repair if asked.
-
- QTErrorMode status = CheckAtomList ( localFile.fileRef, fileSize, 0 );
-
- if ( status != kBadQT_NoError ) {
- if ( doRepair ) {
- AttemptFileRepair ( localFile.fileRef, fileSize, status ); // Will throw if the attempt fails.
- } else if ( status != kBadQT_SmallInner ) {
- XMP_Throw ( "Ill-formed QuickTime file", kXMPErr_BadFileFormat );
- } else {
- return; // ! Ignore these, QT seems to be able to handle them.
- // *** Might want to throw for check-only, ignore when repairing.
- }
- }
-
-} // CheckFileStructure;
-
-
-// =================================================================================================
-// MOV_MetaHandler::CacheFileData
-// ==============================
-
-void MOV_MetaHandler::CacheFileData()
-{
-
- // Pre-check files opened for update. We've found bugs in Apple's QT code that make slightly
- // ill-formed files unreadable.
-
- XMPFiles * parent = this->parent;
-
- const bool isUpdate = XMP_OptionIsSet ( parent->openFlags, kXMPFiles_OpenForUpdate );
- const bool doRepair = XMP_OptionIsSet ( parent->openFlags, kXMPFiles_OpenRepairFile );
-
- if ( isUpdate ) {
- CheckFileStructure ( this, doRepair ); // Will throw for failure.
- }
-
- // Continue with the usual caching of the file's metadata.
-
- this->containsXMP = false;
-
- if ( this->OpenMovie ( kDataHCanRead ) ) {
-
- UserData movieUserData ( GetMovieUserData ( this->mMovie ) );
- if ( movieUserData != 0 ) {
-
- Handle XMPdataHandle ( NewHandle(0) );
- if ( XMPdataHandle != 0 ) {
-
- OSErr err = GetUserData ( movieUserData, XMPdataHandle, kXMPUserDataType, kXMPUserDataTypeIndex );
- if (err != noErr) { // userDataItemNotFound = -2026
-
- packetInfo.writeable = true; // If no packet found, created packets will be writeable
-
- } else {
-
- // Convert handles data, to std::string raw
- this->xmpPacket.clear();
- size_t dataSize = GetHandleSize ( XMPdataHandle );
- HLock(XMPdataHandle);
- this->xmpPacket.assign ( (const char*)(*XMPdataHandle), dataSize );
- HUnlock ( XMPdataHandle );
-
- this->packetInfo.offset = kXMPFiles_UnknownOffset;
- this->packetInfo.length = (XMP_Int32)dataSize;
- this->containsXMP = true;
-
- CreatorAtom_ReadStrings ( mCreatorAtomStrings, movieUserData );
-
- }
-
- DisposeHandle ( XMPdataHandle );
-
- }
-
- }
-
- }
-
-} // MOV_MetaHandler::CacheFileData
-
-// =================================================================================================
-// =================================================================================================
-
-// *** Could be pulled out, maybe refactored and partly shared with AVI and WAV.
-
-#pragma pack(push,1)
-
-// [TODO] Can we switch to using just a full path here?
-struct FSSpecLegacy
-{
- short vRefNum;
- long parID;
- char name[260]; // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/fileio/fs/naming_a_file.asp -- 260 is "old school", 32000 is "new school".
-};
-
-struct CR8R_CreatorAtom
-{
- unsigned long magicLu;
-
- long atom_sizeL; // Size of this structure.
- short atom_vers_majorS; // Major atom version.
- short atom_vers_minorS; // Minor atom version.
-
- // mac
- unsigned long creator_codeLu; // Application code on MacOS.
- unsigned long creator_eventLu; // Invocation appleEvent.
-
- // windows
- char creator_extAC[16]; // Extension allowing registry search to app.
- char creator_flagAC[16]; // Flag passed to app at invocation time.
-
- char creator_nameAC[32]; // Name of the creator application.
-};
-
-typedef CR8R_CreatorAtom** CR8R_CreatorAtomHandle;
-
-typedef enum
-{
- Embed_ExportTypeMovie = 0,
- Embed_ExportTypeStill,
- Embed_ExportTypeAudio,
- Embed_ExportTypeCustom
-}
-Embed_ExportType;
-
-
-struct Embed_ProjectLinkAtom
-{
- // header data
- unsigned long magicLu;
- long atom_sizeL;
- short atom_vers_apiS;
- short atom_vers_codeS;
-
- // the link data
- unsigned long exportType; // See enum. The type of export that generated the file
-
- // [TODO] Can we switch to using just a full path here?
- FSSpecLegacy fullPath; // Full path of the project file
-};
-
-#pragma pack(pop)
-
-// -------------------------------------------------------------------------------------------------
-
-#define kCreatorTool "CreatorTool"
-#define AdobeCreatorAtomVersion_Major 1
-#define AdobeCreatorAtomVersion_Minor 0
-#define AdobeCreatorAtom_Magic 0xBEEFCAFE
-
-#define myCreatorAtom MakeFourCC ( 'C','r','8','r' )
-
-static void CreatorAtom_Initialize ( CR8R_CreatorAtom& creatorAtom )
-{
- memset ( &creatorAtom, 0, sizeof(CR8R_CreatorAtom) );
- creatorAtom.magicLu = AdobeCreatorAtom_Magic;
- creatorAtom.atom_vers_majorS = AdobeCreatorAtomVersion_Major;
- creatorAtom.atom_vers_minorS = AdobeCreatorAtomVersion_Minor;
- creatorAtom.atom_sizeL = sizeof(CR8R_CreatorAtom);
-}
-
-// -------------------------------------------------------------------------------------------------
-
-#define PR_PROJECT_LINK_ATOM_VERS_API 1
-#define PR_PROJECT_LINK_ATOM_VERS_CODE 0
-#define PR_PROJECT_LINK_ATOM_TYPE 'PrmL'
-#define PR_PROJECT_LINK_MAGIC 0x600DF00D // GoodFood
-
-#define myProjectLink MakeFourCC ( 'P','r','m','L')
-
-// -------------------------------------------------------------------------------------------------
-
-static void CreatorAtom_MakeValid ( CR8R_CreatorAtom * creator_atomP )
-{
- // If already valid, no conversion is needed.
- if ( creator_atomP->magicLu == AdobeCreatorAtom_Magic ) return;
-
- Flip4 ( &creator_atomP->magicLu );
- Flip2 ( &creator_atomP->atom_vers_majorS );
- Flip2 ( &creator_atomP->atom_vers_minorS );
-
- Flip4 ( &creator_atomP->atom_sizeL );
- Flip4 ( &creator_atomP->creator_codeLu );
- Flip4 ( &creator_atomP->creator_eventLu );
-
- XMP_Assert ( creator_atomP->magicLu == AdobeCreatorAtom_Magic );
-}
-
-// -------------------------------------------------------------------------------------------------
-
-static void CreatorAtom_ToBE ( CR8R_CreatorAtom * creator_atomP )
-{
- creator_atomP->atom_vers_majorS = MakeUns16BE ( creator_atomP->atom_vers_majorS );
- creator_atomP->atom_vers_minorS = MakeUns16BE ( creator_atomP->atom_vers_minorS );
-
- creator_atomP->magicLu = MakeUns32BE ( creator_atomP->magicLu );
- creator_atomP->atom_sizeL = MakeUns32BE ( creator_atomP->atom_sizeL );
- creator_atomP->creator_codeLu = MakeUns32BE ( creator_atomP->creator_codeLu );
- creator_atomP->creator_eventLu = MakeUns32BE ( creator_atomP->creator_eventLu );
-}
-
-// -------------------------------------------------------------------------------------------------
-
-static void ProjectLinkAtom_MakeValid ( Embed_ProjectLinkAtom * link_atomP )
-{
- // If already valid, no conversion is needed.
- if ( link_atomP->magicLu == PR_PROJECT_LINK_MAGIC ) return;
-
- // do the header
- Flip4 ( &link_atomP->magicLu );
- Flip4 ( &link_atomP->atom_sizeL );
- Flip2 ( &link_atomP->atom_vers_apiS );
- Flip2 ( &link_atomP->atom_vers_codeS );
-
- // do the FSSpec data
- Flip2 ( &link_atomP->fullPath.vRefNum );
- Flip4 ( &link_atomP->fullPath.parID );
-
- XMP_Assert ( link_atomP->magicLu == PR_PROJECT_LINK_MAGIC );
-}
-
-// -------------------------------------------------------------------------------------------------
-
-static void ProjectLinkAtom_Initialize ( Embed_ProjectLinkAtom& epla, Embed_ExportType type,
- const std::string& projectPathString)
-{
-
- memset ( &epla, 0, sizeof(Embed_ProjectLinkAtom) );
-
- epla.magicLu = PR_PROJECT_LINK_MAGIC;
- epla.atom_sizeL = epla.atom_vers_apiS = PR_PROJECT_LINK_ATOM_VERS_API;
- epla.atom_vers_codeS = PR_PROJECT_LINK_ATOM_VERS_CODE;
- epla.atom_sizeL = sizeof(Embed_ProjectLinkAtom);
- epla.exportType = type;
- epla.fullPath.vRefNum = 0;
- epla.fullPath.parID = 0;
-
- strncpy ( epla.fullPath.name, projectPathString.c_str(),
- min ( projectPathString.length(), sizeof(epla.fullPath.name) ) );
-
-}
-
-// -------------------------------------------------------------------------------------------------
-
-static bool Mov_ReadProjectLinkAtom ( UserData& movieUserData, Embed_ProjectLinkAtom* epla )
-{
- Handle PrmLdataHandle ( NewHandle(0) );
- if ( PrmLdataHandle == 0 ) return false;
-
- OSErr err = GetUserData ( movieUserData, PrmLdataHandle, 'PrmL', 1 );
- if ( err != noErr ) return false;
-
- // Convert handles data, to std::string raw
- // std::string PrmLPacket;
- // PrmLPacket.clear();
- size_t dataSize = GetHandleSize ( PrmLdataHandle );
- HLock ( PrmLdataHandle );
-
- bool ok = (dataSize == sizeof(Embed_ProjectLinkAtom));
- if ( ok ) {
- memcpy ( epla, (*PrmLdataHandle), dataSize );
- ProjectLinkAtom_MakeValid ( epla );
- }
-
- HUnlock ( PrmLdataHandle );
- DisposeHandle ( PrmLdataHandle );
-
- return ok;
-
-}
-
-// -------------------------------------------------------------------------------------------------
-
-static bool Mov_ReadCreatorAtom ( UserData& movieUserData, CR8R_CreatorAtom* creatorAtom )
-{
- Handle PrmLdataHandle ( NewHandle(0) );
- if ( PrmLdataHandle == 0 ) return false;
-
- OSErr err = GetUserData ( movieUserData, PrmLdataHandle, 'Cr8r', 1 );
- if ( err != noErr ) return false;
-
- // Convert handles data, to std::string raw
- // std::string PrmLPacket;
- // PrmLPacket.clear();
- size_t dataSize = GetHandleSize ( PrmLdataHandle );
- HLock ( PrmLdataHandle );
-
- bool ok = (sizeof(CR8R_CreatorAtom) == dataSize);
- if ( ok ) {
- memcpy ( creatorAtom, (*PrmLdataHandle), dataSize );
- CreatorAtom_MakeValid ( creatorAtom );
- }
-
- HUnlock ( PrmLdataHandle );
- DisposeHandle ( PrmLdataHandle );
-
- return ok;
-
-}
-
-// -------------------------------------------------------------------------------------------------
-
-static bool Mov_WriteCreatorAtom ( UserData& movieUserData, CR8R_CreatorAtom& creatorAtom, bool mustExist )
-{
- Handle PrmLdataHandle ( NewHandle(sizeof(CR8R_CreatorAtom)) );
- if ( PrmLdataHandle == 0 ) return false;
-
- OSErr err = GetUserData ( movieUserData, PrmLdataHandle, 'Cr8r', 1);
- if ( err == noErr ) {
- while ( ! RemoveUserData ( movieUserData, 'Cr8r', 1 ) ) {};
- } else {
- if ( mustExist ) return false;
- }
-
- // Convert handles data, to std::string raw
- // std::string PrmLPacket;
- // PrmLPacket.clear();
- size_t dataSize = GetHandleSize ( PrmLdataHandle );
- HLock ( PrmLdataHandle );
- memcpy ( (*PrmLdataHandle), &creatorAtom, sizeof(CR8R_CreatorAtom) );
- CreatorAtom_ToBE ( (CR8R_CreatorAtom*)(*PrmLdataHandle) );
- HUnlock ( PrmLdataHandle );
-
- OSErr movieErr = AddUserData ( movieUserData, PrmLdataHandle, 'Cr8r');
-
- DisposeHandle ( PrmLdataHandle );
-
- return movieErr==0;
-}
-
-// -------------------------------------------------------------------------------------------------
-
-#define DoAssignBufferedString(str,buffer) AssignBufferedString ( str, buffer, sizeof(buffer) )
-
-static inline void AssignBufferedString ( std::string & str, const char * buffer, size_t maxLen )
-{
- size_t len;
- for ( len = 0; (len < maxLen) && (buffer[len] != 0); ++len ) { /* empty body */ }
- str.assign ( buffer, len );
-}
-
-// -------------------------------------------------------------------------------------------------
-
-static bool CreatorAtom_ReadStrings ( MOV_MetaHandler::CreatorAtomStrings& creatorAtomStrings,
- UserData& movieUserData )
-{
- Embed_ProjectLinkAtom epla;
- bool ok = Mov_ReadProjectLinkAtom ( movieUserData, &epla );
-
- if ( ok ) {
-
- std::string projectPathString = epla.fullPath.name;
-
- if ( ! projectPathString.empty() ) {
-
- if ( projectPathString[0] == '/' ) {
- creatorAtomStrings.posixProjectPath = projectPathString;
- } else if ( projectPathString.substr(0,4) == std::string("\\\\?\\") ) {
- creatorAtomStrings.uncProjectPath = projectPathString;
- }
-
- switch ( epla.exportType ) {
- case Embed_ExportTypeMovie : creatorAtomStrings.projectRefType = "movie"; break;
- case Embed_ExportTypeStill : creatorAtomStrings.projectRefType = "still"; break;
- case Embed_ExportTypeAudio : creatorAtomStrings.projectRefType = "audio"; break;
- case Embed_ExportTypeCustom : creatorAtomStrings.projectRefType = "custom"; break;
- }
-
- }
-
- }
-
- CR8R_CreatorAtom creatorAtom;
- ok = Mov_ReadCreatorAtom ( movieUserData, &creatorAtom );
-
- if ( ok ) {
-
- char buffer[256];
-
- sprintf ( buffer, "%d", creatorAtom.creator_codeLu );
- creatorAtomStrings.applicationCode = buffer;
-
- sprintf ( buffer, "%d", creatorAtom.creator_eventLu );
- creatorAtomStrings.invocationAppleEvent = buffer;
-
- DoAssignBufferedString ( creatorAtomStrings.extension, creatorAtom.creator_extAC );
- DoAssignBufferedString ( creatorAtomStrings.invocationFlags, creatorAtom.creator_flagAC );
- DoAssignBufferedString ( creatorAtomStrings.creatorTool, creatorAtom.creator_nameAC );
-
- }
-
- return ok;
-
-}
-
-// -------------------------------------------------------------------------------------------------
-
-static bool CreatorAtom_SetProperties ( SXMPMeta& xmpObj,
- const MOV_MetaHandler::CreatorAtomStrings& creatorAtomStrings )
-{
- if ( ! creatorAtomStrings.posixProjectPath.empty() ) {
- xmpObj.SetStructField ( kXMP_NS_CreatorAtom, "macAtom",
- kXMP_NS_CreatorAtom, "posixProjectPath", creatorAtomStrings.posixProjectPath, 0 );
- }
-
- if ( ! creatorAtomStrings.uncProjectPath.empty() ) {
- xmpObj.SetStructField ( kXMP_NS_CreatorAtom, "windowsAtom",
- kXMP_NS_CreatorAtom, "uncProjectPath", creatorAtomStrings.uncProjectPath, 0 );
- }
-
- if ( ! creatorAtomStrings.projectRefType.empty() ) {
- xmpObj.SetStructField ( kXMP_NS_DM, "projectRef", kXMP_NS_DM, "type", creatorAtomStrings.projectRefType.c_str());
- }
-
- if ( ! creatorAtomStrings.applicationCode.empty() ) {
- xmpObj.SetStructField ( kXMP_NS_CreatorAtom, "macAtom",
- kXMP_NS_CreatorAtom, "applicationCode", creatorAtomStrings.applicationCode, 0 );
- }
-
- if ( ! creatorAtomStrings.invocationAppleEvent.empty() ) {
- xmpObj.SetStructField ( kXMP_NS_CreatorAtom, "macAtom",
- kXMP_NS_CreatorAtom, "invocationAppleEvent", creatorAtomStrings.invocationAppleEvent, 0 );
- }
-
- if ( ! creatorAtomStrings.extension.empty() ) {
- xmpObj.SetStructField ( kXMP_NS_CreatorAtom, "windowsAtom",
- kXMP_NS_CreatorAtom, "extension", creatorAtomStrings.extension, 0 );
- }
-
- if ( ! creatorAtomStrings.invocationFlags.empty() ) {
- xmpObj.SetStructField ( kXMP_NS_CreatorAtom, "windowsAtom",
- kXMP_NS_CreatorAtom, "invocationFlags", creatorAtomStrings.invocationFlags, 0 );
- }
-
- if ( ! creatorAtomStrings.creatorTool.empty() ) {
- xmpObj.SetProperty ( kXMP_NS_XMP, "CreatorTool", creatorAtomStrings.creatorTool, 0 );
- }
-
- return ok;
-
-}
-
-// -------------------------------------------------------------------------------------------------
-
-#define EnsureFinalNul(buffer) buffer [ sizeof(buffer) - 1 ] = 0
-
-static bool CreatorAtom_Update ( SXMPMeta& xmpObj,
- UserData& movieUserData )
-{
-
- // Get Creator Atom XMP values.
- bool found = false;
- std::string posixPathString, uncPathString;
- if ( xmpObj.GetStructField ( kXMP_NS_CreatorAtom, "macAtom", kXMP_NS_CreatorAtom, "posixProjectPath", &posixPathString, 0 ) ) found = true;
- if ( xmpObj.GetStructField ( kXMP_NS_CreatorAtom, "windowsAtom", kXMP_NS_CreatorAtom, "uncProjectPath", &uncPathString, 0 ) ) found = true;
-
- std::string applicationCodeString, invocationAppleEventString, extensionString, invocationFlagsString, creatorToolString;
- if ( xmpObj.GetStructField ( kXMP_NS_CreatorAtom, "macAtom", kXMP_NS_CreatorAtom, "applicationCode", &applicationCodeString, 0 ) ) found = true;
- if ( xmpObj.GetStructField ( kXMP_NS_CreatorAtom, "macAtom", kXMP_NS_CreatorAtom, "invocationAppleEvent", &invocationAppleEventString, 0 ) ) found = true;
- if ( xmpObj.GetStructField ( kXMP_NS_CreatorAtom, "windowsAtom", kXMP_NS_CreatorAtom, "extension", &extensionString, 0 ) ) found = true;
- if ( xmpObj.GetStructField ( kXMP_NS_CreatorAtom, "windowsAtom", kXMP_NS_CreatorAtom, "invocationFlags", &invocationFlagsString, 0 ) ) found = true;
- if ( xmpObj.GetProperty ( kXMP_NS_XMP, "CreatorTool", &creatorToolString, 0 ) ) found = true;
-
- // If no Creator Atom information found, don't write anything.
- if ( ! found ) return false;
-
- // Read Legacy Creator Atom.
- unsigned long creatorAtomSize = 0;
- CR8R_CreatorAtom creatorAtomLegacy;
- CreatorAtom_Initialize ( creatorAtomLegacy );
- bool ok = Mov_ReadCreatorAtom ( movieUserData, &creatorAtomLegacy );
-
- // Generate new Creator Atom from XMP.
- CR8R_CreatorAtom creatorAtomViaXMP;
- CreatorAtom_Initialize ( creatorAtomViaXMP );
-
- if ( ! applicationCodeString.empty() ) {
- creatorAtomViaXMP.creator_codeLu = strtoul ( applicationCodeString.c_str(), 0, 0 );
- }
- if ( ! invocationAppleEventString.empty() ) {
- creatorAtomViaXMP.creator_eventLu = strtoul ( invocationAppleEventString.c_str(), 0, 0 );
- }
- if ( ! extensionString.empty() ) {
- strncpy ( creatorAtomViaXMP.creator_extAC, extensionString.c_str(), sizeof(creatorAtomViaXMP.creator_extAC) );
- EnsureFinalNul ( creatorAtomViaXMP.creator_extAC );
- }
- if ( ! invocationFlagsString.empty() ) {
- strncpy ( creatorAtomViaXMP.creator_flagAC, invocationFlagsString.c_str(), sizeof(creatorAtomViaXMP.creator_flagAC) );
- EnsureFinalNul ( creatorAtomViaXMP.creator_flagAC );
- }
- if ( ! creatorToolString.empty() ) {
- strncpy ( creatorAtomViaXMP.creator_nameAC, creatorToolString.c_str(), sizeof(creatorAtomViaXMP.creator_nameAC) );
- EnsureFinalNul ( creatorAtomViaXMP.creator_nameAC );
- }
-
- // Write Creator Atom.
- if ( ok ) {
- // If there's legacy, update if atom generated from XMP doesn't match legacy.
- if ( memcmp ( &creatorAtomViaXMP, &creatorAtomLegacy, sizeof(CR8R_CreatorAtom) ) != 0 ) {
- ok = Mov_WriteCreatorAtom ( movieUserData, creatorAtomViaXMP, true );
- }
- } else {
- // Write completely new atom from XMP.
- ok = Mov_WriteCreatorAtom ( movieUserData, creatorAtomViaXMP, false );
- }
-
- return ok;
-
-}
-
-// =================================================================================================
-
-#endif // XMP_64 || XMP_UNIXBuild
diff --git a/source/XMPFiles/FileHandlers/MOV_Handler.hpp b/source/XMPFiles/FileHandlers/MOV_Handler.hpp
deleted file mode 100644
index 4d398ce..0000000
--- a/source/XMPFiles/FileHandlers/MOV_Handler.hpp
+++ /dev/null
@@ -1,94 +0,0 @@
-#ifndef __MOV_Handler_hpp__
-#define __MOV_Handler_hpp__ 1
-
-// =================================================================================================
-// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2008 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 "XMP_Environment.h" // ! This must be the first include.
-#if ! ( XMP_64 || XMP_UNIXBuild) // Closes at very bottom.
-
-#include "XMPFiles_Impl.hpp"
-
-// Include these first to prevent collision with CIcon
-#if XMP_WinBuild
-#include "QTML.h"
-#include "Movies.h"
-#endif
-
-#if XMP_MacBuild
-#include <Movies.h>
-#endif
-
-// =================================================================================================
-/// \file MOV_Handler.hpp
-/// \brief File format handler for MOV.
-///
-/// This header ...
-///
-// =================================================================================================
-
-extern XMPFileHandler * MOV_MetaHandlerCTor ( XMPFiles * parent );
-
-extern bool MOV_CheckFormat ( XMP_FileFormat format,
- XMP_StringPtr filePath,
- LFA_FileRef fileRef,
- XMPFiles * parent );
-
-static const XMP_OptionBits kMOV_HandlerFlags = (kXMPFiles_CanInjectXMP |
- kXMPFiles_CanExpand |
- kXMPFiles_PrefersInPlace |
- kXMPFiles_AllowsOnlyXMP |
- kXMPFiles_ReturnsRawPacket |
- kXMPFiles_HandlerOwnsFile);
- // In the future, we'll add kXMPFiles_CanReconcile
-
-class MOV_MetaHandler : public XMPFileHandler
-{
-public:
-
- MOV_MetaHandler ( XMPFiles * parent );
- ~MOV_MetaHandler();
-
- void CacheFileData();
- void ProcessXMP();
-
- void UpdateFile ( bool doSafeUpdate );
- void WriteFile ( LFA_FileRef sourceRef, const std::string & sourcePath );
-
- struct CreatorAtomStrings {
- std::string posixProjectPath;
- std::string uncProjectPath;
- std::string projectRefType;
- std::string applicationCode;
- std::string invocationAppleEvent;
- std::string extension;
- std::string invocationFlags;
- std::string creatorTool;
- };
-
- CreatorAtomStrings mCreatorAtomStrings; // ! Public so utility code can muck with them.
-
-protected:
-
- bool mQTInit;
- Handle mMovieDataRef;
- DataHandler mMovieDataHandler;
- Movie mMovie;
- short mMovieResourceID;
- long mFilePermission;
-
- bool OpenMovie ( long inPermission );
- void CloseMovie();
-
-}; // MOV_MetaHandler
-
-// =================================================================================================
-
-#endif // XMP_64 || XMP_UNIXBuild
-#endif /* __MOV_Handler_hpp__ */
diff --git a/source/XMPFiles/FileHandlers/MP3_Handler.cpp b/source/XMPFiles/FileHandlers/MP3_Handler.cpp
index f906900..2c8f547 100644
--- a/source/XMPFiles/FileHandlers/MP3_Handler.cpp
+++ b/source/XMPFiles/FileHandlers/MP3_Handler.cpp
@@ -1,6 +1,6 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2007 Adobe Systems Incorporated
+// Copyright 2008 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -8,100 +8,96 @@
// =================================================================================================
#include "XMP_Environment.h" // ! This must be the first include.
-#if ! XMP_UNIXBuild // Closes at very bottom. Disabled on UNIX until legacy-as-local is fixed.
-
#include "MP3_Handler.hpp"
-#include "ID3_Support.hpp"
-
-using namespace std;
-
-#define mp3TitleChunk "TIT2"
-#define mp3CreateDateChunk3 "TYER"
-#define mp3CreateDateChunk4 "TDRV"
-#define mp3ArtistChunk "TPE1"
-#define mp3AlbumChunk "TALB"
-#define mp3GenreChunk "TCON"
-#define mp3CommentChunk "COMM"
-#define mp3TrackChunk "TRCK"
-
-// DC
-#define kTitle "title"
-
-// XMP
-#define kCreateDate "CreateDate"
-
-// DM
-#define kArtist "artist"
-#define kAlbum "album"
-#define kGenre "genre"
-#define kLogComment "logComment"
-#define kTrack "trackNumber"
// =================================================================================================
/// \file MP3_Handler.cpp
-/// \brief File format handler for MP3.
-///
-/// This header ...
-///
+/// \brief MP3 handler class.
// =================================================================================================
// =================================================================================================
-// MP3_MetaHandlerCTor
+// Helper structs and private routines
// ====================
+struct ReconProps {
+ const char* frameID;
+ const char* ns;
+ const char* prop;
+};
+
+const static XMP_Uns32 XMP_FRAME_ID = 0x50524956;
+
+const static ReconProps reconProps[] = {
+ { "TPE1", kXMP_NS_DM, "artist" },
+ { "TALB", kXMP_NS_DM, "album" },
+ { "TRCK", kXMP_NS_DM, "trackNumber" },
+ // exceptions that need attention:
+ { "TCON", kXMP_NS_DM, "genre" }, // genres might be numeric
+ { "TIT2", kXMP_NS_DC, "title" }, // ["x-default"] language alternatives
+ { "COMM", kXMP_NS_DM, "logComment" }, // two distinct strings, language alternative
+
+ { "TYER", kXMP_NS_XMP, "CreateDate" }, // Year (YYYY) Deprecated since 2.4
+ { "TDAT", kXMP_NS_XMP, "CreateDate" }, // Date (DDMM) Deprecated since 2.4
+ { "TIME", kXMP_NS_XMP, "CreateDate" }, // Time (HHMM) Deprecated since 2.4
+ { "TDRC", kXMP_NS_XMP, "CreateDate" }, // assemble date/time v2.4
+
+ // new reconciliations introduced in Version 5
+ { "TCMP", kXMP_NS_DM, "partOfCompilation" }, // presence/absence of TCMP frame dedides
+ { "USLT", kXMP_NS_DM, "lyrics" },
+ { "TCOM", kXMP_NS_DM, "composer" },
+ { "TPOS", kXMP_NS_DM, "discNumber" }, // * a text field! might contain "/<total>"
+ { "TCOP", kXMP_NS_DC, "rights" }, // ["x-default"] language alternatives
+ { "TPE4", kXMP_NS_DM, "engineer" },
+ { "WCOP", kXMP_NS_XMP_Rights , "WebStatement" },
+
+ { 0, 0, 0 } // must be last as a sentinel
+};
+// =================================================================================================
+// MP3_MetaHandlerCTor
+// ====================
XMPFileHandler * MP3_MetaHandlerCTor ( XMPFiles * parent )
{
return new MP3_MetaHandler ( parent );
-
-} // MP3_MetaHandlerCTor
+}
// =================================================================================================
// MP3_CheckFormat
// ===============
-
// For MP3 we check parts .... See the MP3 spec for offset info.
-//
-//
-
bool MP3_CheckFormat ( XMP_FileFormat format,
- XMP_StringPtr filePath,
- LFA_FileRef inFileRef,
- XMPFiles * parent )
+ XMP_StringPtr filePath,
+ LFA_FileRef file,
+ XMPFiles * parent )
{
- IgnoreParam(format); IgnoreParam(filePath);
- XMP_Assert ( format == kXMP_MP3File );
+ IgnoreParam(filePath); IgnoreParam(parent); //supress warnings
+ XMP_Assert ( format == kXMP_MP3File ); //standard assert
+ LFA_Rewind( file );
- if ( inFileRef == 0 ) return false;
+ XMP_Uns8 header[3];
+ LFA_Read( file, header, 3, true );
+ if ( !CheckBytes( &header[0], "ID3", 3 ))
+ return (parent->format == kXMP_MP3File); // No ID3 signature -> depend on first call hint.
- // BUG FIX 1219125: If we find that the "unsynchronistation" flag is turned on, we should fail on that file.
- // TODO: Support "unsynchronized" files.
+ XMP_Uns8 major = LFA_ReadUns8( file );
+ XMP_Uns8 minor = LFA_ReadUns8( file );
- LFA_Seek ( inFileRef, 0ULL, SEEK_SET );
+ if ( (major<3 || major>4) || (minor == 0xFF) )
+ return false; // only support IDv2.3 and ID3v2.4, minor must not be 0xFF
- char szID [4] = { "xxx" };
- long bytesRead = LFA_Read ( inFileRef, szID, 3 );
- if ( bytesRead != 3 ) return false;
+ XMP_Uns8 flags = LFA_ReadUns8( file );
- if ( strncmp ( szID, "ID3", 3 ) != 0 ) {
-
- return (parent->format == kXMP_MP3File); // No ID3 signature, depend on first call hint.
-
- } else {
+ //TODO
+ if ( flags & 0x10 )
+ XMP_Throw("no support for MP3 with footer",kXMPErr_Unimplemented); //no support for MP3 with footer
+ if ( flags & 0x80 )
+ return false; //no support for unsynchronized MP3 (as before, also see [1219125])
+ if ( flags & 0x0F )
+ XMP_Throw("illegal header lower bits",kXMPErr_Unimplemented);
- // Read the version, flag and size.
- XMP_Uns8 v2 = 0, flags = 0, bMajorVer = 0;
- unsigned long dwLen = 0;
- bool ok = ID3_Support::GetTagInfo ( inFileRef, bMajorVer, v2, flags, dwLen );
-
- if ( ok ) {
- if ( (bMajorVer < 3) || (bMajorVer > 4) ) return false;
- if ( flags & 0x80 ) return false; // 0x80 == "unsynchronized"
- }
-
- }
+ XMP_Uns32 size = LFA_ReadUns32_BE( file );
+ if ( size & 0x80808080 ) return false; //if any bit survives -> not a valid synchsafe 32 bit integer
return true;
-
} // MP3_CheckFormat
@@ -114,8 +110,7 @@ MP3_MetaHandler::MP3_MetaHandler ( XMPFiles * _parent )
this->parent = _parent;
this->handlerFlags = kMP3_HandlerFlags;
this->stdCharForm = kXMP_Char8Bit;
-
-} // MP3_MetaHandler::MP3_MetaHandler
+}
// =================================================================================================
// MP3_MetaHandler::~MP3_MetaHandler
@@ -123,224 +118,631 @@ MP3_MetaHandler::MP3_MetaHandler ( XMPFiles * _parent )
MP3_MetaHandler::~MP3_MetaHandler()
{
- // Nothing to do.
-
-} // MP3_MetaHandler::~MP3_MetaHandler
-
-// =================================================================================================
-// MP3_MetaHandler::UpdateFile
-// ===========================
-
-void MP3_MetaHandler::UpdateFile ( bool doSafeUpdate )
-{
- if ( ! this->needsUpdate ) return;
- if ( doSafeUpdate ) XMP_Throw ( "MP3_MetaHandler::UpdateFile: Safe update not supported", kXMPErr_Unavailable );
-
- bool fReconciliate = !(this->parent->openFlags & kXMPFiles_OpenOnlyXMP);
-
- XMP_StringPtr packetStr = xmpPacket.c_str();
- XMP_StringLen packetLen = (XMP_StringLen)xmpPacket.size();
- if ( packetLen == 0 ) return;
-
- LFA_FileRef fileRef ( this->parent->fileRef );
- if ( fileRef == 0 ) return;
-
- // Get the id3v2 version
- XMP_Uns8 bVersion = 3;
- unsigned long dwTagSize;
- bool ok = ID3_Support::FindID3Tag ( fileRef, dwTagSize, bVersion );
- if ( ! ok ) bVersion = 3;
-
- // Allocate the temp buffer for the native frames we have to overwrite
- unsigned long bufferSize = 7*TAG_MAX_SIZE; // Just enough buffer for all 7 tags
- char buffer[7*TAG_MAX_SIZE];
- memset ( buffer, 0, bufferSize );
- unsigned long dwCurOffset = 0;
-
- if ( fReconciliate ) {
-
- std::string strTitle;
- this->xmpObj.GetLocalizedText ( kXMP_NS_DC, kTitle, "", "x-default", 0, &strTitle, 0 );
- ID3_Support::AddXMPTagToID3Buffer ( buffer, &dwCurOffset, bufferSize, bVersion,
- mp3TitleChunk, strTitle.c_str(), (unsigned long)strTitle.size() );
-
- std::string strDate;
- this->xmpObj.GetProperty ( kXMP_NS_XMP, kCreateDate, &strDate, 0 );
- if ( bVersion == 4 ) {
- ID3_Support::AddXMPTagToID3Buffer ( buffer, &dwCurOffset, bufferSize, bVersion,
- mp3CreateDateChunk4, strDate.c_str(), (unsigned long)strDate.size() );
- } else {
- ID3_Support::AddXMPTagToID3Buffer ( buffer, &dwCurOffset, bufferSize, bVersion,
- mp3CreateDateChunk3, strDate.c_str(), (unsigned long)strDate.size() );
- }
-
- std::string strArtist;
- this->xmpObj.GetProperty ( kXMP_NS_DM, kArtist, &strArtist, 0 );
- ID3_Support::AddXMPTagToID3Buffer ( buffer, &dwCurOffset, bufferSize, bVersion, mp3ArtistChunk,
- strArtist.c_str(), (unsigned long)strArtist.size() );
-
- std::string strAlbum;
- this->xmpObj.GetProperty ( kXMP_NS_DM, kAlbum, &strAlbum, 0 );
- ID3_Support::AddXMPTagToID3Buffer ( buffer, &dwCurOffset, bufferSize, bVersion,
- mp3AlbumChunk, strAlbum.c_str(), (unsigned long)strAlbum.size() );
-
- std::string strGenre;
- this->xmpObj.GetProperty ( kXMP_NS_DM, kGenre, &strGenre, 0 );
- ID3_Support::AddXMPTagToID3Buffer ( buffer, &dwCurOffset, bufferSize, bVersion,
- mp3GenreChunk, strGenre.c_str(), (unsigned long)strGenre.size() );
-
- std::string strComment;
- this->xmpObj.GetProperty ( kXMP_NS_DM, kLogComment, &strComment, 0 );
- ID3_Support::AddXMPTagToID3Buffer ( buffer, &dwCurOffset, bufferSize, bVersion,
- mp3CommentChunk, strComment.c_str(), (unsigned long)strComment.size() );
-
- std::string strTrack;
- this->xmpObj.GetProperty ( kXMP_NS_DM, kTrack, &strTrack, 0 );
- ID3_Support::AddXMPTagToID3Buffer ( buffer, &dwCurOffset, bufferSize, bVersion,
- mp3TrackChunk, strTrack.c_str(), (unsigned long)strTrack.size() );
-
+ // free frames
+ ID3v2Frame* curFrame;
+ while( !this->framesVector.empty() )
+ {
+ curFrame = this->framesVector.back();
+ delete curFrame;
+ framesVector.pop_back();
}
-
- // TODO id3v1 tags
-
- // Saving it all
- ID3_Support::SetMetaData ( fileRef, (char*)packetStr, packetLen, buffer, dwCurOffset, fReconciliate );
-
- this->needsUpdate = false;
-
-} // MP3_MetaHandler::UpdateFile
-
-// =================================================================================================
-// MP3_MetaHandler::WriteFile
-// ==========================
-
-void MP3_MetaHandler::WriteFile ( LFA_FileRef sourceRef,
- const std::string & sourcePath )
-{
- IgnoreParam(sourceRef); IgnoreParam(sourcePath);
-
- XMP_Throw ( "MP3_MetaHandler::WriteFile: Not supported", kXMPErr_Unavailable );
-
-} // MP3_MetaHandler::WriteFile
+}
// =================================================================================================
// MP3_MetaHandler::CacheFileData
// ==============================
-
void MP3_MetaHandler::CacheFileData()
{
- bool fReconciliate = ! ( this->parent->openFlags & kXMPFiles_OpenOnlyXMP );
- bool ok;
+ //*** abort procedures
+ this->containsXMP = false; //assume no XMP for now
+
+ LFA_FileRef file = this->parent->fileRef;
+ XMP_PacketInfo &packetInfo = this->packetInfo;
+
+ LFA_Rewind(file);
+
+ hasID3Tag = id3Header.read( file );
+ majorVersion = id3Header.fields[ID3Header::o_version_major];
+ minorVersion = id3Header.fields[ID3Header::o_version_minor];
+ hasExtHeader = (0 != ( 0x40 & id3Header.fields[ID3Header::o_flags])); //'naturally' false if no ID3Tag
+ hasFooter = ( 0 != ( 0x10 & id3Header.fields[ID3Header::o_flags])); //'naturally' false if no ID3Tag
+
+ // stored size is w/o initial header (thus adding 10)
+ // + but extended header (if existing)
+ // + padding + frames after unsynchronisation (?)
+ // (if no ID3 tag existing, constructed default correctly sets size to 10.)
+ oldTagSize = 10 + synchToInt32(GetUns32BE( &id3Header.fields[ID3Header::o_size] ));
+
+ if (hasExtHeader)
+ {
+ extHeaderSize = synchToInt32( LFA_ReadInt32_BE( file));
+ XMP_Uns8 extHeaderNumFlagBytes = LFA_ReadUns8( file );
+
+ // v2.3 doesn't include the size, while v2.4 does
+ if ( majorVersion < 4 ) extHeaderSize += 4;
+ XMP_Validate( extHeaderSize >= 6, "extHeader size too small", kXMPErr_BadFileFormat );
+
+ bool ok;
+ LFA_Seek(file, extHeaderSize - 6, SEEK_CUR , &ok);
+ XMP_Assert(ok);
+ }
+ else
+ {
+ extHeaderSize = 0; // := there is no such header.
+ }
- this->containsXMP = false;
+ this->framesVector.clear(); //mac precaution
+ ID3v2Frame* curFrame = 0; // reusable
+
+ ////////////////////////////////////////////////////
+ // read frames
+ while ( LFA_Tell(file) < oldTagSize )
+ {
+ curFrame = new ID3v2Frame();
+
+ try {
+ XMP_Int64 frameSize = curFrame->read( file, majorVersion );
+ if (frameSize == 0) // no more frames coming => proceed to padding
+ {
+ delete curFrame; // ..since not becoming part of vector for latter delete.
+ break; // not a throw. There's nothing wrong with padding.
+ }
+ this->containsXMP = true;
+ } catch( XMP_Error e)
+ {
+ delete curFrame;
+ XMP_Throw( e.GetErrMsg(), e.GetID()); // rethrow
+ }
- // We asked the host to allow us to manage the opening of the file
- LFA_FileRef fileRef ( this->parent->fileRef );
- if ( fileRef == 0 ) return;
+ // these are both pointer assignments, no (copy) construction
+ // (MemLeak Note: for all things pushed, memory cleanup is taken care of in destructor.)
+ this->framesVector.push_back( curFrame );
+
+ //remember XMP-Frame, if it occurs:
+ if ( CheckBytes( &curFrame->fields[ID3v2Frame::o_id], "PRIV", 4 ))
+ if( curFrame->contentSize > 8 ) // to avoid adress violation on very small non-XMP PRIV frames
+ if( CheckBytes( &curFrame->content[0], "XMP\0", 4 ))
+ {
+ // be sure that this is the first packet (all else would be illegal format)
+ XMP_Validate( framesMap[ XMP_FRAME_ID] == 0, "two XMP packets in one file", kXMPErr_BadFileFormat );
+ //add this to map, needed on reconciliation
+ framesMap[ XMP_FRAME_ID ] = curFrame;
+
+ this->packetInfo.length = curFrame->contentSize - 4; // content minus "XMP\0"
+ this->packetInfo.offset = ( LFA_Tell(file) - this->packetInfo.length );
+
+ this->xmpPacket.erase(); //safety
+ this->xmpPacket.assign( &curFrame->content[4], curFrame->contentSize - 4 );
+ this->containsXMP = true; // do this last, after all possible failure
+ }
- // Determine the size of the metadata
- unsigned long bufferSize(0);
- ok = ID3_Support::GetMetaData ( fileRef, 0, &bufferSize, 0 );
+ // No space for another frame? => assume into ID3v2.4 padding.
+ if ( LFA_Tell(file) + 10 >= oldTagSize )
+ break;
+ }
- if ( ! ok ) {
+ ////////////////////////////////////////////////////
+ // padding
+ oldPadding = oldTagSize - LFA_Tell( file );
+ oldFramesSize = oldTagSize - 10 - oldPadding;
+
+ XMP_Validate( oldPadding >= 0, "illegal oldTagSize or padding value", kXMPErr_BadFileFormat );
+
+ for ( XMP_Int64 i = oldPadding; i > 0;)
+ {
+ if ( i >= 8 ) // worthwhile optimization
+ {
+ if ( LFA_ReadInt64_BE(file) != 0 )
+ XMP_Throw ( "padding not nulled out.", kXMPErr_BadFileFormat );
+ i -= 8;
+ continue;
+ }
+ if ( LFA_ReadUns8(file) != 0)
+ XMP_Throw ( "padding(2) not nulled out.", kXMPErr_BadFileFormat );
+ i--;
+ }
- packetInfo.writeable = true; // If no packet found, created packets will be writeable
+ //// read ID3v1 tag
+ if ( ! this->containsXMP ) // all else has priority
+ {
+ this->containsXMP = id3v1Tag.read( file, &this->xmpObj );
+ }
- } else if ( bufferSize > 0 ) {
+} // MP3_MetaHandler::CacheFileData
- // Allocate the buffer
- std::string buffer;
- buffer.reserve ( bufferSize );
- buffer.assign ( bufferSize, ' ' );
- // Get the metadata
- XMP_Int64 xmpOffset;
- ok = ID3_Support::GetMetaData ( fileRef, (char*)buffer.c_str(), &bufferSize, &xmpOffset );
- if ( ok ) {
- this->packetInfo.offset = xmpOffset;
- this->packetInfo.length = bufferSize;
- this->xmpPacket.assign(buffer.data(), bufferSize);
- this->xmpObj.ParseFromBuffer ( this->xmpPacket.c_str(), (XMP_StringLen)this->xmpPacket.size() );
- this->containsXMP = true;
- }
+// =================================================================================================
+// MP3_MetaHandler::ProcessXMP
+// ===========================
+//
+// Process the raw XMP and legacy metadata that was previously cached.
+void MP3_MetaHandler::ProcessXMP()
+{
+ // Process the XMP packet.
+ if ( ! this->xmpPacket.empty() ) {
+ XMP_Assert ( this->containsXMP );
+ XMP_StringPtr packetStr = this->xmpPacket.c_str();
+ XMP_StringLen packetLen = (XMP_StringLen) this->xmpPacket.size();
+ this->xmpObj.ParseFromBuffer ( packetStr, packetLen );
+ this->processedXMP = true;
+ }
+ ///////////////////////////////////////////////////////////////////
+ // assumptions on presence-absence "flag tags"
+ // ( unless no xmp whatsoever present )
+ if ( ! this->xmpPacket.empty() ) {
+ this->xmpObj.SetProperty( kXMP_NS_DM, "partOfCompilation", "false" );
}
- if ( fReconciliate ) {
+ ////////////////////////////////////////////////////////////////////
+ // import of legacy properties
+ ID3v2Frame* curFrame;
+ XMP_Bool hasTDRC = false;
+ XMP_DateTime newDateTime;
+
+ if (this->hasID3Tag) // otherwise pretty pointless...
+ for (int r=0; reconProps[r].frameID != 0; r++)
+ {
+ //get the frame ID to look for
+ XMP_Uns32 frameID = GetUns32BE( reconProps[r].frameID );
+
+ // deal with each such frame in the frameVector
+ // (since there might be several, some of them not applicable, i.e. COMM)
+ for ( vector<ID3_Support::ID3v2Frame*>::iterator it = framesVector.begin(); it!=framesVector.end(); ++it)
+ {
+ curFrame = *it;
+ if (frameID != curFrame->id) // nothing applicable. Next!
+ continue;
+
+ // go deal with it!
+ // get the property
+ std::string utf8string;
+ bool result = curFrame->getFrameValue(majorVersion, frameID, &utf8string);
+
+ if (! result)
+ continue; //ignore but preserve this frame (i.e. not applicable COMM frame)
+
+ //////////////////////////////////////////////////////////////////////////////////
+ // if we come as far as here, it's proven that there's a relevant XMP property
+ this->containsXMP = true;
+
+ ID3_Support::ID3v2Frame* t = framesMap[ frameID ];
+ if ( t != 0 ) // an (earlier, relevant) frame?
+ t->active = false;
+
+ // add this to map (needed on reconciliation)
+ // note: above code reaches, that COMM/USLT frames
+ // only then reach this map, if they are 'eng'(lish)
+ // multiple occurences indeed leads to last one survives
+ // ( in this map, all survive in the file )
+ framesMap[ frameID ] = curFrame;
+
+ // now write away as needed;
+ // merely based on existence, relevant even if empty:
+ if ( frameID == 0x54434D50) // TCMP if exists: part of compilation
+ {
+ this->xmpObj.SetProperty( kXMP_NS_DM, "partOfCompilation", "true" );
+ } else if ( ! utf8string.empty() )
+ switch( frameID )
+ {
+ case 0x54495432: // TIT2 -> title["x-default"]
+ case 0x54434F50: // TCOP -> rights["x-default"]
+ this->xmpObj.SetLocalizedText( reconProps[r].ns , reconProps[r].prop,"" , "x-default" , utf8string );
+ break;
+ case 0x54434F4E: // TCON -> genre ( might be numeric string. prior to 2.3 a one-byte numeric value? )
+ {
+ XMP_Int32 pos = 0; // going through input string
+ if ( utf8string[pos] == '(' ) { // number in brackets?
+ pos++;
+ XMP_Uns8 iGenre = (XMP_Uns8) atoi( &utf8string.c_str()[1] );
+ if ( (iGenre > 0) && (iGenre < 127) ) {
+ utf8string.assign( Genres[iGenre] );
+ } else {
+ utf8string.assign( Genres[12] ); // "Other"
+ }
+ } else {
+ // Text, let's "try" to find it anyway (for best upper/lower casing)
+ int i;
+ const char* genreCString = utf8string.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 )) {
+ utf8string.assign( Genres[i] ); // found, let's use the one in the list
+ break;
+ }
+ }
+ // otherwise (if for-loop runs through): leave as is
+ }
+ // write out property (derived or direct, but certainly non-numeric)
+ this->xmpObj.SetProperty( reconProps[r].ns, reconProps[r].prop, utf8string );
+ }
+ break;
+ case 0x54594552: // TYER -> xmp:CreateDate.year
+ {
+ try { // Don't let wrong dates in id3 stop import.
+ if ( !hasTDRC )
+ {
+ newDateTime.year = SXMPUtils::ConvertToInt( utf8string );
+ newDateTime.hasDate = true;
+ }
+ } catch ( ... ) {
+ // Do nothing, let other imports proceed.
+ }
+ break;
+ }
+ case 0x54444154: //TDAT -> xmp:CreateDate.month and day
+ {
+ try { // Don't let wrong dates in id3 stop import.
+ // only if no TDRC has been found before
+ //&& must have the format DDMM
+ if ( !hasTDRC && utf8string.length() == 4 )
+ {
+ newDateTime.day = SXMPUtils::ConvertToInt(utf8string.substr(0,2));
+ newDateTime.month = SXMPUtils::ConvertToInt( utf8string.substr(2,2));
+ newDateTime.hasDate = true;
+ }
+ } catch ( ... ) {
+ // Do nothing, let other imports proceed.
+ }
+ break;
+ }
+ case 0x54494D45: //TIME -> xmp:CreateDate.hours and minutes
+ {
+ try { // Don't let wrong dates in id3 stop import.
+ // only if no TDRC has been found before
+ // && must have the format HHMM
+ if ( !hasTDRC && utf8string.length() == 4 )
+ {
+ newDateTime.hour = SXMPUtils::ConvertToInt(utf8string.substr(0,2));
+ newDateTime.minute = SXMPUtils::ConvertToInt( utf8string.substr(2,2));
+ newDateTime.hasTime = true;
+ }
+ } catch ( ... ) {
+ // Do nothing, let other imports proceed.
+ }
+ break;
+ }
+ case 0x54445243: // TDRC -> xmp:CreateDate //id3 v2.4
+ {
+ try { // Don't let wrong dates in id3 stop import.
+ hasTDRC = true;
+ // This always wins over TYER, TDAT and TIME
+ SXMPUtils::ConvertToDate( utf8string, &newDateTime );
+ } catch ( ... ) {
+ // Do nothing, let other imports proceed.
+ }
+ break;
+ }
+ default:
+ // NB: COMM/USLT need no special fork regarding language alternatives/multiple occurence.
+ // relevant code forks are in ID3_Support::getFrameValue()
+ this->xmpObj.SetProperty( reconProps[r].ns, reconProps[r].prop, utf8string );
+ break;
+ }//switch
+ } //for iterator
+ }//for reconProps
+
+ // import DateTime
+ XMP_DateTime oldDateTime;
+ xmpObj.GetProperty_Date( kXMP_NS_XMP, "CreateDate", &oldDateTime, 0 );
- // ! Note that LoadPropertyFromID3 sets this->containsXMP, update this->processedXMP after!
- LoadPropertyFromID3 ( fileRef, mp3TitleChunk, kXMP_NS_DC, kTitle, true );
- LoadPropertyFromID3 ( fileRef, mp3CreateDateChunk3, kXMP_NS_XMP, kCreateDate );
- LoadPropertyFromID3 ( fileRef, mp3ArtistChunk, kXMP_NS_DM, kArtist );
- LoadPropertyFromID3 ( fileRef, mp3AlbumChunk, kXMP_NS_DM, kAlbum );
- LoadPropertyFromID3 ( fileRef, mp3GenreChunk, kXMP_NS_DM, kGenre );
- LoadPropertyFromID3 ( fileRef, mp3CommentChunk, kXMP_NS_DM, kLogComment );
- LoadPropertyFromID3 ( fileRef, mp3TrackChunk, kXMP_NS_DM, kTrack );
+
+ // NOTE: no further validation nessesary the function "SetProperty_Date" will care about validating date and time
+ // any exception will be caught and block import
+ try {
+ // invalid year will be catched and blocks import
+ XMP_Validate( (newDateTime.year > 0 && newDateTime.year < 9999), "", kXMPErr_BadParam );
+
+ // 2. if year has changed --> everything (date/time) has changed --> overwrite old DateTime with new DateTime
+ if ( ( newDateTime.year != oldDateTime.year ) || // year has changed?
+ // or has same year but new day/month (checking existance month indicates both (day and month) in our case)
+ (( newDateTime.month != 0 ) && ( newDateTime.day != oldDateTime.day || newDateTime.month != oldDateTime.month )) ||
+ // or has same year and same date but different time
+ ( newDateTime.hasTime && ( newDateTime.hour != oldDateTime.minute || newDateTime.hour != oldDateTime.minute )) )
+ {
+ this->xmpObj.SetProperty_Date( kXMP_NS_XMP, "CreateDate", newDateTime );
+ } // ..else: keep old dateTime to don't loose data
+
+ } catch ( ... ) {
+ // Dont import invalid dates from ID3
}
-
- this->processedXMP = this->containsXMP;
-} // MP3_MetaHandler::CacheFileData
-// =================================================================================================
+ // very important to avoid multiple runs! (in which case I'd need to clean certain
+ // fields (i.e. regarding ->active setting)
+ this->processedXMP = true;
-bool MP3_MetaHandler::LoadPropertyFromID3 ( LFA_FileRef inFileRef, char * strFrame, char * strNameSpace, char * strXMPTag, bool fLocalText )
-{
+} // MP3_MetaHandler::ProcessXMP
- // Allocate the temp buffer for the native frames we have to overwrite
- unsigned long bufferSize = TAG_MAX_SIZE;
- std::string buffer;
- buffer.reserve ( bufferSize );
- buffer.assign ( bufferSize, '\0' );
-
- // Get the old XMP tag
- std::string xmpString("");
- if ( fLocalText ) {
- this->xmpObj.GetLocalizedText ( strNameSpace, strXMPTag, "", "x-default", 0, &xmpString, 0 );
- } else {
- this->xmpObj.GetProperty ( strNameSpace, strXMPTag, &xmpString, 0 );
- }
- // Get the frame
- bool ok = ID3_Support::GetFrameData ( inFileRef, strFrame, (char*)buffer.c_str(), bufferSize );
- if ( ok ) {
- if ( ! buffer.empty() ) {
+// =================================================================================================
+// MP3_MetaHandler::UpdateFile
+// ===========================
+void MP3_MetaHandler::UpdateFile ( bool doSafeUpdate )
+{
+ if ( doSafeUpdate )
+ XMP_Throw ( "UCF_MetaHandler::UpdateFile: Safe update not supported", kXMPErr_Unavailable );
+
+ LFA_FileRef file ( this->parent->fileRef );
+
+ // leave 2.3 resp. 2.4 header, since we want to let alone
+ // and don't know enough about the encoding of unrelated frames...
+ XMP_Assert( this->containsXMP );
+
+ tagIsDirty = false;
+ mustShift = false;
+
+ // write out native properties:
+ // * update existing ones
+ // * create new frames as needed
+ // * delete frames if property is gone!
+ // see what there is to do for us:
+
+ // RECON LOOP START
+ for (int r=0; reconProps[r].frameID != 0; r++)
+ {
+ std::string value;
+ bool needDescriptor = false;
+ bool need16LE = true;
+ bool needEncodingByte = true;
+
+ XMP_Uns32 frameID = GetUns32BE( reconProps[r].frameID ); // the would-be frame
+ ID3v2Frame* frame = framesMap[ frameID ]; // the actual frame (if already existing)
+
+ // get XMP property
+ // * honour specific exceptions
+ // * leave value empty() if it doesn't exist ==> frame must be delete/not created
+ switch( frameID )
+ {
+ case 0x54434D50: // TCMP if exists: part of compilation
+ need16LE = false;
+ if ( xmpObj.GetProperty( kXMP_NS_DM, "partOfCompilation", &value, 0 )
+ && ( 0 == stricmp( value.c_str(), "true" ) ))
+ value = "1"; // set a TCMP frame of value 1
+ else
+ value.erase(); // delete/prevent creation of frame
+ break;
+
+ case 0x54495432: // TIT2 -> title["x-default"]
+ case 0x54434F50: // TCOP -> rights["x-default"]
+ if (! xmpObj.GetLocalizedText( reconProps[r].ns, reconProps[r].prop, "", "x-default", 0, &value, 0 )) //jaja, side effect.
+ value.erase(); // if not, erase string.
+ break;
+ case 0x54434F4E: // TCON -> genre
+ {
+ if (! xmpObj.GetProperty( reconProps[r].ns, reconProps[r].prop, &value, 0 ))
+ { // nothing found? -> Erase string. (Leads to Unset below)
+ value.erase();
+ break;
+ }
+ // genre: we need to get the number back, if possible
+ XMP_Int16 iFound = -1; // flag as "not found"
+ for ( int i=0; i < 127; ++i ) {
+ if ( (value.size() == strlen(Genres[i]))
+ && (stricmp( value.c_str(), Genres[i] ) == 0) ) //fixing stricmp buggy on PPC
+ {
+ iFound = i; // Found
+ break;
+ }
+ }
+ if ( iFound == -1 ) // not found known numeric genre?
+ break; // stick with the literal value (also for v2.3, since this is common practice!)
- if ( xmpString.compare ( buffer ) ) {
- if ( fLocalText ) {
- this->xmpObj.SetLocalizedText ( strNameSpace, strXMPTag, 0, "x-default", buffer );
- } else {
- this->xmpObj.SetProperty ( strNameSpace, strXMPTag, buffer, 0 );
+ need16LE = false; // no unicode need for (##)
+ char strGenre[64];
+ snprintf ( strGenre, sizeof(strGenre), "(%d)", iFound ); // AUDIT: Using sizeof(strGenre) is safe.
+ value.assign(strGenre);
+ }
+ break;
+ case 0x434F4D4D: // COMM
+ case 0x55534C54: // USLT, both need descriptor.
+ needDescriptor = true;
+ if (! xmpObj.GetProperty( reconProps[r].ns, reconProps[r].prop, &value, 0 ))
+ value.erase();
+ break;
+ case 0x54594552: //TYER
+ case 0x54444154: //TDAT
+ case 0x54494D45: //TIME
+ {
+ if ( majorVersion <= 3 ) // TYER, TIME and TDAT depricated since v. 2.4 -> else use TDRC
+ {
+ XMP_DateTime dateTime;
+ if (! xmpObj.GetProperty_Date( reconProps[r].ns, reconProps[r].prop, &dateTime, 0 ))
+ { // nothing found? -> Erase string. (Leads to Unset below)
+ value.erase();
+ break;
+ }
+
+ // TYER
+ if ( frameID == 0x54594552 )
+ {
+ XMP_Validate( dateTime.year <= 9999 && dateTime.year > 0 , "Year is out of range", kXMPErr_BadParam);
+ // get only Year!
+ SXMPUtils::ConvertFromInt( dateTime.year, "", &value );
+ break;
+ }
+ // TDAT
+ else if ( frameID == 0x54444154 && dateTime.hasDate ) // date validation made by "GetProperty_Date"
+ {
+ std::string day, month;
+ SXMPUtils::ConvertFromInt( dateTime.day, "", &day );
+ SXMPUtils::ConvertFromInt( dateTime.month, "", &month );
+ if ( dateTime.day < 10 )
+ value = "0";
+ value += day;
+ if ( dateTime.month < 10 )
+ value += "0";
+ value += month;
+ break;
+ }
+ // TIME
+ else if ( frameID == 0x54494D45 && dateTime.hasTime ) // time validation made by "GetProperty_Date" )
+ {
+ std::string hour, minute;
+ SXMPUtils::ConvertFromInt( dateTime.hour, "", &hour );
+ SXMPUtils::ConvertFromInt( dateTime.minute, "", &minute );
+ if ( dateTime.hour < 10 )
+ value = "0";
+ value += hour;
+ if ( dateTime.minute < 10 )
+ value += "0";
+ value += minute;
+ break;
+ }
+ else
+ {
+ value.erase();
+ break;
+ }
}
+ else // v.2.4 --> delete TYER,TIME or TDAT & write into TDRC
+ {
+ value.erase();
+ break;
+ }
+ }
+ case 0x54445243: //TDRC (only v2.4)
+ {
+ // only export for id3 > v2.4
+ if ( majorVersion > 3 ) // (only v2.4)
+ {
+ if (! xmpObj.GetProperty( reconProps[r].ns, reconProps[r].prop, &value, 0 ))
+ value.erase();
+ }
+ break;
}
+ break;
+ case 0x57434F50: //WCOP
+ needEncodingByte = false;
+ need16LE = false;
+ if (! xmpObj.GetProperty( reconProps[r].ns, reconProps[r].prop, &value, 0 ))
+ value.erase(); // if not, erase string
+ break;
+ case 0x5452434B: // TRCK
+ case 0x54504F53: // TPOS
+ need16LE = false;
+ // no break, go on:
+ default:
+ if (! xmpObj.GetProperty( reconProps[r].ns, reconProps[r].prop, &value, 0 ))
+ value.erase(); // if not, erase string
+ break;
+ }
- this->containsXMP = true;
- return true;
+ // [XMP exist] x [frame exist] => four cases:
+ // 1/4) nothing before, nothing now
+ if ( value.empty() && (frame==0))
+ continue; // nothing to do
+
+ // all else means there will be rewrite work to do:
+ tagIsDirty = true;
+ // 2/4) value before, now gone:
+ if ( value.empty() && (frame!=0))
+ {
+ frame->active = false; //mark for non-use
+ continue;
+ }
+ // 3/4) no old value, create new value
+ if ( frame==0)
+ {
+ ID3v2Frame* newFrame=new ID3v2Frame( frameID );
+ newFrame->setFrameValue( value, needDescriptor, need16LE, false, needEncodingByte ); //always write as utf16-le incl. BOM
+ framesVector.push_back( newFrame );
+ framesMap[ frameID ] = newFrame;
+ continue;
}
+ // 4/4) change existing value
+ else // resp. change frame
+ {
+ frame->setFrameValue( value, needDescriptor, need16LE, false, needEncodingByte );
+ }
+ } // RECON LOOP END
+
+ /////////////////////////////////////////////////////////////////////////////////
+ // (Re)Build XMP frame:
+ ID3v2Frame* frame = framesMap[ XMP_FRAME_ID ];
+ if ( frame == 0)
+ {
+ ID3v2Frame* newFrame=new ID3v2Frame( XMP_FRAME_ID );
+ newFrame->setFrameValue( this->xmpPacket, false, false, true );
+ framesVector.push_back( newFrame );
+ framesMap[ XMP_FRAME_ID ] = newFrame;
+ } else
+ frame->setFrameValue( this->xmpPacket, false, false, true );
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // Decision making
+ newFramesSize = 0;
+ for ( XMP_Uns32 i=0; i < framesVector.size(); i++)
+ {
+ if (framesVector[i]->active)
+ newFramesSize += (10 + framesVector[i]->contentSize );
}
- // Couldn't find the frame, let's clean it on the XMP side if that tag exists.
- if ( xmpString.size() != 0 ) {
-
- buffer = "";
+ mustShift = ( newFramesSize > (oldTagSize - 10)) ||
+ //optimization: If more than 8K can be saved by rewriting the MP3, go do it:
+ ((newFramesSize + 8*1024) < oldTagSize );
+
+ if (!mustShift) // fill what we got
+ newTagSize = oldTagSize;
+ else // if need to shift anyway, get some nice 2K padding
+ newTagSize = newFramesSize + 2048 + 10;
+ newPadding = newTagSize -10 - newFramesSize;
+
+ // shifting needed? -> shift
+ if ( mustShift )
+ {
+ XMP_Int64 filesize = LFA_Measure( file );
+ if ( this->hasID3Tag )
+ LFA_Move( file, oldTagSize, file, newTagSize , filesize - oldTagSize ); //fix [2338569]
+ else
+ LFA_Move( file, 0, file, newTagSize, filesize ); // move entire file up.
+ }
- if ( fLocalText ) {
- this->xmpObj.SetLocalizedText ( strNameSpace, strXMPTag, 0, "x-default", buffer );
- } else {
- this->xmpObj.SetProperty ( strNameSpace, strXMPTag, buffer, 0 );
- }
+ // correct size stuff, write out header
+ LFA_Rewind( file );
+ id3Header.write( file, newTagSize);
- this->containsXMP = true;
- return true;
+ // write out tags
+ for ( XMP_Uns32 i=0; i < framesVector.size(); i++)
+ {
+ if ( framesVector[i]->active)
+ framesVector[i]->write(file, majorVersion);
+ }
+ // write out padding:
+ for ( XMP_Int64 i = newPadding; i > 0;)
+ {
+ const XMP_Uns64 zero = 0;
+ if ( i >= 8 ) // worthwhile optimization
+ {
+ LFA_Write( file, &zero, 8 );
+ i -= 8;
+ continue;
+ }
+ LFA_Write( file, &zero, 1 );
+ i--;
}
- return false;
+ // check end of file for ID3v1 tag
+ XMP_Int64 possibleTruncationPoint = LFA_Seek( file, -128, SEEK_END);
+ bool alreadyHasID3v1 = (LFA_ReadInt32_BE( file ) & 0xFFFFFF00) == 0x54414700; // "TAG"
+ if ( ! alreadyHasID3v1 ) // extend file
+ LFA_Extend( file, LFA_Measure( file ) + 128 );
+ id3v1Tag.write( file, &this->xmpObj );
-} // WAV_MetaHandler::LoadPropertyFromID3
+ this->needsUpdate = false; //do last for safety reasons
+} // MP3_MetaHandler::UpdateFile
// =================================================================================================
+// MP3_MetaHandler::WriteFile
+// ==========================
-#endif // XMP_UNIXBuild
+void MP3_MetaHandler::WriteFile ( LFA_FileRef sourceRef,
+ const std::string & sourcePath )
+{
+ IgnoreParam(sourceRef); IgnoreParam(sourcePath);
+ XMP_Throw ( "MP3_MetaHandler::WriteFile: Not supported", kXMPErr_Unimplemented );
+} // MP3_MetaHandler::WriteFile
diff --git a/source/XMPFiles/FileHandlers/MP3_Handler.hpp b/source/XMPFiles/FileHandlers/MP3_Handler.hpp
index 8a321e6..d6be9cf 100644
--- a/source/XMPFiles/FileHandlers/MP3_Handler.hpp
+++ b/source/XMPFiles/FileHandlers/MP3_Handler.hpp
@@ -3,7 +3,7 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2007 Adobe Systems Incorporated
+// Copyright 2008 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -11,17 +11,11 @@
// =================================================================================================
#include "XMP_Environment.h" // ! This must be the first include.
-#if ! XMP_UNIXBuild // Closes at very bottom. Disabled on UNIX until legacy-as-local is fixed.
-
#include "XMPFiles_Impl.hpp"
+#include "ID3_Support.hpp"
-// =================================================================================================
-/// \file MP3_Handler.hpp
-/// \brief File format handler for MP3.
-///
-/// This header ...
-///
-// =================================================================================================
+using namespace std;
+using namespace ID3_Support;
extern XMPFileHandler * MP3_MetaHandlerCTor ( XMPFiles * parent );
@@ -34,13 +28,12 @@ static const XMP_OptionBits kMP3_HandlerFlags = (kXMPFiles_CanInjectXMP |
kXMPFiles_CanExpand |
kXMPFiles_PrefersInPlace |
kXMPFiles_AllowsOnlyXMP |
- kXMPFiles_ReturnsRawPacket);
- // In the future, we'll add kXMPFiles_CanReconcile
+ kXMPFiles_ReturnsRawPacket|
+ kXMPFiles_CanReconcile);
class MP3_MetaHandler : public XMPFileHandler
{
public:
-
MP3_MetaHandler ( XMPFiles * parent );
~MP3_MetaHandler();
@@ -48,13 +41,52 @@ public:
void UpdateFile ( bool doSafeUpdate );
void WriteFile ( LFA_FileRef sourceRef, const std::string & sourcePath );
+ void ProcessXMP();
private:
- bool LoadPropertyFromID3(LFA_FileRef inFileRef, char *strFrame, char *strNameSpace, char *strXMPTag, bool fLocalText = false);
+ ////////////////////////////////////////////////////////////////////////////////////
+ // instance vars
+ XMP_Int64 oldTagSize; // the entire tag, including padding, including 10B header
+ XMP_Int64 oldPadding; // number of padding bytes
+ XMP_Int64 oldFramesSize; // actual space needed by frames := oldTagSize - 10 - oldPadding
+
+ XMP_Int64 newTagSize;
+ XMP_Int64 newPadding;
+ XMP_Int64 newFramesSize;
+
+ // decision making:
+ bool tagIsDirty; // true, if any legacy properties changed.
+ bool mustShift; // entire tag to rewrite? (or possibly just XMP?)
+
+
+ XMP_Uns8 majorVersion, minorVersion; // Version Number post ID3v2, i.e. 3 0 ==> ID3v2.3.0
+ bool hasID3Tag; //incoming file has an ID3 tag?
+ bool hasFooter;
+ bool legacyChanged; // tag rewrite certainly needed?
+
+ ID3Header id3Header;
+
+ XMP_Int64 extHeaderSize;
+ bool hasExtHeader;
+
+ // the frames
+ // * all to be kept till write-out
+ // * parsed/understood only if needed
+ // * vector used to free memory in handler destructor
+ std::vector<ID3_Support::ID3v2Frame*> framesVector;
+
+ // ID3v1 - treated as a single object
+ ID3v1Tag id3v1Tag;
+
+ // * also kept in a map for better import<->export access
+ // * only keeps legacy 'relevant frames' (i.e. no abused COMM frames)
+ // * only keeps last relevant frame
+ // * earlier 'relevant frames' will be deleted. This map also helps in this
+ // * key shall be the FrameID, always interpreted as BE
+ std::map<XMP_Uns32,ID3_Support::ID3v2Frame*> framesMap;
}; // MP3_MetaHandler
// =================================================================================================
-#endif // XMP_UNIXBuild
#endif /* __MP3_Handler_hpp__ */
diff --git a/source/XMPFiles/FileHandlers/MPEG2_Handler.cpp b/source/XMPFiles/FileHandlers/MPEG2_Handler.cpp
index 9c2eb48..438e705 100644
--- a/source/XMPFiles/FileHandlers/MPEG2_Handler.cpp
+++ b/source/XMPFiles/FileHandlers/MPEG2_Handler.cpp
@@ -1,6 +1,6 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2007 Adobe Systems Incorporated
+// Copyright 2005 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
diff --git a/source/XMPFiles/FileHandlers/MPEG2_Handler.hpp b/source/XMPFiles/FileHandlers/MPEG2_Handler.hpp
index 8bb0464..96ef407 100644
--- a/source/XMPFiles/FileHandlers/MPEG2_Handler.hpp
+++ b/source/XMPFiles/FileHandlers/MPEG2_Handler.hpp
@@ -3,7 +3,7 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2007 Adobe Systems Incorporated
+// Copyright 2005 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
diff --git a/source/XMPFiles/FileHandlers/MPEG4_Handler.cpp b/source/XMPFiles/FileHandlers/MPEG4_Handler.cpp
index a3f92d6..06400d1 100644
--- a/source/XMPFiles/FileHandlers/MPEG4_Handler.cpp
+++ b/source/XMPFiles/FileHandlers/MPEG4_Handler.cpp
@@ -1,6 +1,6 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2007 Adobe Systems Incorporated
+// Copyright 2006 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -8,6 +8,8 @@
// =================================================================================================
#include "MPEG4_Handler.hpp"
+#include "ISOBaseMedia_Support.hpp"
+#include "MOOV_Support.hpp"
#include "UnicodeConversions.hpp"
#include "MD5.h"
@@ -26,36 +28,46 @@ using namespace std;
///
// =================================================================================================
-// File and box type codes as big endian 32-bit integers, allows faster comparisons.
+// The basic content of a timecode sample description table entry. Does not include trailing boxes.
-static XMP_Uns32 kBE_ftyp = MakeUns32BE ( 0x66747970UL ); // File header Box, no version/flags.
+#pragma pack ( push, 1 )
-static XMP_Uns32 kBE_mp41 = MakeUns32BE ( 0x6D703431UL ); // Compatibility codes
-static XMP_Uns32 kBE_mp42 = MakeUns32BE ( 0x6D703432UL );
-static XMP_Uns32 kBE_f4v = MakeUns32BE ( 0x66347620UL );
+struct stsdBasicEntry {
+ XMP_Uns32 entrySize;
+ XMP_Uns32 format;
+ XMP_Uns8 reserved_1 [6];
+ XMP_Uns16 dataRefIndex;
+ XMP_Uns32 reserved_2;
+ XMP_Uns32 flags;
+ XMP_Uns32 timeScale;
+ XMP_Uns32 frameDuration;
+ XMP_Uns8 frameCount;
+ XMP_Uns8 reserved_3;
+};
-static XMP_Uns32 kBE_moov = MakeUns32BE ( 0x6D6F6F76UL ); // Container Box, no version/flags.
-static XMP_Uns32 kBE_mvhd = MakeUns32BE ( 0x6D766864UL ); // Data FullBox, has version/flags.
-static XMP_Uns32 kBE_udta = MakeUns32BE ( 0x75647461UL ); // Container Box, no version/flags.
-static XMP_Uns32 kBE_cprt = MakeUns32BE ( 0x63707274UL ); // Data FullBox, has version/flags.
-static XMP_Uns32 kBE_uuid = MakeUns32BE ( 0x75756964UL ); // Data Box, no version/flags.
-static XMP_Uns32 kBE_free = MakeUns32BE ( 0x66726565UL ); // Free space Box, no version/flags.
-static XMP_Uns32 kBE_mdat = MakeUns32BE ( 0x6D646174UL ); // Media data Box, no version/flags.
+#pragma pack ( pop )
-static XMP_Uns32 kBE_xmpUUID [4] = { MakeUns32BE ( 0xBE7ACFCBUL ),
- MakeUns32BE ( 0x97A942E8UL ),
- MakeUns32BE ( 0x9C719994UL ),
- MakeUns32BE ( 0x91E3AFACUL ) };
+// =================================================================================================
// ! The buffer and constants are both already big endian.
#define Get4CharCode(buffPtr) (*((XMP_Uns32*)(buffPtr)))
// =================================================================================================
+static inline bool IsClassicQuickTimeBox ( XMP_Uns32 boxType )
+{
+ if ( (boxType == ISOMedia::k_moov) || (boxType == ISOMedia::k_mdat) || (boxType == ISOMedia::k_pnot) ||
+ (boxType == ISOMedia::k_free) || (boxType == ISOMedia::k_skip) || (boxType == ISOMedia::k_wide) ) return true;
+ return false;
+} // IsClassicQuickTimeBox
+
+// =================================================================================================
+
// Pairs of 3 letter ISO 639-2 codes mapped to 2 letter ISO 639-1 codes from:
// http://www.loc.gov/standards/iso639-2/php/code_list.php
// Would have to write an "==" operator to use std::map, must compare values not pointers.
// ! Not fully sorted, do not use a binary search.
+
static XMP_StringPtr kKnownLangs[] =
{ "aar", "aa", "abk", "ab", "afr", "af", "aka", "ak", "alb", "sq", "sqi", "sq", "amh", "am",
"ara", "ar", "arg", "an", "arm", "hy", "hye", "hy", "asm", "as", "ava", "av", "ave", "ae",
@@ -92,91 +104,161 @@ static XMP_StringPtr kKnownLangs[] =
"tib", "bo", "bod", "bo", "tir", "ti", "ton", "to", "tsn", "tn", "tso", "ts", "tuk", "tk",
"tur", "tr", "twi", "tw", "uig", "ug", "ukr", "uk", "urd", "ur", "uzb", "uz", "ven", "ve",
"vie", "vi", "vol", "vo", "wel", "cy", "cym", "cy", "wln", "wa", "wol", "wo", "xho", "xh",
- "yid", "yi", "yor", "yo", "zha", "za", "zho", "zh", "chi", "zh", "zul", "zu",
+ "yid", "yi", "yor", "yo", "zha", "za", "zho", "zh", "chi", "zh", "zul", "zu",
0, 0 };
-static XMP_StringPtr Lookup2LetterLang ( XMP_StringPtr lang3 )
+static inline XMP_StringPtr Lookup2LetterLang ( XMP_StringPtr lang3 )
{
-
for ( size_t i = 0; kKnownLangs[i] != 0; i += 2 ) {
if ( XMP_LitMatch ( lang3, kKnownLangs[i] ) ) return kKnownLangs[i+1];
}
-
- return lang3; // Not found, return the input.
-
-} // Lookup2LetterLang
+ return "";
+}
+
+static inline XMP_StringPtr Lookup3LetterLang ( XMP_StringPtr lang2 )
+{
+ for ( size_t i = 0; kKnownLangs[i] != 0; i += 2 ) {
+ if ( XMP_LitMatch ( lang2, kKnownLangs[i+1] ) ) return kKnownLangs[i];
+ }
+ return "";
+}
// =================================================================================================
// MPEG4_CheckFormat
// =================
//
-// An MPEG-4 file is an instance of an ISO Base Media file, ISO 14496-12 and -14. An MPEG-4 file
-// must begin with an 'ftyp' box containing 'mp41', 'mp42', or 'f4v ' in the compatible brands.
+// There are 3 variations of recognized file:
+// - Normal MPEG-4 - has an 'ftyp' box containing a known compatible brand but not 'qt '.
+// - Modern QuickTime - has an 'ftyp' box containing 'qt ' as a compatible brand.
+// - Classic QuickTime - has no 'ftyp' box, should have recognized top level boxes.
+//
+// An MPEG-4 or modern QuickTime file is an instance of an ISO Base Media file, ISO 14496-12 and -14.
+// A classic QuickTime file has the same physical box structure, but somewhat different box types.
+// The ISO files must begin with an 'ftyp' box containing 'mp41', 'mp42', 'f4v ', or 'qt ' in the
+// compatible brands.
+//
+// The general box structure is:
//
-// The 'ftyp' box structure is:
+// 0 4 uns32 box size, 0 means "to EoF", 1 means 64-bit size follows
+// 4 4 uns32 box type
+// 8 8 uns64 box size, present only if uns32 size is 1
+// - * box content
+//
+// The 'ftyp' box content is:
//
-// 0 4 uns32 box size, 0 means "to EoF"
-// 4 4 uns32 box type, 'ftyp'
-// 8 8 uns64 box size, if uns32 size is 1
// - 4 uns32 major brand
// - 4 uns32 minor version
// - * uns32 sequence of compatible brands, to the end of the box
-//
-// All integers are big endian.
+
+// ! With the addition of QuickTime support there is some change in behavior in OpenFile when the
+// ! kXMPFiles_OpenStrictly option is used wth a specific file format. The kXMP_MPEG4File and
+// ! kXMP_MOVFile formats are treated uniformly, you can't force "real MOV" or "real MPEG-4". You
+// ! can check afterwards using GetFileInfo to see what the file happens to be.
bool MPEG4_CheckFormat ( XMP_FileFormat format,
XMP_StringPtr filePath,
LFA_FileRef fileRef,
XMPFiles* parent )
{
- XMP_Uns8 buffer [4096];
- XMP_Uns32 ioCount, brandCount, brandOffset, id;
- XMP_Uns64 fileSize, boxSize;
-
- // Do some basic header checks, figure out how many compatible brands are listed.
-
+ XMP_Uns8 buffer [4*1024];
+ XMP_Uns32 ioCount, brandCount, brandOffset;
+ XMP_Uns64 fileSize, nextOffset;
+ ISOMedia::BoxInfo currBox;
+
+ #define IsTolerableBoxChar(ch) ( ((0x20 <= (ch)) && ((ch) <= 0x7E)) || ((ch) == 0xA9) )
+
+ XMP_AbortProc abortProc = parent->abortProc;
+ void * abortArg = parent->abortArg;
+ const bool checkAbort = (abortProc != 0);
+
+ bool openStrictly = XMP_OptionIsSet ( parent->openFlags, kXMPFiles_OpenStrictly);
+
+ // Get the first box's info, see if it is 'ftyp' or not.
+
+ XMP_Assert ( (parent->tempPtr == 0) && (parent->tempUI32 == 0) );
+
fileSize = LFA_Measure ( fileRef );
-
- LFA_Seek ( fileRef, 0, SEEK_SET );
- ioCount = LFA_Read ( fileRef, buffer, 16 ); // Read to the compatible brands, assuming a 32-bit size.
- if ( ioCount < 16 ) return false;
+ nextOffset = ISOMedia::GetBoxInfo ( fileRef, 0, fileSize, &currBox );
+ if ( currBox.headerSize < 8 ) return false; // Can't be an ISO or QuickTime file.
- id = Get4CharCode ( &buffer[4] ); // Is the first box an 'ftyp' box?
- if ( id != kBE_ftyp ) return false;
-
- boxSize = GetUns32BE ( &buffer[0] ); // Get the box length.
- brandOffset = 16;
+ if ( currBox.boxType == ISOMedia::k_ftyp ) {
- if ( boxSize == 0 ) {
- boxSize = fileSize;
- } else if ( boxSize == 1 ) {
- boxSize = GetUns64BE ( &buffer[8] );
- LFA_Seek ( fileRef, 24, SEEK_SET ); // Seek to the compatible brands.
- brandOffset = 24;
- }
-
- if ( (boxSize < brandOffset) || (boxSize > fileSize) ||
- ((boxSize & 0x3) != 0) || (boxSize > 4024) ) return false; // Sanity limit of 1000 brands.
+ // Have an 'ftyp' box, look through the compatible brands. If 'qt ' is present then this is
+ // a modern QuickTime file, regardless of what else is found. Otherwise this is plain ISO if
+ // any of the other recognized brands are found.
+
+ if ( currBox.contentSize < 12 ) return false; // No compatible brands at all.
+ if ( currBox.contentSize > 1024*1024 ) return false; // Sanity check and make sure count fits in 32 bits.
+ brandCount = ((XMP_Uns32)currBox.contentSize - 8) >> 2;
+
+ LFA_Seek ( fileRef, 8, SEEK_CUR ); // Skip the major and minor brands.
+ ioCount = brandOffset = 0;
+
+ bool haveCompatibleBrand = false;
+
+ for ( ; brandCount > 0; --brandCount, brandOffset += 4 ) {
+
+ if ( brandOffset >= ioCount ) {
+ if ( checkAbort && abortProc(abortArg) ) {
+ XMP_Throw ( "MPEG4_CheckFormat - User abort", kXMPErr_UserAbort );
+ }
+ ioCount = 4 * brandCount;
+ if ( ioCount > sizeof(buffer) ) ioCount = sizeof(buffer);
+ ioCount = LFA_Read ( fileRef, buffer, ioCount, kLFA_RequireAll );
+ brandOffset = 0;
+ }
+
+ XMP_Uns32 brand = GetUns32BE ( &buffer[brandOffset] );
+ if ( brand == ISOMedia::k_qt ) { // Don't need to look further.
+ if ( openStrictly && (format != kXMP_MOVFile) ) return false;
+ parent->format = kXMP_MOVFile;
+ parent->tempUI32 = MOOV_Manager::kFileIsModernQT;
+ return true;
+ } else if ( (brand == ISOMedia::k_mp41) || (brand == ISOMedia::k_mp42) || (brand == ISOMedia::k_f4v) ) {
+ haveCompatibleBrand = true; // Need to keep looking in case 'qt ' follows.
+ }
- // Look for the 'mp41', 'mp42', of 'f4v ' compatible brands.
-
- brandCount = (XMP_Uns32)( (boxSize - brandOffset) / 4 );
- while ( brandCount > 0 ) {
-
- XMP_Uns32 clumpSize = brandCount * 4;
- if ( clumpSize > sizeof(buffer) ) clumpSize = sizeof(buffer);
- ioCount = LFA_Read ( fileRef, buffer, clumpSize );
- if ( ioCount < clumpSize ) return false;
-
- for ( XMP_Uns32 i = 0; i < clumpSize; i += 4 ) {
- id = Get4CharCode ( &buffer[i] );
- if ( (id == kBE_mp41) || (id == kBE_mp42) || (id == kBE_f4v) ) return true;
}
-
- brandCount -= clumpSize/4;
+
+ if ( ! haveCompatibleBrand ) return false;
+ if ( openStrictly && (format != kXMP_MPEG4File) ) return false;
+ parent->format = kXMP_MPEG4File;
+ parent->tempUI32 = MOOV_Manager::kFileIsNormalISO;
+ return true;
+
+ } else {
+
+ // No 'ftyp', look for classic QuickTime: 'moov', 'mdat', 'pnot', 'free', 'skip', and 'wide'.
+ // As an expedient, quit when a 'moov' box is found. Tolerate other boxes, they are in the
+ // wild for ill-formed files, e.g. seen when 'moov'/'udta' children get left at top level.
+
+ while ( currBox.boxType != ISOMedia::k_moov ) {
+
+ if ( ! IsClassicQuickTimeBox ( currBox.boxType ) ) {
+ // Make sure the box type is 4 ASCII characters or 0xA9 (MacRoman copyright).
+ XMP_Uns8 b1 = (XMP_Uns8) (currBox.boxType >> 24);
+ XMP_Uns8 b2 = (XMP_Uns8) ((currBox.boxType >> 16) & 0xFF);
+ XMP_Uns8 b3 = (XMP_Uns8) ((currBox.boxType >> 8) & 0xFF);
+ XMP_Uns8 b4 = (XMP_Uns8) (currBox.boxType & 0xFF);
+ bool ok = IsTolerableBoxChar(b1) && IsTolerableBoxChar(b2) &&
+ IsTolerableBoxChar(b3) && IsTolerableBoxChar(b4);
+ if ( ! ok ) return false;
+ }
+ if ( nextOffset >= fileSize ) return false;
+ if ( checkAbort && abortProc(abortArg) ) {
+ XMP_Throw ( "MPEG4_CheckFormat - User abort", kXMPErr_UserAbort );
+ }
+ nextOffset = ISOMedia::GetBoxInfo ( fileRef, nextOffset, fileSize, &currBox );
+
+ }
+
+ if ( openStrictly && (format != kXMP_MOVFile) ) return false;
+ parent->format = kXMP_MOVFile;
+ parent->tempUI32 = MOOV_Manager::kFileIsTraditionalQT;
+ return true;
}
-
+
return false;
} // MPEG4_CheckFormat
@@ -196,12 +278,15 @@ XMPFileHandler * MPEG4_MetaHandlerCTor ( XMPFiles * parent )
// MPEG4_MetaHandler::MPEG4_MetaHandler
// ====================================
-MPEG4_MetaHandler::MPEG4_MetaHandler ( XMPFiles * _parent ) : xmpBoxPos(0)
+MPEG4_MetaHandler::MPEG4_MetaHandler ( XMPFiles * _parent )
+ : fileMode(0), havePreferredXMP(false), xmpBoxPos(0), moovBoxPos(0), xmpBoxSize(0), moovBoxSize(0)
{
this->parent = _parent; // Inherited, can't set in the prefix.
this->handlerFlags = kMPEG4_HandlerFlags;
this->stdCharForm = kXMP_Char8Bit;
+ this->fileMode = (XMP_Uns8)_parent->tempUI32;
+ _parent->tempUI32 = 0;
} // MPEG4_MetaHandler::MPEG4_MetaHandler
@@ -212,675 +297,2137 @@ MPEG4_MetaHandler::MPEG4_MetaHandler ( XMPFiles * _parent ) : xmpBoxPos(0)
MPEG4_MetaHandler::~MPEG4_MetaHandler()
{
- // Nothing to do yet.
+ // Nothing to do.
} // MPEG4_MetaHandler::~MPEG4_MetaHandler
// =================================================================================================
-// GetBoxInfo
-// ==========
-//
-// Seek to the start of a box and extract the type and size, split the size into header and content
-// portions. Leave the file positioned at the first byte of content.
+// SecondsToXMPDate
+// ================
-// ! We're returning the content size, not the raw (full) MPEG-4 box size!
+// *** ASF has similar code with different origin, should make a shared utility.
-static void GetBoxInfo ( LFA_FileRef fileRef, XMP_Uns64 fileSize, XMP_Uns64 boxPos,
- XMP_Uns32 * boxType, XMP_Uns64 * headerSize, XMP_Uns64 * contentSize )
+static void SecondsToXMPDate ( XMP_Uns64 isoSeconds, XMP_DateTime * xmpDate )
{
- XMP_Uns8 buffer [8];
- XMP_Uns32 u32Size;
+ memset ( xmpDate, 0, sizeof(XMP_DateTime) ); // AUDIT: Using sizeof(XMP_DateTime) is safe.
- LFA_Seek ( fileRef, boxPos, SEEK_SET );
- (void) LFA_Read ( fileRef, buffer, 8, kLFA_RequireAll );
+ XMP_Int32 days = (XMP_Int32) (isoSeconds / 86400);
+ isoSeconds -= ((XMP_Uns64)days * 86400);
- u32Size = GetUns32BE ( &buffer[0] );
- *boxType = Get4CharCode ( &buffer[4] );
+ XMP_Int32 hour = (XMP_Int32) (isoSeconds / 3600);
+ isoSeconds -= ((XMP_Uns64)hour * 3600);
- if ( u32Size > 1 ) {
- *headerSize = 8; // Normal explicit size case.
- *contentSize = u32Size - 8;
- } else if ( u32Size == 0 ) {
- *headerSize = 8; // The box goes to EoF.
- *contentSize = fileSize - (boxPos + 8);
- } else {
- XMP_Uns64 u64Size; // Have a 64-bit size.
- (void) LFA_Read ( fileRef, &u64Size, 8, kLFA_RequireAll );
- u64Size = MakeUns64BE ( u64Size );
- *headerSize = 16;
- *contentSize = u64Size - 16;
+ XMP_Int32 minute = (XMP_Int32) (isoSeconds / 60);
+ isoSeconds -= ((XMP_Uns64)minute * 60);
+
+ XMP_Int32 second = (XMP_Int32)isoSeconds;
+
+ xmpDate->year = 1904; // Start with the ISO origin.
+ xmpDate->month = 1;
+ xmpDate->day = 1;
+
+ xmpDate->day += days; // Add in the delta.
+ xmpDate->hour = hour;
+ xmpDate->minute = minute;
+ xmpDate->second = second;
+
+ xmpDate->hasTimeZone = true; // ! Needed for ConvertToUTCTime to do anything.
+ SXMPUtils::ConvertToUTCTime ( xmpDate ); // Normalize the date/time.
+
+} // SecondsToXMPDate
+
+// =================================================================================================
+// XMPDateToSeconds
+// ================
+
+// *** ASF has similar code with different origin, should make a shared utility.
+
+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.
+}
+
+// -------------------------------------------------------------------------------------------------
+
+static XMP_Int32 DaysInMonth ( XMP_Int32 year, XMP_Int32 month )
+{
+ static XMP_Int32 daysInMonth[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+ // Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
+ XMP_Int32 days = daysInMonth[month];
+ if ( (month == 2) && IsLeapYear(year) ) days += 1;
+ return days;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+static void XMPDateToSeconds ( const XMP_DateTime & _xmpDate, XMP_Uns64 * isoSeconds )
+{
+ XMP_DateTime xmpDate = _xmpDate;
+ SXMPUtils::ConvertToUTCTime ( &xmpDate );
+
+ XMP_Uns64 tempSeconds = (XMP_Uns64)xmpDate.second;
+ tempSeconds += (XMP_Uns64)xmpDate.minute * 60;
+ tempSeconds += (XMP_Uns64)xmpDate.hour * 3600;
+
+ XMP_Int32 days = (xmpDate.day - 1);
+
+ --xmpDate.month;
+ while ( xmpDate.month >= 1 ) {
+ days += DaysInMonth ( xmpDate.year, xmpDate.month );
+ --xmpDate.month;
+ }
+
+ --xmpDate.year;
+ while ( xmpDate.year >= 1904 ) {
+ days += (IsLeapYear ( xmpDate.year) ? 366 : 365 );
+ --xmpDate.year;
}
-} // GetBoxInfo
+ tempSeconds += (XMP_Uns64)days * 86400;
+ *isoSeconds = tempSeconds;
+
+} // XMPDateToSeconds
// =================================================================================================
-// MPEG4_MetaHandler::CacheFileData
-// ================================
-//
-// The XMP in MPEG-4 is in a top level XMP 'uuid' box. Legacy metadata might be found in 2 places,
-// the 'moov'/'mvhd' box, and the 'moov'/'udta'/'cprt' boxes. There is at most 1 each of the 'moov',
-// 'mvhd' and 'udta' boxes. There are any number of 'cprt' boxes, including none. The 'udta' box can
-// be large, so don't cache all of it.
+// ImportMVHDItems
+// ===============
-void MPEG4_MetaHandler::CacheFileData()
+static bool ImportMVHDItems ( MOOV_Manager::BoxInfo mvhdInfo, SXMPMeta * xmp )
{
- XMP_Assert ( (! this->containsXMP) && (! this->containsTNail) );
+ XMP_Assert ( mvhdInfo.boxType == ISOMedia::k_mvhd );
+ if ( mvhdInfo.contentSize < 4 ) return false; // Just enough to check the version/flags at first.
- LFA_FileRef fileRef = this->parent->fileRef;
+ XMP_Uns8 mvhdVersion = *mvhdInfo.content;
+ if ( mvhdVersion > 1 ) return false;
+
+ XMP_Uns64 creationTime, modificationTime, duration;
+ XMP_Uns32 timescale;
+
+ if ( mvhdVersion == 0 ) {
+
+ if ( mvhdInfo.contentSize < sizeof ( MOOV_Manager::Content_mvhd_0 ) ) return false;
+ MOOV_Manager::Content_mvhd_0 * mvhdRaw_0 = (MOOV_Manager::Content_mvhd_0*) mvhdInfo.content;
- XMP_AbortProc abortProc = this->parent->abortProc;
- void * abortArg = this->parent->abortArg;
- const bool checkAbort = (abortProc != 0);
+ creationTime = (XMP_Uns64) GetUns32BE ( &mvhdRaw_0->creationTime );
+ modificationTime = (XMP_Uns64) GetUns32BE ( &mvhdRaw_0->modificationTime );
+ timescale = GetUns32BE ( &mvhdRaw_0->timescale );
+ duration = (XMP_Uns64) GetUns32BE ( &mvhdRaw_0->duration );
- XMP_Uns64 fileSize = LFA_Measure ( fileRef );
- XMP_Uns64 outerPos, outerSize, hSize, cSize;
- XMP_Uns32 boxType;
+ } else {
- // The outer loop looks for the top level 'moov' and 'uuid'/XMP boxes.
+ XMP_Assert ( mvhdVersion == 1 );
+ if ( mvhdInfo.contentSize < sizeof ( MOOV_Manager::Content_mvhd_1 ) ) return false;
+ MOOV_Manager::Content_mvhd_1 * mvhdRaw_1 = (MOOV_Manager::Content_mvhd_1*) mvhdInfo.content;
+
+ creationTime = GetUns64BE ( &mvhdRaw_1->creationTime );
+ modificationTime = GetUns64BE ( &mvhdRaw_1->modificationTime );
+ timescale = GetUns32BE ( &mvhdRaw_1->timescale );
+ duration = GetUns64BE ( &mvhdRaw_1->duration );
+
+ }
+
+ bool haveImports = false;
+ XMP_DateTime xmpDate;
+
+ if ( (creationTime >> 32) < 0xFF ) { // Sanity check for bogus date info.
+ SecondsToXMPDate ( creationTime, &xmpDate );
+ xmp->SetProperty_Date ( kXMP_NS_XMP, "CreateDate", xmpDate );
+ haveImports = true;
+ }
+
+ if ( (modificationTime >> 32) < 0xFF ) { // Sanity check for bogus date info.
+ SecondsToXMPDate ( modificationTime, &xmpDate );
+ xmp->SetProperty_Date ( kXMP_NS_XMP, "ModifyDate", xmpDate );
+ haveImports = true;
+ }
+
+ if ( timescale != 0 ) { // Avoid 1/0 for the scale field.
+ char buffer [32]; // A 64-bit number is at most 20 digits.
+ xmp->DeleteProperty ( kXMP_NS_DM, "duration" ); // Delete the whole struct.
+ snprintf ( buffer, sizeof(buffer), "%llu", duration ); // AUDIT: The buffer is big enough.
+ xmp->SetStructField ( kXMP_NS_DM, "duration", kXMP_NS_DM, "value", &buffer[0] );
+ snprintf ( buffer, sizeof(buffer), "1/%u", timescale ); // AUDIT: The buffer is big enough.
+ xmp->SetStructField ( kXMP_NS_DM, "duration", kXMP_NS_DM, "scale", &buffer[0] );
+ haveImports = true;
+ }
+
+ return haveImports;
+
+} // ImportMVHDItems
+
+// =================================================================================================
+// ExportMVHDItems
+// ===============
+
+static void ExportMVHDItems ( const SXMPMeta & xmp, MOOV_Manager * moovMgr )
+{
+ XMP_DateTime xmpDate;
+ bool createFound, modifyFound;
+ XMP_Uns64 createSeconds = 0, modifySeconds = 0;
- bool moovFound = false;
- if ( this->parent->openFlags & kXMPFiles_OpenOnlyXMP ) moovFound = true; // Ignore legacy.
+ MOOV_Manager::BoxInfo mvhdInfo;
+ MOOV_Manager::BoxRef mvhdRef = moovMgr->GetBox ( "moov/mvhd", &mvhdInfo );
+ if ( (mvhdRef == 0) || (mvhdInfo.contentSize < 4) ) return;
- for ( outerPos = 0; (outerPos < fileSize) && ((! this->containsXMP) || (! moovFound)); outerPos += outerSize ) {
+ XMP_Uns8 version = *mvhdInfo.content;
+ if ( version > 1 ) return;
- if ( checkAbort && abortProc(abortArg) ) {
- XMP_Throw ( "MPEG4_MetaHandler::CacheFileData - User abort", kXMPErr_UserAbort );
+ createFound = xmp.GetProperty_Date ( kXMP_NS_XMP, "CreateDate", &xmpDate, 0 );
+ if ( createFound ) XMPDateToSeconds ( xmpDate, &createSeconds );
+
+ modifyFound = xmp.GetProperty_Date ( kXMP_NS_XMP, "ModifyDate", &xmpDate, 0 );
+ if ( modifyFound ) XMPDateToSeconds ( xmpDate, &modifySeconds );
+
+ if ( version == 1 ) {
+
+ // Modify the v1 box in-place.
+
+ if ( mvhdInfo.contentSize < sizeof ( MOOV_Manager::Content_mvhd_1 ) ) return;
+
+ XMP_Uns64 oldCreate = GetUns64BE ( mvhdInfo.content + 4 );
+ XMP_Uns64 oldModify = GetUns64BE ( mvhdInfo.content + 12 );
+
+ if ( createFound ) {
+ if ( createSeconds != oldCreate ) PutUns64BE ( createSeconds, ((XMP_Uns8*)mvhdInfo.content + 4) );
+ moovMgr->NoteChange();
+ }
+ if ( modifyFound ) {
+ if ( modifySeconds != oldModify ) PutUns64BE ( modifySeconds, ((XMP_Uns8*)mvhdInfo.content + 12) );
+ moovMgr->NoteChange();
}
+
+ } else if ( ((createSeconds >> 32) == 0) && ((modifySeconds >> 32) == 0) ) {
- GetBoxInfo ( fileRef, fileSize, outerPos, &boxType, &hSize, &cSize );
- outerSize = hSize + cSize;
+ // Modify the v0 box in-place.
+
+ if ( mvhdInfo.contentSize < sizeof ( MOOV_Manager::Content_mvhd_0 ) ) return;
+
+ XMP_Uns32 oldCreate = GetUns32BE ( mvhdInfo.content + 4 );
+ XMP_Uns32 oldModify = GetUns32BE ( mvhdInfo.content + 8 );
- if ( (! this->containsXMP) && (boxType == kBE_uuid) ) {
+ if ( createFound ) {
+ if ( (XMP_Uns32)createSeconds != oldCreate ) PutUns32BE ( (XMP_Uns32)createSeconds, ((XMP_Uns8*)mvhdInfo.content + 4) );
+ moovMgr->NoteChange();
+ }
+ if ( modifyFound ) {
+ if ( (XMP_Uns32)modifySeconds != oldModify ) PutUns32BE ( (XMP_Uns32)modifySeconds, ((XMP_Uns8*)mvhdInfo.content + 8) );
+ moovMgr->NoteChange();
+ }
+
+ } else {
- XMP_Uns8 uuid [16];
- LFA_Read ( fileRef, uuid, 16, kLFA_RequireAll );
-
- if ( memcmp ( uuid, kBE_xmpUUID, 16 ) == 0 ) {
+ // Replace the v0 box with a v1 box.
+
+ XMP_Assert ( createFound | modifyFound ); // One of them has high bits set.
+ if ( mvhdInfo.contentSize != sizeof ( MOOV_Manager::Content_mvhd_0 ) ) return;
+
+ MOOV_Manager::Content_mvhd_0 * mvhdV0 = (MOOV_Manager::Content_mvhd_0*) mvhdInfo.content;
+ MOOV_Manager::Content_mvhd_1 mvhdV1;
+
+ // Copy the unchanged fields directly.
+
+ mvhdV1.timescale = mvhdV0->timescale;
+ mvhdV1.rate = mvhdV0->rate;
+ mvhdV1.volume = mvhdV0->volume;
+ mvhdV1.pad_1 = mvhdV0->pad_1;
+ mvhdV1.pad_2 = mvhdV0->pad_2;
+ mvhdV1.pad_3 = mvhdV0->pad_3;
+ for ( int i = 0; i < 9; ++i ) mvhdV1.matrix[i] = mvhdV0->matrix[i];
+ for ( int i = 0; i < 6; ++i ) mvhdV1.preDef[i] = mvhdV0->preDef[i];
+ mvhdV1.nextTrackID = mvhdV0->nextTrackID;
+
+ // Set the fields that have changes.
+
+ mvhdV1.vFlags = (1 << 24) | (mvhdV0->vFlags & 0xFFFFFF);
+ mvhdV1.duration = MakeUns64BE ( (XMP_Uns64) GetUns32BE ( &mvhdV0->duration ) );
+
+ XMP_Uns64 temp64;
+
+ temp64 = (XMP_Uns64) GetUns32BE ( &mvhdV0->creationTime );
+ if ( createFound ) temp64 = createSeconds;
+ mvhdV1.creationTime = MakeUns64BE ( temp64 );
+
+ temp64 = (XMP_Uns64) GetUns32BE ( &mvhdV0->modificationTime );
+ if ( modifyFound ) temp64 = modifySeconds;
+ mvhdV1.modificationTime = MakeUns64BE ( temp64 );
+
+ moovMgr->SetBox ( mvhdRef, &mvhdV1, sizeof ( MOOV_Manager::Content_mvhd_1 ) );
+
+ }
+
+} // ExportMVHDItems
+
+// =================================================================================================
+// ImportISOCopyrights
+// ===================
+//
+// The cached 'moov'/'udta'/'cprt' boxes are full boxes. The "real" content is a UInt16 packed 3
+// character language code and a UTF-8 or UTF-16 string.
+
+static bool ImportISOCopyrights ( const std::vector<MOOV_Manager::BoxInfo> & cprtBoxes, SXMPMeta * xmp )
+{
+ bool haveImports = false;
+
+ std::string tempStr;
+ char lang3 [4]; // The unpacked ISO-639-2/T language code with final null.
+ lang3[3] = 0;
+
+ for ( size_t i = 0, limit = cprtBoxes.size(); i < limit; ++i ) {
+
+ const MOOV_Manager::BoxInfo & currBox = cprtBoxes[i];
+ if ( currBox.contentSize < 4+2+1 ) continue; // Want enough for a non-empty value.
+ if ( *currBox.content != 0 ) continue; // Only proceed for version 0, ignore the flags.
+
+ XMP_Uns16 packedLang = GetUns16BE ( currBox.content + 4 );
+ lang3[0] = (packedLang >> 10) + 0x60;
+ lang3[1] = ((packedLang >> 5) & 0x1F) + 0x60;
+ lang3[2] = (packedLang & 0x1F) + 0x60;
+ XMP_StringPtr xmpLang = Lookup2LetterLang ( lang3 );
+ if ( *xmpLang == 0 ) continue;
+
+ XMP_StringPtr textPtr = (XMP_StringPtr) (currBox.content + 6);
+ XMP_StringLen textLen = currBox.contentSize - 6;
+
+ if ( (textLen >= 2) && (GetUns16BE(textPtr) == 0xFEFF) ) {
+ FromUTF16 ( (UTF16Unit*)textPtr, textLen/2, &tempStr, true /* big endian */ );
+ textPtr = tempStr.c_str();
+ }
+
+ xmp->SetLocalizedText ( kXMP_NS_DC, "rights", xmpLang, xmpLang, textPtr );
+ haveImports = true;
+
+ }
+
+ return haveImports;
+
+} // ImportISOCopyrights
+
+// =================================================================================================
+// ExportISOCopyrights
+// ===================
+
+static void ExportISOCopyrights ( const SXMPMeta & xmp, MOOV_Manager * moovMgr )
+{
+ bool haveMappings = false; // True if any ISO-XMP language mappings are found.
+
+ // Go through the ISO 'cprt' items and look for a corresponding XMP item. Ignore the ISO item if
+ // there is no language mapping to XMP. Update the ISO item if the mappable XMP exists, delete
+ // the ISO item if the mappable XMP does not exist. Since the import side would have made sure
+ // the mappable XMP items existed, if they don't now they must have been deleted.
+
+ MOOV_Manager::BoxInfo udtaInfo;
+ MOOV_Manager::BoxRef udtaRef = moovMgr->GetBox ( "moov/udta", &udtaInfo );
+ if ( udtaRef == 0 ) return;
+
+ std::string xmpPath, xmpValue, xmpLang, tempStr;
+ char lang3 [4]; // An unpacked ISO-639-2/T language code.
+ lang3[3] = 0;
+
+ for ( XMP_Uns32 ordinal = udtaInfo.childCount; ordinal > 0; --ordinal ) { // ! Go backwards because of deletions.
+
+ MOOV_Manager::BoxInfo cprtInfo;
+ MOOV_Manager::BoxRef cprtRef = moovMgr->GetNthChild ( udtaRef, ordinal-1, &cprtInfo );
+ if ( (cprtRef == 0) ) break; // Sanity check, should not happen.
+ if ( (cprtInfo.boxType != ISOMedia::k_cprt) || (cprtInfo.contentSize < 6) ) continue;
+ if ( *cprtInfo.content != 0 ) continue; // Only accept version 0, ignore the flags.
+
+ XMP_Uns16 packedLang = GetUns16BE ( cprtInfo.content + 4 );
+ lang3[0] = (packedLang >> 10) + 0x60;
+ lang3[1] = ((packedLang >> 5) & 0x1F) + 0x60;
+ lang3[2] = (packedLang & 0x1F) + 0x60;
+
+ XMP_StringPtr lang2 = Lookup2LetterLang ( lang3 );
+ if ( *lang2 == 0 ) continue; // No language mapping to XMP.
+ haveMappings = true;
+
+ bool xmpFound = xmp.GetLocalizedText ( kXMP_NS_DC, "rights", lang2, lang2, &xmpLang, &xmpValue, 0 );
+ if ( xmpFound ) {
+ if ( (xmpLang.size() < 2) ||
+ ( (xmpLang.size() == 2) && (xmpLang != lang2) ) ||
+ ( (xmpLang.size() > 2) && ( (xmpLang[2] != '-') || (! XMP_LitNMatch ( xmpLang.c_str(), lang2, 2)) ) ) ) {
+ xmpFound = false; // The language does not match, the corresponding XMP does not exist.
+ }
+ }
- // Found the XMP, record the offset and size, read the packet.
+ if ( ! xmpFound ) {
- this->containsXMP = true;
- this->xmpBoxPos = outerPos;
- this->packetInfo.offset = outerPos + hSize + 16;
- this->packetInfo.length = (XMP_Int32) (cSize - 16);
+ // No XMP, delete the ISO item.
+ moovMgr->DeleteNthChild ( udtaRef, ordinal-1 );
- this->xmpPacket.reserve ( this->packetInfo.length );
- this->xmpPacket.assign ( this->packetInfo.length, ' ' );
- LFA_Read ( fileRef, (void*)this->xmpPacket.data(), this->packetInfo.length, kLFA_RequireAll );
+ } else {
+ // Update the ISO item if necessary.
+ XMP_StringPtr isoStr = (XMP_StringPtr)cprtInfo.content + 6;
+ size_t rawLen = cprtInfo.contentSize - 6;
+ if ( (rawLen >= 8) && (GetUns16BE(isoStr) == 0xFEFF) ) {
+ FromUTF16 ( (UTF16Unit*)(isoStr+2), (rawLen-2)/2, &tempStr, true /* big endian */ );
+ isoStr = tempStr.c_str();
+ }
+ if ( xmpValue != isoStr ) {
+ std::string newContent = "vfffll";
+ newContent += xmpValue;
+ memcpy ( (char*)newContent.c_str(), cprtInfo.content, 6 ); // Keep old version, flags, and language.
+ moovMgr->SetBox ( cprtRef, newContent.c_str(), (XMP_Uns32)(newContent.size() + 1) );
}
- } else if ( (! moovFound) && (boxType == kBE_moov) ) {
+ }
- // The middle loop loop looks for the 'moov'/'mvhd' and 'moov'/'udta' boxes.
+ }
- moovFound = true;
-
- XMP_Uns64 middleStart = outerPos + hSize;
- XMP_Uns64 middleEnd = outerPos + outerSize;
- XMP_Uns64 middlePos, middleSize;
+ // Go through the XMP items and look for a corresponding ISO item. Skip if found (did it above),
+ // otherwise add a new ISO item.
- bool mvhdFound = false, udtaFound = false;
+ bool haveXDefault = false;
+ XMP_Index xmpCount = xmp.CountArrayItems ( kXMP_NS_DC, "rights" );
- for ( middlePos = middleStart; (middlePos < middleEnd) && ((! mvhdFound) || (! udtaFound)); middlePos += middleSize ) {
+ for ( XMP_Index xmpIndex = 1; xmpIndex <= xmpCount; ++xmpIndex ) { // ! The first XMP array index is 1.
- if ( checkAbort && abortProc(abortArg) ) {
- XMP_Throw ( "MPEG4_MetaHandler::CacheFileData - User abort", kXMPErr_UserAbort );
- }
+ SXMPUtils::ComposeArrayItemPath ( kXMP_NS_DC, "rights", xmpIndex, &xmpPath );
+ xmp.GetArrayItem ( kXMP_NS_DC, "rights", xmpIndex, &xmpValue, 0 );
+ bool hasLang = xmp.GetQualifier ( kXMP_NS_DC, xmpPath.c_str(), kXMP_NS_XML, "lang", &xmpLang, 0 );
+ if ( ! hasLang ) continue; // Sanity check.
+ if ( xmpLang == "x-default" ) {
+ haveXDefault = true; // See later special case.
+ continue;
+ }
- GetBoxInfo ( fileRef, fileSize, middlePos, &boxType, &hSize, &cSize );
- middleSize = hSize + cSize;
+ XMP_StringPtr isoLang = "";
+ size_t rootLen = xmpLang.find ( '-' );
+ if ( rootLen == std::string::npos ) rootLen = xmpLang.size();
+ if ( rootLen == 2 ) {
+ xmpLang[2] = 0;
+ isoLang = Lookup3LetterLang ( xmpLang.c_str() );
+ if ( *isoLang == 0 ) continue;
+ } else if ( rootLen == 3 ) {
+ xmpLang[3] = 0;
+ isoLang = xmpLang.c_str();
+ } else {
+ continue;
+ }
+ haveMappings = true;
- if ( (! mvhdFound) && (boxType == kBE_mvhd) ) {
+ bool isoFound = false;
+ XMP_Uns16 packedLang = ((isoLang[0] - 0x60) << 10) | ((isoLang[1] - 0x60) << 5) | (isoLang[2] - 0x60);
- // Save the entire 'moov'/'mvhd' box content, it isn't very big.
- mvhdFound = true;
- this->mvhdBox.reserve ( (size_t)cSize );
- this->mvhdBox.assign ( (size_t)cSize, ' ' );
- LFA_Read ( fileRef, (void*)this->mvhdBox.data(), (XMP_Int32)cSize, kLFA_RequireAll );
+ for ( XMP_Uns32 isoIndex = 0; (isoIndex < udtaInfo.childCount) && (! isoFound); ++isoIndex ) {
- } else if ( (! udtaFound) && (boxType == kBE_udta) ) {
+ MOOV_Manager::BoxInfo cprtInfo;
+ MOOV_Manager::BoxRef cprtRef = moovMgr->GetNthChild ( udtaRef, isoIndex, &cprtInfo );
+ if ( (cprtRef == 0) ) break; // Sanity check, should not happen.
+ if ( (cprtInfo.boxType != ISOMedia::k_cprt) || (cprtInfo.contentSize < 6) ) continue;
+ if ( *cprtInfo.content != 0 ) continue; // Only accept version 0, ignore the flags.
+ if ( packedLang != GetUns16BE ( cprtInfo.content + 4 ) ) continue; // Look for matching language.
- // The inner loop looks for the 'moov'/'udta'/'cprt' boxes.
-
- udtaFound = true;
- XMP_Uns64 innerStart = middlePos + hSize;
- XMP_Uns64 innerEnd = middlePos + middleSize;
- XMP_Uns64 innerPos, innerSize;
+ isoFound = true; // Found the language entry, whether or not we update it.
- for ( innerPos = innerStart; innerPos < innerEnd; innerPos += innerSize ) {
-
- if ( checkAbort && abortProc(abortArg) ) {
- XMP_Throw ( "MPEG4_MetaHandler::CacheFileData - User abort", kXMPErr_UserAbort );
- }
+ }
+
+ if ( ! isoFound ) {
+
+ std::string newContent = "vfffll";
+ newContent += xmpValue;
+ *((XMP_Uns32*)newContent.c_str()) = 0; // Set the version and flags to zero.
+ PutUns16BE ( packedLang, (char*)newContent.c_str() + 4 );
+ moovMgr->AddChildBox ( udtaRef, ISOMedia::k_cprt, newContent.c_str(), (XMP_Uns32)(newContent.size() + 1) );
+
+ }
+
+ }
+
+ // If there were no mappings in the loops, export the XMP "x-default" value to the first ISO item.
+
+ if ( ! haveMappings ) {
+
+ MOOV_Manager::BoxInfo cprtInfo;
+ MOOV_Manager::BoxRef cprtRef = moovMgr->GetTypeChild ( udtaRef, ISOMedia::k_cprt, &cprtInfo );
- GetBoxInfo ( fileRef, fileSize, innerPos, &boxType, &hSize, &cSize );
- innerSize = hSize + cSize;
- if ( boxType != kBE_cprt ) continue;
+ if ( (cprtRef != 0) && (cprtInfo.contentSize >= 6) && (*cprtInfo.content == 0) ) {
- // ! Actually capturing structured data - the 'cprt' box content.
- this->cprtBoxes.push_back ( std::string() );
- std::string & newCprt = this->cprtBoxes.back();
- newCprt.reserve ( (size_t)cSize );
- newCprt.assign ( (size_t)cSize, ' ' );
- LFA_Read ( fileRef, (void*)newCprt.data(), (XMP_Int32)cSize, kLFA_RequireAll );
+ bool xmpFound = xmp.GetLocalizedText ( kXMP_NS_DC, "rights", "", "x-default", &xmpLang, &xmpValue, 0 );
+
+ if ( xmpFound && (xmpLang == "x-default") ) {
+
+ // Update the ISO item if necessary.
+ XMP_StringPtr isoStr = (XMP_StringPtr)cprtInfo.content + 6;
+ size_t rawLen = cprtInfo.contentSize - 6;
+ if ( (rawLen >= 8) && (GetUns16BE(isoStr) == 0xFEFF) ) {
+ FromUTF16 ( (UTF16Unit*)(isoStr+2), (rawLen-2)/2, &tempStr, true /* big endian */ );
+ isoStr = tempStr.c_str();
+ }
+ if ( xmpValue != isoStr ) {
+ std::string newContent = "vfffll";
+ newContent += xmpValue;
+ memcpy ( (char*)newContent.c_str(), cprtInfo.content, 6 ); // Keep old version, flags, and language.
+ moovMgr->SetBox ( cprtRef, newContent.c_str(), (XMP_Uns32)(newContent.size() + 1) );
+ }
+
+ }
+
+ }
- } // inner loop
+ }
- } // 'moov'/'udta' box
+} // ExportISOCopyrights
- } // middle loop
+// =================================================================================================
+// ExportQuickTimeItems
+// ====================
- } // 'moov' box
+static void ExportQuickTimeItems ( const SXMPMeta & xmp, TradQT_Manager * qtMgr, MOOV_Manager * moovMgr )
+{
- } // outer loop
+ // The QuickTime 'udta' timecode items are done here for simplicity.
+
+ #define createWithZeroLang true
-} // MPEG4_MetaHandler::CacheFileData
+ qtMgr->ExportSimpleXMP ( kQTilst_Reel, xmp, kXMP_NS_DM, "tapeName" );
+ qtMgr->ExportSimpleXMP ( kQTilst_Timecode, xmp, kXMP_NS_DM, "startTimecode/xmpDM:timeValue", createWithZeroLang );
+ qtMgr->ExportSimpleXMP ( kQTilst_TimeScale, xmp, kXMP_NS_DM, "startTimeScale", createWithZeroLang );
+ qtMgr->ExportSimpleXMP ( kQTilst_TimeSize, xmp, kXMP_NS_DM, "startTimeSampleSize", createWithZeroLang );
+
+ qtMgr->UpdateChangedBoxes ( moovMgr );
+
+} // ExportQuickTimeItems
// =================================================================================================
-// MPEG4_MetaHandler::MakeLegacyDigest
-// ===================================
+// SelectTimeFormat
+// ================
-// *** Will need updating if we process the 'ilst' metadata.
+static const char * SelectTimeFormat ( const MPEG4_MetaHandler::TimecodeTrackInfo & tmcdInfo )
+{
+ const char * timeFormat = 0;
+
+ float fltFPS = (float)tmcdInfo.timeScale / (float)tmcdInfo.frameDuration;
+ int intFPS = (int) (fltFPS + 0.5);
-#define kHexDigits "0123456789ABCDEF"
+ switch ( intFPS ) {
-void MPEG4_MetaHandler::MakeLegacyDigest ( std::string * digestStr )
+ case 30:
+ if ( fltFPS >= 29.985 ) {
+ timeFormat = "30Timecode";
+ } else if ( tmcdInfo.isDropFrame ) {
+ timeFormat = "2997DropTimecode";
+ } else {
+ timeFormat = "2997NonDropTimecode";
+ }
+ break;
+
+ case 24:
+ if ( fltFPS >= 23.988 ) {
+ timeFormat = "24Timecode";
+ } else {
+ timeFormat = "23976Timecode";
+ }
+ break;
+
+ case 25:
+ timeFormat = "25Timecode";
+ break;
+
+ case 50:
+ timeFormat = "50Timecode";
+ break;
+
+ case 60:
+ if ( fltFPS >= 59.97 ) {
+ timeFormat = "60Timecode";
+ } else if ( tmcdInfo.isDropFrame ) {
+ timeFormat = "5994DropTimecode";
+ } else {
+ timeFormat = "5994NonDropTimecode";
+ }
+ break;
+
+ }
+
+ return timeFormat;
+
+} // SelectTimeFormat
+
+// =================================================================================================
+// SelectTimeFormat
+// ================
+
+static const char * SelectTimeFormat ( const SXMPMeta & xmp )
{
- MD5_CTX context;
- unsigned char digestBin [16];
- MD5Init ( &context );
+ bool ok;
+ MPEG4_MetaHandler::TimecodeTrackInfo tmcdInfo;
+
+ XMP_Int64 timeScale;
+ ok = xmp.GetProperty_Int64 ( kXMP_NS_DM, "startTimeScale", &timeScale, 0 );
+ if ( ! ok ) return 0;
+ tmcdInfo.timeScale = (XMP_Uns32)timeScale;
+
+ XMP_Int64 frameDuration;
+ ok = xmp.GetProperty_Int64 ( kXMP_NS_DM, "startTimeSampleSize", &frameDuration, 0 );
+ if ( ! ok ) return 0;
+ tmcdInfo.frameDuration = (XMP_Uns32)frameDuration;
+
+ std::string timecode;
+ ok = xmp.GetProperty ( kXMP_NS_DM, "startTimecode/xmpDM:timeValue", &timecode, 0 );
+ if ( ! ok ) return 0;
+ if ( (timecode.size() == 11) && (timecode[8] == ';') ) tmcdInfo.isDropFrame = true;
+
+ return SelectTimeFormat ( tmcdInfo );
+
+} // SelectTimeFormat
+
+// =================================================================================================
+// ComposeTimecode
+// ===============
+
+static const char * kDecDigits = "0123456789";
+#define TwoDigits(val,str) (str)[0] = kDecDigits[(val)/10]; (str)[1] = kDecDigits[(val)%10]
+
+static bool ComposeTimecode ( const MPEG4_MetaHandler::TimecodeTrackInfo & tmcdInfo, std::string * strValue )
+{
+ float fltFPS = (float)tmcdInfo.timeScale / (float)tmcdInfo.frameDuration;
+ int intFPS = (int) (fltFPS + 0.5);
+ if ( (intFPS != 30) && (intFPS != 24) && (intFPS != 25) && (intFPS != 50) && (intFPS != 60) ) return false;
+
+ XMP_Uns32 framesPerDay = intFPS * 24*60*60;
+ XMP_Uns32 dropLimit = 2; // Used in the drop-frame correction.
+
+ if ( tmcdInfo.isDropFrame ) {
+ if ( intFPS == 30 ) {
+ framesPerDay = 2589408; // = 29.97 * 24*60*60
+ } else if ( intFPS == 60 ) {
+ framesPerDay = 5178816; // = 59.94 * 24*60*60
+ dropLimit = 4;
+ } else {
+ strValue->erase();
+ return false; // Dropframe can only apply to 29.97 and 59.94.
+ }
+ }
+
+ XMP_Uns32 framesPerHour = framesPerDay / 24;
+ XMP_Uns32 framesPerTenMinutes = framesPerHour / 6;
+ XMP_Uns32 framesPerMinute = framesPerTenMinutes / 10;
+
+ XMP_Uns32 frameCount = tmcdInfo.timecodeSample;
+ while (frameCount >= framesPerDay ) frameCount -= framesPerDay; // Normalize to be within 24 hours.
+
+ XMP_Uns32 hours, minHigh, minLow, seconds;
+
+ hours = frameCount / framesPerHour;
+ frameCount -= (hours * framesPerHour);
+
+ minHigh = frameCount / framesPerTenMinutes;
+ frameCount -= (minHigh * framesPerTenMinutes);
+
+ minLow = frameCount / framesPerMinute;
+ frameCount -= (minLow * framesPerMinute);
+
+ // Do some drop-frame corrections at this point: If this is drop-frame and the units of minutes
+ // is non-zero, and the seconds are zero, and the frames are zero or one, the time is illegal.
+ // Perform correction by subtracting 1 from the units of minutes and adding 1798 to the frames.Ê
+ // For example, 1:00:00 becomes 59:28, and 1:00:01 becomes 59:29. A special case can occur for
+ // when the frameCount just before the minHigh calculation is less than framesPerTenMinutes but
+ // more than 10*framesPerMinute. This happens because of roundoff, and will result in a minHigh
+ // of 0 and a minLow of 10.ÊThe drop frame correction mustÊalso be performed for this case.
+
+ if ( tmcdInfo.isDropFrame ) {
+ if ( (minLow == 10) || ((minLow != 0) && (frameCount < dropLimit)) ) {
+ minLow -= 1;
+ frameCount += framesPerMinute;
+ }
+ }
+
+ seconds = frameCount / intFPS;
+ frameCount -= (seconds * intFPS);
+
+ if ( tmcdInfo.isDropFrame ) {
+ *strValue = "hh;mm;ss;ff";
+ } else {
+ *strValue = "hh:mm:ss:ff";
+ }
+
+ char * str = (char*)strValue->c_str();
+ TwoDigits ( hours, str );
+ str[3] = kDecDigits[minHigh]; str[4] = kDecDigits[minLow];
+ TwoDigits ( seconds, str+6 );
+ TwoDigits ( frameCount, str+9 );
+
+ return true;
+
+} // ComposeTimecode
+
+// =================================================================================================
+// DecomposeTimecode
+// =================
+
+static bool DecomposeTimecode ( const char * strValue, MPEG4_MetaHandler::TimecodeTrackInfo * tmcdInfo )
+{
+ float fltFPS = (float)tmcdInfo->timeScale / (float)tmcdInfo->frameDuration;
+ int intFPS = (int) (fltFPS + 0.5);
+ if ( (intFPS != 30) && (intFPS != 24) && (intFPS != 25) && (intFPS != 50) && (intFPS != 60) ) return false;
+
+ XMP_Uns32 framesPerDay = intFPS * 24*60*60;
+
+ int items, hours, minutes, seconds, frames;
+
+ if ( ! tmcdInfo->isDropFrame ) {
+ items = sscanf ( strValue, "%d:%d:%d:%d", &hours, &minutes, &seconds, &frames );
+ } else {
+ items = sscanf ( strValue, "%d;%d;%d;%d", &hours, &minutes, &seconds, &frames );
+ if ( intFPS == 30 ) {
+ framesPerDay = 2589408; // = 29.97 * 24*60*60
+ } else if ( intFPS == 60 ) {
+ framesPerDay = 5178816; // = 59.94 * 24*60*60
+ } else {
+ return false; // Dropframe can only apply to 29.97 and 59.94.
+ }
+ }
- MD5Update ( &context, (XMP_Uns8*)this->mvhdBox.c_str(), (unsigned int) this->mvhdBox.size() );
+ if ( items != 4 ) return false;
+ int minHigh = minutes / 10;
+ int minLow = minutes % 10;
+
+ XMP_Uns32 framesPerHour = framesPerDay / 24;
+ XMP_Uns32 framesPerTenMinutes = framesPerHour / 6;
+ XMP_Uns32 framesPerMinute = framesPerTenMinutes / 10;
+
+ tmcdInfo->timecodeSample = (hours * framesPerHour) + (minHigh * framesPerTenMinutes) +
+ (minLow * framesPerMinute) + (seconds * intFPS) + frames;
+
+ return true;
+
+} // DecomposeTimecode
+
+// =================================================================================================
+// FindTimecodeTrack
+// =================
+//
+// Look for a well-formed timecode track, return the .../mdia/minf/stbl box ref.
+
+static MOOV_Manager::BoxRef FindTimecodeTrack ( const MOOV_Manager & moovMgr )
+{
+
+ // Find a 'trak' box with a handler type of 'tmcd'.
- for ( size_t i = 0, limit = this->cprtBoxes.size(); i < limit; ++i ) {
- const std::string & currCprt = this->cprtBoxes[i];
- MD5Update ( &context, (XMP_Uns8*)currCprt.c_str(), (unsigned int) currCprt.size() );
+ MOOV_Manager::BoxInfo moovInfo;
+ MOOV_Manager::BoxRef moovRef = moovMgr.GetBox ( "moov", &moovInfo );
+ XMP_Assert ( moovRef != 0 );
+
+ MOOV_Manager::BoxInfo trakInfo;
+ MOOV_Manager::BoxRef trakRef;
+
+ size_t i = 0;
+ for ( ; i < moovInfo.childCount; ++i ) {
+
+ trakRef = moovMgr.GetNthChild ( moovRef, i, &trakInfo );
+ if ( trakRef == 0 ) return 0; // Sanity check, should not happen.
+ if ( trakInfo.boxType != ISOMedia::k_trak ) continue;
+
+ MOOV_Manager::BoxRef innerRef;
+ MOOV_Manager::BoxInfo innerInfo;
+
+ innerRef = moovMgr.GetTypeChild ( trakRef, ISOMedia::k_mdia, &innerInfo );
+ if ( innerRef == 0 ) continue;
+
+ innerRef = moovMgr.GetTypeChild ( innerRef, ISOMedia::k_hdlr, &innerInfo );
+ if ( (innerRef == 0) || (innerInfo.contentSize < sizeof ( MOOV_Manager::Content_hdlr )) ) continue;
+
+ const MOOV_Manager::Content_hdlr * hdlr = (MOOV_Manager::Content_hdlr*) innerInfo.content;
+ if ( hdlr->versionFlags != 0 ) continue;
+ if ( GetUns32BE ( &hdlr->handlerType ) == ISOMedia::k_tmcd ) break;
+
+ }
+ if ( i == moovInfo.childCount ) return 0;
+
+ // Find the .../mdia/minf/stbl box.
+
+ MOOV_Manager::BoxInfo tempInfo;
+ MOOV_Manager::BoxRef tempRef, stblRef;
+
+ tempRef = moovMgr.GetTypeChild ( trakRef, ISOMedia::k_mdia, &tempInfo );
+ if ( tempRef == 0 ) return 0;
+
+ tempRef = moovMgr.GetTypeChild ( tempRef, ISOMedia::k_minf, &tempInfo );
+ if ( tempRef == 0 ) return 0;
+
+ stblRef = moovMgr.GetTypeChild ( tempRef, ISOMedia::k_stbl, &tempInfo );
+ return stblRef;
+
+} // FindTimecodeTrack
+
+// =================================================================================================
+// ImportTimecodeItems
+// ===================
+
+static bool ImportTimecodeItems ( const MPEG4_MetaHandler::TimecodeTrackInfo & tmcdInfo,
+ const TradQT_Manager & qtInfo, SXMPMeta * xmp )
+{
+ std::string xmpValue;
+ bool haveItem;
+ bool haveImports = false;
+
+ // The QT user data item '©REL' goes into xmpDM:tapeName, and the 'name' box at the end of the
+ // timecode sample description goes into xmpDM:altTapeName.
+ haveImports |= qtInfo.ImportSimpleXMP ( kQTilst_Reel, xmp, kXMP_NS_DM, "tapeName" );
+ if ( ! tmcdInfo.macName.empty() ) {
+ haveItem = ConvertFromMacLang ( tmcdInfo.macName, tmcdInfo.macLang, &xmpValue );
+ if ( haveItem ) {
+ xmp->SetProperty ( kXMP_NS_DM, "altTapeName", xmpValue.c_str() );
+ haveImports = true;
+ }
+ }
+
+ // The QT user data item '©TSC' goes into xmpDM:startTimeScale. If that isn't present, then
+ // the timecode sample description's timeScale is used.
+ haveItem = qtInfo.ImportSimpleXMP ( kQTilst_TimeScale, xmp, kXMP_NS_DM, "startTimeScale" );
+ if ( tmcdInfo.stsdBoxFound & (! haveItem) ) {
+ xmp->SetProperty_Int64 ( kXMP_NS_DM, "startTimeScale", tmcdInfo.timeScale );
+ haveItem = true;
+ }
+ haveImports |= haveItem;
+
+ // The QT user data item '©TSZ' goes into xmpDM:startTimeSampleSize. If that isn't present, then
+ // the timecode sample description's frameDuration is used.
+ haveItem = qtInfo.ImportSimpleXMP ( kQTilst_TimeSize, xmp, kXMP_NS_DM, "startTimeSampleSize" );
+ if ( tmcdInfo.stsdBoxFound & (! haveItem) ) {
+ xmp->SetProperty_Int64 ( kXMP_NS_DM, "startTimeSampleSize", tmcdInfo.frameDuration );
+ haveItem = true;
}
+ haveImports |= haveItem;
- MD5Final ( digestBin, &context );
+ const char * timeFormat;
+
+ // The Timecode struct type is used for xmpDM:startTimecode and xmpDM:altTimecode. For both, only
+ // the xmpDM:timeValue and xmpDM:timeFormat fields are set.
+
+ // The QT user data item '©TIM' goes into xmpDM:startTimecode/xmpDM:timeValue. This is an already
+ // formatted timecode string. The XMP values of xmpDM:startTimeScale, xmpDM:startTimeSampleSize,
+ // and xmpDM:startTimecode/xmpDM:timeValue are used to select the timeFormat value.
+ haveImports |= qtInfo.ImportSimpleXMP ( kQTilst_Timecode, xmp, kXMP_NS_DM, "startTimecode/xmpDM:timeValue" );
+ timeFormat = SelectTimeFormat ( *xmp );
+ if ( timeFormat != 0 ) {
+ xmp->SetProperty ( kXMP_NS_DM, "startTimecode/xmpDM:timeFormat", timeFormat );
+ haveImports = true;
+ }
+
+ if ( tmcdInfo.stsdBoxFound ) {
+
+ haveItem = ComposeTimecode ( tmcdInfo, &xmpValue );
+ if ( haveItem ) {
+ xmp->SetProperty ( kXMP_NS_DM, "altTimecode/xmpDM:timeValue", xmpValue.c_str() );
+ haveImports = true;
+ }
+
+ timeFormat = SelectTimeFormat ( tmcdInfo );
+ if ( timeFormat != 0 ) {
+ xmp->SetProperty ( kXMP_NS_DM, "altTimecode/xmpDM:timeFormat", timeFormat );
+ haveImports = true;
+ }
- char buffer [40];
- for ( int in = 0, out = 0; in < 16; in += 1, out += 2 ) {
- XMP_Uns8 byte = digestBin[in];
- buffer[out] = kHexDigits [ byte >> 4 ];
- buffer[out+1] = kHexDigits [ byte & 0xF ];
}
- buffer[32] = 0;
- digestStr->erase();
- digestStr->append ( buffer, 32 );
-} // MPEG4_MetaHandler::MakeLegacyDigest
+ return haveImports;
+
+} // ImportTimecodeItems
// =================================================================================================
+// ExportTimecodeItems
+// ===================
+
+static void ExportTimecodeItems ( const SXMPMeta & xmp, MPEG4_MetaHandler::TimecodeTrackInfo * tmcdInfo,
+ TradQT_Manager * qtMgr, MOOV_Manager * moovMgr )
+{
+ // Export the items that go into the timecode track:
+ // - the timescale and frame duration in the first 'stsd' table entry
+ // - the 'name' box appended to the first 'stsd' table entry
+ // - the first timecode sample
+ // ! The QuickTime 'udta' timecode items are handled in ExportQuickTimeItems.
+
+ if ( ! tmcdInfo->stsdBoxFound ) return; // Don't make changes unless there is a well-formed timecode track.
+
+ MOOV_Manager::BoxRef stblRef = FindTimecodeTrack ( *moovMgr );
+ if ( stblRef == 0 ) return;
+
+ MOOV_Manager::BoxInfo stsdInfo;
+ MOOV_Manager::BoxRef stsdRef;
+
+ stsdRef = moovMgr->GetTypeChild ( stblRef, ISOMedia::k_stsd, &stsdInfo );
+ if ( stsdRef == 0 ) return;
+ if ( stsdInfo.contentSize < (8 + sizeof ( MOOV_Manager::Content_stsd_entry )) ) return;
+ if ( GetUns32BE ( stsdInfo.content + 4 ) == 0 ) return; // Make sure the entry count is non-zero.
+
+ const MOOV_Manager::Content_stsd_entry * stsdRawEntry = (MOOV_Manager::Content_stsd_entry*) (stsdInfo.content + 8);
+
+ XMP_Uns32 stsdEntrySize = GetUns32BE ( &stsdRawEntry->entrySize );
+ if ( stsdEntrySize > (stsdInfo.contentSize - 4) ) stsdEntrySize = stsdInfo.contentSize - 4;
+ if ( stsdEntrySize < sizeof ( MOOV_Manager::Content_stsd_entry ) ) return;
+
+ bool ok;
+ std::string xmpValue;
+ XMP_Int64 int64; // Used to allow UInt32 values, GetProperty_Int is SInt32.
+
+ // The tmcdInfo timeScale field is set from xmpDM:startTimeScale.
+ ok = xmp.GetProperty_Int64 ( kXMP_NS_DM, "startTimeScale", &int64, 0 );
+ if ( ok && (int64 <= 0xFFFFFFFF) && ((XMP_Uns32)int64 != tmcdInfo->timeScale) ) {
+ tmcdInfo->timeScale = (XMP_Uns32)int64;
+ PutUns32BE ( tmcdInfo->timeScale, (void*)&stsdRawEntry->timeScale );
+ moovMgr->NoteChange();
+ }
+
+ // The tmcdInfo frameDuration field is set from xmpDM:startTimeSampleSize.
+ ok = xmp.GetProperty_Int64 ( kXMP_NS_DM, "startTimeSampleSize", &int64, 0 );
+ if ( ok && (int64 <= 0xFFFFFFFF) && ((XMP_Uns32)int64 != tmcdInfo->frameDuration) ) {
+ tmcdInfo->frameDuration = (XMP_Uns32)int64;
+ PutUns32BE ( tmcdInfo->frameDuration, (void*)&stsdRawEntry->frameDuration );
+ moovMgr->NoteChange();
+ }
+
+ // The tmcdInfo isDropFrame flag is set from xmpDM:altTimecode/xmpDM:timeValue. The timeScale
+ // and frameDuration must be updated first, they are used by DecomposeTimecode. Compute the new
+ // UInt32 timecode sample, but it gets written to the file later by UpdateFile.
+
+ ok = xmp.GetProperty ( kXMP_NS_DM, "altTimecode/xmpDM:timeValue", &xmpValue, 0 );
+ if ( ok && (xmpValue.size() == 11) ) {
+
+ bool oldDropFrame = tmcdInfo->isDropFrame;
+ tmcdInfo->isDropFrame = false;
+ if ( xmpValue[8] == ';' ) tmcdInfo->isDropFrame = true;
+ if ( oldDropFrame != tmcdInfo->isDropFrame ) {
+ XMP_Uns32 flags = GetUns32BE ( &stsdRawEntry->flags );
+ flags = (flags & 0xFFFFFFFE) | (XMP_Uns32)tmcdInfo->isDropFrame;
+ PutUns32BE ( flags, (void*)&stsdRawEntry->flags );
+ moovMgr->NoteChange();
+ }
+
+ XMP_Uns32 oldSample = tmcdInfo->timecodeSample;
+ ok = DecomposeTimecode ( xmpValue.c_str(), tmcdInfo );
+ if ( ok && (oldSample != tmcdInfo->timecodeSample) ) moovMgr->NoteChange();
-struct MVHD_v1 { // v1 v0 - offsets within the content portion of the 'mvhd' box
- XMP_Uns8 version; // 0 0
- XMP_Uns8 flags [3]; // 1 1
- XMP_Uns64 creationTime; // 4 4 - Uns32 in v0
- XMP_Uns64 modificationTime; // 12 8 - Uns32 in v0
- XMP_Uns32 timescale; // 20 12
- XMP_Uns64 duration; // 24 16 - Uns32 in v0
- XMP_Int32 rate; // 32 20
- XMP_Int16 volume; // 36 24
- XMP_Uns16 pad_1; // 38 26
- XMP_Uns32 pad_2, pad_3; // 40 28
- XMP_Int32 matrix [9]; // 48 36
- XMP_Uns32 preDef [6]; // 84 72
- XMP_Uns32 nextTrackID; // 108 96
-}; // 112 100
+ }
+
+ // The 'name' box attached to the first 'stsd' table entry is set from xmpDM:altTapeName.
+
+ bool replaceNameBox = false;
+
+ ok = xmp.GetProperty ( kXMP_NS_DM, "altTapeName", &xmpValue, 0 );
+ if ( (! ok) || xmpValue.empty() ) {
+ if ( tmcdInfo->nameOffset != 0 ) replaceNameBox = true; // No XMP, get rid of existing name.
+ } else {
+ std::string macValue;
+ ok = ConvertToMacLang ( xmpValue, tmcdInfo->macLang, &macValue );
+ if ( ok && (macValue != tmcdInfo->macName) ) {
+ tmcdInfo->macName = macValue;
+ replaceNameBox = true; // Write changed name.
+ }
+ }
+
+ if ( replaceNameBox ) {
+
+ // To replace the 'name' box we have to create an entire new 'stsd' box, and attach the
+ // new name to the first 'stsd' table entry. The 'name' box content is a UInt16 text length,
+ // UInt16 language code, and Mac encoded text with no nul termination.
+
+ if ( tmcdInfo->macName.size() > 0xFFFF ) tmcdInfo->macName.erase ( 0xFFFF );
+
+ ISOMedia::BoxInfo oldNameInfo;
+ XMP_Assert ( (oldNameInfo.headerSize == 0) && (oldNameInfo.contentSize == 0) );
+ if ( tmcdInfo->nameOffset != 0 ) {
+ const XMP_Uns8 * oldNamePtr = stsdInfo.content + tmcdInfo->nameOffset;
+ const XMP_Uns8 * oldNameLimit = stsdInfo.content + stsdInfo.contentSize;
+ (void) ISOMedia::GetBoxInfo ( oldNamePtr, oldNameLimit, &oldNameInfo );
+ }
+
+ XMP_Uns32 oldNameBoxSize = (XMP_Uns32)oldNameInfo.headerSize + (XMP_Uns32)oldNameInfo.contentSize;
+ XMP_Uns32 newNameBoxSize = 0;
+ if ( ! tmcdInfo->macName.empty() ) newNameBoxSize = 4+4 + 2+2 + (XMP_Uns32)tmcdInfo->macName.size();
+
+ XMP_Uns32 stsdNewContentSize = stsdInfo.contentSize - oldNameBoxSize + newNameBoxSize;
+ RawDataBlock stsdNewContent;
+ stsdNewContent.assign ( stsdNewContentSize, 0 ); // Get the space allocated, direct fill below.
+
+ XMP_Uns32 stsdPrefixSize = tmcdInfo->nameOffset;
+ if ( tmcdInfo->nameOffset == 0 ) stsdPrefixSize = 4+4 + sizeof ( MOOV_Manager::Content_stsd_entry );
+
+ XMP_Uns32 oldSuffixOffset = stsdPrefixSize + oldNameBoxSize;
+ XMP_Uns32 newSuffixOffset = stsdPrefixSize + newNameBoxSize;
+ XMP_Uns32 stsdSuffixSize = stsdInfo.contentSize - oldSuffixOffset;
+
+ memcpy ( &stsdNewContent[0], stsdInfo.content, stsdPrefixSize );
+ if ( stsdSuffixSize != 0 ) memcpy ( &stsdNewContent[newSuffixOffset], (stsdInfo.content + oldSuffixOffset), stsdSuffixSize );
+
+ XMP_Uns32 newEntrySize = stsdEntrySize - oldNameBoxSize + newNameBoxSize;
+ MOOV_Manager::Content_stsd_entry * stsdNewEntry = (MOOV_Manager::Content_stsd_entry*) (&stsdNewContent[0] + 8);
+ PutUns32BE ( newEntrySize, &stsdNewEntry->entrySize );
+
+ if ( newNameBoxSize != 0 ) {
+ PutUns32BE ( newNameBoxSize, &stsdNewContent[stsdPrefixSize] );
+ PutUns32BE ( ISOMedia::k_name, &stsdNewContent[stsdPrefixSize+4] );
+ PutUns16BE ( (XMP_Uns16)tmcdInfo->macName.size(), &stsdNewContent[stsdPrefixSize+8] );
+ PutUns16BE ( tmcdInfo->macLang, &stsdNewContent[stsdPrefixSize+10] );
+ memcpy ( &stsdNewContent[stsdPrefixSize+12], tmcdInfo->macName.c_str(), tmcdInfo->macName.size() );
+ }
+
+ moovMgr->SetBox ( stsdRef, &stsdNewContent[0], stsdNewContentSize );
+
+ }
-static void ExtractMVHD_v0 ( XMP_Uns8 * buffer, MVHD_v1 * mvhd ) // Always convert to the v1 form.
+} // ExportTimecodeItems
+
+// =================================================================================================
+// ImportCr8rItems
+// ===============
+
+#pragma pack ( push, 1 )
+
+struct PrmLBoxContent {
+ XMP_Uns32 magic;
+ XMP_Uns32 size;
+ XMP_Uns16 verAPI;
+ XMP_Uns16 verCode;
+ XMP_Uns32 exportType;
+ XMP_Uns16 MacVRefNum;
+ XMP_Uns32 MacParID;
+ char filePath[260];
+};
+
+enum { kExportTypeMovie = 0, kExportTypeStill = 1, kExportTypeAudio = 2, kExportTypeCustom = 3 };
+
+struct Cr8rBoxContent {
+ XMP_Uns32 magic;
+ XMP_Uns32 size;
+ XMP_Uns16 majorVer;
+ XMP_Uns16 minorVer;
+ XMP_Uns32 creatorCode;
+ XMP_Uns32 appleEvent;
+ char fileExt[16];
+ char appOptions[16];
+ char appName[32];
+};
+
+#pragma pack ( pop )
+
+// -------------------------------------------------------------------------------------------------
+
+static bool ImportCr8rItems ( const MOOV_Manager & moovMgr, SXMPMeta * xmp )
{
- mvhd->version = buffer[0];
- mvhd->flags[0] = buffer[1];
- mvhd->flags[1] = buffer[2];
- mvhd->flags[2] = buffer[3];
- mvhd->creationTime = GetUns32BE ( &buffer[ 4] );
- mvhd->modificationTime = GetUns32BE ( &buffer[ 8] );
- mvhd->timescale = GetUns32BE ( &buffer[12] );
- mvhd->duration = GetUns32BE ( &buffer[16] );
- mvhd->rate = GetUns32BE ( &buffer[20] );
- mvhd->volume = GetUns16BE ( &buffer[24] );
- mvhd->pad_1 = GetUns16BE ( &buffer[26] );
- mvhd->pad_2 = GetUns32BE ( &buffer[28] );
- mvhd->pad_3 = GetUns32BE ( &buffer[32] );
- for ( int i = 0, j = 36; i < 9; ++i, j += 4 ) mvhd->matrix[i] = GetUns32BE ( &buffer[j] );
- for ( int i = 0, j = 72; i < 6; ++i, j += 4 ) mvhd->preDef[i] = GetUns32BE ( &buffer[j] );
- mvhd->nextTrackID = GetUns32BE ( &buffer[96] );
-}
+ bool haveXMP = false;
+
+ MOOV_Manager::BoxInfo infoPrmL, infoCr8r;
+ MOOV_Manager::BoxRef refPrmL = moovMgr.GetBox ( "moov/udta/PrmL", &infoPrmL );
+ MOOV_Manager::BoxRef refCr8r = moovMgr.GetBox ( "moov/udta/Cr8r", &infoCr8r );
+
+ bool havePrmL = ( (refPrmL != 0) && (infoPrmL.contentSize == sizeof ( PrmLBoxContent )) );
+ bool haveCr8r = ( (refCr8r != 0) && (infoCr8r.contentSize == sizeof ( Cr8rBoxContent )) );
+
+ if ( havePrmL ) {
+
+ PrmLBoxContent rawPrmL;
+ XMP_Assert ( sizeof ( rawPrmL ) == 282 );
+ XMP_Assert ( sizeof ( rawPrmL.filePath ) == 260 );
+ memcpy ( &rawPrmL, infoPrmL.content, sizeof ( rawPrmL ) );
+ if ( rawPrmL.magic != 0xBEEFCAFE ) {
+ Flip4 ( &rawPrmL.exportType ); // The only numeric field that we care about.
+ }
-static void ExtractMVHD_v1 ( XMP_Uns8 * buffer, MVHD_v1 * mvhd )
-{
- mvhd->version = buffer[0];
- mvhd->flags[0] = buffer[1];
- mvhd->flags[1] = buffer[2];
- mvhd->flags[2] = buffer[3];
- mvhd->creationTime = GetUns64BE ( &buffer[ 4] );
- mvhd->modificationTime = GetUns64BE ( &buffer[12] );
- mvhd->timescale = GetUns32BE ( &buffer[20] );
- mvhd->duration = GetUns64BE ( &buffer[24] );
- mvhd->rate = GetUns32BE ( &buffer[32] );
- mvhd->volume = GetUns16BE ( &buffer[36] );
- mvhd->pad_1 = GetUns16BE ( &buffer[38] );
- mvhd->pad_2 = GetUns32BE ( &buffer[40] );
- mvhd->pad_3 = GetUns32BE ( &buffer[44] );
- for ( int i = 0, j = 48; i < 9; ++i, j += 4 ) mvhd->matrix[i] = GetUns32BE ( &buffer[j] );
- for ( int i = 0, j = 84; i < 6; ++i, j += 4 ) mvhd->preDef[i] = GetUns32BE ( &buffer[j] );
- mvhd->nextTrackID = GetUns32BE ( &buffer[108] );
-}
+ rawPrmL.filePath[259] = 0; // Ensure a terminating nul.
+ if ( rawPrmL.filePath[0] != 0 ) {
+ if ( rawPrmL.filePath[0] == '/' ) {
+ haveXMP = true;
+ xmp->SetStructField ( kXMP_NS_CreatorAtom, "macAtom",
+ kXMP_NS_CreatorAtom, "posixProjectPath", rawPrmL.filePath );
+ } else if ( XMP_LitNMatch ( rawPrmL.filePath, "\\\\?\\", 4 ) ) {
+ haveXMP = true;
+ xmp->SetStructField ( kXMP_NS_CreatorAtom, "windowsAtom",
+ kXMP_NS_CreatorAtom, "uncProjectPath", rawPrmL.filePath );
+ }
+ }
+
+ const char * exportStr = 0;
+ switch ( rawPrmL.exportType ) {
+ case kExportTypeMovie : exportStr = "movie"; break;
+ case kExportTypeStill : exportStr = "still"; break;
+ case kExportTypeAudio : exportStr = "audio"; break;
+ case kExportTypeCustom : exportStr = "custom"; break;
+ }
+ if ( exportStr != 0 ) {
+ haveXMP = true;
+ xmp->SetStructField ( kXMP_NS_DM, "projectRef", kXMP_NS_DM, "type", exportStr );
+ }
+
+ }
+
+ if ( haveCr8r ) {
+
+ Cr8rBoxContent rawCr8r;
+ XMP_Assert ( sizeof ( rawCr8r ) == 84 );
+ XMP_Assert ( sizeof ( rawCr8r.fileExt ) == 16 );
+ XMP_Assert ( sizeof ( rawCr8r.appOptions ) == 16 );
+ XMP_Assert ( sizeof ( rawCr8r.appName ) == 32 );
+ memcpy ( &rawCr8r, infoCr8r.content, sizeof ( rawCr8r ) );
+ if ( rawCr8r.magic != 0xBEEFCAFE ) {
+ Flip4 ( &rawCr8r.creatorCode ); // The only numeric fields that we care about.
+ Flip4 ( &rawCr8r.appleEvent );
+ }
+
+ std::string fieldPath;
+
+ SXMPUtils::ComposeStructFieldPath ( kXMP_NS_CreatorAtom, "macAtom", kXMP_NS_CreatorAtom, "applicationCode", &fieldPath );
+ if ( rawCr8r.creatorCode != 0 ) {
+ haveXMP = true;
+ xmp->SetProperty_Int64 ( kXMP_NS_CreatorAtom, fieldPath.c_str(), (XMP_Int64)rawCr8r.creatorCode ); // ! Unsigned trickery.
+ }
+
+ SXMPUtils::ComposeStructFieldPath ( kXMP_NS_CreatorAtom, "macAtom", kXMP_NS_CreatorAtom, "invocationAppleEvent", &fieldPath );
+ if ( rawCr8r.appleEvent != 0 ) {
+ haveXMP = true;
+ xmp->SetProperty_Int64 ( kXMP_NS_CreatorAtom, fieldPath.c_str(), (XMP_Int64)rawCr8r.appleEvent ); // ! Unsigned trickery.
+ }
+
+ rawCr8r.fileExt[15] = 0; // Ensure a terminating nul.
+ if ( rawCr8r.fileExt[0] != 0 ) {
+ haveXMP = true;
+ xmp->SetStructField ( kXMP_NS_CreatorAtom, "windowsAtom", kXMP_NS_CreatorAtom, "extension", rawCr8r.fileExt );
+ }
+
+ rawCr8r.appOptions[15] = 0; // Ensure a terminating nul.
+ if ( rawCr8r.appOptions[0] != 0 ) {
+ haveXMP = true;
+ xmp->SetStructField ( kXMP_NS_CreatorAtom, "windowsAtom", kXMP_NS_CreatorAtom, "invocationFlags", rawCr8r.appOptions );
+ }
+
+ rawCr8r.appName[31] = 0; // Ensure a terminating nul.
+ if ( rawCr8r.appName[0] != 0 ) {
+ haveXMP = true;
+ xmp->SetProperty ( kXMP_NS_XMP, "CreatorTool", rawCr8r.appName );
+ }
+
+ }
+
+ return haveXMP;
+
+} // ImportCr8rItems
// =================================================================================================
-// MPEG4_MetaHandler::ProcessXMP
-// =============================
+// ExportCr8rItems
+// ===============
+
+static inline void SetBufferedString ( char * dest, const std::string source, size_t limit )
+{
+ memset ( dest, 0, limit );
+ size_t count = source.size();
+ if ( count >= limit ) count = limit - 1; // Ensure a terminating nul.
+ memcpy ( dest, source.c_str(), count );
+}
-#define kAlmostMaxSeconds 0x7FFFFF00
+// -------------------------------------------------------------------------------------------------
-void MPEG4_MetaHandler::ProcessXMP()
+static void ExportCr8rItems ( const SXMPMeta & xmp, MOOV_Manager * moovMgr )
{
- if ( this->processedXMP ) return;
- this->processedXMP = true; // Make sure only called once.
+ bool haveNewCr8r = false;
+ std::string creatorCode, appleEvent, fileExt, appOptions, appName;
- if ( this->containsXMP ) {
- FillPacketInfo ( this->xmpPacket, &this->packetInfo );
- this->xmpObj.ParseFromBuffer ( this->xmpPacket.c_str(), (XMP_StringLen)this->xmpPacket.size() );
+ haveNewCr8r |= xmp.GetStructField ( kXMP_NS_CreatorAtom, "macAtom", kXMP_NS_CreatorAtom, "applicationCode", &creatorCode, 0 );
+ haveNewCr8r |= xmp.GetStructField ( kXMP_NS_CreatorAtom, "macAtom", kXMP_NS_CreatorAtom, "invocationAppleEvent", &appleEvent, 0 );
+ haveNewCr8r |= xmp.GetStructField ( kXMP_NS_CreatorAtom, "windowsAtom", kXMP_NS_CreatorAtom, "extension", &fileExt, 0 );
+ haveNewCr8r |= xmp.GetStructField ( kXMP_NS_CreatorAtom, "windowsAtom", kXMP_NS_CreatorAtom, "invocationFlags", &appOptions, 0 );
+ haveNewCr8r |= xmp.GetProperty ( kXMP_NS_XMP, "CreatorTool", &appName, 0 );
+
+ MOOV_Manager::BoxInfo infoCr8r;
+ MOOV_Manager::BoxRef refCr8r = moovMgr->GetBox ( "moov/udta/Cr8r", &infoCr8r );
+ bool haveOldCr8r = ( (refCr8r != 0) && (infoCr8r.contentSize == sizeof ( Cr8rBoxContent )) );
+
+ if ( ! haveNewCr8r ) {
+ if ( haveOldCr8r ) {
+ MOOV_Manager::BoxRef udtaRef = moovMgr->GetBox ( "moov/udta", 0 );
+ moovMgr->DeleteTypeChild ( udtaRef, 0x43723872 /* 'Cr8r' */ );
+ }
+ return;
}
- if ( this->mvhdBox.empty() && this->cprtBoxes.empty() ) return; // No legacy, we're done.
+ Cr8rBoxContent newCr8r;
+ const Cr8rBoxContent * oldCr8r = (Cr8rBoxContent*) infoCr8r.content;
+
+ if ( ! haveOldCr8r ) {
+
+ memset ( &newCr8r, 0, sizeof(newCr8r) );
+ newCr8r.magic = MakeUns32BE ( 0xBEEFCAFE );
+ newCr8r.size = MakeUns32BE ( sizeof ( newCr8r ) );
+ newCr8r.majorVer = MakeUns16BE ( 1 );
+
+ } else {
+
+ memcpy ( &newCr8r, oldCr8r, sizeof(newCr8r) );
+ if ( GetUns32BE ( &newCr8r.magic ) != 0xBEEFCAFE ) { // Make sure we write BE numbers.
+ Flip4 ( &newCr8r.magic );
+ Flip4 ( &newCr8r.size );
+ Flip2 ( &newCr8r.majorVer );
+ Flip2 ( &newCr8r.minorVer );
+ Flip4 ( &newCr8r.creatorCode );
+ Flip4 ( &newCr8r.appleEvent );
+ }
+
+ }
- std::string oldDigest;
- bool oldDigestFound = this->xmpObj.GetStructField ( kXMP_NS_XMP, "NativeDigests", kXMP_NS_XMP, "MPEG-4", &oldDigest, 0 );
+ if ( ! creatorCode.empty() ) {
+ newCr8r.creatorCode = MakeUns32BE ( (XMP_Uns32) strtoul ( creatorCode.c_str(), 0, 0 ) );
+ }
- if ( oldDigestFound ) {
- std::string newDigest;
- this->MakeLegacyDigest ( &newDigest );
- if ( oldDigest == newDigest ) return; // No legacy changes.
+ if ( ! appleEvent.empty() ) {
+ newCr8r.appleEvent = MakeUns32BE ( (XMP_Uns32) strtoul ( appleEvent.c_str(), 0, 0 ) );
}
- // If we get here we need to import the legacy metadata. Either there is no old digest in the
- // XMP, or the digests differ. In the former case keep any existing XMP, in the latter case take
- // new legacy values. So, oldDigestFound means digestsDiffer, else we would have returned.
+ if ( ! fileExt.empty() ) SetBufferedString ( newCr8r.fileExt, fileExt, sizeof ( newCr8r.fileExt ) );
+ if ( ! appOptions.empty() ) SetBufferedString ( newCr8r.appOptions, appOptions, sizeof ( newCr8r.appOptions ) );
+ if ( ! appName.empty() ) SetBufferedString ( newCr8r.appName, appName, sizeof ( newCr8r.appName ) );
- // *** The "official" MPEG-4 metadata might not be very interesting. It looks like the 'ilst'
- // *** metadata (invented by Apple?) is more interesting. There appears to be no official
- // *** documentation.
+ moovMgr->SetBox ( "moov/udta/Cr8r", &newCr8r, sizeof(newCr8r) );
+
+} // ExportCr8rItems
- if ( ! this->mvhdBox.empty() ) {
+// =================================================================================================
+// GetAtomInfo
+// ===========
+
+struct AtomInfo {
+ XMP_Int64 atomSize;
+ XMP_Uns32 atomType;
+ bool hasLargeSize;
+};
+
+enum { // ! Do not rearrange, code depends on this order.
+ kBadQT_NoError = 0, // No errors.
+ kBadQT_SmallInner = 1, // An extra 1..7 bytes at the end of an inner span.
+ kBadQT_LargeInner = 2, // More serious inner garbage, found as invalid atom length.
+ kBadQT_SmallOuter = 3, // An extra 1..7 bytes at the end of the file.
+ kBadQT_LargeOuter = 4 // More serious EOF garbage, found as invalid atom length.
+};
+typedef XMP_Uns8 QTErrorMode;
+
+static QTErrorMode GetAtomInfo ( const LFA_FileRef qtFile, XMP_Int64 spanSize, int nesting, AtomInfo * info )
+{
+ QTErrorMode status = kBadQT_NoError;
+ XMP_Uns8 buffer [8];
- MVHD_v1 mvhd;
- XMP_DateTime xmpDate;
+ info->hasLargeSize = false;
- if ( this->mvhdBox.size() == 100 ) { // *** Should check the version - extract to a static function.
- ExtractMVHD_v0 ( (XMP_Uns8*)(this->mvhdBox.data()), &mvhd );
- } else if ( this->mvhdBox.size() == 112 ) {
- ExtractMVHD_v1 ( (XMP_Uns8*)(this->mvhdBox.data()), &mvhd );
+ LFA_Read ( qtFile, buffer, 8, kLFA_RequireAll ); // Will throw if 8 bytes aren't available.
+ info->atomSize = GetUns32BE ( &buffer[0] ); // ! Yes, the initial size is big endian UInt32.
+ info->atomType = GetUns32BE ( &buffer[4] );
+
+ if ( info->atomSize == 0 ) { // Does the atom extend to EOF?
+
+ if ( nesting != 0 ) return kBadQT_LargeInner;
+ info->atomSize = spanSize; // This outer atom goes to EOF.
+
+ } else if ( info->atomSize == 1 ) { // Does the atom have a 64-bit size?
+
+ if ( spanSize < 16 ) { // Is there room in the span for the 16 byte header?
+ status = kBadQT_LargeInner;
+ if ( nesting == 0 ) status += 2; // Convert to "outer".
+ return status;
}
- if ( oldDigestFound || (! this->xmpObj.DoesPropertyExist ( kXMP_NS_XMP, "CreateDate" )) ) {
- if ( (mvhd.creationTime >> 32) < 0xFF ) { // Sanity check for bogus date info.
+ LFA_Read ( qtFile, buffer, 8, kLFA_RequireAll );
+ info->atomSize = (XMP_Int64) GetUns64BE ( &buffer[0] );
+ info->hasLargeSize = true;
- memset ( &xmpDate, 0, sizeof(xmpDate) ); // AUDIT: Using sizeof(xmpDate) is safe.
- xmpDate.year = 1904; // Start at midnight, January 1 1904, UTC
- xmpDate.month = 1; // ! Note that the XMP binary fields are signed to allow
- xmpDate.day = 1; // ! offsets and normalization in both directions.
+ }
- while ( mvhd.creationTime > kAlmostMaxSeconds ) {
- xmpDate.second += kAlmostMaxSeconds;
- SXMPUtils::ConvertToUTCTime ( &xmpDate ); // ! For the normalization side effect.
- mvhd.creationTime -= kAlmostMaxSeconds;
- }
- xmpDate.second += (XMP_Uns32)mvhd.creationTime;
- SXMPUtils::ConvertToUTCTime ( &xmpDate ); // ! For the normalization side effect.
-
- this->xmpObj.SetProperty_Date ( kXMP_NS_XMP, "CreateDate", xmpDate );
- this->containsXMP = true;
+ XMP_Assert ( status == kBadQT_NoError );
+ return status;
- }
+} // GetAtomInfo
+
+// =================================================================================================
+// CheckAtomList
+// =============
+//
+// Check that a sequence of atoms fills a given span. The I/O position must be at the start of the
+// span, it is left just past the span on success. Recursive checks are done for top level 'moov'
+// atoms, and second level 'udta' atoms ('udta' inside 'moov').
+//
+// Checking continues for "small inner" errors. They will be reported if no other kinds of errors
+// are found, otherwise the other error is reported. Checking is immediately aborted for any "large"
+// error. The rationale is that QuickTime can apparently handle small inner errors. They might be
+// arise from updates that shorten an atom by less than 8 bytes. Larger shrinkage should introduce a
+// 'free' atom.
+
+static QTErrorMode CheckAtomList ( const LFA_FileRef qtFile, XMP_Int64 spanSize, int nesting )
+{
+ QTErrorMode status = kBadQT_NoError;
+ AtomInfo info;
+
+ const static XMP_Uns32 moovAtomType = 0x6D6F6F76; // ! Don't use MakeUns32BE, already big endian.
+ const static XMP_Uns32 udtaAtomType = 0x75647461;
+
+ for ( ; spanSize >= 8; spanSize -= info.atomSize ) {
+
+ QTErrorMode atomStatus = GetAtomInfo ( qtFile, spanSize, nesting, &info );
+ if ( atomStatus != kBadQT_NoError ) return atomStatus;
+
+ XMP_Int64 headerSize = 8;
+ if ( info.hasLargeSize ) headerSize = 16;
+
+ if ( (info.atomSize < headerSize) || (info.atomSize > spanSize) ) {
+ status = kBadQT_LargeInner;
+ if ( nesting == 0 ) status += 2; // Convert to "outer".
+ return status;
}
- if ( oldDigestFound || (! this->xmpObj.DoesPropertyExist ( kXMP_NS_XMP, "ModifyDate" )) ) {
- if ( (mvhd.modificationTime >> 32) < 0xFF ) { // Sanity check for bogus date info.
+ bool doChildren = false;
+ if ( (nesting == 0) && (info.atomType == moovAtomType) ) doChildren = true;
+ if ( (nesting == 1) && (info.atomType == udtaAtomType) ) doChildren = true;
- memset ( &xmpDate, 0, sizeof(xmpDate) ); // AUDIT: Using sizeof(xmpDate) is safe.
- xmpDate.year = 1904; // Start at midnight, January 1 1904, UTC
- xmpDate.month = 1;
- xmpDate.day = 1;
+ XMP_Int64 dataSize = info.atomSize - headerSize;
- while ( mvhd.modificationTime > kAlmostMaxSeconds ) {
- xmpDate.second += kAlmostMaxSeconds;
- SXMPUtils::ConvertToUTCTime ( &xmpDate ); // ! For the normalization side effect.
- mvhd.modificationTime -= kAlmostMaxSeconds;
- }
- xmpDate.second += (XMP_Uns32)mvhd.modificationTime;
- SXMPUtils::ConvertToUTCTime ( &xmpDate ); // ! For the normalization side effect.
-
- this->xmpObj.SetProperty_Date ( kXMP_NS_XMP, "ModifyDate", xmpDate );
- this->containsXMP = true;
+ if ( ! doChildren ) {
+ LFA_Seek ( qtFile, dataSize, SEEK_CUR );
+ } else {
+ QTErrorMode innerStatus = CheckAtomList ( qtFile, dataSize, nesting+1 );
+ if ( innerStatus > kBadQT_SmallInner ) return innerStatus; // Quit for serious errors.
+ if ( status == kBadQT_NoError ) status = innerStatus; // Remember small inner errors.
+ }
- }
+ }
+
+ XMP_Assert ( status <= kBadQT_SmallInner ); // Else already returned.
+ // ! Make sure inner kBadQT_SmallInner is propagated if this span is OK.
+
+ if ( spanSize != 0 ) {
+ LFA_Seek ( qtFile, spanSize, SEEK_CUR ); // ! Skip the trailing garbage of this span.
+ status = kBadQT_SmallInner;
+ if ( spanSize >= 8 ) status = kBadQT_LargeInner;
+ if ( nesting == 0 ) status += 2; // Convert to "outer".
+ }
+
+ return status;
+
+} // CheckAtomList
+
+// =================================================================================================
+// AttemptFileRepair
+// =================
+
+static void AttemptFileRepair ( LFA_FileRef qtFile, XMP_Int64 fileSpace, QTErrorMode status )
+{
+
+ switch ( status ) {
+ case kBadQT_NoError : return; // Sanity check.
+ case kBadQT_SmallInner : return; // Fixed in normal update code for the 'udta' box.
+ case kBadQT_LargeInner : XMP_Throw ( "Can't repair QuickTime file", kXMPErr_BadFileFormat );
+ case kBadQT_SmallOuter : break; // Truncate file below.
+ case kBadQT_LargeOuter : break; // Truncate file below.
+ default : XMP_Throw ( "Invalid QuickTime error mode", kXMPErr_InternalFailure );
+ }
+
+ AtomInfo info;
+ XMP_Int64 headerSize;
+
+ // Process the top level atoms until an error is found.
+
+ LFA_Seek ( qtFile, 0, SEEK_SET );
+
+ for ( ; fileSpace >= 8; fileSpace -= info.atomSize ) {
+
+ QTErrorMode atomStatus = GetAtomInfo ( qtFile, fileSpace, 0, &info );
+
+ headerSize = 8; // ! Set this before checking atomStatus, used after the loop.
+ if ( info.hasLargeSize ) headerSize = 16;
+
+ if ( atomStatus != kBadQT_NoError ) break;
+ if ( (info.atomSize < headerSize) || (info.atomSize > fileSpace) ) break;
+
+ XMP_Int64 dataSize = info.atomSize - headerSize;
+ LFA_Seek ( qtFile, dataSize, SEEK_CUR );
+
+ }
+
+ // Truncate the file. If fileSpace >= 8 then the loop exited early due to a bad atom, seek back
+ // to the atom's start. Otherwise, the loop exited because no more atoms are possible, no seek.
+
+ if ( fileSpace >= 8 ) LFA_Seek ( qtFile, -headerSize, SEEK_CUR );
+ XMP_Int64 currPos = LFA_Tell ( qtFile );
+ LFA_Truncate ( qtFile, currPos );
+
+} // AttemptFileRepair
+
+// =================================================================================================
+// CheckQTFileStructure
+// ====================
+
+static void CheckQTFileStructure ( XMPFileHandler * thiz, bool doRepair )
+{
+ XMPFiles * parent = thiz->parent;
+ LFA_FileRef fileRef = parent->fileRef;
+ XMP_Int64 fileSize = LFA_Measure ( fileRef );
+
+ // Check the basic file structure and try to repair if asked.
+
+ LFA_Seek ( fileRef, 0, SEEK_SET );
+ QTErrorMode status = CheckAtomList ( fileRef, fileSize, 0 );
+
+ if ( status != kBadQT_NoError ) {
+ if ( doRepair || (status == kBadQT_SmallInner) || (status == kBadQT_SmallOuter) ) {
+ AttemptFileRepair ( fileRef, fileSize, status ); // Will throw if the attempt fails.
+ } else if ( status != kBadQT_SmallInner ) {
+ XMP_Throw ( "Ill-formed QuickTime file", kXMPErr_BadFileFormat );
+ } else {
+ return; // ! Ignore these, QT seems to be able to handle them.
+ // *** Might want to throw for check-only, ignore when repairing.
+ }
+ }
+
+} // CheckQTFileStructure;
+
+// =================================================================================================
+// CheckFinalBox
+// =============
+//
+// Before appending anything new, check if the final top level box has a "to EoF" length. If so, fix
+// it to have an explicit length.
+
+static void CheckFinalBox ( LFA_FileRef fileRef, XMP_AbortProc abortProc, void * abortArg )
+{
+ const bool checkAbort = (abortProc != 0);
+
+ XMP_Uns64 fileSize = LFA_Measure ( fileRef );
+
+ // Find the last 2 boxes in the file. Need the previous to last in case it is an Apple 'wide' box.
+
+ XMP_Uns64 prevPos, lastPos, nextPos;
+ ISOMedia::BoxInfo prevBox, lastBox;
+ XMP_Uns8 buffer [16]; // Enough to create an extended header.
+
+ memset ( &prevBox, 0, sizeof(prevBox) ); // AUDIT: Using sizeof(prevBox) is safe.
+ memset ( &lastBox, 0, sizeof(lastBox) ); // AUDIT: Using sizeof(lastBox) is safe.
+ prevPos = lastPos = nextPos = 0;
+ while ( nextPos != fileSize ) {
+ if ( checkAbort && abortProc(abortArg) ) {
+ XMP_Throw ( "MPEG4_MetaHandler::CheckFinalBox - User abort", kXMPErr_UserAbort );
+ }
+ prevBox = lastBox;
+ prevPos = lastPos;
+ lastPos = nextPos;
+ nextPos = ISOMedia::GetBoxInfo ( fileRef, lastPos, fileSize, &lastBox, true /* throw errors */ );
+ }
+
+ // See if the last box is valid and has a "to EoF" size.
+
+ if ( lastBox.headerSize < 8 ) XMP_Throw ( "MPEG-4 final box is invalid", kXMPErr_EnforceFailure );
+ LFA_Seek ( fileRef, lastPos, SEEK_SET );
+ LFA_Read ( fileRef, buffer, 4 );
+ XMP_Uns64 lastSize = GetUns32BE ( &buffer[0] ); // ! Yes, the file has a 32-bit value.
+ if ( lastSize != 0 ) return;
+
+ // Have a final "to EoF" box, try to write the explicit size.
+
+ lastSize = lastBox.headerSize + lastBox.contentSize;
+ if ( lastSize <= 0xFFFFFFFFUL ) {
+
+ // Fill in the 32-bit exact size.
+ PutUns32BE ( (XMP_Uns32)lastSize, &buffer[0] );
+ LFA_Seek ( fileRef, lastPos, SEEK_SET );
+ LFA_Write ( fileRef, buffer, 4 );
+
+ } else {
+
+ // Try to convert to using an extended header.
+
+ if ( (prevBox.boxType != ISOMedia::k_wide) || (prevBox.headerSize != 8) || (prevBox.contentSize != 0) ) {
+ XMP_Throw ( "Can't expand final box header", kXMPErr_EnforceFailure );
}
+ XMP_Assert ( prevPos == (lastPos - 8) );
+
+ PutUns32BE ( 1, &buffer[0] );
+ PutUns32BE ( lastBox.boxType, &buffer[4] );
+ PutUns64BE ( lastSize, &buffer[8] );
+ LFA_Seek ( fileRef, prevPos, SEEK_SET );
+ LFA_Write ( fileRef, buffer, 16 );
+
+ }
+
+} // CheckFinalBox
+
+// =================================================================================================
+// WriteBoxHeader
+// ==============
+
+static void WriteBoxHeader ( LFA_FileRef fileRef, XMP_Uns32 boxType, XMP_Uns64 boxSize )
+{
+ XMP_Uns32 u32;
+ XMP_Uns64 u64;
+ XMP_Enforce ( boxSize >= 8 ); // The size must be the full size, not just the content.
+
+ if ( boxSize <= 0xFFFFFFFF ) {
+
+ u32 = MakeUns32BE ( (XMP_Uns32)boxSize );
+ LFA_Write ( fileRef, &u32, 4 );
+ u32 = MakeUns32BE ( boxType );
+ LFA_Write ( fileRef, &u32, 4 );
+
+ } else {
+
+ u32 = MakeUns32BE ( 1 );
+ LFA_Write ( fileRef, &u32, 4 );
+ u32 = MakeUns32BE ( boxType );
+ LFA_Write ( fileRef, &u32, 4 );
+ u64 = MakeUns64BE ( boxSize );
+ LFA_Write ( fileRef, &u64, 8 );
+
+ }
+
+} // WriteBoxHeader
+
+// =================================================================================================
+// WipeBoxFree
+// ===========
+//
+// Change the box's type to 'free' (or create a 'free' box) and zero the content.
- if ( oldDigestFound || (! this->xmpObj.DoesPropertyExist ( kXMP_NS_DM, "duration" )) ) {
- if ( mvhd.timescale != 0 ) { // Avoid 1/0 for the scale field.
- char buffer [32]; // A 64-bit number is at most 20 digits.
- this->xmpObj.DeleteProperty ( kXMP_NS_DM, "duration" ); // Delete the whole struct.
- snprintf ( buffer, sizeof(buffer), "%llu", mvhd.duration ); // AUDIT: The buffer is big enough.
- this->xmpObj.SetStructField ( kXMP_NS_DM, "duration", kXMP_NS_DM, "value", &buffer[0] );
- snprintf ( buffer, sizeof(buffer), "1/%u", mvhd.timescale ); // AUDIT: The buffer is big enough.
- this->xmpObj.SetStructField ( kXMP_NS_DM, "duration", kXMP_NS_DM, "scale", &buffer[0] );
- this->containsXMP = true;
+static XMP_Uns8 kZeroes [64*1024]; // C semantics guarantee zero initialization.
+
+static void WipeBoxFree ( LFA_FileRef fileRef, XMP_Uns64 boxOffset, XMP_Uns32 boxSize )
+{
+ if ( boxSize == 0 ) return;
+ XMP_Enforce ( boxSize >= 8 );
+
+ LFA_Seek ( fileRef, boxOffset, SEEK_SET );
+ XMP_Uns32 u32;
+ u32 = MakeUns32BE ( boxSize ); // ! The actual size should not change, but might have had a long header.
+ LFA_Write ( fileRef, &u32, 4 );
+ u32 = MakeUns32BE ( ISOMedia::k_free );
+ LFA_Write ( fileRef, &u32, 4 );
+
+ XMP_Uns32 ioCount = sizeof ( kZeroes );
+ for ( boxSize -= 8; boxSize > 0; boxSize -= ioCount ) {
+ if ( ioCount > boxSize ) ioCount = boxSize;
+ LFA_Write ( fileRef, &kZeroes[0], ioCount );
+ }
+
+} // WipeBoxFree
+
+// =================================================================================================
+// CreateFreeSpaceList
+// ===================
+
+struct SpaceInfo {
+ XMP_Uns64 offset, size;
+ SpaceInfo() : offset(0), size(0) {};
+ SpaceInfo ( XMP_Uns64 _offset, XMP_Uns64 _size ) : offset(_offset), size(_size) {};
+};
+
+typedef std::vector<SpaceInfo> FreeSpaceList;
+
+static void CreateFreeSpaceList ( LFA_FileRef fileRef, XMP_Uns64 fileSize,
+ XMP_Uns64 oldOffset, XMP_Uns32 oldSize, FreeSpaceList * spaceList )
+{
+ XMP_Uns64 boxPos, boxNext, adjacentFree;
+ ISOMedia::BoxInfo currBox;
+
+ LFA_Seek ( fileRef, 0, SEEK_SET );
+ spaceList->clear();
+
+ for ( boxPos = 0; boxPos < fileSize; boxPos = boxNext ) {
+
+ boxNext = ISOMedia::GetBoxInfo ( fileRef, boxPos, fileSize, &currBox, true /* throw errors */ );
+ XMP_Uns64 currSize = currBox.headerSize + currBox.contentSize;
+
+ if ( (currBox.boxType == ISOMedia::k_free) ||
+ (currBox.boxType == ISOMedia::k_skip) ||
+ ((boxPos == oldOffset) && (currSize == oldSize)) ) {
+
+ if ( spaceList->empty() || (boxPos != adjacentFree) ) {
+ spaceList->push_back ( SpaceInfo ( boxPos, currSize ) );
+ adjacentFree = boxPos + currSize;
+ } else {
+ SpaceInfo * lastSpace = &spaceList->back();
+ lastSpace->size += currSize;
}
+
}
+
+ }
+
+} // CreateFreeSpaceList
+
+// =================================================================================================
+// MPEG4_MetaHandler::CacheFileData
+// ================================
+//
+// There are 3 file variants: normal ISO Base Media, modern QuickTime, and classic QuickTime. The
+// XMP is placed differently between the ISO and two QuickTime forms, and there is different but not
+// colliding native metadata. The entire 'moov' subtree is cached, along with the top level 'uuid'
+// box of XMP if present.
+
+void MPEG4_MetaHandler::CacheFileData()
+{
+ XMP_Assert ( ! this->containsXMP );
+
+ XMPFiles * parent = this->parent;
+ XMP_OptionBits openFlags = parent->openFlags;
+
+ LFA_FileRef fileRef = parent->fileRef;
+
+ XMP_AbortProc abortProc = parent->abortProc;
+ void * abortArg = parent->abortArg;
+ const bool checkAbort = (abortProc != 0);
+
+ // First do some special case repair to QuickTime files, based on bad files in the wild.
+ const bool isUpdate = XMP_OptionIsSet ( openFlags, kXMPFiles_OpenForUpdate );
+ const bool doRepair = XMP_OptionIsSet ( openFlags, kXMPFiles_OpenRepairFile );
+
+ if ( isUpdate && (parent->format == kXMP_MOVFile) ) {
+ CheckQTFileStructure ( this, doRepair ); // Will throw for failure.
}
- if ( oldDigestFound || (! this->xmpObj.DoesPropertyExist ( kXMP_NS_DC, "rights" )) ) {
+ // Cache the top level 'moov' and 'uuid'/XMP boxes.
- std::string tempStr;
- char lang3 [4]; // The unpacked ISO-639-2/T language code.
- lang3[3] = 0;
+ XMP_Uns64 fileSize = LFA_Measure ( fileRef );
- for ( size_t i = 0, limit = this->cprtBoxes.size(); i < limit; ++i ) {
- if ( this->cprtBoxes[i].size() < 7 ) continue;
+ XMP_Uns64 boxPos, boxNext;
+ ISOMedia::BoxInfo currBox;
- const XMP_Uns8 * currCprt = (XMP_Uns8*) (this->cprtBoxes[i].c_str()); // ! Actually structured data!
- size_t rawLen = this->cprtBoxes[i].size() - 6;
- if ( currCprt[0] != 0 ) continue; // Only proceed for version 0, ignore the flags.
+ bool xmpOnly = XMP_OptionIsSet ( openFlags, kXMPFiles_OpenOnlyXMP );
+ bool haveISOFile = (this->fileMode == MOOV_Manager::kFileIsNormalISO);
- XMP_Uns16 packedLang = GetUns16BE ( &currCprt[4] );
- lang3[0] = (packedLang >> 10) | 0x60;
- lang3[1] = ((packedLang >> 5) & 0x1F) | 0x60;
- lang3[2] = (packedLang & 0x1F) | 0x60;
-
- XMP_StringPtr xmpLang = Lookup2LetterLang ( lang3 );
- XMP_StringPtr xmpValue = (XMP_StringPtr) &currCprt[6];
+ bool uuidFound = (! haveISOFile); // Ignore the XMP 'uuid' box for QuickTime files.
+ bool moovIgnored = (xmpOnly & haveISOFile); // Ignore the 'moov' box for XMP-only ISO files.
+ bool moovFound = moovIgnored;
+
+ for ( boxPos = 0; boxPos < fileSize; boxPos = boxNext ) {
+
+ if ( checkAbort && abortProc(abortArg) ) {
+ XMP_Throw ( "MPEG4_MetaHandler::CacheFileData - User abort", kXMPErr_UserAbort );
+ }
+
+
+ boxNext = ISOMedia::GetBoxInfo ( fileRef, boxPos, fileSize, &currBox );
+
+ if ( (! moovFound) && (currBox.boxType == ISOMedia::k_moov) ) {
- if ( (rawLen >= 8) && (GetUns16BE ( xmpValue ) == 0xFEFF) ) {
- FromUTF16 ( (UTF16Unit*)xmpValue, rawLen/2, &tempStr, true /* big endian */ );
- xmpValue = tempStr.c_str();
+ XMP_Uns64 fullMoovSize = currBox.headerSize + currBox.contentSize;
+ if ( fullMoovSize > moovBoxSizeLimit ) { // From here on we know 32-bit offsets are safe.
+ XMP_Throw ( "Oversize 'moov' box", kXMPErr_EnforceFailure );
}
- this->xmpObj.SetLocalizedText ( kXMP_NS_DC, "rights", xmpLang, "", xmpValue );
- this->containsXMP = true;
+ this->moovMgr.fullSubtree.assign ( (XMP_Uns32)fullMoovSize, 0 );
+ LFA_Seek ( fileRef, boxPos, SEEK_SET );
+ LFA_Read ( fileRef, &this->moovMgr.fullSubtree[0], (XMP_Uns32)fullMoovSize );
+
+ this->moovBoxPos = boxPos;
+ this->moovBoxSize = (XMP_Uns32)fullMoovSize;
+ moovFound = true;
+ if ( uuidFound ) break; // Exit the loop when both are found.
+
+ } else if ( (! uuidFound) && (currBox.boxType == ISOMedia::k_uuid) ) {
+
+ if ( currBox.contentSize < 16 ) continue;
+
+ XMP_Uns8 uuid [16];
+ LFA_Read ( fileRef, uuid, 16, kLFA_RequireAll );
+ if ( memcmp ( uuid, ISOMedia::k_xmpUUID, 16 ) != 0 ) continue; // Check for the XMP GUID.
+
+ XMP_Uns64 fullUuidSize = currBox.headerSize + currBox.contentSize;
+ if ( fullUuidSize > moovBoxSizeLimit ) { // From here on we know 32-bit offsets are safe.
+ XMP_Throw ( "Oversize XMP 'uuid' box", kXMPErr_EnforceFailure );
+ }
+
+ this->packetInfo.offset = boxPos + currBox.headerSize + 16; // The 16 is for the UUID.
+ this->packetInfo.length = (XMP_Int32) (currBox.contentSize - 16);
+
+ this->xmpPacket.assign ( this->packetInfo.length, ' ' );
+ LFA_Read ( fileRef, (void*)this->xmpPacket.data(), this->packetInfo.length, kLFA_RequireAll );
+
+ this->xmpBoxPos = boxPos;
+ this->xmpBoxSize = (XMP_Uns32)fullUuidSize;
+ uuidFound = true;
+ if ( moovFound ) break; // Exit the loop when both are found.
}
}
+
+ if ( (! moovFound) && (! moovIgnored) ) XMP_Throw ( "No 'moov' box", kXMPErr_BadFileFormat );
-} // MPEG4_MetaHandler::ProcessXMP
+} // MPEG4_MetaHandler::CacheFileData
// =================================================================================================
-// MPEG4_MetaHandler::PickNewLocation
-// ==================================
-//
-// Pick a new location for the XMP. This is the first available 'free' space before any 'mdat' box,
-// otherwise the end of the file. Any existing XMP 'uuid' box has already been marked as 'free'.
+// MPEG4_MetaHandler::ProcessXMP
+// =============================
-void MPEG4_MetaHandler::PickNewLocation()
+void MPEG4_MetaHandler::ProcessXMP()
{
- LFA_FileRef fileRef = this->parent->fileRef;
- XMP_Uns64 fileSize = LFA_Measure ( fileRef );
+ if ( this->processedXMP ) return;
+ this->processedXMP = true; // Make sure only called once.
- XMP_Uns32 xmpBoxSize = 4+4+16 + (XMP_Uns32)this->xmpPacket.size();
+ XMPFiles * parent = this->parent;
+ XMP_OptionBits openFlags = parent->openFlags;
- XMP_Uns64 currPos, prevPos; // Info about the most recent 2 boxes.
- XMP_Uns32 currType, prevType;
- XMP_Uns64 currSize, prevSize, hSize, cSize;
+ bool xmpOnly = XMP_OptionIsSet ( openFlags, kXMPFiles_OpenOnlyXMP );
+ bool haveISOFile = (this->fileMode == MOOV_Manager::kFileIsNormalISO);
+
+ // Process the cached XMP (from the 'uuid' box) if that is all we want and this is an ISO file.
+
+ if ( xmpOnly & haveISOFile ) {
+
+ this->containsXMP = this->havePreferredXMP = (this->packetInfo.length != 0);
+
+ if ( this->containsXMP ) {
+ FillPacketInfo ( this->xmpPacket, &this->packetInfo );
+ this->xmpObj.ParseFromBuffer ( this->xmpPacket.c_str(), (XMP_StringLen)this->xmpPacket.size() );
+ this->xmpObj.DeleteProperty ( kXMP_NS_XMP, "NativeDigests" ); // No longer used.
+ }
+
+ return;
- XMP_Uns32 be32Size;
- XMP_Uns64 be64Size;
+ }
- XMP_AbortProc abortProc = this->parent->abortProc;
- void * abortArg = this->parent->abortArg;
- const bool checkAbort = (abortProc != 0);
+ // Parse the cached 'moov' subtree, parse the preferred XMP.
- bool pastMDAT = false;
+ if ( this->moovMgr.fullSubtree.empty() ) XMP_Throw ( "No 'moov' box", kXMPErr_BadFileFormat );
+ this->moovMgr.ParseMemoryTree ( this->fileMode );
- currType = 0;
- prevPos = prevSize = currSize = 0;
- for ( currPos = 0; currPos < fileSize; currPos += currSize ) {
+ if ( (this->xmpBoxPos == 0) || (! haveISOFile) ) {
- if ( checkAbort && abortProc(abortArg) ) {
- XMP_Throw ( "MPEG4_MetaHandler::UpdateFile - User abort", kXMPErr_UserAbort );
- }
+ // Look for the QuickTime moov/uuid/XMP_ box.
- prevPos += prevSize; // ! We'll go through at least once, the first box is 'ftyp'.
- prevType = currType; // ! Care is needed to preserve the prevBox and currBox info when
- prevSize = currSize; // ! the loop exits because it hits EoF.
- XMP_Assert ( (prevPos + prevSize) == currPos );
+ MOOV_Manager::BoxInfo xmpInfo;
+ MOOV_Manager::BoxRef xmpRef = this->moovMgr.GetBox ( "moov/udta/XMP_", &xmpInfo );
- GetBoxInfo ( fileRef, fileSize, currPos, &currType, &hSize, &cSize );
- currSize = hSize + cSize;
+ if ( (xmpRef != 0) && (xmpInfo.contentSize != 0) ) {
- if ( pastMDAT ) continue; // Keep scanning to the end of the file.
- if ( currType == kBE_mdat ) pastMDAT = true;
- if ( currType != kBE_free ) continue;
- if ( currSize >= xmpBoxSize ) break;
+ this->xmpBoxPos = this->moovBoxPos + this->moovMgr.GetParsedOffset ( xmpRef );
+ this->packetInfo.offset = this->xmpBoxPos + this->moovMgr.GetHeaderSize ( xmpRef );
+ this->packetInfo.length = xmpInfo.contentSize;
+
+ this->xmpPacket.assign ( (char*)xmpInfo.content, this->packetInfo.length );
+ this->havePreferredXMP = (! haveISOFile);
- if ( prevType == kBE_free ) {
- // If we get here the prevBox and currBox are both 'free' and neither big enough alone.
- // Pretend to combine them for this check and for possible following checks. We don't
- // really compbine them, we just remember the start and total length.
- currPos = prevPos;
- currSize += prevSize;
- prevSize = 0; // ! For the start of the next loop pass.
- if ( currSize >= xmpBoxSize ) break;
}
-
+
+ }
+
+ if ( this->xmpBoxPos != 0 ) {
+ this->containsXMP = true;
+ FillPacketInfo ( this->xmpPacket, &this->packetInfo );
+ this->xmpObj.ParseFromBuffer ( this->xmpPacket.c_str(), (XMP_StringLen)this->xmpPacket.size() );
+ this->xmpObj.DeleteProperty ( kXMP_NS_XMP, "NativeDigests" ); // No longer used.
}
- if ( currPos < fileSize ) {
+ // Import the non-XMP items. Do the imports in reverse priority order, last import wins!
+
+ MOOV_Manager::BoxInfo mvhdInfo;
+ MOOV_Manager::BoxRef mvhdRef = this->moovMgr.GetBox ( "moov/mvhd", &mvhdInfo );
+ bool mvhdFound = ((mvhdRef != 0) && (mvhdInfo.contentSize != 0));
+
+ MOOV_Manager::BoxInfo udtaInfo;
+ MOOV_Manager::BoxRef udtaRef = this->moovMgr.GetBox ( "moov/udta", &udtaInfo );
+ std::vector<MOOV_Manager::BoxInfo> cprtBoxes;
+
+ if ( udtaRef != 0 ) {
+ for ( XMP_Uns32 i = 0; i < udtaInfo.childCount; ++i ) {
+ MOOV_Manager::BoxInfo currInfo;
+ MOOV_Manager::BoxRef currRef = this->moovMgr.GetNthChild ( udtaRef, i, &currInfo );
+ if ( currRef == 0 ) break; // Sanity check, should not happen.
+ if ( currInfo.boxType != ISOMedia::k_cprt ) continue;
+ cprtBoxes.push_back ( currInfo );
+ }
+ }
+ bool cprtFound = (! cprtBoxes.empty());
+
+ bool tradQTFound = this->tradQTMgr.ParseCachedBoxes ( this->moovMgr );
+ bool tmcdFound = this->ParseTimecodeTrack();
- // Put the XMP at the start of the 'free' space. Increase the size of the XMP if the excess
- // is less than 8 bytes, otherwise write a new 'free' box header.
+ if ( this->fileMode == MOOV_Manager::kFileIsNormalISO ) {
+
+ if ( mvhdFound ) this->containsXMP |= ImportMVHDItems ( mvhdInfo, &this->xmpObj );
+ if ( cprtFound ) this->containsXMP |= ImportISOCopyrights ( cprtBoxes, &this->xmpObj );
+ } else { // This is a QuickTime file, either traditional or modern.
+
+ if ( mvhdFound ) this->containsXMP |= ImportMVHDItems ( mvhdInfo, &this->xmpObj );
+ if ( cprtFound ) this->containsXMP |= ImportISOCopyrights ( cprtBoxes, &this->xmpObj );
+ if ( tmcdFound | tradQTFound ) {
+ // Some of the timecode items are in the .../udta/©... set but handled by ImportTimecodeItems.
+ this->containsXMP |= ImportTimecodeItems ( this->tmcdInfo, this->tradQTMgr, &this->xmpObj );
+ }
+
+ this->containsXMP |= ImportCr8rItems ( this->moovMgr, &this->xmpObj );
+
+ }
+
+} // MPEG4_MetaHandler::ProcessXMP
+
+// =================================================================================================
+// MPEG4_MetaHandler::ParseTimecodeTrack
+// =====================================
+
+bool MPEG4_MetaHandler::ParseTimecodeTrack()
+{
+ MOOV_Manager::BoxRef stblRef = FindTimecodeTrack ( this->moovMgr );
+ if ( stblRef == 0 ) return false;
+
+ // Find the .../stbl/stsd box and process the first table entry.
+
+ MOOV_Manager::BoxInfo stsdInfo;
+ MOOV_Manager::BoxRef stsdRef;
+
+ stsdRef = this->moovMgr.GetTypeChild ( stblRef, ISOMedia::k_stsd, &stsdInfo );
+ if ( stsdRef == 0 ) return false;
+ if ( stsdInfo.contentSize < (8 + sizeof ( MOOV_Manager::Content_stsd_entry )) ) return false;
+ if ( GetUns32BE ( stsdInfo.content + 4 ) == 0 ) return false; // Make sure the entry count is non-zero.
+
+ const MOOV_Manager::Content_stsd_entry * stsdRawEntry = (MOOV_Manager::Content_stsd_entry*) (stsdInfo.content + 8);
+
+ XMP_Uns32 stsdEntrySize = GetUns32BE ( &stsdRawEntry->entrySize );
+ if ( stsdEntrySize > (stsdInfo.contentSize - 4) ) stsdEntrySize = stsdInfo.contentSize - 4;
+ if ( stsdEntrySize < sizeof ( MOOV_Manager::Content_stsd_entry ) ) return false;
+
+ XMP_Uns32 stsdEntryFormat = GetUns32BE ( &stsdRawEntry->format );
+ if ( stsdEntryFormat != ISOMedia::k_tmcd ) return false;
+
+ this->tmcdInfo.timeScale = GetUns32BE ( &stsdRawEntry->timeScale );
+ this->tmcdInfo.frameDuration = GetUns32BE ( &stsdRawEntry->frameDuration );
+
+ XMP_Uns32 flags = GetUns32BE ( &stsdRawEntry->flags );
+ this->tmcdInfo.isDropFrame = flags & 0x1;
+
+ // Look for a trailing 'name' box on the first stsd table entry.
+
+ XMP_Uns32 stsdTrailerSize = stsdEntrySize - sizeof ( MOOV_Manager::Content_stsd_entry );
+ if ( stsdTrailerSize > 8 ) { // Room for a non-empty 'name' box?
+
+ const XMP_Uns8 * trailerStart = stsdInfo.content + 8 + sizeof ( MOOV_Manager::Content_stsd_entry );
+ const XMP_Uns8 * trailerLimit = trailerStart + stsdTrailerSize;
+ const XMP_Uns8 * trailerPos;
+ const XMP_Uns8 * trailerNext;
+ ISOMedia::BoxInfo trailerInfo;
- this->xmpBoxPos = currPos;
- XMP_Assert ( (currType == kBE_free) && (currSize >= xmpBoxSize) );
+ for ( trailerPos = trailerStart; trailerPos < trailerLimit; trailerPos = trailerNext ) {
+
+ trailerNext = ISOMedia::GetBoxInfo ( trailerPos, trailerLimit, &trailerInfo );
+
+ if ( trailerInfo.boxType == ISOMedia::k_name ) {
+
+ this->tmcdInfo.nameOffset = (XMP_Uns32) (trailerPos - stsdInfo.content);
+
+ if ( trailerInfo.contentSize > 4 ) {
+
+ XMP_Uns16 textLen = GetUns16BE ( trailerPos + trailerInfo.headerSize );
+ this->tmcdInfo.macLang = GetUns16BE ( trailerPos + trailerInfo.headerSize + 2 );
+
+ if ( trailerInfo.contentSize >= (XMP_Uns64)(textLen + 4) ) {
+ const char * textPtr = (char*) (trailerPos + trailerInfo.headerSize + 4);
+ this->tmcdInfo.macName.assign ( textPtr, textLen );
+ }
+
+ }
+
+ break; // Done after finding the first 'name' box.
- XMP_Uns64 excessSpace = currSize - xmpBoxSize;
- if ( excessSpace < 8 ) {
- this->xmpPacket.append ( (size_t)excessSpace, ' ' );
- } else {
- LFA_Seek ( fileRef, (currPos + xmpBoxSize), SEEK_SET );
- if ( excessSpace <= 0xFFFFFFFFULL ) {
- be32Size = MakeUns32BE ( (XMP_Uns32)excessSpace );
- LFA_Write ( fileRef, &be32Size, 4 );
- LFA_Write ( fileRef, &kBE_free, 4 );
- } else {
- be32Size = MakeUns32BE ( 1 );
- be64Size = MakeUns64BE ( excessSpace );
- LFA_Write ( fileRef, &be32Size, 4 );
- LFA_Write ( fileRef, &kBE_free, 4 );
- LFA_Write ( fileRef, &be64Size, 8 );
}
+
}
+ }
+
+ // Find the timecode sample.
+
+ XMP_Uns64 sampleOffset = 0;
+ MOOV_Manager::BoxInfo tempInfo;
+ MOOV_Manager::BoxRef tempRef;
+
+ tempRef = this->moovMgr.GetTypeChild ( stblRef, ISOMedia::k_stsc, &tempInfo );
+ if ( tempRef == 0 ) return false;
+ if ( tempInfo.contentSize < (8 + sizeof ( MOOV_Manager::Content_stsc_entry )) ) return false;
+ if ( GetUns32BE ( tempInfo.content + 4 ) == 0 ) return false; // Make sure the entry count is non-zero.
+
+ XMP_Uns32 firstChunkNumber = GetUns32BE ( tempInfo.content + 8 ); // Want first field of first entry.
+
+ tempRef = this->moovMgr.GetTypeChild ( stblRef, ISOMedia::k_stco, &tempInfo );
+
+ if ( tempRef != 0 ) {
+
+ if ( tempInfo.contentSize < (8 + 4) ) return false;
+ XMP_Uns32 stcoCount = GetUns32BE ( tempInfo.content + 4 );
+ if ( stcoCount < firstChunkNumber ) return false;
+ XMP_Uns32 * stcoPtr = (XMP_Uns32*) (tempInfo.content + 8);
+ sampleOffset = GetUns32BE ( &stcoPtr[firstChunkNumber-1] ); // ! Chunk number is 1-based.
+
} else {
- // Appending the XMP, make sure the current final box has an explicit size.
+ tempRef = this->moovMgr.GetTypeChild ( stblRef, ISOMedia::k_co64, &tempInfo );
+ if ( (tempRef == 0) || (tempInfo.contentSize < (8 + 8)) ) return false;
+ XMP_Uns32 co64Count = GetUns32BE ( tempInfo.content + 4 );
+ if ( co64Count < firstChunkNumber ) return false;
+ XMP_Uns64 * co64Ptr = (XMP_Uns64*) (tempInfo.content + 8);
+ sampleOffset = GetUns64BE ( &co64Ptr[firstChunkNumber-1] ); // ! Chunk number is 1-based.
+
+ }
+
+ if ( sampleOffset != 0 ) {
+
+ // Read the timecode sample. Need to reopen the file if the XMPFile was open for read-only,
+ // normally all I/O is done within CacheFileData.
+
+ bool openForRead = XMP_OptionIsSet ( this->parent->openFlags, kXMPFiles_OpenForRead);
- this->xmpBoxPos = fileSize;
- XMP_Assert ( currPos == fileSize );
+ LFA_FileRef fileRef = this->parent->fileRef;
+ if ( openForRead ) {
+ XMP_Assert ( fileRef == 0 );
+ fileRef = LFA_Open ( this->parent->filePath.c_str(), 'r' );
+ }
+
+ if ( fileRef != 0 ) { // The reopen might have failed.
+ LFA_Seek ( fileRef, sampleOffset, SEEK_SET );
+ LFA_Read ( fileRef, &this->tmcdInfo.timecodeSample, 4, kLFA_RequireAll );
+ this->tmcdInfo.timecodeSample = MakeUns32BE ( this->tmcdInfo.timecodeSample );
+ if ( openForRead ) LFA_Close ( fileRef );
+ }
- currPos -= currSize; // Move back to the final box's origin.
- LFA_Seek ( fileRef, currPos, SEEK_SET );
- LFA_Read ( fileRef, &be32Size, 4, kLFA_RequireAll );
- be32Size = MakeUns32BE ( be32Size );
+ }
+
+ // Finally update this->tmcdInfo to remember (for update) that there is an OK timecode track.
+
+ this->tmcdInfo.stsdBoxFound = true;
+ this->tmcdInfo.sampleOffset = sampleOffset;
+ return true;
- if ( be32Size == 0 ) {
+} // MPEG4_MetaHandler::ParseTimecodeTrack
+
+// =================================================================================================
+// MPEG4_MetaHandler::UpdateTopLevelBox
+// ====================================
+
+void MPEG4_MetaHandler::UpdateTopLevelBox ( XMP_Uns64 oldOffset, XMP_Uns32 oldSize,
+ const XMP_Uns8 * newBox, XMP_Uns32 newSize )
+{
+ if ( (oldSize == 0) && (newSize == 0) ) return; // Sanity check, should not happen.
+
+ LFA_FileRef fileRef = this->parent->fileRef;
+ XMP_Uns64 oldFileSize = LFA_Measure ( fileRef );
+
+ XMP_AbortProc abortProc = this->parent->abortProc;
+ void * abortArg = this->parent->abortArg;
+
+ if ( newSize == oldSize ) {
+
+ // Trivial case, update the existing box in-place.
+ LFA_Seek ( fileRef, oldOffset, SEEK_SET );
+ LFA_Write ( fileRef, newBox, oldSize );
+
+ } else if ( (oldOffset + oldSize) == oldFileSize ) {
+
+ // The old box was at the end, write the new and truncate the file if necessary.
+ LFA_Seek ( fileRef, oldOffset, SEEK_SET );
+ LFA_Write ( fileRef, newBox, newSize );
+ LFA_Truncate ( fileRef, (oldOffset + newSize) ); // Does nothing if new size is bigger.
+
+ } else if ( (newSize < oldSize) && ((oldSize - newSize) >= 8) ) {
+
+ // The new size is smaller and there is enough room to create a free box.
+ LFA_Seek ( fileRef, oldOffset, SEEK_SET );
+ LFA_Write ( fileRef, newBox, newSize );
+ WipeBoxFree ( fileRef, (oldOffset + newSize), (oldSize - newSize) );
+
+ } else {
+
+ // Look for a trailing free box with enough space. If not found, consider any free space.
+ // If still not found, append the new box and make the old one free.
+
+ ISOMedia::BoxInfo nextBoxInfo;
+ (void) ISOMedia::GetBoxInfo ( fileRef, (oldOffset + oldSize), oldFileSize, &nextBoxInfo, true /* throw errors */ );
+
+ XMP_Uns64 totalRoom = oldSize + nextBoxInfo.headerSize + nextBoxInfo.contentSize;
- // The current final box is "to-EoF", we need to write the actual size. If the size fits
- // in 32 bits then we just set it in the leading Uns32 field instead of the "to-EoF"
- // value. Otherwise we have to insert a 64-bit size. If the previous box is 'free' then
- // we take 8 bytes from the end of it, shift the size/type parts of the final box up 8
- // bytes, making space for the 64-bit size. Give up if the previous box is not 'free'.
+ bool nextIsFree = (nextBoxInfo.boxType == ISOMedia::k_free) || (nextBoxInfo.boxType == ISOMedia::k_skip);
+ bool haveEnoughRoom = (newSize == totalRoom) ||
+ ( (newSize < totalRoom) && ((totalRoom - newSize) >= 8) );
- if ( currSize <= 0xFFFFFFFFULL ) {
+ if ( nextIsFree & haveEnoughRoom ) {
- // The size fits in 32 bits, reuse the leading Uns32 size.
- be32Size = MakeUns32BE ( (XMP_Uns32)currSize );
- LFA_Seek ( fileRef, currPos, SEEK_SET );
- LFA_Write ( fileRef, &be32Size, 4 );
+ LFA_Seek ( fileRef, oldOffset, SEEK_SET );
+ LFA_Write ( fileRef, newBox, newSize );
- } else if ( prevType != kBE_free ) {
+ if ( newSize < totalRoom ) {
+ // Don't wipe, at most 7 old bytes left, it will be covered by the free header.
+ WriteBoxHeader ( fileRef, ISOMedia::k_free, (totalRoom - newSize) );
+ }
- // We need to insert a 64-bit size, but the previous box is not 'free'.
- XMP_Throw ( "MPEG4_MetaHandler::PickNewLocation - Can't set box size", kXMPErr_ExternalFailure );
+ } else {
- } else if ( prevSize == 8 ) {
+ // Create a list of all top level free space, including the old space as free. Use the
+ // earliest space that fits. If none, append.
+
+ FreeSpaceList spaceList;
+ CreateFreeSpaceList ( fileRef, oldFileSize, oldOffset, oldSize, &spaceList );
+
+ size_t freeSlot, limit;
+ for ( freeSlot = 0, limit = spaceList.size(); freeSlot < limit; ++freeSlot ) {
+ XMP_Uns64 freeSize = spaceList[freeSlot].size;
+ if ( (newSize == freeSize) || ( (newSize < freeSize) && ((freeSize - newSize) >= 8) ) ) break;
+ }
+
+ if ( freeSlot == spaceList.size() ) {
+
+ // No available free space, append the new box.
+ CheckFinalBox ( fileRef, abortProc, abortArg );
+ LFA_Seek ( fileRef, 0, SEEK_END );
+ LFA_Write ( fileRef, newBox, newSize );
+ WipeBoxFree ( fileRef, oldOffset, oldSize );
- // Absorb the whole free box.
- LFA_Seek ( fileRef, prevPos, SEEK_SET );
- be32Size = MakeUns32BE ( 1 );
- LFA_Write ( fileRef, &be32Size, 4 );
- LFA_Write ( fileRef, &currType, 4 );
- be64Size = MakeUns64BE ( currSize );
- LFA_Write ( fileRef, &be64Size, 8 );
-
} else {
+
+ // Use the available free space. Wipe non-overlapping parts of the old box. The old
+ // box is either included in the new space, or is fully disjoint.
+
+ SpaceInfo & newSpace = spaceList[freeSlot];
+
+ bool oldIsDisjoint = ((oldOffset + oldSize) <= newSpace.offset) || // Old is in front.
+ ((newSpace.offset + newSpace.size) <= oldOffset); // Old is behind.
+
+ XMP_Assert ( (newSize == newSpace.size) ||
+ ( (newSize < newSpace.size) && ((newSpace.size - newSize) >= 8) ) );
+
+ XMP_Assert ( oldIsDisjoint ||
+ ( (newSpace.offset <= oldOffset) &&
+ ((oldOffset + oldSize) <= (newSpace.offset + newSpace.size)) ) /* old is included */ );
- // Trim 8 bytes off the end of the free box.
+ XMP_Uns64 newFreeOffset = newSpace.offset + newSize;
+ XMP_Uns64 newFreeSize = newSpace.size - newSize;
+
+ LFA_Seek ( fileRef, newSpace.offset, SEEK_SET );
+ LFA_Write ( fileRef, newBox, newSize );
+
+ if ( newFreeSize > 0 ) WriteBoxHeader ( fileRef, ISOMedia::k_free, newFreeSize );
- prevSize -= 8;
-
- LFA_Seek ( fileRef, prevPos, SEEK_SET );
- LFA_Read ( fileRef, &be32Size, 4, kLFA_RequireAll );
- if ( be32Size != MakeUns32BE ( 1 ) ) {
- be32Size = MakeUns32BE ( (XMP_Uns32)prevSize );
- LFA_Seek ( fileRef, prevPos, SEEK_SET );
- LFA_Write ( fileRef, &be32Size, 4 );
+ if ( oldIsDisjoint ) {
+
+ WipeBoxFree ( fileRef, oldOffset, oldSize );
+
} else {
- be64Size = MakeUns64BE ( prevSize );
- LFA_Seek ( fileRef, (prevPos + 8), SEEK_SET );
- LFA_Write ( fileRef, &be64Size, 8 );
- }
- LFA_Seek ( fileRef, (currPos - 8), SEEK_SET );
- be32Size = MakeUns32BE ( 1 );
- LFA_Write ( fileRef, &be32Size, 4 );
- LFA_Write ( fileRef, &currType, 4 );
- be64Size = MakeUns64BE ( currSize );
- LFA_Write ( fileRef, &be64Size, 8 );
+ // Clear the exposed portion of the old box.
+
+ XMP_Uns64 zeroStart = newFreeOffset + 8;
+ if ( newFreeSize > 0xFFFFFFFF ) zeroStart += 8;
+ if ( oldOffset > zeroStart ) zeroStart = oldOffset;
+ XMP_Uns64 zeroEnd = newFreeOffset + newFreeSize;
+ if ( (oldOffset + oldSize) < zeroEnd ) zeroEnd = oldOffset + oldSize;
+
+ if ( zeroStart < zeroEnd ) { // The new box might cover the old.
+ XMP_Assert ( (zeroEnd - zeroStart) <= (XMP_Uns64)oldSize );
+ XMP_Uns32 zeroSize = (XMP_Uns32) (zeroEnd - zeroStart);
+ LFA_Seek ( fileRef, zeroStart, SEEK_SET );
+ for ( XMP_Uns32 ioCount = sizeof ( kZeroes ); zeroSize > 0; zeroSize -= ioCount ) {
+ if ( ioCount > zeroSize ) ioCount = zeroSize;
+ LFA_Write ( fileRef, &kZeroes[0], ioCount );
+ }
+ }
+ }
+
}
}
-
+
}
-
-} // MPEG4_MetaHandler::PickNewLocation
+
+} // MPEG4_MetaHandler::UpdateTopLevelBox
// =================================================================================================
// MPEG4_MetaHandler::UpdateFile
// =============================
-
-// *** There are no writebacks to legacy metadata yet. We need to resolve the questions about
-// *** standard MPEG-4 metadata versus 'ilst' metadata.
-
-// *** The current logic for the XMP is simple. Use the existing XMP if it is big enough, next look
-// *** for 'free' space before any 'mdat' boxes, finally append to the end.
-
-// ! MPEG-4 can be indexed with absolute offsets, only the 'moov' and XMP 'uuid' boxes can be moved!
+//
+// Revamp notes:
+// The 'moov' subtree and possibly the XMP 'uuid' box get updated. Compose the new copy of each and
+// see if it fits in existing space, incorporating adjacent 'free' boxes if necessary. If that won't
+// work, look for a sufficient 'free' box anywhere in the file. As a last resort, append the new copy.
+// Assume no location sensitive data within 'moov', i.e. no offsets into it. This lets it be moved
+// and its children freely rearranged.
void MPEG4_MetaHandler::UpdateFile ( bool doSafeUpdate )
{
- if ( ! this->needsUpdate ) return;
+ if ( ! this->needsUpdate ) { // If needsUpdate is set then at least the XMP changed.
+ return;
+ }
+
this->needsUpdate = false; // Make sure only called once.
XMP_Assert ( ! doSafeUpdate ); // This should only be called for "unsafe" updates.
XMP_AbortProc abortProc = this->parent->abortProc;
void * abortArg = this->parent->abortArg;
const bool checkAbort = (abortProc != 0);
-
+
LFA_FileRef fileRef = this->parent->fileRef;
XMP_Uns64 fileSize = LFA_Measure ( fileRef );
- XMP_Uns32 be32Size;
-
- // Make sure the XMP has a current legacy digest.
- std::string newDigest;
- this->MakeLegacyDigest ( &newDigest );
- this->xmpObj.SetStructField ( kXMP_NS_XMP, "NativeDigests", kXMP_NS_XMP, "MPEG-4", newDigest.c_str(), kXMP_DeleteExisting );
- XMP_StringLen xmpLen = (XMP_StringLen)this->xmpPacket.size();
- try {
- this->xmpObj.SerializeToBuffer ( &this->xmpPacket, (kXMP_UseCompactFormat | kXMP_ExactPacketLength), xmpLen );
- } catch ( ... ) {
- this->xmpObj.SerializeToBuffer ( &this->xmpPacket, kXMP_UseCompactFormat );
- }
-
- #if XMP_DebugBuild // Sanity check that the XMP is where we think it is.
+ bool haveISOFile = (this->fileMode == MOOV_Manager::kFileIsNormalISO);
- if ( this->xmpBoxPos != 0 ) {
-
- XMP_Uns32 boxType;
- XMP_Uns64 hSize, cSize;
- XMP_Uns8 uuid [16];
+ // Update the 'moov' subtree with exports from the XMP, but not the XMP itself (for QT files).
- GetBoxInfo ( fileRef, LFA_Measure ( fileRef ), this->xmpBoxPos, &boxType, &hSize, &cSize );
- LFA_Read ( fileRef, uuid, 16, kLFA_RequireAll );
+ ExportMVHDItems ( this->xmpObj, &this->moovMgr );
+ ExportISOCopyrights ( this->xmpObj, &this->moovMgr );
+ ExportQuickTimeItems ( this->xmpObj, &this->tradQTMgr, &this->moovMgr );
+ ExportTimecodeItems ( this->xmpObj, &this->tmcdInfo, &this->tradQTMgr, &this->moovMgr );
- if ( ((this->xmpBoxPos + hSize + 16) != (XMP_Uns64)this->packetInfo.offset) ||
- ((cSize - 16) != (XMP_Uns64)this->packetInfo.length) ||
- (boxType != kBE_uuid) || (memcmp ( uuid, kBE_xmpUUID, 16 ) != 0) ) {
- XMP_Throw ( "Inaccurate MPEG-4 packet info", kXMPErr_InternalFailure );
- }
-
- }
+ if ( ! haveISOFile ) ExportCr8rItems ( this->xmpObj, &this->moovMgr );
+
+ // Try to update the XMP in-place if that is all that changed, or if it is in a preferred 'uuid' box.
+ // The XMP has already been serialized by common code to the appropriate length. Otherwise, update
+ // the 'moov'/'udta'/'XMP_' box in the MOOV_Manager, or the 'uuid' XMP box in the file.
- #endif
+ bool useUuidXMP = (this->fileMode == MOOV_Manager::kFileIsNormalISO);
- if ( (this->xmpBoxPos != 0) && (this->xmpPacket.size() <= (size_t)this->packetInfo.length) ) {
+ if ( (this->xmpPacket.size() == (size_t)this->packetInfo.length) &&
+ ( (useUuidXMP & this->havePreferredXMP) || (! this->moovMgr.IsChanged()) ) ) {
- // Update existing XMP in-place.
-
- if ( this->xmpPacket.size() < (size_t)this->packetInfo.length ) {
- // They ought to match, cheap to be sure.
- size_t extraSpace = (size_t)this->packetInfo.length - this->xmpPacket.size();
- this->xmpPacket.append ( extraSpace, ' ' );
- }
-
- XMP_Assert ( this->xmpPacket.size() == (size_t)this->packetInfo.length );
+ // Update the existing XMP in-place.
LFA_Seek ( fileRef, this->packetInfo.offset, SEEK_SET );
- LFA_Write ( fileRef, this->xmpPacket.data(), (XMP_Int32)this->xmpPacket.size() );
+ LFA_Write ( fileRef, this->xmpPacket.c_str(), (XMP_Int32)this->xmpPacket.size() );
- } else if ( (this->xmpBoxPos != 0) &&
- ((XMP_Uns64)(this->packetInfo.offset + this->packetInfo.length) == fileSize) ) {
+ } else if ( ! useUuidXMP ) {
- // The XMP is already at the end of the file, rewrite the whole 'uuid' box.
-
- XMP_Assert ( this->xmpPacket.size() > (size_t)this->packetInfo.length );
-
- LFA_Seek ( fileRef, this->xmpBoxPos, SEEK_SET );
- be32Size = MakeUns32BE ( 4+4+16 + (XMP_Uns32)this->xmpPacket.size() ); // ! XMP must be 4GB or less!
- LFA_Write ( fileRef, &be32Size, 4 );
- LFA_Write ( fileRef, &kBE_uuid, 4 );
- LFA_Write ( fileRef, &kBE_xmpUUID, 16 );
- LFA_Write ( fileRef, this->xmpPacket.data(), (XMP_Int32)this->xmpPacket.size() );
+ // Don't leave an old uuid XMP around (if we know about it).
+ if ( (! havePreferredXMP) && (this->xmpBoxSize != 0) ) {
+ WipeBoxFree ( fileRef, this->xmpBoxPos, this->xmpBoxSize );
+ }
+
+ // The udta form of XMP has just the XMP packet.
+ this->moovMgr.SetBox ( "moov/udta/XMP_", this->xmpPacket.c_str(), (XMP_Uns32)this->xmpPacket.size() );
} else {
-
- // We are injecting first time XMP, or making the existing XMP larger. Pick a new location
- // for the XMP. This is the first available space before any 'mdat' box, otherwise the end
- // of the file. Available space can be any contiguous combination of 'free' space with the
- // existing XMP. Mark any existing XMP as 'free' first, this simplifies the logic and we
- // can't do it later since we might reuse the space.
-
- if ( this->xmpBoxPos != 0 ) {
- LFA_Seek ( fileRef, (this->xmpBoxPos + 4), SEEK_SET );
- LFA_Write ( fileRef, "free", 4 );
- }
+
+ // Don't leave an old 'moov'/'udta'/'XMP_' box around.
+ MOOV_Manager::BoxRef udtaRef = this->moovMgr.GetBox ( "moov/udta", 0 );
+ if ( udtaRef != 0 ) this->moovMgr.DeleteTypeChild ( udtaRef, ISOMedia::k_XMP_ );
- this->PickNewLocation(); // ! Might increase the size of the XMP packet.
+ // The uuid form of XMP has the 16-byte UUID in front of the XMP packet. Form the complete
+ // box (including size/type header) for UpdateTopLevelBox.
+ RawDataBlock uuidBox;
+ XMP_Uns32 uuidSize = 4+4 + 16 + (XMP_Uns32)this->xmpPacket.size();
+ uuidBox.assign ( uuidSize, 0 );
+ PutUns32BE ( uuidSize, &uuidBox[0] );
+ PutUns32BE ( ISOMedia::k_uuid, &uuidBox[4] );
+ memcpy ( &uuidBox[8], ISOMedia::k_xmpUUID, 16 );
+ memcpy ( &uuidBox[24], this->xmpPacket.c_str(), this->xmpPacket.size() );
+ this->UpdateTopLevelBox ( this->xmpBoxPos, this->xmpBoxSize, &uuidBox[0], uuidSize );
- LFA_Seek ( fileRef, this->xmpBoxPos, SEEK_SET );
- be32Size = MakeUns32BE ( 4+4+16 + (XMP_Uns32)this->xmpPacket.size() ); // ! XMP must be 4GB or less!
- LFA_Write ( fileRef, &be32Size, 4 );
- LFA_Write ( fileRef, &kBE_uuid, 4 );
- LFA_Write ( fileRef, &kBE_xmpUUID, 16 );
- LFA_Write ( fileRef, this->xmpPacket.data(), (XMP_Int32)this->xmpPacket.size() );
+ }
+
+ // Update the 'moov' subtree if necessary, and finally update the timecode sample.
+ if ( this->moovMgr.IsChanged() ) {
+ this->moovMgr.UpdateMemoryTree();
+ this->UpdateTopLevelBox ( moovBoxPos, moovBoxSize, &this->moovMgr.fullSubtree[0], (XMP_Uns32)this->moovMgr.fullSubtree.size() );
}
+ if ( this->tmcdInfo.sampleOffset != 0 ) {
+ LFA_Seek ( fileRef, this->tmcdInfo.sampleOffset, SEEK_SET );
+ XMP_Uns32 sample = MakeUns32BE ( this->tmcdInfo.timecodeSample );
+ LFA_Write ( fileRef, &sample, 4 );
+ }
+
} // MPEG4_MetaHandler::UpdateFile
// =================================================================================================
@@ -894,14 +2441,14 @@ void MPEG4_MetaHandler::UpdateFile ( bool doSafeUpdate )
void MPEG4_MetaHandler::WriteFile ( LFA_FileRef sourceRef, const std::string & sourcePath )
{
XMP_Assert ( this->needsUpdate );
-
+
LFA_FileRef destRef = this->parent->fileRef;
-
+
LFA_Seek ( sourceRef, 0, SEEK_SET );
LFA_Seek ( destRef, 0, SEEK_SET );
LFA_Copy ( sourceRef, destRef, LFA_Measure ( sourceRef ),
this->parent->abortProc, this->parent->abortArg );
-
+
this->UpdateFile ( false );
} // MPEG4_MetaHandler::WriteFile
diff --git a/source/XMPFiles/FileHandlers/MPEG4_Handler.hpp b/source/XMPFiles/FileHandlers/MPEG4_Handler.hpp
index 9da2741..9d00781 100644
--- a/source/XMPFiles/FileHandlers/MPEG4_Handler.hpp
+++ b/source/XMPFiles/FileHandlers/MPEG4_Handler.hpp
@@ -3,7 +3,7 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2007 Adobe Systems Incorporated
+// Copyright 2006 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -12,6 +12,9 @@
#include "XMPFiles_Impl.hpp"
+#include "MOOV_Support.hpp"
+#include "QuickTime_Support.hpp"
+
// ================================================================================================
/// \file MPEG4_Handler.hpp
/// \brief File format handler for MPEG-4.
@@ -23,9 +26,9 @@
extern XMPFileHandler * MPEG4_MetaHandlerCTor ( XMPFiles * parent );
extern bool MPEG4_CheckFormat ( XMP_FileFormat format,
- XMP_StringPtr filePath,
- LFA_FileRef fileRef,
- XMPFiles * parent );
+ XMP_StringPtr filePath,
+ LFA_FileRef fileRef,
+ XMPFiles * parent );
static const XMP_OptionBits kMPEG4_HandlerFlags = ( kXMPFiles_CanInjectXMP |
kXMPFiles_CanExpand |
@@ -43,24 +46,46 @@ public:
void CacheFileData();
void ProcessXMP();
-
+
void UpdateFile ( bool doSafeUpdate );
void WriteFile ( LFA_FileRef sourceRef, const std::string & sourcePath );
MPEG4_MetaHandler ( XMPFiles * _parent );
virtual ~MPEG4_MetaHandler();
+ struct TimecodeTrackInfo { // Info about a QuickTime timecode track.
+ bool stsdBoxFound, isDropFrame;
+ XMP_Uns32 timeScale;
+ XMP_Uns32 frameDuration;
+ XMP_Uns32 timecodeSample;
+ XMP_Uns64 sampleOffset; // Absolute file offset of the timecode sample, 0 if none.
+ XMP_Uns32 nameOffset; // The offset of the 'name' box relative to the 'stsd' box content.
+ XMP_Uns16 macLang; // The Mac language code of the trailing 'name' box.
+ std::string macName; // The text part of the trailing 'name' box, in macLang encoding.
+ TimecodeTrackInfo()
+ : stsdBoxFound(false), isDropFrame(false), timeScale(0), frameDuration(0),
+ timecodeSample(0), sampleOffset(0), nameOffset(0), macLang(0) {};
+ };
+
private:
- MPEG4_MetaHandler() : xmpBoxPos(0) {}; // Hidden on purpose.
+ MPEG4_MetaHandler() : fileMode(0), havePreferredXMP(false),
+ xmpBoxPos(0), moovBoxPos(0), xmpBoxSize(0), moovBoxSize(0) {}; // Hidden on purpose.
+
+ bool ParseTimecodeTrack();
- void MakeLegacyDigest ( std::string * digestStr );
- void PickNewLocation();
+ void UpdateTopLevelBox ( XMP_Uns64 oldOffset, XMP_Uns32 oldSize, const XMP_Uns8 * newBox, XMP_Uns32 newSize );
+ XMP_Uns8 fileMode;
+ bool havePreferredXMP;
XMP_Uns64 xmpBoxPos; // The file offset of the XMP box (the size field, not the content).
+ XMP_Uns64 moovBoxPos; // The file offset of the 'moov' box (the size field, not the content).
+ XMP_Uns32 xmpBoxSize, moovBoxSize; // The full size of the boxes, not just the content.
+
+ MOOV_Manager moovMgr;
+ TradQT_Manager tradQTMgr;
- std::string mvhdBox; // ! Both contain binary data, but std::string is handy.
- std::vector<std::string> cprtBoxes;
+ TimecodeTrackInfo tmcdInfo;
}; // MPEG4_MetaHandler
diff --git a/source/XMPFiles/FileHandlers/P2_Handler.cpp b/source/XMPFiles/FileHandlers/P2_Handler.cpp
index eac0edf..5e3af85 100644
--- a/source/XMPFiles/FileHandlers/P2_Handler.cpp
+++ b/source/XMPFiles/FileHandlers/P2_Handler.cpp
@@ -1,6 +1,6 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2008 Adobe Systems Incorporated
+// Copyright 2007 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -10,6 +10,7 @@
#include "P2_Handler.hpp"
#include "MD5.h"
+#include <cmath>
using namespace std;
@@ -184,9 +185,9 @@ bool P2_CheckFormat ( XMP_FileFormat format,
tempPath += clipName;
size_t pathLen = tempPath.size() + 1; // Include a terminating nul.
- parent->handlerTemp = malloc ( pathLen );
- if ( parent->handlerTemp == 0 ) XMP_Throw ( "No memory for P2 clip path", kXMPErr_NoMemory );
- memcpy ( parent->handlerTemp, tempPath.c_str(), pathLen ); // AUDIT: Safe, allocated above.
+ parent->tempPtr = malloc ( pathLen );
+ if ( parent->tempPtr == 0 ) XMP_Throw ( "No memory for P2 clip path", kXMPErr_NoMemory );
+ memcpy ( parent->tempPtr, tempPath.c_str(), pathLen ); // AUDIT: Safe, allocated above.
return true;
@@ -213,12 +214,12 @@ P2_MetaHandler::P2_MetaHandler ( XMPFiles * _parent ) : expat(0), clipMetadata(0
this->handlerFlags = kP2_HandlerFlags;
this->stdCharForm = kXMP_Char8Bit;
- // Extract the root path and clip name from handlerTemp.
+ // Extract the root path and clip name from tempPtr.
- XMP_Assert ( this->parent->handlerTemp != 0 );
- this->rootPath = (char*)this->parent->handlerTemp;
- free ( this->parent->handlerTemp );
- this->parent->handlerTemp = 0;
+ XMP_Assert ( this->parent->tempPtr != 0 );
+ this->rootPath = (char*)this->parent->tempPtr;
+ free ( this->parent->tempPtr );
+ this->parent->tempPtr = 0;
SplitLeafName ( &this->rootPath, &this->clipName );
@@ -232,9 +233,9 @@ P2_MetaHandler::~P2_MetaHandler()
{
this->CleanupLegacyXML();
- if ( this->parent->handlerTemp != 0 ) {
- free ( this->parent->handlerTemp );
- this->parent->handlerTemp = 0;
+ if ( this->parent->tempPtr != 0 ) {
+ free ( this->parent->tempPtr );
+ this->parent->tempPtr = 0;
}
} // P2_MetaHandler::~P2_MetaHandler
@@ -256,11 +257,6 @@ void P2_MetaHandler::MakeClipFilePath ( std::string * path, XMP_StringPtr suffix
void P2_MetaHandler::CleanupLegacyXML()
{
-
- if ( ! this->defaultNS.empty() ) {
- SXMPMeta::DeleteNamespace ( this->defaultNS.c_str() );
- this->defaultNS.erase();
- }
if ( this->expat != 0 ) { delete ( this->expat ); this->expat = 0; }
@@ -568,16 +564,42 @@ void P2_MetaHandler::SetVideoFrameInfoFromLegacyXML ( XML_NodePtr legacyVideoCon
dmHeight = "720";
dmWidth = "960";
dmPixelAspectRatio = "1920/1440";
- } else if ( ( p2Codec == "AVC-I_1080/59.94i" ) ||
- ( p2Codec == "AVC-I_1080/50i" ) ||
- ( p2Codec == "AVC-I_1080/29.97p" ) ||
- ( p2Codec == "AVC-I_1080/25p" ) ||
- ( p2Codec == "AVC-I_720/59.94p" ) ||
- ( p2Codec == "AVC-I_720/50p" ) ) {
- // There are two "flavors" of the AVC-Intra codec that compress to different widths (and, therefore
- // different pixel aspect ratios), but no way I can find to distinguish between them in the
- // legacy XML. Until this is resolved we'll just report the codec name.
- dmVideoCompressor = "AVC-Intra";
+ } else if ( ( p2Codec.compare ( 0, 6, "AVC-I_" ) == 0 ) ) {
+
+ // This is AVC-Intra footage. The framerate and PAR depend on the "class" attribute in the P2 XML.
+ const XMP_StringPtr codecClass = legacyProp->GetAttrValue( "Class" );
+
+ if ( XMP_LitMatch ( codecClass, "100" ) ) {
+
+ dmVideoCompressor = "AVC-Intra 100";
+ dmPixelAspectRatio = "1/1";
+
+ if ( p2Codec.compare ( 6, 4, "1080" ) == 0 ) {
+ dmHeight = "1080";
+ dmWidth = "1920";
+ } else if ( p2Codec.compare ( 6, 3, "720" ) == 0 ) {
+ dmHeight = "720";
+ dmWidth = "1280";
+ }
+
+ } else if ( XMP_LitMatch ( codecClass, "50" ) ) {
+
+ dmVideoCompressor = "AVC-Intra 50";
+ dmPixelAspectRatio = "1920/1440";
+
+ if ( p2Codec.compare ( 6, 4, "1080" ) == 0 ) {
+ dmHeight = "1080";
+ dmWidth = "1440";
+ } else if ( p2Codec.compare ( 6, 3, "720" ) == 0 ) {
+ dmHeight = "720";
+ dmWidth = "960";
+ }
+
+ } else {
+ // Unknown codec class -- we don't have enough info to determine the
+ // codec, PAR, or aspect ratio
+ dmVideoCompressor = "AVC-Intra";
+ }
}
if ( dmWidth == "720" ) {
@@ -662,7 +684,8 @@ void P2_MetaHandler::SetStartTimecodeFromLegacyXML ( XML_NodePtr legacyVideoCont
if ( (legacyProp != 0) && legacyProp->IsLeafContentNode() ) {
const std::string p2FrameRate = legacyProp->GetLeafContentValue();
- const XMP_StringPtr p2DropFrameFlag = legacyProp->GetAttrValue ( "DropFrameFlag" );
+ XMP_StringPtr p2DropFrameFlag = legacyProp->GetAttrValue ( "DropFrameFlag" );
+ if ( p2DropFrameFlag == 0 ) p2DropFrameFlag = ""; // Make tests easier.
std::string dmTimeFormat;
if ( ( p2FrameRate == "50i" ) || ( p2FrameRate == "25p" ) ) {
@@ -679,21 +702,21 @@ void P2_MetaHandler::SetStartTimecodeFromLegacyXML ( XML_NodePtr legacyVideoCont
} else if ( p2FrameRate == "59.94p" ) {
- if ( p2DropFrameFlag == "true" ) {
+ if ( XMP_LitMatch ( p2DropFrameFlag, "true" ) ) {
dmTimeFormat = "5994DropTimecode";
- } else if ( p2DropFrameFlag == "false" ) {
+ } else if ( XMP_LitMatch ( p2DropFrameFlag, "false" ) ) {
dmTimeFormat = "5994NonDropTimecode";
}
- } else if ( ( p2FrameRate == "59.94i" ) || ( p2FrameRate == "29.97p" ) ) {
+ } else if ( (p2FrameRate == "59.94i") || (p2FrameRate == "29.97p") ) {
if ( p2DropFrameFlag != 0 ) {
- if ( std::strcmp ( p2DropFrameFlag, "false" ) == 0 ) {
+ if ( XMP_LitMatch ( p2DropFrameFlag, "false" ) ) {
dmTimeFormat = "2997NonDropTimecode";
- } else if ( std::strcmp ( p2DropFrameFlag, "true" ) == 0 ) {
+ } else if ( XMP_LitMatch ( p2DropFrameFlag, "true" ) ) {
// Drop frame NTSC timecode uses semicolons instead of colons as separators.
std::string::iterator currCharIt = p2StartTimecode.begin();
@@ -725,6 +748,101 @@ void P2_MetaHandler::SetStartTimecodeFromLegacyXML ( XML_NodePtr legacyVideoCont
} // P2_MetaHandler::SetStartTimecodeFromLegacyXML
+
+// =================================================================================================
+// P2_MetaHandler::SetGPSPropertyFromLegacyXML
+// ===========================================
+
+void P2_MetaHandler::SetGPSPropertyFromLegacyXML ( XML_NodePtr legacyLocationContext, bool digestFound, XMP_StringPtr propName, XMP_StringPtr legacyPropName )
+{
+
+ if ( digestFound || (! this->xmpObj.DoesPropertyExist ( kXMP_NS_EXIF, propName )) ) {
+
+ XMP_StringPtr p2NS = this->p2NS.c_str();
+ XML_NodePtr legacyGPSProp = legacyLocationContext->GetNamedElement ( p2NS, legacyPropName );
+
+ if ( ( legacyGPSProp != 0 ) && legacyGPSProp->IsLeafContentNode() ) {
+
+ this->xmpObj.DeleteProperty ( kXMP_NS_EXIF, propName );
+
+ const std::string legacyGPSValue = legacyGPSProp->GetLeafContentValue();
+
+ if ( ! legacyGPSValue.empty() ) {
+
+ // Convert from decimal to sexagesimal GPS coordinates
+ char direction = '\0';
+ double degrees = 0.0;
+ const int numFieldsRead = sscanf ( legacyGPSValue.c_str(), "%c%lf", &direction, &degrees );
+
+ if ( numFieldsRead == 2 ) {
+ double wholeDegrees = 0.0;
+ const double fractionalDegrees = modf ( degrees, &wholeDegrees );
+ const double minutes = fractionalDegrees * 60.0;
+ char xmpValue [128];
+
+ sprintf ( xmpValue, "%d,%.5lf%c", static_cast<int>(wholeDegrees), minutes, direction );
+ this->xmpObj.SetProperty ( kXMP_NS_EXIF, propName, xmpValue );
+ this->containsXMP = true;
+
+ }
+
+ }
+
+ }
+
+ }
+
+} // P2_MetaHandler::SetGPSPropertyFromLegacyXML
+
+// =================================================================================================
+// P2_MetaHandler::SetAltitudeFromLegacyXML
+// ========================================
+
+void P2_MetaHandler::SetAltitudeFromLegacyXML ( XML_NodePtr legacyLocationContext, bool digestFound )
+{
+
+ if ( digestFound || (! this->xmpObj.DoesPropertyExist ( kXMP_NS_EXIF, "GPSAltitude" )) ) {
+
+ XMP_StringPtr p2NS = this->p2NS.c_str();
+ XML_NodePtr legacyAltitudeProp = legacyLocationContext->GetNamedElement ( p2NS, "Altitude" );
+
+ if ( ( legacyAltitudeProp != 0 ) && legacyAltitudeProp->IsLeafContentNode() ) {
+
+ this->xmpObj.DeleteProperty ( kXMP_NS_EXIF, "GPSAltitude" );
+
+ const std::string legacyGPSValue = legacyAltitudeProp->GetLeafContentValue();
+
+ if ( ! legacyGPSValue.empty() ) {
+
+ int altitude = 0;
+
+ if ( sscanf ( legacyGPSValue.c_str(), "%d", &altitude ) == 1) {
+
+ if ( altitude >= 0 ) {
+ // At or above sea level.
+ this->xmpObj.SetProperty ( kXMP_NS_EXIF, "GPSAltitudeRef", "0" );
+ } else {
+ // Below sea level.
+ altitude = -altitude;
+ this->xmpObj.SetProperty ( kXMP_NS_EXIF, "GPSAltitudeRef", "1" );
+ }
+
+ char xmpValue [128];
+
+ sprintf ( xmpValue, "%d/1", altitude );
+ this->xmpObj.SetProperty ( kXMP_NS_EXIF, "GPSAltitude", xmpValue );
+ this->containsXMP = true;
+
+ }
+
+ }
+
+ }
+
+ }
+
+} // P2_MetaHandler::SetAltitudeFromLegacyXML
+
// =================================================================================================
// P2_MetaHandler::ForceChildElement
// =================================
@@ -813,12 +931,44 @@ void P2_MetaHandler::MakeLegacyDigest ( std::string * digestStr )
this->DigestLegacyItem ( md5Context, legacyContext, "ShotMark" );
legacyContext = this->clipMetadata->GetNamedElement ( p2NS, "Access" );
+ /* Rather return than create the digest because the "Access" element is listed as "required" in the P2 spec.
+ So a P2 file without an "Access" element does not follow the spec and might be corrupt.*/
if ( legacyContext == 0 ) return;
this->DigestLegacyItem ( md5Context, legacyContext, "Creator" );
this->DigestLegacyItem ( md5Context, legacyContext, "CreationDate" );
this->DigestLegacyItem ( md5Context, legacyContext, "LastUpdateDate" );
+ legacyContext = this->clipMetadata->GetNamedElement ( p2NS, "Shoot" );
+
+ if ( legacyContext != 0 ) {
+ this->DigestLegacyItem ( md5Context, legacyContext, "Shooter" );
+
+ legacyContext = legacyContext->GetNamedElement ( p2NS, "Location" );
+
+ if ( legacyContext != 0 ) {
+ this->DigestLegacyItem ( md5Context, legacyContext, "PlaceName" );
+ this->DigestLegacyItem ( md5Context, legacyContext, "Longitude" );
+ this->DigestLegacyItem ( md5Context, legacyContext, "Latitude" );
+ this->DigestLegacyItem ( md5Context, legacyContext, "Altitude" );
+ }
+ }
+
+ legacyContext = this->clipMetadata->GetNamedElement ( p2NS, "Scenario" );
+
+ if ( legacyContext != 0 ) {
+ this->DigestLegacyItem ( md5Context, legacyContext, "SceneNo." );
+ this->DigestLegacyItem ( md5Context, legacyContext, "TakeNo." );
+ }
+
+ legacyContext = this->clipMetadata->GetNamedElement ( p2NS, "Device" );
+
+ if ( legacyContext != 0 ) {
+ this->DigestLegacyItem ( md5Context, legacyContext, "Manufacturer" );
+ this->DigestLegacyItem ( md5Context, legacyContext, "SerialNo." );
+ this->DigestLegacyItem ( md5Context, legacyContext, "ModelName" );
+ }
+
MD5Final ( digestBin, &md5Context );
char buffer [40];
@@ -838,7 +988,7 @@ void P2_MetaHandler::MakeLegacyDigest ( std::string * digestStr )
void P2_MetaHandler::CacheFileData()
{
- XMP_Assert ( (! this->containsXMP) && (! this->containsTNail) );
+ XMP_Assert ( ! this->containsXMP );
// Make sure the clip's .XMP file exists.
@@ -906,12 +1056,6 @@ void P2_MetaHandler::ProcessXMP()
this->xmpObj.ParseFromBuffer ( this->xmpPacket.c_str(), (XMP_StringLen)this->xmpPacket.size() );
}
- // --------------------------------------------------------------
- // *** This is a minimal Q&D example of legacy metadata handling.
- // *** Hack: Special case trickery to detect and clean up default XML namespace usage.
-
- bool haveDefaultNS = SXMPMeta::GetNamespaceURI ( "_dflt_", 0 ); // Is there already a default namespace?
-
std::string xmlPath;
this->MakeClipFilePath ( &xmlPath, ".XML" );
@@ -919,7 +1063,7 @@ void P2_MetaHandler::ProcessXMP()
xmlFile.fileRef = LFA_Open ( xmlPath.c_str(), 'r' );
if ( xmlFile.fileRef == 0 ) return; // The open failed.
- this->expat = XMP_NewExpatAdapter();
+ this->expat = XMP_NewExpatAdapter ( ExpatAdapter::kUseLocalNamespaces );
if ( this->expat == 0 ) XMP_Throw ( "P2_MetaHandler: Can't create Expat adapter", kXMPErr_NoMemory );
XMP_Uns8 buffer [64*1024];
@@ -933,12 +1077,6 @@ void P2_MetaHandler::ProcessXMP()
LFA_Close ( xmlFile.fileRef );
xmlFile.fileRef = 0;
- if ( ! haveDefaultNS ) {
- // No prior default XML namespace. If there is one now, remember it and delete it when done.
- haveDefaultNS = SXMPMeta::GetNamespaceURI ( "_dflt_", &this->defaultNS );
- XMP_Assert ( haveDefaultNS == (! this->defaultNS.empty()) );
- }
-
// The root element should be P2Main in some namespace. At least 2 different namespaces are in
// use (ending in "v3.0" and "v3.1"). Take whatever this file uses.
@@ -997,7 +1135,7 @@ void P2_MetaHandler::ProcessXMP()
legacyProp = legacyContext->GetNamedElement ( p2NS, "Creator" );
if ( (legacyProp != 0) && legacyProp->IsLeafContentNode() ) {
this->xmpObj.DeleteProperty ( kXMP_NS_DC, "creator" );
- this->xmpObj.AppendArrayItem ( kXMP_NS_DC, "creator", kXMP_PropArrayIsUnordered,
+ this->xmpObj.AppendArrayItem ( kXMP_NS_DC, "creator", kXMP_PropArrayIsOrdered,
legacyProp->GetLeafContentValue() );
this->containsXMP = true;
}
@@ -1006,26 +1144,48 @@ void P2_MetaHandler::ProcessXMP()
this->SetXMPPropertyFromLegacyXML ( digestFound, legacyContext, kXMP_NS_XMP, "CreateDate", "CreationDate", false );
this->SetXMPPropertyFromLegacyXML ( digestFound, legacyContext, kXMP_NS_XMP, "ModifyDate", "LastUpdateDate", false );
- if ( digestFound || (! this->xmpObj.DoesPropertyExist ( kXMP_NS_XMP, "Rating" )) ) {
- legacyProp = legacyContext->GetNamedElement ( p2NS, "ShotMark" );
- if ( (legacyProp != 0) && legacyProp->IsLeafContentNode() ) {
+ if ( digestFound || (! this->xmpObj.DoesPropertyExist ( kXMP_NS_DM, "good" )) ) {
+ legacyProp = this->clipMetadata->GetNamedElement ( p2NS, "ShotMark" );
+ if ( (legacyProp == 0) || (! legacyProp->IsLeafContentNode()) ) {
+ this->xmpObj.DeleteProperty ( kXMP_NS_DM, "good" );
+ } else {
XMP_StringPtr markValue = legacyProp->GetLeafContentValue();
-
- // Translate "marked" clips as having a rating of 1 and "unmarked" clips as having a rating of 0
- if ((markValue == 0) || (strcmp(markValue, "false") == 0) || (strcmp(markValue, "0") == 0)) {
- this->xmpObj.SetProperty ( kXMP_NS_XMP, "Rating", "0", kXMP_DeleteExisting );
+ if ( markValue == 0 ) {
+ this->xmpObj.DeleteProperty ( kXMP_NS_DM, "good" );
+ } else if ( XMP_LitMatch ( markValue, "true" ) || XMP_LitMatch ( markValue, "1" ) ) {
+ this->xmpObj.SetProperty_Bool ( kXMP_NS_DM, "good", true, kXMP_DeleteExisting );
this->containsXMP = true;
- } else if ((strcmp(markValue, "true") == 0) || (strcmp(markValue, "1") == 0)) {
- this->xmpObj.SetProperty ( kXMP_NS_XMP, "Rating", "1", kXMP_DeleteExisting );
+ } else if ( XMP_LitMatch ( markValue, "false" ) || XMP_LitMatch ( markValue, "0" ) ) {
+ this->xmpObj.SetProperty_Bool ( kXMP_NS_DM, "good", false, kXMP_DeleteExisting );
this->containsXMP = true;
}
}
}
- legacyContext = this->clipMetadata->GetNamedElement ( p2NS, "Location" );
+ legacyContext = this->clipMetadata->GetNamedElement ( p2NS, "Shoot" );
+ if ( legacyContext != 0 ) {
+ this->SetXMPPropertyFromLegacyXML ( digestFound, legacyContext, kXMP_NS_TIFF, "Artist", "Shooter", false );
+ legacyContext = legacyContext->GetNamedElement ( p2NS, "Location" );
+ }
if ( legacyContext != 0 ) {
this->SetXMPPropertyFromLegacyXML ( digestFound, legacyContext, kXMP_NS_DM, "shotLocation", "PlaceName", false );
+ this->SetGPSPropertyFromLegacyXML ( legacyContext, digestFound, "GPSLongitude", "Longitude" );
+ this->SetGPSPropertyFromLegacyXML ( legacyContext, digestFound, "GPSLatitude", "Latitude" );
+ this->SetAltitudeFromLegacyXML ( legacyContext, digestFound );
+ }
+
+ legacyContext = this->clipMetadata->GetNamedElement ( p2NS, "Device" );
+ if ( legacyContext != 0 ) {
+ this->SetXMPPropertyFromLegacyXML ( digestFound, legacyContext, kXMP_NS_TIFF, "Make", "Manufacturer", false );
+ this->SetXMPPropertyFromLegacyXML ( digestFound, legacyContext, kXMP_NS_EXIF_Aux, "SerialNumber", "SerialNo.", false );
+ this->SetXMPPropertyFromLegacyXML ( digestFound, legacyContext, kXMP_NS_TIFF, "Model", "ModelName", false );
+ }
+
+ legacyContext = this->clipMetadata->GetNamedElement ( p2NS, "Scenario" );
+ if ( legacyContext != 0 ) {
+ this->SetXMPPropertyFromLegacyXML ( digestFound, legacyContext, kXMP_NS_DM, "scene", "SceneNo.", false );
+ this->SetXMPPropertyFromLegacyXML ( digestFound, legacyContext, kXMP_NS_DM, "takeNumber", "TakeNo.", false );
}
CleanupAndExit
diff --git a/source/XMPFiles/FileHandlers/P2_Handler.hpp b/source/XMPFiles/FileHandlers/P2_Handler.hpp
index 8581fbd..3d0cd96 100644
--- a/source/XMPFiles/FileHandlers/P2_Handler.hpp
+++ b/source/XMPFiles/FileHandlers/P2_Handler.hpp
@@ -3,7 +3,7 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2008 Adobe Systems Incorporated
+// Copyright 2007 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -90,10 +90,12 @@ private:
void SetVideoFrameInfoFromLegacyXML ( XML_NodePtr legacyVideoContext, bool digestFound );
void SetStartTimecodeFromLegacyXML ( XML_NodePtr legacyVideoContext, bool digestFound );
+ void SetGPSPropertyFromLegacyXML ( XML_NodePtr legacyLocationContext, bool digestFound, XMP_StringPtr propName, XMP_StringPtr legacyPropName );
+ void SetAltitudeFromLegacyXML ( XML_NodePtr legacyLocationContext, bool digestFound );
XML_Node * ForceChildElement ( XML_Node * parent, XMP_StringPtr localName, int indent = 0 );
- std::string rootPath, clipName, p2NS, defaultNS;
+ std::string rootPath, clipName, p2NS;
ExpatAdapter * expat;
XML_Node * clipMetadata; // ! Don't delete, points into the Expat tree.
diff --git a/source/XMPFiles/FileHandlers/PNG_Handler.cpp b/source/XMPFiles/FileHandlers/PNG_Handler.cpp
index 7e82eaa..0185fce 100644
--- a/source/XMPFiles/FileHandlers/PNG_Handler.cpp
+++ b/source/XMPFiles/FileHandlers/PNG_Handler.cpp
@@ -1,6 +1,6 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2007 Adobe Systems Incorporated
+// Copyright 2006 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -112,17 +112,6 @@ void PNG_MetaHandler::CacheFileData()
} // PNG_MetaHandler::CacheFileData
// =================================================================================================
-// PNG_MetaHandler::ProcessTNail
-// ==============================
-
-void PNG_MetaHandler::ProcessTNail()
-{
-
- XMP_Throw ( "PNG_MetaHandler::ProcessTNail isn't implemented yet", kXMPErr_Unimplemented );
-
-} // PNG_MetaHandler::ProcessTNail
-
-// =================================================================================================
// PNG_MetaHandler::ProcessXMP
// ============================
//
@@ -260,6 +249,7 @@ bool PNG_MetaHandler::SafeWriteFile ()
ret = true;
} catch ( ... ) {
LFA_Close ( updateRef );
+ LFA_Delete ( updatePath.c_str() );
this->parent->filePath = origPath;
this->parent->fileRef = origRef;
throw;
diff --git a/source/XMPFiles/FileHandlers/PNG_Handler.hpp b/source/XMPFiles/FileHandlers/PNG_Handler.hpp
index 92ee15c..cc978c3 100644
--- a/source/XMPFiles/FileHandlers/PNG_Handler.hpp
+++ b/source/XMPFiles/FileHandlers/PNG_Handler.hpp
@@ -3,7 +3,7 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2007 Adobe Systems Incorporated
+// Copyright 2006 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -25,24 +25,22 @@
extern XMPFileHandler* PNG_MetaHandlerCTor ( XMPFiles* parent );
extern bool PNG_CheckFormat ( XMP_FileFormat format,
- XMP_StringPtr filePath,
- LFA_FileRef fileRef,
- XMPFiles* parent );
+ XMP_StringPtr filePath,
+ LFA_FileRef fileRef,
+ XMPFiles * parent );
static const XMP_OptionBits kPNG_HandlerFlags = ( kXMPFiles_CanInjectXMP |
kXMPFiles_CanExpand |
kXMPFiles_PrefersInPlace |
kXMPFiles_AllowsOnlyXMP |
kXMPFiles_ReturnsRawPacket |
- kXMPFiles_NeedsReadOnlyPacket
- );
+ kXMPFiles_NeedsReadOnlyPacket );
class PNG_MetaHandler : public XMPFileHandler
{
public:
void CacheFileData();
- void ProcessTNail();
void ProcessXMP();
void UpdateFile ( bool doSafeUpdate );
diff --git a/source/XMPFiles/FileHandlers/PSD_Handler.cpp b/source/XMPFiles/FileHandlers/PSD_Handler.cpp
index 600be29..b81ef0c 100644
--- a/source/XMPFiles/FileHandlers/PSD_Handler.cpp
+++ b/source/XMPFiles/FileHandlers/PSD_Handler.cpp
@@ -1,6 +1,6 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2008 Adobe Systems Incorporated
+// Copyright 2006 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -13,6 +13,7 @@
#include "PSIR_Support.hpp"
#include "IPTC_Support.hpp"
#include "ReconcileLegacy.hpp"
+#include "Reconcile_Impl.hpp"
#include "MD5.h"
@@ -47,17 +48,17 @@ bool PSD_CheckFormat ( XMP_FileFormat format,
XMP_Assert ( format == kXMP_PhotoshopFile );
IOBuffer ioBuf;
-
+
LFA_Seek ( fileRef, 0, SEEK_SET );
if ( ! CheckFileSpace ( fileRef, &ioBuf, 34 ) ) return false; // 34 = header plus 2 lengths
-
+
if ( ! CheckBytes ( ioBuf.ptr, "8BPS", 4 ) ) return false;
ioBuf.ptr += 4; // Move to the version.
XMP_Uns16 version = GetUns16BE ( ioBuf.ptr );
if ( (version != 1) && (version != 2) ) return false;
return true;
-
+
} // PSD_CheckFormat
// =================================================================================================
@@ -107,18 +108,18 @@ void PSD_MetaHandler::CacheFileData()
{
LFA_FileRef fileRef = this->parent->fileRef;
XMP_PacketInfo & packetInfo = this->packetInfo;
-
+
XMP_AbortProc abortProc = this->parent->abortProc;
void * abortArg = this->parent->abortArg;
const bool checkAbort = (abortProc != 0);
-
- XMP_Assert ( (! this->containsXMP) && (! this->containsTNail) );
+
+ XMP_Assert ( ! this->containsXMP );
// Set containsXMP to true here only if the XMP image resource is found.
-
+
if ( checkAbort && abortProc(abortArg) ) {
XMP_Throw ( "PSD_MetaHandler::CacheFileData - User abort", kXMPErr_UserAbort );
}
-
+
XMP_Uns8 psdHeader[30];
XMP_Int64 filePos;
XMP_Uns32 ioLen, cmLen, psirLen;
@@ -127,12 +128,12 @@ void PSD_MetaHandler::CacheFileData()
ioLen = LFA_Read ( fileRef, psdHeader, 30 );
if ( ioLen != 30 ) return; // Throw?
-
+
this->imageHeight = GetUns32BE ( &psdHeader[14] );
this->imageWidth = GetUns32BE ( &psdHeader[18] );
-
+
cmLen = GetUns32BE ( &psdHeader[26] );
-
+
XMP_Int64 psirOrigin = 26 + 4 + cmLen;
filePos = LFA_Seek ( fileRef, psirOrigin, SEEK_SET );
@@ -142,12 +143,12 @@ void PSD_MetaHandler::CacheFileData()
if ( ioLen != 4 ) return; // Throw?
psirLen = GetUns32BE ( &psdHeader[0] );
-
+
this->psirMgr.ParseFileResources ( fileRef, psirLen );
-
+
PSIR_Manager::ImgRsrcInfo xmpInfo;
bool found = this->psirMgr.GetImgRsrc ( kPSIR_XMP, &xmpInfo );
-
+
if ( found ) {
// printf ( "PSD_MetaHandler::CacheFileData - XMP packet offset %d (0x%X), size %d\n",
@@ -163,58 +164,8 @@ void PSD_MetaHandler::CacheFileData()
this->containsXMP = true;
}
-
-} // PSD_MetaHandler::CacheFileData
-
-// =================================================================================================
-// PSD_MetaHandler::ProcessTNail
-// =============================
-//
-// A Photoshop file has a thumbnail in image resource 1036. This has the following layout:
-//
-// offset length description
-// 0 4 Format: 0 = kRawRGB, 1 = kJpegRGB
-// 4 4 Thumbnail image width in pixels
-// 8 4 Thumbnail image height in pixels
-// 12 4 Width bytes: Padded row bytes = (width * bits per pixel + 31) / 32 * 4
-// 16 4 Total size = widthbytes * height * planes
-// 20 4 Size after compression, used for consistency check
-// 24 2 Bits per pixel, must be 24
-// 26 2 Number of planes, must be 1
-// 28 - Thumbnail image stream
-//
-// We return the full image resource as the tnailImage part of the XMP_ThumbnailInfo. The client is
-// responsible for all further parsing, including conversions on little endian machines. (Since
-// Photoshop image resources are always stored big endian.)
-//
-// Actually image resource 1036 is for Photoshop 5 and later. Photoshop 4 used image resource 1033,
-// which was similar but used BGR pixels. We are ignoring the earlier format.
-
-void PSD_MetaHandler::ProcessTNail()
-{
- this->processedTNail = true; // Make sure we only come through here once.
- this->containsTNail = false; // Set it to true after all of the info is gathered.
-
- PSIR_Manager::ImgRsrcInfo tnailRsrc;
- bool found = this->psirMgr.GetImgRsrc ( kPSIR_Thumbnail, &tnailRsrc );
- if ( ! found ) return;
-
- this->tnailInfo.fileFormat = this->parent->format;
- this->tnailInfo.tnailFormat = kXMP_PShopTNail;
-
- this->tnailInfo.fullWidth = this->imageWidth;
- this->tnailInfo.fullHeight = this->imageHeight;
-
- this->tnailInfo.tnailImage = (XMP_Uns8*)tnailRsrc.dataPtr;
- this->tnailInfo.tnailSize = tnailRsrc.dataLen;
- this->tnailInfo.tnailWidth = GetUns32BE ( this->tnailInfo.tnailImage + 4 );
- this->tnailInfo.tnailHeight = GetUns32BE ( this->tnailInfo.tnailImage + 8 );
- this->tnailInfo.fullOrientation = this->tnailInfo.tnailOrientation = 0; // ! Not present in PSD files.
-
- this->containsTNail = true;
-
-} // PSD_MetaHandler::ProcessTNail
+} // PSD_MetaHandler::CacheFileData
// =================================================================================================
// PSD_MetaHandler::ProcessXMP
@@ -224,37 +175,19 @@ void PSD_MetaHandler::ProcessTNail()
void PSD_MetaHandler::ProcessXMP()
{
-
+
this->processedXMP = true; // Make sure we only come through here once.
// Set up everything for the legacy import, but don't do it yet. This lets us do a forced legacy
// import if the XMP packet gets parsing errors.
-
- // Parse the IPTC and Exif, determine the last-legacy priority. For PSD files the relevant
- // legacy priorities (ignoring Mac pnot and ANPA resources) are:
- // kLegacyJTP_PSIR_OldCaption - highest
- // kLegacyJTP_PSIR_IPTC
- // kLegacyJTP_None - lowest
-
- // ! Yes, TIFF tags 270 (Image Description, dc:description), 315 (Artist, dc:creator), and
- // ! 33432 (Copyright, dc:rights) are ignored in Photoshop files. The only imported legacy forms
- // ! of these are IPTC DataSets 2:120 (Caption), 2:80 (By-line), and 2:116 ( Copyright Notice).
-
- bool found;
- RecJTP_LegacyPriority lastLegacy = kLegacyJTP_None;
-
+
bool readOnly = ((this->parent->openFlags & kXMPFiles_OpenForUpdate) == 0);
if ( readOnly ) {
this->iptcMgr = new IPTC_Reader();
this->exifMgr = new TIFF_MemoryReader();
} else {
- #if ! XMP_UNIXBuild
- this->iptcMgr = new IPTC_Writer(); // ! Parse it later.
- #else
- // ! Hack until the legacy-as-local issues are resolved for generic UNIX.
- this->iptcMgr = new IPTC_Reader(); // ! Import IPTC but don't export it.
- #endif
+ this->iptcMgr = new IPTC_Writer(); // ! Parse it later.
this->exifMgr = new TIFF_FileWriter();
}
@@ -265,20 +198,25 @@ void PSD_MetaHandler::ProcessXMP()
PSIR_Manager::ImgRsrcInfo iptcInfo, exifInfo;
bool haveIPTC = psir.GetImgRsrc ( kPSIR_IPTC, &iptcInfo );
bool haveExif = psir.GetImgRsrc ( kPSIR_Exif, &exifInfo );
+ int iptcDigestState = kDigestMatches;
- found = psir.GetImgRsrc ( kPSIR_OldCaption, 0 );
- if ( ! found ) found = psir.GetImgRsrc ( kPSIR_OldCaptionPStr, 0 );
- if ( found ) lastLegacy = kLegacyJTP_PSIR_OldCaption;
+ if ( haveExif ) exif.ParseMemoryStream ( exifInfo.dataPtr, exifInfo.dataLen );
if ( haveIPTC ) {
- iptc.ParseMemoryDataSets ( iptcInfo.dataPtr, iptcInfo.dataLen );
- if ( lastLegacy < kLegacyJTP_PSIR_IPTC ) lastLegacy = kLegacyJTP_PSIR_IPTC;
- }
-
- if ( haveExif ) {
- exif.ParseMemoryStream ( exifInfo.dataPtr, exifInfo.dataLen );
+
+ bool haveDigest = false;
+ PSIR_Manager::ImgRsrcInfo digestInfo;
+ haveDigest = psir.GetImgRsrc ( kPSIR_IPTCDigest, &digestInfo );
+ if ( digestInfo.dataLen != 16 ) haveDigest = false;
+
+ if ( ! haveDigest ) {
+ iptcDigestState = kDigestMissing;
+ } else {
+ iptcDigestState = PhotoDataUtils::CheckIPTCDigest ( iptcInfo.dataPtr, iptcInfo.dataLen, digestInfo.dataPtr );
+ }
+
}
-
+
XMP_OptionBits options = 0;
if ( this->containsXMP ) options |= k2XMP_FileHadXMP;
if ( haveIPTC ) options |= k2XMP_FileHadIPTC;
@@ -288,6 +226,8 @@ void PSD_MetaHandler::ProcessXMP()
// exception. This tells the caller that an error happened, but gives them recovered legacy
// should they want to proceed with that.
+ bool haveXMP = false;
+
if ( ! this->xmpPacket.empty() ) {
XMP_Assert ( this->containsXMP );
// Common code takes care of packetInfo.charForm, .padSize, and .writeable.
@@ -295,18 +235,24 @@ void PSD_MetaHandler::ProcessXMP()
XMP_StringLen packetLen = (XMP_StringLen)this->xmpPacket.size();
try {
this->xmpObj.ParseFromBuffer ( packetStr, packetLen );
+ haveXMP = true;
} catch ( ... ) {
XMP_ClearOption ( options, k2XMP_FileHadXMP );
- ImportJTPtoXMP ( kXMP_JPEGFile, lastLegacy, &exif, psir, &iptc, &this->xmpObj, options );
+ if ( haveIPTC ) iptc.ParseMemoryDataSets ( iptcInfo.dataPtr, iptcInfo.dataLen );
+ if ( iptcDigestState == kDigestMatches ) iptcDigestState = kDigestMissing;
+ ImportPhotoData ( exif, iptc, psir, iptcDigestState, &this->xmpObj, options );
throw; // ! Rethrow the exception, don't absorb it.
}
}
// Process the legacy metadata.
- ImportJTPtoXMP ( kXMP_PhotoshopFile, lastLegacy, &exif, psir, &iptc, &this->xmpObj, options );
+ if ( haveIPTC && (! haveXMP) && (iptcDigestState == kDigestMatches) ) iptcDigestState = kDigestMissing;
+ bool parseIPTC = (iptcDigestState != kDigestMatches) || (! readOnly);
+ if ( parseIPTC ) iptc.ParseMemoryDataSets ( iptcInfo.dataPtr, iptcInfo.dataLen );
+ ImportPhotoData ( exif, iptc, psir, iptcDigestState, &this->xmpObj, options );
this->containsXMP = true; // Assume we now have something in the XMP.
-
+
} // PSD_MetaHandler::ProcessXMP
// =================================================================================================
@@ -317,22 +263,33 @@ void PSD_MetaHandler::UpdateFile ( bool doSafeUpdate )
{
XMP_Assert ( ! doSafeUpdate ); // This should only be called for "unsafe" updates.
- // Decide whether to do an in-place update. This can only happen if all of the following are true:
- // - There is an XMP packet in the file.
- // - The are no changes to the legacy image resources. (The IPTC and EXIF are in the PSIR.)
- // - The new XMP can fit in the old space.
-
- ExportXMPtoJTP ( kXMP_PhotoshopFile, &this->xmpObj, this->exifMgr, &this->psirMgr, this->iptcMgr );
-
XMP_Int64 oldPacketOffset = this->packetInfo.offset;
XMP_Int32 oldPacketLength = this->packetInfo.length;
- // printf ( "PSD_MetaHandler::UpdateFile - XMP old packet offset %lld (0x%llX), size %d\n",
- // oldPacketOffset, oldPacketOffset, oldPacketLength );
-
+
if ( oldPacketOffset == kXMPFiles_UnknownOffset ) oldPacketOffset = 0; // ! Simplify checks.
if ( oldPacketLength == kXMPFiles_UnknownLength ) oldPacketLength = 0;
+
+ bool fileHadXMP = ((oldPacketOffset != 0) && (oldPacketLength != 0));
+
+ // Update the IPTC-IIM and native TIFF/Exif metadata. ExportPhotoData also trips the tiff: and
+ // exif: copies from the XMP, so reserialize the now final XMP packet.
- bool doInPlace = (this->xmpPacket.size() <= (size_t)this->packetInfo.length);
+ ExportPhotoData ( kXMP_PhotoshopFile, &this->xmpObj, this->exifMgr, this->iptcMgr, &this->psirMgr );
+
+ try {
+ XMP_OptionBits options = kXMP_UseCompactFormat;
+ if ( fileHadXMP ) options |= kXMP_ExactPacketLength;
+ this->xmpObj.SerializeToBuffer ( &this->xmpPacket, options, oldPacketLength );
+ } catch ( ... ) {
+ this->xmpObj.SerializeToBuffer ( &this->xmpPacket, kXMP_UseCompactFormat );
+ }
+
+ // Decide whether to do an in-place update. This can only happen if all of the following are true:
+ // - There is an XMP packet in the file.
+ // - The are no changes to the legacy image resources. (The IPTC and EXIF are in the PSIR.)
+ // - The new XMP can fit in the old space.
+
+ bool doInPlace = (fileHadXMP && (this->xmpPacket.size() <= (size_t)oldPacketLength));
if ( this->psirMgr.IsLegacyChanged() ) doInPlace = false;
if ( doInPlace ) {
@@ -340,7 +297,7 @@ void PSD_MetaHandler::UpdateFile ( bool doSafeUpdate )
#if GatherPerformanceData
sAPIPerf->back().extraInfo += ", PSD in-place update";
#endif
-
+
if ( this->xmpPacket.size() < (size_t)this->packetInfo.length ) {
// They ought to match, cheap to be sure.
size_t extraSpace = (size_t)this->packetInfo.length - this->xmpPacket.size();
@@ -348,14 +305,14 @@ void PSD_MetaHandler::UpdateFile ( bool doSafeUpdate )
}
LFA_FileRef liveFile = this->parent->fileRef;
-
+
XMP_Assert ( this->xmpPacket.size() == (size_t)oldPacketLength ); // ! Done by common PutXMP logic.
-
+
// printf ( "PSD_MetaHandler::UpdateFile - XMP in-place packet offset %lld (0x%llX), size %d\n",
// oldPacketOffset, oldPacketOffset, this->xmpPacket.size() );
LFA_Seek ( liveFile, oldPacketOffset, SEEK_SET );
LFA_Write ( liveFile, this->xmpPacket.c_str(), (XMP_StringLen)this->xmpPacket.size() );
-
+
} else {
#if GatherPerformanceData
@@ -364,16 +321,16 @@ void PSD_MetaHandler::UpdateFile ( bool doSafeUpdate )
std::string origPath = this->parent->filePath;
LFA_FileRef origRef = this->parent->fileRef;
-
+
std::string updatePath;
LFA_FileRef updateRef = 0;
-
+
CreateTempFile ( origPath, &updatePath, kCopyMacRsrc );
updateRef = LFA_Open ( updatePath.c_str(), 'w' );
-
+
this->parent->filePath = updatePath;
this->parent->fileRef = updateRef;
-
+
try {
XMP_Assert ( ! this->skipReconcile );
this->skipReconcile = true;
@@ -382,21 +339,22 @@ void PSD_MetaHandler::UpdateFile ( bool doSafeUpdate )
} catch ( ... ) {
this->skipReconcile = false;
LFA_Close ( updateRef );
+ LFA_Delete ( updatePath.c_str() );
this->parent->filePath = origPath;
this->parent->fileRef = origRef;
throw;
}
-
+
LFA_Close ( origRef );
LFA_Delete ( origPath.c_str() );
-
+
LFA_Close ( updateRef );
LFA_Rename ( updatePath.c_str(), origPath.c_str() );
this->parent->filePath = origPath;
this->parent->fileRef = 0;
-
+
}
-
+
this->needsUpdate = false;
} // PSD_MetaHandler::UpdateFile
@@ -412,55 +370,57 @@ void PSD_MetaHandler::UpdateFile ( bool doSafeUpdate )
void PSD_MetaHandler::WriteFile ( LFA_FileRef sourceRef, const std::string & sourcePath )
{
LFA_FileRef destRef = this->parent->fileRef;
-
+
XMP_AbortProc abortProc = this->parent->abortProc;
void * abortArg = this->parent->abortArg;
const bool checkAbort = (abortProc != 0);
- XMP_Uns32 sourceLen = (XMP_Uns32) LFA_Measure ( sourceRef );
+ XMP_Uns64 sourceLen = LFA_Measure ( sourceRef );
if ( sourceLen == 0 ) return; // Tolerate empty files.
-
+
// Reconcile the legacy metadata, unless this is called from UpdateFile. Reserialize the XMP to
// get standard padding, PutXMP has probably done an in-place serialize. Set the XMP image resource.
-
+
if ( ! skipReconcile ) {
- ExportXMPtoJTP ( kXMP_PhotoshopFile, &this->xmpObj, this->exifMgr, &this->psirMgr, this->iptcMgr );
+ // Update the IPTC-IIM and native TIFF/Exif metadata, and reserialize the now final XMP packet.
+ ExportPhotoData ( kXMP_JPEGFile, &this->xmpObj, this->exifMgr, this->iptcMgr, &this->psirMgr );
+ this->xmpObj.SerializeToBuffer ( &this->xmpPacket, kXMP_UseCompactFormat );
}
-
+
this->xmpObj.SerializeToBuffer ( &this->xmpPacket, kXMP_UseCompactFormat );
this->packetInfo.offset = kXMPFiles_UnknownOffset;
this->packetInfo.length = (XMP_StringLen)this->xmpPacket.size();
FillPacketInfo ( this->xmpPacket, &this->packetInfo );
this->psirMgr.SetImgRsrc ( kPSIR_XMP, this->xmpPacket.c_str(), (XMP_StringLen)this->xmpPacket.size() );
-
+
// Copy the file header and color mode section, then write the updated image resource section,
// and copy the tail of the source file (layer and mask section to EOF).
-
+
LFA_Seek ( sourceRef, 0, SEEK_SET );
LFA_Truncate (destRef, 0 );
-
+
LFA_Copy ( sourceRef, destRef, 26 ); // Copy the file header.
-
+
XMP_Uns32 cmLen;
LFA_Read ( sourceRef, &cmLen, 4 );
LFA_Write ( destRef, &cmLen, 4 ); // Copy the color mode section length.
cmLen = GetUns32BE ( &cmLen );
LFA_Copy ( sourceRef, destRef, cmLen ); // Copy the color mode section contents.
-
+
XMP_Uns32 irLen;
LFA_Read ( sourceRef, &irLen, 4 ); // Get the source image resource section length.
irLen = GetUns32BE ( &irLen );
-
+
this->psirMgr.UpdateFileResources ( sourceRef, destRef, 0, abortProc, abortArg );
-
- XMP_Uns32 tailOffset = 26 + 4 + cmLen + 4 + irLen;
- XMP_Uns32 tailLength = sourceLen - tailOffset;
+
+ XMP_Uns64 tailOffset = 26 + 4 + cmLen + 4 + irLen;
+ XMP_Uns64 tailLength = sourceLen - tailOffset;
LFA_Seek ( sourceRef, tailOffset, SEEK_SET );
LFA_Seek ( destRef, 0, SEEK_END );
LFA_Copy ( sourceRef, destRef, tailLength ); // Copy the tail of the file.
-
+
this->needsUpdate = false;
} // PSD_MetaHandler::WriteFile
diff --git a/source/XMPFiles/FileHandlers/PSD_Handler.hpp b/source/XMPFiles/FileHandlers/PSD_Handler.hpp
index 385d9bf..7ac56b5 100644
--- a/source/XMPFiles/FileHandlers/PSD_Handler.hpp
+++ b/source/XMPFiles/FileHandlers/PSD_Handler.hpp
@@ -3,7 +3,7 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2007 Adobe Systems Incorporated
+// Copyright 2006 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -38,7 +38,6 @@ static const XMP_OptionBits kPSD_HandlerFlags = (kXMPFiles_CanInjectXMP |
kXMPFiles_CanReconcile |
kXMPFiles_AllowsOnlyXMP |
kXMPFiles_ReturnsRawPacket |
- kXMPFiles_ReturnsTNail |
kXMPFiles_AllowsSafeUpdate);
class PSD_MetaHandler : public XMPFileHandler
@@ -46,7 +45,6 @@ class PSD_MetaHandler : public XMPFileHandler
public:
void CacheFileData();
- void ProcessTNail();
void ProcessXMP();
void UpdateFile ( bool doSafeUpdate );
diff --git a/source/XMPFiles/FileHandlers/PostScript_Handler.cpp b/source/XMPFiles/FileHandlers/PostScript_Handler.cpp
index 2b74646..e925833 100644
--- a/source/XMPFiles/FileHandlers/PostScript_Handler.cpp
+++ b/source/XMPFiles/FileHandlers/PostScript_Handler.cpp
@@ -1,6 +1,6 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2007 Adobe Systems Incorporated
+// Copyright 2004 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -53,15 +53,15 @@ bool PostScript_CheckFormat ( XMP_FileFormat format,
XMP_Int64 psOffset;
size_t psLength;
- long temp1, temp2;
+ XMP_Uns32 temp1, temp2;
// Check for the binary EPSF preview header.
LFA_Seek ( fileRef, 0, SEEK_SET );
if ( ! CheckFileSpace ( fileRef, &ioBuf, 4 ) ) return false;
temp1 = GetUns32BE ( ioBuf.ptr );
-
- if ( temp1 == (long)0xC5D0D3C6 ) {
+
+ if ( temp1 == 0xC5D0D3C6 ) {
if ( ! CheckFileSpace ( fileRef, &ioBuf, 30 ) ) return false;
@@ -86,13 +86,12 @@ bool PostScript_CheckFormat ( XMP_FileFormat format,
// Check the PostScript DSC major version number.
- temp1 = LONG_MIN; // Will safely overflow if there are digits, remain negative if there aren't.
+ temp1 = 0;
while ( (ioBuf.ptr < ioBuf.limit) && ('0' <= *ioBuf.ptr) && (*ioBuf.ptr <= '9') ) {
temp1 = (temp1 * 10) + (*ioBuf.ptr - '0');
- if ( temp1 < 0 ) return false; // Overflow.
+ if ( temp1 > 1000 ) return false; // Overflow.
ioBuf.ptr += 1;
}
- // if ( temp1 < 0 ) break; *** Covered by 3.0 check.
if ( temp1 < 3 ) return false; // The version must be at least 3.0.
if ( ! CheckFileSpace ( fileRef, &ioBuf, 3 ) ) return false;
@@ -101,14 +100,13 @@ bool PostScript_CheckFormat ( XMP_FileFormat format,
// Check the PostScript DSC minor version number.
- temp2 = LONG_MIN; // Will safely overflow if there are digits, remain negative if there aren't.
+ temp2 = 0;
while ( (ioBuf.ptr < ioBuf.limit) && ('0' <= *ioBuf.ptr) && (*ioBuf.ptr <= '9') ) {
temp2 = (temp2 * 10) + (*ioBuf.ptr - '0');
- if ( temp2 < 0 ) return false; // Overflow.
+ if ( temp2 > 1000 ) return false; // Overflow.
ioBuf.ptr += 1;
}
- if ( temp2 < 0 ) return false; // No digits or overflow.
- // Note that we don't care about the actual minor version number.
+ // We don't care about the actual minor version number.
if ( format == kXMP_PostScriptFile ) {
@@ -128,13 +126,12 @@ bool PostScript_CheckFormat ( XMP_FileFormat format,
// Check the EPS major version number.
- temp1 = LONG_MIN; // Will safely overflow if there are digits, remain negative if there aren't.
+ temp1 = 0;
while ( (ioBuf.ptr < ioBuf.limit) && ('0' <= *ioBuf.ptr) && (*ioBuf.ptr <= '9') ) {
temp1 = (temp1 * 10) + (*ioBuf.ptr - '0');
- if ( temp1 < 0 ) return false; // Overflow.
+ if ( temp1 > 1000 ) return false; // Overflow.
ioBuf.ptr += 1;
}
- // if ( temp1 < 0 ) break; *** Covered by 3.0 check.
if ( temp1 < 3 ) return false; // The version must be at least 3.0.
if ( ! CheckFileSpace ( fileRef, &ioBuf, 3 ) ) return false;
@@ -143,14 +140,13 @@ bool PostScript_CheckFormat ( XMP_FileFormat format,
// Check the EPS minor version number.
- temp2 = LONG_MIN; // Will safely overflow if there are digits, remain negative if there aren't.
+ temp2 = 0;
while ( (ioBuf.ptr < ioBuf.limit) && ('0' <= *ioBuf.ptr) && (*ioBuf.ptr <= '9') ) {
temp2 = (temp2 * 10) + (*ioBuf.ptr - '0');
- if ( temp2 < 0 ) return false; // Overflow.
+ if ( temp2 > 1000 ) return false; // Overflow.
ioBuf.ptr += 1;
}
- if ( temp2 < 0 ) return false; // No digits or overflow.
- // Note that we don't care about the actual minor version number.
+ // We don't care about the actual minor version number.
if ( ! CheckFileSpace ( fileRef, &ioBuf, 1 ) ) return false;
if ( (*ioBuf.ptr != kLF) && (*ioBuf.ptr != kCR) ) return false;
@@ -214,9 +210,9 @@ int PostScript_MetaHandler::FindPostScriptHint()
LFA_Seek ( fileRef, 0, SEEK_SET );
if ( ! CheckFileSpace ( fileRef, &ioBuf, 4 ) ) return false;
- long temp1 = GetUns32BE ( ioBuf.ptr );
+ XMP_Uns32 temp1 = GetUns32BE ( ioBuf.ptr );
- if ( temp1 == (long)0xC5D0D3C6 ) {
+ if ( temp1 == 0xC5D0D3C6 ) {
if ( ! CheckFileSpace ( fileRef, &ioBuf, 30 ) ) return false;
@@ -442,7 +438,7 @@ bool PostScript_MetaHandler::FindLastPacket()
// -------------------------------
// Pick the last the valid packet.
- long snipCount = scanner.GetSnipCount();
+ int snipCount = scanner.GetSnipCount();
XMPScanner::SnipInfoVector snips ( snipCount );
scanner.Report ( snips );
diff --git a/source/XMPFiles/FileHandlers/PostScript_Handler.hpp b/source/XMPFiles/FileHandlers/PostScript_Handler.hpp
index 4170451..c0d4e37 100644
--- a/source/XMPFiles/FileHandlers/PostScript_Handler.hpp
+++ b/source/XMPFiles/FileHandlers/PostScript_Handler.hpp
@@ -3,7 +3,7 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2007 Adobe Systems Incorporated
+// Copyright 2004 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
diff --git a/source/XMPFiles/FileHandlers/RIFF_Handler.cpp b/source/XMPFiles/FileHandlers/RIFF_Handler.cpp
new file mode 100644
index 0000000..daa4455
--- /dev/null
+++ b/source/XMPFiles/FileHandlers/RIFF_Handler.cpp
@@ -0,0 +1,347 @@
+// =================================================================================================
+// ADOBE SYSTEMS INCORPORATED
+// Copyright 2009 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 "RIFF.hpp"
+#include "RIFF_Handler.hpp"
+
+using namespace std;
+
+// =================================================================================================
+/// \file RIFF_Handler.cpp
+/// \brief File format handler for RIFF.
+// =================================================================================================
+
+// =================================================================================================
+// RIFF_MetaHandlerCTor
+// ====================
+
+XMPFileHandler * RIFF_MetaHandlerCTor ( XMPFiles * parent )
+{
+ return new RIFF_MetaHandler ( parent );
+}
+
+// =================================================================================================
+// RIFF_CheckFormat
+// ===============
+//
+// An RIFF file must begin with "RIFF", a 4 byte length, then the chunkType (AVI or WAV)
+// The specified length MUST (in practice: SHOULD) match fileSize-8, but we don't bother checking this here.
+
+bool RIFF_CheckFormat ( XMP_FileFormat format,
+ XMP_StringPtr filePath,
+ LFA_FileRef file,
+ XMPFiles* parent )
+{
+ IgnoreParam(format); IgnoreParam(parent);
+ XMP_Assert ( (format == kXMP_AVIFile) || (format == kXMP_WAVFile) );
+ LFA_Rewind( file );
+
+ XMP_Uns8 chunkID[12];
+ LFA_Read( file, chunkID, 12, true );
+ if ( ! CheckBytes( &chunkID[0], "RIFF", 4 )) return false;
+
+ if ( CheckBytes(&chunkID[8],"AVI ",4) && format == kXMP_AVIFile ) return true;
+ if ( CheckBytes(&chunkID[8],"WAVE",4) && format == kXMP_WAVFile ) return true;
+
+ return false;
+} // RIFF_CheckFormat
+
+// =================================================================================================
+// RIFF_MetaHandler::RIFF_MetaHandler
+// ================================
+
+RIFF_MetaHandler::RIFF_MetaHandler ( XMPFiles * _parent )
+{
+ this->parent = _parent;
+ this->handlerFlags = kRIFF_HandlerFlags;
+ this->stdCharForm = kXMP_Char8Bit;
+
+ this->oldFileSize = this->newFileSize = this->trailingGarbageSize = 0;
+ this->level = 0;
+ this->listInfoChunk = this->listTdatChunk = 0;
+ this->dispChunk = this->bextChunk = this->cr8rChunk = this->prmlChunk = 0;
+ this->xmpChunk = 0;
+ this->lastChunk = 0;
+ this->hasListInfoINAM = false;
+}
+
+// =================================================================================================
+// RIFF_MetaHandler::~RIFF_MetaHandler
+// =================================
+
+RIFF_MetaHandler::~RIFF_MetaHandler()
+{
+ while ( ! this->riffChunks.empty() )
+ {
+ delete this->riffChunks.back();
+ this->riffChunks.pop_back();
+ }
+}
+
+// =================================================================================================
+// RIFF_MetaHandler::CacheFileData
+// ==============================
+
+void RIFF_MetaHandler::CacheFileData()
+{
+ this->containsXMP = false; //assume for now
+
+ LFA_FileRef file = this->parent->fileRef;
+ this->oldFileSize = LFA_Measure( file );
+ if ( (this->parent->format == kXMP_WAVFile) && (this->oldFileSize > 0xFFFFFFFF) )
+ XMP_Throw ( "RIFF_MetaHandler::CacheFileData: WAV Files larger 4GB not supported", kXMPErr_Unimplemented );
+
+ LFA_Rewind( file );
+ this->level = 0;
+
+ // parse top-level chunks (most likely just one, except large avi files)
+ XMP_Int64 filePos = 0;
+ while ( filePos < this->oldFileSize )
+ {
+
+ this->riffChunks.push_back( (RIFF::ContainerChunk*) RIFF::getChunk( NULL, this ) );
+
+ // Tolerate limited forms of trailing garbage in a file. Some apps append private data.
+
+ filePos = LFA_Tell( file );
+ XMP_Int64 fileTail = this->oldFileSize - filePos;
+
+ if ( fileTail != 0 ) {
+
+ if ( fileTail < 12 ) {
+
+ this->oldFileSize = filePos; // Pretend the file is smaller.
+ this->trailingGarbageSize = fileTail;
+
+ } else if ( this->parent->format == kXMP_WAVFile ) {
+
+ if ( fileTail < 1024*1024 ) {
+ this->oldFileSize = filePos; // Pretend the file is smaller.
+ this->trailingGarbageSize = fileTail;
+ } else {
+ XMP_Throw ( "Excessive garbage at end of file", kXMPErr_BadFileFormat )
+ }
+
+ } else {
+
+ XMP_Int32 chunkInfo [3];
+ LFA_Read ( file, &chunkInfo, 12, kLFA_RequireAll );
+ LFA_Seek ( file, -12, SEEK_CUR );
+ if ( (GetUns32LE ( &chunkInfo[0] ) != RIFF::kChunk_RIFF) || (GetUns32LE ( &chunkInfo[2] ) != RIFF::kType_AVIX) ) {
+ if ( fileTail < 1024*1024 ) {
+ this->oldFileSize = filePos; // Pretend the file is smaller.
+ this->trailingGarbageSize = fileTail;
+ } else {
+ XMP_Throw ( "Excessive garbage at end of file", kXMPErr_BadFileFormat )
+ }
+ }
+
+ }
+
+ }
+
+ }
+
+ // covered before => internal error if it occurs
+ XMP_Validate( LFA_Tell( file ) == this->oldFileSize,
+ "RIFF_MetaHandler::CacheFileData: unknown data at end of file",
+ kXMPErr_InternalFailure );
+
+} // RIFF_MetaHandler::CacheFileData
+
+// =================================================================================================
+// RIFF_MetaHandler::ProcessXMP
+// ============================
+
+void RIFF_MetaHandler::ProcessXMP()
+{
+ SXMPUtils::RemoveProperties ( &this->xmpObj, 0, 0, kXMPUtil_DoAllProperties );
+ // process physical packet first
+ if ( this->containsXMP ) this->xmpObj.ParseFromBuffer ( this->xmpPacket.c_str(), (XMP_StringLen)this->xmpPacket.size() );
+ // then import native properties:
+ RIFF::importProperties( this );
+ this->processedXMP = true;
+}
+
+// =================================================================================================
+// RIFF_MetaHandler::UpdateFile
+// ===========================
+
+void RIFF_MetaHandler::UpdateFile ( bool doSafeUpdate )
+{
+ XMP_Validate( this->needsUpdate, "nothing to update", kXMPErr_InternalFailure );
+
+ ////////////////////////////////////////////////////////////////////////////////////////
+ //////////// PASS 1: basics, exports, packet reserialze
+ LFA_FileRef file = this->parent->fileRef;
+ RIFF::containerVect *rc = &this->riffChunks;
+
+ //temptemp
+ //printf( "BEFORE:\n%s\n", rc->at(0)->toString().c_str() );
+
+ XMP_Enforce( rc->size() >= 1);
+ RIFF::ContainerChunk* mainChunk = rc->at(0);
+ this->lastChunk = rc->at( rc->size() - 1 ); // (may or may not coincide with mainChunk: )
+ XMP_Enforce( mainChunk != NULL );
+
+ RIFF::relocateWronglyPlacedXMPChunk( this );
+ // [2435625] lifted disablement for AVI
+ RIFF::exportAndRemoveProperties( this );
+
+ // always rewrite both LISTs (implicit size changes, e.g. through 0-term corrections may
+ // have very well led to size changes...)
+ // set XMP packet info, re-serialize
+ this->packetInfo.charForm = stdCharForm;
+ this->packetInfo.writeable = true;
+ this->packetInfo.offset = kXMPFiles_UnknownOffset;
+ this->packetInfo.length = kXMPFiles_UnknownLength;
+
+ // re-serialization ( needed because of above exportAndRemoveProperties() )
+ try {
+ if ( this->xmpChunk == 0 ) // new chunk? pad with 2K
+ this->xmpObj.SerializeToBuffer ( &this->xmpPacket, kXMP_NoOptions , 2048 );
+ else // otherwise try to match former size
+ this->xmpObj.SerializeToBuffer ( &this->xmpPacket, kXMP_ExactPacketLength , (XMP_Uns32) this->xmpChunk->oldSize-8 );
+ } catch ( ... ) { // if that fails, be happy with whatever.
+ this->xmpObj.SerializeToBuffer ( &this->xmpPacket, kXMP_NoOptions );
+ }
+
+ if ( (this->xmpPacket.size() & 1) == 1 ) this->xmpPacket += ' '; // Force the XMP to have an even size.
+
+ // if missing, add xmp packet at end:
+ if( this->xmpChunk == 0 )
+ this->xmpChunk = new RIFF::XMPChunk( this->lastChunk );
+ // * parenting happens within this call.
+ // * size computation will happen in XMPChunk::changesAndSize()
+ // * actual data will be set in XMPChunk::write()
+
+ ////////////////////////////////////////////////////////////////////////////////////////
+ // PASS 2: compute sizes, optimize container structure (no writing yet)
+ {
+ this->newFileSize = 0;
+
+ // note: going through forward (not vice versa) is an important condition,
+ // so that parking LIST:Tdat,Cr8r, PrmL to last chunk is doable
+ // when encountered en route
+ for ( XMP_Uns32 chunkNo = 0; chunkNo < rc->size(); chunkNo++ )
+ {
+ RIFF::Chunk* cur = rc->at(chunkNo);
+ cur->changesAndSize( this );
+ this->newFileSize += cur->newSize;
+ if ( this->newFileSize % 2 == 1 ) this->newFileSize++; // pad byte
+ }
+ this->newFileSize += this->trailingGarbageSize;
+ } // PASS2
+
+ ////////////////////////////////////////////////////////////////////////////////////////
+ // PASS 2a: verify no chunk violates 2GB boundaries
+ switch( this->parent->format )
+ {
+ // NB: <4GB for ALL chunks asserted before in ContainerChunk::changesAndSize()
+
+ case kXMP_AVIFile:
+ // ensure no chunk (single or multi, no matter) crosses 2 GB ...
+ for ( XMP_Int32 chunkNo = 0; chunkNo < (XMP_Int32)rc->size(); chunkNo++ )
+ {
+ if ( rc->at(chunkNo)->oldSize <= 0x80000000LL ) // ... if <2GB before
+ XMP_Validate( rc->at(chunkNo)->newSize <= 0x80000000LL,
+ "Chunk grew beyond 2 GB", kXMPErr_Unimplemented );
+ }
+
+ // compatibility: if single-chunk AND below <1GB, ensure <1GB
+ if ( ( rc->size() > 1 ) && ( rc->at(0)->oldSize < 0x40000000 ) )
+ {
+ XMP_Validate( rc->at(0)->newSize < 0x40000000LL, "compatibility: mainChunk must remain < 1GB" , kXMPErr_Unimplemented );
+ }
+
+ // [2473381] compatibility: if single-chunk AND >2GB,<4GB, ensure <4GB
+ if ( ( rc->size() > 1 ) &&
+ ( rc->at(0)->oldSize > 0x80000000LL ) && // 2GB
+ ( rc->at(0)->oldSize < 0x100000000LL ) ) // 4GB
+ {
+ XMP_Validate( rc->at(0)->newSize < 0x100000000LL, "compatibility: mainChunk must remain < 4GB" , kXMPErr_Unimplemented );
+ }
+
+ break;
+
+ case kXMP_WAVFile:
+ XMP_Validate( 1 == rc->size(), "WAV must be single-chunk", kXMPErr_InternalFailure );
+ XMP_Validate( rc->at(0)->newSize <= 0xFFFFFFFFLL, "WAV above 4 GB not supported", kXMPErr_Unimplemented );
+ break;
+
+ default:
+ XMP_Throw( "unknown format", kXMPErr_InternalFailure );
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////
+ // PASS 3: write avix chunk(s) if applicable (shrinks or stays)
+ // and main chunk. -- operation order depends on mainHasShrunk.
+ {
+ // if needed, extend file beforehand
+ if ( this->newFileSize > this->oldFileSize ) LFA_Extend( file, newFileSize );
+
+ RIFF::Chunk* mainChunk = rc->at(0);
+
+ XMP_Int64 mainGrowth = mainChunk->newSize - mainChunk->oldSize;
+ XMP_Enforce( mainGrowth >= 0 ); // main always stays or grows
+
+ //temptemp
+ //printf( "AFTER:\n%s\n", rc->at(0)->toString().c_str() );
+
+ if ( rc->size() > 1 ) // [2457482]
+ XMP_Validate( mainGrowth == 0, "mainChunk must not grow, if multiple RIFF chunks", kXMPErr_InternalFailure );
+
+ // back to front:
+
+ XMP_Int64 avixStart = newFileSize; // count from the back
+
+ if ( this->trailingGarbageSize != 0 ) {
+ XMP_Int64 goodDataEnd = this->newFileSize - this->trailingGarbageSize;
+ LFA_Move ( file, this->oldFileSize, file, goodDataEnd, this->trailingGarbageSize );
+ avixStart = goodDataEnd;
+ }
+
+ for ( XMP_Int32 chunkNo = ((XMP_Int32)rc->size()) -1; chunkNo >= 0; chunkNo-- )
+ {
+ RIFF::Chunk* cur = rc->at(chunkNo);
+
+ avixStart -= cur->newSize;
+ if ( avixStart % 2 == 1 ) // rewind one more
+ avixStart -= 1;
+
+ LFA_Seek( file, avixStart , SEEK_SET );
+
+ if ( cur->hasChange ) // need explicit write-out ?
+ cur->write( this, file, chunkNo == 0 );
+ else // or will a simple move do?
+ {
+ XMP_Enforce( cur->oldSize == cur->newSize );
+ if ( cur->oldPos != avixStart ) // important optimization: only move if there's a need to
+ LFA_Move( file, cur->oldPos, file, avixStart, cur->newSize );
+ }
+ }
+
+ // if needed, shrink file afterwards
+ if ( this->newFileSize < this->oldFileSize ) LFA_Truncate( file, this->newFileSize );
+ } // PASS 3
+
+ this->needsUpdate = false; //do last for safety
+} // RIFF_MetaHandler::UpdateFile
+
+// =================================================================================================
+// RIFF_MetaHandler::WriteFile
+// ==========================
+
+void RIFF_MetaHandler::WriteFile ( LFA_FileRef sourceRef,
+ const std::string & sourcePath )
+{
+ IgnoreParam(sourceRef); IgnoreParam(sourcePath);
+ XMP_Throw ( "RIFF_MetaHandler::WriteFile: Not supported (must go through UpdateFile", kXMPErr_Unavailable );
+}
+
diff --git a/source/XMPFiles/FileHandlers/RIFF_Handler.hpp b/source/XMPFiles/FileHandlers/RIFF_Handler.hpp
new file mode 100644
index 0000000..093fd63
--- /dev/null
+++ b/source/XMPFiles/FileHandlers/RIFF_Handler.hpp
@@ -0,0 +1,70 @@
+// =================================================================================================
+// ADOBE SYSTEMS INCORPORATED
+// Copyright 2009 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.
+// =================================================================================================
+#ifndef __RIFF_Handler_hpp__
+#define __RIFF_Handler_hpp__ 1
+
+#include "XMP_Const.h"
+#include "XMPFiles_Impl.hpp"
+#include "RIFF_Support.hpp"
+
+// =================================================================================================
+/// \file RIFF_Handler.hpp
+/// \brief File format handler for RIFF (AVI, WAV).
+// =================================================================================================
+
+extern XMPFileHandler * RIFF_MetaHandlerCTor ( XMPFiles * parent );
+
+extern bool RIFF_CheckFormat ( XMP_FileFormat format,
+ XMP_StringPtr filePath,
+ LFA_FileRef fileRef,
+ XMPFiles * parent );
+
+static const XMP_OptionBits kRIFF_HandlerFlags = (kXMPFiles_CanInjectXMP |
+ kXMPFiles_CanExpand |
+ kXMPFiles_PrefersInPlace |
+ kXMPFiles_AllowsOnlyXMP |
+ kXMPFiles_ReturnsRawPacket |
+ kXMPFiles_CanReconcile
+ );
+
+class RIFF_MetaHandler : public XMPFileHandler
+{
+public:
+ RIFF_MetaHandler ( XMPFiles* parent );
+ ~RIFF_MetaHandler();
+
+ void CacheFileData();
+ void ProcessXMP();
+
+ void UpdateFile ( bool doSafeUpdate );
+ void WriteFile ( LFA_FileRef sourceRef, const std::string & sourcePath );
+
+ ////////////////////////////////////////////////////////////////////////////////////
+ // instance vars
+ // most often just one RIFF:* (except for AVI,[AVIX] >1 GB)
+ std::vector<RIFF::ContainerChunk*> riffChunks;
+ XMP_Int64 oldFileSize, newFileSize, trailingGarbageSize;
+
+ // state variables, needed during parsing
+ XMP_Uns8 level;
+
+ RIFF::ContainerChunk *listInfoChunk, *listTdatChunk;
+ RIFF::ValueChunk* dispChunk;
+ RIFF::ValueChunk* bextChunk;
+ RIFF::ValueChunk* cr8rChunk;
+ RIFF::ValueChunk* prmlChunk;
+ RIFF::XMPChunk* xmpChunk;
+ RIFF::ContainerChunk* lastChunk;
+ bool hasListInfoINAM; // needs to be known for the special 3-way merge around dc:title
+
+}; // RIFF_MetaHandler
+
+// =================================================================================================
+
+#endif /* __RIFF_Handler_hpp__ */
diff --git a/source/XMPFiles/FileHandlers/SWF_Handler.cpp b/source/XMPFiles/FileHandlers/SWF_Handler.cpp
index 100f79e..9ae2b25 100644
--- a/source/XMPFiles/FileHandlers/SWF_Handler.cpp
+++ b/source/XMPFiles/FileHandlers/SWF_Handler.cpp
@@ -1,6 +1,6 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2007 Adobe Systems Incorporated
+// Copyright 2006 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -130,17 +130,6 @@ void SWF_MetaHandler::CacheFileData()
} // SWF_MetaHandler::CacheFileData
// =================================================================================================
-// SWF_MetaHandler::ProcessTNail
-// ==============================
-
-void SWF_MetaHandler::ProcessTNail()
-{
-
- XMP_Throw ( "SWF_MetaHandler::ProcessTNail isn't implemented yet", kXMPErr_Unimplemented );
-
-} // SWF_MetaHandler::ProcessTNail
-
-// =================================================================================================
// SWF_MetaHandler::ProcessXMP
// ============================
//
diff --git a/source/XMPFiles/FileHandlers/SWF_Handler.hpp b/source/XMPFiles/FileHandlers/SWF_Handler.hpp
index 511ddd6..91ad760 100644
--- a/source/XMPFiles/FileHandlers/SWF_Handler.hpp
+++ b/source/XMPFiles/FileHandlers/SWF_Handler.hpp
@@ -3,7 +3,7 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2007 Adobe Systems Incorporated
+// Copyright 2006 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -29,23 +29,21 @@
extern XMPFileHandler* SWF_MetaHandlerCTor ( XMPFiles* parent );
extern bool SWF_CheckFormat ( XMP_FileFormat format,
- XMP_StringPtr filePath,
- LFA_FileRef fileRef,
- XMPFiles* parent );
+ XMP_StringPtr filePath,
+ LFA_FileRef fileRef,
+ XMPFiles * parent );
static const XMP_OptionBits kSWF_HandlerFlags = ( kXMPFiles_CanInjectXMP |
kXMPFiles_CanExpand |
kXMPFiles_PrefersInPlace |
kXMPFiles_AllowsOnlyXMP |
- kXMPFiles_ReturnsRawPacket
- );
+ kXMPFiles_ReturnsRawPacket );
class SWF_MetaHandler : public XMPFileHandler
{
public:
void CacheFileData();
- void ProcessTNail();
void ProcessXMP();
XMP_OptionBits GetSerializeOptions();
@@ -56,8 +54,6 @@ public:
SWF_MetaHandler ( XMPFiles* parent );
virtual ~SWF_MetaHandler();
-
-
}; // SWF_MetaHandler
// =================================================================================================
diff --git a/source/XMPFiles/FileHandlers/Scanner_Handler.cpp b/source/XMPFiles/FileHandlers/Scanner_Handler.cpp
index 4959ca0..1af7326 100644
--- a/source/XMPFiles/FileHandlers/Scanner_Handler.cpp
+++ b/source/XMPFiles/FileHandlers/Scanner_Handler.cpp
@@ -1,6 +1,6 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2007 Adobe Systems Incorporated
+// Copyright 2004 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -107,9 +107,6 @@ PickMainPacket ( std::vector<CandidateInfo>& candidates, bool beLenient )
for ( child = 0; child < (int)candidates.size(); ++child ) {
if ( pruned[child] || (child == pkt) ) continue; // Skip already pruned ones and self.
-#if 0 // *** Disable for now, SXMPUtils::HasContainedDoc is Adobe private.
- pruned[child] = SXMPUtils::HasContainedDoc ( *candidates[pkt].xmpObj, *candidates[child].xmpObj );
-#endif
}
}
diff --git a/source/XMPFiles/FileHandlers/Scanner_Handler.hpp b/source/XMPFiles/FileHandlers/Scanner_Handler.hpp
index 625b565..a6f23b5 100644
--- a/source/XMPFiles/FileHandlers/Scanner_Handler.hpp
+++ b/source/XMPFiles/FileHandlers/Scanner_Handler.hpp
@@ -3,7 +3,7 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2007 Adobe Systems Incorporated
+// Copyright 2004 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
diff --git a/source/XMPFiles/FileHandlers/SonyHDV_Handler.cpp b/source/XMPFiles/FileHandlers/SonyHDV_Handler.cpp
index 2dca918..d3758da 100644
--- a/source/XMPFiles/FileHandlers/SonyHDV_Handler.cpp
+++ b/source/XMPFiles/FileHandlers/SonyHDV_Handler.cpp
@@ -1,6 +1,6 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2008 Adobe Systems Incorporated
+// Copyright 2007 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -134,16 +134,16 @@ bool SonyHDV_CheckFormat ( XMP_FileFormat format,
clipName = leafName;
// Set tempPath to <root>/<clip-name>, e.g. ".../MyMovie/00_0001_2007-08-06_165555". This is
- // passed to the handler via the handlerTemp hackery.
+ // passed to the handler via the tempPtr hackery.
tempPath = rootPath;
tempPath += kDirChar;
tempPath += clipName;
size_t pathLen = tempPath.size() + 1; // Include a terminating nul.
- parent->handlerTemp = malloc ( pathLen );
- if ( parent->handlerTemp == 0 ) XMP_Throw ( "No memory for SonyHDV clip info", kXMPErr_NoMemory );
- memcpy ( parent->handlerTemp, tempPath.c_str(), pathLen ); // AUDIT: Safe, allocated above.
+ parent->tempPtr = malloc ( pathLen );
+ if ( parent->tempPtr == 0 ) XMP_Throw ( "No memory for SonyHDV clip info", kXMPErr_NoMemory );
+ memcpy ( parent->tempPtr, tempPath.c_str(), pathLen ); // AUDIT: Safe, allocated above.
return true;
@@ -486,11 +486,11 @@ SonyHDV_MetaHandler::SonyHDV_MetaHandler ( XMPFiles * _parent )
// Extract the root path and clip name.
- XMP_Assert ( this->parent->handlerTemp != 0 );
+ XMP_Assert ( this->parent->tempPtr != 0 );
- this->rootPath.assign ( (char*) this->parent->handlerTemp );
- free ( this->parent->handlerTemp );
- this->parent->handlerTemp = 0;
+ this->rootPath.assign ( (char*) this->parent->tempPtr );
+ free ( this->parent->tempPtr );
+ this->parent->tempPtr = 0;
SplitLeafName ( &this->rootPath, &this->clipName );
@@ -503,9 +503,9 @@ SonyHDV_MetaHandler::SonyHDV_MetaHandler ( XMPFiles * _parent )
SonyHDV_MetaHandler::~SonyHDV_MetaHandler()
{
- if ( this->parent->handlerTemp != 0 ) {
- free ( this->parent->handlerTemp );
- this->parent->handlerTemp = 0;
+ if ( this->parent->tempPtr != 0 ) {
+ free ( this->parent->tempPtr );
+ this->parent->tempPtr = 0;
}
} // SonyHDV_MetaHandler::~SonyHDV_MetaHandler
@@ -630,7 +630,7 @@ void SonyHDV_MetaHandler::MakeLegacyDigest ( std::string * digestStr )
void SonyHDV_MetaHandler::CacheFileData()
{
- XMP_Assert ( (! this->containsXMP) && (! this->containsTNail) );
+ XMP_Assert ( ! this->containsXMP );
// See if the clip's .XMP file exists.
diff --git a/source/XMPFiles/FileHandlers/SonyHDV_Handler.hpp b/source/XMPFiles/FileHandlers/SonyHDV_Handler.hpp
index a69f0dc..2af4ffc 100644
--- a/source/XMPFiles/FileHandlers/SonyHDV_Handler.hpp
+++ b/source/XMPFiles/FileHandlers/SonyHDV_Handler.hpp
@@ -3,7 +3,7 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2008 Adobe Systems Incorporated
+// Copyright 2007 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
diff --git a/source/XMPFiles/FileHandlers/TIFF_Handler.cpp b/source/XMPFiles/FileHandlers/TIFF_Handler.cpp
index a12e718..15332e6 100644
--- a/source/XMPFiles/FileHandlers/TIFF_Handler.cpp
+++ b/source/XMPFiles/FileHandlers/TIFF_Handler.cpp
@@ -1,6 +1,6 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2008 Adobe Systems Incorporated
+// Copyright 2006 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -13,6 +13,7 @@
#include "PSIR_Support.hpp"
#include "IPTC_Support.hpp"
#include "ReconcileLegacy.hpp"
+#include "Reconcile_Impl.hpp"
#include "MD5.h"
@@ -42,19 +43,19 @@ bool TIFF_CheckFormat ( XMP_FileFormat format,
{
IgnoreParam(format); IgnoreParam(filePath); IgnoreParam(parent);
XMP_Assert ( format == kXMP_TIFFFile );
-
+
enum { kMinimalTIFFSize = 4+4+2+12+4 }; // Header plus IFD with 1 entry.
IOBuffer ioBuf;
-
+
LFA_Seek ( fileRef, 0, SEEK_SET );
if ( ! CheckFileSpace ( fileRef, &ioBuf, kMinimalTIFFSize ) ) return false;
-
+
bool leTIFF = CheckBytes ( ioBuf.ptr, "\x49\x49\x2A\x00", 4 );
bool beTIFF = CheckBytes ( ioBuf.ptr, "\x4D\x4D\x00\x2A", 4 );
-
+
return (leTIFF | beTIFF);
-
+
} // TIFF_CheckFormat
// =================================================================================================
@@ -106,24 +107,24 @@ void TIFF_MetaHandler::CacheFileData()
{
LFA_FileRef fileRef = this->parent->fileRef;
XMP_PacketInfo & packetInfo = this->packetInfo;
-
+
XMP_AbortProc abortProc = this->parent->abortProc;
void * abortArg = this->parent->abortArg;
const bool checkAbort = (abortProc != 0);
-
- XMP_Assert ( (! this->containsXMP) && (! this->containsTNail) );
+
+ XMP_Assert ( ! this->containsXMP );
// Set containsXMP to true here only if the XMP tag is found.
-
+
if ( checkAbort && abortProc(abortArg) ) {
XMP_Throw ( "TIFF_MetaHandler::CacheFileData - User abort", kXMPErr_UserAbort );
}
-
+
this->tiffMgr.ParseFileStream ( fileRef );
-
+
TIFF_Manager::TagInfo dngInfo;
if ( this->tiffMgr.GetTag ( kTIFF_PrimaryIFD, kTIFF_DNGVersion, &dngInfo ) ) {
- // Reject DNG files that are version 2.0 or beyond, this is being written at the time of
+ // Reject DNG files that are version 2.0 or beyond, this is being written at the time of
// DNG version 1.2. The DNG team says it is OK to use 2.0, not strictly 1.2. Use the
// DNGBackwardVersion if it is present, else the DNGVersion. Note that the version value is
// supposed to be type BYTE, so the file order is always essentially big endian.
@@ -135,10 +136,10 @@ void TIFF_MetaHandler::CacheFileData()
if ( majorVersion > 1 ) XMP_Throw ( "DNG version beyond 1.x", kXMPErr_BadTIFF );
}
-
+
TIFF_Manager::TagInfo xmpInfo;
bool found = this->tiffMgr.GetTag ( kTIFF_PrimaryIFD, kTIFF_XMP, &xmpInfo );
-
+
if ( found ) {
this->packetInfo.offset = this->tiffMgr.GetValueOffset ( kTIFF_PrimaryIFD, kTIFF_XMP );
@@ -152,27 +153,8 @@ void TIFF_MetaHandler::CacheFileData()
this->containsXMP = true;
}
-
-} // TIFF_MetaHandler::CacheFileData
-
-// =================================================================================================
-// TIFF_MetaHandler::ProcessTNail
-// ==============================
-//
-// Do the same processing as JPEG, even though Exif says that uncompressed images can only have
-// uncompressed thumbnails. Who know what someone might write?
-
-// *** Should extract this code into a utility shared with the JPEG handler.
-void TIFF_MetaHandler::ProcessTNail()
-{
- this->processedTNail = true; // Make sure we only come through here once.
- this->containsTNail = false; // Set it to true after all of the info is gathered.
-
- this->containsTNail = this->tiffMgr.GetTNailInfo ( &this->tnailInfo );
- if ( this->containsTNail ) this->tnailInfo.fileFormat = this->parent->format;
-
-} // TIFF_MetaHandler::ProcessTNail
+} // TIFF_MetaHandler::CacheFileData
// =================================================================================================
// TIFF_MetaHandler::ProcessXMP
@@ -184,29 +166,19 @@ void TIFF_MetaHandler::ProcessTNail()
void TIFF_MetaHandler::ProcessXMP()
{
-
+
this->processedXMP = true; // Make sure we only come through here once.
// Set up everything for the legacy import, but don't do it yet. This lets us do a forced legacy
// import if the XMP packet gets parsing errors.
- // Parse the IPTC and PSIR, determine the last-legacy priority. For TIFF files the relevant
- // legacy priorities (ignoring Mac pnot and ANPA resources) are:
- // kLegacyJTP_TIFF_IPTC - highest
- // kLegacyJTP_TIFF_TIFF_Tags
- // kLegacyJTP_PSIR_OldCaption
- // kLegacyJTP_PSIR_IPTC - yes, a TIFF file can have the IPTC in 2 places
- // kLegacyJTP_None - lowest
-
// ! Photoshop 6 wrote annoyingly wacky TIFF files. It buried a lot of the Exif metadata inside
// ! image resource 1058, itself inside of tag 34377 in the 0th IFD. Take care of this before
// ! doing any of the legacy metadata presence or priority analysis. Delete image resource 1058
// ! to get rid of the buried Exif, but don't mark the XMPFiles object as changed. This change
// ! should not trigger an update, but should be included as part of a normal update.
-
- bool found;
- RecJTP_LegacyPriority lastLegacy = kLegacyJTP_None;
+ bool found;
bool readOnly = ((this->parent->openFlags & kXMPFiles_OpenForUpdate) == 0);
if ( readOnly ) {
@@ -214,24 +186,16 @@ void TIFF_MetaHandler::ProcessXMP()
this->iptcMgr = new IPTC_Reader();
} else {
this->psirMgr = new PSIR_FileWriter();
- #if ! XMP_UNIXBuild
- this->iptcMgr = new IPTC_Writer(); // ! Parse it later.
- #else
- // ! Hack until the legacy-as-local issues are resolved for generic UNIX.
- this->iptcMgr = new IPTC_Reader(); // ! Import IPTC but don't export it.
- #endif
+ this->iptcMgr = new IPTC_Writer(); // ! Parse it later.
}
TIFF_Manager & tiff = this->tiffMgr; // Give the compiler help in recognizing non-aliases.
PSIR_Manager & psir = *this->psirMgr;
IPTC_Manager & iptc = *this->iptcMgr;
-
+
TIFF_Manager::TagInfo psirInfo;
bool havePSIR = tiff.GetTag ( kTIFF_PrimaryIFD, kTIFF_PSIR, &psirInfo );
-
- TIFF_Manager::TagInfo iptcInfo;
- bool haveIPTC = tiff.GetTag ( kTIFF_PrimaryIFD, kTIFF_IPTC, &iptcInfo ); // The TIFF IPTC tag.
-
+
if ( havePSIR ) { // ! Do the Photoshop 6 integration before other legacy analysis.
psir.ParseMemoryResources ( psirInfo.dataPtr, psirInfo.dataLen );
PSIR_Manager::ImgRsrcInfo buriedExif;
@@ -241,46 +205,51 @@ void TIFF_MetaHandler::ProcessXMP()
if ( ! readOnly ) psir.DeleteImgRsrc ( kPSIR_Exif );
}
}
-
- if ( haveIPTC ) { // At this point "haveIPTC" means from TIFF tag 33723.
- iptc.ParseMemoryDataSets ( iptcInfo.dataPtr, iptcInfo.dataLen );
- lastLegacy = kLegacyJTP_TIFF_IPTC;
- }
-
- if ( lastLegacy < kLegacyJTP_TIFF_TIFF_Tags ) {
- found = tiff.GetTag ( kTIFF_PrimaryIFD, kTIFF_ImageDescription, 0 );
- if ( ! found ) found = tiff.GetTag ( kTIFF_PrimaryIFD, kTIFF_Artist, 0 );
- if ( ! found ) found = tiff.GetTag ( kTIFF_PrimaryIFD, kTIFF_Copyright, 0 );
- if ( found ) lastLegacy = kLegacyJTP_TIFF_TIFF_Tags;
- }
-
- if ( havePSIR ) {
- if ( lastLegacy < kLegacyJTP_PSIR_OldCaption ) {
- found = psir.GetImgRsrc ( kPSIR_OldCaption, 0 );
- if ( ! found ) found = psir.GetImgRsrc ( kPSIR_OldCaptionPStr, 0 );
- if ( found ) lastLegacy = kLegacyJTP_PSIR_OldCaption;
- }
+ TIFF_Manager::TagInfo iptcInfo;
+ bool haveIPTC = tiff.GetTag ( kTIFF_PrimaryIFD, kTIFF_IPTC, &iptcInfo ); // The TIFF IPTC tag.
+ int iptcDigestState = kDigestMatches;
- if ( ! haveIPTC ) {
- PSIR_Manager::ImgRsrcInfo iptcInfo;
- haveIPTC = psir.GetImgRsrc ( kPSIR_IPTC, &iptcInfo );
- if ( haveIPTC ) {
- iptc.ParseMemoryDataSets ( iptcInfo.dataPtr, iptcInfo.dataLen );
- if ( lastLegacy < kLegacyJTP_PSIR_IPTC ) lastLegacy = kLegacyJTP_PSIR_IPTC;
+ if ( haveIPTC ) {
+
+ bool haveDigest = false;
+ PSIR_Manager::ImgRsrcInfo digestInfo;
+ if ( havePSIR ) haveDigest = psir.GetImgRsrc ( kPSIR_IPTCDigest, &digestInfo );
+ if ( digestInfo.dataLen != 16 ) haveDigest = false;
+
+ if ( ! haveDigest ) {
+
+ iptcDigestState = kDigestMissing;
+
+ } else {
+
+ // Older versions of Photoshop wrote tag 33723 with type LONG, but ignored the trailing
+ // zero padding for the IPTC digest. If the full digest differs, recheck without the padding.
+
+ iptcDigestState = PhotoDataUtils::CheckIPTCDigest ( iptcInfo.dataPtr, iptcInfo.dataLen, digestInfo.dataPtr );
+
+ if ( (iptcDigestState == kDigestDiffers) && (kTIFF_TypeSizes[iptcInfo.type] > 1) ) {
+ XMP_Uns8 * endPtr = (XMP_Uns8*)iptcInfo.dataPtr + iptcInfo.dataLen - 1;
+ XMP_Uns8 * minPtr = endPtr - kTIFF_TypeSizes[iptcInfo.type] + 1;
+ while ( (endPtr >= minPtr) && (*endPtr == 0) ) --endPtr;
+ XMP_Uns32 unpaddedLen = (XMP_Uns32) (endPtr - (XMP_Uns8*)iptcInfo.dataPtr + 1);
+ iptcDigestState = PhotoDataUtils::CheckIPTCDigest ( iptcInfo.dataPtr, unpaddedLen, digestInfo.dataPtr );
}
+
}
}
-
+
XMP_OptionBits options = k2XMP_FileHadExif; // TIFF files are presumed to have Exif legacy.
+ if ( haveIPTC ) options |= k2XMP_FileHadIPTC;
if ( this->containsXMP ) options |= k2XMP_FileHadXMP;
- if ( haveIPTC || (lastLegacy == kLegacyJTP_PSIR_OldCaption) ) options |= k2XMP_FileHadIPTC;
// Process the XMP packet. If it fails to parse, do a forced legacy import but still throw an
// exception. This tells the caller that an error happened, but gives them recovered legacy
// should they want to proceed with that.
+ bool haveXMP = false;
+
if ( ! this->xmpPacket.empty() ) {
XMP_Assert ( this->containsXMP );
// Common code takes care of packetInfo.charForm, .padSize, and .writeable.
@@ -288,16 +257,23 @@ void TIFF_MetaHandler::ProcessXMP()
XMP_StringLen packetLen = (XMP_StringLen)this->xmpPacket.size();
try {
this->xmpObj.ParseFromBuffer ( packetStr, packetLen );
+ haveXMP = true;
} catch ( ... ) {
XMP_ClearOption ( options, k2XMP_FileHadXMP );
- ImportJTPtoXMP ( kXMP_TIFFFile, lastLegacy, &tiff, psir, &iptc, &this->xmpObj, options );
+ if ( haveIPTC ) iptc.ParseMemoryDataSets ( iptcInfo.dataPtr, iptcInfo.dataLen );
+ if ( iptcDigestState == kDigestMatches ) iptcDigestState = kDigestMissing;
+ ImportPhotoData ( tiff, iptc, psir, iptcDigestState, &this->xmpObj, options );
throw; // ! Rethrow the exception, don't absorb it.
}
}
// Process the legacy metadata.
- ImportJTPtoXMP ( kXMP_TIFFFile, lastLegacy, &tiff, psir, &iptc, &this->xmpObj, options );
+ if ( haveIPTC && (! haveXMP) && (iptcDigestState == kDigestMatches) ) iptcDigestState = kDigestMissing;
+ bool parseIPTC = (iptcDigestState != kDigestMatches) || (! readOnly);
+ if ( parseIPTC ) iptc.ParseMemoryDataSets ( iptcInfo.dataPtr, iptcInfo.dataLen );
+ ImportPhotoData ( tiff, iptc, psir, iptcDigestState, &this->xmpObj, options );
+
this->containsXMP = true; // Assume we now have something in the XMP.
} // TIFF_MetaHandler::ProcessXMP
@@ -320,57 +296,63 @@ void TIFF_MetaHandler::UpdateFile ( bool doSafeUpdate )
XMP_AbortProc abortProc = this->parent->abortProc;
void * abortArg = this->parent->abortArg;
- // Decide whether to do an in-place update. This can only happen if all of the following are true:
- // - There is an XMP packet in the file.
- // - The are no changes to the legacy tags. (The IPTC and PSIR are in the TIFF tags.)
- // - The new XMP can fit in the old space.
-
- ExportXMPtoJTP ( kXMP_TIFFFile, &this->xmpObj, &this->tiffMgr, this->psirMgr, this->iptcMgr );
-
XMP_Int64 oldPacketOffset = this->packetInfo.offset;
XMP_Int32 oldPacketLength = this->packetInfo.length;
-
+
if ( oldPacketOffset == kXMPFiles_UnknownOffset ) oldPacketOffset = 0; // ! Simplify checks.
if ( oldPacketLength == kXMPFiles_UnknownLength ) oldPacketLength = 0;
+
+ bool fileHadXMP = ((oldPacketOffset != 0) && (oldPacketLength != 0));
+
+ // Update the IPTC-IIM and native TIFF/Exif metadata. ExportPhotoData also trips the tiff: and
+ // exif: copies from the XMP, so reserialize the now final XMP packet.
+
+ ExportPhotoData ( kXMP_TIFFFile, &this->xmpObj, &this->tiffMgr, this->iptcMgr, this->psirMgr );
- bool doInPlace = (this->xmpPacket.size() <= (size_t)this->packetInfo.length);
+ try {
+ XMP_OptionBits options = kXMP_UseCompactFormat;
+ if ( fileHadXMP ) options |= kXMP_ExactPacketLength;
+ this->xmpObj.SerializeToBuffer ( &this->xmpPacket, options, oldPacketLength );
+ } catch ( ... ) {
+ this->xmpObj.SerializeToBuffer ( &this->xmpPacket, kXMP_UseCompactFormat );
+ }
+
+ // Decide whether to do an in-place update. This can only happen if all of the following are true:
+ // - There is an XMP packet in the file.
+ // - The are no changes to the legacy tags. (The IPTC and PSIR are in the TIFF tags.)
+ // - The new XMP can fit in the old space.
+
+ bool doInPlace = (fileHadXMP && (this->xmpPacket.size() <= (size_t)oldPacketLength));
if ( this->tiffMgr.IsLegacyChanged() ) doInPlace = false;
- if ( doInPlace ) {
+ if ( ! doInPlace ) {
+
+ #if GatherPerformanceData
+ sAPIPerf->back().extraInfo += ", TIFF append update";
+ #endif
+
+ this->tiffMgr.SetTag ( kTIFF_PrimaryIFD, kTIFF_XMP, kTIFF_UndefinedType, (XMP_Uns32)this->xmpPacket.size(), this->xmpPacket.c_str() );
+ this->tiffMgr.UpdateFileStream ( destRef );
+
+ } else {
#if GatherPerformanceData
sAPIPerf->back().extraInfo += ", TIFF in-place update";
#endif
-
+
if ( this->xmpPacket.size() < (size_t)this->packetInfo.length ) {
// They ought to match, cheap to be sure.
size_t extraSpace = (size_t)this->packetInfo.length - this->xmpPacket.size();
this->xmpPacket.append ( extraSpace, ' ' );
}
-
+
LFA_FileRef liveFile = this->parent->fileRef;
-
+
XMP_Assert ( this->xmpPacket.size() == (size_t)oldPacketLength ); // ! Done by common PutXMP logic.
-
+
LFA_Seek ( liveFile, oldPacketOffset, SEEK_SET );
LFA_Write ( liveFile, this->xmpPacket.c_str(), (XMP_Int32)this->xmpPacket.size() );
-
- } else {
- #if GatherPerformanceData
- sAPIPerf->back().extraInfo += ", TIFF append update";
- #endif
-
- // Reserialize the XMP to get standard padding, PutXMP has probably done an in-place serialize.
- this->xmpObj.SerializeToBuffer ( &this->xmpPacket, kXMP_UseCompactFormat );
- this->packetInfo.offset = kXMPFiles_UnknownOffset;
- this->packetInfo.length = (XMP_Int32)this->xmpPacket.size();
- FillPacketInfo ( this->xmpPacket, &this->packetInfo );
-
- this->tiffMgr.SetTag ( kTIFF_PrimaryIFD, kTIFF_XMP, kTIFF_UndefinedType, (XMP_Uns32)this->xmpPacket.size(), this->xmpPacket.c_str() );
-
- this->tiffMgr.UpdateFileStream ( destRef );
-
}
this->needsUpdate = false;
@@ -389,12 +371,12 @@ void TIFF_MetaHandler::WriteFile ( LFA_FileRef sourceRef, const std::string & so
LFA_FileRef destRef = this->parent->fileRef;
XMP_AbortProc abortProc = this->parent->abortProc;
void * abortArg = this->parent->abortArg;
-
+
XMP_Int64 fileLen = LFA_Measure ( sourceRef );
if ( fileLen > 0xFFFFFFFFLL ) { // Check before making a copy of the file.
XMP_Throw ( "TIFF fles can't exceed 4GB", kXMPErr_BadTIFF );
}
-
+
LFA_Seek ( sourceRef, 0, SEEK_SET );
LFA_Truncate ( destRef, 0 );
LFA_Copy ( sourceRef, destRef, fileLen, abortProc, abortArg );
diff --git a/source/XMPFiles/FileHandlers/TIFF_Handler.hpp b/source/XMPFiles/FileHandlers/TIFF_Handler.hpp
index 01c1e06..aabb509 100644
--- a/source/XMPFiles/FileHandlers/TIFF_Handler.hpp
+++ b/source/XMPFiles/FileHandlers/TIFF_Handler.hpp
@@ -3,7 +3,7 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2007 Adobe Systems Incorporated
+// Copyright 2006 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -38,7 +38,6 @@ static const XMP_OptionBits kTIFF_HandlerFlags = (kXMPFiles_CanInjectXMP |
kXMPFiles_CanReconcile |
kXMPFiles_AllowsOnlyXMP |
kXMPFiles_ReturnsRawPacket |
- kXMPFiles_ReturnsTNail |
kXMPFiles_AllowsSafeUpdate);
class TIFF_MetaHandler : public XMPFileHandler
@@ -46,7 +45,6 @@ class TIFF_MetaHandler : public XMPFileHandler
public:
void CacheFileData();
- void ProcessTNail();
void ProcessXMP();
void UpdateFile ( bool doSafeUpdate );
diff --git a/source/XMPFiles/FileHandlers/Trivial_Handler.cpp b/source/XMPFiles/FileHandlers/Trivial_Handler.cpp
index c96c944..3ebdb67 100644
--- a/source/XMPFiles/FileHandlers/Trivial_Handler.cpp
+++ b/source/XMPFiles/FileHandlers/Trivial_Handler.cpp
@@ -1,6 +1,6 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2007 Adobe Systems Incorporated
+// Copyright 2004 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
diff --git a/source/XMPFiles/FileHandlers/Trivial_Handler.hpp b/source/XMPFiles/FileHandlers/Trivial_Handler.hpp
index 9729ba0..9db851c 100644
--- a/source/XMPFiles/FileHandlers/Trivial_Handler.hpp
+++ b/source/XMPFiles/FileHandlers/Trivial_Handler.hpp
@@ -3,7 +3,7 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2007 Adobe Systems Incorporated
+// Copyright 2004 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
diff --git a/source/XMPFiles/FileHandlers/UCF_Handler.cpp b/source/XMPFiles/FileHandlers/UCF_Handler.cpp
index e9c2219..cea781e 100644
--- a/source/XMPFiles/FileHandlers/UCF_Handler.cpp
+++ b/source/XMPFiles/FileHandlers/UCF_Handler.cpp
@@ -1,6 +1,6 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2008 Adobe Systems Incorporated
+// Copyright 2007 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -116,7 +116,11 @@ bool UCF_CheckFormat ( XMP_FileFormat format,
XMP_LitMatch( mimetype, "application/vnd.adobe.x-mars" ) || //Mars plugin(labs only), Acrobat8
XMP_LitMatch( mimetype, "application/vnd.adobe.pdfxml" ) || //Mars plugin(labs only), Acrobat 9
XMP_LitMatch( mimetype, "vnd.adobe.x-asnd" ) || //Adobe Sound Document (Soundbooth Team)
- XMP_LitMatch( mimetype, "application/vnd.adobe.indesign-idml-package" ) ) //inCopy (inDesign) IDML Document
+ XMP_LitMatch( mimetype, "application/vnd.adobe.indesign-idml-package" ) || //inCopy (inDesign) IDML Document
+ XMP_LitMatch( mimetype, "application/vnd.adobe.incopy-package" ) || // InDesign Document
+ XMP_LitMatch( mimetype, "application/vnd.adobe.indesign-package" ) || // InDesign Document
+
+ false ) // "sentinel"
// *** ==> unknown are also treated as not acceptable
okMimetype = true;
@@ -129,7 +133,7 @@ bool UCF_CheckFormat ( XMP_FileFormat format,
//.airi - temporary Adobe Air Files
//application/vnd.adobe.air-application-intermediate-package+zip
- delete mimetype;
+ delete [] mimetype;
return okMimetype;
} // UCF_CheckFormat
diff --git a/source/XMPFiles/FileHandlers/UCF_Handler.hpp b/source/XMPFiles/FileHandlers/UCF_Handler.hpp
index ec60f86..2f9af5e 100644
--- a/source/XMPFiles/FileHandlers/UCF_Handler.hpp
+++ b/source/XMPFiles/FileHandlers/UCF_Handler.hpp
@@ -3,7 +3,7 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2008 Adobe Systems Incorporated
+// Copyright 2007 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
diff --git a/source/XMPFiles/FileHandlers/WAV_Handler.cpp b/source/XMPFiles/FileHandlers/WAV_Handler.cpp
deleted file mode 100644
index 6820d01..0000000
--- a/source/XMPFiles/FileHandlers/WAV_Handler.cpp
+++ /dev/null
@@ -1,707 +0,0 @@
-// =================================================================================================
-// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2008 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 "XMP_Environment.h" // ! This must be the first include.
-#if ! XMP_UNIXBuild // Closes at very bottom. Disabled on UNIX until legacy-as-local is fixed.
-
-#if XMP_WinBuild
- #pragma warning ( disable : 4996 ) // '...' was declared deprecated
-#endif
-
-#include "WAV_Handler.hpp"
-#include "RIFF_Support.hpp"
-#include "Reconcile_Impl.hpp"
-#include "XMP_Const.h"
-
-using namespace std;
-
-#define kXMPUserDataType MakeFourCC ( '_', 'P', 'M', 'X' ) /* Yes, backwards! */
-
-#define formtypeWAVE MakeFourCC('W', 'A', 'V', 'E')
-
-
-// -------------------------------------------------------------------------------------------------
-// Premiere Pro specific info for reconciliation
-
-// ! MakeFourCC warning: The MakeFourCC macro creates a 32 bit int with the letters "reversed". This
-// ! is a leftover of the original Win-only code. It happens to work OK on little endian machines,
-// ! when stored in memory the letters are in the expected order. To be safe, always use the XMP
-// ! endian control macros when storing to memory or loading from memory.
-
-// FourCC codes for the RIFF chunks
-#define wavWaveTitleChunk MakeFourCC('D','I','S','P')
-#define wavInfoCreateDateChunk MakeFourCC('I','C','R','D')
-#define wavInfoArtistChunk MakeFourCC('I','A','R','T')
-#define wavInfoAlbumChunk MakeFourCC('I','N','A','M')
-#define wavInfoGenreChunk MakeFourCC('I','G','N','R')
-#define wavInfoCommentChunk MakeFourCC('I','C','M','T')
-#define wavInfoEngineerChunk MakeFourCC('I','E','N','G')
-#define wavInfoCopyrightChunk MakeFourCC('I','C','O','P')
-#define wavInfoSoftwareChunk MakeFourCC('I','S','F','T')
-
-#define wavInfoTag MakeFourCC('I','N','F','O')
-#define wavWaveTag MakeFourCC('W','A','V','E')
-
-// DC
-#define kTitle "title"
-#define kCopyright "rights"
-
-// XMP
-#define kCreateDate "CreateDate"
-
-// DM
-#define kArtist "artist"
-#define kAlbum "album"
-#define kGenre "genre"
-#define kLogComment "logComment"
-#define kEngineer "engineer"
-#define kSoftware "CreatorTool"
-
-// -------------------------------------------------------------------------------------------------
-// Legacy digest info
-// ------------------
-//
-// The original WAV handler code didn't keep a legacy digest, it imported the legacy on every open.
-// Because local encoding is used for the legacy, this can cause loss in the XMP. (The use of local
-// encoding itself is an issue, the AVI handler is using UTF-8.)
-//
-// The legacy digest for WAV is a list of chunk IDs and a 128-bit MD5 digest, formatted like:
-// DISP,IART,ICMT,ICOP,ICRD,IENG,IGNR,INAM,ISFT;012345678ABCDEF012345678ABCDEF
-//
-// The list of IDs are the recognized legacy chunks, in alphabetical order. This the full list that
-// could be recognized, not restricted to those actually present in the specific file. When opening
-// a file the new software's list is used to create the comparison digest, not the list from the
-// file. So that changes to the recognized list will trigger an import.
-//
-// The MD5 digest is computed from the full legacy chunk, including the ID and length portions, not
-// including any pad byte for odd data length. The length must be in file (little endian) order,
-// not native machine order. The legacy chunks are added to the digest in list (alphabetical by ID)
-// order.
-//
-// Legacy can be imported in 3 circumstances:
-//
-// 1. If the file does not yet have XMP, all of the legacy is imported.
-//
-// 2. If the file has XMP and a digest, and the recomputed digest differs from the saved digest, the
-// legacy is imported. The digest comparison is the full string, including the list of IDs. A
-// check is made for each legacy item:
-// 2a. If the legacy item is missing or has an empty value, the corresponding XMP is deleted.
-// 2b. If the corresponding XMP is missing, the legacy value is imported.
-// 2c. If the new legacy value differs from a local encoding of the XMP value, the legacy value
-// is imported.
-//
-// 3. If the file has XMP but no digest, legacy is imported for items that have no corresponding XMP.
-// Any existing XMP is left alone. This is protection for tools that might be XMP aware but do
-// not provide a digest or any legacy reconciliation.
-
-#define TAG_MAX_SIZE 5024
-
-// =================================================================================================
-
-static inline int GetStringRiffSize ( const std::string & str )
-{
- int l = (int)strlen ( const_cast<char *> (str.data()) );
- if ( l & 1 ) ++l;
- return l;
-}
-
-// =================================================================================================
-/// \file WAV_Handler.cpp
-/// \brief File format handler for WAV.
-///
-/// This header ...
-///
-// =================================================================================================
-
-// =================================================================================================
-// WAV_MetaHandlerCTor
-// ===================
-
-XMPFileHandler * WAV_MetaHandlerCTor ( XMPFiles * parent )
-{
- return new WAV_MetaHandler ( parent );
-
-} // WAV_MetaHandlerCTor
-
-// =================================================================================================
-// WAV_CheckFormat
-// ===============
-//
-// A WAVE file must begin with "RIFF", a 4 byte little endian length, then "WAVE". The length should
-// be fileSize-8, but we don't bother checking this here.
-
-bool WAV_CheckFormat ( XMP_FileFormat format,
- XMP_StringPtr filePath,
- LFA_FileRef fileRef,
- XMPFiles * parent )
-{
- IgnoreParam(format); IgnoreParam(parent);
- XMP_Assert ( format == kXMP_WAVFile );
-
- if ( fileRef == 0 ) return false;
-
- enum { kBufferSize = 12 };
- XMP_Uns8 buffer [kBufferSize];
-
- LFA_Seek ( fileRef, 0, SEEK_SET );
- LFA_Read ( fileRef, buffer, kBufferSize );
-
- // "RIFF" is 52 49 46 46, "WAVE" is 57 41 56 45
- if ( (! CheckBytes ( &buffer[0], "\x52\x49\x46\x46", 4 )) ||
- (! CheckBytes ( &buffer[8], "\x57\x41\x56\x45", 4 )) ) return false;
-
- return true;
-
-} // WAV_CheckFormat
-
-// =================================================================================================
-// WAV_MetaHandler::WAV_MetaHandler
-// ================================
-
-WAV_MetaHandler::WAV_MetaHandler ( XMPFiles * _parent )
-{
- this->parent = _parent;
- this->handlerFlags = kWAV_HandlerFlags;
- this->stdCharForm = kXMP_Char8Bit;
-
-} // WAV_MetaHandler::WAV_MetaHandler
-
-// =================================================================================================
-// WAV_MetaHandler::~WAV_MetaHandler
-// =================================
-
-WAV_MetaHandler::~WAV_MetaHandler()
-{
- // Nothing to do.
-} // WAV_MetaHandler::~WAV_MetaHandler
-
-
-
-// =================================================================================================
-// WAV_MetaHandler::UpdateFile
-// ===========================
-
-void WAV_MetaHandler::UpdateFile ( bool doSafeUpdate )
-{
- if ( ! this->needsUpdate ) return;
- if ( doSafeUpdate ) XMP_Throw ( "WAV_MetaHandler::UpdateFile: Safe update not supported", kXMPErr_Unavailable );
-
- bool fReconciliate = ! (this->parent->openFlags & kXMPFiles_OpenOnlyXMP);
- bool ok;
-
- std::string strTitle, strArtist, strComment, strCopyright, strCreateDate,
- strEngineer, strGenre, strAlbum, strSoftware;
-
- if ( fReconciliate ) {
-
- // Get the legacy item values, create the new digest, and add the digest to the XMP. The
- // legacy chunks in alphabetical ID order are:
- // DISP - title
- // IART - artist
- // ICMT - log comment
- // ICOP - copyright
- // ICRD - create date
- // IENG - engineer
- // IGNR - genre
- // INAM - album
- // ISFT - software
-
- MD5_CTX md5Ctx;
- std::string digestStr;
- XMP_Uns8 md5Val[16];
- static char* hexDigits = "0123456789ABCDEF";
-
- MD5Init ( &md5Ctx );
-
- // Prepare the legacy values and compute the new digest.
-
- PrepareLegacyExport ( kXMP_NS_DC, kTitle, wavWaveTitleChunk, &strTitle, &digestStr, &md5Ctx, true /* LangAlt */ );
- PrepareLegacyExport ( kXMP_NS_DM, kArtist, wavInfoArtistChunk, &strArtist, &digestStr, &md5Ctx );
- PrepareLegacyExport ( kXMP_NS_DM, kLogComment, wavInfoCommentChunk, &strComment, &digestStr, &md5Ctx );
- PrepareLegacyExport ( kXMP_NS_DC, kCopyright, wavInfoCopyrightChunk, &strCopyright, &digestStr, &md5Ctx, true /* LangAlt */ );
- PrepareLegacyExport ( kXMP_NS_XMP, kCreateDate, wavInfoCreateDateChunk, &strCreateDate, &digestStr, &md5Ctx );
- PrepareLegacyExport ( kXMP_NS_DM, kEngineer, wavInfoEngineerChunk, &strEngineer, &digestStr, &md5Ctx );
- PrepareLegacyExport ( kXMP_NS_DM, kGenre, wavInfoGenreChunk, &strGenre, &digestStr, &md5Ctx );
- PrepareLegacyExport ( kXMP_NS_DM, kAlbum, wavInfoAlbumChunk, &strAlbum, &digestStr, &md5Ctx );
- PrepareLegacyExport ( kXMP_NS_XMP, kSoftware, wavInfoSoftwareChunk, &strSoftware, &digestStr, &md5Ctx );
-
- // Finish the digest and add it to the XMP.
-
- MD5Final ( md5Val, &md5Ctx );
-
- digestStr[digestStr.size()-1] = ';';
- for ( size_t i = 0; i < 16; ++i ) {
- XMP_Uns8 byte = md5Val[i];
- digestStr += hexDigits [byte >> 4];
- digestStr += hexDigits [byte & 0xF];
- }
-
- XMP_StringLen oldLen = (XMP_StringLen)this->xmpPacket.size();
- this->xmpObj.SetProperty ( kXMP_NS_WAV, "NativeDigest", digestStr.c_str() );
- try {
- this->xmpObj.SerializeToBuffer ( &this->xmpPacket, (kXMP_UseCompactFormat | kXMP_ExactPacketLength),
- oldLen );
- } catch ( ... ) {
- this->xmpObj.SerializeToBuffer ( &this->xmpPacket, kXMP_UseCompactFormat );
- }
-
- }
-
- XMP_StringPtr packetStr = this->xmpPacket.c_str();
- XMP_StringLen packetLen = (XMP_StringLen)this->xmpPacket.size();
- if ( packetLen == 0 ) return;
-
- // Make sure we're writing an even number of bytes as required by the RIFF specification
- if ( (this->xmpPacket.size() & 1) == 1 )
- this->xmpPacket.push_back (' ');
- XMP_Assert ( (this->xmpPacket.size() & 1) == 0 );
- packetStr = this->xmpPacket.c_str(); // ! Make sure they are current.
- packetLen = (XMP_StringLen)this->xmpPacket.size();
-
- LFA_FileRef fileRef ( this->parent->fileRef );
- if ( fileRef == 0 ) return;
-
- RIFF_Support::RiffState riffState;
- long numTags = RIFF_Support::OpenRIFF ( fileRef, riffState );
- if ( numTags == 0 ) return;
-
- ok = RIFF_Support::PutChunk ( fileRef, riffState, formtypeWAVE, kXMPUserDataType, (char*)packetStr, packetLen );
- if ( ! ok ) return;
-
- ok = CreatorAtom::Update ( this->xmpObj, fileRef, formtypeWAVE, riffState );
- if ( ! ok ) return;
-
- // If needed, reconciliate the XMP data back into the native metadata.
- if ( fReconciliate ) {
-
- PutChunk ( fileRef, riffState, wavWaveTag, wavWaveTitleChunk, strTitle.c_str(), (XMP_Int32)strTitle.size() );
-
- // Pad the old tags
- RIFF_Support::MarkChunkAsPadding ( fileRef, riffState, 0, wavInfoCreateDateChunk, 0 );
- RIFF_Support::MarkChunkAsPadding ( fileRef, riffState, 0, wavInfoArtistChunk, 0 );
- RIFF_Support::MarkChunkAsPadding ( fileRef, riffState, 0, wavInfoAlbumChunk, 0 );
- RIFF_Support::MarkChunkAsPadding ( fileRef, riffState, 0, wavInfoGenreChunk, 0 );
- RIFF_Support::MarkChunkAsPadding ( fileRef, riffState, 0, wavInfoCommentChunk, 0 );
- RIFF_Support::MarkChunkAsPadding ( fileRef, riffState, 0, wavInfoEngineerChunk, 0 );
- RIFF_Support::MarkChunkAsPadding ( fileRef, riffState, 0, wavInfoCopyrightChunk, 0 );
- RIFF_Support::MarkChunkAsPadding ( fileRef, riffState, 0, wavInfoSoftwareChunk, 0 );
-
- // Get the old INFO list
- std::string strOldInfo;
- unsigned long lOldSize = 0;
- bool ok = RIFF_Support::GetRIFFChunk ( fileRef, riffState, FOURCC_LIST, wavWaveTag, wavInfoTag, 0, &lOldSize );
- if ( ok ) {
- // We have to get rid of the "INFO" first.
- strOldInfo.reserve ( lOldSize );
- strOldInfo.assign ( lOldSize, ' ' );
- RIFF_Support::GetRIFFChunk ( fileRef, riffState, FOURCC_LIST, wavWaveTag, wavInfoTag, (char*)strOldInfo.c_str(), &lOldSize );
- // lOldSize -= 4;
- }
-
- // TODO: Cleaning up the OldInfo from the padding
-
- // Pad the old INFO list
- RIFF_Support::MarkChunkAsPadding ( fileRef, riffState, wavWaveTag, FOURCC_LIST, wavInfoTag );
-
- // Calculating the new INFO list size
- XMP_Int32 dwListSize = 4 + 8 * 8 +
- GetStringRiffSize ( strCreateDate ) +
- GetStringRiffSize ( strArtist ) +
- GetStringRiffSize ( strAlbum ) +
- GetStringRiffSize ( strGenre ) +
- GetStringRiffSize ( strComment ) +
- GetStringRiffSize ( strEngineer ) +
- GetStringRiffSize ( strCopyright ) +
- GetStringRiffSize ( strSoftware ) +
- lOldSize; // list id (4 bytes) + 8 tags hdrs (8 each)
-
- ok = MakeChunk ( fileRef, riffState, formtypeWAVE, dwListSize + 8 );
- if ( ! ok ) return; // If there's an error making a chunk, bail
-
- // Building the INFO list header
- RIFF_Support::ltag listtag;
- listtag.id = MakeUns32LE ( FOURCC_LIST );
- listtag.len = MakeUns32LE ( dwListSize );
- listtag.subid = MakeUns32LE ( wavInfoTag );
- LFA_Write ( fileRef, &listtag, 12 );
-
- // Writing all the chunks
- RIFF_Support::WriteChunk ( fileRef, wavInfoCreateDateChunk, strCreateDate.c_str(), GetStringRiffSize ( strCreateDate ) );
- RIFF_Support::WriteChunk ( fileRef, wavInfoArtistChunk, strArtist.c_str(), GetStringRiffSize ( strArtist ) );
- RIFF_Support::WriteChunk ( fileRef, wavInfoAlbumChunk, strAlbum.c_str(), GetStringRiffSize ( strAlbum ) );
- RIFF_Support::WriteChunk ( fileRef, wavInfoGenreChunk, strGenre.c_str(), GetStringRiffSize ( strGenre ) );
- RIFF_Support::WriteChunk ( fileRef, wavInfoCommentChunk, strComment.c_str(), GetStringRiffSize ( strComment ) );
- RIFF_Support::WriteChunk ( fileRef, wavInfoEngineerChunk, strEngineer.c_str(), GetStringRiffSize ( strEngineer ) );
- RIFF_Support::WriteChunk ( fileRef, wavInfoCopyrightChunk, strCopyright.c_str(), GetStringRiffSize ( strCopyright ) );
- RIFF_Support::WriteChunk ( fileRef, wavInfoSoftwareChunk, strSoftware.c_str(), GetStringRiffSize ( strSoftware ) );
-
- LFA_Write ( fileRef, strOldInfo.c_str(), lOldSize );
-
- }
-
- this->needsUpdate = false;
-
-} // WAV_MetaHandler::UpdateFile
-
-// =================================================================================================
-// WAV_MetaHandler::WriteFile
-// ==========================
-
-void WAV_MetaHandler::WriteFile ( LFA_FileRef sourceRef,
- const std::string & sourcePath )
-{
- IgnoreParam(sourceRef); IgnoreParam(sourcePath);
-
- XMP_Throw ( "WAV_MetaHandler::WriteFile: Not supported", kXMPErr_Unavailable );
-
-} // WAV_MetaHandler::WriteFile
-
-// =================================================================================================
-
-static void AddDigestItem ( XMP_Uns32 legacyID, std::string & legacyStr, std::string * digestStr, MD5_CTX * md5 )
-{
-
- XMP_Uns32 leID = MakeUns32LE ( legacyID );
- XMP_Uns32 leLen = MakeUns32LE ( (XMP_Uns32)legacyStr.size());
-
- digestStr->append ( (char*)(&leID), 4 );
- digestStr->append ( "," );
-
- MD5Update ( md5, (XMP_Uns8*)&leID, 4 );
- MD5Update ( md5, (XMP_Uns8*)&leLen, 4 );
- MD5Update ( md5, (XMP_Uns8*)legacyStr.c_str(), (XMP_Int32)legacyStr.size() );
-
-} // AddDigestItem
-
-// =================================================================================================
-
-static void AddCurrentDigestItem ( LFA_FileRef fileRef, RIFF_Support::RiffState riffState,
- XMP_Uns32 tagID, XMP_Uns32 parentID, std::string * digestStr, MD5_CTX * md5 )
-{
- unsigned long legacySize;
- std::string legacyStr;
-
- bool found = RIFF_Support::GetRIFFChunk ( fileRef, riffState, tagID, parentID, 0, 0, &legacySize );
- if ( found ) {
- legacyStr.reserve ( legacySize );
- legacyStr.assign ( legacySize, ' ' );
- (void) RIFF_Support::GetRIFFChunk ( fileRef, riffState, tagID, parentID, 0, (char*)legacyStr.c_str(), &legacySize );
- }
-
- AddDigestItem ( tagID, legacyStr, digestStr, md5 );
-
-} // AddCurrentDigestItem
-
-// =================================================================================================
-
-static void CreateCurrentDigest ( LFA_FileRef fileRef, RIFF_Support::RiffState riffState, std::string * digestStr )
-{
- MD5_CTX md5Ctx;
- XMP_Uns8 md5Val[16];
- static char* hexDigits = "0123456789ABCDEF";
-
- MD5Init ( &md5Ctx );
-
- AddCurrentDigestItem ( fileRef, riffState, wavWaveTitleChunk, wavWaveTag, digestStr, &md5Ctx );
- AddCurrentDigestItem ( fileRef, riffState, wavInfoArtistChunk, wavInfoTag, digestStr, &md5Ctx );
- AddCurrentDigestItem ( fileRef, riffState, wavInfoCommentChunk, wavInfoTag, digestStr, &md5Ctx );
- AddCurrentDigestItem ( fileRef, riffState, wavInfoCopyrightChunk, wavInfoTag, digestStr, &md5Ctx);
- AddCurrentDigestItem ( fileRef, riffState, wavInfoCreateDateChunk, wavInfoTag, digestStr, &md5Ctx );
- AddCurrentDigestItem ( fileRef, riffState, wavInfoEngineerChunk, wavInfoTag, digestStr, &md5Ctx );
- AddCurrentDigestItem ( fileRef, riffState, wavInfoGenreChunk, wavInfoTag, digestStr, &md5Ctx );
- AddCurrentDigestItem ( fileRef, riffState, wavInfoAlbumChunk, wavInfoTag, digestStr, &md5Ctx );
- AddCurrentDigestItem ( fileRef, riffState, wavInfoSoftwareChunk, wavInfoTag, digestStr, &md5Ctx );
-
- MD5Final ( md5Val, &md5Ctx );
-
- (*digestStr)[digestStr->size()-1] = ';';
- for ( size_t i = 0; i < 16; ++i ) {
- XMP_Uns8 byte = md5Val[i];
- (*digestStr) += hexDigits [byte >> 4];
- (*digestStr) += hexDigits [byte & 0xF];
- }
-
-} // CreateCurrentDigest
-
-
-// =================================================================================================
-// WAV_MetaHandler::CacheFileData
-// ==============================
-
-void WAV_MetaHandler::CacheFileData()
-{
-
- this->containsXMP = false;
- bool fReconciliate = ! (this->parent->openFlags & kXMPFiles_OpenOnlyXMP);
- bool keepExistingXMP = false; // By default an import will replace existing XMP.
- bool haveLegacyItem, haveXMPItem;
-
- LFA_FileRef fileRef ( this->parent->fileRef ); //*** simplify to assignment
-
- bool updateFile = XMP_OptionIsSet ( this->parent->openFlags, kXMPFiles_OpenForUpdate );
- if ( updateFile ) {
-
- // Workaround for bad files in the field that have a bad size in the outermost RIFF chunk.
- // Repair the cases where the length is too long (beyond EOF). Don't repair a length that is
- // less than EOF, we don't know if there actually are multiple top level chunks. There is
- // also a check and "runtime repair" inside ReadTag, needed for read-only file access.
-
- XMP_Int64 fileLen = LFA_Measure ( fileRef );
- XMP_Uns32 riffLen;
-
- LFA_Seek ( fileRef, 4, SEEK_SET );
- LFA_Read ( fileRef, &riffLen, 4 );
- riffLen = GetUns32LE ( &riffLen );
-
- if ( (fileLen >= 8) && ((XMP_Int64)riffLen > (fileLen - 8)) ) { // Is the initial chunk too long?
-
- bool repairFile = XMP_OptionIsSet ( this->parent->openFlags, kXMPFiles_OpenRepairFile );
- if ( ! repairFile ) {
- XMP_Throw ( "Initial RIFF tag exceeds file length", kXMPErr_BadValue );
- } else {
- riffLen = MakeUns32LE ( (XMP_Uns32)fileLen - 8 );
- LFA_Seek ( fileRef, 4, SEEK_SET );
- LFA_Write ( fileRef, &riffLen, 4 );
- }
-
- }
-
- }
-
- // Contnue with normal processing.
-
- RIFF_Support::RiffState riffState;
- long numTags = RIFF_Support::OpenRIFF ( fileRef, riffState );
- if ( numTags == 0 ) return; //*** shouldn't we throw ? XMP_Throw("invalid file format") or such?
-
- // Determine the size of the metadata
- unsigned long bufferSize(0);
- haveLegacyItem = RIFF_Support::GetRIFFChunk ( fileRef, riffState, kXMPUserDataType /* _PMX, the xmp packet */, 0, 0, 0, &bufferSize );
-
- if ( ! haveLegacyItem ) {
-
- packetInfo.writeable = true; // If no packet found, created packets will be writeable
-
- } else if ( bufferSize > 0 ) {
-
- // Size and clear the buffer
- this->xmpPacket.reserve(bufferSize);
- this->xmpPacket.assign(bufferSize, ' ');
-
- // Get the metadata
- XMP_Uns64 xmpPacketPosition;
- haveLegacyItem = RIFF_Support::GetRIFFChunk ( fileRef, riffState, kXMPUserDataType /* _PMX, the xmp packet */, 0, 0,
- const_cast<char *>(this->xmpPacket.data()), &bufferSize, &xmpPacketPosition );
- if ( haveLegacyItem ) {
- this->packetInfo.offset = xmpPacketPosition;
- this->packetInfo.length = bufferSize;
- this->xmpObj.ParseFromBuffer ( this->xmpPacket.c_str(), (XMP_StringLen)this->xmpPacket.size() );
- this->containsXMP = true;
- }
-
- }
-
- // Figure out what to do overall. If there is no XMP, everything gets imported. If there is XMP
- // but no digest, only import if there is no corresponding XMP. If there is XMP and a digest
- // that matches, nothing is imported. If there is XMP and a digest that does not match, import
- // everything that provides "new info".
-
- if ( fReconciliate && this->containsXMP ) {
-
- std::string savedDigest;
- haveXMPItem = this->xmpObj.GetProperty ( kXMP_NS_WAV, "NativeDigest", &savedDigest, 0 );
-
- if ( ! haveXMPItem ) {
- keepExistingXMP = true;
- } else {
- std::string currDigest;
- CreateCurrentDigest ( fileRef, riffState, &currDigest );
- if ( currDigest == savedDigest ) fReconciliate = false;
- }
-
- }
-
- // Now import the individual legacy items.
-
- if ( fReconciliate ) {
-
- ImportLegacyItem ( riffState, wavWaveTitleChunk, wavWaveTag, kXMP_NS_DC, kTitle, keepExistingXMP, true /* LangAlt */ );
- ImportLegacyItem ( riffState, wavInfoCreateDateChunk, wavInfoTag, kXMP_NS_XMP, kCreateDate, keepExistingXMP );
- ImportLegacyItem ( riffState, wavInfoArtistChunk, wavInfoTag, kXMP_NS_DM, kArtist, keepExistingXMP );
- ImportLegacyItem ( riffState, wavInfoAlbumChunk, wavInfoTag, kXMP_NS_DM, kAlbum, keepExistingXMP );
- ImportLegacyItem ( riffState, wavInfoGenreChunk, wavInfoTag, kXMP_NS_DM, kGenre, keepExistingXMP );
- ImportLegacyItem ( riffState, wavInfoCommentChunk, wavInfoTag, kXMP_NS_DM, kLogComment, keepExistingXMP );
- ImportLegacyItem ( riffState, wavInfoEngineerChunk, wavInfoTag, kXMP_NS_DM, kEngineer, keepExistingXMP );
- ImportLegacyItem ( riffState, wavInfoCopyrightChunk, wavInfoTag, kXMP_NS_DC, kCopyright, keepExistingXMP, true /* LangAlt */ );
- ImportLegacyItem ( riffState, wavInfoSoftwareChunk, wavInfoTag, kXMP_NS_XMP, kSoftware, keepExistingXMP );
-
- }
-
- CreatorAtom::Import ( this->xmpObj, fileRef, riffState );
-
- // Update the xmpPacket, as the xmpObj might have been updated with legacy info
- if ( this->packetInfo.length == kXMPFiles_UnknownLength )
- {
- // kXMPFiles_UnknownLength <=> no prior packet, thus do not attempt
- // to put in place (creates several seconds of delay
- this->xmpObj.SerializeToBuffer ( &this->xmpPacket, (kXMP_UseCompactFormat ) );
- }
- else
- {
- try {
- this->xmpObj.SerializeToBuffer ( &this->xmpPacket,
- (kXMP_UseCompactFormat | kXMP_ExactPacketLength) , this->packetInfo.length );
- } catch ( XMP_Error ) {
- this->xmpObj.SerializeToBuffer ( &this->xmpPacket, (kXMP_UseCompactFormat ) );
- }
- }
-
- this->processedXMP = this->containsXMP;
-
-} // WAV_MetaHandler::CacheFileData
-
-// =================================================================================================
-
-void WAV_MetaHandler::UTF8ToMBCS ( std::string * inoutStr )
-{
- std::string localStr;
-
- try {
- ReconcileUtils::UTF8ToLocal ( inoutStr->c_str(), inoutStr->size(), &localStr );
- inoutStr->swap ( localStr );
- } catch ( ... ) {
- inoutStr->erase();
- }
-
-}
-
-// =================================================================================================
-
-void WAV_MetaHandler::MBCSToUTF8 ( std::string * inoutStr )
-{
- std::string utf8Str;
-
- try {
- ReconcileUtils::LocalToUTF8 ( inoutStr->c_str(), inoutStr->size(), &utf8Str );
- inoutStr->swap ( utf8Str );
- } catch ( ... ) {
- inoutStr->erase();
- }
-
-}
-
-// =================================================================================================
-
-void WAV_MetaHandler::PrepareLegacyExport ( XMP_StringPtr xmpNS, XMP_StringPtr xmpProp, XMP_Uns32 legacyID, std::string * legacyStr,
- std::string * digestStr, MD5_CTX * md5, bool langAlt /* = false */ )
-{
- if ( ! langAlt ) {
- this->xmpObj.GetProperty ( xmpNS, xmpProp, legacyStr, 0 );
- } else {
- this->xmpObj.GetLocalizedText ( xmpNS, xmpProp, "", "x-default", 0, legacyStr, 0 );
- }
- UTF8ToMBCS ( legacyStr ); // Convert the XMP value to local encoding.
-
- // ! Add a 0 pad byte if the value has an odd length. This would be better done inside RIFF_Support,
- // ! but that means changing too much at this moment.
-
- if ( (legacyStr->size() & 1) == 1 ) {
- (*legacyStr) += " ";
- (*legacyStr)[legacyStr->size()-1] = 0;
- }
-
- if ( legacyID == wavWaveTitleChunk ) { // ! The title gets 32 bit LE type code of 1 inserted.
- legacyStr->insert ( 0, "1234" );
- PutUns32LE ( 1, (void*)legacyStr->c_str() );
- }
-
- AddDigestItem ( legacyID, *legacyStr, digestStr, md5 );
-
-} // WAV_MetaHandler::PrepareLegacyExport
-
-// =================================================================================================
-
-void WAV_MetaHandler::ImportLegacyItem ( RIFF_Support::RiffState & inOutRiffState,
- XMP_Uns32 tagID,
- XMP_Uns32 parentID,
- XMP_StringPtr xmpNS,
- XMP_StringPtr xmpProp,
- bool keepExistingXMP,
- bool langAlt /* = false */ )
-{
- LFA_FileRef fileRef ( this->parent->fileRef );
-
- bool haveLegacyItem, haveXMPItem;
- std::string legacyStr, xmpStr;
- unsigned long legacySize;
-
- if ( ! langAlt ) {
- haveXMPItem = this->xmpObj.GetProperty ( xmpNS, xmpProp, &xmpStr, 0 );
- } else {
- haveXMPItem = this->xmpObj.GetLocalizedText ( xmpNS, xmpProp, "", "x-default", 0, &xmpStr, 0 );
- }
-
- haveLegacyItem = RIFF_Support::GetRIFFChunk ( fileRef, inOutRiffState, tagID, parentID, 0, 0, &legacySize );
- if ( (legacySize == 0) || ((tagID == wavWaveTitleChunk) && (legacySize <= 4)) ) haveLegacyItem = false;
- if ( haveXMPItem && keepExistingXMP ) haveLegacyItem = false; // Simplify following checks.
-
- if ( ! haveLegacyItem ) {
-
- // No legacy item, delete the corresponding XMP if we're not keeping existing XMP.
-
- if ( haveXMPItem && (! keepExistingXMP) ) {
- if ( ! langAlt ) {
- this->xmpObj.DeleteProperty ( xmpNS, xmpProp );
- } else {
- std::string xdPath;
- SXMPUtils::ComposeLangSelector ( xmpNS, xmpProp, "x-default", &xdPath );
- this->xmpObj.DeleteProperty ( xmpNS, xdPath.c_str() );
- if ( this->xmpObj.CountArrayItems ( xmpNS, xmpProp ) == 0 ) this->xmpObj.DeleteProperty ( xmpNS, xmpProp );
- }
- }
-
- } else {
-
- // Have a legacy Item, update the XMP as appropriate.
-
- XMP_Assert ( (! haveXMPItem) || (! keepExistingXMP) );
-
- legacyStr.reserve ( legacySize );
- legacyStr.assign ( legacySize, ' ' );
- haveLegacyItem = RIFF_Support::GetRIFFChunk ( fileRef, inOutRiffState, tagID, parentID, 0, (char*)legacyStr.c_str(), &legacySize );
- XMP_Assert ( haveLegacyItem );
-
- if ( tagID == wavWaveTitleChunk ) { // Check and strip the type code from the title.
- XMP_Assert ( legacySize > 4 );
- XMP_Uns32 typeCode = GetUns32LE ( legacyStr.data() );
- if( typeCode != 1 ) return; // Bail if the type code isn't 1.
- legacyStr.erase ( 0, 4 ); // Reduce the value to just the text part.
- }
-
- if ( haveXMPItem ) {
- // Don't update the XMP if the legacy value matches the localized XMP value.
- UTF8ToMBCS ( &xmpStr );
- if ( xmpStr == legacyStr ) return;
- }
-
- MBCSToUTF8 ( &legacyStr );
- if ( ! langAlt ) {
- this->xmpObj.SetProperty ( xmpNS, xmpProp, legacyStr.c_str(), 0 );
- } else {
- this->xmpObj.SetLocalizedText ( xmpNS, xmpProp, "", "x-default", legacyStr.c_str(), 0 );
- }
- this->containsXMP = true;
-
- }
-
-} // WAV_MetaHandler::LoadPropertyFromRIFF
-
-// =================================================================================================
-
-#endif // XMP_UNIXBuild
diff --git a/source/XMPFiles/FileHandlers/WAV_Handler.hpp b/source/XMPFiles/FileHandlers/WAV_Handler.hpp
deleted file mode 100644
index 4bd9aae..0000000
--- a/source/XMPFiles/FileHandlers/WAV_Handler.hpp
+++ /dev/null
@@ -1,75 +0,0 @@
-#ifndef __WAV_Handler_hpp__
-#define __WAV_Handler_hpp__ 1
-
-// =================================================================================================
-// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2008 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 "XMP_Environment.h" // ! This must be the first include.
-#if ! XMP_UNIXBuild // Closes at very bottom. Disabled on UNIX until legacy-as-local is fixed.
-
-#include "XMPFiles_Impl.hpp"
-
-#include "MD5.h"
-
-// =================================================================================================
-/// \file WAV_Handler.hpp
-/// \brief File format handler for WAV.
-///
-/// This header ...
-///
-// =================================================================================================
-
-namespace RIFF_Support
-{
- class RiffState;
-}
-
-extern XMPFileHandler * WAV_MetaHandlerCTor ( XMPFiles * parent );
-
-extern bool WAV_CheckFormat ( XMP_FileFormat format,
- XMP_StringPtr filePath,
- LFA_FileRef fileRef,
- XMPFiles * parent );
-
-static const XMP_OptionBits kWAV_HandlerFlags = ( kXMPFiles_CanInjectXMP |
- kXMPFiles_CanExpand |
- kXMPFiles_PrefersInPlace |
- kXMPFiles_AllowsOnlyXMP |
- kXMPFiles_ReturnsRawPacket );
- // In the future, we'll add kXMPFiles_CanReconcile
-
-class WAV_MetaHandler : public XMPFileHandler
-{
-public:
-
- WAV_MetaHandler ( XMPFiles * parent );
- ~WAV_MetaHandler();
-
- void CacheFileData();
-
- void UpdateFile ( bool doSafeUpdate );
- void WriteFile ( LFA_FileRef sourceRef, const std::string & sourcePath );
-
-private:
-
- void ImportLegacyItem ( RIFF_Support::RiffState& inOutRiffState, XMP_Uns32 tagID, XMP_Uns32 parentID,
- XMP_StringPtr xmpNS, XMP_StringPtr xmpProp, bool keepExistingXMP, bool langAlt = false );
-
- void PrepareLegacyExport ( XMP_StringPtr xmpNS, XMP_StringPtr xmpProp, XMP_Uns32 legacyID, std::string * legacyStr,
- std::string * digestStr, MD5_CTX * md5, bool langAlt = false );
-
- void UTF8ToMBCS ( std::string * str );
- void MBCSToUTF8 ( std::string * str );
-
-}; // WAV_MetaHandler
-
-// =================================================================================================
-
-#endif // XMP_UNIXBuild
-#endif // __WAV_Handler_hpp__
diff --git a/source/XMPFiles/FileHandlers/XDCAMEX_Handler.cpp b/source/XMPFiles/FileHandlers/XDCAMEX_Handler.cpp
index 47c0851..009ced0 100644
--- a/source/XMPFiles/FileHandlers/XDCAMEX_Handler.cpp
+++ b/source/XMPFiles/FileHandlers/XDCAMEX_Handler.cpp
@@ -1,6 +1,6 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2008 Adobe Systems Incorporated
+// Copyright 2008 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -136,9 +136,9 @@ bool XDCAMEX_CheckFormat ( XMP_FileFormat format,
tempPath += kDirChar;
tempPath += clipName;
size_t pathLen = tempPath.size() + 1; // Include a terminating nul.
- parent->handlerTemp = malloc ( pathLen );
- if ( parent->handlerTemp == 0 ) XMP_Throw ( "No memory for XDCAMEX clip info", kXMPErr_NoMemory );
- memcpy ( parent->handlerTemp, tempPath.c_str(), pathLen );
+ parent->tempPtr = malloc ( pathLen );
+ if ( parent->tempPtr == 0 ) XMP_Throw ( "No memory for XDCAMEX clip info", kXMPErr_NoMemory );
+ memcpy ( parent->tempPtr, tempPath.c_str(), pathLen );
return true;
@@ -164,13 +164,13 @@ XDCAMEX_MetaHandler::XDCAMEX_MetaHandler ( XMPFiles * _parent ) : expat(0)
this->handlerFlags = kXDCAMEX_HandlerFlags;
this->stdCharForm = kXMP_Char8Bit;
- // Extract the root path and clip name from handlerTemp.
+ // Extract the root path and clip name from tempPtr.
- XMP_Assert ( this->parent->handlerTemp != 0 );
+ XMP_Assert ( this->parent->tempPtr != 0 );
- this->rootPath.assign ( (char*) this->parent->handlerTemp );
- free ( this->parent->handlerTemp );
- this->parent->handlerTemp = 0;
+ this->rootPath.assign ( (char*) this->parent->tempPtr );
+ free ( this->parent->tempPtr );
+ this->parent->tempPtr = 0;
SplitLeafName ( &this->rootPath, &this->clipName );
@@ -184,9 +184,9 @@ XDCAMEX_MetaHandler::~XDCAMEX_MetaHandler()
{
this->CleanupLegacyXML();
- if ( this->parent->handlerTemp != 0 ) {
- free ( this->parent->handlerTemp );
- this->parent->handlerTemp = 0;
+ if ( this->parent->tempPtr != 0 ) {
+ free ( this->parent->tempPtr );
+ this->parent->tempPtr = 0;
}
} // XDCAMEX_MetaHandler::~XDCAMEX_MetaHandler
@@ -272,11 +272,6 @@ void XDCAMEX_MetaHandler::MakeLegacyDigest ( std::string * digestStr )
void XDCAMEX_MetaHandler::CleanupLegacyXML()
{
-
- if ( ! this->defaultNS.empty() ) {
- SXMPMeta::DeleteNamespace ( this->defaultNS.c_str() );
- this->defaultNS.erase();
- }
if ( this->expat != 0 ) { delete ( this->expat ); this->expat = 0; }
@@ -290,7 +285,7 @@ void XDCAMEX_MetaHandler::CleanupLegacyXML()
void XDCAMEX_MetaHandler::CacheFileData()
{
- XMP_Assert ( (! this->containsXMP) && (! this->containsTNail) );
+ XMP_Assert ( ! this->containsXMP );
// See if the clip's .XMP file exists.
@@ -381,7 +376,7 @@ void XDCAMEX_MetaHandler::GetTakeDuration ( const std::string & takeURI, std::st
takeXMLFile.fileRef = LFA_Open ( takePath.c_str(), 'r' );
if ( takeXMLFile.fileRef == 0 ) return; // The open failed.
- ExpatAdapter * expat = XMP_NewExpatAdapter();
+ ExpatAdapter * expat = XMP_NewExpatAdapter ( ExpatAdapter::kUseLocalNamespaces );
if ( this->expat == 0 ) return;
XMP_Uns8 buffer [64*1024];
@@ -459,7 +454,7 @@ void XDCAMEX_MetaHandler::GetTakeUMID ( const std::string& clipUMID,
mediaproXMLFile.fileRef = LFA_Open ( mediapropath.c_str(), 'r' );
if ( mediaproXMLFile.fileRef == 0 ) return; // The open failed.
- ExpatAdapter * expat = XMP_NewExpatAdapter();
+ ExpatAdapter * expat = XMP_NewExpatAdapter ( ExpatAdapter::kUseLocalNamespaces );
if ( this->expat == 0 ) return;
XMP_Uns8 buffer [64*1024];
@@ -562,15 +557,11 @@ void XDCAMEX_MetaHandler::ProcessXMP()
std::string xmlPath;
this->MakeClipFilePath ( &xmlPath, "M01.XML" );
- // *** Hack: Special case trickery to detect and clean up default XML namespace usage.
-
- bool haveDefaultNS = SXMPMeta::GetNamespaceURI ( "_dflt_", 0 ); // Is there already a default namespace?
-
AutoFile xmlFile;
xmlFile.fileRef = LFA_Open ( xmlPath.c_str(), 'r' );
if ( xmlFile.fileRef == 0 ) return; // The open failed.
- this->expat = XMP_NewExpatAdapter();
+ this->expat = XMP_NewExpatAdapter ( ExpatAdapter::kUseLocalNamespaces );
if ( this->expat == 0 ) XMP_Throw ( "XDCAMEX_MetaHandler: Can't create Expat adapter", kXMPErr_NoMemory );
XMP_Uns8 buffer [64*1024];
@@ -584,12 +575,6 @@ void XDCAMEX_MetaHandler::ProcessXMP()
LFA_Close ( xmlFile.fileRef );
xmlFile.fileRef = 0;
- if ( ! haveDefaultNS ) {
- // No prior default XML namespace. If there is one now, remember it and delete it when done.
- haveDefaultNS = SXMPMeta::GetNamespaceURI ( "_dflt_", &this->defaultNS );
- XMP_Assert ( haveDefaultNS == (! this->defaultNS.empty()) );
- }
-
// The root element should be NonRealTimeMeta in some namespace. Take whatever this file uses.
XML_Node & xmlTree = this->expat->tree;
diff --git a/source/XMPFiles/FileHandlers/XDCAMEX_Handler.hpp b/source/XMPFiles/FileHandlers/XDCAMEX_Handler.hpp
index 63852b5..ee7b23f 100644
--- a/source/XMPFiles/FileHandlers/XDCAMEX_Handler.hpp
+++ b/source/XMPFiles/FileHandlers/XDCAMEX_Handler.hpp
@@ -3,7 +3,7 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2008 Adobe Systems Incorporated
+// Copyright 2008 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -69,7 +69,7 @@ private:
void CleanupLegacyXML();
- std::string rootPath, clipName, defaultNS, xdcNS, legacyNS, clipUMID;
+ std::string rootPath, clipName, xdcNS, legacyNS, clipUMID;
ExpatAdapter * expat;
XML_Node * clipMetadata;
diff --git a/source/XMPFiles/FileHandlers/XDCAM_Handler.cpp b/source/XMPFiles/FileHandlers/XDCAM_Handler.cpp
index 6ccfcab..d88109e 100644
--- a/source/XMPFiles/FileHandlers/XDCAM_Handler.cpp
+++ b/source/XMPFiles/FileHandlers/XDCAM_Handler.cpp
@@ -1,6 +1,6 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2008 Adobe Systems Incorporated
+// Copyright 2007 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -21,25 +21,25 @@ using namespace std;
/// well-defined layout and naming rules. There are 2 different layouts for XDCAM, called FAM and SAM.
/// The FAM layout is used by "normal" XDCAM devices. The SAM layout is used by XDCAM-EX devices.
///
-/// A typical FAM layout looks like:
+/// A typical FAM layout looks like (note mixed case for General, Clip, Edit, and Sub folders):
///
/// .../MyMovie/
/// INDEX.XML
/// DISCMETA.XML
/// MEDIAPRO.XML
-/// GENERAL/
+/// General/
/// unknown files
-/// CLIP/
+/// Clip/
/// C0001.MXF
/// C0001M01.XML
/// C0001M01.XMP
/// C0002.MXF
/// C0002M01.XML
/// C0002M01.XMP
-/// SUB/
+/// Sub/
/// C0001S01.MXF
/// C0002S01.MXF
-/// EDIT/
+/// Edit/
/// E0001E01.SMI
/// E0001M01.XML
/// E0002E01.SMI
@@ -112,10 +112,10 @@ using namespace std;
// parentName - empty
// leafName - "C0001"
//
-// If the client passed a FAM file path, like ".../MyMovie/EDIT/E0001E01.SMI", they are:
+// If the client passed a FAM file path, like ".../MyMovie/Edit/E0001E01.SMI", they are:
// rootPath - "..."
// gpName - "MyMovie"
-// parentName - "EDIT"
+// parentName - "EDIT" (common code has shifted the case)
// leafName - "E0001E01"
//
// If the client passed a SAM file path, like ".../MyMovie/PROAV/CLPR/C0001/C0001A02.MXF", they are:
@@ -134,7 +134,7 @@ using namespace std;
// ! folder, we will fail to do the leading 'E' to 'C' coercion. We might also erroneously remove a
// ! suffix from a mapped essence file with a name like ClipX01.MXF.
-// ! The common code has shifted the gpName, parentName, and leafName strings to upper case. It has
+// ! The common code has shifted the gpName, parentName, and leafName strings to uppercase. It has
// ! also made sure that for a logical clip path the rootPath is an existing folder, and that the
// ! file exists for a full file path.
@@ -169,6 +169,7 @@ bool XDCAM_CheckFormat ( XMP_FileFormat format,
// This is the existing file case. See if this is FAM or SAM, tweak the clip name as needed.
if ( (parentName == "CLIP") || (parentName == "EDIT") || (parentName == "SUB") ) {
+ // ! The standard says Clip/Edit/Sub, but the caller has already shifted to upper case.
isFAM = true;
} else if ( (gpName != "CLPR") && (gpName != "EDTR") ) {
return false;
@@ -215,7 +216,7 @@ bool XDCAM_CheckFormat ( XMP_FileFormat format,
// Make sure the general XDCAM package structure is legit. Set tempPath as a bogus path of the
// form <root>/<FAM-or-SAM>/<clip>, e.g. ".../MyMovie/FAM/C0001". This is passed the handler via
- // the handlerTemp hackery.
+ // the tempPtr hackery.
if ( isFAM ) {
@@ -228,7 +229,7 @@ bool XDCAM_CheckFormat ( XMP_FileFormat format,
if ( GetChildMode ( tempPath, "MEDIAPRO.XML" ) != kFMode_IsFile ) return false;
tempPath += kDirChar;
- tempPath += "CLIP";
+ tempPath += "Clip"; // ! Yes, mixed case.
tempPath += kDirChar;
tempPath += clipName;
tempPath += "M01.XML";
@@ -278,9 +279,9 @@ bool XDCAM_CheckFormat ( XMP_FileFormat format,
// from here to there.
size_t pathLen = tempPath.size() + 1; // Include a terminating nul.
- parent->handlerTemp = malloc ( pathLen );
- if ( parent->handlerTemp == 0 ) XMP_Throw ( "No memory for XDCAM clip info", kXMPErr_NoMemory );
- memcpy ( parent->handlerTemp, tempPath.c_str(), pathLen ); // AUDIT: Safe, allocated above.
+ parent->tempPtr = malloc ( pathLen );
+ if ( parent->tempPtr == 0 ) XMP_Throw ( "No memory for XDCAM clip info", kXMPErr_NoMemory );
+ memcpy ( parent->tempPtr, tempPath.c_str(), pathLen ); // AUDIT: Safe, allocated above.
return true;
@@ -307,13 +308,13 @@ XDCAM_MetaHandler::XDCAM_MetaHandler ( XMPFiles * _parent ) : isFAM(false), expa
this->handlerFlags = kXDCAM_HandlerFlags;
this->stdCharForm = kXMP_Char8Bit;
- // Extract the root path, clip name, and FAM/SAM flag from handlerTemp.
+ // Extract the root path, clip name, and FAM/SAM flag from tempPtr.
- XMP_Assert ( this->parent->handlerTemp != 0 );
+ XMP_Assert ( this->parent->tempPtr != 0 );
- this->rootPath.assign ( (char*) this->parent->handlerTemp );
- free ( this->parent->handlerTemp );
- this->parent->handlerTemp = 0;
+ this->rootPath.assign ( (char*) this->parent->tempPtr );
+ free ( this->parent->tempPtr );
+ this->parent->tempPtr = 0;
SplitLeafName ( &this->rootPath, &this->clipName );
@@ -333,9 +334,9 @@ XDCAM_MetaHandler::~XDCAM_MetaHandler()
{
this->CleanupLegacyXML();
- if ( this->parent->handlerTemp != 0 ) {
- free ( this->parent->handlerTemp );
- this->parent->handlerTemp = 0;
+ if ( this->parent->tempPtr != 0 ) {
+ free ( this->parent->tempPtr );
+ this->parent->tempPtr = 0;
}
} // XDCAM_MetaHandler::~XDCAM_MetaHandler
@@ -351,7 +352,7 @@ void XDCAM_MetaHandler::MakeClipFilePath ( std::string * path, XMP_StringPtr suf
*path += kDirChar;
if ( this->isFAM ) {
- *path += "CLIP";
+ *path += "Clip"; // ! Yes, mixed case.
} else {
*path += "PROAV";
*path += kDirChar;
@@ -427,11 +428,6 @@ void XDCAM_MetaHandler::MakeLegacyDigest ( std::string * digestStr )
void XDCAM_MetaHandler::CleanupLegacyXML()
{
-
- if ( ! this->defaultNS.empty() ) {
- SXMPMeta::DeleteNamespace ( this->defaultNS.c_str() );
- this->defaultNS.erase();
- }
if ( this->expat != 0 ) { delete ( this->expat ); this->expat = 0; }
@@ -445,7 +441,7 @@ void XDCAM_MetaHandler::CleanupLegacyXML()
void XDCAM_MetaHandler::CacheFileData()
{
- XMP_Assert ( (! this->containsXMP) && (! this->containsTNail) );
+ XMP_Assert ( ! this->containsXMP );
// See if the clip's .XMP file exists.
@@ -515,17 +511,11 @@ void XDCAM_MetaHandler::ProcessXMP()
std::string xmlPath, umid;
this->MakeClipFilePath ( &xmlPath, "M01.XML" );
- // --------------------------------------------------------------
- // *** This is a minimal Q&D example of legacy metadata handling.
- // *** Hack: Special case trickery to detect and clean up default XML namespace usage.
-
- bool haveDefaultNS = SXMPMeta::GetNamespaceURI ( "_dflt_", 0 ); // Is there already a default namespace?
-
AutoFile xmlFile;
xmlFile.fileRef = LFA_Open ( xmlPath.c_str(), 'r' );
if ( xmlFile.fileRef == 0 ) return; // The open failed.
- this->expat = XMP_NewExpatAdapter();
+ this->expat = XMP_NewExpatAdapter ( ExpatAdapter::kUseLocalNamespaces );
if ( this->expat == 0 ) XMP_Throw ( "XDCAM_MetaHandler: Can't create Expat adapter", kXMPErr_NoMemory );
XMP_Uns8 buffer [64*1024];
@@ -539,12 +529,6 @@ void XDCAM_MetaHandler::ProcessXMP()
LFA_Close ( xmlFile.fileRef );
xmlFile.fileRef = 0;
- if ( ! haveDefaultNS ) {
- // No prior default XML namespace. If there is one now, remember it and delete it when done.
- haveDefaultNS = SXMPMeta::GetNamespaceURI ( "_dflt_", &this->defaultNS );
- XMP_Assert ( haveDefaultNS == (! this->defaultNS.empty()) );
- }
-
// The root element should be NonRealTimeMeta in some namespace. Take whatever this file uses.
XML_Node & xmlTree = this->expat->tree;
diff --git a/source/XMPFiles/FileHandlers/XDCAM_Handler.hpp b/source/XMPFiles/FileHandlers/XDCAM_Handler.hpp
index abd861b..55f61dc 100644
--- a/source/XMPFiles/FileHandlers/XDCAM_Handler.hpp
+++ b/source/XMPFiles/FileHandlers/XDCAM_Handler.hpp
@@ -3,7 +3,7 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2008 Adobe Systems Incorporated
+// Copyright 2007 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -68,7 +68,7 @@ private:
void MakeLegacyDigest ( std::string * digestStr );
void CleanupLegacyXML();
- std::string rootPath, clipName, defaultNS, xdcNS, legacyNS;
+ std::string rootPath, clipName, xdcNS, legacyNS;
bool isFAM;
diff --git a/source/XMPFiles/FormatSupport/ASF_Support.cpp b/source/XMPFiles/FormatSupport/ASF_Support.cpp
index 3baa7d0..1180f9d 100644
--- a/source/XMPFiles/FormatSupport/ASF_Support.cpp
+++ b/source/XMPFiles/FormatSupport/ASF_Support.cpp
@@ -1,6 +1,6 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2008 Adobe Systems Incorporated
+// Copyright 2006 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -1120,26 +1120,27 @@ void ASF_LegacyManager::ImportLegacy ( SXMPMeta* xmp )
if ( ! broadcastSet ) {
ConvertMSDateToISODate ( fields[fieldCreationDate], &utf8 );
- xmp->SetProperty ( kXMP_NS_XMP, "CreateDate", utf8.c_str(), kXMP_DeleteExisting );
+ if ( ! utf8.empty() ) xmp->SetProperty ( kXMP_NS_XMP, "CreateDate", utf8.c_str(), kXMP_DeleteExisting );
}
FromUTF16 ( (UTF16Unit*)fields[fieldTitle].c_str(), (fields[fieldTitle].size() / 2), &utf8, false );
- xmp->SetLocalizedText ( kXMP_NS_DC, "title", "", "x-default", utf8.c_str(), kXMP_DeleteExisting );
+ if ( ! utf8.empty() ) xmp->SetLocalizedText ( kXMP_NS_DC, "title", "", "x-default", utf8.c_str(), kXMP_DeleteExisting );
xmp->DeleteProperty ( kXMP_NS_DC, "creator" );
FromUTF16 ( (UTF16Unit*)fields[fieldAuthor].c_str(), (fields[fieldAuthor].size() / 2), &utf8, false );
- SXMPUtils::SeparateArrayItems ( xmp, kXMP_NS_DC, "creator", kXMPUtil_AllowCommas, utf8.c_str() );
+ if ( ! utf8.empty() ) SXMPUtils::SeparateArrayItems ( xmp, kXMP_NS_DC, "creator",
+ (kXMP_PropArrayIsOrdered | kXMPUtil_AllowCommas), utf8.c_str() );
FromUTF16 ( (UTF16Unit*)fields[fieldCopyright].c_str(), (fields[fieldCopyright].size() / 2), &utf8, false );
- xmp->SetLocalizedText ( kXMP_NS_DC, "rights", "", "x-default", utf8.c_str(), kXMP_DeleteExisting );
+ if ( ! utf8.empty() ) xmp->SetLocalizedText ( kXMP_NS_DC, "rights", "", "x-default", utf8.c_str(), kXMP_DeleteExisting );
FromUTF16 ( (UTF16Unit*)fields[fieldDescription].c_str(), (fields[fieldDescription].size() / 2), &utf8, false );
- xmp->SetLocalizedText ( kXMP_NS_DC, "description", "", "x-default", utf8.c_str(), kXMP_DeleteExisting );
+ if ( ! utf8.empty() ) xmp->SetLocalizedText ( kXMP_NS_DC, "description", "", "x-default", utf8.c_str(), kXMP_DeleteExisting );
- xmp->SetProperty ( kXMP_NS_XMP_Rights, "WebStatement", fields[fieldCopyrightURL].c_str(), kXMP_DeleteExisting );
+ if ( ! utf8.empty() ) xmp->SetProperty ( kXMP_NS_XMP_Rights, "WebStatement", fields[fieldCopyrightURL].c_str(), kXMP_DeleteExisting );
#if ! Exclude_LicenseURL_Recon
- xmp->SetProperty ( kXMP_NS_XMP_Rights, "Certificate", fields[fieldLicenseURL].c_str(), kXMP_DeleteExisting );
+ if ( ! fields[fieldLicenseURL].empty() ) xmp->SetProperty ( kXMP_NS_XMP_Rights, "Certificate", fields[fieldLicenseURL].c_str(), kXMP_DeleteExisting );
#endif
imported = true;
@@ -1393,6 +1394,7 @@ void ASF_LegacyManager::ConvertMSDateToISODate ( std::string& source, std::strin
date.second = second;
date.nanoSecond = nanoSec;
+ date.hasTimeZone = true; // ! Needed for ConvertToUTCTime to do anything.
SXMPUtils::ConvertToUTCTime ( &date ); // Normalize the date/time.
SXMPUtils::ConvertFromDate ( date, dest ); // Convert to an ISO 8601 string.
diff --git a/source/XMPFiles/FormatSupport/ASF_Support.hpp b/source/XMPFiles/FormatSupport/ASF_Support.hpp
index 5260cb3..e170cb2 100644
--- a/source/XMPFiles/FormatSupport/ASF_Support.hpp
+++ b/source/XMPFiles/FormatSupport/ASF_Support.hpp
@@ -3,7 +3,7 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2008 Adobe Systems Incorporated
+// Copyright 2006 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
diff --git a/source/XMPFiles/FormatSupport/ID3_Support.cpp b/source/XMPFiles/FormatSupport/ID3_Support.cpp
deleted file mode 100644
index ee8c008..0000000
--- a/source/XMPFiles/FormatSupport/ID3_Support.cpp
+++ /dev/null
@@ -1,1156 +0,0 @@
-// =================================================================================================
-// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2008 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 "XMP_Environment.h" // ! This must be the first include.
-#if ! XMP_UNIXBuild // Closes at very bottom. Disabled on UNIX until legacy-as-local is fixed.
-
-#include "XMP_Const.h"
-
-#include "ID3_Support.hpp"
-
-#include "UnicodeConversions.hpp"
-#include "Reconcile_Impl.hpp"
-
-#include <stdio.h>
-
-#if XMP_WinBuild
- #pragma warning ( disable : 4996 ) // '...' was declared deprecated
-#endif
-
-// For more information about the id3v2 specification
-// Please refer to http://www.id3.org/develop.html
-
-namespace ID3_Support {
-
- 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
- };
-
- // Some types
- #ifndef XMP_Int64
- typedef unsigned long long XMP_Int64;
- #endif
-
- static bool FindXMPFrame(LFA_FileRef inFileRef, XMP_Int64 &posXMP, XMP_Int64 &posPAD, unsigned long &dwExtendedTag, unsigned long &dwLen);
- static unsigned long SkipExtendedHeader(LFA_FileRef inFileRef, XMP_Uns8 bVersion, XMP_Uns8 flag);
- static bool GetFrameInfo(LFA_FileRef inFileRef, XMP_Uns8 bVersion, char *strFrameID, XMP_Uns8 &cflag1, XMP_Uns8 &cflag2, unsigned long &dwSize);
- static bool ReadSize(LFA_FileRef inFileRef, XMP_Uns8 bVersion, unsigned long &dwSize);
- static unsigned long CalculateSize(XMP_Uns8 bVersion, unsigned long dwSizeIn);
- static bool LoadTagHeaderAndUnknownFrames(LFA_FileRef inFileRef, char *strBuffer, size_t strBufferLen, bool fRecon, unsigned long &posPad);
-
- #define GetFilePosition(file) LFA_Seek ( file, 0, SEEK_CUR )
-
- const unsigned long k_dwTagHeaderSize = 10;
- const unsigned long k_dwFrameHeaderSize = 10;
- const unsigned long k_dwXMPLabelSize = 4; // 4 (size of "XMP\0")
-
-// ID3v2 flags %abcd0000
-// Where:
-// a - Unsynchronisation
-// b - Extended header
-// c - Experimental indicator
-// d - Footer present
- const unsigned char flagUnsync = 0x80; // (MSb)
- const unsigned char flagExt = 0x40;
- const unsigned char flagExp = 0x20;
- const unsigned char flagFooter = 0x10;
-
-
-#ifndef Trace_ID3_Support
- #define Trace_ID3_Support 0
-#endif
-
-// =================================================================================================
-
-#if XMP_WinBuild
-
- #define stricmp _stricmp
-
-#else
-
- static int stricmp ( const char * left, const char * right ) // Case insensitive ASCII compare.
- {
- char chL = *left; // ! Allow for 0 passes in the loop (one string is empty).
- char chR = *right; // ! Return -1 for stricmp ( "a", "Z" ).
-
- for ( ; (*left != 0) && (*right != 0); ++left, ++right ) {
- chL = *left;
- chR = *right;
- if ( chL == chR ) continue;
- if ( ('A' <= chL) && (chL <= 'Z') ) chL |= 0x20;
- if ( ('A' <= chR) && (chR <= 'Z') ) chR |= 0x20;
- if ( chL != chR ) break;
- }
-
- if ( chL == chR ) return 0;
- if ( chL < chR ) return -1;
- return 1;
-
- }
-
-#endif
-
-// =================================================================================================
-
-// *** Load Scenario:
-// - Check for id3v2 tag
-//
-// - Parse through the frame for "PRIV" + UTF8 Encoding + "XMP"
-//
-// - If found, load it.
-bool GetMetaData ( LFA_FileRef inFileRef, char* buffer, unsigned long* pBufferSize, ::XMP_Int64* fileOffset )
-{
-
- if ( pBufferSize == 0 ) return false;
-
- unsigned long dwSizeIn = *pBufferSize;
- *pBufferSize = 0;
- XMP_Int64 posXMP = 0ULL, posPAD = 0ULL;
- unsigned long dwLen = 0, dwExtendedTag = 0;
- if ( ! FindXMPFrame ( inFileRef, posXMP, posPAD, dwExtendedTag, dwLen ) ) return false;
-
- // Found the XMP frame! Get the rest of frame into the buffer
- unsigned long dwXMPBufferLen = dwLen - k_dwXMPLabelSize;
- *pBufferSize = dwXMPBufferLen;
-
- if ( fileOffset != 0 ) *fileOffset = posXMP + k_dwXMPLabelSize;
-
- if ( buffer != 0 ) {
- // Seek 4 bytes ahead to get the XMP data.
- LFA_Seek ( inFileRef, posXMP+k_dwXMPLabelSize, SEEK_SET );
- if ( dwXMPBufferLen > dwSizeIn ) dwXMPBufferLen = dwSizeIn;
- LFA_Read ( inFileRef, buffer, dwXMPBufferLen ); // Get the XMP frame
- }
-
- return true;
-
-}
-
-// =================================================================================================
-
-bool FindFrame ( LFA_FileRef inFileRef, char* strFrame, XMP_Int64 & posFrame, unsigned long & dwLen )
-{
- // Taking into account that the first Tag is the ID3 tag
- bool fReturn = false;
- LFA_Seek ( inFileRef, 0ULL, SEEK_SET );
-
- #if Trace_ID3_Support
- fprintf ( stderr, "ID3_Support::FindFrame : Looking for %s\n", strFrame );
- #endif
-
- // Read the tag name
- char szID[4] = {"xxx"};
- long bytesRead = LFA_Read ( inFileRef, szID, 3 );
- if ( bytesRead == 0 ) return fReturn;
-
- // Check for "ID3"
- if ( strcmp ( szID, "ID3" ) != 0 ) return fReturn;
-
- // Read the version, flag and size
- XMP_Uns8 v1 = 0, v2 = 0, flags = 0;
- unsigned long dwTagSize = 0;
-
- if ( ! GetTagInfo ( inFileRef, v1, v2, flags, dwTagSize ) ) return fReturn;
- if ( dwTagSize == 0 ) return fReturn;
- if ( v1 > 4 ) return fReturn; // We don't support anything newer than id3v2 4.0
-
- // If there's an extended header, ignore it
- XMP_Int32 dwExtendedTag = SkipExtendedHeader(inFileRef, v1, flags);
- dwTagSize -= dwExtendedTag;
-
- // Enumerate through the frames
- XMP_Int64 posCur = 0ULL;
- posCur = GetFilePosition ( inFileRef );
- XMP_Int64 posEnd = posCur + dwTagSize;
-
- while ( posCur < posEnd ) {
-
- if ( (posEnd - posCur) < k_dwTagHeaderSize ) break; // Not enough room for a header, must be padding.
-
- char szFrameID[5] = {"xxxx"};
- unsigned long dwFrameSize = 0;
- XMP_Uns8 cflag1 = 0, cflag2 = 0;
-
- // Get the next frame
- if ( ! GetFrameInfo ( inFileRef, v1, szFrameID, cflag1, cflag2, dwFrameSize ) ) break;
-
- // Are we in a padding frame?
- if ( dwFrameSize == 0 ) break;
-
- // Is it the Frame we're looking for?
- if ( strcmp ( szFrameID, strFrame ) == 0 ) {
- posFrame = GetFilePosition ( inFileRef );
- dwLen = dwFrameSize;
- fReturn = true;
- break;
- } else {
- // Jump to the next frame
- LFA_Seek ( inFileRef, dwFrameSize, SEEK_CUR );
- }
-
- posCur = GetFilePosition ( inFileRef );
- }
-
- #if Trace_ID3_Support
- if ( fReturn ) {
- fprintf ( stderr, " Found %s, offset %d, length %d\n", strFrame, (long)posFrame, dwLen );
- }
- #endif
-
- return fReturn;
-}
-
-// =================================================================================================
-
-bool GetFrameData ( LFA_FileRef inFileRef, char* strFrame, char* buffer, unsigned long &dwBufferSize )
-{
- char strData[TAG_MAX_SIZE+4]; // Plus 4 for two worst case UTF-16 nul terminators.
- size_t sdPos = 0; // Offset within strData to the value.
- memset ( &strData[0], 0, sizeof(strData) );
-
- if ( (buffer == 0) || (dwBufferSize > TAG_MAX_SIZE) ) return false;
-
- const unsigned long dwSizeIn = dwBufferSize;
- XMP_Int64 posFrame = 0ULL;
- unsigned long dwLen = 0;
- XMP_Uns8 bEncoding = 0;
-
- // Find the frame
- if ( ! FindFrame ( inFileRef, strFrame, posFrame, dwLen ) ) return false;
- #if Trace_ID3_Support
- fprintf ( stderr, " Getting frame data\n" );
- #endif
-
- if ( dwLen <= 0 ) {
-
- dwBufferSize = 1;
- buffer[0] = 0;
-
- } else {
-
- // Get the value for a typical text frame, having an encoding byte followed by the value.
- // COMM frames are special, see below. First get encoding, and the frame data into strData.
-
- dwBufferSize = dwLen - 1; // Don't count the encoding byte.
-
- // Seek to the frame
- LFA_Seek ( inFileRef, posFrame, SEEK_SET );
-
- // Read the Encoding
- LFA_Read ( inFileRef, &bEncoding, 1 );
- if ( bEncoding > 3 ) return false;
-
- // Get the frame
- if ( dwBufferSize > dwSizeIn ) dwBufferSize = dwSizeIn;
-
- if ( dwBufferSize >= TAG_MAX_SIZE ) return false; // No room for data.
- LFA_Read ( inFileRef, &strData[0], dwBufferSize );
-
- if ( strcmp ( strFrame, "COMM" ) == 0 ) {
-
- // A COMM frame has a 3 byte language tag, then an encoded and nul terminated description
- // string, then the encoded value string. Set dwOffset to the offset to the value.
-
- unsigned long dwOffset = 3; // Skip the 3 byte language code.
-
- if ( (bEncoding == 0) || (bEncoding == 3) ) {
- dwOffset += (unsigned long)(strlen ( &strData[3] ) + 1); // Skip the descriptor and nul.
- } else {
- UTF16Unit* u16Ptr = (UTF16Unit*) (&strData[3]);
- for ( ; *u16Ptr != 0; ++u16Ptr ) dwOffset += 2; // Skip the descriptor.
- dwOffset += 2; // Skip the nul also.
- }
-
- if ( dwOffset >= dwBufferSize ) return false;
- dwBufferSize -= dwOffset;
-
- sdPos = dwOffset;
-
- #if Trace_ID3_Support
- fprintf ( stderr, " COMM frame, dwOffset %d\n", dwOffset );
- #endif
-
- }
-
- // Encoding translation
- switch ( bEncoding ) {
-
- case 1: // UTF-16 with a BOM. (Might be missing for empty string.)
- case 2: // Big endian UTF-16 with no BOM.
- {
- bool bigEndian = true; // Assume big endian if no BOM.
- UTF16Unit* u16Ptr = (UTF16Unit*) &strData[sdPos];
-
- if ( GetUns16BE ( u16Ptr ) == 0xFEFF ) {
- ++u16Ptr; // Don't translate the BOM.
- } else if ( GetUns16BE ( u16Ptr ) == 0xFFFE ) {
- bigEndian = false;
- ++u16Ptr; // Don't translate the BOM.
- }
-
- size_t u16Len = 0; // Count the UTF-16 units, not bytes.
- for ( UTF16Unit* temp = u16Ptr; *temp != 0; ++temp ) ++u16Len;
-
- std::string utf8Str;
- FromUTF16 ( u16Ptr, u16Len, &utf8Str, bigEndian );
- if ( utf8Str.size() >= (sizeof(strData) - sdPos) ) return false;
- strcpy ( &strData[sdPos], utf8Str.c_str() ); // AUDIT: Protected by the above check.
- }
- break;
-
- case 0: // ISO Latin-1 (8859-1).
- {
- std::string utf8Str;
- char* localPtr = &strData[sdPos];
- size_t localLen = dwBufferSize;
-
- ReconcileUtils::Latin1ToUTF8 ( localPtr, localLen, &utf8Str );
- if ( utf8Str.size() >= (sizeof(strData) - sdPos) ) return false;
- strcpy ( &strData[sdPos], utf8Str.c_str() ); // AUDIT: Protected by the above check.
- }
- break;
-
- case 3: // UTF-8
- default:
- // Handled appropriately
- break;
-
- }
-
- char * strTemp = &strData[sdPos];
-
- if ( strcmp ( strFrame, "TCON" ) == 0 ) {
-
- char str[TAG_MAX_SIZE];
- str[0] = 0;
- if ( strlen ( &strData[sdPos] ) >= sizeof(str) ) return false;
- strcpy ( str, &strData[sdPos] ); // AUDIT: Protected by the above check.
-
- #if Trace_ID3_Support
- fprintf ( stderr, " TCON frame, first char '%c'\n", str[0] );
- #endif
-
- // Genre: let's get the "string" value
- if ( str[0] == '(' ) {
- int iGenre = atoi(str+1);
- if ( (iGenre > 0) && (iGenre < 127) ) {
- strTemp = Genres[iGenre];
- } else {
- strTemp = Genres[12];
- }
- } else {
- // Text, let's "try" to find it anyway
- int i = 0;
- for ( i=0; i < 127; ++i ) {
- if ( stricmp ( str, Genres[i] ) == 0 ) {
- strTemp = Genres[i]; // Found, let's use the one in the list
- break;
- }
- }
- if ( i == 127 ) strTemp = Genres[12]; // Not found
- }
-
- }
-
- #if Trace_ID3_Support
- fprintf ( stderr, " Have data, length %d, \"%s\"\n", strlen(strTemp), strTemp );
- #endif
-
- if ( strlen(strTemp) >= dwSizeIn ) return false;
- strcpy ( buffer, strTemp ); // AUDIT: Protected by the above check.
-
- }
-
- return true;
-
-}
-
-// =================================================================================================
-
-bool AddXMPTagToID3Buffer ( char * strCur, unsigned long * pdwCurOffset, unsigned long dwMaxSize, XMP_Uns8 bVersion,
- char *strFrameName, const char * strXMPTagTemp, unsigned long dwXMPLengthTemp )
-{
- char strGenre[64];
- const char * strXMPTag = strXMPTagTemp;
- XMP_Int32 dwCurOffset = *pdwCurOffset;
- XMP_Uns8 bEncoding = 0;
- long dwXMPLength = dwXMPLengthTemp;
-
- if ( dwXMPLength == 0 ) return false;
-
- if ( strcmp ( strFrameName, "TCON" ) == 0 ) {
-
- // Genre: we need to get the number back...
- int iFound = 12;
-
- for ( int i=0; i < 127; ++i ) {
- if ( stricmp ( strXMPTag, Genres[i] ) == 0 ) {
- iFound = i; // Found
- break;
- }
- }
-
- snprintf ( strGenre, sizeof(strGenre), "(%d)", iFound ); // AUDIT: Using sizeof(strGenre) is safe.
- strXMPTag = strGenre;
- dwXMPLength = (long)strlen(strXMPTag);
-
- }
-
- // Stick with the ID3v2.3 encoding choices, they are a proper subset of ID3v2.4.
- // 0 - ISO Latin-1
- // 1 - UTF-16 with BOM
- // For 3rd party reliability we always write UTF-16 as little endian. For example, Windows
- // Media Player fails to honor the BOM, it assumes little endian.
-
- std::string tempLatin1, tempUTF8;
- ReconcileUtils::UTF8ToLatin1 ( strXMPTag, dwXMPLength, &tempLatin1 );
- ReconcileUtils::Latin1ToUTF8 ( tempLatin1.data(), tempLatin1.size(), &tempUTF8 );
- if ( ((size_t)dwXMPLength != tempUTF8.size()) || (memcmp ( strXMPTag, tempUTF8.data(), dwXMPLength ) != 0) ) {
- bEncoding = 1; // Will convert to UTF-16 later.
- } else {
- strXMPTag = tempLatin1.c_str(); // Use the Latin-1 encoding for output.
- dwXMPLength = (long)tempLatin1.size();
- }
-
- std::string strUTF16;
- if ( bEncoding == 1 ) {
- ToUTF16 ( (UTF8Unit*)strXMPTag, dwXMPLength, &strUTF16, false /* little endian */ );
- dwXMPLength = (long)strUTF16.size() + 2; // ! Include the (to be inserted) BOM in the count.
- }
-
- // Frame Structure
- // Frame ID $xx xx xx xx (four characters)
- // Size 4 * %0xxxxxxx <<--- IMPORTANT NOTE: This is true only in v4.0 (v3.0 uses a UInt32)
- // Flags $xx xx
- // Encoding $xx (Not included in the frame header)
- // Special case: "COMM" which we have to include "XXX\0" in front of it (also not included in the frame header)
-
- unsigned long dwFrameSize = dwXMPLength + 1; // 1 == Encoding;
-
- bool fCOMM = (strcmp ( strFrameName, "COMM" ) == 0);
- if ( fCOMM ) {
- dwFrameSize += 3; // The "XXX" language part.
- dwFrameSize += ((bEncoding == 0) ? 1 : 4 ); // The empty descriptor string.
- }
-
- if ( (dwCurOffset + k_dwFrameHeaderSize + dwFrameSize) > dwMaxSize ) return false;
-
- unsigned long dwCalculated = CalculateSize ( bVersion, dwFrameSize );
-
- // FrameID
- if ( (dwMaxSize - dwCurOffset) < 4 ) return false;
- memcpy ( strCur+dwCurOffset, strFrameName, 4 ); // AUDIT: Protected by the above check.
- dwCurOffset += 4;
-
- // Frame Size - written as big endian
- strCur[dwCurOffset] = (char)(dwCalculated >> 24);
- ++dwCurOffset;
- strCur[dwCurOffset] = (char)((dwCalculated >> 16) & 0xFF);
- ++dwCurOffset;
- strCur[dwCurOffset] = (char)((dwCalculated >> 8) & 0xFF);
- ++dwCurOffset;
- strCur[dwCurOffset] = (char)(dwCalculated & 0xFF);
- ++dwCurOffset;
-
- // Flags
- strCur[dwCurOffset] = 0;
- ++dwCurOffset;
- strCur[dwCurOffset] = 0;
- ++dwCurOffset;
-
- // Encoding
- strCur[dwCurOffset] = bEncoding;
- ++dwCurOffset;
-
- // COMM extras: XXX language and empty encoded descriptor string.
- if ( fCOMM ) {
- if ( (dwMaxSize - dwCurOffset) < 3 ) return false;
- memcpy ( strCur+dwCurOffset, "XXX", 3 ); // AUDIT: Protected by the above check.
- dwCurOffset += 3;
- if ( bEncoding == 0 ) {
- strCur[dwCurOffset] = 0;
- ++dwCurOffset;
- } else {
- strCur[dwCurOffset] = 0xFF;
- ++dwCurOffset;
- strCur[dwCurOffset] = 0xFE;
- ++dwCurOffset;
- strCur[dwCurOffset] = 0;
- ++dwCurOffset;
- strCur[dwCurOffset] = 0;
- ++dwCurOffset;
- }
- }
-
- if ( bEncoding == 1 ) {
- // Add the BOM "FFFE"
- strCur[dwCurOffset] = 0xFF;
- ++dwCurOffset;
- strCur[dwCurOffset] = 0xFE;
- ++dwCurOffset;
- dwXMPLength -= 2; // The BOM was included above.
- // Copy the Unicode data
- if ( (long)(dwMaxSize - dwCurOffset) < dwXMPLength ) return false;
- memcpy ( strCur+dwCurOffset, strUTF16.data(), dwXMPLength ); // AUDIT: Protected by the above check.
- dwCurOffset += dwXMPLength;
- } else {
- // Copy the data
- if ( (long)(dwMaxSize - dwCurOffset) < dwXMPLength ) return false;
- memcpy ( strCur+dwCurOffset, strXMPTag, dwXMPLength ); // AUDIT: Protected by the above check.
- dwCurOffset += dwXMPLength;
- }
-
- *pdwCurOffset = dwCurOffset;
-
- return true;
-
-}
-
-// =================================================================================================
-
-static void OffsetAudioData ( LFA_FileRef inFileRef, XMP_Int64 audioOffset, XMP_Int64 oldAudioBase )
-{
- enum { kBuffSize = 64*1024 };
- XMP_Uns8 buffer [kBuffSize];
-
- const XMP_Int64 posEOF = LFA_Measure ( inFileRef );
- XMP_Int64 posCurrentCopy; // ! Must be a signed type!
-
- posCurrentCopy = posEOF;
- while ( posCurrentCopy >= (oldAudioBase + kBuffSize) ) {
- posCurrentCopy -= kBuffSize; // *** Xcode 2.3 seemed to generate bad code using a for loop.
- LFA_Seek ( inFileRef, posCurrentCopy, SEEK_SET );
- LFA_Read ( inFileRef, buffer, kBuffSize );
- LFA_Seek ( inFileRef, (posCurrentCopy + audioOffset), SEEK_SET );
- LFA_Write ( inFileRef, buffer, kBuffSize );
- }
-
- if ( posCurrentCopy != oldAudioBase ) {
- XMP_Uns32 remainder = (XMP_Uns32) (posCurrentCopy - oldAudioBase);
- XMP_Assert ( remainder < kBuffSize );
- LFA_Seek ( inFileRef, oldAudioBase, SEEK_SET );
- LFA_Read ( inFileRef, buffer, remainder );
- LFA_Seek ( inFileRef, (oldAudioBase + audioOffset), SEEK_SET );
- LFA_Write ( inFileRef, buffer, remainder );
- }
-
-}
-
-// =================================================================================================
-
-bool SetMetaData ( LFA_FileRef inFileRef, char* strXMPPacket, unsigned long dwXMPPacketSize,
- char* strLegacyFrames, unsigned long dwFullLegacySize, bool fRecon )
-{
- // The ID3 section layout:
- // ID3 header, 10 bytes
- // Unrecognized ID3 frames
- // Legacy ID3 metadata frames (artist, album, genre, etc.)
- // XMP frame, content is "XMP\0" plus the packet
- // padding
-
- // ID3 Buffer vars
- const unsigned long kiMaxBuffer = 100*1000;
- char szID3Buffer [kiMaxBuffer]; // Must be enough for the ID3 header, unknown ID3 frames, and legacy ID3 metadata.
- unsigned long id3BufferLen = 0; // The amount of stuff currently in the buffer.
-
- unsigned long dwOldID3ContentSize = 0; // The size of the existing ID3 content (not counting the header).
- unsigned long dwNewID3ContentSize = 0; // The size of the updated ID3 content (not counting the header).
-
- unsigned long newPadSize = 0;
-
- XMP_Uns8 bMajorVersion = 3;
-
- bool fFoundID3 = FindID3Tag ( inFileRef, dwOldID3ContentSize, bMajorVersion );
- if ( (bMajorVersion > 4) || (bMajorVersion < 3) ) return false; // Not supported
-
- // Now that we know the version of the ID3 tag, let's format the size of the XMP frame.
-
- #define k_XMPPrefixSize (k_dwFrameHeaderSize + k_dwXMPLabelSize)
- char szXMPPrefix [k_XMPPrefixSize] = { 'P', 'R', 'I', 'V', 0, 0, 0, 0, 0, 0, 'X', 'M', 'P', 0 };
- unsigned long dwXMPContentSize = k_dwXMPLabelSize + dwXMPPacketSize;
- unsigned long dwFullXMPFrameSize = k_dwFrameHeaderSize + dwXMPContentSize;
-
- unsigned long dwFormattedTemp = CalculateSize ( bMajorVersion, dwXMPContentSize );
-
- szXMPPrefix[4] = (char)(dwFormattedTemp >> 24);
- szXMPPrefix[5] = (char)((dwFormattedTemp >> 16) & 0xFF);
- szXMPPrefix[6] = (char)((dwFormattedTemp >> 8) & 0xFF);
- szXMPPrefix[7] = (char)(dwFormattedTemp & 0xFF);
-
- // Set up the ID3 buffer with the ID3 header and any existing unrecognized ID3 frames.
-
- if ( ! fFoundID3 ) {
-
- // Case 1 - No id3v2 tag: Create the tag with the XMP frame.
- // Create the tag
- // ID3v2/file identifier "ID3"
- // ID3v2 version $03 00
- // ID3v2 flags %abcd0000
- // ID3v2 size 4 * %0xxxxxxx
-
- XMP_Assert ( dwOldID3ContentSize == 0 );
-
- char szID3Header [k_dwTagHeaderSize] = { 'I', 'D', '3', 3, 0, 0, 0, 0, 0, 0 };
-
- // Copy the ID3 header
- if ( sizeof(szID3Buffer) < k_dwTagHeaderSize ) return false;
- memcpy ( szID3Buffer, szID3Header, k_dwTagHeaderSize ); // AUDIT: Protected by the above check.
- id3BufferLen = k_dwTagHeaderSize;
-
- newPadSize = 100;
- dwNewID3ContentSize = dwFullLegacySize + dwFullXMPFrameSize + newPadSize;
-
- } else {
-
- // Case 2 - id3v2 tag is present
- // 1. Copy all the unknown tags
- // 2. Make the rest padding (to be used right there).
-
- if ( (k_dwFrameHeaderSize + dwOldID3ContentSize) > kiMaxBuffer ) {
- // The ID3Buffer is not big enough to fit the id3v2 tag... let's bail...
- return false;
- }
-
- LoadTagHeaderAndUnknownFrames ( inFileRef, szID3Buffer, sizeof(szID3Buffer), fRecon, id3BufferLen );
-
- unsigned long spareLen = (k_dwFrameHeaderSize + dwOldID3ContentSize) - id3BufferLen;
-
- if ( spareLen >= (dwFullLegacySize + dwFullXMPFrameSize) ) {
-
- // The exising ID3 header can hold the update.
- dwNewID3ContentSize = dwOldID3ContentSize;
- newPadSize = spareLen - (dwFullLegacySize + dwFullXMPFrameSize);
-
- } else {
-
- // The existing ID3 header is too small, it will have to grow.
- newPadSize = 100;
- dwNewID3ContentSize = (id3BufferLen - k_dwTagHeaderSize) +
- dwFullLegacySize + dwFullXMPFrameSize + newPadSize;
-
- }
-
- }
-
- // Move the audio data if the ID3 frame is new or has to grow.
-
- XMP_Assert ( dwNewID3ContentSize >= dwOldID3ContentSize );
-
- if ( dwNewID3ContentSize > dwOldID3ContentSize ) {
- unsigned long audioOffset = dwNewID3ContentSize - dwOldID3ContentSize;
- unsigned long oldAudioBase = k_dwTagHeaderSize + dwOldID3ContentSize;
- if ( ! fFoundID3 ) {
- // We're injecting an entire ID3 section.
- audioOffset = k_dwTagHeaderSize + dwNewID3ContentSize;
- oldAudioBase = 0;
- }
- OffsetAudioData ( inFileRef, audioOffset, oldAudioBase );
- }
-
- // Set the new size for the ID3 content. This always uses the 4x7 format.
-
- dwFormattedTemp = CalculateSize ( 4, dwNewID3ContentSize );
- szID3Buffer[6] = (char)(dwFormattedTemp >> 24);
- szID3Buffer[7] = (char)((dwFormattedTemp >> 16) & 0xFF);
- szID3Buffer[8] = (char)((dwFormattedTemp >> 8) & 0xFF);
- szID3Buffer[9] = (char)(dwFormattedTemp & 0xFF);
-
- // Write the partial ID3 buffer (ID3 header plus unknown tags)
- LFA_Seek ( inFileRef, 0, SEEK_SET );
- LFA_Write ( inFileRef, szID3Buffer, id3BufferLen );
-
- // Append the new legacy metadata frames
- if ( dwFullLegacySize > 0 ) {
- LFA_Write ( inFileRef, strLegacyFrames, dwFullLegacySize );
- }
-
- // Append the XMP frame prefix
- LFA_Write ( inFileRef, szXMPPrefix, k_XMPPrefixSize );
-
- // Append the XMP packet
- LFA_Write ( inFileRef, strXMPPacket, dwXMPPacketSize );
-
- // Append the padding.
- if ( newPadSize > 0 ) {
- std::string szPad;
- szPad.reserve ( newPadSize );
- szPad.assign ( newPadSize, '\0' );
- LFA_Write ( inFileRef, const_cast<char *>(szPad.data()), newPadSize );
- }
-
- LFA_Flush ( inFileRef );
-
- return true;
-
-}
-
-// =================================================================================================
-
-bool LoadTagHeaderAndUnknownFrames ( LFA_FileRef inFileRef, char * strBuffer, size_t strBufferLen, bool fRecon, unsigned long & posPad )
-{
-
- LFA_Seek ( inFileRef, 3ULL, SEEK_SET ); // Point after the "ID3"
-
- // Get the tag info
- unsigned long dwOffset = 0;
- XMP_Uns8 v1 = 0, v2 = 0, flags = 0;
- unsigned long dwTagSize = 0;
- GetTagInfo ( inFileRef, v1, v2, flags, dwTagSize );
-
- unsigned long dwExtendedTag = SkipExtendedHeader ( inFileRef, v1, flags );
-
- LFA_Seek ( inFileRef, 0ULL, SEEK_SET );
- XMP_Assert ( strBufferLen >= k_dwTagHeaderSize );
- LFA_Read ( inFileRef, strBuffer, k_dwTagHeaderSize );
- dwOffset += k_dwTagHeaderSize;
-
- // Completely ignore the Extended Header
- if ( ((flags & flagExt) == flagExt) && (dwExtendedTag > 0) ) {
- strBuffer[5] = strBuffer[5] & 0xBF; // If the flag has been set, let's reset it
- LFA_Seek ( inFileRef, dwExtendedTag, SEEK_CUR ); // And let's seek up to after the extended header
- }
-
- // Enumerate through the frames
- XMP_Int64 posCur = 0ULL;
- posCur = GetFilePosition ( inFileRef );
- XMP_Int64 posEnd = posCur + dwTagSize;
-
- while ( posCur < posEnd ) {
-
- XMP_Assert ( k_dwTagHeaderSize == 10 );
- if ( (posEnd - posCur) < k_dwTagHeaderSize ) break; // Not enough room for a header, must be padding.
-
- char szFrameID[5] = {"xxxx"};
- unsigned long dwFrameSize = 0;
- XMP_Uns8 cflag1 = 0, cflag2 = 0;
-
- // Get the next frame
- if ( ! GetFrameInfo ( inFileRef, v1, szFrameID, cflag1, cflag2, dwFrameSize ) ) break;
-
- // Are we in a padding frame?
- if ( dwFrameSize == 0 ) break; // We just hit a padding frame
-
- bool fIgnore = false;
- bool knownID = (strcmp ( szFrameID, "TIT2" ) == 0) ||
- (strcmp ( szFrameID, "TYER" ) == 0) ||
- (strcmp ( szFrameID, "TDRV" ) == 0) ||
- (strcmp ( szFrameID, "TPE1" ) == 0) ||
- (strcmp ( szFrameID, "TALB" ) == 0) ||
- (strcmp ( szFrameID, "TCON" ) == 0) ||
- (strcmp ( szFrameID, "COMM" ) == 0) ||
- (strcmp ( szFrameID, "TRCK" ) == 0);
-
- // If a known frame, just ignore
- // Note: If recon is turned off, let's consider all known frames as unknown
- if ( knownID && fRecon ) {
-
- fIgnore = true;
-
- } else if ( strcmp ( szFrameID, "PRIV" ) == 0 ) {
-
- // Read the "PRIV" frame
- // <Header for "PRIV">
- // Short content descrip. <text string according to encoding> $00 (00)
- // The actual data <full text string according to encoding>
-
- // Get the PRIV descriptor
- char szXMPTag[4] = {"xxx"};
- if ( LFA_Read ( inFileRef, &szXMPTag, k_dwXMPLabelSize ) != 0 ) {
- // Is it a XMP "PRIV"
- if ( (szXMPTag[3] == 0) && (strcmp ( szXMPTag, "XMP" ) == 0) ) fIgnore = true;
- LFA_Seek ( inFileRef, -(long)k_dwXMPLabelSize, SEEK_CUR );
- }
-
- }
-
- if ( fIgnore ) {
- LFA_Seek ( inFileRef, dwFrameSize, SEEK_CUR );
- } else {
- // Unknown frame, let's copy it
- LFA_Seek ( inFileRef, -(long)k_dwFrameHeaderSize, SEEK_CUR );
- if ( (dwOffset > strBufferLen) || ((dwFrameSize + k_dwFrameHeaderSize) > (strBufferLen - dwOffset)) ) {
- XMP_Throw ( "Avoiding I/O buffer overflow", kXMPErr_InternalFailure );
- }
- LFA_Read ( inFileRef, (strBuffer + dwOffset), (dwFrameSize + k_dwFrameHeaderSize) );
- dwOffset += dwFrameSize+k_dwFrameHeaderSize;
- }
-
- posCur = GetFilePosition ( inFileRef );
-
- }
-
- posPad = dwOffset;
-
- return true;
-
-}
-
-// =================================================================================================
-
-bool FindID3Tag ( LFA_FileRef inFileRef, unsigned long & dwLen, XMP_Uns8 & bMajorVer )
-{
- // id3v2 tag:
- // ID3v2/file identifier "ID3"
- // ID3v2 version $04 00
- // ID3v2 flags %abcd0000
- // ID3v2 size 4 * %0xxxxxxx
-
- // Taking into account that the first Tag is the ID3 tag
- LFA_Seek ( inFileRef, 0ULL, SEEK_SET );
-
- // Read the tag name
- char szID[4] = {"xxx"};
- long bytesRead = LFA_Read ( inFileRef, szID, 3 );
- if ( bytesRead == 0 ) return false;
-
- // Check for "ID3"
- if ( strcmp ( szID, "ID3" ) != 0 ) return false;
-
- // Read the version, flag and size
- XMP_Uns8 v2 = 0, flags = 0;
- if ( ! GetTagInfo ( inFileRef, bMajorVer, v2, flags, dwLen ) ) return false;
-
- return true;
-
-}
-
-// =================================================================================================
-
-bool GetTagInfo ( LFA_FileRef inFileRef, XMP_Uns8 & v1, XMP_Uns8 & v2, XMP_Uns8 & flags, unsigned long & dwTagSize )
-{
-
- if ((LFA_Read(inFileRef, &v1, 1)) == 0) return false;
- if ((LFA_Read(inFileRef, &v2, 1)) == 0) return false;
- if ((LFA_Read(inFileRef, &flags, 1)) == 0) return false;
- if (!ReadSize(inFileRef, 4, dwTagSize)) return false; // Tag size is always using the size reading method.
-
- return true;
-
-}
-
-// =================================================================================================
-
-static bool FindXMPFrame ( LFA_FileRef inFileRef, XMP_Int64 & posXMP, XMP_Int64 & posPAD, unsigned long & dwExtendedTag, unsigned long & dwLen )
-{
- // Taking into account that the first Tag is the ID3 tag
- bool fReturn = false;
- dwExtendedTag = 0;
- posPAD = 0;
-
- LFA_Seek ( inFileRef, 0ULL, SEEK_SET );
-
- // Read the tag name
- char szID[4] = {"xxx"};
- long bytesRead = LFA_Read ( inFileRef, szID, 3 );
- if ( bytesRead == 0 ) return fReturn;
-
- // Check for "ID3"
- if ( strcmp ( szID, "ID3") != 0 ) return fReturn;
-
- // Read the version, flag and size
- XMP_Uns8 v1 = 0, v2 = 0, flags = 0;
- unsigned long dwTagSize = 0;
- if ( ! GetTagInfo ( inFileRef, v1, v2, flags, dwTagSize ) ) return fReturn;
- if ( dwTagSize == 0 ) return fReturn;
- if ( v1 > 4 ) return fReturn; // We don't support anything newer than id3v2 4.0
-
- // If there's an extended header, ignore it
- dwExtendedTag = SkipExtendedHeader(inFileRef, v1, flags);
- dwTagSize -= dwExtendedTag;
-
- // Enumerate through the frames
- XMP_Int64 posCur = 0ULL;
- posCur = GetFilePosition ( inFileRef );
- XMP_Int64 posEnd = posCur + dwTagSize;
-
- while ( posCur < posEnd ) {
-
- if ( (posEnd - posCur) < k_dwTagHeaderSize ) break; // Not enough room for a header, must be padding.
-
- char szFrameID[5] = {"xxxx"};
- unsigned long dwFrameSize = 0;
- XMP_Uns8 cflag1 = 0, cflag2 = 0;
-
- // Get the next frame
- if ( ! GetFrameInfo ( inFileRef, v1, szFrameID, cflag1, cflag2, dwFrameSize ) ) {
- // Set the file pointer to the XMP or the start
- LFA_Seek ( inFileRef, fReturn ? posXMP : 0ULL, SEEK_SET );
- break;
- }
-
- // Are we in a padding frame?
- if ( dwFrameSize == 0 ) {
-
- // We just hit a padding frame
- LFA_Seek ( inFileRef, -(long)k_dwFrameHeaderSize, SEEK_CUR );
- posPAD = GetFilePosition ( inFileRef );
-
- // Set the file pointer to the XMP or the start
- LFA_Seek ( inFileRef, fReturn ? posXMP : 0ULL, SEEK_SET );
- break;
-
- }
-
- // Is it a "PRIV"?
- if ( strcmp(szFrameID, "PRIV") != 0 ) {
-
- // Jump to the next frame
- LFA_Seek ( inFileRef, dwFrameSize, SEEK_CUR );
-
- } else {
-
- // Read the "PRIV" frame
- // <Header for "PRIV">
- // Short content descrip. <text string according to encoding> $00 (00)
- // The actual data <full text string according to encoding>
-
- unsigned long dwBytesRead = 0;
-
- // Get the PRIV descriptor
- char szXMPTag[4] = {"xxx"};
- if (LFA_Read(inFileRef, &szXMPTag, k_dwXMPLabelSize) == 0) return fReturn;
- dwBytesRead += k_dwXMPLabelSize;
-
- // Is it a XMP "PRIV"
- if ( (szXMPTag[3] == 0) && (strcmp ( szXMPTag, "XMP" ) == 0) ) {
- dwLen = dwFrameSize;
- LFA_Seek ( inFileRef, -(long)k_dwXMPLabelSize, SEEK_CUR );
- posXMP = GetFilePosition ( inFileRef );
- fReturn = true;
- dwBytesRead -= k_dwXMPLabelSize;
- }
-
- // Didn't find it, let skip the rest of the frame and continue
- LFA_Seek ( inFileRef, dwFrameSize - dwBytesRead, SEEK_CUR );
-
- }
-
- posCur = GetFilePosition ( inFileRef );
-
- }
-
- return fReturn;
-
-}
-
-// =================================================================================================
-
-// Returns the size of the extended header
-static unsigned long SkipExtendedHeader ( LFA_FileRef inFileRef, XMP_Uns8 bVersion, XMP_Uns8 flags )
-{
- if ( flags & flagExt ) {
-
- unsigned long dwExtSize = 0; // <-- This will include the size (full extended header size)
-
- if ( ReadSize ( inFileRef, bVersion, dwExtSize ) ) {
- if ( bVersion < 4 ) dwExtSize += 4; // v3 doesn't include the size, while v4 does.
- LFA_Seek ( inFileRef, (size_t)(dwExtSize - 4), SEEK_CUR );
- }
-
- return dwExtSize;
-
- }
-
- return 0;
-
-}
-
-// =================================================================================================
-
-static bool GetFrameInfo ( LFA_FileRef inFileRef, XMP_Uns8 bVersion, char * strFrameID, XMP_Uns8 & cflag1, XMP_Uns8 & cflag2, unsigned long & dwSize)
-{
- // Frame ID $xx xx xx xx (four characters)
- // Size 4 * %0xxxxxxx <<--- IMPORTANT NOTE: This is true only in v4.0 (v3.0 uses a XMP_Int32)
- // Flags $xx xx
-
- if ( strFrameID == 0 ) return false;
-
- if ( LFA_Read ( inFileRef, strFrameID, 4 ) == 0 ) return false;
- if ( ! ReadSize ( inFileRef, bVersion, dwSize ) ) return false;
- if ( LFA_Read ( inFileRef, &cflag1, 1 ) == 0 ) return false;
- if ( LFA_Read ( inFileRef, &cflag2, 1 ) == 0 ) return false;
-
- return true;
-
-}
-
-// =================================================================================================
-
-static bool ReadSize ( LFA_FileRef inFileRef, XMP_Uns8 bVersion, unsigned long & dwSize )
-{
- char s4 = 0, s3 = 0, s2 = 0, s1 = 0;
-
- if ( LFA_Read ( inFileRef, &s4, 1 ) == 0 ) return false;
- if ( LFA_Read ( inFileRef, &s3, 1 ) == 0 ) return false;
- if ( LFA_Read ( inFileRef, &s2, 1 ) == 0 ) return false;
- if ( LFA_Read ( inFileRef, &s1, 1 ) == 0 ) return false;
-
- if ( bVersion > 3 ) {
- dwSize = ((s4 & 0x7f) << 21) | ((s3 & 0x7f) << 14) | ((s2 & 0x7f) << 7) | (s1 & 0x7f);
- } else {
- dwSize = ((s4 << 24) | (s3 << 16) | (s2 << 8) | s1);
- }
-
- return true;
-
-}
-
-// =================================================================================================
-
-static unsigned long CalculateSize ( XMP_Uns8 bVersion, unsigned long dwSizeIn )
-{
- unsigned long dwReturn;
-
- if ( bVersion <= 3 ) {
- dwReturn = dwSizeIn;
- } else {
- dwReturn = dwSizeIn & 0x7f; // Expand to 7 bits per byte.
- dwSizeIn = dwSizeIn >> 7;
- dwReturn |= ((dwSizeIn & 0x7f) << 8);
- dwSizeIn = dwSizeIn >> 7;
- dwReturn |= ((dwSizeIn & 0x7f) << 16);
- dwSizeIn = dwSizeIn >> 7;
- dwReturn |= ((dwSizeIn & 0x7f) << 24);
- }
-
- return dwReturn;
-
-}
-
-} // namespace ID3_Support
-
-// =================================================================================================
-
-#endif // XMP_UNIXBuild
diff --git a/source/XMPFiles/FormatSupport/ID3_Support.hpp b/source/XMPFiles/FormatSupport/ID3_Support.hpp
index 975ccf4..e243d24 100644
--- a/source/XMPFiles/FormatSupport/ID3_Support.hpp
+++ b/source/XMPFiles/FormatSupport/ID3_Support.hpp
@@ -3,7 +3,7 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2007 Adobe Systems Incorporated
+// Copyright 2008 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -11,31 +11,774 @@
// =================================================================================================
#include "XMP_Environment.h" // ! This must be the first include.
-#if ! XMP_UNIXBuild // Closes at very bottom. Disabled on UNIX until legacy-as-local is fixed.
-
-#include <vector>
-
#include "XMP_Const.h"
#include "XMPFiles_Impl.hpp"
+#include "UnicodeConversions.hpp"
+#include "Reconcile_Impl.hpp"
+#include <vector>
+
+#if XMP_WinBuild
+ #define stricmp _stricmp
+#else
+ static int stricmp ( const char * left, const char * right ) // Case insensitive ASCII compare.
+ {
+ char chL = *left; // ! Allow for 0 passes in the loop (one string is empty).
+ char chR = *right; // ! Return -1 for stricmp ( "a", "Z" ).
-#define TAG_MAX_SIZE 5024
+ for ( ; (*left != 0) && (*right != 0); ++left, ++right ) {
+ chL = *left;
+ chR = *right;
+ if ( chL == chR ) continue;
+ if ( ('A' <= chL) && (chL <= 'Z') ) chL |= 0x20;
+ if ( ('A' <= chR) && (chR <= 'Z') ) chR |= 0x20;
+ if ( chL != chR ) break;
+ }
+
+ if ( chL == chR ) return 0;
+ if ( chL < chR ) return -1;
+ return 1;
+ }
+#endif
+
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
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
+ };
- bool GetMetaData ( LFA_FileRef inFileRef, char* buffer, unsigned long* pBufferSize, XMP_Int64* fileOffset );
- bool SetMetaData ( LFA_FileRef inFileRef, char * buffer, unsigned long bufferSize,
- char * strReconciliatedFrames, unsigned long dwReconciliatedFramesSize, bool fRecon );
+ //////////////////////////////////////////////////////////
+ // ID3 specific helper routines
+ inline XMP_Int32 synchToInt32(XMP_Uns32 rawDataBE) // see part 6.2 of spec
+ {
+ XMP_Validate( 0 == (rawDataBE & 0x80808080),"input not synchsafe", kXMPErr_InternalFailure );
+ XMP_Int32 r =
+ ( rawDataBE & 0x0000007F )
+ + ( rawDataBE >> 1 & 0x00003F80 )
+ + ( rawDataBE >> 2 & 0x001FC000 )
+ + ( rawDataBE >> 3 & 0x0FE00000 );
+ return r;
+ }
- bool GetTagInfo ( LFA_FileRef inFileRef, XMP_Uns8 & v1, XMP_Uns8 & v2, XMP_Uns8 & flags, unsigned long & dwTagSize );
- bool FindID3Tag ( LFA_FileRef inFileRef, unsigned long & dwLen, XMP_Uns8 & bMajorVer );
+ inline XMP_Uns32 int32ToSynch(XMP_Int32 value)
+ {
+ XMP_Validate( 0 <= 0x0FFFFFFF, "value too big", kXMPErr_InternalFailure );
+ XMP_Uns32 r =
+ ( (value & 0x0000007F) << 0 )
+ + ( (value & 0x00003F80) << 1 )
+ + ( (value & 0x001FC000) << 2 )
+ + ( (value & 0x0FE00000) << 3 );
+ return r;
+ }
- bool AddXMPTagToID3Buffer ( char * strCur, unsigned long * pdwCurOffset, unsigned long dwMaxSize,
- XMP_Uns8 bVersion, char * strFrameName, const char * strXMPTag, unsigned long dwXMPLength );
+ //////////////////////////////////////////////////////////
+ // data structures
+ class ID3Header
+ {
+ public:
+ const static XMP_Uns16 o_id = 0;
+ const static XMP_Uns16 o_version_major = 3;
+ const static XMP_Uns16 o_version_minor = 4;
+ const static XMP_Uns16 o_flags = 5;
+ const static XMP_Uns16 o_size = 6;
- bool GetFrameData ( LFA_FileRef inFileRef, char * strFrame, char * buffer, unsigned long & dwBufferSize );
+ const static int FIXED_SIZE = 10;
+ char fields[FIXED_SIZE];
-} // namespace ID3_Support
+ bool read(LFA_FileRef file)
+ {
+ LFA_Read( file, fields, FIXED_SIZE, true );
+
+ if ( !CheckBytes( &fields[ID3Header::o_id], "ID3", 3 ) )
+ {
+ // chuck in default contents:
+ const static char defaultHeader[FIXED_SIZE] = { 'I', 'D', '3', 3, 0, 0, 0, 0, 0, 0 };
+ memcpy( this->fields, defaultHeader, FIXED_SIZE); // NBA: implicitly protected by FIXED_SIZE design.
+
+ return false; // no header found (o.k.) thus stick with new, default header constructed above
+ }
+
+ XMP_Uns8 major = fields[o_version_major];
+ XMP_Uns8 minor = fields[o_version_minor];
+ XMP_Validate( major==3 || major==4, "invalid ID3 major version", kXMPErr_BadFileFormat );
+ XMP_Validate( minor != 0xFF, "invalid ID3 minor version", kXMPErr_BadFileFormat );
+
+ return true;
+ }
+
+ void write(LFA_FileRef file, XMP_Int64 tagSize)
+ {
+ XMP_Assert( tagSize < 0x10000000 ); // 256 MB limit due to synching.
+ XMP_Assert( tagSize > 0x0 ); // 256 MB limit due to synching.
+ PutUns32BE( ( int32ToSynch ( (XMP_Uns32)tagSize - 10 )), &this->fields[ID3Header::o_size] );
+
+ LFA_Write( file, fields, FIXED_SIZE );
+ }
+
+ ~ID3Header()
+ {
+ //nothing
+ };
+ }; // ID3Header
+
+
+ class ID3v2Frame
+ {
+ public:
+ // fixed
+ const static XMP_Uns16 o_id = 0;
+ const static XMP_Uns16 o_size = 4; // size after unsync, excludes frame header.
+ const static XMP_Uns16 o_flags = 8;
+
+ const static int FIXED_SIZE = 10;
+ char fields[FIXED_SIZE];
+
+ XMP_Uns32 id;
+ XMP_Uns16 flags;
+
+ bool active; //default: true. flag is lowered, if another frame with replaces this one as "last meaningful frame of its kind"
+ bool changed; //default: false. flag is raised, if setString() is used
+
+ // variable-size frame content
+ char* content;
+ XMP_Int32 contentSize; // size of variable content, right as its stored in o_size (total size - 10)
+
+ /**
+ * a mere empty hull for reading in frames.
+ */
+ ID3v2Frame():id(0),content(0),contentSize(0),flags(0),active(true),changed(false)
+ {
+ memset(fields, 0, FIXED_SIZE);
+ }
+
+ /**
+ * frame constructor
+ */
+ ID3v2Frame( XMP_Uns32 id ):id(0),content(0),contentSize(0),flags(0),active(true),changed(false)
+ {
+ memset(fields, 0, FIXED_SIZE);
+
+ this->id = id;
+ PutUns32BE( id, &fields[o_id]);
+ this->flags = 0x0000;
+ PutUns16BE( flags, &fields[o_flags]);
+ }
+
+ /**
+ * copy constructor:
+ */
+ ID3v2Frame( const ID3v2Frame& orig)
+ {
+ XMP_Throw("not implemented",kXMPErr_InternalFailure);
+ }
+
+ /**
+ * sets a new Content (aka property)
+ */
+ void setFrameValue( const std::string& rawvalue, bool needDescriptor = false , bool utf16le = false, bool isXMPPRIVFrame = false, bool needEncodingByte = true )
+ {
+ // the thing to insert
+ std::string value;
+ value.erase();
+
+ if ( isXMPPRIVFrame )
+ {
+ // this flag must be used exclusive:
+ XMP_Assert( ! needDescriptor );
+ XMP_Assert( ! utf16le );
+
+ value.append( "XMP\0", 4 );
+ value.append( rawvalue );
+ value.append( "\0", 1 ); // final zero byte
+ }
+ else // not an XMP Priv frame
+ {
+ if (needEncodingByte) // don't add encoding byte for example for WCOP
+ {
+ if ( utf16le )
+ value.append( "\x1", 1 ); // 'unicode encoding' (must be UTF-16, be or le)
+ else
+ value.append( "\x0", 1 );
+ }
+
+ if ( needDescriptor ) // language descriptor
+ value.append( "eng", 3 ); // language, empty content description
+ if ( utf16le )
+ {
+ if ( needDescriptor )
+ value.append( "\xFF\xFE\0\0", 4 ); // BOM, 16-bit zero (empty descriptor)
+
+ value.append( "\xFF\xFE", 2 );
+ std::string utf16str;
+ ToUTF16( (XMP_Uns8*) rawvalue.c_str(), rawvalue.size(), &utf16str, false );
+ value.append( utf16str );
+ value.append( "\0\0", 2 );
+ }
+ else // write as plain Latin-1
+ {
+ std::string convertedValue; // just precaution (mostly used for plain ascii like numbers, etc)
+ convertedValue.erase();
+ ReconcileUtils::UTF8ToLatin1( rawvalue.c_str(), rawvalue.size(), &convertedValue );
+
+ if ( needDescriptor )
+ value.append( "\0", 1 ); // BOM, 16-bit zero (empty descriptor)
+ value.append( convertedValue );
+ value.append( "\0", 1 ); // final zero byte
+ }
+ } // of "not an XMP Priv frame"
+
+ this->changed = true;
+ this->release();
+
+ XMP_StringPtr valueCStr = value.c_str();
+ this->contentSize = (XMP_Int32) value.size();
+ XMP_Validate( this->contentSize < 0xA00000, "XMP Property exceeds 20MB in size", kXMPErr_InternalFailure );
+ content = new char[ this->contentSize ];
+ memcpy( content , valueCStr , this->contentSize );
+ }
+
+ void write(LFA_FileRef file, XMP_Uns8 majorVersion)
+ {
+ // write back size field:
+ if ( majorVersion < 4 )
+ PutUns32BE( contentSize, &fields[o_size] );
+ else
+ PutUns32BE( int32ToSynch( contentSize ), &fields[o_size] );
+
+ // write out fixed fields
+ LFA_Write( file, this->fields, FIXED_SIZE);
+ // write out contents
+ LFA_Write( file, this->content, contentSize );
+ }
+
+ /**
+ * majorVersion must be 3 or 4
+ * @returns total size of frame
+ */
+ XMP_Int64 read(LFA_FileRef file, XMP_Uns8 majorVersion )
+ {
+ XMP_Assert( majorVersion == 3 || majorVersion == 4 );
+ this->release(); // ensures/allows reuse of 'curFrame'
+
+ XMP_Int64 start = LFA_Tell( file );
+ LFA_Read( file, fields, FIXED_SIZE, true );
+
+ id = GetUns32BE( &fields[o_id] );
+
+ //only padding to come?
+ if (id == 0x00000000)
+ {
+ LFA_Seek( file, -10, SEEK_CUR ); //rewind 10 byte
+ return 0; // zero signals, not a valid frame
+ }
+ //verify id to be valid (4x A-Z and 0-9)
+ XMP_Validate(
+ (( fields[0]>0x40 && fields[0]<0x5B) || ( fields[0]>0x2F && fields[0]<0x3A)) && //A-Z,0-9
+ (( fields[1]>0x40 && fields[1]<0x5B) || ( fields[1]>0x2F && fields[1]<0x3A)) &&
+ (( fields[2]>0x40 && fields[2]<0x5B) || ( fields[2]>0x2F && fields[2]<0x3A)) &&
+ (( fields[3]>0x40 && fields[3]<0x5B) || ( fields[3]>0x2F && fields[3]<0x3A))
+ ,"invalid Frame ID", kXMPErr_BadFileFormat);
+
+ flags = GetUns16BE( &fields[o_flags] );
+ XMP_Validate( 0==(flags & 0xEE ),"invalid lower bits in frame flags",kXMPErr_BadFileFormat );
+
+ //*** flag handling, spec :429ff aka line 431ff (i.e. Frame should be discarded)
+ // compression and all of that..., unsynchronisation
+ contentSize = ( majorVersion < 4) ?
+ GetUns32BE( &fields[o_size] )
+ : synchToInt32(GetUns32BE( &fields[o_size] ));
+
+ XMP_Validate( contentSize >= 0, "negative frame size", kXMPErr_BadFileFormat );
+ // >20MB in a single frame?, assume broken file
+ XMP_Validate( contentSize < 0xA00000, "single frame exceeds 20MB", kXMPErr_BadFileFormat );
-#endif // XMP_UNIXBuild
+ content = new char[ contentSize ];
+
+ LFA_Read( file, content, contentSize, true );
+ XMP_Assert( (LFA_Tell( file ) - start) == FIXED_SIZE + contentSize ); //trivial
+ return LFA_Tell( file ) - start;
+ }
+
+ /**
+ * two types of COMM frames should be preserved but otherwise ignored
+ * * a 6-field long header just having
+ * encoding(1 byte),lang(3 bytes) and 0x00 31 (no descriptor, "1" as content")
+ * perhaps only used to indicate client language
+ * * COMM frames whose description begins with engiTun, these are iTunes flags
+ *
+ * returns true: job done as expted
+ * false: do not use this frame, but preserve (i.e. iTunes marker COMM frame)
+ */
+ bool advancePastCOMMDescriptor(XMP_Int32& pos)
+ {
+ if ( (contentSize-pos) <= 3 )
+ return false; // silent error, no room left behing language tag
+
+ if ( !CheckBytes( &content[pos], "eng", 3 ) )
+ return false; // not an error, but leave all non-eng tags alone...
+
+ pos += 3; // skip lang tag
+ if ( pos >= contentSize )
+ return false; // silent error
+
+ // skip past descriptor:
+ while ( pos < contentSize )
+ if ( content[pos++] == 0x00 )
+ break;
+ // skip yet-another double zero if applicable
+ if ( (pos<contentSize) && (content[pos] == 0x00) )
+ pos++;
+
+ // check for "1" language indicator comment
+ if (pos == 5)
+ if ( contentSize == 6)
+ if ( GetUns16BE(&content[4]) == 0x0031 )
+ return false;
+
+ // check for iTunes tag-comment
+ if ( pos > 4 ) //there's a content descriptor ( 1 + 3 + zeroTerminator)
+ {
+ std::string descriptor = std::string(&content[4],pos-1);
+ if ( 0 == descriptor.substr(0,4).compare( "iTun" )) // begins with engiTun ?
+ return false; // leave alone, then
+ }
+
+ return true; //o.k., descriptor skipped, time for the real thing.
+ }
+
+ /*
+ * returns the frame content as a proper UTF8 string
+ * * w/o the initial encoding byte
+ * * dealing with BOM's
+ *
+ * @returns: by value: character string with the value
+ * as return value: false if frame is "not of intereset" despite a generally
+ * "interesting" frame ID, these are
+ * * iTunes-'marker' COMM frame
+ */
+ bool getFrameValue( XMP_Uns8 majorVersion, XMP_Uns32 frameID, std::string* utf8string )
+ {
+ XMP_Assert( contentSize >= 0 );
+ XMP_Assert( contentSize < 0xA00000 ); // more than 20 MB per Propety would be very odd...
+ XMP_Assert( content != 0 );
+
+ if ( contentSize == 0)
+ {
+ utf8string->erase();
+ return true; // ...it is "of interest", even if empty contents.
+ }
+
+ XMP_Int32 pos = 0; // going through input string
+ XMP_Uns8 encByte = 0;
+ // WCOP does not have an encoding byte
+ // => for all others: use [0] as EncByte, advance pos
+ if ( frameID != 0x57434F50 )
+ {
+ encByte = content[0];
+ pos++;
+ }
+
+ // mode specific forks (so far only COMM)
+ bool commMode = (
+ ( frameID == 0x434F4D4D ) || // COMM
+ ( frameID == 0x55534C54 ) // USLT
+ );
+
+ switch (encByte)
+ {
+ case 0: //ISO-8859-1, 0-terminated
+ {
+ if (commMode)
+ if (! advancePastCOMMDescriptor( pos )) // do and check result
+ return false; // not a frame of interest!
+
+ char* localPtr = &content[pos];
+ size_t localLen = contentSize - pos;
+ ReconcileUtils::Latin1ToUTF8 ( localPtr, localLen, utf8string );
+ }
+ break;
+ case 1: // Unicode, v2.4: UTF-16 (undetermined Endianess), with BOM, terminated 0x00 00
+ case 2: // UTF-16BE without BOM, terminated 0x00 00
+ {
+ std::string tmp(this->content, this->contentSize);
+
+ if (commMode)
+ if (! advancePastCOMMDescriptor( pos )) // do and check result
+ return false; // not a frame of interest!
+
+ bool bigEndian = true; // assume for now (if no BOM follows)
+ if ( GetUns16BE(&content[pos]) == 0xFEFF )
+ {
+ pos += 2;
+ bigEndian = true;
+ }
+ else if ( GetUns16BE(&content[pos]) == 0xFFFE )
+ {
+ pos += 2;
+ bigEndian = false;
+ }
+
+ FromUTF16( (UTF16Unit*)&content[pos], (contentSize - pos) / 2, utf8string, bigEndian );
+ }
+ break;
+ case 3: // UTF-8 unicode, terminated \0
+ // swallow any BOM, just in case
+ if ( (GetUns32BE(&content[pos]) & 0xFFFFFF00 ) == 0xEFBBBF00 )
+ pos += 3;
+
+ if (commMode)
+ if (! advancePastCOMMDescriptor( pos )) // do and check result
+ return false; // not a frame of interest!
+
+ utf8string->assign( &content[pos], contentSize - pos );
+ break;
+ default:
+ XMP_Throw ( "unknown text encoding", kXMPErr_BadFileFormat); //COULDDO assume latin-1 or utf-8 as best-effort
+ break;
+ }
+ return true;
+ }
+
+ ~ID3v2Frame()
+ {
+ this->release();
+ }
+
+ void release()
+ {
+ if ( content )
+ delete content;
+ content = 0;
+ contentSize = 0;
+ }
+ }; // ID3Header
+
+
+
+ class ID3v1Tag
+ {
+ public:
+ // fixed
+ const static XMP_Uns16 o_tag = 0; // must be "TAG"
+ const static XMP_Uns16 o_title = 3;
+ const static XMP_Uns16 o_artist = 33;
+ const static XMP_Uns16 o_album = 63;
+ const static XMP_Uns16 o_year = 93;
+ const static XMP_Uns16 o_comment = 97;
+ const static XMP_Uns16 o_genre = 127; // last by: index, or 255
+
+ // optional (ID3v1.1) track number)
+ const static XMP_Uns16 o_zero = 125; //must be zero, for trackNo to be valid
+ const static XMP_Uns16 o_trackNo = 126; //trackNo
+
+ const static int FIXED_SIZE = 128;
+
+ /**
+ * @returns returns true, if ID3v1 (or v1.1) exists, otherwise false.
+ // sets XMP properties en route
+ */
+ bool read( LFA_FileRef file, SXMPMeta* meta )
+ {
+ if ( LFA_Measure( file ) <= 128 ) // ensure sufficient room
+ return false;
+ LFA_Seek( file, -128, SEEK_END);
+
+ XMP_Uns32 tagID = LFA_ReadInt32_BE( file );
+ tagID = tagID & 0xFFFFFF00; // wipe 4th byte
+ if ( tagID != 0x54414700 )
+ return false; // must be "TAG"
+ LFA_Seek( file, -1, SEEK_CUR ); //rewind 1
+
+ /////////////////////////////////////////////////////////
+ XMP_Uns8 buffer[31]; // nothing is bigger here, than 30 bytes (offsets [0]-[29])
+ buffer[30]=0; // wipe last byte
+ std::string utf8string;
+
+ // title //////////////////////////////////////////////////////
+ LFA_Read( file, buffer, 30, true );
+ std::string title( (char*) buffer ); //security: guaranteed to 0-terminate after 30 bytes
+ if ( ! title.empty() )
+ {
+ ReconcileUtils::Latin1ToUTF8 ( title.c_str(), title.size(), &utf8string );
+ meta->SetLocalizedText( kXMP_NS_DC, "title", "" , "x-default" , utf8string.c_str() );
+ }
+ // artist //////////////////////////////////////////////////////
+ LFA_Read( file, buffer, 30, true );
+ std::string artist( (char*) buffer );
+ if ( ! artist.empty() )
+ {
+ ReconcileUtils::Latin1ToUTF8 ( artist.c_str(), artist.size(), &utf8string );
+ meta->SetProperty( kXMP_NS_DM, "artist", utf8string.c_str() );
+ }
+ // album //////////////////////////////////////////////////////
+ LFA_Read( file, buffer, 30, true );
+ std::string album( (char*) buffer );
+ if ( ! album.empty() )
+ {
+ ReconcileUtils::Latin1ToUTF8 ( album.c_str(), album.size(), &utf8string );
+ meta->SetProperty( kXMP_NS_DM, "album", utf8string.c_str() );
+ }
+ // year //////////////////////////////////////////////////////
+ LFA_Read( file, buffer, 4, true );
+ buffer[4]=0; // ensure 0-term
+ std::string year( (char*) buffer );
+ if ( ! year.empty() )
+ { // should be moot for a year, but let's be safe:
+ ReconcileUtils::Latin1ToUTF8 ( year.c_str(), year.size(), &utf8string );
+ meta->SetProperty( kXMP_NS_XMP, "CreateDate", utf8string.c_str() );
+ }
+ // comment //////////////////////////////////////////////////////
+ LFA_Read( file, buffer, 30, true );
+ std::string comment( (char*) buffer );
+ if ( ! comment.empty() )
+ {
+ ReconcileUtils::Latin1ToUTF8 ( comment.c_str(), comment.size(), &utf8string );
+ meta->SetProperty( kXMP_NS_DM, "logComment", utf8string.c_str() );
+ }
+ // trackNo (ID3v1.1) /////////////////////////////////////////////
+ if ( buffer[28] == 0 )
+ {
+ XMP_Uns8 trackNo = buffer[29];
+ if ( trackNo > 0 ) // 0 := unset
+ {
+ std::string trackStr;
+ SXMPUtils::ConvertFromInt( trackNo, 0, &trackStr );
+ meta->SetProperty( kXMP_NS_DM, "trackNumber", trackStr.c_str() );
+ }
+ }
+ // genre //////////////////////////////////////////////////////
+ XMP_Uns8 genreNo = LFA_ReadUns8( file );
+ if ( (genreNo > 0) && (genreNo < 127) ) // 0 := unset, 127 := 'unknown'
+ {
+ meta->SetProperty( kXMP_NS_DM, "genre", Genres[genreNo] );
+ }
+
+ return true; // ID3Tag found
+ }
+
+ void write( LFA_FileRef file, SXMPMeta* meta )
+ {
+ // move in position (extension if applicable happens by caller)
+ std::string zeros( 128, '\0' );
+ std::string utf8, latin1;
+
+ LFA_Seek( file, -128, SEEK_END);
+ LFA_Write( file, zeros.data(), 128 );
+
+ // TAG /////////////////////////////////////////////
+ LFA_Seek( file, -128, SEEK_END);
+ LFA_WriteUns8( file, 'T' );
+ LFA_WriteUns8( file, 'A' );
+ LFA_WriteUns8( file, 'G' );
+
+ // title //////////////////////////////////////////////////////
+ if ( meta->GetLocalizedText( kXMP_NS_DC, "title", "", "x-default", 0, &utf8, kXMP_NoOptions ))
+ {
+ LFA_Seek( file, -128 + 3, SEEK_END);
+ ReconcileUtils::UTF8ToLatin1( utf8.c_str(), utf8.size(), &latin1 );
+ LFA_Write( file, latin1.c_str(), MIN( 30, (XMP_Int32)latin1.size() ) );
+ }
+ // artist //////////////////////////////////////////////////////
+ if ( meta->GetProperty( kXMP_NS_DM, "artist", &utf8, kXMP_NoOptions ))
+ {
+ LFA_Seek( file, -128 + 33, SEEK_END);
+ ReconcileUtils::UTF8ToLatin1( utf8.c_str(), utf8.size(), &latin1 );
+ LFA_Write( file, latin1.c_str(), MIN( 30, (XMP_Int32)latin1.size() ) );
+ }
+ // album //////////////////////////////////////////////////////
+ if ( meta->GetProperty( kXMP_NS_DM, "album", &utf8, kXMP_NoOptions ))
+ {
+ LFA_Seek( file, -128 + 63, SEEK_END);
+ ReconcileUtils::UTF8ToLatin1( utf8.c_str(), utf8.size(), &latin1 );
+ LFA_Write( file, latin1.c_str(), MIN( 30, (XMP_Int32)latin1.size() ) );
+ }
+ // year //////////////////////////////////////////////////////
+ if ( meta->GetProperty( kXMP_NS_XMP, "CreateDate", &utf8, kXMP_NoOptions ))
+ {
+ XMP_DateTime dateTime;
+ SXMPUtils::ConvertToDate( utf8, &dateTime );
+ if ( dateTime.hasDate )
+ {
+ SXMPUtils::ConvertFromInt ( dateTime.year, "", &latin1 );
+ LFA_Seek ( file, -128 + 93, SEEK_END );
+ LFA_Write ( file, latin1.c_str(), MIN ( 4, (XMP_Int32)latin1.size() ) );
+ }
+ }
+ // comment (write 30 bytes first, see truncation later) ////////////
+ if ( meta->GetProperty( kXMP_NS_DM, "logComment", &utf8, kXMP_NoOptions ))
+ {
+ LFA_Seek ( file, -128 + 97, SEEK_END );
+ ReconcileUtils::UTF8ToLatin1 ( utf8.c_str(), utf8.size(), &latin1 );
+ LFA_Write ( file, latin1.c_str(), MIN ( 30, (XMP_Int32)latin1.size() ) );
+ }
+ // genre ////////////////////////////////////////////////////////////////
+ if ( meta->GetProperty( kXMP_NS_DM, "genre", &utf8, kXMP_NoOptions ))
+ {
+ XMP_Uns8 genreNo = 0;
+
+ // try to find (case insensitive) match:
+ 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;
+ } // if
+ } // for
+
+ LFA_Seek( file, -128 + 127, SEEK_END);
+ LFA_WriteUns8( file, genreNo );
+ }
+
+ // trackNo ////////////////////////////////////////////////////////////
+ if ( meta->GetProperty( kXMP_NS_DM, "trackNumber", &utf8, kXMP_NoOptions ))
+ {
+ XMP_Uns8 trackNo = 0;
+ try
+ {
+ trackNo = (XMP_Uns8) SXMPUtils::ConvertToInt( utf8.c_str() );
+
+ LFA_Seek( file, -128 + 125, SEEK_END);
+ LFA_WriteUns8( file , 0 ); // ID3v1.1 extension
+ LFA_WriteUns8( file, trackNo );
+ }
+ catch ( XMP_Error& )
+ {
+ // forgive, just don't set this one.
+ }
+ }
+ }
+
+ }; // ID3v1Tag
+
+} // namespace ID3_Support
#endif // __ID3_Support_hpp__
diff --git a/source/XMPFiles/FormatSupport/IPTC_Support.cpp b/source/XMPFiles/FormatSupport/IPTC_Support.cpp
index 48e0309..b1514dd 100644
--- a/source/XMPFiles/FormatSupport/IPTC_Support.cpp
+++ b/source/XMPFiles/FormatSupport/IPTC_Support.cpp
@@ -1,6 +1,6 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2006-2008 Adobe Systems Incorporated
+// Copyright 2006 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -17,6 +17,11 @@
///
// =================================================================================================
+enum { kUTF8_IncomingMode = 0, kUTF8_LosslessMode = 1, kUTF8_AlwaysMode = 2 };
+#ifndef kUTF8_Mode
+ #define kUTF8_Mode kUTF8_AlwaysMode
+#endif
+
const DataSetCharacteristics kKnownDataSets[] =
{ { kIPTC_ObjectType, kIPTC_UnmappedText, 67, "", "" }, // Not mapped to XMP.
{ kIPTC_IntellectualGenre, kIPTC_MapSpecial, 68, kXMP_NS_IPTCCore, "IntellectualGenre" }, // Only the name part is in the XMP.
@@ -40,14 +45,14 @@ const DataSetCharacteristics kKnownDataSets[] =
{ kIPTC_RefService, kIPTC_UnmappedText, 10, "", "" }, // Not mapped to XMP. ! Interleave 2:45, 2:47, 2:50!
{ kIPTC_RefDate, kIPTC_UnmappedText, 8, "", "" }, // Not mapped to XMP. ! Interleave 2:45, 2:47, 2:50!
{ kIPTC_RefNumber, kIPTC_UnmappedText, 8, "", "" }, // Not mapped to XMP. ! Interleave 2:45, 2:47, 2:50!
- { kIPTC_DateCreated, kIPTC_MapSpecial, 8, kXMP_NS_Photoshop, "DateCreated" }, // Combined with 2:60, TimeCreated.
- { kIPTC_TimeCreated, kIPTC_MapSpecial, 11, "", "" }, // Combined with 2:55, DateCreated.
- { kIPTC_DigitalCreateDate, kIPTC_UnmappedText, 8, "", "" }, // Not mapped to XMP.
- { kIPTC_DigitalCreateTime, kIPTC_UnmappedText, 11, "", "" }, // Not mapped to XMP.
+ { kIPTC_DateCreated, kIPTC_MapSpecial, 8, kXMP_NS_Photoshop, "DateCreated" }, // ! Reformatted date. Combined with 2:60, TimeCreated.
+ { kIPTC_TimeCreated, kIPTC_UnmappedText, 11, "", "" }, // ! Combined with 2:55, DateCreated.
+ { kIPTC_DigitalCreateDate, kIPTC_Map3Way, 8, "", "" }, // ! 3 way Exif-IPTC-XMP date/time set. Combined with 2:63, DigitalCreateTime.
+ { kIPTC_DigitalCreateTime, kIPTC_UnmappedText, 11, "", "" }, // ! Combined with 2:62, DigitalCreateDate.
{ kIPTC_OriginProgram, kIPTC_UnmappedText, 32, "", "" }, // Not mapped to XMP.
{ kIPTC_ProgramVersion, kIPTC_UnmappedText, 10, "", "" }, // Not mapped to XMP.
{ kIPTC_ObjectCycle, kIPTC_UnmappedText, 1, "", "" }, // Not mapped to XMP.
- { kIPTC_Creator, kIPTC_MapSimple, 32, kXMP_NS_Photoshop, "Author" }, // ! Aliased to dc:creator[1].
+ { kIPTC_Creator, kIPTC_Map3Way, 32, "", "" }, // ! In the 3 way Exif-IPTC-XMP set.
{ kIPTC_CreatorJobtitle, kIPTC_MapSimple, 32, kXMP_NS_Photoshop, "AuthorsPosition" },
{ kIPTC_City, kIPTC_MapSimple, 32, kXMP_NS_Photoshop, "City" },
{ kIPTC_Location, kIPTC_MapSimple, 32, kXMP_NS_IPTCCore, "Location" },
@@ -58,9 +63,9 @@ const DataSetCharacteristics kKnownDataSets[] =
{ kIPTC_Headline, kIPTC_MapSimple, 256, kXMP_NS_Photoshop, "Headline" },
{ kIPTC_Provider, kIPTC_MapSimple, 32, kXMP_NS_Photoshop, "Credit" },
{ kIPTC_Source, kIPTC_MapSimple, 32, kXMP_NS_Photoshop, "Source" },
- { kIPTC_CopyrightNotice, kIPTC_MapLangAlt, 128, kXMP_NS_DC, "rights" },
+ { kIPTC_CopyrightNotice, kIPTC_Map3Way, 128, "", "" }, // ! In the 3 way Exif-IPTC-XMP set.
{ kIPTC_Contact, kIPTC_UnmappedText, 128, "", "" }, // Not mapped to XMP.
- { kIPTC_Description, kIPTC_MapLangAlt, 2000, kXMP_NS_DC, "description" },
+ { kIPTC_Description, kIPTC_Map3Way, 2000, "", "" }, // ! In the 3 way Exif-IPTC-XMP set.
{ kIPTC_DescriptionWriter, kIPTC_MapSimple, 32, kXMP_NS_Photoshop, "CaptionWriter" },
{ kIPTC_RasterizedCaption, kIPTC_UnmappedBin, 7360, "", "" }, // Not mapped to XMP. ! Binary data!
{ kIPTC_ImageType, kIPTC_UnmappedText, 2, "", "" }, // Not mapped to XMP.
@@ -72,8 +77,8 @@ const DataSetCharacteristics kKnownDataSets[] =
{ kIPTC_AudioDuration, kIPTC_UnmappedText, 6, "", "" }, // Not mapped to XMP.
{ kIPTC_AudioOutcue, kIPTC_UnmappedText, 64, "", "" }, // Not mapped to XMP.
{ kIPTC_PreviewFormat, kIPTC_UnmappedBin, 2, "", "" }, // Not mapped to XMP. ! Binary data!
- { kIPTC_PreviewFormatVers, kIPTC_UnmappedText, 2, "", "" }, // Not mapped to XMP. ! Binary data!
- { kIPTC_PreviewData, kIPTC_UnmappedText, 256000, "", "" }, // Not mapped to XMP. ! Binary data!
+ { kIPTC_PreviewFormatVers, kIPTC_UnmappedBin, 2, "", "" }, // Not mapped to XMP. ! Binary data!
+ { kIPTC_PreviewData, kIPTC_UnmappedBin, 256000, "", "" }, // Not mapped to XMP. ! Binary data!
{ 255, kIPTC_MapSpecial, 0, 0, 0 } }; // ! Must be last as a sentinel.
// A combination of the IPTC "Subject Reference System Guidelines" and IIMv4.1 Appendix G.
@@ -142,7 +147,7 @@ static const DataSetCharacteristics* FindKnownDataSet ( XMP_Uns8 id )
// IPTC_Manager::ParseMemoryDataSets
// =================================
//
-// Parse the legacy IIM block, keeping information about all 2:* DataSets and size of other records.
+// Parse the legacy IIM block. Offsets and lengths are kept for any 1:90 DataSet and all of Record 2.
void IPTC_Manager::ParseMemoryDataSets ( const void* data, XMP_Uns32 length, bool copyData /* = true */ )
{
@@ -188,7 +193,8 @@ void IPTC_Manager::ParseMemoryDataSets ( const void* data, XMP_Uns32 length, boo
this->utf8Encoding = false;
- bool foundRec2 = false;
+ bool found190 = false;
+ bool found2xx = false;
for ( ; iptcPtr <= iptcLimit; iptcPtr += dsLen ) {
@@ -217,25 +223,28 @@ void IPTC_Manager::ParseMemoryDataSets ( const void* data, XMP_Uns32 length, boo
if ( recNum == 0 ) continue; // Should not be a record 0. Throw instead?
if ( recNum == 1 ) {
- if ( (dsNum == 90) && (dsLen >= 3) ) {
- if ( memcmp ( iptcPtr, "\x1B\x25\x47", 3 ) == 0 ) this->utf8Encoding = true;
+ if ( dsNum == 90 ) {
+ found190 = true;
+ this->offset190 = (XMP_Uns32) (dsPtr - this->iptcContent);
+ this->length190 = 5 + dsLen;
+ if ( (dsLen == 3) && (memcmp ( iptcPtr, "\x1B\x25\x47", 3 ) == 0) ) this->utf8Encoding = true;
}
continue; // Ignore all other record 1 DataSets.
}
if ( recNum == 2 ) {
- if ( ! foundRec2 ) {
- foundRec2 = true;
- this->rec2Offset = (XMP_Uns32) (dsPtr - this->iptcContent);
- this->rec2Length = this->iptcLength - this->rec2Offset; // ! In case there are no other records.
+ if ( ! found2xx ) {
+ found2xx = true;
+ this->offset2xx = (XMP_Uns32) (dsPtr - this->iptcContent);
+ this->length2xx = this->iptcLength - this->offset2xx; // ! In case there are no other records.
}
} else {
- this->rec2Length = (XMP_Uns32) (dsPtr - (this->iptcContent + this->rec2Offset));
- break; // The records are in ascending order, done.
+ this->length2xx = (XMP_Uns32) (dsPtr - (this->iptcContent + this->offset2xx));
+ break; // The records are in ascending order, done with 2:xx DataSets.
}
XMP_Assert ( recNum == 2 );
- if ( dsNum == 0 ) continue; // Ignore 2:00 when reading.
+ if ( dsNum == 0 ) continue; // Ignore 2:00 when reading, it is created explicitly when writing.
DataSetInfo dsInfo ( dsNum, dsLen, iptcPtr );
DataSetMap::iterator dsPos = this->dataSets.find ( dsNum );
@@ -246,7 +255,7 @@ void IPTC_Manager::ParseMemoryDataSets ( const void* data, XMP_Uns32 length, boo
if ( (knownDS == 0) || (knownDS->mapForm == kIPTC_MapArray) ) {
repeatable = true; // Allow repeats for unknown DataSets.
- } else if ( dsNum == kIPTC_SubjectCode ) {
+ } else if ( (dsNum == kIPTC_Creator) || (dsNum == kIPTC_SubjectCode) ) {
repeatable = true;
}
@@ -276,7 +285,7 @@ size_t IPTC_Manager::GetDataSet ( XMP_Uns8 id, DataSetInfo* info, size_t which /
if ( which >= dsCount ) return 0; // Valid range for which is 0 .. count-1.
if ( info != 0 ) {
- for ( size_t i = 0; i < which; ++i ) ++dsPos; // ??? dsPos += which;
+ for ( size_t i = 0; i < which; ++i ) ++dsPos; // Can't do "dsPos += which", no iter+int operator.
*info = dsPos->second;
}
@@ -290,6 +299,8 @@ size_t IPTC_Manager::GetDataSet ( XMP_Uns8 id, DataSetInfo* info, size_t which /
size_t IPTC_Manager::GetDataSet_UTF8 ( XMP_Uns8 id, std::string * utf8Str, size_t which /* = 0 */ ) const
{
+ if ( utf8Str != 0 ) utf8Str->erase();
+
DataSetInfo dsInfo;
size_t dsCount = GetDataSet ( id, &dsInfo, which );
if ( dsCount == 0 ) return 0;
@@ -297,14 +308,10 @@ size_t IPTC_Manager::GetDataSet_UTF8 ( XMP_Uns8 id, std::string * utf8Str, size_
if ( utf8Str != 0 ) {
if ( this->utf8Encoding ) {
utf8Str->assign ( (char*)dsInfo.dataPtr, dsInfo.dataLen );
- } else {
- #if ! XMP_UNIXBuild
- ReconcileUtils::LocalToUTF8 ( dsInfo.dataPtr, dsInfo.dataLen, utf8Str );
- #else
- // ! Hack until legacy-as-local issues are resolved for generic UNIX.
- if ( ! ReconcileUtils::IsUTF8 ( dsInfo.dataPtr, dsInfo.dataLen ) ) return 0;
- utf8Str->assign ( (char*)dsInfo.dataPtr, dsInfo.dataLen );
- #endif
+ } else if ( ! ignoreLocalText ) {
+ ReconcileUtils::LocalToUTF8 ( dsInfo.dataPtr, dsInfo.dataLen, utf8Str );
+ } else if ( ReconcileUtils::IsASCII ( dsInfo.dataPtr, dsInfo.dataLen ) ) {
+ utf8Str->assign ( (char*)dsInfo.dataPtr, dsInfo.dataLen );
}
}
@@ -339,8 +346,6 @@ void IPTC_Manager::DisposeLooseValue ( DataSetInfo & dsInfo )
// =================================================================================================
// =================================================================================================
-#if ! XMP_UNIXBuild // ! Disable IPTC output for generic UNIX until the legacy-as-local issues are resolved.
-
// =================================================================================================
// IPTC_Writer::~IPTC_Writer
// =========================
@@ -367,39 +372,57 @@ void IPTC_Writer::SetDataSet_UTF8 ( XMP_Uns8 id, const void* utf8Ptr, XMP_Uns32
// Decide which character encoding to use and get a temporary pointer to the value.
- XMP_Uns8 * tempPtr;
- XMP_Uns32 dataLen;
-
- std::string localStr, rtStr;
-
- if ( this->utf8Encoding ) {
+ XMP_Uns8 * tempPtr;
+ XMP_Uns32 dataLen;
+ std::string localStr;
- // We're already using UTF-8.
+ if ( kUTF8_Mode == kUTF8_AlwaysMode ) {
+
+ // Always use UTF-8.
+ if ( ! this->utf8Encoding ) this->ConvertToUTF8();
tempPtr = (XMP_Uns8*) utf8Ptr;
dataLen = utf8Len;
- } else {
+ } else if ( kUTF8_Mode == kUTF8_IncomingMode ) {
-// *** Disable the round trip loss checking for now. We only use UTF-8 if the input had it.
-
- ReconcileUtils::UTF8ToLocal ( utf8Ptr, utf8Len, &localStr );
-// ReconcileUtils::LocalToUTF8 ( localStr.data(), localStr.size(), &rtStr );
-
-// if ( (rtStr.size() == utf8Len) && (memcmp ( rtStr.data(), utf8Ptr, utf8Len ) == 0) ) {
-
- // It round-tripped without loss, keep local encoding.
+ // Only use UTF-8 if that was what the parsed block used.
+ if ( this->utf8Encoding ) {
+ tempPtr = (XMP_Uns8*) utf8Ptr;
+ dataLen = utf8Len;
+ } else if ( ! ignoreLocalText ) {
+ ReconcileUtils::UTF8ToLocal ( utf8Ptr, utf8Len, &localStr );
tempPtr = (XMP_Uns8*) localStr.data();
- dataLen = (XMP_Uns32)localStr.size();
-
-// } else {
-
-// // Had round-trip loss, change to UTF-8 for all text DataSets.
-// this->ConvertToUTF8();
-// XMP_Assert ( this->utf8Encoding );
-// tempPtr = (XMP_Uns8*) utf8Ptr;
-// dataLen = utf8Len;
-
-// }
+ dataLen = (XMP_Uns32) localStr.size();
+ } else {
+ if ( ! ReconcileUtils::IsASCII ( utf8Ptr, utf8Len ) ) return;
+ tempPtr = (XMP_Uns8*) utf8Ptr;
+ dataLen = utf8Len;
+ }
+
+ } else if ( kUTF8_Mode == kUTF8_LosslessMode ) {
+
+ // Convert to UTF-8 if needed to prevent round trip loss.
+ if ( this->utf8Encoding ) {
+ tempPtr = (XMP_Uns8*) utf8Ptr;
+ dataLen = utf8Len;
+ } else if ( ! ignoreLocalText ) {
+ std::string rtStr;
+ ReconcileUtils::UTF8ToLocal ( utf8Ptr, utf8Len, &localStr );
+ ReconcileUtils::LocalToUTF8 ( localStr.data(), localStr.size(), &rtStr );
+ if ( (rtStr.size() == utf8Len) && (memcmp ( rtStr.data(), utf8Ptr, utf8Len ) == 0) ) {
+ tempPtr = (XMP_Uns8*) localStr.data(); // No loss, keep local encoding.
+ dataLen = (XMP_Uns32) localStr.size();
+ } else {
+ this->ConvertToUTF8(); // Had loss, change everything to UTF-8.
+ XMP_Assert ( this->utf8Encoding );
+ tempPtr = (XMP_Uns8*) utf8Ptr;
+ dataLen = utf8Len;
+ }
+ } else {
+ if ( ! ReconcileUtils::IsASCII ( utf8Ptr, utf8Len ) ) return;
+ tempPtr = (XMP_Uns8*) utf8Ptr;
+ dataLen = utf8Len;
+ }
}
@@ -422,7 +445,7 @@ void IPTC_Writer::SetDataSet_UTF8 ( XMP_Uns8 id, const void* utf8Ptr, XMP_Uns32
if ( knownDS->mapForm == kIPTC_MapArray ) {
repeatable = true;
- } else if ( id == kIPTC_SubjectCode ) {
+ } else if ( (id == kIPTC_Creator) || (id == kIPTC_SubjectCode) ) {
repeatable = true;
}
@@ -500,35 +523,43 @@ void IPTC_Writer::DeleteDataSet ( XMP_Uns8 id, long which /* = -1 */ )
// IPTC_Writer::UpdateMemoryDataSets
// =================================
//
-// Reconstruct the entire IIM block. Start with DataSet 1:0 and 1:90 if UTF-8 encoding is used,
-// then 2:0, then 2:xx DataSets that have values. This does not include any alignment padding, that
-// is an artifact of some specific wrappers such as Photoshop image resources.
+// Reconstruct the entire IIM block. This does not include any final alignment padding, that is an
+// artifact of some specific wrappers such as Photoshop image resources.
-XMP_Uns32 IPTC_Writer::UpdateMemoryDataSets ( void** dataPtr )
+void IPTC_Writer::UpdateMemoryDataSets()
{
- if ( ! this->changed ) {
- if ( dataPtr != 0 ) *dataPtr = this->iptcContent;
- return this->iptcLength;
- }
+ if ( ! this->changed ) return;
DataSetMap::iterator dsPos;
DataSetMap::iterator dsEnd = this->dataSets.end();
-// if ( this->utf8Encoding ) { *** Disable round trip loss checking for now. ***
-// if ( ! this->CheckRoundTripLoss() ) this->ConvertToLocal();
-// }
+ if ( kUTF8_Mode == kUTF8_LosslessMode ) {
+ if ( this->utf8Encoding ) {
+ if ( ! this->CheckRoundTripLoss() ) this->ConvertToLocal();
+ } else {
+ if ( this->CheckRoundTripLoss() ) this->ConvertToUTF8();
+ }
+ }
// Compute the length of the new IIM block, including space for records other than 2. All other
- // records are preserved as-is, including 1:90. If we ever start changing the encoding, we will
- // have to remove any existing 1:90 and insert a new one.
+ // records are preserved as-is, except for 1:90. If local text is used then 1:90 is omitted,
+ // if UTF-8 text is used then 1:90 is written.
+
+ XMP_Uns32 midOffset = this->offset190 + this->length190;
+ XMP_Uns32 midLength = this->offset2xx - midOffset;
+
+ XMP_Uns32 suffixOffset = this->offset2xx + this->length2xx;
+ XMP_Uns32 suffixLength = this->iptcLength - suffixOffset;
- XMP_Uns32 newLength = (5+2); // For 2:0.
- newLength += (this->iptcLength - rec2Length); // For records other than 2.
+ XMP_Uns32 newLength = this->offset190 + (5+2); // For things before 1:90 and 2:0.
+ if ( this->utf8Encoding ) newLength += (5+3); // For 1:90, if written.
+ newLength += midLength; // For things between 1:90 and 2:xx.
+ newLength += suffixLength; // For records after 2.
- for ( dsPos = this->dataSets.begin(); dsPos != dsEnd; ++dsPos ) {
+ for ( dsPos = this->dataSets.begin(); dsPos != dsEnd; ++dsPos ) { // Accumulate the 2:xx sizes.
XMP_Uns32 dsLen = dsPos->second.dataLen;
newLength += (5 + dsLen);
- if ( dsLen > 0x7FFF ) newLength += 4; // We always use a 4 byte extended length.
+ if ( dsLen > 0x7FFF ) newLength += 4; // We always use a 4 byte extended length for big values.
}
// Allocate the new IIM block.
@@ -538,25 +569,35 @@ XMP_Uns32 IPTC_Writer::UpdateMemoryDataSets ( void** dataPtr )
XMP_Uns8* dsPtr = newContent;
- XMP_Uns32 prefixLength = this->rec2Offset;
- XMP_Uns32 suffixOffset = this->rec2Offset + this->rec2Length;
- XMP_Uns32 suffixLength = this->iptcLength - suffixOffset;
+ // Write the 1:xx DataSets, including whatever is appropriate for 1:90.
+
+ if ( this->offset190 > 0 ) { // Whatever was before an existing 1:90.
+ memcpy ( dsPtr, this->iptcContent, this->offset190 ); // AUDIT: Within range of allocation.
+ dsPtr += this->offset190;
+ }
- if ( prefixLength > 0 ) { // Write the records before 2.
- memcpy ( dsPtr, this->iptcContent, prefixLength ); // AUDIT: Within range of allocation.
- dsPtr += prefixLength;
+ if ( this->utf8Encoding ) { // Write 1:90 only if text is UTF-8.
+ memcpy ( dsPtr, "\x1C\x01\x5A\x00\x03\x1B\x25\x47", (5+3) ); // AUDIT: Within range of allocation.
+ dsPtr += (5+3);
}
- if ( ! this->utf8Encoding ) {
+ if ( midLength > 0 ) { // Write the remainder before record 2.
+ memcpy ( dsPtr, (this->iptcContent + midOffset), midLength ); // AUDIT: Within range of allocation.
+ dsPtr += midLength;
+ }
+
+ // Write the 2:xx DataSets, starting with an explicit 2:00.
+
+ if ( this->utf8Encoding ) {
+ // Start with 2:00 for version 4.
+ memcpy ( dsPtr, "\x1C\x02\x00\x00\x02\x00\x04", (5+2) ); // AUDIT: Within range of allocation.
+ dsPtr += (5+2);
+ } else {
// Start with 2:00 for version 2.
// *** We should probably write version 4 all the time. This is a late CS3 change, don't want
// *** to risk breaking other apps that might be strict about version checking.
memcpy ( dsPtr, "\x1C\x02\x00\x00\x02\x00\x02", (5+2) ); // AUDIT: Within range of allocation.
dsPtr += (5+2);
- } else {
- // Start with 2:00 for version 4.
- memcpy ( dsPtr, "\x1C\x02\x00\x00\x02\x00\x04", (5+2) ); // AUDIT: Within range of allocation.
- dsPtr += (5+2);
}
// Fill in the record 2 DataSets that have values.
@@ -601,15 +642,8 @@ XMP_Uns32 IPTC_Writer::UpdateMemoryDataSets ( void** dataPtr )
XMP_Assert ( this->iptcLength == newLength );
this->ownedContent = (newLength > 0); // We really do own the new content, if not empty.
- // Done.
-
- if ( dataPtr != 0 ) *dataPtr = this->iptcContent;
- return this->iptcLength;
-
} // IPTC_Writer::UpdateMemoryDataSets
-#if 0 // *** Disable the round trip loss checking for now.
-
// =================================================================================================
// IPTC_Writer::ConvertToUTF8
// ==========================
@@ -631,7 +665,7 @@ void IPTC_Writer::ConvertToUTF8()
ReconcileUtils::LocalToUTF8 ( dsInfo.dataPtr, dsInfo.dataLen, &utf8Str );
this->DisposeLooseValue ( dsInfo );
- dsInfo.dataLen = utf8Str.size();
+ dsInfo.dataLen = (XMP_Uns32)utf8Str.size();
dsInfo.dataPtr = (XMP_Uns8*) malloc ( dsInfo.dataLen );
if ( dsInfo.dataPtr == 0 ) XMP_Throw ( "Out of memory", kXMPErr_NoMemory );
memcpy ( dsInfo.dataPtr, utf8Str.data(), dsInfo.dataLen ); // AUDIT: Safe, malloc'ed dataLen bytes above.
@@ -663,7 +697,7 @@ void IPTC_Writer::ConvertToLocal()
ReconcileUtils::UTF8ToLocal ( dsInfo.dataPtr, dsInfo.dataLen, &localStr );
this->DisposeLooseValue ( dsInfo );
- dsInfo.dataLen = localStr.size();
+ dsInfo.dataLen = (XMP_Uns32)localStr.size();
dsInfo.dataPtr = (XMP_Uns8*) malloc ( dsInfo.dataLen );
if ( dsInfo.dataPtr == 0 ) XMP_Throw ( "Out of memory", kXMPErr_NoMemory );
memcpy ( dsInfo.dataPtr, localStr.data(), dsInfo.dataLen ); // AUDIT: Safe, malloc'ed dataLen bytes above.
@@ -708,8 +742,4 @@ bool IPTC_Writer::CheckRoundTripLoss()
} // IPTC_Writer::CheckRoundTripLoss
-#endif // Round-trip loss checking
-
// =================================================================================================
-
-#endif // ! XMP_UNIXBuild
diff --git a/source/XMPFiles/FormatSupport/IPTC_Support.hpp b/source/XMPFiles/FormatSupport/IPTC_Support.hpp
index 25ac9c2..470a6d4 100644
--- a/source/XMPFiles/FormatSupport/IPTC_Support.hpp
+++ b/source/XMPFiles/FormatSupport/IPTC_Support.hpp
@@ -3,7 +3,7 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2006-2007 Adobe Systems Incorporated
+// Copyright 2006 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -114,11 +114,12 @@ enum { // List of recognized 2:* IIM DataSets. The names are from IIMv4 and IPTC
kIPTC_PreviewData = 202
};
-enum { // Forms of mapping legacy IPTC to XMP.
+enum { // Forms of mapping legacy IPTC to XMP. Order is significant, see PhotoDataUtils::Import2WayIPTC!
kIPTC_MapSimple, // The XMP is simple, the last DataSet occurrence is kept.
kIPTC_MapLangAlt, // The XMP is a LangAlt x-default item, the last DataSet occurrence is kept.
kIPTC_MapArray, // The XMP is an unordered array, all DataSets are kept.
kIPTC_MapSpecial, // The mapping requires DataSet specific code.
+ kIPTC_Map3Way, // Has a 3 way mapping between Exif, IPTC, and XMP.
kIPTC_UnmappedText, // A text DataSet that is not mapped to XMP.
kIPTC_UnmappedBin // A binary DataSet that is not mapped to XMP.
};
@@ -201,12 +202,18 @@ public:
bool UsingUTF8() const { return this->utf8Encoding; };
+ // --------------------------------------------------
+ // Update the DataSets to reflect the changed values.
+
+ virtual void UpdateMemoryDataSets() = 0;
+
// ---------------------------------------------------------------------------------------------
- // Update the DataSets to reflect the changed values. Returns the new size of the DataSets. The
- // returned dataPtr must be treated as read only. It exists until the IPTC_Manager destructor
- // is called. Can be used with read-only instances to get the raw data block info.
+ // Get the location and size of the full IPTC block. The client must call UpdateMemoryDataSets
+ // first if appropriate. The returned dataPtr must be treated as read only. It exists until the
+ // IPTC_Manager destructor is called.
- virtual XMP_Uns32 UpdateMemoryDataSets ( void** dataPtr ) = 0;
+ XMP_Uns32 GetBlockInfo ( void** dataPtr ) const
+ { if ( dataPtr != 0 ) *dataPtr = this->iptcContent; return this->iptcLength; };
// ---------------------------------------------------------------------------------------------
@@ -221,13 +228,13 @@ protected:
DataSetMap dataSets;
XMP_Uns8* iptcContent;
- XMP_Uns32 iptcLength, rec2Offset, rec2Length;
+ XMP_Uns32 iptcLength, offset190, length190, offset2xx, length2xx;
bool changed;
bool ownedContent; // True if IPTC_Manager destructor needs to release the content block.
bool utf8Encoding; // True if text values are encoded as UTF-8.
- IPTC_Manager() : iptcContent(0), iptcLength(0), rec2Offset(0), rec2Length(0),
+ IPTC_Manager() : iptcContent(0), iptcLength(0), offset190(0), length190(0), offset2xx(0), length2xx(0),
changed(false), ownedContent(false), utf8Encoding(false) {};
void DisposeLooseValue ( DataSetInfo & dsInfo );
@@ -254,8 +261,7 @@ public:
bool IsChanged() { return false; };
- XMP_Uns32 UpdateMemoryDataSets ( void** dataPtr )
- { if ( dataPtr != 0 ) *dataPtr = iptcContent; return iptcLength; };
+ void UpdateMemoryDataSets() { NotAppropriate(); };
virtual ~IPTC_Reader() {};
@@ -269,8 +275,6 @@ private:
// IPTC_Writer
// ===========
-#if ! XMP_UNIXBuild // ! Disable IPTC output for generic UNIX until the legacy-as-local issues are resolved.
-
class IPTC_Writer : public IPTC_Manager {
public:
@@ -280,7 +284,7 @@ public:
bool IsChanged() { return changed; };
- XMP_Uns32 UpdateMemoryDataSets ( void** dataPtr );
+ void UpdateMemoryDataSets ();
IPTC_Writer() {};
@@ -288,19 +292,13 @@ public:
private:
-#if 0
-
void ConvertToUTF8();
void ConvertToLocal();
bool CheckRoundTripLoss();
-
-#endif // *** Disable the round trip loss checking for now.
}; // IPTC_Writer
-#endif // ! XMP_UNIXBuild
-
// =================================================================================================
#endif // __IPTC_Support_hpp__
diff --git a/source/XMPFiles/FormatSupport/ISOBaseMedia_Support.cpp b/source/XMPFiles/FormatSupport/ISOBaseMedia_Support.cpp
new file mode 100644
index 0000000..7d94734
--- /dev/null
+++ b/source/XMPFiles/FormatSupport/ISOBaseMedia_Support.cpp
@@ -0,0 +1,149 @@
+// =================================================================================================
+// ADOBE SYSTEMS INCORPORATED
+// Copyright 2007 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 "ISOBaseMedia_Support.hpp"
+#include "XMPFiles_Impl.hpp"
+
+// =================================================================================================
+/// \file ISOBaseMedia_Support.cpp
+/// \brief Manager for parsing and serializing ISO Base Media files ( MPEG-4 and JPEG-2000).
+///
+// =================================================================================================
+
+namespace ISOMedia {
+
+static BoxInfo voidInfo;
+
+// =================================================================================================
+// GetBoxInfo - from memory
+// ========================
+
+const XMP_Uns8 * GetBoxInfo ( const XMP_Uns8 * boxPtr, const XMP_Uns8 * boxLimit,
+ BoxInfo * info, bool throwErrors /* = false */ )
+{
+ XMP_Uns32 u32Size;
+
+ if ( info == 0 ) info = &voidInfo;
+ info->boxType = info->headerSize = 0;
+ info->contentSize = 0;
+
+ if ( boxPtr >= boxLimit ) XMP_Throw ( "Bad offset to GetBoxInfo", kXMPErr_InternalFailure );
+
+ if ( (boxLimit - boxPtr) < 8 ) { // Is there enough space for a standard box header?
+ if ( throwErrors ) XMP_Throw ( "No space for ISO box header", kXMPErr_BadFileFormat );
+ info->headerSize = (XMP_Uns32) (boxLimit - boxPtr);
+ return boxLimit;
+ }
+
+ u32Size = GetUns32BE ( boxPtr );
+ info->boxType = GetUns32BE ( boxPtr+4 );
+
+ if ( u32Size >= 8 ) {
+ info->headerSize = 8; // Normal explicit size case.
+ info->contentSize = u32Size - 8;
+ } else if ( u32Size == 0 ) {
+ info->headerSize = 8; // The box goes to EoF - treat it as "to limit".
+ info->contentSize = (boxLimit - boxPtr) - 8;
+ } else if ( u32Size != 1 ) {
+ if ( throwErrors ) XMP_Throw ( "Bad ISO box size, 2..7", kXMPErr_BadFileFormat );
+ info->headerSize = 8; // Bad total size in the range of 2..7, treat as 8.
+ info->contentSize = 0;
+ } else {
+ if ( (boxLimit - boxPtr) < 16 ) { // Is there enough space for an extended box header?
+ if ( throwErrors ) XMP_Throw ( "No space for ISO extended header", kXMPErr_BadFileFormat );
+ info->headerSize = (XMP_Uns32) (boxLimit - boxPtr);
+ return boxLimit;
+ }
+ XMP_Uns64 u64Size = GetUns64BE ( boxPtr+8 );
+ if ( u64Size < 16 ) {
+ if ( throwErrors ) XMP_Throw ( "Bad ISO extended box size, < 16", kXMPErr_BadFileFormat );
+ u64Size = 16; // Treat bad total size as 16.
+ }
+ info->headerSize = 16;
+ info->contentSize = u64Size - 16;
+ }
+
+ XMP_Assert ( (XMP_Uns64)(boxLimit - boxPtr) >= (XMP_Uns64)info->headerSize );
+ if ( info->contentSize > (XMP_Uns64)((boxLimit - boxPtr) - info->headerSize) ) {
+ if ( throwErrors ) XMP_Throw ( "Bad ISO box content size", kXMPErr_BadFileFormat );
+ info->contentSize = ((boxLimit - boxPtr) - info->headerSize); // Trim a bad content size to the limit.
+ }
+
+ return (boxPtr + info->headerSize + info->contentSize);
+
+} // GetBoxInfo
+
+// =================================================================================================
+// GetBoxInfo - from a file
+// ========================
+
+XMP_Uns64 GetBoxInfo ( LFA_FileRef fileRef, const XMP_Uns64 boxOffset, const XMP_Uns64 boxLimit,
+ BoxInfo * info, bool doSeek /* = true */, bool throwErrors /* = false */ )
+{
+ XMP_Uns8 buffer [8];
+ XMP_Uns32 u32Size;
+
+ if ( info == 0 ) info = &voidInfo;
+ info->boxType = info->headerSize = 0;
+ info->contentSize = 0;
+
+ if ( boxOffset >= boxLimit ) XMP_Throw ( "Bad offset to GetBoxInfo", kXMPErr_InternalFailure );
+
+ if ( (boxLimit - boxOffset) < 8 ) { // Is there enough space for a standard box header?
+ if ( throwErrors ) XMP_Throw ( "No space for ISO box header", kXMPErr_BadFileFormat );
+ info->headerSize = (XMP_Uns32) (boxLimit - boxOffset);
+ return boxLimit;
+ }
+
+ if ( doSeek ) LFA_Seek ( fileRef, boxOffset, SEEK_SET );
+ (void) LFA_Read ( fileRef, buffer, 8, kLFA_RequireAll );
+
+ u32Size = GetUns32BE ( &buffer[0] );
+ info->boxType = GetUns32BE ( &buffer[4] );
+
+ if ( u32Size >= 8 ) {
+ info->headerSize = 8; // Normal explicit size case.
+ info->contentSize = u32Size - 8;
+ } else if ( u32Size == 0 ) {
+ info->headerSize = 8; // The box goes to EoF.
+ info->contentSize = LFA_Measure(fileRef) - (boxOffset + 8);
+ } else if ( u32Size != 1 ) {
+ if ( throwErrors ) XMP_Throw ( "Bad ISO box size, 2..7", kXMPErr_BadFileFormat );
+ info->headerSize = 8; // Bad total size in the range of 2..7, treat as 8.
+ info->contentSize = 0;
+ } else {
+ if ( (boxLimit - boxOffset) < 16 ) { // Is there enough space for an extended box header?
+ if ( throwErrors ) XMP_Throw ( "No space for ISO extended header", kXMPErr_BadFileFormat );
+ info->headerSize = (XMP_Uns32) (boxLimit - boxOffset);
+ return boxLimit;
+ }
+ (void) LFA_Read ( fileRef, buffer, 8, kLFA_RequireAll );
+ XMP_Uns64 u64Size = GetUns64BE ( &buffer[0] );
+ if ( u64Size < 16 ) {
+ if ( throwErrors ) XMP_Throw ( "Bad ISO extended box size, < 16", kXMPErr_BadFileFormat );
+ u64Size = 16; // Treat bad total size as 16.
+ }
+ info->headerSize = 16;
+ info->contentSize = u64Size - 16;
+ }
+
+ XMP_Assert ( (boxLimit - boxOffset) >= info->headerSize );
+ if ( info->contentSize > (boxLimit - boxOffset - info->headerSize) ) {
+ if ( throwErrors ) XMP_Throw ( "Bad ISO box content size", kXMPErr_BadFileFormat );
+ info->contentSize = (boxLimit - boxOffset - info->headerSize); // Trim a bad content size to the limit.
+ }
+
+ return (boxOffset + info->headerSize + info->contentSize);
+
+} // GetBoxInfo
+
+} // namespace ISO_Media
+
+
+// =================================================================================================
diff --git a/source/XMPFiles/FormatSupport/ISOBaseMedia_Support.hpp b/source/XMPFiles/FormatSupport/ISOBaseMedia_Support.hpp
new file mode 100644
index 0000000..4f50df7
--- /dev/null
+++ b/source/XMPFiles/FormatSupport/ISOBaseMedia_Support.hpp
@@ -0,0 +1,100 @@
+#ifndef __ISOBaseMedia_Support_hpp__
+#define __ISOBaseMedia_Support_hpp__ 1
+
+// =================================================================================================
+// ADOBE SYSTEMS INCORPORATED
+// Copyright 2007 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 "XMP_Environment.h" // ! This must be the first include.
+
+#include "XMP_Const.h"
+#include "LargeFileAccess.hpp"
+
+// =================================================================================================
+/// \file ISOBaseMedia_Support.hpp
+/// \brief XMPFiles support for the ISO Base Media File Format.
+///
+/// \note These classes are for use only when directly compiled and linked. They should not be
+/// packaged in a DLL by themselves. They do not provide any form of C++ ABI protection.
+// =================================================================================================
+
+namespace ISOMedia {
+
+ enum {
+ k_ftyp = 0x66747970UL, // File header Box, no version/flags.
+
+ k_mp41 = 0x6D703431UL, // Compatible brand codes
+ k_mp42 = 0x6D703432UL,
+ k_f4v = 0x66347620UL,
+ k_qt = 0x71742020UL,
+
+ k_moov = 0x6D6F6F76UL, // Container Box, no version/flags.
+ k_mvhd = 0x6D766864UL, // Data FullBox, has version/flags.
+ k_hdlr = 0x68646C72UL,
+ k_udta = 0x75647461UL, // Container Box, no version/flags.
+ k_cprt = 0x63707274UL, // Data FullBox, has version/flags.
+ k_uuid = 0x75756964UL, // Data Box, no version/flags.
+ k_free = 0x66726565UL, // Free space Box, no version/flags.
+ k_mdat = 0x6D646174UL, // Media data Box, no version/flags.
+
+ k_trak = 0x7472616BUL, // Types for the QuickTime timecode track.
+ k_tkhd = 0x746B6864UL,
+ k_mdia = 0x6D646961UL,
+ k_mdhd = 0x6D646864UL,
+ k_tmcd = 0x746D6364UL,
+ k_mhlr = 0x6D686C72UL,
+ k_minf = 0x6D696E66UL,
+ k_stbl = 0x7374626CUL,
+ k_stsd = 0x73747364UL,
+ k_stsc = 0x73747363UL,
+ k_stco = 0x7374636FUL,
+ k_co64 = 0x636F3634UL,
+
+ k_meta = 0x6D657461UL, // Types for the iTunes metadata boxes.
+ k_ilst = 0x696C7374UL,
+ k_mdir = 0x6D646972UL,
+ k_mean = 0x6D65616EUL,
+ k_name = 0x6E616D65UL,
+ k_data = 0x64617461UL,
+ k_hyphens = 0x2D2D2D2DUL,
+
+ k_skip = 0x736B6970UL, // Additional classic QuickTime top level boxes.
+ k_wide = 0x77696465UL,
+ k_pnot = 0x706E6F74UL,
+
+ k_XMP_ = 0x584D505FUL // The QuickTime variant XMP box.
+ };
+
+ static XMP_Uns32 k_xmpUUID [4] = { MakeUns32BE ( 0xBE7ACFCBUL ),
+ MakeUns32BE ( 0x97A942E8UL ),
+ MakeUns32BE ( 0x9C719994UL ),
+ MakeUns32BE ( 0x91E3AFACUL ) };
+
+ struct BoxInfo {
+ XMP_Uns32 boxType; // In memory as native endian!
+ XMP_Uns32 headerSize; // Normally 8 or 16, less than 8 if available space is too small.
+ XMP_Uns64 contentSize; // Always the real size, never 0 for "to EoF".
+ BoxInfo() : boxType(0), headerSize(0), contentSize(0) {};
+ };
+
+ // Get basic info about a box in memory, returning a pointer to the following box.
+ const XMP_Uns8 * GetBoxInfo ( const XMP_Uns8 * boxPtr, const XMP_Uns8 * boxLimit,
+ BoxInfo * info, bool throwErrors = false );
+
+ // Get basic info about a box in a file, returning the offset of the following box. The I/O
+ // pointer is left at the start of the box's content. Returns the offset of the following box.
+ XMP_Uns64 GetBoxInfo ( LFA_FileRef fileRef, const XMP_Uns64 boxOffset, const XMP_Uns64 boxLimit,
+ BoxInfo * info, bool doSeek = true, bool throwErrors = false );
+
+// XMP_Uns32 CountChildBoxes ( LFA_FileRef fileRef, const XMP_Uns64 childOffset, const XMP_Uns64 childLimit );
+
+} // namespace ISO_Media
+
+// =================================================================================================
+
+#endif // __ISOBaseMedia_Support_hpp__
diff --git a/source/XMPFiles/FormatSupport/MOOV_Support.cpp b/source/XMPFiles/FormatSupport/MOOV_Support.cpp
new file mode 100644
index 0000000..1c89176
--- /dev/null
+++ b/source/XMPFiles/FormatSupport/MOOV_Support.cpp
@@ -0,0 +1,542 @@
+// =================================================================================================
+// ADOBE SYSTEMS INCORPORATED
+// Copyright 2009 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 "MOOV_Support.hpp"
+
+#include "ISOBaseMedia_Support.hpp"
+
+#include <string.h>
+
+// =================================================================================================
+/// \file MOOV_Support.cpp
+/// \brief XMPFiles support for the 'moov' box in MPEG-4 and QuickTime files.
+// =================================================================================================
+
+// =================================================================================================
+// =================================================================================================
+// MOOV_Manager - The parsing and reading routines are all commmon
+// =================================================================================================
+// =================================================================================================
+
+#ifndef TraceParseMoovTree
+ #define TraceParseMoovTree 0
+#endif
+
+#ifndef TraceUpdateMoovTree
+ #define TraceUpdateMoovTree 0
+#endif
+
+// =================================================================================================
+// MOOV_Manager::PickContentPtr
+// ============================
+
+XMP_Uns8 * MOOV_Manager::PickContentPtr ( const BoxNode & node ) const
+{
+ if ( node.contentSize == 0 ) {
+ return 0;
+ } else if ( node.changed ) {
+ return (XMP_Uns8*) &node.changedContent[0];
+ } else {
+ return (XMP_Uns8*) &this->fullSubtree[0] + node.offset + node.headerSize;
+ }
+} // MOOV_Manager::PickContentPtr
+
+// =================================================================================================
+// MOOV_Manager::FillBoxInfo
+// =========================
+
+void MOOV_Manager::FillBoxInfo ( const BoxNode & node, BoxInfo * info ) const
+{
+ if ( info == 0 ) return;
+
+ info->boxType = node.boxType;
+ info->childCount = (XMP_Uns32)node.children.size();
+ info->contentSize = node.contentSize;
+ info->content = PickContentPtr ( node );
+
+} // MOOV_Manager::FillBoxInfo
+
+// =================================================================================================
+// MOOV_Manager::GetBox
+// ====================
+//
+// Find a box given the type path. Pick the first child of each type.
+
+MOOV_Manager::BoxRef MOOV_Manager::GetBox ( const char * boxPath, BoxInfo * info ) const
+{
+ size_t pathLen = strlen(boxPath);
+ XMP_Assert ( (pathLen >= 4) && XMP_LitNMatch ( boxPath, "moov", 4 ) );
+ if ( info != 0 ) memset ( info, 0, sizeof(BoxInfo) );
+
+ const char * pathPtr = boxPath + 5; // Skip the "moov/" portion.
+ const char * pathEnd = boxPath + pathLen;
+
+ BoxRef currRef = &this->moovNode;
+
+ while ( pathPtr < pathEnd ) {
+
+ XMP_Assert ( (pathEnd - pathPtr) >= 4 );
+ XMP_Uns32 boxType = GetUns32BE ( pathPtr );
+ pathPtr += 5; // ! Don't care that the last step goes 1 too far.
+
+ currRef = this->GetTypeChild ( currRef, boxType, 0 );
+ if ( currRef == 0 ) return 0;
+
+ }
+
+ this->FillBoxInfo ( *((BoxNode*)currRef), info );
+ return currRef;
+
+} // MOOV_Manager::GetBox
+
+// =================================================================================================
+// MOOV_Manager::GetNthChild
+// =========================
+
+MOOV_Manager::BoxRef MOOV_Manager::GetNthChild ( BoxRef parentRef, size_t childIndex, BoxInfo * info ) const
+{
+ XMP_Assert ( parentRef != 0 );
+ const BoxNode & parent = *((BoxNode*)parentRef);
+ if ( info != 0 ) memset ( info, 0, sizeof(BoxInfo) );
+
+ if ( childIndex >= parent.children.size() ) return 0;
+
+ const BoxNode & currNode = parent.children[childIndex];
+
+ this->FillBoxInfo ( currNode, info );
+ return &currNode;
+
+} // MOOV_Manager::GetNthChild
+
+// =================================================================================================
+// MOOV_Manager::GetTypeChild
+// ==========================
+
+MOOV_Manager::BoxRef MOOV_Manager::GetTypeChild ( BoxRef parentRef, XMP_Uns32 childType, BoxInfo * info ) const
+{
+ XMP_Assert ( parentRef != 0 );
+ const BoxNode & parent = *((BoxNode*)parentRef);
+ if ( info != 0 ) memset ( info, 0, sizeof(BoxInfo) );
+ if ( parent.children.empty() ) return 0;
+
+ size_t i = 0, limit = parent.children.size();
+ for ( ; i < limit; ++i ) {
+ const BoxNode & currNode = parent.children[i];
+ if ( currNode.boxType == childType ) {
+ this->FillBoxInfo ( currNode, info );
+ return &currNode;
+ }
+ }
+
+ return 0;
+
+} // MOOV_Manager::GetTypeChild
+
+// =================================================================================================
+// MOOV_Manager::GetParsedOffset
+// =============================
+
+XMP_Uns32 MOOV_Manager::GetParsedOffset ( BoxRef ref ) const
+{
+ XMP_Assert ( ref != 0 );
+ const BoxNode & node = *((BoxNode*)ref);
+
+ if ( node.changed ) return 0;
+ return node.offset;
+
+} // MOOV_Manager::GetParsedOffset
+
+// =================================================================================================
+// MOOV_Manager::GetHeaderSize
+// ===========================
+
+XMP_Uns32 MOOV_Manager::GetHeaderSize ( BoxRef ref ) const
+{
+ XMP_Assert ( ref != 0 );
+ const BoxNode & node = *((BoxNode*)ref);
+
+ if ( node.changed ) return 0;
+ return node.headerSize;
+
+} // MOOV_Manager::GetHeaderSize
+
+// =================================================================================================
+// MOOV_Manager::ParseMemoryTree
+// =============================
+//
+// Parse the fullSubtree data, building the BoxNode tree for the stuff that we care about. Tolerate
+// errors like content ending too soon, make a best effoert to parse what we can.
+
+void MOOV_Manager::ParseMemoryTree ( XMP_Uns8 fileMode )
+{
+ this->fileMode = fileMode;
+
+ this->moovNode.offset = this->moovNode.boxType = 0;
+ this->moovNode.headerSize = this->moovNode.contentSize = 0;
+ this->moovNode.children.clear();
+ this->moovNode.changedContent.clear();
+ this->moovNode.changed = false;
+
+ if ( this->fullSubtree.empty() ) return;
+
+ ISOMedia::BoxInfo moovInfo;
+ const XMP_Uns8 * moovOrigin = &this->fullSubtree[0];
+ const XMP_Uns8 * moovLimit = moovOrigin + this->fullSubtree.size();
+
+ (void) ISOMedia::GetBoxInfo ( moovOrigin, moovLimit, &moovInfo );
+ XMP_Enforce ( moovInfo.boxType == ISOMedia::k_moov );
+
+ XMP_Uns64 fullMoovSize = moovInfo.headerSize + moovInfo.contentSize;
+ if ( fullMoovSize > moovBoxSizeLimit ) { // From here on we know 32-bit offsets are safe.
+ XMP_Throw ( "Oversize 'moov' box", kXMPErr_EnforceFailure );
+ }
+
+ this->moovNode.boxType = ISOMedia::k_moov;
+ this->moovNode.headerSize = moovInfo.headerSize;
+ this->moovNode.contentSize = (XMP_Uns32)moovInfo.contentSize;
+
+ bool ignoreMetaBoxes = (fileMode == kFileIsTraditionalQT); // ! Don't want, these don't follow ISO spec.
+ #if TraceParseMoovTree
+ fprintf ( stderr, "Parsing 'moov' subtree, moovNode @ 0x%X, ignoreMetaBoxes = %d\n",
+ &this->moovNode, ignoreMetaBoxes );
+ #endif
+ this->ParseNestedBoxes ( &this->moovNode, "moov", ignoreMetaBoxes );
+
+} // MOOV_Manager::ParseMemoryTree
+
+// =================================================================================================
+// MOOV_Manager::ParseNestedBoxes
+// ==============================
+//
+// Add the current level of child boxes to the parent node, recurse as appropriate.
+
+void MOOV_Manager::ParseNestedBoxes ( BoxNode * parentNode, const std::string & parentPath, bool ignoreMetaBoxes )
+{
+ ISOMedia::BoxInfo isoInfo;
+ const XMP_Uns8 * moovOrigin = &this->fullSubtree[0];
+ const XMP_Uns8 * childOrigin = moovOrigin + parentNode->offset + parentNode->headerSize;
+ const XMP_Uns8 * childLimit = childOrigin + parentNode->contentSize;
+ const XMP_Uns8 * nextChild;
+
+ parentNode->contentSize = 0; // Exclude nested box size.
+ if ( parentNode->boxType == ISOMedia::k_meta ) { // ! The 'meta' box is a FullBox.
+ parentNode->contentSize = 4;
+ childOrigin += 4;
+ }
+
+ for ( const XMP_Uns8 * currChild = childOrigin; currChild < childLimit; currChild = nextChild ) {
+
+ nextChild = ISOMedia::GetBoxInfo ( currChild, childLimit, &isoInfo );
+ if ( (isoInfo.boxType == 0) &&
+ (isoInfo.headerSize < 8) &&
+ (isoInfo.contentSize == 0) ) continue; // Skip trailing padding that QT sometimes writes.
+
+ XMP_Uns32 childOffset = (XMP_Uns32) (currChild - moovOrigin);
+ parentNode->children.push_back ( BoxNode ( childOffset, isoInfo.boxType, isoInfo.headerSize, (XMP_Uns32)isoInfo.contentSize ) );
+ BoxNode * newChild = &parentNode->children.back();
+
+ #if TraceParseMoovTree
+ size_t depth = (parentPath.size()+1) / 5;
+ for ( size_t i = 0; i < depth; ++i ) fprintf ( stderr, " " );
+ XMP_Uns32 be32 = MakeUns32BE ( newChild->boxType );
+ XMP_Uns32 addr32 = (XMP_Uns32) this->PickContentPtr ( *newChild );
+ fprintf ( stderr, " Parsed %s/%.4s, offset 0x%X, size %d, content @ 0x%X, BoxNode @ 0x%X\n",
+ parentPath.c_str(), &be32, newChild->offset, newChild->contentSize, addr32, newChild );
+ #endif
+
+ const char * pathSuffix = 0; // Set to non-zero for boxes of interest.
+ char buffer[6]; buffer[0] = 0;
+
+ switch ( isoInfo.boxType ) { // Want these boxes regardless of parent.
+ case ISOMedia::k_udta : pathSuffix = "/udta"; break;
+ case ISOMedia::k_meta : pathSuffix = "/meta"; break;
+ case ISOMedia::k_ilst : pathSuffix = "/ilst"; break;
+ case ISOMedia::k_trak : pathSuffix = "/trak"; break;
+ case ISOMedia::k_mdia : pathSuffix = "/mdia"; break;
+ case ISOMedia::k_minf : pathSuffix = "/minf"; break;
+ case ISOMedia::k_stbl : pathSuffix = "/stbl"; break;
+ }
+ if ( pathSuffix != 0 ) {
+ this->ParseNestedBoxes ( newChild, (parentPath + pathSuffix), ignoreMetaBoxes );
+ }
+
+ }
+
+} // MOOV_Manager::ParseNestedBoxes
+
+// =================================================================================================
+// MOOV_Manager::NoteChange
+// ========================
+
+void MOOV_Manager::NoteChange()
+{
+
+ this->moovNode.changed = true;
+
+} // MOOV_Manager::NoteChange
+
+// =================================================================================================
+// MOOV_Manager::SetBox
+// ====================
+//
+// Save the new data, set this box's changed flag, and set the top changed flag.
+
+void MOOV_Manager::SetBox ( BoxRef theBox, const void* dataPtr, XMP_Uns32 size )
+{
+ XMP_Enforce ( size < moovBoxSizeLimit );
+ BoxNode * node = (BoxNode*)theBox;
+
+ if ( node->contentSize == size ) {
+
+ XMP_Uns8 * oldContent = PickContentPtr ( *node );
+ if ( memcmp ( oldContent, dataPtr, size ) == 0 ) return; // No change.
+ memcpy ( oldContent, dataPtr, size ); // Update the old content in-place
+ this->moovNode.changed = true;
+
+ #if TraceUpdateMoovTree
+ XMP_Uns32 be32 = MakeUns32BE ( node->boxType );
+ fprintf ( stderr, "Updated '%.4s', parse offset 0x%X, same size\n", &be32, node->offset );
+ #endif
+
+ } else {
+
+ node->changedContent.assign ( size, 0 ); // Fill with 0's first to get the storage.
+ memcpy ( &node->changedContent[0], dataPtr, size );
+ node->contentSize = size;
+ node->changed = true;
+ this->moovNode.changed = true;
+
+ #if TraceUpdateMoovTree
+ XMP_Uns32 be32 = MakeUns32BE ( node->boxType );
+ XMP_Uns32 addr32 = (XMP_Uns32) this->PickContentPtr ( *node );
+ fprintf ( stderr, "Updated '%.4s', parse offset 0x%X, new size %d, new content @ 0x%X\n",
+ &be32, node->offset, node->contentSize, addr32 );
+ #endif
+
+ }
+
+} // MOOV_Manager::SetBox
+
+// =================================================================================================
+// MOOV_Manager::SetBox
+// ====================
+//
+// Like above, but create the path to the box if necessary.
+
+void MOOV_Manager::SetBox ( const char * boxPath, const void* dataPtr, XMP_Uns32 size )
+{
+ XMP_Enforce ( size < moovBoxSizeLimit );
+
+ size_t pathLen = strlen(boxPath);
+ XMP_Assert ( (pathLen >= 4) && XMP_LitNMatch ( boxPath, "moov", 4 ) );
+
+ const char * pathPtr = boxPath + 5; // Skip the "moov/" portion.
+ const char * pathEnd = boxPath + pathLen;
+
+ BoxRef parentRef = 0;
+ BoxRef currRef = &this->moovNode;
+
+ while ( pathPtr < pathEnd ) {
+
+ XMP_Assert ( (pathEnd - pathPtr) >= 4 );
+ XMP_Uns32 boxType = GetUns32BE ( pathPtr );
+ pathPtr += 5; // ! Don't care that the last step goes 1 too far.
+
+ parentRef = currRef;
+ currRef = this->GetTypeChild ( parentRef, boxType, 0 );
+ if ( currRef == 0 ) currRef = this->AddChildBox ( parentRef, boxType, 0, 0 );
+
+ }
+
+ this->SetBox ( currRef, dataPtr, size );
+
+} // MOOV_Manager::SetBox
+
+// =================================================================================================
+// MOOV_Manager::AddChildBox
+// =========================
+
+MOOV_Manager::BoxRef MOOV_Manager::AddChildBox ( BoxRef parentRef, XMP_Uns32 childType, const void* dataPtr, XMP_Uns32 size )
+{
+ BoxNode * parent = (BoxNode*)parentRef;
+ XMP_Assert ( parent != 0 );
+
+ parent->children.push_back ( BoxNode ( 0, childType, 0, 0 ) );
+ BoxNode * newNode = &parent->children.back();
+ this->SetBox ( newNode, dataPtr, size );
+
+ return newNode;
+
+} // MOOV_Manager::AddChildBox
+
+// =================================================================================================
+// MOOV_Manager::DeleteNthChild
+// ============================
+
+bool MOOV_Manager::DeleteNthChild ( BoxRef parentRef, size_t childIndex )
+{
+ BoxNode * parent = (BoxNode*)parentRef;
+
+ if ( childIndex >= parent->children.size() ) return false;
+
+ parent->children.erase ( parent->children.begin() + childIndex );
+ return true;
+
+} // MOOV_Manager::DeleteNthChild
+
+// =================================================================================================
+// MOOV_Manager::DeleteTypeChild
+// =============================
+
+bool MOOV_Manager::DeleteTypeChild ( BoxRef parentRef, XMP_Uns32 childType )
+{
+ BoxNode * parent = (BoxNode*)parentRef;
+
+ BoxListPos child = parent->children.begin();
+ BoxListPos limit = parent->children.end();
+
+ for ( ; child != limit; ++child ) {
+ if ( child->boxType == childType ) {
+ parent->children.erase ( child );
+ this->moovNode.changed = true;
+ return true;
+ }
+ }
+
+ return false;
+
+} // MOOV_Manager::DeleteTypeChild
+
+// =================================================================================================
+// MOOV_Manager::NewSubtreeSize
+// ============================
+//
+// Determine the new (changed) size of a subtree. Ignore 'free' and 'wide' boxes.
+
+XMP_Uns32 MOOV_Manager::NewSubtreeSize ( const BoxNode & node, const std::string & parentPath )
+{
+ XMP_Uns32 subtreeSize = 8 + node.contentSize; // All boxes will have 8 byte headers.
+
+ if ( (node.boxType == ISOMedia::k_free) || (node.boxType == ISOMedia::k_wide) ) {
+ }
+
+ for ( size_t i = 0, limit = node.children.size(); i < limit; ++i ) {
+
+ char suffix[6];
+ suffix[0] = '/';
+ PutUns32BE ( node.boxType, &suffix[1] );
+ suffix[5] = 0;
+ std::string nodePath = parentPath + suffix;
+
+ subtreeSize += this->NewSubtreeSize ( node.children[i], nodePath );
+ XMP_Enforce ( subtreeSize < moovBoxSizeLimit );
+
+ }
+
+ return subtreeSize;
+
+} // MOOV_Manager::NewSubtreeSize
+
+// =================================================================================================
+// MOOV_Manager::AppendNewSubtree
+// ==============================
+//
+// Append this node's header, content, and children. Because the 'meta' box is a FullBox with nested
+// boxes, there can be both content and children. Ignore 'free' and 'wide' boxes.
+
+#define IncrNewPtr(count) { newPtr += count; XMP_Enforce ( newPtr <= newEnd ); }
+
+#if TraceUpdateMoovTree
+ static XMP_Uns8 * newOrigin;
+#endif
+
+XMP_Uns8 * MOOV_Manager::AppendNewSubtree ( const BoxNode & node, const std::string & parentPath,
+ XMP_Uns8 * newPtr, XMP_Uns8 * newEnd )
+{
+ if ( (node.boxType == ISOMedia::k_free) || (node.boxType == ISOMedia::k_wide) ) {
+ }
+
+ XMP_Assert ( (node.boxType != ISOMedia::k_meta) ? (node.children.empty() || (node.contentSize == 0)) :
+ (node.children.empty() || (node.contentSize == 4)) );
+
+ XMP_Enforce ( (XMP_Uns32)(newEnd - newPtr) >= (8 + node.contentSize) );
+
+ #if TraceUpdateMoovTree
+ XMP_Uns32 be32 = MakeUns32BE ( node.boxType );
+ XMP_Uns32 newOffset = (XMP_Uns32) (newPtr - newOrigin);
+ XMP_Uns32 addr32 = (XMP_Uns32) this->PickContentPtr ( node );
+ fprintf ( stderr, " Appending %s/%.4s @ 0x%X, size %d, content @ 0x%X\n",
+ parentPath.c_str(), &be32, newOffset, node.contentSize, addr32 );
+ #endif
+
+ // Leave the size as 0 for now, append the type and content.
+
+ XMP_Uns8 * boxOrigin = newPtr; // Save origin to fill in the final size.
+ PutUns32BE ( node.boxType, (newPtr + 4) );
+ IncrNewPtr ( 8 );
+
+ if ( node.contentSize != 0 ) {
+ const XMP_Uns8 * content = PickContentPtr( node );
+ memcpy ( newPtr, content, node.contentSize );
+ IncrNewPtr ( node.contentSize );
+ }
+
+ // Append the nested boxes.
+
+ if ( ! node.children.empty() ) {
+
+ char suffix[6];
+ suffix[0] = '/';
+ PutUns32BE ( node.boxType, &suffix[1] );
+ suffix[5] = 0;
+ std::string nodePath = parentPath + suffix;
+
+ for ( size_t i = 0, limit = node.children.size(); i < limit; ++i ) {
+ newPtr = this->AppendNewSubtree ( node.children[i], nodePath, newPtr, newEnd );
+ }
+
+ }
+
+ // Fill in the final size.
+
+ PutUns32BE ( (XMP_Uns32)(newPtr - boxOrigin), boxOrigin );
+
+ return newPtr;
+
+} // MOOV_Manager::AppendNewSubtree
+
+// =================================================================================================
+// MOOV_Manager::UpdateMemoryTree
+// ==============================
+
+void MOOV_Manager::UpdateMemoryTree()
+{
+ if ( ! this->IsChanged() ) return;
+
+ XMP_Uns32 newSize = this->NewSubtreeSize ( this->moovNode, "" );
+ XMP_Enforce ( newSize < moovBoxSizeLimit );
+
+ RawDataBlock newData;
+ newData.assign ( newSize, 0 ); // Prefill with zeroes, can't append multiple items to a vector.
+
+ XMP_Uns8 * newPtr = &newData[0];
+ XMP_Uns8 * newEnd = newPtr + newSize;
+
+ #if TraceUpdateMoovTree
+ fprintf ( stderr, "Starting MOOV_Manager::UpdateMemoryTree\n" );
+ newOrigin = newPtr;
+ #endif
+
+ XMP_Uns8 * trueEnd = this->AppendNewSubtree ( this->moovNode, "", newPtr, newEnd );
+ XMP_Enforce ( trueEnd == newEnd );
+
+ this->fullSubtree.swap ( newData );
+ this->ParseMemoryTree ( this->fileMode );
+
+} // MOOV_Manager::UpdateMemoryTree
diff --git a/source/XMPFiles/FormatSupport/MOOV_Support.hpp b/source/XMPFiles/FormatSupport/MOOV_Support.hpp
new file mode 100644
index 0000000..3e19a9d
--- /dev/null
+++ b/source/XMPFiles/FormatSupport/MOOV_Support.hpp
@@ -0,0 +1,215 @@
+#ifndef __MOOV_Support_hpp__
+#define __MOOV_Support_hpp__ 1
+
+// =================================================================================================
+// ADOBE SYSTEMS INCORPORATED
+// Copyright 2009 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 "XMP_Environment.h" // ! This must be the first include.
+
+#include <vector>
+
+#include "XMP_Const.h"
+#include "XMPFiles_Impl.hpp"
+#include "EndianUtils.hpp"
+
+typedef vector<XMP_Uns8> RawDataBlock;
+
+#define moovBoxSizeLimit 100*1024*1024
+
+// =================================================================================================
+// MOOV_Manager
+// ============
+
+class MOOV_Manager {
+public:
+
+ // ---------------------------------------------------------------------------------------------
+ // Types and constants
+
+ enum { // Values for fileMode.
+ kFileIsNormalISO = 0, // A "normal" MPEG-4 file, no 'qt ' compatible brand.
+ kFileIsModernQT = 1, // Has an 'ftyp' box and 'qt ' compatible brand.
+ kFileIsTraditionalQT = 2 // Old QuickTime, no 'ftyp' box.
+ };
+
+ typedef const void * BoxRef; // Valid until a sibling or higher box is added or deleted.
+
+ struct BoxInfo {
+ XMP_Uns32 boxType; // In memory as native endian, compares work with ISOMedia::k_* constants.
+ XMP_Uns32 childCount; // ! A 'meta' box has both content (version/flags) and children!
+ XMP_Uns32 contentSize; // Does not include the size of nested boxes.
+ const XMP_Uns8 * content; // Null if contentSize is zero.
+ BoxInfo() : boxType(0), childCount(0), contentSize(0), content(0) {};
+ };
+
+ // ---------------------------------------------------------------------------------------------
+ // GetBox - Pick a box given a '/' separated list of box types. Picks the 1st of each type.
+ // GetNthChild - Pick the overall n-th child of the parent, zero based.
+ // GetTypeChild - Pick the first child of the given type.
+ // GetParsedOffset - Get the box's offset in the parsed tree, 0 if changed since parsing.
+ // GetHeaderSize - Get the box's header size in the parsed tree, 0 if changed since parsing.
+
+ BoxRef GetBox ( const char * boxPath, BoxInfo * info ) const;
+
+ BoxRef GetNthChild ( BoxRef parentRef, size_t childIndex, BoxInfo * info ) const;
+ BoxRef GetTypeChild ( BoxRef parentRef, XMP_Uns32 childType, BoxInfo * info ) const;
+
+ XMP_Uns32 GetParsedOffset ( BoxRef ref ) const;
+ XMP_Uns32 GetHeaderSize ( BoxRef ref ) const;
+
+ // ---------------------------------------------------------------------------------------------
+ // NoteChange - Note overall change, value was directly replaced.
+ // SetBox(ref) - Replace the content with a copy of the given data.
+ // SetBox(path) - Like above, but creating path to the box if necessary.
+ // AddChildBox - Add a child of the given type, using a copy of the given data (may be null)
+
+ void NoteChange();
+
+ void SetBox ( BoxRef theBox, const void* dataPtr, XMP_Uns32 size );
+ void SetBox ( const char * boxPath, const void* dataPtr, XMP_Uns32 size );
+
+ BoxRef AddChildBox ( BoxRef parentRef, XMP_Uns32 childType, const void * dataPtr, XMP_Uns32 size );
+
+ // ---------------------------------------------------------------------------------------------
+ // DeleteNthChild - Delete the overall n-th child, return true if there was one.
+ // DeleteTypeChild - Delete the first child of the given type, return true if there was one.
+
+ bool DeleteNthChild ( BoxRef parentRef, size_t childIndex );
+ bool DeleteTypeChild ( BoxRef parentRef, XMP_Uns32 childType );
+
+ // ---------------------------------------------------------------------------------------------
+
+ bool IsChanged() const { return this->moovNode.changed; };
+
+ // ---------------------------------------------------------------------------------------------
+ // The client is expected to fill in fullSubtree before calling ParseMemoryTree, and directly
+ // use fullSubtree after calling UpdateMemoryTree.
+ //
+ // IMPORTANT: We only support cases where the 'moov' subtree is significantly less than 4 GB, in
+ // particular with a threshold of probably 100 MB. This has 2 big impacts: we can safely use
+ // 32-bit offsets and sizes, and comfortably assume everything will fit in available heap space.
+
+ RawDataBlock fullSubtree; // The entire 'moov' box, straight from the file or from UpdateMemoryTree.
+
+ void ParseMemoryTree ( XMP_Uns8 fileMode );
+ void UpdateMemoryTree();
+
+ // ---------------------------------------------------------------------------------------------
+
+ #pragma pack (1) // ! These must match the file layout!
+
+ struct Content_mvhd_0 {
+ XMP_Uns32 vFlags; // 0
+ XMP_Uns32 creationTime; // 4
+ XMP_Uns32 modificationTime; // 8
+ XMP_Uns32 timescale; // 12
+ XMP_Uns32 duration; // 16
+ XMP_Int32 rate; // 20
+ XMP_Int16 volume; // 24
+ XMP_Uns16 pad_1; // 26
+ XMP_Uns32 pad_2, pad_3; // 28
+ XMP_Int32 matrix [9]; // 36
+ XMP_Uns32 preDef [6]; // 72
+ XMP_Uns32 nextTrackID; // 96
+ }; // 100
+
+ struct Content_mvhd_1 {
+ XMP_Uns32 vFlags; // 0
+ XMP_Uns64 creationTime; // 4
+ XMP_Uns64 modificationTime; // 12
+ XMP_Uns32 timescale; // 20
+ XMP_Uns64 duration; // 24
+ XMP_Int32 rate; // 32
+ XMP_Int16 volume; // 36
+ XMP_Uns16 pad_1; // 38
+ XMP_Uns32 pad_2, pad_3; // 40
+ XMP_Int32 matrix [9]; // 48
+ XMP_Uns32 preDef [6]; // 84
+ XMP_Uns32 nextTrackID; // 108
+ }; // 112
+
+ struct Content_hdlr { // An 'hdlr' box as defined by ISO 14496-12. Maps OK to the QuickTime box.
+ XMP_Uns32 versionFlags; // 0
+ XMP_Uns32 preDef; // 4
+ XMP_Uns32 handlerType; // 8
+ XMP_Uns32 reserved [3]; // 12
+ // Plus optional component name string, null terminated UTF-8.
+ }; // 24
+
+ struct Content_stsd_entry {
+ XMP_Uns32 entrySize; // 0
+ XMP_Uns32 format; // 4
+ XMP_Uns8 reserved_1 [6]; // 8
+ XMP_Uns16 dataRefIndex; // 14
+ XMP_Uns32 reserved_2; // 16
+ XMP_Uns32 flags; // 20
+ XMP_Uns32 timeScale; // 24
+ XMP_Uns32 frameDuration; // 28
+ XMP_Uns8 frameCount; // 32
+ XMP_Uns8 reserved_3; // 33
+ // Plus optional trailing ISO boxes.
+ }; // 34
+
+ struct Content_stsc_entry {
+ XMP_Uns32 firstChunkNumber; // 0
+ XMP_Uns32 samplesPerChunk; // 4
+ XMP_Uns32 sampleDescrID; // 8
+ }; // 12
+
+ // ---------------------------------------------------------------------------------------------
+
+ MOOV_Manager() : fileMode(0)
+ {
+ XMP_Assert ( sizeof ( Content_mvhd_0 ) == 100 ); // Make sure the structs really are packed.
+ XMP_Assert ( sizeof ( Content_mvhd_1 ) == 112 );
+ XMP_Assert ( sizeof ( Content_hdlr ) == 24 );
+ XMP_Assert ( sizeof ( Content_stsd_entry ) == 34 );
+ XMP_Assert ( sizeof ( Content_stsc_entry ) == 12 );
+ };
+
+ virtual ~MOOV_Manager() {};
+
+private:
+
+ struct BoxNode;
+ typedef std::vector<BoxNode> BoxList;
+ typedef BoxList::iterator BoxListPos;
+
+ struct BoxNode {
+ // ! Don't have a parent link, it will get destroyed by vector growth!
+
+ XMP_Uns32 offset; // The offset in the fullSubtree, 0 if not in the parse.
+ XMP_Uns32 boxType;
+ XMP_Uns32 headerSize; // The actual header size in the fullSubtree, 0 if not in the parse.
+ XMP_Uns32 contentSize; // The current content size, does not include nested boxes.
+ BoxList children;
+ RawDataBlock changedContent; // Might be empty even if changed is true.
+ bool changed; // If true, the content is in changedContent, else in fullSubtree.
+
+ BoxNode() : offset(0), boxType(0), headerSize(0), contentSize(0), changed(false) {};
+ BoxNode ( XMP_Uns32 _offset, XMP_Uns32 _boxType, XMP_Uns32 _headerSize, XMP_Uns32 _contentSize )
+ : offset(_offset), boxType(_boxType), headerSize(_headerSize), contentSize(_contentSize), changed(false) {};
+
+ };
+
+ XMP_Uns8 fileMode;
+ BoxNode moovNode;
+
+ void ParseNestedBoxes ( BoxNode * parentNode, const std::string & parentPath, bool ignoreMetaBoxes );
+
+ XMP_Uns8 * PickContentPtr ( const BoxNode & node ) const;
+ void FillBoxInfo ( const BoxNode & node, BoxInfo * info ) const;
+
+ XMP_Uns32 NewSubtreeSize ( const BoxNode & node, const std::string & parentPath );
+ XMP_Uns8 * AppendNewSubtree ( const BoxNode & node, const std::string & parentPath,
+ XMP_Uns8 * newPtr, XMP_Uns8 * newEnd );
+
+}; // MOOV_Manager
+
+#endif // __MOOV_Support_hpp__
diff --git a/source/XMPFiles/FormatSupport/MacScriptExtracts.h b/source/XMPFiles/FormatSupport/MacScriptExtracts.h
new file mode 100644
index 0000000..9856183
--- /dev/null
+++ b/source/XMPFiles/FormatSupport/MacScriptExtracts.h
@@ -0,0 +1,244 @@
+#ifndef __MacScriptExtracts__
+#define __MacScriptExtracts__
+
+// Extracts of script (smXyz) and language (langXyz) enums from Apple's old Script.h.
+// These are used to support "traditional" QuickTime metadata processing.
+
+/*
+ Script codes:
+ These specify a Mac OS encoding that is related to a FOND ID range.
+ Some of the encodings have several variants (e.g. for different localized systems)
+ which all share the same script code.
+ Not all of these script codes are currently supported by Apple software.
+ Notes:
+ - Script code 0 (smRoman) is also used (instead of smGreek) for the Greek encoding
+ in the Greek localized system.
+ - Script code 28 (smEthiopic) is also used for the Inuit encoding in the Inuktitut
+ system.
+*/
+enum {
+ smRoman = 0,
+ smJapanese = 1,
+ smTradChinese = 2, /* Traditional Chinese*/
+ smKorean = 3,
+ smArabic = 4,
+ smHebrew = 5,
+ smGreek = 6,
+ smCyrillic = 7,
+ smRSymbol = 8, /* Right-left symbol*/
+ smDevanagari = 9,
+ smGurmukhi = 10,
+ smGujarati = 11,
+ smOriya = 12,
+ smBengali = 13,
+ smTamil = 14,
+ smTelugu = 15,
+ smKannada = 16, /* Kannada/Kanarese*/
+ smMalayalam = 17,
+ smSinhalese = 18,
+ smBurmese = 19,
+ smKhmer = 20, /* Khmer/Cambodian*/
+ smThai = 21,
+ smLao = 22,
+ smGeorgian = 23,
+ smArmenian = 24,
+ smSimpChinese = 25, /* Simplified Chinese*/
+ smTibetan = 26,
+ smMongolian = 27,
+ smEthiopic = 28,
+ smGeez = 28, /* Synonym for smEthiopic*/
+ smCentralEuroRoman = 29, /* For Czech, Slovak, Polish, Hungarian, Baltic langs*/
+ smVietnamese = 30,
+ smExtArabic = 31, /* extended Arabic*/
+ smUninterp = 32 /* uninterpreted symbols, e.g. palette symbols*/
+};
+
+/* Extended script code for full Unicode input*/
+enum {
+ smUnicodeScript = 0x7E
+};
+
+/* Obsolete script code names (kept for backward compatibility):*/
+enum {
+ smChinese = 2, /* (Use smTradChinese or smSimpChinese)*/
+ smRussian = 7, /* Use smCyrillic*/
+ /* smMaldivian = 25: deleted, no code for Maldivian*/
+ smLaotian = 22, /* Use smLao */
+ smAmharic = 28, /* Use smEthiopic or smGeez*/
+ smSlavic = 29, /* Use smCentralEuroRoman*/
+ smEastEurRoman = 29, /* Use smCentralEuroRoman*/
+ smSindhi = 31, /* Use smExtArabic*/
+ smKlingon = 32
+};
+
+/*
+ Language codes:
+ These specify a language implemented using a particular Mac OS encoding.
+ Not all of these language codes are currently supported by Apple software.
+*/
+enum {
+ langEnglish = 0, /* smRoman script*/
+ langFrench = 1, /* smRoman script*/
+ langGerman = 2, /* smRoman script*/
+ langItalian = 3, /* smRoman script*/
+ langDutch = 4, /* smRoman script*/
+ langSwedish = 5, /* smRoman script*/
+ langSpanish = 6, /* smRoman script*/
+ langDanish = 7, /* smRoman script*/
+ langPortuguese = 8, /* smRoman script*/
+ langNorwegian = 9, /* (Bokmal) smRoman script*/
+ langHebrew = 10, /* smHebrew script*/
+ langJapanese = 11, /* smJapanese script*/
+ langArabic = 12, /* smArabic script*/
+ langFinnish = 13, /* smRoman script*/
+ langGreek = 14, /* Greek script (monotonic) using smRoman script code*/
+ langIcelandic = 15, /* modified smRoman/Icelandic script*/
+ langMaltese = 16, /* Roman script*/
+ langTurkish = 17, /* modified smRoman/Turkish script*/
+ langCroatian = 18, /* modified smRoman/Croatian script*/
+ langTradChinese = 19, /* Chinese (Mandarin) in traditional characters*/
+ langUrdu = 20, /* smArabic script*/
+ langHindi = 21, /* smDevanagari script*/
+ langThai = 22, /* smThai script*/
+ langKorean = 23 /* smKorean script*/
+};
+
+enum {
+ langLithuanian = 24, /* smCentralEuroRoman script*/
+ langPolish = 25, /* smCentralEuroRoman script*/
+ langHungarian = 26, /* smCentralEuroRoman script*/
+ langEstonian = 27, /* smCentralEuroRoman script*/
+ langLatvian = 28, /* smCentralEuroRoman script*/
+ langSami = 29, /* language of the Sami people of N. Scandinavia */
+ langFaroese = 30, /* modified smRoman/Icelandic script */
+ langFarsi = 31, /* modified smArabic/Farsi script*/
+ langPersian = 31, /* Synonym for langFarsi*/
+ langRussian = 32, /* smCyrillic script*/
+ langSimpChinese = 33, /* Chinese (Mandarin) in simplified characters*/
+ langFlemish = 34, /* smRoman script*/
+ langIrishGaelic = 35, /* smRoman or modified smRoman/Celtic script (without dot above) */
+ langAlbanian = 36, /* smRoman script*/
+ langRomanian = 37, /* modified smRoman/Romanian script*/
+ langCzech = 38, /* smCentralEuroRoman script*/
+ langSlovak = 39, /* smCentralEuroRoman script*/
+ langSlovenian = 40, /* modified smRoman/Croatian script*/
+ langYiddish = 41, /* smHebrew script*/
+ langSerbian = 42, /* smCyrillic script*/
+ langMacedonian = 43, /* smCyrillic script*/
+ langBulgarian = 44, /* smCyrillic script*/
+ langUkrainian = 45, /* modified smCyrillic/Ukrainian script*/
+ langByelorussian = 46, /* smCyrillic script*/
+ langBelorussian = 46 /* Synonym for langByelorussian */
+};
+
+enum {
+ langUzbek = 47, /* Cyrillic script*/
+ langKazakh = 48, /* Cyrillic script*/
+ langAzerbaijani = 49, /* Azerbaijani in Cyrillic script*/
+ langAzerbaijanAr = 50, /* Azerbaijani in Arabic script*/
+ langArmenian = 51, /* smArmenian script*/
+ langGeorgian = 52, /* smGeorgian script*/
+ langMoldavian = 53, /* smCyrillic script*/
+ langKirghiz = 54, /* Cyrillic script*/
+ langTajiki = 55, /* Cyrillic script*/
+ langTurkmen = 56, /* Cyrillic script*/
+ langMongolian = 57, /* Mongolian in smMongolian script*/
+ langMongolianCyr = 58, /* Mongolian in Cyrillic script*/
+ langPashto = 59, /* Arabic script*/
+ langKurdish = 60, /* smArabic script*/
+ langKashmiri = 61, /* Arabic script*/
+ langSindhi = 62, /* Arabic script*/
+ langTibetan = 63, /* smTibetan script*/
+ langNepali = 64, /* smDevanagari script*/
+ langSanskrit = 65, /* smDevanagari script*/
+ langMarathi = 66, /* smDevanagari script*/
+ langBengali = 67, /* smBengali script*/
+ langAssamese = 68, /* smBengali script*/
+ langGujarati = 69, /* smGujarati script*/
+ langPunjabi = 70 /* smGurmukhi script*/
+};
+
+enum {
+ langOriya = 71, /* smOriya script*/
+ langMalayalam = 72, /* smMalayalam script*/
+ langKannada = 73, /* smKannada script*/
+ langTamil = 74, /* smTamil script*/
+ langTelugu = 75, /* smTelugu script*/
+ langSinhalese = 76, /* smSinhalese script*/
+ langBurmese = 77, /* smBurmese script*/
+ langKhmer = 78, /* smKhmer script*/
+ langLao = 79, /* smLao script*/
+ langVietnamese = 80, /* smVietnamese script*/
+ langIndonesian = 81, /* smRoman script*/
+ langTagalog = 82, /* Roman script*/
+ langMalayRoman = 83, /* Malay in smRoman script*/
+ langMalayArabic = 84, /* Malay in Arabic script*/
+ langAmharic = 85, /* smEthiopic script*/
+ langTigrinya = 86, /* smEthiopic script*/
+ langOromo = 87, /* smEthiopic script*/
+ langSomali = 88, /* smRoman script*/
+ langSwahili = 89, /* smRoman script*/
+ langKinyarwanda = 90, /* smRoman script*/
+ langRuanda = 90, /* synonym for langKinyarwanda*/
+ langRundi = 91, /* smRoman script*/
+ langNyanja = 92, /* smRoman script*/
+ langChewa = 92, /* synonym for langNyanja*/
+ langMalagasy = 93, /* smRoman script*/
+ langEsperanto = 94 /* Roman script*/
+};
+
+enum {
+ langWelsh = 128, /* modified smRoman/Celtic script*/
+ langBasque = 129, /* smRoman script*/
+ langCatalan = 130, /* smRoman script*/
+ langLatin = 131, /* smRoman script*/
+ langQuechua = 132, /* smRoman script*/
+ langGuarani = 133, /* smRoman script*/
+ langAymara = 134, /* smRoman script*/
+ langTatar = 135, /* Cyrillic script*/
+ langUighur = 136, /* Arabic script*/
+ langDzongkha = 137, /* (lang of Bhutan) smTibetan script*/
+ langJavaneseRom = 138, /* Javanese in smRoman script*/
+ langSundaneseRom = 139, /* Sundanese in smRoman script*/
+ langGalician = 140, /* smRoman script*/
+ langAfrikaans = 141 /* smRoman script */
+};
+
+enum {
+ langBreton = 142, /* smRoman or modified smRoman/Celtic script */
+ langInuktitut = 143, /* Inuit script using smEthiopic script code */
+ langScottishGaelic = 144, /* smRoman or modified smRoman/Celtic script */
+ langManxGaelic = 145, /* smRoman or modified smRoman/Celtic script */
+ langIrishGaelicScript = 146, /* modified smRoman/Gaelic script (using dot above) */
+ langTongan = 147, /* smRoman script */
+ langGreekAncient = 148, /* Classical Greek, polytonic orthography */
+ langGreenlandic = 149, /* smRoman script */
+ langAzerbaijanRoman = 150, /* Azerbaijani in Roman script */
+ langNynorsk = 151 /* Norwegian Nyorsk in smRoman*/
+};
+
+enum {
+ langUnspecified = 32767 /* Special code for use in resources (such as 'itlm') */
+};
+
+/*
+ Obsolete language code names (kept for backward compatibility):
+ Misspelled, ambiguous, misleading, considered pejorative, archaic, etc.
+*/
+enum {
+ langPortugese = 8, /* Use langPortuguese*/
+ langMalta = 16, /* Use langMaltese*/
+ langYugoslavian = 18, /* (use langCroatian, langSerbian, etc.)*/
+ langChinese = 19, /* (use langTradChinese or langSimpChinese)*/
+ langLettish = 28, /* Use langLatvian */
+ langLapponian = 29, /* Use langSami*/
+ langLappish = 29, /* Use langSami*/
+ langSaamisk = 29, /* Use langSami */
+ langFaeroese = 30, /* Use langFaroese */
+ langIrish = 35, /* Use langIrishGaelic */
+ langGalla = 87, /* Use langOromo */
+ langAfricaans = 141, /* Use langAfrikaans */
+ langGreekPoly = 148 /* Use langGreekAncient*/
+};
+
+#endif /* __MacScriptExtracts__ */
diff --git a/source/XMPFiles/FormatSupport/PNG_Support.cpp b/source/XMPFiles/FormatSupport/PNG_Support.cpp
index f074729..988edda 100644
--- a/source/XMPFiles/FormatSupport/PNG_Support.cpp
+++ b/source/XMPFiles/FormatSupport/PNG_Support.cpp
@@ -1,6 +1,6 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2007 Adobe Systems Incorporated
+// Copyright 2008 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
diff --git a/source/XMPFiles/FormatSupport/PNG_Support.hpp b/source/XMPFiles/FormatSupport/PNG_Support.hpp
index 1a5aaae..b10f899 100644
--- a/source/XMPFiles/FormatSupport/PNG_Support.hpp
+++ b/source/XMPFiles/FormatSupport/PNG_Support.hpp
@@ -3,7 +3,7 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2007 Adobe Systems Incorporated
+// Copyright 2007 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
diff --git a/source/XMPFiles/FormatSupport/PSIR_FileWriter.cpp b/source/XMPFiles/FormatSupport/PSIR_FileWriter.cpp
index f8e6290..0e57b49 100644
--- a/source/XMPFiles/FormatSupport/PSIR_FileWriter.cpp
+++ b/source/XMPFiles/FormatSupport/PSIR_FileWriter.cpp
@@ -1,6 +1,6 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2006-2008 Adobe Systems Incorporated
+// Copyright 2006 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -20,26 +20,15 @@
// =================================================================================================
// IsMetadataImgRsrc
// =================
-//
-// The only image resources of possible interest as metadata have type '8BIM' and IDs:
-// 1008, 1020, 1028, 1034, 1035, 1036, 1058, 1060, 1061
static inline bool IsMetadataImgRsrc ( XMP_Uns16 id )
{
-
- if ( (id < 1008) || (id > 1061) ) {
- return false;
- } else if ( id >= 1058 ) {
- if ( id == 1059 ) return false;
- } else if ( id > 1036 ) {
- return false;
- } else if ( id > 1028 ) {
- if ( id < 1034 ) return false;
- } else if ( id < 1028 ) {
- if ( (id != 1008) && (id != 1020) ) return false;
- }
-
- return true;
+ if ( id == 0 ) return false;
+
+ int i;
+ for ( i = 0; id < kPSIR_MetadataIDs[i]; ++i ) {}
+ if ( id == kPSIR_MetadataIDs[i] ) return true;
+ return false;
} // IsMetadataImgRsrc
@@ -97,9 +86,9 @@ bool PSIR_FileWriter::GetImgRsrc ( XMP_Uns16 id, ImgRsrcInfo* info ) const
{
InternalRsrcMap::const_iterator rsrcPos = this->imgRsrcs.find ( id );
if ( rsrcPos == this->imgRsrcs.end() ) return false;
-
+
const InternalRsrcInfo & rsrcInfo = rsrcPos->second;
-
+
if ( info != 0 ) {
info->id = rsrcInfo.id;
info->dataLen = rsrcInfo.dataLen;
@@ -108,7 +97,7 @@ bool PSIR_FileWriter::GetImgRsrc ( XMP_Uns16 id, ImgRsrcInfo* info ) const
}
return true;
-
+
} // PSIR_FileWriter::GetImgRsrc
// =================================================================================================
@@ -126,7 +115,7 @@ void PSIR_FileWriter::SetImgRsrc ( XMP_Uns16 id, const void* clientPtr, XMP_Uns3
InternalRsrcMap::value_type mapValue ( id, InternalRsrcInfo ( id, length, this->fileParsed ) );
rsrcPos = this->imgRsrcs.insert ( rsrcPos, mapValue );
rsrcPtr = &rsrcPos->second;
-
+
} else {
rsrcPtr = &rsrcPos->second;
@@ -136,12 +125,12 @@ void PSIR_FileWriter::SetImgRsrc ( XMP_Uns16 id, const void* clientPtr, XMP_Uns3
(memcmp ( rsrcPtr->dataPtr, clientPtr, length ) == 0) ) {
return;
}
-
+
rsrcPtr->FreeData(); // Release any existing data allocation.
rsrcPtr->dataLen = length; // And this might be changing.
}
-
+
rsrcPtr->changed = true;
rsrcPtr->dataPtr = malloc ( length );
if ( rsrcPtr->dataPtr == 0 ) XMP_Throw ( "Out of memory", kXMPErr_NoMemory );
@@ -159,7 +148,7 @@ void PSIR_FileWriter::DeleteImgRsrc ( XMP_Uns16 id )
{
InternalRsrcMap::iterator rsrcPos = this->imgRsrcs.find ( id );
if ( rsrcPos == this->imgRsrcs.end() ) return; // Nothing to delete.
-
+
this->imgRsrcs.erase ( id );
this->changed = true;
if ( id != kPSIR_XMP ) this->legacyDeleted = true;
@@ -175,7 +164,7 @@ bool PSIR_FileWriter::IsLegacyChanged()
if ( ! this->changed ) return false;
if ( this->legacyDeleted ) return true;
-
+
InternalRsrcMap::iterator irPos = this->imgRsrcs.begin();
InternalRsrcMap::iterator irEnd = this->imgRsrcs.end();
@@ -185,7 +174,7 @@ bool PSIR_FileWriter::IsLegacyChanged()
}
return false; // Can get here if the XMP is the only thing changed.
-
+
} // PSIR_FileWriter::IsLegacyChanged
// =================================================================================================
@@ -199,7 +188,7 @@ void PSIR_FileWriter::ParseMemoryResources ( const void* data, XMP_Uns32 length,
if ( length == 0 ) return;
// Allocate space for the full in-memory data and copy it.
-
+
if ( ! copyData ) {
this->memContent = (XMP_Uns8*) data;
XMP_Assert ( ! this->ownedContent );
@@ -211,15 +200,15 @@ void PSIR_FileWriter::ParseMemoryResources ( const void* data, XMP_Uns32 length,
this->ownedContent = true;
}
this->memLength = length;
-
+
// Capture the info for all of the resources.
-
+
XMP_Uns8* psirPtr = this->memContent;
XMP_Uns8* psirEnd = psirPtr + length;
XMP_Uns8* psirLimit = psirEnd - kMinImgRsrcSize;
-
+
while ( psirPtr <= psirLimit ) {
-
+
XMP_Uns8* origin = psirPtr; // The beginning of this resource.
XMP_Uns32 type = GetUns32BE(psirPtr);
XMP_Uns16 id = GetUns16BE(psirPtr+4);
@@ -228,7 +217,7 @@ void PSIR_FileWriter::ParseMemoryResources ( const void* data, XMP_Uns32 length,
XMP_Uns8* namePtr = psirPtr;
XMP_Uns16 nameLen = namePtr[0]; // ! The length for the Pascal string, w/ room for "+2".
psirPtr += ((nameLen + 2) & 0xFFFE); // ! Round up to an even offset. Yes, +2!
-
+
if ( psirPtr > psirEnd-4 ) break; // Bad image resource. Throw instead?
XMP_Uns32 dataLen = GetUns32BE(psirPtr);
@@ -236,9 +225,9 @@ void PSIR_FileWriter::ParseMemoryResources ( const void* data, XMP_Uns32 length,
XMP_Uns32 dataOffset = (XMP_Uns32) ( psirPtr - this->memContent );
XMP_Uns8* nextRsrc = psirPtr + ((dataLen + 1) & 0xFFFFFFFEUL); // ! Round up to an even offset.
-
+
if ( (dataLen > length) || (psirPtr > psirEnd-dataLen) ) break; // Bad image resource. Throw instead?
-
+
if ( type == k8BIM ) {
InternalRsrcMap::value_type mapValue ( id, InternalRsrcInfo ( id, dataLen, kIsMemoryBased ) );
InternalRsrcMap::iterator rsrcPos = this->imgRsrcs.insert ( this->imgRsrcs.end(), mapValue );
@@ -252,9 +241,9 @@ void PSIR_FileWriter::ParseMemoryResources ( const void* data, XMP_Uns32 length,
XMP_Assert ( (rsrcLength & 1) == 0 );
this->otherRsrcs.push_back ( OtherRsrcInfo ( rsrcOffset, rsrcLength ) );
}
-
+
psirPtr = nextRsrc;
-
+
}
} // PSIR_FileWriter::ParseMemoryResources
@@ -266,28 +255,28 @@ void PSIR_FileWriter::ParseMemoryResources ( const void* data, XMP_Uns32 length,
void PSIR_FileWriter::ParseFileResources ( LFA_FileRef fileRef, XMP_Uns32 length )
{
bool ok;
-
+
this->DeleteExistingInfo();
this->fileParsed = true;
if ( length == 0 ) return;
-
+
// Parse the image resource block.
IOBuffer ioBuf;
ioBuf.filePos = LFA_Seek ( fileRef, 0, SEEK_CUR );
-
+
XMP_Int64 psirOrigin = ioBuf.filePos; // Need this to determine the resource data offsets.
XMP_Int64 fileEnd = ioBuf.filePos + length;
-
+
std::string rsrcPName;
-
+
while ( (ioBuf.filePos + (ioBuf.ptr - ioBuf.data)) < fileEnd ) {
-
+
ok = CheckFileSpace ( fileRef, &ioBuf, 12 ); // The minimal image resource takes 12 bytes.
if ( ! ok ) break; // Bad image resource. Throw instead?
XMP_Int64 thisRsrcPos = ioBuf.filePos + (ioBuf.ptr - ioBuf.data);
-
+
XMP_Uns32 type = GetUns32BE(ioBuf.ptr);
XMP_Uns16 id = GetUns16BE(ioBuf.ptr+4);
ioBuf.ptr += 6; // Advance to the resource name.
@@ -296,7 +285,7 @@ void PSIR_FileWriter::ParseFileResources ( LFA_FileRef fileRef, XMP_Uns32 length
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 ( nameLen > 0 ) rsrcPName.assign ( (char*)(ioBuf.ptr), paddedLen ); // ! Include the length byte and pad.
ioBuf.ptr += paddedLen; // Move to the data length.
@@ -317,7 +306,7 @@ void PSIR_FileWriter::ParseFileResources ( LFA_FileRef fileRef, XMP_Uns32 length
InternalRsrcMap::value_type mapValue ( id, InternalRsrcInfo ( id, dataLen, kIsFileBased ) );
InternalRsrcMap::iterator newRsrc = this->imgRsrcs.insert ( this->imgRsrcs.end(), mapValue );
InternalRsrcInfo* rsrcPtr = &newRsrc->second;
-
+
rsrcPtr->origOffset = (XMP_Uns32)thisDataPos;
if ( nameLen > 0 ) {
@@ -325,7 +314,7 @@ void PSIR_FileWriter::ParseFileResources ( LFA_FileRef fileRef, XMP_Uns32 length
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.
}
-
+
if ( ! IsMetadataImgRsrc ( id ) ) {
MoveToOffset ( fileRef, nextRsrcPos, &ioBuf );
continue;
@@ -346,9 +335,9 @@ void PSIR_FileWriter::ParseFileResources ( LFA_FileRef fileRef, XMP_Uns32 length
LFA_Read ( fileRef, (void*)rsrcPtr->dataPtr, dataLen );
FillBuffer ( fileRef, nextRsrcPos, &ioBuf );
}
-
+
}
-
+
#if 0
{
printf ( "\nPSIR_FileWriter::ParseFileResources, count = %d\n", this->imgRsrcs.size() );
@@ -362,7 +351,7 @@ void PSIR_FileWriter::ParseFileResources ( LFA_FileRef fileRef, XMP_Uns32 length
}
}
#endif
-
+
} // PSIR_FileWriter::ParseFileResources
// =================================================================================================
@@ -372,9 +361,9 @@ void PSIR_FileWriter::ParseFileResources ( LFA_FileRef fileRef, XMP_Uns32 length
XMP_Uns32 PSIR_FileWriter::UpdateMemoryResources ( void** dataPtr )
{
if ( this->fileParsed ) XMP_Throw ( "Not memory based", kXMPErr_EnforceFailure );
-
+
// Compute the size and allocate the new image resource block.
-
+
XMP_Uns32 newLength = 0;
InternalRsrcMap::iterator irPos = this->imgRsrcs.begin();
@@ -391,27 +380,27 @@ XMP_Uns32 PSIR_FileWriter::UpdateMemoryResources ( void** dataPtr )
newLength += ((nameLen + 2) & 0xFFFFFFFEUL); // ! Yes, +2.
}
}
-
+
for ( size_t i = 0; i < this->otherRsrcs.size(); ++i ) { // Add in the non-8BIM resources.
newLength += this->otherRsrcs[i].rsrcLength;
}
-
+
XMP_Uns8* newContent = (XMP_Uns8*) malloc ( newLength );
if ( newContent == 0 ) XMP_Throw ( "Out of memory", kXMPErr_NoMemory );
-
+
// Fill in the new image resource block.
-
+
XMP_Uns8* rsrcPtr = newContent;
for ( irPos = this->imgRsrcs.begin(); irPos != irEnd; ++irPos ) { // Do the 8BIM resources.
const InternalRsrcInfo & rsrcInfo = irPos->second;
-
+
PutUns32BE ( k8BIM, rsrcPtr );
rsrcPtr += 4;
PutUns16BE ( rsrcInfo.id, rsrcPtr );
rsrcPtr += 2;
-
+
if ( rsrcInfo.rsrcName == 0 ) {
PutUns16BE ( 0, rsrcPtr );
rsrcPtr += 2;
@@ -427,7 +416,7 @@ XMP_Uns32 PSIR_FileWriter::UpdateMemoryResources ( void** dataPtr )
++rsrcPtr;
}
}
-
+
PutUns32BE ( rsrcInfo.dataLen, rsrcPtr );
rsrcPtr += 4;
if ( rsrcInfo.dataLen > (newLength - (rsrcPtr - newContent)) ) {
@@ -451,17 +440,17 @@ XMP_Uns32 PSIR_FileWriter::UpdateMemoryResources ( void** dataPtr )
memcpy ( rsrcPtr, srcPtr, srcLen ); // AUDIT: Protected by the above check.
rsrcPtr += srcLen; // No need to pad, included in the original resource length.
}
-
+
XMP_Assert ( rsrcPtr == (newContent + newLength) );
-
+
// Parse the rebuilt image resource block. This is the easiest way to reconstruct the map.
-
+
this->ParseMemoryResources ( newContent, newLength, false );
this->ownedContent = (newLength > 0); // ! We really do own the new content, if not empty.
-
+
if ( dataPtr != 0 ) *dataPtr = newContent;
return newLength;
-
+
} // PSIR_FileWriter::UpdateMemoryResources
// =================================================================================================
@@ -473,20 +462,20 @@ XMP_Uns32 PSIR_FileWriter::UpdateFileResources ( LFA_FileRef sourceRef, LFA_File
{
IgnoreParam(ioBuf);
const XMP_Uns32 zero32 = 0;
-
+
const bool checkAbort = (abortProc != 0);
-
+
struct RsrcHeader {
XMP_Uns32 type;
XMP_Uns16 id;
};
XMP_Assert ( (offsetof(RsrcHeader,type) == 0) && (offsetof(RsrcHeader,id) == 4) );
-
+
if ( this->memParsed ) XMP_Throw ( "Not file based", kXMPErr_EnforceFailure );
-
+
XMP_Int64 destLenOffset = LFA_Seek ( destRef, 0, SEEK_CUR );
XMP_Uns32 destLength = 0;
-
+
LFA_Write ( destRef, &destLength, 4 ); // Write a placeholder for the new PSIR section length.
#if 0
@@ -502,7 +491,7 @@ XMP_Uns32 PSIR_FileWriter::UpdateFileResources ( LFA_FileRef sourceRef, LFA_File
}
}
#endif
-
+
// First write all of the '8BIM' resources from the map. Use the internal data if present, else
// copy the data from the file.
@@ -516,11 +505,11 @@ XMP_Uns32 PSIR_FileWriter::UpdateFileResources ( LFA_FileRef sourceRef, LFA_File
for ( ; rsrcPos != rsrcEnd; ++rsrcPos ) {
InternalRsrcInfo& currRsrc = rsrcPos->second;
-
+
outHeader.id = MakeUns16BE ( currRsrc.id );
LFA_Write ( destRef, &outHeader, 6 );
destLength += 6;
-
+
if ( currRsrc.rsrcName == 0 ) {
LFA_Write ( destRef, &zero32, 2 );
destLength += 2;
@@ -531,7 +520,7 @@ XMP_Uns32 PSIR_FileWriter::UpdateFileResources ( LFA_FileRef sourceRef, LFA_File
LFA_Write ( destRef, currRsrc.rsrcName, paddedLen );
destLength += paddedLen;
}
-
+
XMP_Uns32 dataLen = MakeUns32BE ( currRsrc.dataLen );
LFA_Write ( destRef, &dataLen, 4 );
// printf ( " #%d, offset %d (0x%X), dataLen %d\n", currRsrc.id, destLength, destLength, currRsrc.dataLen );
@@ -542,18 +531,18 @@ XMP_Uns32 PSIR_FileWriter::UpdateFileResources ( LFA_FileRef sourceRef, LFA_File
LFA_Seek ( sourceRef, currRsrc.origOffset, SEEK_SET );
LFA_Copy ( sourceRef, destRef, currRsrc.dataLen );
}
-
+
destLength += 4 + currRsrc.dataLen;
-
+
if ( (currRsrc.dataLen & 1) != 0 ) {
LFA_Write ( destRef, &zero32, 1 ); // ! Pad the data to an even length.
++destLength;
}
}
-
+
// Now write all of the non-8BIM resources. Copy the entire resource chunk from the source file.
-
+
// printf ( "\nPSIR_FileWriter::UpdateFileResources - other resources\n" );
for ( size_t i = 0; i < this->otherRsrcs.size(); ++i ) {
// printf ( " offset %d (0x%X), length %d",
@@ -570,7 +559,7 @@ XMP_Uns32 PSIR_FileWriter::UpdateFileResources ( LFA_FileRef sourceRef, LFA_File
XMP_Uns32 outLen = MakeUns32BE ( destLength );
LFA_Write ( destRef, &outLen, 4 );
LFA_Seek ( destRef, 0, SEEK_END );
-
+
// *** Not rebuilding the internal map - turns out we never want it, why pay for the I/O.
// *** Should probably add an option for all of these cases, memory and file based.
diff --git a/source/XMPFiles/FormatSupport/PSIR_MemoryReader.cpp b/source/XMPFiles/FormatSupport/PSIR_MemoryReader.cpp
index 592f3e2..c372dce 100644
--- a/source/XMPFiles/FormatSupport/PSIR_MemoryReader.cpp
+++ b/source/XMPFiles/FormatSupport/PSIR_MemoryReader.cpp
@@ -1,6 +1,6 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2006-2007 Adobe Systems Incorporated
+// Copyright 2006 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
diff --git a/source/XMPFiles/FormatSupport/PSIR_Support.hpp b/source/XMPFiles/FormatSupport/PSIR_Support.hpp
index a651bef..8b5c507 100644
--- a/source/XMPFiles/FormatSupport/PSIR_Support.hpp
+++ b/source/XMPFiles/FormatSupport/PSIR_Support.hpp
@@ -3,7 +3,7 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2006-2008 Adobe Systems Incorporated
+// Copyright 2006 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -38,8 +38,6 @@
/// This is the case for all of the derived classes, even though the memory based ones happen to
/// have all of the image resources in memory. Being "handled" means being in the image resource
/// map used by GetImgRsrc. The handled image resources are:
-/// \li 1008 - Ancient caption PString
-/// \li 1020 - Ancient caption string
/// \li 1028 - IPTC
/// \li 1034 - Copyrighted flag
/// \li 1035 - Copyright information URL
@@ -51,7 +49,7 @@
/// packaged in a DLL by themselves. They do not provide any form of C++ ABI protection.
// =================================================================================================
-
+
// These aren't inside PSIR_Manager because the static array can't be initialized there.
enum {
@@ -59,26 +57,23 @@ enum {
kMinImgRsrcSize = 4+2+2+4 // The minimum size for an image resource.
};
-enum { kPSIR_MetadataCount = 9 }; // 1 2 3 4 5 6 7 8 9
-static const XMP_Uns16 kPSIR_MetadataIDs[] = { 1008, 1020, 1028, 1034, 1035, 1036, 1058, 1060, 1061, 0 };
-
enum {
- kPSIR_OldCaptionPStr = 1008,
- kPSIR_OldCaption = 1020,
kPSIR_IPTC = 1028,
kPSIR_CopyrightFlag = 1034,
kPSIR_CopyrightURL = 1035,
- kPSIR_Thumbnail = 1036,
kPSIR_Exif = 1058,
kPSIR_XMP = 1060,
kPSIR_IPTCDigest = 1061
};
+enum { kPSIR_MetadataCount = 6 };
+static const XMP_Uns16 kPSIR_MetadataIDs[] = // ! Must be in descending order with 0 sentinel.
+ { kPSIR_IPTCDigest, kPSIR_XMP, kPSIR_Exif, kPSIR_CopyrightURL, kPSIR_CopyrightFlag, kPSIR_IPTC, 0 };
// =================================================================================================
// =================================================================================================
-// NOTE: Although Photoshop image resources have a type and ID, for metadatya we only care about
+// NOTE: Although Photoshop image resources have a type and ID, for metadatya we only care about
// those of type "8BIM". Resources of other types are preserved in files, but can't be individually
// accessed through the PSIR_Manager API.
@@ -101,10 +96,10 @@ public:
ImgRsrcInfo ( XMP_Uns16 _id, XMP_Uns32 _dataLen, void* _dataPtr, XMP_Uns32 _origOffset )
: id(_id), dataLen(_dataLen), dataPtr(_dataPtr), origOffset(_origOffset) {};
};
-
+
// The origOffset is the absolute file offset for file parses, the memory block offset for
// memory parses. It is the offset of the resource data portion, not the overall resource.
-
+
// ---------------------------------------------------------------------------------------------
// Get the information about a "handled" image resource. Returns false if the image resource is
// not handled, even if it was present in the parsed input.
@@ -113,20 +108,20 @@ public:
// ---------------------------------------------------------------------------------------------
// Set the value for an image resource. It can be any resource, even one not originally handled.
-
+
virtual void SetImgRsrc ( XMP_Uns16 id, const void* dataPtr, XMP_Uns32 length ) = 0;
// ---------------------------------------------------------------------------------------------
// Delete an image resource. Does nothing if the image resource does not exist.
-
+
virtual void DeleteImgRsrc ( XMP_Uns16 id ) = 0;
// ---------------------------------------------------------------------------------------------
// Determine if the image resources are changed.
-
+
virtual bool IsChanged() = 0;
virtual bool IsLegacyChanged() = 0;
-
+
// ---------------------------------------------------------------------------------------------
virtual void ParseMemoryResources ( const void* data, XMP_Uns32 length, bool copyData = true ) = 0;
@@ -138,7 +133,7 @@ public:
// by \c UpdateMemoryResources must be treated as read only. It exists until the PSIR_Manager
// destructor is called. UpdateMemoryResources can be used on a read-only instance to get the
// raw data block info.
-
+
virtual XMP_Uns32 UpdateMemoryResources ( void** dataPtr ) = 0;
virtual XMP_Uns32 UpdateFileResources ( LFA_FileRef sourceRef, LFA_FileRef destRef,
IOBuffer * ioBuf, XMP_AbortProc abortProc, void * abortArg ) = 0;
@@ -166,9 +161,9 @@ class PSIR_MemoryReader : public PSIR_Manager { // The leaf class for memory-bas
public:
bool GetImgRsrc ( XMP_Uns16 id, ImgRsrcInfo* info ) const;
-
+
void SetImgRsrc ( XMP_Uns16 id, const void* dataPtr, XMP_Uns32 length ) { NotAppropriate(); };
-
+
void DeleteImgRsrc ( XMP_Uns16 id ) { NotAppropriate(); };
bool IsChanged() { return false; };
@@ -184,7 +179,7 @@ public:
PSIR_MemoryReader() : ownedContent(false), psirLength(0), psirContent(0) {};
virtual ~PSIR_MemoryReader() { if ( this->ownedContent ) free ( this->psirContent ); };
-
+
private:
// Memory usage notes: PSIR_MemoryReader is for memory-based read-only usage (both apply). There
@@ -192,12 +187,12 @@ private:
// PSIR stream.
bool ownedContent;
-
+
XMP_Uns32 psirLength;
XMP_Uns8* psirContent;
-
+
typedef std::map<XMP_Uns16,ImgRsrcInfo> ImgRsrcMap;
-
+
ImgRsrcMap imgRsrcs;
static inline void NotAppropriate() { XMP_Throw ( "Not appropriate for PSIR_Reader", kXMPErr_InternalFailure ); };
@@ -225,10 +220,10 @@ public:
bool IsChanged() { return this->changed; };
bool IsLegacyChanged();
-
+
void ParseMemoryResources ( const void* data, XMP_Uns32 length, bool copyData = true );
void ParseFileResources ( LFA_FileRef file, XMP_Uns32 length );
-
+
XMP_Uns32 UpdateMemoryResources ( void** dataPtr );
XMP_Uns32 UpdateFileResources ( LFA_FileRef sourceRef, LFA_FileRef destRef,
IOBuffer * ioBuf, XMP_AbortProc abortProc, void * abortArg );
@@ -241,16 +236,16 @@ public:
// Memory usage notes: PSIR_FileWriter is for file-based OR read/write usage. For memory-based
// streams the dataPtr and rsrcName are initially into the stream, they become a separate
// allocation if changed. For file-based streams they are always a separate allocation.
-
+
// ! The working data values are always big endian, no matter where stored. It is the client's
// ! responsibility to flip them as necessary.
-
+
static const bool kIsFileBased = true; // For use in the InternalRsrcInfo constructor.
static const bool kIsMemoryBased = false;
struct InternalRsrcInfo {
public:
-
+
bool changed;
bool fileBased;
XMP_Uns16 id;
@@ -270,7 +265,7 @@ public:
if ( this->rsrcName != 0 ) { free ( this->rsrcName ); this->rsrcName = 0; }
}
}
-
+
InternalRsrcInfo ( XMP_Uns16 _id, XMP_Uns32 _dataLen, bool _fileBased )
: changed(false), fileBased(_fileBased), id(_id), dataLen(_dataLen), dataPtr(0),
origOffset(0), rsrcName(0) {};
@@ -289,9 +284,9 @@ public:
InternalRsrcInfo() // Hidden on purpose, fileBased must be properly set.
: changed(false), fileBased(false), id(0), dataLen(0), dataPtr(0), origOffset(0), rsrcName(0) {};
-
+
};
-
+
// The origOffset is the absolute file offset for file parses, the memory block offset for
// memory parses. It is the offset of the resource data portion, not the overall resource.
@@ -306,7 +301,7 @@ private:
typedef std::map<XMP_Uns16,InternalRsrcInfo> InternalRsrcMap;
InternalRsrcMap imgRsrcs;
-
+
struct OtherRsrcInfo { // For the resources of types other than "8BIM".
XMP_Uns32 rsrcOffset; // The offset of the resource origin, the type field.
XMP_Uns32 rsrcLength; // The full length of the resource, offset to the next resource.
@@ -315,7 +310,7 @@ private:
: rsrcOffset(_rsrcOffset), rsrcLength(_rsrcLength) {};
};
std::vector<OtherRsrcInfo> otherRsrcs;
-
+
void DeleteExistingInfo();
}; // PSIR_FileWriter
diff --git a/source/XMPFiles/FormatSupport/QuickTime_Support.cpp b/source/XMPFiles/FormatSupport/QuickTime_Support.cpp
index 8ba1221..31091ea 100644
--- a/source/XMPFiles/FormatSupport/QuickTime_Support.cpp
+++ b/source/XMPFiles/FormatSupport/QuickTime_Support.cpp
@@ -1,6 +1,6 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2008 Adobe Systems Incorporated
+// Copyright 2009 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -8,84 +8,1141 @@
// =================================================================================================
#include "XMP_Environment.h"
-#if ! ( XMP_64 || XMP_UNIXBuild) // Closes at very bottom.
+
+#if XMP_MacBuild
+ #include "Script.h"
+#else
+ #include "MacScriptExtracts.h"
+#endif
#include "QuickTime_Support.hpp"
-#if XMP_MacBuild
- #include <Movies.h>
-#elif XMP_WinBuild
- #include "QTML.h"
- #include "Movies.h"
+#include "UnicodeConversions.hpp"
+#include "UnicodeInlines.incl_cpp"
+#include "Reconcile_Impl.hpp"
+
+// =================================================================================================
+
+static const char * kMacRomanUTF8 [128] = { // UTF-8 mappings for MacRoman 80..FF.
+ "\xC3\x84", "\xC3\x85", "\xC3\x87", "\xC3\x89", "\xC3\x91", "\xC3\x96", "\xC3\x9C", "\xC3\xA1",
+ "\xC3\xA0", "\xC3\xA2", "\xC3\xA4", "\xC3\xA3", "\xC3\xA5", "\xC3\xA7", "\xC3\xA9", "\xC3\xA8",
+ "\xC3\xAA", "\xC3\xAB", "\xC3\xAD", "\xC3\xAC", "\xC3\xAE", "\xC3\xAF", "\xC3\xB1", "\xC3\xB3",
+ "\xC3\xB2", "\xC3\xB4", "\xC3\xB6", "\xC3\xB5", "\xC3\xBA", "\xC3\xB9", "\xC3\xBB", "\xC3\xBC",
+ "\xE2\x80\xA0", "\xC2\xB0", "\xC2\xA2", "\xC2\xA3", "\xC2\xA7", "\xE2\x80\xA2", "\xC2\xB6", "\xC3\x9F",
+ "\xC2\xAE", "\xC2\xA9", "\xE2\x84\xA2", "\xC2\xB4", "\xC2\xA8", "\xE2\x89\xA0", "\xC3\x86", "\xC3\x98",
+ "\xE2\x88\x9E", "\xC2\xB1", "\xE2\x89\xA4", "\xE2\x89\xA5", "\xC2\xA5", "\xC2\xB5", "\xE2\x88\x82", "\xE2\x88\x91",
+ "\xE2\x88\x8F", "\xCF\x80", "\xE2\x88\xAB", "\xC2\xAA", "\xC2\xBA", "\xCE\xA9", "\xC3\xA6", "\xC3\xB8",
+ "\xC2\xBF", "\xC2\xA1", "\xC2\xAC", "\xE2\x88\x9A", "\xC6\x92", "\xE2\x89\x88", "\xE2\x88\x86", "\xC2\xAB",
+ "\xC2\xBB", "\xE2\x80\xA6", "\xC2\xA0", "\xC3\x80", "\xC3\x83", "\xC3\x95", "\xC5\x92", "\xC5\x93",
+ "\xE2\x80\x93", "\xE2\x80\x94", "\xE2\x80\x9C", "\xE2\x80\x9D", "\xE2\x80\x98", "\xE2\x80\x99", "\xC3\xB7", "\xE2\x97\x8A",
+ "\xC3\xBF", "\xC5\xB8", "\xE2\x81\x84", "\xE2\x82\xAC", "\xE2\x80\xB9", "\xE2\x80\xBA", "\xEF\xAC\x81", "\xEF\xAC\x82",
+ "\xE2\x80\xA1", "\xC2\xB7", "\xE2\x80\x9A", "\xE2\x80\x9E", "\xE2\x80\xB0", "\xC3\x82", "\xC3\x8A", "\xC3\x81",
+ "\xC3\x8B", "\xC3\x88", "\xC3\x8D", "\xC3\x8E", "\xC3\x8F", "\xC3\x8C", "\xC3\x93", "\xC3\x94",
+ "\xEF\xA3\xBF", "\xC3\x92", "\xC3\x9A", "\xC3\x9B", "\xC3\x99", "\xC4\xB1", "\xCB\x86", "\xCB\x9C",
+ "\xC2\xAF", "\xCB\x98", "\xCB\x99", "\xCB\x9A", "\xC2\xB8", "\xCB\x9D", "\xCB\x9B", "\xCB\x87"
+};
+
+static const XMP_Uns32 kMacRomanCP [128] = { // Unicode codepoints for MacRoman 80..FF.
+ 0x00C4, 0x00C5, 0x00C7, 0x00C9, 0x00D1, 0x00D6, 0x00DC, 0x00E1,
+ 0x00E0, 0x00E2, 0x00E4, 0x00E3, 0x00E5, 0x00E7, 0x00E9, 0x00E8,
+ 0x00EA, 0x00EB, 0x00ED, 0x00EC, 0x00EE, 0x00EF, 0x00F1, 0x00F3,
+ 0x00F2, 0x00F4, 0x00F6, 0x00F5, 0x00FA, 0x00F9, 0x00FB, 0x00FC,
+ 0x2020, 0x00B0, 0x00A2, 0x00A3, 0x00A7, 0x2022, 0x00B6, 0x00DF,
+ 0x00AE, 0x00A9, 0x2122, 0x00B4, 0x00A8, 0x2260, 0x00C6, 0x00D8,
+ 0x221E, 0x00B1, 0x2264, 0x2265, 0x00A5, 0x00B5, 0x2202, 0x2211,
+ 0x220F, 0x03C0, 0x222B, 0x00AA, 0x00BA, 0x03A9, 0x00E6, 0x00F8,
+ 0x00BF, 0x00A1, 0x00AC, 0x221A, 0x0192, 0x2248, 0x2206, 0x00AB,
+ 0x00BB, 0x2026, 0x00A0, 0x00C0, 0x00C3, 0x00D5, 0x0152, 0x0153,
+ 0x2013, 0x2014, 0x201C, 0x201D, 0x2018, 0x2019, 0x00F7, 0x25CA,
+ 0x00FF, 0x0178, 0x2044, 0x20AC, 0x2039, 0x203A, 0xFB01, 0xFB02,
+ 0x2021, 0x00B7, 0x201A, 0x201E, 0x2030, 0x00C2, 0x00CA, 0x00C1,
+ 0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF, 0x00CC, 0x00D3, 0x00D4,
+ 0xF8FF, 0x00D2, 0x00DA, 0x00DB, 0x00D9, 0x0131, 0x02C6, 0x02DC, // ! U+F8FF is private use solid Apple icon.
+ 0x00AF, 0x02D8, 0x02D9, 0x02DA, 0x00B8, 0x02DD, 0x02DB, 0x02C7
+};
+
+// -------------------------------------------------------------------------------------------------
+
+static const XMP_Uns16 kMacLangToScript_0_94 [95] = {
+
+ /* langEnglish (0) */ smRoman,
+ /* langFrench (1) */ smRoman,
+ /* langGerman (2) */ smRoman,
+ /* langItalian (3) */ smRoman,
+ /* langDutch (4) */ smRoman,
+ /* langSwedish (5) */ smRoman,
+ /* langSpanish (6) */ smRoman,
+ /* langDanish (7) */ smRoman,
+ /* langPortuguese (8) */ smRoman,
+ /* langNorwegian (9) */ smRoman,
+
+ /* langHebrew (10) */ smHebrew,
+ /* langJapanese (11) */ smJapanese,
+ /* langArabic (12) */ smArabic,
+ /* langFinnish (13) */ smRoman,
+ /* langGreek (14) */ smRoman,
+ /* langIcelandic (15) */ smRoman,
+ /* langMaltese (16) */ smRoman,
+ /* langTurkish (17) */ smRoman,
+ /* langCroatian (18) */ smRoman,
+ /* langTradChinese (19) */ smTradChinese,
+
+ /* langUrdu (20) */ smArabic,
+ /* langHindi (21) */ smDevanagari,
+ /* langThai (22) */ smThai,
+ /* langKorean (23) */ smKorean,
+ /* langLithuanian (24) */ smCentralEuroRoman,
+ /* langPolish (25) */ smCentralEuroRoman,
+ /* langHungarian (26) */ smCentralEuroRoman,
+ /* langEstonian (27) */ smCentralEuroRoman,
+ /* langLatvian (28) */ smCentralEuroRoman,
+ /* langSami (29) */ kNoMacScript, // ! Not known, missing from Apple comments.
+
+ /* langFaroese (30) */ smRoman,
+ /* langFarsi (31) */ smArabic,
+ /* langRussian (32) */ smCyrillic,
+ /* langSimpChinese (33) */ smSimpChinese,
+ /* langFlemish (34) */ smRoman,
+ /* langIrishGaelic (35) */ smRoman,
+ /* langAlbanian (36) */ smRoman,
+ /* langRomanian (37) */ smRoman,
+ /* langCzech (38) */ smCentralEuroRoman,
+ /* langSlovak (39) */ smCentralEuroRoman,
+
+ /* langSlovenian (40) */ smRoman,
+ /* langYiddish (41) */ smHebrew,
+ /* langSerbian (42) */ smCyrillic,
+ /* langMacedonian (43) */ smCyrillic,
+ /* langBulgarian (44) */ smCyrillic,
+ /* langUkrainian (45) */ smCyrillic,
+ /* langBelorussian (46) */ smCyrillic,
+ /* langUzbek (47) */ smCyrillic,
+ /* langKazakh (48) */ smCyrillic,
+ /* langAzerbaijani (49) */ smCyrillic,
+
+ /* langAzerbaijanAr (50) */ smArabic,
+ /* langArmenian (51) */ smArmenian,
+ /* langGeorgian (52) */ smGeorgian,
+ /* langMoldavian (53) */ smCyrillic,
+ /* langKirghiz (54) */ smCyrillic,
+ /* langTajiki (55) */ smCyrillic,
+ /* langTurkmen (56) */ smCyrillic,
+ /* langMongolian (57) */ smMongolian,
+ /* langMongolianCyr (58) */ smCyrillic,
+ /* langPashto (59) */ smArabic,
+
+ /* langKurdish (60) */ smArabic,
+ /* langKashmiri (61) */ smArabic,
+ /* langSindhi (62) */ smArabic,
+ /* langTibetan (63) */ smTibetan,
+ /* langNepali (64) */ smDevanagari,
+ /* langSanskrit (65) */ smDevanagari,
+ /* langMarathi (66) */ smDevanagari,
+ /* langBengali (67) */ smBengali,
+ /* langAssamese (68) */ smBengali,
+ /* langGujarati (69) */ smGujarati,
+
+ /* langPunjabi (70) */ smGurmukhi,
+ /* langOriya (71) */ smOriya,
+ /* langMalayalam (72) */ smMalayalam,
+ /* langKannada (73) */ smKannada,
+ /* langTamil (74) */ smTamil,
+ /* langTelugu (75) */ smTelugu,
+ /* langSinhalese (76) */ smSinhalese,
+ /* langBurmese (77) */ smBurmese,
+ /* langKhmer (78) */ smKhmer,
+ /* langLao (79) */ smLao,
+
+ /* langVietnamese (80) */ smVietnamese,
+ /* langIndonesian (81) */ smRoman,
+ /* langTagalog (82) */ smRoman,
+ /* langMalayRoman (83) */ smRoman,
+ /* langMalayArabic (84) */ smArabic,
+ /* langAmharic (85) */ smEthiopic,
+ /* langTigrinya (86) */ smEthiopic,
+ /* langOromo (87) */ smEthiopic,
+ /* langSomali (88) */ smRoman,
+ /* langSwahili (89) */ smRoman,
+
+ /* langKinyarwanda (90) */ smRoman,
+ /* langRundi (91) */ smRoman,
+ /* langNyanja (92) */ smRoman,
+ /* langMalagasy (93) */ smRoman,
+ /* langEsperanto (94) */ smRoman
+
+}; // kMacLangToScript_0_94
+
+static const XMP_Uns16 kMacLangToScript_128_151 [24] = {
+
+ /* langWelsh (128) */ smRoman,
+ /* langBasque (129) */ smRoman,
+
+ /* langCatalan (130) */ smRoman,
+ /* langLatin (131) */ smRoman,
+ /* langQuechua (132) */ smRoman,
+ /* langGuarani (133) */ smRoman,
+ /* langAymara (134) */ smRoman,
+ /* langTatar (135) */ smCyrillic,
+ /* langUighur (136) */ smArabic,
+ /* langDzongkha (137) */ smTibetan,
+ /* langJavaneseRom (138) */ smRoman,
+ /* langSundaneseRom (139) */ smRoman,
+
+ /* langGalician (140) */ smRoman,
+ /* langAfrikaans (141) */ smRoman,
+ /* langBreton (142) */ smRoman,
+ /* langInuktitut (143) */ smEthiopic,
+ /* langScottishGaelic (144) */ smRoman,
+ /* langManxGaelic (145) */ smRoman,
+ /* langIrishGaelicScript (146) */ smRoman,
+ /* langTongan (147) */ smRoman,
+ /* langGreekAncient (148) */ smGreek,
+ /* langGreenlandic (149) */ smRoman,
+
+ /* langAzerbaijanRoman (150) */ smRoman,
+ /* langNynorsk (151) */ smRoman
+
+}; // kMacLangToScript_128_151
+
+// -------------------------------------------------------------------------------------------------
+
+static const char * kMacToXMPLang_0_94 [95] = {
+
+ /* langEnglish (0) */ "en",
+ /* langFrench (1) */ "fr",
+ /* langGerman (2) */ "de",
+ /* langItalian (3) */ "it",
+ /* langDutch (4) */ "nl",
+ /* langSwedish (5) */ "sv",
+ /* langSpanish (6) */ "es",
+ /* langDanish (7) */ "da",
+ /* langPortuguese (8) */ "pt",
+ /* langNorwegian (9) */ "no",
+
+ /* langHebrew (10) */ "he",
+ /* langJapanese (11) */ "ja",
+ /* langArabic (12) */ "ar",
+ /* langFinnish (13) */ "fi",
+ /* langGreek (14) */ "el",
+ /* langIcelandic (15) */ "is",
+ /* langMaltese (16) */ "mt",
+ /* langTurkish (17) */ "tr",
+ /* langCroatian (18) */ "hr",
+ /* langTradChinese (19) */ "zh",
+
+ /* langUrdu (20) */ "ur",
+ /* langHindi (21) */ "hi",
+ /* langThai (22) */ "th",
+ /* langKorean (23) */ "ko",
+ /* langLithuanian (24) */ "lt",
+ /* langPolish (25) */ "pl",
+ /* langHungarian (26) */ "hu",
+ /* langEstonian (27) */ "et",
+ /* langLatvian (28) */ "lv",
+ /* langSami (29) */ "se",
+
+ /* langFaroese (30) */ "fo",
+ /* langFarsi (31) */ "fa",
+ /* langRussian (32) */ "ru",
+ /* langSimpChinese (33) */ "zh",
+ /* langFlemish (34) */ "nl",
+ /* langIrishGaelic (35) */ "ga",
+ /* langAlbanian (36) */ "sq",
+ /* langRomanian (37) */ "ro",
+ /* langCzech (38) */ "cs",
+ /* langSlovak (39) */ "sk",
+
+ /* langSlovenian (40) */ "sl",
+ /* langYiddish (41) */ "yi",
+ /* langSerbian (42) */ "sr",
+ /* langMacedonian (43) */ "mk",
+ /* langBulgarian (44) */ "bg",
+ /* langUkrainian (45) */ "uk",
+ /* langBelorussian (46) */ "be",
+ /* langUzbek (47) */ "uz",
+ /* langKazakh (48) */ "kk",
+ /* langAzerbaijani (49) */ "az",
+
+ /* langAzerbaijanAr (50) */ "az",
+ /* langArmenian (51) */ "hy",
+ /* langGeorgian (52) */ "ka",
+ /* langMoldavian (53) */ "ro",
+ /* langKirghiz (54) */ "ky",
+ /* langTajiki (55) */ "tg",
+ /* langTurkmen (56) */ "tk",
+ /* langMongolian (57) */ "mn",
+ /* langMongolianCyr (58) */ "mn",
+ /* langPashto (59) */ "ps",
+
+ /* langKurdish (60) */ "ku",
+ /* langKashmiri (61) */ "ks",
+ /* langSindhi (62) */ "sd",
+ /* langTibetan (63) */ "bo",
+ /* langNepali (64) */ "ne",
+ /* langSanskrit (65) */ "sa",
+ /* langMarathi (66) */ "mr",
+ /* langBengali (67) */ "bn",
+ /* langAssamese (68) */ "as",
+ /* langGujarati (69) */ "gu",
+
+ /* langPunjabi (70) */ "pa",
+ /* langOriya (71) */ "or",
+ /* langMalayalam (72) */ "ml",
+ /* langKannada (73) */ "kn",
+ /* langTamil (74) */ "ta",
+ /* langTelugu (75) */ "te",
+ /* langSinhalese (76) */ "si",
+ /* langBurmese (77) */ "my",
+ /* langKhmer (78) */ "km",
+ /* langLao (79) */ "lo",
+
+ /* langVietnamese (80) */ "vi",
+ /* langIndonesian (81) */ "id",
+ /* langTagalog (82) */ "tl",
+ /* langMalayRoman (83) */ "ms",
+ /* langMalayArabic (84) */ "ms",
+ /* langAmharic (85) */ "am",
+ /* langTigrinya (86) */ "ti",
+ /* langOromo (87) */ "om",
+ /* langSomali (88) */ "so",
+ /* langSwahili (89) */ "sw",
+
+ /* langKinyarwanda (90) */ "rw",
+ /* langRundi (91) */ "rn",
+ /* langNyanja (92) */ "ny",
+ /* langMalagasy (93) */ "mg",
+ /* langEsperanto (94) */ "eo"
+
+}; // kMacToXMPLang_0_94
+
+static const char * kMacToXMPLang_128_151 [24] = {
+
+ /* langWelsh (128) */ "cy",
+ /* langBasque (129) */ "eu",
+
+ /* langCatalan (130) */ "ca",
+ /* langLatin (131) */ "la",
+ /* langQuechua (132) */ "qu",
+ /* langGuarani (133) */ "gn",
+ /* langAymara (134) */ "ay",
+ /* langTatar (135) */ "tt",
+ /* langUighur (136) */ "ug",
+ /* langDzongkha (137) */ "dz",
+ /* langJavaneseRom (138) */ "jv",
+ /* langSundaneseRom (139) */ "su",
+
+ /* langGalician (140) */ "gl",
+ /* langAfrikaans (141) */ "af",
+ /* langBreton (142) */ "br",
+ /* langInuktitut (143) */ "iu",
+ /* langScottishGaelic (144) */ "gd",
+ /* langManxGaelic (145) */ "gv",
+ /* langIrishGaelicScript (146) */ "ga",
+ /* langTongan (147) */ "to",
+ /* langGreekAncient (148) */ "", // ! Has no ISO 639-1 2 letter code.
+ /* langGreenlandic (149) */ "kl",
+
+ /* langAzerbaijanRoman (150) */ "az",
+ /* langNynorsk (151) */ "nn"
+
+}; // kMacToXMPLang_128_151
+
+// -------------------------------------------------------------------------------------------------
+
+#if XMP_WinBuild
+
+static UINT kMacScriptToWinCP[34] = {
+ /* smRoman (0) */ 10000, // There don't seem to be symbolic constants.
+ /* smJapanese (1) */ 10001, // From http://msdn.microsoft.com/en-us/library/dd317756(VS.85).aspx
+ /* smTradChinese (2) */ 10002,
+ /* smKorean (3) */ 10003,
+ /* smArabic (4) */ 10004,
+ /* smHebrew (5) */ 10005,
+ /* smGreek (6) */ 10006,
+ /* smCyrillic (7) */ 10007,
+ /* smRSymbol (8) */ 0,
+ /* smDevanagari (9) */ 0,
+ /* smGurmukhi (10) */ 0,
+ /* smGujarati (11) */ 0,
+ /* smOriya (12) */ 0,
+ /* smBengali (13) */ 0,
+ /* smTamil (14) */ 0,
+ /* smTelugu (15) */ 0,
+ /* smKannada (16) */ 0,
+ /* smMalayalam (17) */ 0,
+ /* smSinhalese (18) */ 0,
+ /* smBurmese (19) */ 0,
+ /* smKhmer (20) */ 0,
+ /* smThai (21) */ 10021,
+ /* smLao (22) */ 0,
+ /* smGeorgian (23) */ 0,
+ /* smArmenian (24) */ 0,
+ /* smSimpChinese (25) */ 10008,
+ /* smTibetan (26) */ 0,
+ /* smMongolian (27) */ 0,
+ /* smEthiopic (28) */ 0,
+ /* smGeez (28) */ 0,
+ /* smCentralEuroRoman (29) */ 10029,
+ /* smVietnamese (30) */ 0,
+ /* smExtArabic (31) */ 0,
+ /* smUninterp (32) */ 0
+}; // kMacScriptToWinCP
+
+static UINT kMacToWinCP_0_94 [95] = {
+
+ /* langEnglish (0) */ 0,
+ /* langFrench (1) */ 0,
+ /* langGerman (2) */ 0,
+ /* langItalian (3) */ 0,
+ /* langDutch (4) */ 0,
+ /* langSwedish (5) */ 0,
+ /* langSpanish (6) */ 0,
+ /* langDanish (7) */ 0,
+ /* langPortuguese (8) */ 0,
+ /* langNorwegian (9) */ 0,
+
+ /* langHebrew (10) */ 10005,
+ /* langJapanese (11) */ 10001,
+ /* langArabic (12) */ 10004,
+ /* langFinnish (13) */ 0,
+ /* langGreek (14) */ 10006,
+ /* langIcelandic (15) */ 10079,
+ /* langMaltese (16) */ 0,
+ /* langTurkish (17) */ 10081,
+ /* langCroatian (18) */ 10082,
+ /* langTradChinese (19) */ 10002,
+
+ /* langUrdu (20) */ 0,
+ /* langHindi (21) */ 0,
+ /* langThai (22) */ 10021,
+ /* langKorean (23) */ 10003,
+ /* langLithuanian (24) */ 0,
+ /* langPolish (25) */ 0,
+ /* langHungarian (26) */ 0,
+ /* langEstonian (27) */ 0,
+ /* langLatvian (28) */ 0,
+ /* langSami (29) */ 0,
+
+ /* langFaroese (30) */ 0,
+ /* langFarsi (31) */ 0,
+ /* langRussian (32) */ 0,
+ /* langSimpChinese (33) */ 10008,
+ /* langFlemish (34) */ 0,
+ /* langIrishGaelic (35) */ 0,
+ /* langAlbanian (36) */ 0,
+ /* langRomanian (37) */ 10010,
+ /* langCzech (38) */ 0,
+ /* langSlovak (39) */ 0,
+
+ /* langSlovenian (40) */ 0,
+ /* langYiddish (41) */ 0,
+ /* langSerbian (42) */ 0,
+ /* langMacedonian (43) */ 0,
+ /* langBulgarian (44) */ 0,
+ /* langUkrainian (45) */ 10017,
+ /* langBelorussian (46) */ 0,
+ /* langUzbek (47) */ 0,
+ /* langKazakh (48) */ 0,
+ /* langAzerbaijani (49) */ 0,
+
+ /* langAzerbaijanAr (50) */ 0,
+ /* langArmenian (51) */ 0,
+ /* langGeorgian (52) */ 0,
+ /* langMoldavian (53) */ 0,
+ /* langKirghiz (54) */ 0,
+ /* langTajiki (55) */ 0,
+ /* langTurkmen (56) */ 0,
+ /* langMongolian (57) */ 0,
+ /* langMongolianCyr (58) */ 0,
+ /* langPashto (59) */ 0,
+
+ /* langKurdish (60) */ 0,
+ /* langKashmiri (61) */ 0,
+ /* langSindhi (62) */ 0,
+ /* langTibetan (63) */ 0,
+ /* langNepali (64) */ 0,
+ /* langSanskrit (65) */ 0,
+ /* langMarathi (66) */ 0,
+ /* langBengali (67) */ 0,
+ /* langAssamese (68) */ 0,
+ /* langGujarati (69) */ 0,
+
+ /* langPunjabi (70) */ 0,
+ /* langOriya (71) */ 0,
+ /* langMalayalam (72) */ 0,
+ /* langKannada (73) */ 0,
+ /* langTamil (74) */ 0,
+ /* langTelugu (75) */ 0,
+ /* langSinhalese (76) */ 0,
+ /* langBurmese (77) */ 0,
+ /* langKhmer (78) */ 0,
+ /* langLao (79) */ 0,
+
+ /* langVietnamese (80) */ 0,
+ /* langIndonesian (81) */ 0,
+ /* langTagalog (82) */ 0,
+ /* langMalayRoman (83) */ 0,
+ /* langMalayArabic (84) */ 0,
+ /* langAmharic (85) */ 0,
+ /* langTigrinya (86) */ 0,
+ /* langOromo (87) */ 0,
+ /* langSomali (88) */ 0,
+ /* langSwahili (89) */ 0,
+
+ /* langKinyarwanda (90) */ 0,
+ /* langRundi (91) */ 0,
+ /* langNyanja (92) */ 0,
+ /* langMalagasy (93) */ 0,
+ /* langEsperanto (94) */ 0
+
+}; // kMacToWinCP_0_94
+
#endif
-namespace QuickTime_Support
+// =================================================================================================
+// GetMacScript
+// ============
+
+static XMP_Uns16 GetMacScript ( XMP_Uns16 macLang )
{
+ XMP_Uns16 macScript = kNoMacScript;
+
+ if ( macLang <= 94 ) {
+ macScript = kMacLangToScript_0_94[macLang];
+ } else if ( (128 <= macLang) && (macLang <= 151) ) {
+ macScript = kMacLangToScript_0_94[macLang-128];
+ }
+
+ return macScript;
+
+} // GetMacScript
+
+// =================================================================================================
+// GetWinCP
+// ========
+
+#if XMP_WinBuild
+
+static UINT GetWinCP ( XMP_Uns16 macLang )
+{
+ UINT winCP = 0;
+
+ if ( macLang <= 94 ) winCP = kMacToWinCP_0_94[macLang];
+
+ if ( winCP == 0 ) {
+ XMP_Uns16 macScript = GetMacScript ( macLang );
+ if ( macScript != kNoMacScript ) winCP = kMacScriptToWinCP[macScript];
+ }
+
+ return winCP;
+
+} // GetWinCP
+
+#endif
+
+// =================================================================================================
+// GetXMPLang
+// ==========
+
+static XMP_StringPtr GetXMPLang ( XMP_Uns16 macLang )
+{
+ XMP_StringPtr xmpLang = "";
+
+ if ( macLang <= 94 ) {
+ xmpLang = kMacToXMPLang_0_94[macLang];
+ } else if ( (128 <= macLang) && (macLang <= 151) ) {
+ xmpLang = kMacToXMPLang_128_151[macLang-128];
+ }
+
+ return xmpLang;
+
+} // GetXMPLang
+
+// =================================================================================================
+// GetMacLang
+// ==========
+
+static XMP_Uns16 GetMacLang ( std::string * xmpLang )
+{
+ if ( *xmpLang == "" ) return kNoMacLang;
+
+ size_t hyphenPos = xmpLang->find ( '-' ); // Make sure the XMP language is "generic".
+ if ( hyphenPos != std::string::npos ) xmpLang->erase ( hyphenPos );
+
+ for ( XMP_Uns16 i = 0; i <= 94; ++i ) { // Using std::map would be faster.
+ if ( *xmpLang == kMacToXMPLang_0_94[i] ) return i;
+ }
+
+ for ( XMP_Uns16 i = 128; i <= 151; ++i ) { // Using std::map would be faster.
+ if ( *xmpLang == kMacToXMPLang_128_151[i-128] ) return i;
+ }
+
+ return kNoMacLang;
+
+} // GetMacLang
+
+// =================================================================================================
+// MacRomanToUTF8
+// ==============
- bool sMainInitOK = false;
+static void MacRomanToUTF8 ( const std::string & macRoman, std::string * utf8 )
+{
+ utf8->erase();
+
+ for ( XMP_Uns8* chPtr = (XMP_Uns8*)macRoman.c_str(); *chPtr != 0; ++chPtr ) { // ! Don't trust that char is unsigned.
+ if ( *chPtr < 0x80 ) {
+ (*utf8) += (char)*chPtr;
+ } else {
+ (*utf8) += kMacRomanUTF8[(*chPtr)-0x80];
+ }
+ }
+
+} // MacRomanToUTF8
+
+// =================================================================================================
+// UTF8ToMacRoman
+// ==============
+
+static void UTF8ToMacRoman ( const std::string & utf8, std::string * macRoman )
+{
+ macRoman->erase();
+ bool inNonMRSpan = false;
+
+ for ( const XMP_Uns8 * chPtr = (XMP_Uns8*)utf8.c_str(); *chPtr != 0; ++chPtr ) { // ! Don't trust that char is unsigned.
+ if ( *chPtr < 0x80 ) {
+ (*macRoman) += (char)*chPtr;
+ inNonMRSpan = false;
+ } else {
+ XMP_Uns32 cp = GetCodePoint ( &chPtr );
+ --chPtr; // Make room for the loop increment.
+ XMP_Uns8 mr;
+ for ( mr = 0; (mr < 0x80) && (cp != kMacRomanCP[mr]); ++mr ) {}; // Using std::map would be faster.
+ if ( mr < 0x80 ) {
+ (*macRoman) += (char)(mr+0x80);
+ inNonMRSpan = false;
+ } else if ( ! inNonMRSpan ) {
+ (*macRoman) += '?';
+ inNonMRSpan = true;
+ }
+ }
+ }
+
+} // UTF8ToMacRoman
+
+// =================================================================================================
+// IsMacLangKnown
+// ==============
+
+static inline bool IsMacLangKnown ( XMP_Uns16 macLang )
+{
+ XMP_Uns16 macScript = GetMacScript ( macLang );
+ if ( macScript == kNoMacScript ) return false;
+
+ #if XMP_UNIXBuild
+ if ( macScript != smRoman ) return false;
+ #elif XMP_WinBuild
+ if ( GetWinCP(macLang) == 0 ) return false;
+ #endif
+
+ return true;
+
+} // IsMacLangKnown
+
+// =================================================================================================
+// ConvertToMacLang
+// ================
+
+bool ConvertToMacLang ( const std::string & utf8Value, XMP_Uns16 macLang, std::string * macValue )
+{
+ macValue->erase();
+ if ( macLang == kNoMacLang ) macLang = 0; // *** Zero is English, ought to use the "active" OS lang.
+ if ( ! IsMacLangKnown ( macLang ) ) return false;
+
+ #if XMP_MacBuild
+ XMP_Uns16 macScript = GetMacScript ( macLang );
+ ReconcileUtils::UTF8ToMacEncoding ( macScript, macLang, (XMP_Uns8*)utf8Value.c_str(), utf8Value.size(), macValue );
+ #elif XMP_UNIXBuild
+ UTF8ToMacRoman ( utf8Value, macValue );
+ #elif XMP_WinBuild
+ UINT winCP = GetWinCP ( macLang );
+ ReconcileUtils::UTF8ToWinEncoding ( winCP, (XMP_Uns8*)utf8Value.c_str(), utf8Value.size(), macValue );
+ #endif
+
+ return true;
+
+} // ConvertToMacLang
+
+// =================================================================================================
+// ConvertFromMacLang
+// ==================
+
+bool ConvertFromMacLang ( const std::string & macValue, XMP_Uns16 macLang, std::string * utf8Value )
+{
+ utf8Value->erase();
+ if ( ! IsMacLangKnown ( macLang ) ) return false;
+
+ #if XMP_MacBuild
+ XMP_Uns16 macScript = GetMacScript ( macLang );
+ ReconcileUtils::MacEncodingToUTF8 ( macScript, macLang, (XMP_Uns8*)macValue.c_str(), macValue.size(), utf8Value );
+ #elif XMP_UNIXBuild
+ MacRomanToUTF8 ( macValue, utf8Value );
+ #elif XMP_WinBuild
+ UINT winCP = GetWinCP ( macLang );
+ ReconcileUtils::WinEncodingToUTF8 ( winCP, (XMP_Uns8*)macValue.c_str(), macValue.size(), utf8Value );
+ #endif
+
+ return true;
- // =============================================================================================
+} // ConvertFromMacLang
- bool MainInitialize ( bool ignoreInit )
- {
- OSStatus err = noErr;
+// =================================================================================================
+// =================================================================================================
+// TradQT_Manager
+// =================================================================================================
+// =================================================================================================
+
+// =================================================================================================
+// TradQT_Manager::ParseCachedBoxes
+// ================================
+//
+// Parse the cached '©...' children of the 'moov'/'udta' box. The contents of each cached box are
+// a sequence of "mini boxes" analogous to XMP AltText arrays. Each mini box has a 16-bit size,
+// 16-bit language code, and text. The size is only the text size. The language codes are Macintosh
+// Script Manager langXyz codes. The text encoding is implicit in the language, see comments in
+// Apple's Script.h header.
+
+bool TradQT_Manager::ParseCachedBoxes ( const MOOV_Manager & moovMgr )
+{
+ MOOV_Manager::BoxInfo udtaInfo;
+ MOOV_Manager::BoxRef udtaRef = moovMgr.GetBox ( "moov/udta", &udtaInfo );
+ if ( udtaRef == 0 ) return false;
+
+ for ( XMP_Uns32 i = 0; i < udtaInfo.childCount; ++i ) {
+
+ MOOV_Manager::BoxInfo currInfo;
+ MOOV_Manager::BoxRef currRef = moovMgr.GetNthChild ( udtaRef, i, &currInfo );
+ if ( currRef == 0 ) break; // Sanity check, should not happen.
+ if ( (currInfo.boxType >> 24) != 0xA9 ) continue;
+ if ( currInfo.contentSize < 2+2+1 ) continue; // Want enough for a non-empty value.
+
+ InfoMapPos newInfo = this->parsedBoxes.insert ( this->parsedBoxes.end(),
+ InfoMap::value_type ( currInfo.boxType, ParsedBoxInfo ( currInfo.boxType ) ) );
+ std::vector<ValueInfo> * newValues = &newInfo->second.values;
+
+ XMP_Uns8 * boxPtr = (XMP_Uns8*) currInfo.content;
+ XMP_Uns8 * boxEnd = boxPtr + currInfo.contentSize;
+ XMP_Uns16 miniLen, macLang;
+
+ for ( ; boxPtr < boxEnd-4; boxPtr += miniLen ) {
+
+ miniLen = 4 + GetUns16BE ( boxPtr ); // ! Include header in local miniLen.
+ macLang = GetUns16BE ( boxPtr+2);
+ if ( (miniLen <= 4) || (miniLen > (boxEnd - boxPtr)) ) continue; // Ignore bad or empty values.
+
+ XMP_StringPtr valuePtr = (char*)(boxPtr+4);
+ size_t valueLen = miniLen - 4;
+
+ newValues->push_back ( ValueInfo() );
+ ValueInfo * newValue = &newValues->back();
+
+ // Only set the XMP language if the Mac script is known, i.e. the value can be converted.
+
+ newValue->macLang = macLang;
+ if ( IsMacLangKnown ( macLang ) ) newValue->xmpLang = GetXMPLang ( macLang );
+ newValue->macValue.assign ( valuePtr, valueLen );
+
+ }
+
+ }
+
+ return (! this->parsedBoxes.empty());
+
+} // TradQT_Manager::ParseCachedBoxes
+
+// =================================================================================================
+// TradQT_Manager::ImportSimpleXMP
+// ===============================
+//
+// Update a simple XMP property if the QT value looks newer.
+
+bool TradQT_Manager::ImportSimpleXMP ( XMP_Uns32 id, SXMPMeta * xmp, XMP_StringPtr ns, XMP_StringPtr prop ) const
+{
+
+ try {
+
+ InfoMapCPos infoPos = this->parsedBoxes.find ( id );
+ if ( infoPos == this->parsedBoxes.end() ) return false;
+ if ( infoPos->second.values.empty() ) return false;
+
+ std::string xmpValue, tempValue;
+ XMP_OptionBits flags;
+ bool xmpExists = xmp->GetProperty ( ns, prop, &xmpValue, &flags );
+ if ( xmpExists && (! XMP_PropIsSimple ( flags )) ) {
+ XMP_Throw ( "TradQT_Manager::ImportSimpleXMP - XMP property must be simple", kXMPErr_BadParam );
+ }
+
+ bool convertOK;
+ const ValueInfo & qtItem = infoPos->second.values[0]; // ! Use the first QT entry.
+
+ if ( xmpExists ) {
+ convertOK = ConvertToMacLang ( xmpValue, qtItem.macLang, &tempValue );
+ if ( ! convertOK ) return false; // throw?
+ if ( tempValue == qtItem.macValue ) return false; // QT value matches back converted XMP value.
+ }
+
+ convertOK = ConvertFromMacLang ( qtItem.macValue, qtItem.macLang, &tempValue );
+ if ( ! convertOK ) return false; // throw?
+ xmp->SetProperty ( ns, prop, tempValue.c_str() );
+ return true;
+
+ } catch ( ... ) {
+
+ return false; // Don't let one failure abort other imports.
+
+ }
+
+} // TradQT_Manager::ImportSimpleXMP
+
+// =================================================================================================
+// TradQT_Manager::ImportLangItem
+// ==============================
+//
+// Update a specific XMP AltText item if the QuickTime value looks newer.
+
+bool TradQT_Manager::ImportLangItem ( const ValueInfo & qtItem, SXMPMeta * xmp,
+ XMP_StringPtr ns, XMP_StringPtr langArray ) const
+{
+
+ try {
+
+ XMP_StringPtr genericLang, specificLang;
+ if ( qtItem.xmpLang[0] != 0 ) {
+ genericLang = qtItem.xmpLang;
+ specificLang = qtItem.xmpLang;
+ } else {
+ genericLang = "";
+ specificLang = "x-default";
+ }
+
+ bool convertOK;
+ std::string xmpValue, tempValue, actualLang;
+ bool xmpExists = xmp->GetLocalizedText ( ns, langArray, genericLang, specificLang, &actualLang, &xmpValue, 0 );
+ if ( xmpExists ) {
+ convertOK = ConvertToMacLang ( xmpValue, qtItem.macLang, &tempValue );
+ if ( ! convertOK ) return false; // throw?
+ if ( tempValue == qtItem.macValue ) return true; // QT value matches back converted XMP value.
+ specificLang = actualLang.c_str();
+ }
+
+ convertOK = ConvertFromMacLang ( qtItem.macValue, qtItem.macLang, &tempValue );
+ if ( ! convertOK ) return false; // throw?
+ xmp->SetLocalizedText ( ns, langArray, "", specificLang, tempValue.c_str() );
+ return true;
+
+ } catch ( ... ) {
+
+ return false; // Don't let one failure abort other imports.
+
+ }
+
+} // TradQT_Manager::ImportLangItem
+
+// =================================================================================================
+// TradQT_Manager::ImportLangAltXMP
+// ================================
+//
+// Update items in the XMP array if the QT value looks newer.
+
+bool TradQT_Manager::ImportLangAltXMP ( XMP_Uns32 id, SXMPMeta * xmp, XMP_StringPtr ns, XMP_StringPtr langArray ) const
+{
+
+ try {
+
+ InfoMapCPos infoPos = this->parsedBoxes.find ( id );
+ if ( infoPos == this->parsedBoxes.end() ) return false;
+ if ( infoPos->second.values.empty() ) return false; // Quit now if there are no values.
+
+ XMP_OptionBits flags;
+ bool xmpExists = xmp->GetProperty ( ns, langArray, 0, &flags );
+ if ( ! xmpExists ) {
+ xmp->SetProperty ( ns, langArray, 0, kXMP_PropArrayIsAltText );
+ } else if ( ! XMP_ArrayIsAltText ( flags ) ) {
+ XMP_Throw ( "TradQT_Manager::ImportLangAltXMP - XMP array must be AltText", kXMPErr_BadParam );
+ }
+
+ // Process all of the QT values, looking up the appropriate XMP language for each.
- if ( ignoreInit ) {
- sMainInitOK = true;
- return true;
+ bool haveMappings = false;
+ const ValueVector & qtValues = infoPos->second.values;
+
+ for ( size_t i = 0, limit = qtValues.size(); i < limit; ++i ) {
+ const ValueInfo & qtItem = qtValues[i];
+ if ( *qtItem.xmpLang == 0 ) continue; // Only do known mappings in the loop.
+ haveMappings |= this->ImportLangItem ( qtItem, xmp, ns, langArray );
+ }
+
+ if ( ! haveMappings ) {
+ // If nothing mapped, process the first QT item to XMP's "x-default".
+ haveMappings = this->ImportLangItem ( qtValues[0], xmp, ns, langArray ); // ! No xmpLang implies "x-default".
}
- #if XMP_WinBuild
- err = ::InitializeQTML ( 0 );
- #endif
+ return haveMappings;
+
+ } catch ( ... ) {
+
+ return false; // Don't let one failure abort other imports.
+
+ }
+
+} // TradQT_Manager::ImportLangAltXMP
+
+// =================================================================================================
+// TradQT_Manager::ExportSimpleXMP
+// ===============================
+//
+// Export a simple XMP value to the first existing QuickTime item. Delete all of the QT values if the
+// XMP value is empty or the XMP does not exist.
+
+// ! We don't create new QuickTime items since we don't know the language.
+
+void TradQT_Manager::ExportSimpleXMP ( XMP_Uns32 id, const SXMPMeta & xmp, XMP_StringPtr ns, XMP_StringPtr prop,
+ bool createWithZeroLang /* = false */ )
+{
+ std::string xmpValue, macValue;
+
+ InfoMapPos infoPos = this->parsedBoxes.find ( id );
+ bool qtFound = (infoPos != this->parsedBoxes.end()) && (! infoPos->second.values.empty());
- if ( err == noErr ) err = ::EnterMovies();
- if ( err == noErr ) sMainInitOK = true;
+ bool xmpFound = xmp.GetProperty ( ns, prop, &xmpValue, 0 );
+ if ( (! xmpFound) || (xmpValue.empty()) ) {
+ if ( qtFound ) {
+ this->parsedBoxes.erase ( infoPos );
+ this->changed = true;
+ }
+ return;
+ }
+
+ XMP_Assert ( xmpFound );
+ if ( ! qtFound ) {
+ if ( ! createWithZeroLang ) return;
+ infoPos = this->parsedBoxes.insert ( this->parsedBoxes.end(),
+ InfoMap::value_type ( id, ParsedBoxInfo ( id ) ) );
+ ValueVector * newValues = &infoPos->second.values;
+ newValues->push_back ( ValueInfo() );
+ ValueInfo * newValue = &newValues->back();
+ newValue->macLang = 0; // Happens to be langEnglish.
+ newValue->xmpLang = kMacToXMPLang_0_94[0];
+ this->changed = infoPos->second.changed = true;
+ }
- return sMainInitOK;
+ ValueInfo * qtItem = &infoPos->second.values[0]; // ! Use the first QT entry.
+ if ( ! IsMacLangKnown ( qtItem->macLang ) ) return;
- } // MainInitialize
+ bool convertOK = ConvertToMacLang ( xmpValue, qtItem->macLang, &macValue );
+ if ( convertOK && (macValue != qtItem->macValue) ) {
+ qtItem->macValue = macValue;
+ this->changed = infoPos->second.changed = true;
+ }
- // =============================================================================================
+} // TradQT_Manager::ExportSimpleXMP
- void MainTerminate ( bool ignoreInit )
- {
+// =================================================================================================
+// TradQT_Manager::ExportLangAltXMP
+// ================================
+//
+// Export XMP LangAlt array items to QuickTime, where the language and encoding mappings are known.
+// If there are no known language and encoding mappings, map the XMP default item to the first
+// existing QuickTime item.
+
+void TradQT_Manager::ExportLangAltXMP ( XMP_Uns32 id, const SXMPMeta & xmp, XMP_StringPtr ns, XMP_StringPtr langArray )
+{
+ bool haveMappings = false;
+ std::string xmpPath, xmpValue, xmpLang, macValue;
+
+ InfoMapPos infoPos = this->parsedBoxes.find ( id );
+ if ( infoPos == this->parsedBoxes.end() ) {
+ infoPos = this->parsedBoxes.insert ( this->parsedBoxes.end(),
+ InfoMap::value_type ( id, ParsedBoxInfo ( id ) ) );
+ }
+
+ ValueVector * qtValues = &infoPos->second.values;
+ XMP_Index xmpCount = xmp.CountArrayItems ( ns, langArray );
+ bool convertOK;
+
+ if ( xmpCount == 0 ) {
+ // Delete the "mappable" QuickTime items if there are no XMP values. Leave the others alone.
+ for ( int i = (int)qtValues->size()-1; i > 0; --i ) { // ! Need a signed index.
+ if ( (*qtValues)[i].xmpLang[0] != 0 ) {
+ qtValues->erase ( qtValues->begin() + i );
+ this->changed = infoPos->second.changed = true;
+ }
+ }
+ return;
+ }
+
+ // Go through the XMP and look for a related macLang QuickTime item to update or create.
+
+ for ( XMP_Index xmpIndex = 1; xmpIndex <= xmpCount; ++xmpIndex ) { // ! XMP index starts at 1!
+
+ SXMPUtils::ComposeArrayItemPath ( ns, langArray, xmpIndex, &xmpPath );
+ xmp.GetProperty ( ns, xmpPath.c_str(), &xmpValue, 0 );
+ xmp.GetQualifier ( ns, xmpPath.c_str(), kXMP_NS_XML, "lang", &xmpLang, 0 );
+ if ( xmpLang == "x-default" ) continue;
+
+ XMP_Uns16 macLang = GetMacLang ( &xmpLang );
+ if ( macLang == kNoMacLang ) continue;
+
+ size_t qtIndex, qtLimit;
+ for ( qtIndex = 0, qtLimit = qtValues->size(); qtIndex < qtLimit; ++qtIndex ) {
+ if ( (*qtValues)[qtIndex].macLang == macLang ) break;
+ }
- if ( ignoreInit ) return;
+ if ( qtIndex == qtLimit ) {
+ // No existing QuickTime item, try to create one.
+ if ( ! IsMacLangKnown ( macLang ) ) continue;
+ qtValues->push_back ( ValueInfo() );
+ qtIndex = qtValues->size() - 1;
+ ValueInfo * newItem = &((*qtValues)[qtIndex]);
+ newItem->macLang = macLang;
+ newItem->xmpLang = GetXMPLang ( macLang ); // ! Use the 2 character root language.
+ }
- ::ExitMovies();
+ ValueInfo * qtItem = &((*qtValues)[qtIndex]);
+ qtItem->marked = true; // Mark it whether updated or not, don't delete it in the next pass.
- #if XMP_WinBuild
- ::TerminateQTML();
- #endif
+ convertOK = ConvertToMacLang ( xmpValue, qtItem->macLang, &macValue );
+ if ( convertOK && (macValue != qtItem->macValue) ) {
+ qtItem->macValue.swap ( macValue ); // No need to make a copy.
+ haveMappings = true;
+ }
+
+ }
+ this->changed |= haveMappings;
+ infoPos->second.changed |= haveMappings;
- } // MainTerminate
+ // Go through the QuickTime items that are unmarked and delete those that have an xmpLang
+ // and known macScript. Clear all marks.
+
+ for ( int i = (int)qtValues->size()-1; i > 0; --i ) { // ! Need a signed index.
+ ValueInfo * qtItem = &((*qtValues)[i]);
+ if ( qtItem->marked ) {
+ qtItem->marked = false;
+ } else if ( (qtItem->xmpLang[0] != 0) && IsMacLangKnown ( qtItem->macLang ) ) {
+ qtValues->erase ( qtValues->begin() + i );
+ this->changed = infoPos->second.changed = true;
+ }
+ }
- // =============================================================================================
+ // If there were no mappings, export the XMP default item to the first QT item.
- bool ThreadInitialize()
- {
- OSStatus err = noErr;
+ if ( (! haveMappings) && (! qtValues->empty()) ) {
+
+ bool ok = xmp.GetLocalizedText ( ns, langArray, "", "x-default", 0, &xmpValue, 0 );
+ if ( ! ok ) return;
- #if XMP_MacBuild
- err = ::EnterMoviesOnThread ( 0 );
- #endif
+ ValueInfo * qtItem = &((*qtValues)[0]);
+ if ( ! IsMacLangKnown ( qtItem->macLang ) ) return;
- return (err == noErr);
+ convertOK = ConvertToMacLang ( xmpValue, qtItem->macLang, &macValue );
+ if ( convertOK && (macValue != qtItem->macValue) ) {
+ qtItem->macValue.swap ( macValue ); // No need to make a copy.
+ this->changed = infoPos->second.changed = true;
+ }
+
+ }
+
+} // TradQT_Manager::ExportLangAltXMP
+
+// =================================================================================================
+// TradQT_Manager::UpdateChangedBoxes
+// ==================================
+
+void TradQT_Manager::UpdateChangedBoxes ( MOOV_Manager * moovMgr )
+{
+ MOOV_Manager::BoxInfo udtaInfo;
+ MOOV_Manager::BoxRef udtaRef = moovMgr->GetBox ( "moov/udta", &udtaInfo );
+ XMP_Assert ( (udtaRef != 0) || (udtaInfo.childCount == 0) );
+
+ if ( udtaRef != 0 ) { // Might not have been a moov/udta box in the parse.
+
+ // First go through the moov/udta/©... children and delete those that are not in the map.
+
+ for ( XMP_Uns32 ordinal = udtaInfo.childCount; ordinal > 0; --ordinal ) { // ! Go backwards because of deletions.
+
+ MOOV_Manager::BoxInfo currInfo;
+ MOOV_Manager::BoxRef currRef = moovMgr->GetNthChild ( udtaRef, (ordinal-1), &currInfo );
+ if ( currRef == 0 ) break; // Sanity check, should not happen.
+ if ( (currInfo.boxType >> 24) != 0xA9 ) continue;
+ if ( currInfo.contentSize < 2+2+1 ) continue; // These were skipped by ParseCachedBoxes.
+
+ InfoMapPos infoPos = this->parsedBoxes.find ( currInfo.boxType );
+ if ( infoPos == this->parsedBoxes.end() ) moovMgr->DeleteNthChild ( udtaRef, (ordinal-1) );
- } // ThreadInitialize
+ }
- // =============================================================================================
+ }
+
+ // Now go through the changed items in the map and update them in the moov/udta subtree.
+
+ InfoMapCPos infoPos = this->parsedBoxes.begin();
+ InfoMapCPos infoEnd = this->parsedBoxes.end();
+
+ for ( ; infoPos != infoEnd; ++infoPos ) {
- void ThreadTerminate()
- {
+ ParsedBoxInfo * qtItem = (ParsedBoxInfo*) &infoPos->second;
+ if ( ! qtItem->changed ) continue;
+ qtItem->changed = false;
+
+ XMP_Uns32 qtTotalSize = 0; // Total size of the QT values, ignoring empty values.
+ for ( size_t i = 0, limit = qtItem->values.size(); i < limit; ++i ) {
+ if ( ! qtItem->values[i].macValue.empty() ) {
+ if ( qtItem->values[i].macValue.size() > 0xFFFF ) qtItem->values[i].macValue.erase ( 0xFFFF );
+ qtTotalSize += (XMP_Uns32)(2+2 + qtItem->values[i].macValue.size());
+ }
+ }
+
+ if ( udtaRef == 0 ) { // Might not have been a moov/udta box in the parse.
+ moovMgr->SetBox ( "moov/udta", 0, 0 );
+ udtaRef = moovMgr->GetBox ( "moov/udta", &udtaInfo );
+ XMP_Assert ( udtaRef != 0 );
+ }
+
+ if ( qtTotalSize == 0 ) {
- #if XMP_MacBuild
- ::ExitMoviesOnThread();
- #endif
+ moovMgr->DeleteTypeChild ( udtaRef, qtItem->id );
- } // ThreadTerminate
+ } else {
+
+ // Compose the complete box content.
+
+ RawDataBlock fullValue;
+ fullValue.assign ( qtTotalSize, 0 );
+ XMP_Uns8 * valuePtr = &fullValue[0];
+
+ for ( size_t i = 0, limit = qtItem->values.size(); i < limit; ++i ) {
+ XMP_Assert ( qtItem->values[i].macValue.size() <= 0xFFFF );
+ XMP_Uns16 textSize = (XMP_Uns16)qtItem->values[i].macValue.size();
+ if ( textSize == 0 ) continue;
+ PutUns16BE ( textSize, valuePtr ); valuePtr += 2;
+ PutUns16BE ( qtItem->values[i].macLang, valuePtr ); valuePtr += 2;
+ memcpy ( valuePtr, qtItem->values[i].macValue.c_str(), textSize ); valuePtr += textSize;
+ }
+
+ // Look for an existing box to update, else add a new one.
-} // namespace QuickTime_Support
+ MOOV_Manager::BoxInfo itemInfo;
+ MOOV_Manager::BoxRef itemRef = moovMgr->GetTypeChild ( udtaRef, qtItem->id, &itemInfo );
+
+ if ( itemRef != 0 ) {
+ moovMgr->SetBox ( itemRef, &fullValue[0], qtTotalSize );
+ } else {
+ moovMgr->AddChildBox ( udtaRef, qtItem->id, &fullValue[0], qtTotalSize );
+ }
+
+ }
-#endif
+ }
+
+} // TradQT_Manager::UpdateChangedBoxes
+
+// =================================================================================================
diff --git a/source/XMPFiles/FormatSupport/QuickTime_Support.hpp b/source/XMPFiles/FormatSupport/QuickTime_Support.hpp
index 24f903d..160dfc8 100644
--- a/source/XMPFiles/FormatSupport/QuickTime_Support.hpp
+++ b/source/XMPFiles/FormatSupport/QuickTime_Support.hpp
@@ -3,27 +3,101 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2007 Adobe Systems Incorporated
+// Copyright 2009 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 "XMP_Environment.h" // ! This must be the first include.
-#if ! ( XMP_64 || XMP_UNIXBuild) // Closes at very bottom.
+#include "XMP_Environment.h" // ! This must be the first include.
-namespace QuickTime_Support
-{
- extern bool sMainInitOK;
+#include <string>
+#include <vector>
+#include <map>
+
+#include "XMPFiles_Impl.hpp"
+#include "LargeFileAccess.hpp"
+#include "ISOBaseMedia_Support.hpp"
+#include "MOOV_Support.hpp"
+
+// =================================================================================================
+// =================================================================================================
+
+// =================================================================================================
+// TradQT_Manager
+// ==============
+
+// Support for selected traditional QuickTime metadata items. The supported items are the children
+// of the 'moov'/'udta' box whose type begins with 0xA9, a MacRoman copyright symbol. Each of these
+// is a box whose contents are a sequence of "mini boxes" analogous to XMP AltText arrays. Each mini
+// box has a 16-bit size, 16-bit language code, and text. The language code values are the old
+// Macintosh Script Manager langXyz codes, the text encoding is implicit, see Mac Script.h.
+
+enum { // List of recognized items from the QuickTime 'moov'/'udta' box.
+ // These items are defined by Adobe.
+ kQTilst_Reel = 0xA952454CUL, // '©REL'
+ kQTilst_Timecode = 0xA954494DUL, // '©TIM'
+ kQTilst_TimeScale = 0xA9545343UL, // '©TSC'
+ kQTilst_TimeSize = 0xA954535AUL // '©TSZ'
+};
+
+enum {
+ kNoMacLang = 0xFFFF,
+ kNoMacScript = 0xFFFF
+};
+
+extern bool ConvertToMacLang ( const std::string & utf8Value, XMP_Uns16 macLang, std::string * macValue );
+extern bool ConvertFromMacLang ( const std::string & macValue, XMP_Uns16 macLang, std::string * utf8Value );
+
+class TradQT_Manager {
+public:
+
+ TradQT_Manager() : changed(false) {};
+
+ bool ParseCachedBoxes ( const MOOV_Manager & moovMgr );
+
+ bool ImportSimpleXMP ( XMP_Uns32 id, SXMPMeta * xmp, XMP_StringPtr ns, XMP_StringPtr prop ) const;
+ bool ImportLangAltXMP ( XMP_Uns32 id, SXMPMeta * xmp, XMP_StringPtr ns, XMP_StringPtr langArray ) const;
+
+ void ExportSimpleXMP ( XMP_Uns32 id, const SXMPMeta & xmp, XMP_StringPtr ns, XMP_StringPtr prop,
+ bool createWithZeroLang = false );
+ void ExportLangAltXMP ( XMP_Uns32 id, const SXMPMeta & xmp, XMP_StringPtr ns, XMP_StringPtr langArray );
+
+ bool IsChanged() const { return this->changed; };
- bool MainInitialize ( bool ignoreInit ); // For the main thread.
- void MainTerminate ( bool ignoreInit );
+ void UpdateChangedBoxes ( MOOV_Manager * moovMgr );
+
+private:
+
+ struct ValueInfo {
+ bool marked;
+ XMP_Uns16 macLang;
+ XMP_StringPtr xmpLang; // ! Only set if macLang is known, i.e. the value can be converted.
+ std::string macValue;
+ ValueInfo() : marked(false), macLang(kNoMacLang), xmpLang("") {};
+ };
+ typedef std::vector<ValueInfo> ValueVector;
+ typedef ValueVector::iterator ValueInfoPos;
+ typedef ValueVector::const_iterator ValueInfoCPos;
+
+ struct ParsedBoxInfo {
+ XMP_Uns32 id;
+ ValueVector values;
+ bool changed;
+ ParsedBoxInfo() : id(0), changed(false) {};
+ ParsedBoxInfo ( XMP_Uns32 _id ) : id(_id), changed(false) {};
+ };
+
+ typedef std::map < XMP_Uns32, ParsedBoxInfo > InfoMap; // Metadata item kind and content info.
+ typedef InfoMap::iterator InfoMapPos;
+ typedef InfoMap::const_iterator InfoMapCPos;
- bool ThreadInitialize(); // For background threads.
- void ThreadTerminate();
+ InfoMap parsedBoxes;
+ bool changed;
+
+ bool ImportLangItem ( const ValueInfo & qtItem, SXMPMeta * xmp, XMP_StringPtr ns, XMP_StringPtr langArray ) const;
-} // namespace QuickTime_Support
+}; // TradQT_Manager
-#endif // XMP_64 || XMP_UNIXBuild
#endif // __QuickTime_Support_hpp__
diff --git a/source/XMPFiles/FormatSupport/RIFF.cpp b/source/XMPFiles/FormatSupport/RIFF.cpp
new file mode 100644
index 0000000..3992edd
--- /dev/null
+++ b/source/XMPFiles/FormatSupport/RIFF.cpp
@@ -0,0 +1,879 @@
+// =================================================================================================
+// ADOBE SYSTEMS INCORPORATED
+// Copyright 2009 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.
+// =================================================================================================
+
+// must have access to handler class fields...
+#include "RIFF.hpp"
+#include "RIFF_Support.hpp"
+#include "RIFF_Handler.hpp"
+
+using namespace RIFF;
+
+namespace RIFF {
+
+// GENERAL STATIC FUNCTIONS ////////////////////////////////////////
+
+Chunk* getChunk ( ContainerChunk* parent, RIFF_MetaHandler* handler )
+{
+ LFA_FileRef file = handler->parent->fileRef;
+ XMP_Uns8 level = handler->level;
+ XMP_Uns32 peek = LFA_PeekUns32_LE( file );
+
+ if ( level == 0 )
+ {
+ XMP_Validate( peek == kChunk_RIFF, "expected RIFF chunk not found", kXMPErr_BadFileFormat );
+ XMP_Enforce( parent == NULL );
+ }
+ else
+ {
+ XMP_Validate( peek != kChunk_RIFF, "unexpected RIFF chunk below top-level", kXMPErr_BadFileFormat );
+ XMP_Enforce( parent != NULL );
+ }
+
+ switch( peek )
+ {
+ case kChunk_RIFF:
+ return new ContainerChunk( parent, handler );
+ case kChunk_LIST:
+ {
+ if ( level != 1 ) break; // only care on this level
+
+ // look further (beyond 4+4 = beyond id+size) to check on relevance
+ LFA_Seek( file, 8, SEEK_CUR );
+ XMP_Uns32 containerType = LFA_PeekUns32_LE( file );
+ LFA_Seek( file, -8, SEEK_CUR );
+
+ bool isRelevantList = ( containerType== kType_INFO || containerType == kType_Tdat );
+ if ( !isRelevantList ) break;
+
+ return new ContainerChunk( parent, handler );
+ }
+ case kChunk_XMP:
+ if ( level != 1 ) break; // ignore on inappropriate levels (might be compound metadata?)
+ return new XMPChunk( parent, handler );
+ case kChunk_DISP:
+ {
+ if ( level != 1 ) break; // only care on this level
+ // peek even further to see if type is 0x001 and size is reasonable
+ LFA_Seek( file , 4, SEEK_CUR ); // jump DISP
+ XMP_Uns32 dispSize = LFA_ReadUns32_LE( file );
+ XMP_Uns32 dispType = LFA_ReadUns32_LE( file );
+ LFA_Seek( file , -12, SEEK_CUR); // rewind, be in front of chunkID again
+
+ // only take as a relevant disp if both criteria met,
+ // otherwise treat as generic chunk!
+ if ( (dispType == 0x0001) && ( dispSize < 256 * 1024 ) )
+ {
+ ValueChunk* r = new ValueChunk( parent, handler );
+ handler->dispChunk = r;
+ return r;
+ }
+ break; // treat as irrelevant (non-0x1) DISP chunks as generic chunk
+ }
+ case kChunk_bext:
+ {
+ if ( level != 1 ) break; // only care on this level
+ // store for now in a value chunk
+ ValueChunk* r = new ValueChunk( parent, handler );
+ handler->bextChunk = r;
+ return r;
+ }
+ case kChunk_PrmL:
+ {
+ if ( level != 1 ) break; // only care on this level
+ ValueChunk* r = new ValueChunk( parent, handler );
+ handler->prmlChunk = r;
+ return r;
+ }
+ case kChunk_Cr8r:
+ {
+ if ( level != 1 ) break; // only care on this level
+ ValueChunk* r = new ValueChunk( parent, handler );
+ handler->cr8rChunk = r;
+ return r;
+ }
+ case kChunk_JUNQ:
+ case kChunk_JUNK:
+ {
+ JunkChunk* r = new JunkChunk( parent, handler );
+ return r;
+ }
+ }
+ // this "default:" section must be ouside switch bracket, to be
+ // reachable by all those break statements above:
+
+
+ // digest 'valuable' container chunks: LIST:INFO, LIST:Tdat
+ bool insideRelevantList = ( level==2 && parent->id == kChunk_LIST
+ && ( parent->containerType== kType_INFO || parent->containerType == kType_Tdat ));
+
+ if ( insideRelevantList )
+ {
+ ValueChunk* r = new ValueChunk( parent, handler );
+ return r;
+ }
+
+ // general chunk of no interest, treat as unknown blob
+ return new Chunk( parent, handler, true, chunk_GENERAL );
+}
+
+// BASE CLASS CHUNK ///////////////////////////////////////////////
+// ad hoc creation
+Chunk::Chunk( ContainerChunk* parent, ChunkType c, XMP_Uns32 id )
+{
+ this->chunkType = c; // base class assumption
+ this->parent = parent;
+ this->id = id;
+ this->oldSize = 0;
+ this->newSize = 8;
+ this->oldPos = 0; // inevitable for ad-hoc
+ this->needSizeFix = false;
+
+ // good parenting for latter destruction
+ if ( this->parent != NULL )
+ {
+ this->parent->children.push_back( this );
+ if( this->chunkType == chunk_VALUE )
+ this->parent->childmap.insert( make_pair( this->id, (ValueChunk*) this ) );
+ }
+}
+
+// parsing creation
+Chunk::Chunk( ContainerChunk* parent, RIFF_MetaHandler* handler, bool skip, ChunkType c )
+{
+ chunkType = c; // base class assumption
+ this->parent = parent;
+ this->oldSize = 0;
+ this->hasChange = false; // [2414649] valid assumption at creation time
+
+ LFA_FileRef file = handler->parent->fileRef;
+
+ this->oldPos = LFA_Tell( file );
+ this->id = LFA_ReadUns32_LE( file );
+ this->oldSize = LFA_ReadUns32_LE( file ) + 8;
+
+ // Make sure the size is within expected bounds.
+ XMP_Int64 chunkEnd = this->oldPos + this->oldSize;
+ XMP_Int64 chunkLimit = handler->oldFileSize;
+ if ( parent != 0 ) chunkLimit = parent->oldPos + parent->oldSize;
+ if ( chunkEnd > chunkLimit ) {
+ bool isUpdate = XMP_OptionIsSet ( handler->parent->openFlags, kXMPFiles_OpenForUpdate );
+ bool repairFile = XMP_OptionIsSet ( handler->parent->openFlags, kXMPFiles_OpenRepairFile );
+ if ( (! isUpdate) || (repairFile && (parent == 0)) ) {
+ this->oldSize = chunkLimit - this->oldPos;
+ } else {
+ XMP_Throw ( "Bad RIFF chunk size", kXMPErr_BadFileFormat );
+ }
+ }
+
+ this->newSize = this->oldSize;
+ this->needSizeFix = false;
+
+ if ( skip )
+ {
+ bool ok;
+ LFA_Seek( file, this->oldSize - 8 , SEEK_CUR, &ok );
+ XMP_Validate( ok , "skipped beyond end of file (truncated file?)", kXMPErr_BadFileFormat );
+ }
+
+ // "good parenting", essential for latter destruction.
+ if ( this->parent != NULL )
+ {
+ this->parent->children.push_back( this );
+ if( this->chunkType == chunk_VALUE )
+ this->parent->childmap.insert( make_pair( this->id, (ValueChunk*) this ) );
+ }
+}
+
+void Chunk::changesAndSize( RIFF_MetaHandler* handler )
+{
+ // only unknown chunks should reach this method,
+ // all others must reach overloads, hence little to do here:
+ hasChange = false; // unknown chunk ==> no change, naturally
+ this->newSize = this->oldSize;
+}
+
+std::string Chunk::toString(XMP_Uns8 level )
+{
+ char buffer[256];
+ snprintf( buffer, 255, "%.4s -- "
+ "oldSize: 0x%.8llX, "
+ "newSize: 0x%.8llX, "
+ "oldPos: 0x%.8llX\n",
+ (char*)(&this->id), this->oldSize, this->newSize, this->oldPos );
+ return std::string(buffer);
+}
+
+void Chunk::write( RIFF_MetaHandler* handler, LFA_FileRef file , bool isMainChunk )
+{
+ throw new XMP_Error(kXMPErr_InternalFailure, "Chunk::write never to be called for unknown chunks.");
+}
+
+Chunk::~Chunk()
+{
+ //nothing
+}
+
+// CONTAINER CHUNK /////////////////////////////////////////////////
+// a) creation
+// [2376832] expectedSize - minimum padding "parking size" to use, if not available append to end
+ContainerChunk::ContainerChunk( ContainerChunk* parent, XMP_Uns32 id, XMP_Uns32 containerType ) : Chunk( NULL /* !! */, chunk_CONTAINER, id )
+{
+ // accept no unparented ConatinerChunks
+ XMP_Enforce( parent != NULL );
+
+ this->containerType = containerType;
+ this->newSize = 12;
+ this->parent = parent;
+
+ chunkVect* siblings = &parent->children;
+
+ // add at end. ( oldSize==0 will flag optimization later in the process)
+ siblings->push_back( this );
+}
+
+// b) parsing
+ContainerChunk::ContainerChunk( ContainerChunk* parent, RIFF_MetaHandler* handler ) : Chunk( parent, handler, false, chunk_CONTAINER )
+{
+ bool repairMode = ( 0 != ( handler->parent->openFlags & kXMPFiles_OpenRepairFile ));
+
+ try
+ {
+ LFA_FileRef file = handler->parent->fileRef;
+ XMP_Uns8 level = handler->level;
+
+ // get type of container chunk
+ this->containerType = LFA_ReadUns32_LE( file );
+
+ // ensure legality of top-level chunks
+ if ( level == 0 && handler->riffChunks.size() > 0 )
+ {
+ XMP_Validate( handler->parent->format == kXMP_AVIFile, "only AVI may have multiple top-level chunks", kXMPErr_BadFileFormat );
+ XMP_Validate( this->containerType == kType_AVIX, "all chunks beyond main chunk must be type AVIX", kXMPErr_BadFileFormat );
+ }
+
+ // has *relevant* subChunks? (there might be e.g. non-INFO LIST chunks we don't care about)
+ bool hasSubChunks = ( ( this->id == kChunk_RIFF ) ||
+ ( this->id == kChunk_LIST && this->containerType == kType_INFO ) ||
+ ( this->id == kChunk_LIST && this->containerType == kType_Tdat )
+ );
+ XMP_Int64 endOfChunk = this->oldPos + this->oldSize;
+
+ // this statement catches beyond-EoF-offsets on any level
+ // exception: level 0, tolerate if in repairMode
+ if ( (level == 0) && repairMode && (endOfChunk > handler->oldFileSize) )
+ {
+ endOfChunk = handler->oldFileSize; // assign actual file size
+ this->oldSize = endOfChunk - this->oldPos; //reversely calculate correct oldSize
+ }
+
+ XMP_Validate( endOfChunk <= handler->oldFileSize, "offset beyond EoF", kXMPErr_BadFileFormat );
+
+ Chunk* curChild = 0;
+ if ( hasSubChunks )
+ {
+ handler->level++;
+ while ( LFA_Tell( file ) < endOfChunk )
+ {
+ curChild = RIFF::getChunk( this, handler );
+
+ // digest pad byte - no value validation (0), since some 3rd party files have non-0-padding.
+ if ( LFA_Tell(file) % 2 == 1 )
+ {
+ // [1521093] tolerate missing pad byte at very end of file:
+ XMP_Uns8 pad;
+ LFA_Read ( file, &pad, 1 ); // Read the pad, tolerate being at EOF.
+
+ }
+
+ // within relevant LISTs, relentlesly delete junk chunks (create a single one
+ // at end as part of updateAndChanges()
+ if ( (containerType== kType_INFO || containerType == kType_Tdat)
+ && ( curChild->chunkType == chunk_JUNK ) )
+ {
+ this->children.pop_back();
+ delete curChild;
+ } // for other chunks: join neighouring Junk chunks into one
+ else if ( (curChild->chunkType == chunk_JUNK) && ( this->children.size() >= 2 ) )
+ {
+ // nb: if there are e.g 2 chunks, then last one is at(1), prev one at(0) ==> '-2'
+ Chunk* prevChunk = this->children.at( this->children.size() - 2 );
+ if ( prevChunk->chunkType == chunk_JUNK )
+ {
+ // stack up size to prior chunk
+ prevChunk->oldSize += curChild->oldSize;
+ prevChunk->newSize += curChild->newSize;
+ XMP_Enforce( prevChunk->oldSize == prevChunk->newSize );
+ // destroy current chunk
+ this->children.pop_back();
+ delete curChild;
+ }
+ }
+ }
+ handler->level--;
+ XMP_Validate( LFA_Tell( file ) == endOfChunk, "subchunks exceed outer chunk size", kXMPErr_BadFileFormat );
+
+ // pointers for later legacy processing
+ if ( level==1 && this->id==kChunk_LIST && this->containerType == kType_INFO )
+ handler->listInfoChunk = this;
+ if ( level==1 && this->id==kChunk_LIST && this->containerType == kType_Tdat )
+ handler->listTdatChunk = this;
+ }
+ else // skip non-interest container chunk
+ {
+ bool ok;
+ LFA_Seek( file, this->oldSize - 8 - 4, SEEK_CUR, &ok );
+ XMP_Validate( ok , "skipped beyond end of file 2 (truncated file?)", kXMPErr_BadFileFormat );
+ } // if - else
+
+ } // try
+ catch (XMP_Error& e) {
+ this->release(); // free resources
+ if ( this->parent != 0)
+ this->parent->children.pop_back(); // hereby taken care of, so removing myself...
+
+ throw e; // re-throw
+ }
+}
+
+void ContainerChunk::changesAndSize( RIFF_MetaHandler* handler )
+{
+
+ // Walk the container subtree adjusting the children that have size changes. The only containers
+ // are RIFF and LIST chunks, they are treated differently.
+ //
+ // LISTs get recomposed as a whole. Existing JUNK children of a LIST are removed, existing real
+ // children are left in order with their new size, new children have already been appended. The
+ // LIST as a whole gets a new size that is the sum of the final children.
+ //
+ // Special rules apply to various children of a RIFF container. FIrst, adjacent JUNK children
+ // are combined, this simplifies maximal reuse. The children are recursively adjusted in order
+ // to get their final size.
+ //
+ // Try to determine the final placement of each RIFF child using general rules:
+ // - if the size is unchanged: leave at current location
+ // - if the chunk is at the end of the last RIFF chunk and grows: leave at current location
+ // - if there is enough following JUNK: add part of the JUNK, adjust remaining JUNK size
+ // - if it shrinks by 9 bytes or more: carve off trailing JUNK
+ // - try to find adequate JUNK in the current parent
+ //
+ // Use child-specific rules as a last resort:
+ // - if it is LIST:INFO: delete it, must be in first RIFF chunk
+ // - for others: move to end of last RIFF chunk, make old space JUNK
+
+ // ! Don't create any junk chunks of exactly 8 bytes, just a header and no content. That has a
+ // ! size field of zero, which hits a crashing bug in some versions of Windows Media Player.
+
+ bool isRIFFContainer = (this->id == kChunk_RIFF);
+ bool isLISTContainer = (this->id == kChunk_LIST);
+ XMP_Enforce ( isRIFFContainer | isLISTContainer );
+
+ XMP_Index childIndex; // Could be local to the loops, this simplifies debuging. Need a signed type!
+ Chunk * currChild;
+
+ if ( this->children.empty() ) {
+ if ( isRIFFContainer) {
+ this->newSize = 12; // Keep a minimal size container.
+ } else {
+ this->newSize = 0; // Will get removed from parent in outer call.
+ }
+ this->hasChange = true;
+ return; // Nothing more to do without children.
+ }
+
+ // Collapse adjacent RIFF junk children, remove all LIST junk children. Work back to front to
+ // simplify the effect of .erase() on the loop. Purposely ignore the first chunk.
+
+ for ( childIndex = (XMP_Index)this->children.size() - 1; childIndex > 0; --childIndex ) {
+
+ currChild = this->children[childIndex];
+ if ( currChild->chunkType != chunk_JUNK ) continue;
+
+ if ( isRIFFContainer ) {
+ Chunk * prevChild = this->children[childIndex-1];
+ if ( prevChild->chunkType != chunk_JUNK ) continue;
+ prevChild->oldSize += currChild->oldSize;
+ prevChild->newSize += currChild->newSize;
+ prevChild->hasChange = true;
+ }
+
+ this->children.erase ( this->children.begin() + childIndex );
+ delete currChild;
+ this->hasChange = true;
+
+ }
+
+ // Process the children of RIFF and LIST containers to get their final size. Remove empty
+ // children. Work back to front to simplify the effect of .erase() on the loop. Do not ignore
+ // the first chunk.
+
+ for ( childIndex = (XMP_Index)this->children.size() - 1; childIndex >= 0; --childIndex ) {
+
+ currChild = this->children[childIndex];
+
+ ++handler->level;
+ currChild->changesAndSize ( handler );
+ --handler->level;
+
+ if ( (currChild->newSize == 8) || (currChild->newSize == 0) ) { // ! The newSIze is supposed to include the header.
+ this->children.erase ( this->children.begin() + childIndex );
+ delete currChild;
+ this->hasChange = true;
+ } else {
+ this->hasChange |= currChild->hasChange;
+ currChild->needSizeFix = (currChild->newSize != currChild->oldSize);
+ if ( currChild->needSizeFix && (currChild->newSize > currChild->oldSize) &&
+ (this == handler->lastChunk) && (childIndex+1 == (XMP_Index)this->children.size()) ) {
+ // Let an existing last-in-file chunk grow in-place. Shrinking is conceptually OK,
+ // but complicates later sanity check that the main AVI chunk is not OK to append
+ // other chunks later. Ignore new chunks, they might reuse junk space.
+ if ( currChild->oldSize != 0 ) currChild->needSizeFix = false;
+ }
+ }
+
+ }
+
+ // Go through the children of a RIFF container, adjusting the placement as necessary. In brief,
+ // things can only grow at the end of the last RIFF chunk, and non-junk chunks can't be shifted.
+
+ if ( isRIFFContainer ) {
+
+ for ( childIndex = 0; childIndex < (XMP_Index)this->children.size(); ++childIndex ) {
+
+ currChild = this->children[childIndex];
+ if ( ! currChild->needSizeFix ) continue;
+ currChild->needSizeFix = false;
+
+ XMP_Int64 sizeDiff = currChild->newSize - currChild->oldSize; // Positive for growth.
+ XMP_Uns8 padSize = (currChild->newSize & 1); // Need a pad for odd size.
+
+ // See if the following chunk is junk that can be utilized.
+
+ Chunk * nextChild = 0;
+ if ( childIndex+1 < (XMP_Index)this->children.size() ) nextChild = this->children[childIndex+1];
+
+ if ( (nextChild != 0) && (nextChild->chunkType == chunk_JUNK) ) {
+ if ( nextChild->newSize >= (9 + sizeDiff + padSize) ) {
+
+ // Incorporate part of the trailing junk, or make the trailing junk grow.
+ nextChild->newSize -= sizeDiff;
+ nextChild->newSize -= padSize;
+ nextChild->hasChange = true;
+ continue;
+
+ } else if ( nextChild->newSize == (sizeDiff + padSize) ) {
+
+ // Incorporate all of the trailing junk.
+ this->children.erase ( this->children.begin() + childIndex + 1 );
+ delete nextChild;
+ continue;
+
+ }
+ }
+
+ // See if the chunk shrinks enough to turn the leftover space into junk.
+
+ if ( (sizeDiff + padSize) <= -9 ) {
+ this->children.insert ( (this->children.begin() + childIndex + 1), new JunkChunk ( NULL, ((-sizeDiff) - padSize) ) );
+ continue;
+ }
+
+ // Look through the parent for a usable span of junk.
+
+ XMP_Index junkIndex;
+ Chunk * junkChunk = 0;
+ for ( junkIndex = 0; junkIndex < (XMP_Index)this->children.size(); ++junkIndex ) {
+ junkChunk = this->children[junkIndex];
+ if ( junkChunk->chunkType != chunk_JUNK ) continue;
+ if ( (junkChunk->newSize >= (9 + currChild->newSize + padSize)) ||
+ (junkChunk->newSize == (currChild->newSize + padSize)) ) break;
+ }
+
+ if ( junkIndex < (XMP_Index)this->children.size() ) {
+
+ // Use part or all of the junk for the relocated chunk, replace the old space with junk.
+
+ if ( junkChunk->newSize == (currChild->newSize + padSize) ) {
+
+ // The found junk is an exact fit.
+ this->children[junkIndex] = currChild;
+ delete junkChunk;
+
+ } else {
+
+ // The found junk has excess space. Insert the moving chunk and shrink the junk.
+ XMP_Assert ( junkChunk->newSize >= (9 + currChild->newSize + padSize) );
+ junkChunk->newSize -= (currChild->newSize + padSize);
+ junkChunk->hasChange = true;
+ this->children.insert ( (this->children.begin() + junkIndex), currChild );
+ if ( junkIndex < childIndex ) ++childIndex; // The insertion moved the current child.
+
+ }
+
+ if ( currChild->oldSize != 0 ) {
+ this->children[childIndex] = new JunkChunk ( 0, currChild->oldSize ); // Replace the old space with junk.
+ } else {
+ this->children.erase ( this->children.begin() + childIndex ); // Remove the newly created chunk's old location.
+ --childIndex; // Make the next loop iteration not skip a chunk.
+ }
+
+ continue;
+
+ }
+
+ // If this is a LIST:INFO chunk not in the last of multiple RIFF chunks, then give up
+ // and replace it with oldSize junk. Preserve the first RIFF chunk's original size.
+
+ bool isListInfo = (currChild->id == kChunk_LIST) && (currChild->chunkType == chunk_CONTAINER) &&
+ (((ContainerChunk*)currChild)->containerType == kType_INFO);
+
+ if ( isListInfo && (handler->riffChunks.size() > 1) &&
+ (this->id == kChunk_RIFF) && (this != handler->lastChunk) ) {
+
+ if ( currChild->oldSize != 0 ) {
+ this->children[childIndex] = new JunkChunk ( 0, currChild->oldSize );
+ } else {
+ this->children.erase ( this->children.begin() + childIndex );
+ --childIndex; // Make the next loop iteration not skip a chunk.
+ }
+
+ delete currChild;
+ continue;
+
+ }
+
+ // Move the chunk to the end of the last RIFF chunk and make the old space junk.
+
+ if ( (this == handler->lastChunk) && (childIndex+1 == (XMP_Index)this->children.size()) ) continue; // Already last.
+
+ handler->lastChunk->children.push_back( currChild );
+ if ( currChild->oldSize != 0 ) {
+ this->children[childIndex] = new JunkChunk ( 0, currChild->oldSize ); // Replace the old space with junk.
+ } else {
+ this->children.erase ( this->children.begin() + childIndex ); // Remove the newly created chunk's old location.
+ --childIndex; // Make the next loop iteration not skip a chunk.
+ }
+
+ }
+
+ }
+
+ // Compute the finished container's new size (for both RIFF and LIST).
+
+ this->newSize = 12; // Start with standard container header.
+ for ( childIndex = 0; childIndex < (XMP_Index)this->children.size(); ++childIndex ) {
+ currChild = this->children[childIndex];
+ this->newSize += currChild->newSize;
+ this->newSize += (this->newSize & 1); // Round up if odd.
+ }
+
+ XMP_Validate ( (this->newSize <= 0xFFFFFFFFLL), "No single chunk may be above 4 GB", kXMPErr_Unimplemented );
+
+}
+
+std::string ContainerChunk::toString(XMP_Uns8 level )
+{
+ XMP_Int64 offset= 12; // compute offsets, just for informational purposes
+ // (actually only correct for first chunk)
+
+ char buffer[256];
+ snprintf( buffer, 255, "%.4s:%.4s, "
+ "oldSize: 0x%8llX, "
+ "newSize: 0x%.8llX, "
+ "oldPos: 0x%.8llX\n",
+ (char*)(&this->id), (char*)(&this->containerType), this->oldSize, this->newSize, this->oldPos );
+
+ std::string r(buffer);
+ chunkVectIter iter;
+ for( iter = this->children.begin(); iter != this->children.end(); iter++ )
+ {
+ char buffer[256];
+ snprintf( buffer, 250, "offset 0x%.8llX", offset );
+ r += std::string ( level*4, ' ' ) + std::string( buffer ) + ":" + (*iter)->toString( level + 1 );
+ offset += (*iter)->newSize;
+ if ( offset % 2 == 1 )
+ offset++;
+ }
+ return std::string(r);
+}
+
+void ContainerChunk::write( RIFF_MetaHandler* handler, LFA_FileRef file, bool isMainChunk )
+{
+ bool ok;
+ if ( isMainChunk )
+ LFA_Rewind( file );
+
+ // enforce even position
+ XMP_Int64 chunkStart = LFA_Tell(file);
+ XMP_Int64 chunkEnd = chunkStart + this->newSize;
+ XMP_Enforce( chunkStart % 2 == 0 );
+ chunkVect *rc = &this->children;
+
+ // [2473303] have to write back-to-front to avoid stomp-on-feet
+ XMP_Int64 childStart = chunkEnd;
+ for ( XMP_Int32 chunkNo = (XMP_Int32)(rc->size() -1); chunkNo >= 0; chunkNo-- )
+ {
+ Chunk* cur = rc->at(chunkNo);
+
+ // pad byte first
+ if ( cur->newSize % 2 == 1 )
+ {
+ childStart--;
+ LFA_Seek( file, childStart, SEEK_SET );
+ LFA_WriteUns8( file, 0 );
+ }
+
+ // then contents
+ childStart-= cur->newSize;
+ LFA_Seek( file, childStart, SEEK_SET );
+ switch ( cur->chunkType )
+ {
+ case chunk_GENERAL: //COULDDO enfore no change, since not write-out-able
+ if ( cur->oldPos != childStart )
+ LFA_Move( file, cur->oldPos, file, childStart, cur->oldSize, 0, 0 );
+ break;
+ default:
+ cur->write( handler, file, false );
+ break;
+ } // switch
+
+ } // for
+ XMP_Enforce ( chunkStart + 12 == childStart);
+ LFA_Seek( file, chunkStart, SEEK_SET, &ok );
+
+ LFA_WriteUns32_LE( file, this->id );
+ LFA_WriteUns32_LE( file, (XMP_Uns32) this->newSize - 8 ); // validated in changesAndSize() above
+ LFA_WriteUns32_LE( file, this->containerType );
+
+}
+
+void ContainerChunk::release()
+{
+ // free subchunks
+ Chunk* curChunk;
+ while( ! this->children.empty() )
+ {
+ curChunk = this->children.back();
+ delete curChunk;
+ this->children.pop_back();
+ }
+}
+
+ContainerChunk::~ContainerChunk()
+{
+ this->release(); // free resources
+}
+
+// XMP CHUNK ///////////////////////////////////////////////
+// a) create
+
+// a) creation
+XMPChunk::XMPChunk( ContainerChunk* parent ) : Chunk( parent, chunk_XMP , kChunk_XMP )
+{
+ // nothing
+}
+
+// b) parse
+XMPChunk::XMPChunk( ContainerChunk* parent, RIFF_MetaHandler* handler ) : Chunk( parent, handler, false, chunk_XMP )
+{
+ chunkType = chunk_XMP;
+ LFA_FileRef file = handler->parent->fileRef;
+ XMP_Uns8 level = handler->level;
+
+ handler->packetInfo.offset = this->oldPos + 8;
+ handler->packetInfo.length = (XMP_Int32) this->oldSize - 8;
+
+ handler->xmpPacket.reserve ( handler->packetInfo.length );
+ handler->xmpPacket.assign ( handler->packetInfo.length, ' ' );
+ LFA_Read ( file, (void*)handler->xmpPacket.data(), handler->packetInfo.length, kLFA_RequireAll );
+
+ handler->containsXMP = true; // last, after all possible failure
+
+ // pointer for later processing
+ handler->xmpChunk = this;
+}
+
+void XMPChunk::changesAndSize( RIFF_MetaHandler* handler )
+{
+ XMP_Enforce( &handler->xmpPacket != 0 );
+ XMP_Enforce( handler->xmpPacket.size() > 0 );
+ this->newSize = 8 + handler->xmpPacket.size();
+
+ XMP_Validate( this->newSize <= 0xFFFFFFFFLL, "no single chunk may be above 4 GB", kXMPErr_InternalFailure );
+
+ // a complete no-change would have been caught in XMPFiles common code anyway
+ this->hasChange = true;
+}
+
+void XMPChunk::write( RIFF_MetaHandler* handler, LFA_FileRef file, bool isMainChunk )
+{
+ LFA_WriteUns32_LE( file, kChunk_XMP );
+ LFA_WriteUns32_LE( file, (XMP_Uns32) this->newSize - 8 ); // validated in changesAndSize() above
+ LFA_Write( file, handler->xmpPacket.data(), (XMP_Int32)handler->xmpPacket.size() );
+}
+
+// Value CHUNK ///////////////////////////////////////////////
+// a) creation
+ValueChunk::ValueChunk( ContainerChunk* parent, std::string value, XMP_Uns32 id ) : Chunk( parent, chunk_VALUE, id )
+{
+ this->oldValue = std::string();
+ this->SetValue( value );
+}
+
+// b) parsing
+ValueChunk::ValueChunk( ContainerChunk* parent, RIFF_MetaHandler* handler ) : Chunk( parent, handler, false, chunk_VALUE )
+{
+ // set value: -----------------
+ LFA_FileRef file = handler->parent->fileRef;
+ XMP_Uns8 level = handler->level;
+
+ // unless changed through reconciliation, assume for now.
+ // IMPORTANT to stay true to the original (no \0 cleanup or similar)
+ // since unknown value chunks might not be fully understood,
+ // hence must be precisely preserved !!!
+
+ XMP_Int32 length = (XMP_Int32) this->oldSize - 8;
+ this->oldValue.reserve( length );
+ this->oldValue.assign( length + 1, '\0' );
+ LFA_Read ( file, (void*)this->oldValue.data(), length, kLFA_RequireAll );
+
+ this->newValue = this->oldValue;
+ this->newSize = this->oldSize;
+}
+
+void ValueChunk::SetValue( std::string value, bool optionalNUL /* = false */ )
+{
+ this->newValue.assign( value );
+ if ( (! optionalNUL) || ((value.size() & 1) == 1) ) {
+ // ! The NUL should be optional in WAV to avoid a parsing bug in Audition 3 - can't handle implicit pad byte.
+ this->newValue.append( 1, '\0' ); // append zero termination as explicit part of string
+ }
+ this->newSize = this->newValue.size() + 8;
+}
+
+void ValueChunk::changesAndSize( RIFF_MetaHandler* handler )
+{
+ // Don't simply assign to this->hasChange, it might already be true.
+ if ( this->newValue.size() != this->oldValue.size() ) {
+ this->hasChange = true;
+ } else if ( strncmp ( this->oldValue.c_str(), this->newValue.c_str(), this->newValue.size() ) != 0 ) {
+ this->hasChange = true;
+ }
+}
+
+void ValueChunk::write( RIFF_MetaHandler* handler, LFA_FileRef file, bool isMainChunk )
+{
+ LFA_WriteUns32_LE( file, this->id );
+ LFA_WriteUns32_LE( file, (XMP_Uns32)this->newSize - 8 );
+ LFA_Write( file, this->newValue.data() , (XMP_Int32)this->newSize - 8 );
+}
+
+/* remove value chunk if existing.
+ return true if it was existing. */
+bool ContainerChunk::removeValue( XMP_Uns32 id )
+{
+ valueMap* cm = &this->childmap;
+ valueMapIter iter = cm->find( id );
+
+ if( iter == cm->end() )
+ return false; //not found
+
+ ValueChunk* propChunk = iter->second;
+
+ // remove from vector (difficult)
+ chunkVect* cv = &this->children;
+ chunkVectIter cvIter;
+ for (cvIter = cv->begin(); cvIter != cv->end(); ++cvIter )
+ {
+ if ( (*cvIter)->id == id )
+ break; // found!
+ }
+ XMP_Validate( cvIter != cv->end(), "property not found in children vector", kXMPErr_InternalFailure );
+ cv->erase( cvIter );
+
+ // remove from map (easy)
+ cm->erase( iter );
+
+ delete propChunk;
+ return true; // found and removed
+}
+
+/* returns iterator to (first) occurence of this chunk.
+ iterator to the end of the map if chunk pointer is not found */
+chunkVectIter ContainerChunk::getChild( Chunk* needle )
+{
+ chunkVectIter iter;
+ for( iter = this->children.begin(); iter != this->children.end(); iter++ )
+ {
+ Chunk* temp1 = *iter;
+ Chunk* temp2 = needle;
+ if ( (*iter) == needle ) return iter;
+ }
+ return this->children.end();
+}
+
+/* replaces a chunk by a JUNK chunk.
+ Also frees memory of prior chunk. */
+void ContainerChunk::replaceChildWithJunk( Chunk* child, bool deleteChild )
+{
+ chunkVectIter iter = getChild( child );
+ if ( iter == this->children.end() ) {
+ throw new XMP_Error(kXMPErr_InternalFailure, "replaceChildWithJunk: childChunk not found.");
+ }
+
+ *iter = new JunkChunk ( NULL, child->oldSize );
+ if ( deleteChild ) delete child;
+
+ this->hasChange = true;
+}
+
+// JunkChunk ///////////////////////////////////////////////////
+// a) creation
+JunkChunk::JunkChunk( ContainerChunk* parent, XMP_Int64 size ) : Chunk( parent, chunk_JUNK, kChunk_JUNK )
+{
+ XMP_Assert( size >= 8 );
+ this->oldSize = size;
+ this->newSize = size;
+ this->hasChange = true;
+}
+
+// b) parsing
+JunkChunk::JunkChunk( ContainerChunk* parent, RIFF_MetaHandler* handler ) : Chunk( parent, handler, true, chunk_JUNK )
+{
+ chunkType = chunk_JUNK;
+}
+
+void JunkChunk::changesAndSize( RIFF_MetaHandler* handler )
+{
+ this->newSize = this->oldSize; // optimization at a later stage
+ XMP_Validate( this->newSize <= 0xFFFFFFFFLL, "no single chunk may be above 4 GB", kXMPErr_InternalFailure );
+ if ( this->id == kChunk_JUNQ ) this->hasChange = true; // Force ID change to JUNK.
+}
+
+// zeroBuffer, etc to write out empty native padding
+const static XMP_Uns32 kZeroBufferSize64K = 64 * 1024;
+static XMP_Uns8 kZeroes64K [ kZeroBufferSize64K ]; // C semantics guarantee zero initialization.
+
+void JunkChunk::write( RIFF_MetaHandler* handler, LFA_FileRef file, bool isMainChunk )
+{
+ LFA_WriteUns32_LE( file, kChunk_JUNK ); // write JUNK, never JUNQ
+ XMP_Enforce( this->newSize < 0xFFFFFFFF );
+ XMP_Enforce( this->newSize >= 8 ); // minimum size of any chunk
+ XMP_Uns32 innerSize = (XMP_Uns32)this->newSize - 8;
+ LFA_WriteUns32_LE( file, innerSize );
+
+ // write out in 64K chunks
+ while ( innerSize > kZeroBufferSize64K )
+ {
+ LFA_Write( file, kZeroes64K , kZeroBufferSize64K );
+ innerSize -= kZeroBufferSize64K;
+ }
+ LFA_Write( file, kZeroes64K , innerSize );
+}
+
+} // namespace RIFF
diff --git a/source/XMPFiles/FormatSupport/RIFF.hpp b/source/XMPFiles/FormatSupport/RIFF.hpp
new file mode 100644
index 0000000..34b2100
--- /dev/null
+++ b/source/XMPFiles/FormatSupport/RIFF.hpp
@@ -0,0 +1,316 @@
+#ifndef __RIFF_hpp__
+#define __RIFF_hpp__ 1
+
+// =================================================================================================
+// ADOBE SYSTEMS INCORPORATED
+// Copyright 2009 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 "XMP_Environment.h" // ! This must be the first include.
+#include <vector>
+#include <map>
+#include "XMPFiles_Impl.hpp"
+
+// ahead declaration:
+class RIFF_MetaHandler;
+
+namespace RIFF {
+
+ enum ChunkType {
+ chunk_GENERAL, //unknown or not relevant
+ chunk_CONTAINER,
+ chunk_XMP,
+ chunk_VALUE,
+ chunk_JUNK,
+ NO_CHUNK // used as precessor to first chunk, etc.
+ };
+
+ // ahead declarations
+ class Chunk;
+ class ContainerChunk;
+ class ValueChunk;
+ class XMPChunk;
+
+ // (scope: only used in RIFF_Support and RIFF_Handler.cpp
+ // ==> no need to overspecify with lengthy names )
+
+ typedef std::vector<Chunk*> chunkVect; // coulddo: narrow down toValueChunk (could give trouble with JUNK though)
+ typedef chunkVect::iterator chunkVectIter; // or refactor ??
+
+ typedef std::vector<ContainerChunk*> containerVect;
+ typedef containerVect::iterator containerVectIter;
+
+ typedef std::map<XMP_Uns32,ValueChunk*> valueMap;
+ typedef valueMap::iterator valueMapIter;
+
+
+ // format chunks+types
+ const XMP_Uns32 kChunk_RIFF = 0x46464952;
+ const XMP_Uns32 kType_AVI_ = 0x20495641;
+ const XMP_Uns32 kType_AVIX = 0x58495641;
+ const XMP_Uns32 kType_WAVE = 0x45564157;
+
+ const XMP_Uns32 kChunk_JUNK = 0x4B4E554A;
+ const XMP_Uns32 kChunk_JUNQ = 0x514E554A;
+
+ // other container chunks
+ const XMP_Uns32 kChunk_LIST = 0x5453494C;
+ const XMP_Uns32 kType_INFO = 0x4F464E49;
+ const XMP_Uns32 kType_Tdat = 0x74616454;
+
+ // other relevant chunks
+ const XMP_Uns32 kChunk_XMP = 0x584D505F; // "_PMX"
+
+ // relevant for Index Correction
+ // LIST:
+ const XMP_Uns32 kType_hdrl = 0x6C726468;
+ const XMP_Uns32 kType_strl = 0x6C727473;
+ const XMP_Uns32 kChunk_indx = 0x78646E69;
+ const XMP_Uns32 kChunk_ixXX = 0x58587869;
+ const XMP_Uns32 kType_movi = 0x69766F6D;
+
+ //should occur only in AVI
+ const XMP_Uns32 kChunk_Cr8r = 0x72387243;
+ const XMP_Uns32 kChunk_PrmL = 0x4C6D7250;
+
+ //should occur only in WAV
+ const XMP_Uns32 kChunk_DISP = 0x50534944;
+ const XMP_Uns32 kChunk_bext = 0x74786562;
+
+ // LIST/INFO constants
+ const XMP_Uns32 kPropChunkIART = 0x54524149;
+ const XMP_Uns32 kPropChunkICMT = 0x544D4349;
+ const XMP_Uns32 kPropChunkICOP = 0x504F4349;
+ const XMP_Uns32 kPropChunkICRD = 0x44524349;
+ const XMP_Uns32 kPropChunkIENG = 0x474E4549;
+ const XMP_Uns32 kPropChunkIGNR = 0x524E4749;
+ const XMP_Uns32 kPropChunkINAM = 0x4D414E49;
+ const XMP_Uns32 kPropChunkISFT = 0x54465349;
+ const XMP_Uns32 kPropChunkIARL = 0x4C524149;
+
+ const XMP_Uns32 kPropChunkIMED = 0x44454D49;
+ const XMP_Uns32 kPropChunkISRF = 0x46525349;
+ const XMP_Uns32 kPropChunkICMS = 0x4C524149;
+ const XMP_Uns32 kPropChunkIPRD = 0x534D4349;
+ const XMP_Uns32 kPropChunkISRC = 0x44525049;
+ const XMP_Uns32 kPropChunkITCH = 0x43525349;
+
+ const XMP_Uns32 kPropChunk_tc_O =0x4F5F6374;
+ const XMP_Uns32 kPropChunk_tc_A =0x415F6374;
+ const XMP_Uns32 kPropChunk_rn_O =0x4F5F6E72;
+ const XMP_Uns32 kPropChunk_rn_A =0x415F6E72;
+
+ ///////////////////////////////////////////////////////////////
+
+ enum PropType { // from a simplified, opinionated legacy angle
+ prop_SIMPLE,
+ prop_TIMEVALUE,
+ prop_LOCALIZED_TEXT,
+ prop_ARRAYITEM, // ( here: a solitary one)
+ };
+
+ struct Mapping {
+ XMP_Uns32 chunkID;
+ const char* ns;
+ const char* prop;
+ PropType propType;
+ };
+
+ // bext Mappings, piece-by-piece:
+ static Mapping bextDescription = { 0, kXMP_NS_BWF, "description", prop_SIMPLE };
+ static Mapping bextOriginator = { 0, kXMP_NS_BWF, "originator", prop_SIMPLE };
+ static Mapping bextOriginatorRef = { 0, kXMP_NS_BWF, "originatorReference", prop_SIMPLE };
+ static Mapping bextOriginationDate = { 0, kXMP_NS_BWF, "originationDate", prop_SIMPLE };
+ static Mapping bextOriginationTime = { 0, kXMP_NS_BWF, "originationTime", prop_SIMPLE };
+ static Mapping bextTimeReference = { 0, kXMP_NS_BWF, "timeReference", prop_SIMPLE };
+ static Mapping bextVersion = { 0, kXMP_NS_BWF, "version", prop_SIMPLE };
+ static Mapping bextUMID = { 0, kXMP_NS_BWF, "umid", prop_SIMPLE };
+ static Mapping bextCodingHistory = { 0, kXMP_NS_BWF, "codingHistory", prop_SIMPLE };
+
+ // LIST:INFO properties
+ static Mapping listInfoProps[] = {
+ // reconciliations CS4 and before:
+ { kPropChunkIART, kXMP_NS_DM, "artist" , prop_SIMPLE },
+ { kPropChunkICMT, kXMP_NS_DM, "logComment" , prop_SIMPLE },
+ { kPropChunkICOP, kXMP_NS_DC, "rights" , prop_LOCALIZED_TEXT },
+ { kPropChunkICRD, kXMP_NS_XMP, "CreateDate" , prop_SIMPLE },
+ { kPropChunkIENG, kXMP_NS_DM, "engineer" , prop_SIMPLE },
+ { kPropChunkIGNR, kXMP_NS_DM, "genre" , prop_SIMPLE },
+ { kPropChunkINAM, kXMP_NS_DC, "title" , prop_LOCALIZED_TEXT }, // ( was wrongly dc:album in pre-CS4)
+ { kPropChunkISFT, kXMP_NS_XMP, "CreatorTool", prop_SIMPLE },
+
+ // RIFF/*/LIST/INFO properties, new in CS5, both AVI and WAV
+
+ { kPropChunkIMED, kXMP_NS_DC, "source" , prop_SIMPLE },
+ { kPropChunkISRF, kXMP_NS_DC, "type" , prop_ARRAYITEM },
+ // TO ENABLE { kPropChunkIARL, kXMP_NS_DC, "subject" , prop_SIMPLE }, // array !! (not x-default language alternative)
+ //{ kPropChunkICMS, to be decided, "" , prop_SIMPLE },
+ //{ kPropChunkIPRD, to be decided, "" , prop_SIMPLE },
+ //{ kPropChunkISRC, to be decided, "" , prop_SIMPLE },
+ //{ kPropChunkITCH, to be decided, "" , prop_SIMPLE },
+
+ { 0, 0, 0 } // sentinel
+ };
+
+ static Mapping listTdatProps[] = {
+ // reconciliations CS4 and before:
+ { kPropChunk_tc_O, kXMP_NS_DM, "startTimecode" , prop_TIMEVALUE }, // special: must end up in dm:timeValue child
+ { kPropChunk_tc_A, kXMP_NS_DM, "altTimecode" , prop_TIMEVALUE }, // special: must end up in dm:timeValue child
+ { kPropChunk_rn_O, kXMP_NS_DM, "tapeName" , prop_SIMPLE },
+ { kPropChunk_rn_A, kXMP_NS_DM, "altTapeName" , prop_SIMPLE },
+ { 0, 0, 0 } // sentinel
+ };
+
+ // =================================================================================================
+ // ImportCr8rItems
+ // ===============
+ #pragma pack ( push, 1 )
+ struct PrmLBoxContent {
+ XMP_Uns32 magic;
+ XMP_Uns32 size;
+ XMP_Uns16 verAPI;
+ XMP_Uns16 verCode;
+ XMP_Uns32 exportType;
+ XMP_Uns16 MacVRefNum;
+ XMP_Uns32 MacParID;
+ char filePath[260];
+ };
+
+ enum { kExportTypeMovie = 0, kExportTypeStill = 1, kExportTypeAudio = 2, kExportTypeCustom = 3 };
+
+ struct Cr8rBoxContent {
+ XMP_Uns32 magic;
+ XMP_Uns32 size;
+ XMP_Uns16 majorVer;
+ XMP_Uns16 minorVer;
+ XMP_Uns32 creatorCode;
+ XMP_Uns32 appleEvent;
+ char fileExt[16];
+ char appOptions[16];
+ char appName[32];
+ };
+ #pragma pack ( pop )
+
+ // static getter, determines appropriate chunkType (peeking)and returns
+ // the respective constructor. It's the caller's responsibility to
+ // delete obtained chunk.
+ Chunk* getChunk ( ContainerChunk* parent, RIFF_MetaHandler* handler );
+
+ class Chunk
+ {
+ public:
+ ChunkType chunkType; // set by constructor
+ ContainerChunk* parent; // 0 on top-level
+
+ XMP_Uns32 id; // the first four bytes, first byte of highest value
+ XMP_Int64 oldSize; // actual chunk size INCLUDING the 8/12 header bytes,
+ XMP_Int64 oldPos; // file position of this chunk
+
+ // both set as part of changesAndSize()
+ XMP_Int64 newSize;
+ bool hasChange;
+ bool needSizeFix; // used in changesAndSize() only
+
+ // Constructors ///////////////////////
+ // parsing
+ Chunk( ContainerChunk* parent, RIFF_MetaHandler* handler, bool skip, ChunkType c /*= chunk_GENERAL*/ );
+ // ad-hoc creation
+ Chunk( ContainerChunk* parent, ChunkType c, XMP_Uns32 id );
+
+ /* returns true, if something has changed in chunk (which needs specific write-out,
+ this->newSize is expected to be set by this routine */
+ virtual void changesAndSize( RIFF_MetaHandler* handler );
+ virtual std::string toString(XMP_Uns8 level = 0);
+ virtual void write( RIFF_MetaHandler* handler, LFA_FileRef file, bool isMainChunk = false );
+
+ virtual ~Chunk();
+
+ }; // class Chunk
+
+ class XMPChunk : public Chunk
+ {
+ public:
+ XMPChunk( ContainerChunk* parent );
+ XMPChunk( ContainerChunk* parent, RIFF_MetaHandler* handler );
+
+ void changesAndSize( RIFF_MetaHandler* handler );
+ void write( RIFF_MetaHandler* handler, LFA_FileRef file, bool isMainChunk = false );
+
+ };
+
+ // any chunk, whose value should be stored, e.g. LIST:INFO, LIST:Tdat
+ class ValueChunk : public Chunk
+ {
+ public:
+ std::string oldValue, newValue;
+
+ // for ad-hoc creation (upon write)
+ ValueChunk( ContainerChunk* parent, std::string value, XMP_Uns32 id );
+
+ // for parsing
+ ValueChunk( ContainerChunk* parent, RIFF_MetaHandler* handler );
+
+ enum { kNULisOptional = true };
+
+ void SetValue( std::string value, bool optionalNUL = false );
+ void changesAndSize( RIFF_MetaHandler* handler );
+ void write( RIFF_MetaHandler* handler, LFA_FileRef file, bool isMainChunk = false );
+
+ // own destructor not needed.
+ };
+
+ // relevant (level 1) JUNQ and JUNK chunks...
+ class JunkChunk : public Chunk
+ {
+ public:
+ // construction
+ JunkChunk( ContainerChunk* parent, XMP_Int64 size );
+ // parsing
+ JunkChunk( ContainerChunk* parent, RIFF_MetaHandler* handler );
+
+ // own destructor not needed.
+
+ void changesAndSize( RIFF_MetaHandler* handler );
+ void write( RIFF_MetaHandler* handler, LFA_FileRef file, bool isMainChunk = false );
+ };
+
+
+ class ContainerChunk : public Chunk
+ {
+ public:
+ XMP_Uns32 containerType; // e.g. kType_INFO as in "LIST:INFO"
+
+ chunkVect children; // used for cleanup/destruction, ordering...
+ valueMap childmap; // only for efficient *value* access (inside LIST), *not* used for other containers
+
+ // construct
+ ContainerChunk( ContainerChunk* parent, XMP_Uns32 id, XMP_Uns32 containerType );
+ // parse
+ ContainerChunk( ContainerChunk* parent, RIFF_MetaHandler* handler );
+
+ bool removeValue( XMP_Uns32 id );
+
+ /* returns iterator to (first) occurence of this chunk.
+ iterator to the end of the map if chunk pointer is not found */
+ chunkVectIter getChild( Chunk* needle );
+
+ void replaceChildWithJunk( Chunk* child, bool deleteChild = true );
+
+ void changesAndSize( RIFF_MetaHandler* handler );
+ std::string toString(XMP_Uns8 level = 0);
+ void write( RIFF_MetaHandler* handler, LFA_FileRef file, bool isMainChunk = false );
+
+ // destroy
+ void release(); // used by destructor and on error in constructor
+ ~ContainerChunk();
+
+ }; // class ContainerChunk
+
+} // namespace RIFF
+
+
+#endif // __RIFF_hpp__
diff --git a/source/XMPFiles/FormatSupport/RIFF_Support.cpp b/source/XMPFiles/FormatSupport/RIFF_Support.cpp
index 9574119..fe7e568 100644
--- a/source/XMPFiles/FormatSupport/RIFF_Support.cpp
+++ b/source/XMPFiles/FormatSupport/RIFF_Support.cpp
@@ -1,851 +1,930 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2008 Adobe Systems Incorporated
+// Copyright 2008 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 "RIFF_Support.hpp"
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
-#if XMP_WinBuild
- #pragma warning ( disable : 4996 ) // '...' was declared deprecated
-#endif
+// must have access to handler class fields...
+#include "RIFF.hpp"
+#include "RIFF_Handler.hpp"
+#include "RIFF_Support.hpp"
+#include "Reconcile_Impl.hpp"
-namespace RIFF_Support {
+using namespace RIFF;
+namespace RIFF {
- #define ckidPremierePadding MakeFourCC ('J','U','N','Q')
- #define formtypeAVIX MakeFourCC ('A', 'V', 'I', 'X')
+// The minimum BEXT chunk size should be 610 (incl. 8 byte header/size field)
+XMP_Int32 MIN_BEXT_SIZE = 610; // = > 8 + ( 256+32+32+10+8+4+4+2+64+190+0 )
+// An assumed secure max value of 100 MB.
+XMP_Int32 MAX_BEXT_SIZE = 100 * 1024 * 1024;
- #ifndef AVIMAXCHUNKSIZE
- #define AVIMAXCHUNKSIZE ((UInt32) 0x80000000) /* 2 GB */
- #endif
+// CR8R, PrmL have fixed sizes
+XMP_Int32 CR8R_SIZE = 0x5C;
+XMP_Int32 PRML_SIZE = 0x122;
+static const char* sHexChars = "0123456789ABCDEF";
- typedef struct
- {
- long id;
- UInt32 len;
- } atag;
+// Encode a string of raw data bytes into a HexString (w/o spaces, i.e. "DEADBEEF").
+// No insertation/acceptance of whitespace/linefeeds. No output/tolerance of lowercase.
+// returns true, if *all* characters returned are zero (or if 0 bytes are returned).
+static bool EncodeToHexString ( XMP_StringPtr rawStr,
+ XMP_StringLen rawLen,
+ std::string* encodedStr )
+{
+ bool allZero = true; // assume for now
- // Local function declarations
- static bool ReadTag ( LFA_FileRef inFileRef, long * outTag, UInt32 * outLength, long * subtype, UInt64 & inOutPosition, UInt64 maxOffset );
- static void AddTag ( RiffState & inOutRiffState, long tag, UInt32 len, UInt64 & inOutPosition, long parentID, long parentnum, long subtypeID );
- static long SubRead ( LFA_FileRef inFileRef, RiffState & inOutRiffState, long parentid, UInt32 parentlen, UInt64 & inOutPosition );
- static bool ReadChunk ( LFA_FileRef inFileRef, UInt64 & pos, UInt32 len, char * outBuffer );
+ if ( (rawStr == 0) && (rawLen != 0) )
+ XMP_Throw ( "EncodeToHexString: null rawStr", kXMPErr_BadParam );
+ if ( encodedStr == 0 )
+ XMP_Throw ( "EncodeToHexString: null encodedStr", kXMPErr_BadParam );
- #define GetFilePosition(file) LFA_Seek ( file, 0, SEEK_CUR )
-
- // =============================================================================================
+ encodedStr->erase();
+ if ( rawLen == 0 ) return allZero;
+ encodedStr->reserve ( rawLen * 2 );
- bool GetMetaData ( LFA_FileRef inFileRef, long tagID, char * outBuffer, unsigned long * outBufferSize )
+ for( XMP_Uns32 i = 0; i < rawLen; i++ )
{
- RiffState riffState;
-
- long numTags = OpenRIFF ( inFileRef, riffState );
- if ( numTags == 0 ) return false;
-
- return GetRIFFChunk ( inFileRef, riffState, tagID, 0, 0, outBuffer, outBufferSize );
-
- }
+ // first, second nibble
+ XMP_Uns8 first = rawStr[i] >> 4;
+ XMP_Uns8 second = rawStr[i] & 0xF;
- // =============================================================================================
+ if ( allZero && (( first != 0 ) || (second != 0)))
+ allZero = false;
- bool SetMetaData ( LFA_FileRef inFileRef, long riffType, long tagID, const char * inBuffer, unsigned long inBufferSize )
- {
- RiffState riffState;
-
- long numTags = OpenRIFF ( inFileRef, riffState );
- if ( numTags == 0 ) return false;
-
- return PutChunk ( inFileRef, riffState, riffType, tagID, inBuffer, inBufferSize );
-
+ encodedStr->append( 1, sHexChars[first] );
+ encodedStr->append( 1, sHexChars[second] );
}
- // =============================================================================================
+ return allZero;
+} // EncodeToHexString
- bool MarkChunkAsPadding ( LFA_FileRef inFileRef, RiffState & inOutRiffState, long riffType, long tagID, long subtypeID )
- {
- UInt32 len;
- UInt64 pos;
- atag tag;
-
- try {
-
- bool found = FindChunk ( inOutRiffState, tagID, riffType, subtypeID, NULL, &len, &pos );
- if ( ! found ) return false;
-
- if ( subtypeID != 0 ) {
- pos -= 12;
- } else {
- pos -= 8;
- }
+// -------------------------------------------------------------------------------------------------
+// DecodeFromHexString
+// ----------------
+//
+// Decode a hex string to raw data bytes.
+// * Input must be all uppercase and w/o any whitespace, strictly (0-9A-Z)* (i.e. "DEADBEEF0099AABC")
+// * No insertation/acceptance of whitespace/linefeeds.
+// * bNo use/tolerance of lowercase.
+// * Number of bytes in the encoded String must be even.
+// * returns true if everything went well, false if illegal (non 0-9A-F) character encountered
+
+static bool DecodeFromHexString ( XMP_StringPtr encodedStr,
+ XMP_StringLen encodedLen,
+ std::string* rawStr )
+{
+ if ( (encodedLen % 2) != 0 )
+ return false;
+ rawStr->erase();
+ if ( encodedLen == 0 ) return true;
+ rawStr->reserve ( encodedLen / 2 );
+
+ for( XMP_Uns32 i = 0; i < encodedLen; )
+ {
+ XMP_Uns8 upperNibble = encodedStr[i];
+ if ( (upperNibble < 48) || ( (upperNibble > 57 ) && ( upperNibble < 65 ) ) || (upperNibble > 70) )
+ return false;
+ if ( upperNibble >= 65 )
+ upperNibble -= 7; // shift A-F area adjacent to 0-9
+ upperNibble -= 48; // 'shift' to a value [0..15]
+ upperNibble = ( upperNibble << 4 );
+ i++;
+
+ XMP_Uns8 lowerNibble = encodedStr[i];
+ if ( (lowerNibble < 48) || ( (lowerNibble > 57 ) && ( lowerNibble < 65 ) ) || (lowerNibble > 70) )
+ return false;
+ if ( lowerNibble >= 65 )
+ lowerNibble -= 7; // shift A-F area adjacent to 0-9
+ lowerNibble -= 48; // 'shift' to a value [0..15]
+ i++;
- tag.id = MakeUns32LE ( ckidPremierePadding );
- LFA_Seek ( inFileRef, pos, SEEK_SET );
- LFA_Write ( inFileRef, &tag, 4 );
-
- pos += 8;
- AddTag ( inOutRiffState, ckidPremierePadding, len, pos, 0, 0, 0 );
-
- } catch(...) {
+ rawStr->append ( 1, (upperNibble + lowerNibble) );
+ }
+ return true;
+} // DecodeFromHexString
+
+// Converts input string to an ascii output string
+// - terminates at first 0
+// - replaces all non ascii with 0x3F ('?')
+// - produces up to maxOut characters (note that several UTF-8 character bytes can 'melt' to one byte '?' in ascii.)
+static XMP_StringLen convertToASCII( XMP_StringPtr input, XMP_StringLen inputLen, std::string* output, XMP_StringLen maxOutputLen )
+{
+ if ( (input == 0) && (inputLen != 0) )
+ XMP_Throw ( "convertToASCII: null input string", kXMPErr_BadParam );
+ if ( output == 0)
+ XMP_Throw ( "convertToASCII: null output string", kXMPErr_BadParam );
+ if ( maxOutputLen == 0)
+ XMP_Throw ( "convertToASCII: zero maxOutputLen chars", kXMPErr_BadParam );
- return false; // If a write fails, it throws, so we return false.
+ output->reserve(inputLen);
+ output->erase();
+ bool isUTF8 = ReconcileUtils::IsUTF8( input, inputLen );
+ XMP_StringLen outputLen = 0;
+
+ for ( XMP_Uns32 i=0; i < inputLen; i++ )
+ {
+ XMP_Uns8 c = (XMP_Uns8) input[i];
+ if ( c == 0 ) // early 0 termination, leave.
+ break;
+ if ( c > 127 ) // uft-8 multi-byte sequence.
+ {
+ if ( isUTF8 ) // skip all high bytes
+ {
+ // how many bytes in this ?
+ if ( c >= 0xC2 && c <= 0xDF )
+ i+=1; // 2-byte sequence
+ else if ( c >= 0xE0 && c <= 0xEF )
+ i+=2; // 3-byte sequence
+ else if ( c >= 0xF0 && c <= 0xF4 )
+ i+=3; // 4-byte sequence
+ else
+ continue; //invalid sequence, look for next 'low' byte ..
+ } // thereafter and 'else': just append a question mark:
+ output->append( 1, '?' );
}
-
- return true;
+ else // regular valid ascii. 1 byte.
+ {
+ output->append( 1, input[i] );
+ }
+ outputLen++;
+ if ( outputLen >= maxOutputLen )
+ break; // (may be even or even greater due to UFT-8 multi-byte jumps)
}
- // =============================================================================================
+ return outputLen;
+}
- bool PutChunk ( LFA_FileRef inFileRef, RiffState & inOutRiffState, long riffType, long tagID, const char * inBuffer, UInt32 inBufferSize )
+/**
+ * ensures that native property gets returned as UTF-8 (may or mayn not already be UTF-8)
+ * - also takes care of "moot padding" (pre-mature zero termination)
+ * - propertyExists: it is important to know if there as an existing, non zero property
+ * even (in the event of serverMode) it is not actually returned, but an empty string instead.
+ */
+static std::string nativePropertyToUTF8 ( XMP_StringPtr cstring, XMP_StringLen maxSize, bool* propertyExists )
+{
+ // the value might be properly 0-terminated, prematurely or not
+ // at all, hence scan through to find actual size
+ XMP_StringLen size = 0;
+ for ( size = 0; size < maxSize; size++ )
{
- UInt32 len;
- UInt64 pos;
- atag tag;
-
- // Make sure we're writting an even number of bytes. Required by the RIFF specification.
- XMP_Assert ( (inBufferSize & 1) == 0 );
-
- try {
-
- bool found = FindChunk ( inOutRiffState, tagID, 0, 0, NULL, &len, &pos );
- if ( found ) {
+ if ( cstring[size] == 0 )
+ break;
+ }
- if ( len == inBufferSize ) {
- LFA_Seek ( inFileRef, pos, SEEK_SET );
- LFA_Write ( inFileRef, inBuffer, inBufferSize );
- return true;
- }
-
- pos -= 8;
- tag.id = MakeUns32LE ( ckidPremierePadding );
- LFA_Seek ( inFileRef, pos, SEEK_SET );
- LFA_Write ( inFileRef, &tag, 4 );
-
- if ( len > inBufferSize ) {
- pos += 8;
- AddTag ( inOutRiffState, ckidPremierePadding, len, pos, 0, 0, 0 );
- }
+ (*propertyExists) = ( size > 0 );
+
+ std::string utf8("");
+ if ( ReconcileUtils::IsUTF8( cstring, size ) )
+ utf8 = std::string( cstring, size ); //use utf8 directly
+ else
+ {
+ if ( ! ignoreLocalText )
+ {
+ #if ! UNIX_ENV // n/a anyway, since always ignoreLocalText on Unix
+ ReconcileUtils::LocalToUTF8( cstring, size, &utf8 );
+ #endif
+ }
+ }
+ return utf8;
+}
- }
+// reads maxSize bytes from file (not "up to", exactly fullSize)
+// puts it into a string, sets respective tree property
+static std::string getBextField ( const char* data, XMP_Uns32 offset, XMP_Uns32 maxSize )
+{
+ if (data == 0)
+ XMP_Throw ( "getBextField: null data pointer", kXMPErr_BadParam );
+ if ( maxSize == 0)
+ XMP_Throw ( "getBextField: maxSize must be greater than 0", kXMPErr_BadParam );
+
+ std::string r;
+ convertToASCII( data+offset, maxSize, &r, maxSize );
+ return r;
+}
- } catch ( ... ) {
+static void importBextChunkToXMP( RIFF_MetaHandler* handler, ValueChunk* bextChunk )
+{
+ // if there's a bext chunk, there is data...
+ handler->containsXMP = true; // very important for treatment on caller level
+
+ XMP_Enforce( bextChunk->oldSize >= MIN_BEXT_SIZE );
+ XMP_Enforce( bextChunk->oldSize < MAX_BEXT_SIZE );
+
+ const char* data = bextChunk->oldValue.data();
+ std::string value;
+
+ // register bext namespace:
+ SXMPMeta::RegisterNamespace( kXMP_NS_BWF, "bext:", 0 );
+
+ // bextDescription ------------------------------------------------
+ value = getBextField( data, 0, 256 );
+ if ( value.size() > 0 )
+ handler->xmpObj.SetProperty( bextDescription.ns, bextDescription.prop, value.c_str() );
+
+ // bextOriginator -------------------------------------------------
+ value = getBextField( data, 256, 32 );
+ if ( value.size() > 0 )
+ handler->xmpObj.SetProperty( bextOriginator.ns , bextOriginator.prop, value.c_str() );
+
+ // bextOriginatorRef ----------------------------------------------
+ value = getBextField( data, 256+32, 32 );
+ if ( value.size() > 0 )
+ handler->xmpObj.SetProperty( bextOriginatorRef.ns , bextOriginatorRef.prop, value.c_str() );
+
+ // bextOriginationDate --------------------------------------------
+ value = getBextField( data, 256+32+32, 10 );
+ if ( value.size() > 0 )
+ handler->xmpObj.SetProperty( bextOriginationDate.ns , bextOriginationDate.prop, value.c_str() );
+
+ // bextOriginationTime --------------------------------------------
+ value = getBextField( data, 256+32+32+10, 8 );
+ if ( value.size() > 0 )
+ handler->xmpObj.SetProperty( bextOriginationTime.ns , bextOriginationTime.prop, value.c_str() );
+
+ // bextTimeReference ----------------------------------------------
+ // thanx to nice byte order, all 8 bytes can be read as one:
+ XMP_Uns64 timeReferenceFull = GetUns64LE( &(data[256+32+32+10+8 ] ) );
+ value.erase();
+ SXMPUtils::ConvertFromInt64( timeReferenceFull, "%llu", &value );
+ handler->xmpObj.SetProperty( bextTimeReference.ns, bextTimeReference.prop, value );
+
+ // bextVersion ----------------------------------------------------
+ XMP_Uns16 bwfVersion = GetUns16LE( &(data[256+32+32+10+8+8] ) );
+ value.erase();
+ SXMPUtils::ConvertFromInt( bwfVersion, "", &value );
+ handler->xmpObj.SetProperty( bextVersion.ns, bextVersion.prop, value );
+
+ // bextUMID -------------------------------------------------------
+ // binary string is already in memory, must convert to hex string
+ std::string umidString;
+ bool allZero = EncodeToHexString( &(data[256+32+32+10+8+8+2]), 64, &umidString );
+ if (! allZero )
+ handler->xmpObj.SetProperty( bextUMID.ns, bextUMID.prop, umidString );
+
+ // bextCodingHistory ----------------------------------------------
+ bool hasCodingHistory = bextChunk->oldSize > MIN_BEXT_SIZE;
+
+ if ( hasCodingHistory )
+ {
+ XMP_StringLen codingHistorySize = (XMP_StringLen) (bextChunk->oldSize - MIN_BEXT_SIZE);
+ std::string codingHistory;
+ convertToASCII( &data[MIN_BEXT_SIZE-8], codingHistorySize, &codingHistory, codingHistorySize );
+ if (! codingHistory.empty() )
+ handler->xmpObj.SetProperty( bextCodingHistory.ns, bextCodingHistory.prop, codingHistory );
+ }
+} // importBextChunkToXMP
- // If a write fails, it throws, so we return false
- return false;
+static void importPrmLToXMP( RIFF_MetaHandler* handler, ValueChunk* prmlChunk )
+{
+ bool haveXMP = false;
- }
-
- bool ok = MakeChunk ( inFileRef, inOutRiffState, riffType, (inBufferSize + 8) );
- if ( ! ok ) return false;
-
- return WriteChunk ( inFileRef, tagID, inBuffer, inBufferSize );
+ XMP_Enforce( prmlChunk->oldSize == PRML_SIZE );
+ PrmLBoxContent rawPrmL;
+ XMP_Assert ( sizeof ( rawPrmL ) == PRML_SIZE - 8 ); // double check tight packing.
+ XMP_Assert ( sizeof ( rawPrmL.filePath ) == 260 );
+ memcpy ( &rawPrmL, prmlChunk->oldValue.data(), sizeof (rawPrmL) );
+ if ( rawPrmL.magic != 0xBEEFCAFE ) {
+ Flip4 ( &rawPrmL.exportType ); // The only numeric field that we care about.
}
- // =============================================================================================
-
- bool RewriteChunk ( LFA_FileRef inFileRef, RiffState & inOutRiffState, long tagID, long parentID, const char * inData )
- {
- UInt32 len;
- UInt64 pos;
-
- try {
- if ( FindChunk ( inOutRiffState, tagID, parentID, 0, NULL, &len, &pos ) ) {
- LFA_Seek ( inFileRef, pos, SEEK_SET );
- LFA_Write ( inFileRef, inData, len );
- }
- } catch ( ... ) {
- return false;
+ rawPrmL.filePath[259] = 0; // Ensure a terminating nul.
+ if ( rawPrmL.filePath[0] != 0 ) {
+ if ( rawPrmL.filePath[0] == '/' ) {
+ haveXMP = true;
+ handler->xmpObj.SetStructField ( kXMP_NS_CreatorAtom, "macAtom",
+ kXMP_NS_CreatorAtom, "posixProjectPath", rawPrmL.filePath );
+ } else if ( XMP_LitNMatch ( rawPrmL.filePath, "\\\\?\\", 4 ) ) {
+ haveXMP = true;
+ handler->xmpObj.SetStructField ( kXMP_NS_CreatorAtom, "windowsAtom",
+ kXMP_NS_CreatorAtom, "uncProjectPath", rawPrmL.filePath );
}
-
- return true;
-
}
- // =============================================================================================
-
- bool MakeChunk ( LFA_FileRef inFileRef, RiffState & inOutRiffState, long riffType, UInt32 len )
- {
- long starttag, taglen;
- UInt32 rifflen, avail;
- UInt64 pos;
-
- /* look for top level Premiere padding chunk */
- starttag = 0;
- while ( FindChunk ( inOutRiffState, ckidPremierePadding, riffType, 0, &starttag, reinterpret_cast<unsigned long*>(&taglen), &pos ) ) {
-
- pos -= 8;
- taglen += 8;
- long extra = taglen - len;
- if ( extra < 0 ) continue;
-
- RiffIterator iter = inOutRiffState.tags.begin();
- iter += (starttag - 1);
+ const char * exportStr = 0;
+ switch ( rawPrmL.exportType ) {
+ case kExportTypeMovie : exportStr = "movie"; break;
+ case kExportTypeStill : exportStr = "still"; break;
+ case kExportTypeAudio : exportStr = "audio"; break;
+ case kExportTypeCustom : exportStr = "custom"; break;
+ }
+ if ( exportStr != 0 ) {
+ haveXMP = true;
+ handler->xmpObj.SetStructField ( kXMP_NS_DM, "projectRef", kXMP_NS_DM, "type", exportStr );
+ }
- if ( extra == 0 ) {
-
- iter->len = 0;
+ handler->containsXMP |= haveXMP; // mind the '|='
+} // importCr8rToXMP
- } else {
+static void importCr8rToXMP( RIFF_MetaHandler* handler, ValueChunk* cr8rChunk )
+{
+ bool haveXMP = false;
- atag pad;
- UInt64 padpos;
+ XMP_Enforce( cr8rChunk->oldSize == CR8R_SIZE );
+ Cr8rBoxContent rawCr8r;
+ XMP_Assert ( sizeof ( rawCr8r ) == CR8R_SIZE - 8 ); // double check tight packing.
+ memcpy ( &rawCr8r, cr8rChunk->oldValue.data(), sizeof (rawCr8r) );
- /* need 8 bytes extra to be able to split it */
- extra -= 8;
- if ( extra < 0 ) continue;
-
- try{
- padpos = pos + len;
- LFA_Seek ( inFileRef, padpos, SEEK_SET );
- pad.id = MakeUns32LE ( ckidPremierePadding );
- pad.len = MakeUns32LE ( extra );
- LFA_Write ( inFileRef, &pad, sizeof(pad) );
- } catch ( ... ) {
- return false;
- }
-
- iter->pos = padpos + 8;
- iter->len = extra;
+ if ( rawCr8r.magic != 0xBEEFCAFE ) {
+ Flip4 ( &rawCr8r.creatorCode ); // The only numeric fields that we care about.
+ Flip4 ( &rawCr8r.appleEvent );
+ }
- }
+ std::string fieldPath;
- /* seek back to start of original padding chunk */
- LFA_Seek ( inFileRef, pos, SEEK_SET );
+ SXMPUtils::ComposeStructFieldPath ( kXMP_NS_CreatorAtom, "macAtom", kXMP_NS_CreatorAtom, "applicationCode", &fieldPath );
+ if ( rawCr8r.creatorCode != 0 ) {
+ haveXMP = true;
+ handler->xmpObj.SetProperty_Int64 ( kXMP_NS_CreatorAtom, fieldPath.c_str(), (XMP_Int64)rawCr8r.creatorCode ); // ! Unsigned trickery.
+ }
- return true;
+ SXMPUtils::ComposeStructFieldPath ( kXMP_NS_CreatorAtom, "macAtom", kXMP_NS_CreatorAtom, "invocationAppleEvent", &fieldPath );
+ if ( rawCr8r.appleEvent != 0 ) {
+ haveXMP = true;
+ handler->xmpObj.SetProperty_Int64 ( kXMP_NS_CreatorAtom, fieldPath.c_str(), (XMP_Int64)rawCr8r.appleEvent ); // ! Unsigned trickery.
+ }
- }
-
- /* can't take padding chunk, so append new chunk to end of file */
-
- rifflen = inOutRiffState.rifflen + 8;
- avail = AVIMAXCHUNKSIZE - rifflen;
-
- LFA_Seek ( inFileRef, 0, SEEK_END );
- pos = GetFilePosition ( inFileRef );
-
- if ( (pos & 1) == 1 ) {
- // The file length is odd, need a pad byte.
- XMP_Uns8 pad = 0;
- LFA_Write ( inFileRef, &pad, 1 );
- ++pos;
- }
-
- if ( avail < len ) {
+ rawCr8r.fileExt[15] = 0; // Ensure a terminating nul.
+ if ( rawCr8r.fileExt[0] != 0 ) {
+ haveXMP = true;
+ handler->xmpObj.SetStructField ( kXMP_NS_CreatorAtom, "windowsAtom", kXMP_NS_CreatorAtom, "extension", rawCr8r.fileExt );
+ }
- /* if needed, create new AVIX chunk */
- ltag avix;
-
- avix.id = MakeUns32LE ( FOURCC_RIFF );
- avix.len = MakeUns32LE ( 4 + len );
- avix.subid = MakeUns32LE ( formtypeAVIX );
- LFA_Write(inFileRef, &avix, sizeof(avix));
+ rawCr8r.appOptions[15] = 0; // Ensure a terminating nul.
+ if ( rawCr8r.appOptions[0] != 0 ) {
+ haveXMP = true;
+ handler->xmpObj.SetStructField ( kXMP_NS_CreatorAtom, "windowsAtom", kXMP_NS_CreatorAtom, "invocationFlags", rawCr8r.appOptions );
+ }
+
+ rawCr8r.appName[31] = 0; // Ensure a terminating nul.
+ if ( rawCr8r.appName[0] != 0 ) {
+ haveXMP = true;
+ handler->xmpObj.SetProperty ( kXMP_NS_XMP, "CreatorTool", rawCr8r.appName );
+ }
- pos += 12;
- AddTag ( inOutRiffState, avix.id, len, pos, 0, 0, 0 );
+ handler->containsXMP |= haveXMP; // mind the '|='
+} // importCr8rToXMP
- } else {
- /* otherwise, rewrite length of last RIFF chunk in file */
- pos = inOutRiffState.riffpos + 4;
- rifflen = inOutRiffState.rifflen + len;
- XMP_Uns32 fileLen = MakeUns32LE ( rifflen );
- LFA_Seek ( inFileRef, pos, SEEK_SET );
- LFA_Write ( inFileRef, &fileLen, 4 );
- inOutRiffState.rifflen = rifflen;
-
- /* prepare to write data */
- LFA_Seek ( inFileRef, 0, SEEK_END );
+static void importListChunkToXMP( RIFF_MetaHandler* handler, ContainerChunk* listChunk, Mapping mapping[], bool xmpHasPriority )
+{
+ valueMap* cm = &listChunk->childmap;
+ for (int p=0; mapping[p].chunkID != 0; p++) // go through legacy chunks
+ {
+ valueMapIter result = cm->find(mapping[p].chunkID);
+ if( result != cm->end() ) // if value found
+ {
+ ValueChunk* propChunk = result->second;
+
+ bool propertyExists = false;
+ std::string utf8 = nativePropertyToUTF8(
+ propChunk->oldValue.c_str(),
+ (XMP_StringLen)propChunk->oldValue.size(), &propertyExists );
+
+ if ( utf8.size() > 0 ) // if property is not-empty, set Property
+ {
+ switch ( mapping[p].propType )
+ {
+ case prop_TIMEVALUE:
+ if ( xmpHasPriority &&
+ handler->xmpObj.DoesStructFieldExist( mapping[p].ns, mapping[p].prop, kXMP_NS_DM, "timeValue" ))
+ break; // skip if XMP has precedence and exists
+ handler->xmpObj.SetStructField( mapping[p].ns, mapping[p].prop,
+ kXMP_NS_DM, "timeValue", utf8.c_str() );
+ break;
+ case prop_LOCALIZED_TEXT:
+ if ( xmpHasPriority && handler->xmpObj.GetLocalizedText( mapping[p].ns ,
+ mapping[p].prop, "" , "x-default", 0, 0, 0 ))
+ break; // skip if XMP has precedence and exists
+ handler->xmpObj.SetLocalizedText( mapping[p].ns , mapping[p].prop,
+ "" , "x-default" , utf8.c_str() );
+ if ( mapping[p].chunkID == kPropChunkINAM )
+ handler->hasListInfoINAM = true; // needs to be known for special 3-way merge around dc:title
+ break;
+ case prop_ARRAYITEM:
+ if ( xmpHasPriority &&
+ handler->xmpObj.DoesArrayItemExist( mapping[p].ns, mapping[p].prop, 1 ))
+ break; // skip if XMP has precedence and exists
+ handler->xmpObj.DeleteProperty( mapping[p].ns, mapping[p].prop );
+ handler->xmpObj.AppendArrayItem( mapping[p].ns, mapping[p].prop, kXMP_PropValueIsArray, utf8.c_str(), kXMP_NoOptions );
+ break;
+ case prop_SIMPLE:
+ if ( xmpHasPriority &&
+ handler->xmpObj.DoesPropertyExist( mapping[p].ns, mapping[p].prop ))
+ break; // skip if XMP has precedence and exists
+ handler->xmpObj.SetProperty( mapping[p].ns, mapping[p].prop, utf8.c_str() );
+ break;
+ default:
+ XMP_Throw( "internal error" , kXMPErr_InternalFailure );
+ }
+ handler->containsXMP = true; // very important for treatment on caller level
+ }
+ else if ( ! propertyExists) // otherwise remove it.
+ { // [2389942] don't, if legacy value is existing but non-retrievable (due to server mode)
+ switch ( mapping[p].propType )
+ {
+ case prop_TIMEVALUE:
+ if ( (!xmpHasPriority) && // forward deletion only if XMP has no priority
+ handler->xmpObj.DoesPropertyExist( mapping[p].ns, mapping[p].prop ))
+ handler->xmpObj.DeleteProperty( mapping[p].ns, mapping[p].prop );
+ break;
+ case prop_LOCALIZED_TEXT:
+ if ( (!xmpHasPriority) && // forward deletion only if XMP has no priority
+ handler->xmpObj.DoesPropertyExist( mapping[p].ns, mapping[p].prop ))
+ handler->xmpObj.DeleteLocalizedText( mapping[p].ns, mapping[p].prop, "", "x-default" );
+ break;
+ case prop_ARRAYITEM:
+ case prop_SIMPLE:
+ if ( (!xmpHasPriority) && // forward deletion only if XMP has no priority
+ handler->xmpObj.DoesPropertyExist( mapping[p].ns, mapping[p].prop ))
+ handler->xmpObj.DeleteProperty( mapping[p].ns, mapping[p].prop );
+ break;
+ default:
+ XMP_Throw( "internal error" , kXMPErr_InternalFailure );
+ }
+ }
}
-
- return true;
-
+ } // for
+}
+void importProperties( RIFF_MetaHandler* handler )
+{
+ bool hasDigest = handler->xmpObj.GetProperty( kXMP_NS_WAV, "NativeDigest", NULL , NULL );
+ if ( hasDigest )
+ {
+ // remove! since it now becomse a 'new' handler file
+ handler->xmpObj.DeleteProperty( kXMP_NS_WAV, "NativeDigest" );
}
- // =============================================================================================
-
- bool WriteChunk ( LFA_FileRef inFileRef, long tagID, const char * data, UInt32 len )
+ // BWF Bext extension chunk -----------------------------------------------
+ if ( handler->parent->format == kXMP_WAVFile && // applies only to WAV
+ handler->bextChunk != 0 ) //skip if no BEXT chunk found.
{
- atag ck;
- ck.id = MakeUns32LE ( tagID );
- ck.len = MakeUns32LE ( len );
-
- try {
- LFA_Write ( inFileRef, &ck, 8 );
- LFA_Write ( inFileRef, data, len );
- } catch ( ... ) {
- return false;
- }
-
- return true;
+ importBextChunkToXMP( handler, handler->bextChunk );
}
- // =============================================================================================
-
- long OpenRIFF ( LFA_FileRef inFileRef, RiffState & inOutRiffState )
+ // PrmL chunk --------------------------------------------------------------
+ if ( handler->prmlChunk != 0 && handler->prmlChunk->oldSize == PRML_SIZE )
{
- UInt64 pos = 0;
- long tag, subtype;
- UInt32 len;
-
- const XMP_Int64 fileLen = LFA_Measure ( inFileRef );
- if ( fileLen < 8 ) return 0;
-
- LFA_Seek ( inFileRef, 0, SEEK_SET );
-
- while ( ReadTag ( inFileRef, &tag, &len, &subtype, pos, fileLen ) ) {
- if ( tag != FOURCC_RIFF ) break;
- AddTag ( inOutRiffState, tag, len, pos, 0, 0, subtype );
- if ( subtype != 0 ) SubRead ( inFileRef, inOutRiffState, subtype, len, pos );
- }
-
- return (long) inOutRiffState.tags.size();
-
+ importPrmLToXMP( handler, handler->prmlChunk );
}
- // =============================================================================================
-
- static bool ReadTag ( LFA_FileRef inFileRef, long * outTag, UInt32 * outLength, long * subtype, UInt64 & inOutPosition, UInt64 maxOffset )
+ // Cr8r chunk --------------------------------------------------------------
+ if ( handler->cr8rChunk != 0 && handler->cr8rChunk->oldSize == CR8R_SIZE )
{
- UInt32 realLength;
-
- long bytesRead;
- bytesRead = LFA_Read ( inFileRef, outTag, 4 );
- if ( bytesRead != 4 ) return false;
- *outTag = GetUns32LE ( outTag );
-
- bytesRead = LFA_Read ( inFileRef, outLength, 4 );
- if ( bytesRead != 4 ) return false;
- *outLength = GetUns32LE ( outLength );
-
- realLength = *outLength;
- realLength += (realLength & 1); // Round up to an even value.
-
- inOutPosition = GetFilePosition ( inFileRef ); // The file offset of the data portion.
- UInt64 maxLength = maxOffset - inOutPosition;
-
- if ( (inOutPosition > maxOffset) || ((UInt64)(*outLength) > maxLength) ) {
-
- bool ignoreLastPad = true; // Ignore cases where a final pad byte is missing.
- UInt64 fileLen = LFA_Measure ( inFileRef );
- if ( inOutPosition > (maxOffset + 1) ) ignoreLastPad = false;
- if ( (UInt64)(*outLength) > (maxLength + 1) ) ignoreLastPad = false;
-
- if ( ! ignoreLastPad ) {
-
- // Workaround for bad files in the field that have a bad size in the outermost RIFF
- // chunk. Do a "runtime repair" of cases where the length is too long (beyond EOF).
- // This handles read-only usage, update usage is repaired (or not) in the handler.
-
- bool oversizeRIFF = (inOutPosition == 8) && // Is this the initial 'RIFF' chunk?
- (fileLen >= 8); // Is the file at least of the minimal size?
-
- if ( ! oversizeRIFF ) {
- XMP_Throw ( "RIFF tag exceeds maximum length", kXMPErr_BadValue );
- } else {
- *outLength = (UInt32)(fileLen) - 8;
- realLength = *outLength;
- realLength += (realLength & 1); // Round up to an even value.
- }
+ importCr8rToXMP( handler, handler->cr8rChunk );
+ }
- }
+ // LIST:INFO --------------------------------------------------------------
+ if ( handler->listInfoChunk != 0) //skip if no LIST:INFO chunk found.
+ importListChunkToXMP( handler, handler->listInfoChunk, listInfoProps, hasDigest );
- }
-
- *subtype = 0;
+ // LIST:Tdat --------------------------------------------------------------
+ if ( handler->listTdatChunk != 0)
+ importListChunkToXMP( handler, handler->listTdatChunk, listTdatProps, hasDigest );
- if ( (*outTag != FOURCC_LIST) && (*outTag != FOURCC_RIFF) ) {
+ // DISP (do last, higher priority than INAM ) -----------------------------
+ bool takeXMP = false; // assume for now
+ if ( hasDigest )
+ {
+ std::string actualLang, value;
+ bool r = handler->xmpObj.GetLocalizedText( kXMP_NS_DC, "title", "" , "x-default" , &actualLang, &value, NULL );
+ if ( r && (actualLang == "x-default") ) takeXMP = true;
+ }
- UInt64 tempPos = inOutPosition + realLength;
- if ( tempPos <= maxOffset ) {
- LFA_Seek ( inFileRef, tempPos, SEEK_SET );
- } else if ( (tempPos == (maxOffset + 1)) && (maxOffset == (UInt64)LFA_Measure(inFileRef)) ) {
- LFA_Seek ( inFileRef, 0, SEEK_END ); // Hack to tolerate a missing final pad byte.
- } else {
- XMP_Throw ( "Bad RIFF offset", kXMPErr_BadValue );
+ if ( (!takeXMP) && handler->dispChunk != 0) //skip if no LIST:INFO chunk found.
+ {
+ std::string* value = &handler->dispChunk->oldValue;
+ if ( value->size() >= 4 ) // ignore contents if file too small
+ {
+ XMP_StringPtr cstring = value->c_str();
+ XMP_StringLen size = (XMP_StringLen) value->size();
+
+ size -= 4; // skip first four bytes known to contain constant
+ cstring += 4;
+
+ bool propertyExists = false;
+ std::string utf8 = nativePropertyToUTF8( cstring, size, &propertyExists );
+
+ if ( utf8.size() > 0 )
+ {
+ handler->xmpObj.SetLocalizedText( kXMP_NS_DC, "title", "" , "x-default" , utf8.c_str() );
+ handler->containsXMP = true; // very important for treatment on caller level
}
-
- } else {
-
- bytesRead = LFA_Read ( inFileRef, subtype, 4 );
- if ( bytesRead != 4 ) return false;
- *subtype = GetUns32LE ( subtype );
-
- *outLength -= 4;
- realLength -= 4;
-
- // Special case:
- // Since the 'movi' chunk can contain billions of subchunks, skip over the 'movi' subchunk.
- //
- // The 'movi' subtype is added to the list as the TAG.
- // The subtype is returned empty so nobody will try to parse the subchunks.
-
- if ( *subtype == listtypeAVIMOVIE ) {
- inOutPosition = GetFilePosition ( inFileRef );
- UInt64 tempPos = inOutPosition + realLength;
- if ( tempPos <= maxOffset ) {
- LFA_Seek ( inFileRef, tempPos, SEEK_SET );
- } else if ( (tempPos == (maxOffset + 1)) && (maxOffset == (UInt64)LFA_Measure(inFileRef)) ) {
- LFA_Seek ( inFileRef, 0, SEEK_END ); // Hack to tolerate a missing final pad byte.
- } else {
- XMP_Throw ( "Bad RIFF offset", kXMPErr_BadValue );
+ else
+ {
+ // found as part of [2389942]
+ // forward deletion may only happen if no LIST:INFO/INAM is present:
+ if ( ! handler->hasListInfoINAM &&
+ ! propertyExists ) // ..[2389942]part2: and if truly no legacy property
+ { // (not just an unreadable one due to ServerMode).
+ handler->xmpObj.DeleteProperty( kXMP_NS_DC, "title" );
}
- *outLength += 4;
- *outTag = *subtype;
- *subtype = 0;
}
+ } // if size sufficient
+ } // handler->dispChunk
- inOutPosition = GetFilePosition ( inFileRef );
+} // importProperties
- }
+////////////////////////////////////////////////////////////////////////////////
+// EXPORT
+////////////////////////////////////////////////////////////////////////////////
+
+void relocateWronglyPlacedXMPChunk( RIFF_MetaHandler* handler )
+{
+ LFA_FileRef file = handler->parent->fileRef;
+ RIFF::containerVect *rc = &handler->riffChunks;
+ RIFF::ContainerChunk* lastChunk = rc->at( rc->size()-1 );
+
+ // 1) XMPPacket
+ // needChunk exists but is not in lastChunk ?
+ if (
+ handler->xmpChunk != 0 && // XMP Chunk existing?
+ (XMP_Uns32)rc->size() > 1 && // more than 1 top-level chunk (otherwise pointless)
+ lastChunk->getChild( handler->xmpChunk ) == lastChunk->children.end() // not already in last chunk?
+ )
+ {
+ RIFF::ContainerChunk* cur;
+ chunkVectIter child;
+ XMP_Int32 chunkNo;
+
+ // find and relocate to last chunk:
+ for ( chunkNo = (XMP_Int32)rc->size()-2 ; chunkNo >= 0; chunkNo-- ) // ==> start with second-last chunk
+ {
+ cur = rc->at(chunkNo);
+ child = cur->getChild( handler->xmpChunk );
+ if ( child != cur->children.end() ) // found?
+ break;
+ } // for
+
+ if ( chunkNo < 0 ) // already in place? nothing left to do.
+ return;
+
+ lastChunk->children.push_back( *child ); // nb: order matters!
+ cur->replaceChildWithJunk( *child, false );
+ cur->hasChange = true; // [2414649] initialize early-on i.e: here
+ } // if
+} // relocateWronglyPlacedXMPChunk
+
+// writes to buffer up to max size,
+// 0 termination only if shorter than maxSize
+// converts down to ascii
+static void setBextField ( std::string* value, XMP_Uns8* data, XMP_Uns32 offset, XMP_Uns32 maxSize )
+{
+ XMP_Validate( value != 0, "setBextField: null value string pointer", kXMPErr_BadParam );
+ XMP_Validate( data != 0, "setBextField: null data value", kXMPErr_BadParam );
+ XMP_Validate( maxSize > 0, "setBextField: maxSize must be greater than 0", kXMPErr_BadParam );
- return true;
+ std::string ascii;
+ XMP_StringLen actualSize = convertToASCII( value->data(), (XMP_StringLen) value->size() , &ascii , maxSize );
+ strncpy( (char*)(data + offset), ascii.data(), actualSize );
+}
- }
+// add bwf-bext related data to bext chunk, create if not existing yet.
+// * in fact, since bext is fully fixed and known, there can be no unknown subchunks worth keeping:
+// * prepare bext chunk in buffer
+// * value changed/created if needed only, otherways remove chunk
+// * remove bext-mapped properties from xmp (non-redundant storage)
+// note: ValueChunk**: adress of pointer to allow changing the pointer itself (i.e. chunk creation)
+static void exportXMPtoBextChunk( RIFF_MetaHandler* handler, ValueChunk** bextChunk )
+{
+ // register bext namespace ( if there was no import, this is news, otherwise harmless moot)
+ SXMPMeta::RegisterNamespace( kXMP_NS_BWF, "bext:", 0 );
- // =============================================================================================
+ bool chunkUsed = false; // assume for now
+ SXMPMeta* xmp = &handler->xmpObj;
- static void AddTag ( RiffState & inOutRiffState, long tag, UInt32 len, UInt64 & inOutPosition, long parentID, long parentnum, long subtypeID )
+ // prepare buffer, need to know CodingHistory size as the only variable
+ XMP_Int32 bextBufferSize = MIN_BEXT_SIZE - 8; // -8 because of header
+ std::string value;
+ if ( xmp->GetProperty( bextCodingHistory.ns, bextCodingHistory.prop, &value, kXMP_NoOptions ))
{
- RiffTag newTag;
-
- newTag.pos = inOutPosition;
- newTag.tagID = tag;
- newTag.len = len;
- newTag.parent = parentnum;
- newTag.parentID = parentID;
- newTag.subtypeID = subtypeID;
-
- inOutRiffState.tags.push_back ( newTag );
-
- if ( tag == FOURCC_RIFF ) {
- inOutRiffState.riffpos = inOutPosition - 12;
- inOutRiffState.rifflen = len + 4;
- }
-
+ bextBufferSize += ((XMP_StringLen)value.size()) + 1 ; // add to size (and a trailing zero)
}
- // =============================================================================================
+ // create and clear buffer
+ XMP_Uns8* buffer = new XMP_Uns8[bextBufferSize];
+ for (XMP_Int32 i = 0; i < bextBufferSize; i++ )
+ buffer[i] = 0;
- static long SubRead ( LFA_FileRef inFileRef, RiffState & inOutRiffState, long parentid, UInt32 parentlen, UInt64 & inOutPosition )
+ // grab props, write into buffer, remove from XMP ///////////////////////////
+ // bextDescription ------------------------------------------------
+ if ( xmp->GetProperty( bextDescription.ns, bextDescription.prop, &value, kXMP_NoOptions ) )
{
- long tag;
- long subtype = 0;
- long parentnum;
- UInt32 len, total, childlen;
- UInt64 oldpos;
-
- total = 0;
- parentnum = (long) inOutRiffState.tags.size() - 1;
-
- UInt64 maxOffset = inOutPosition + parentlen;
-
- while ( parentlen > 0 ) {
-
- oldpos = inOutPosition;
- ReadTag ( inFileRef, &tag, &len, &subtype, inOutPosition, maxOffset );
- AddTag ( inOutRiffState, tag, len, inOutPosition, parentid, parentnum, subtype );
- len += (len & 1); //padding byte
-
- if ( subtype == 0 ) {
- childlen = 8 + len;
- } else {
- childlen = 12 + SubRead ( inFileRef, inOutRiffState, subtype, len, inOutPosition );
- }
-
- if ( parentlen < childlen ) parentlen = childlen;
- parentlen -= childlen;
- total += childlen;
-
- }
-
- return total;
-
+ setBextField( &value, (XMP_Uns8*) buffer, 0, 256 );
+ xmp->DeleteProperty( bextDescription.ns, bextDescription.prop) ;
+ chunkUsed = true;
}
-
- // =============================================================================================
-
- bool GetRIFFChunk ( LFA_FileRef inFileRef, RiffState & inOutRiffState, long tagID,
- long parentID, long subtypeID, char * outBuffer, unsigned long * outBufferSize,
- UInt64* posPtr )
+ // bextOriginator -------------------------------------------------
+ if ( xmp->GetProperty( bextOriginator.ns , bextOriginator.prop, &value, kXMP_NoOptions ) )
{
- UInt32 len;
- UInt64 pos;
-
- bool found = FindChunk ( inOutRiffState, tagID, parentID, subtypeID, 0, &len, &pos );
- if ( ! found ) return false;
-
- if ( posPtr != 0 )
- *posPtr = pos; // return position CBR
-
- if ( outBuffer == 0 ) {
- *outBufferSize = (unsigned long)len;
- return true; // Found, but not wanted.
- }
-
- if ( len > *outBufferSize )
- len = *outBufferSize;
- found = ReadChunk ( inFileRef, pos, len, outBuffer );
-
- return found;
+ setBextField( &value, (XMP_Uns8*) buffer, 256, 32 );
+ xmp->DeleteProperty( bextOriginator.ns , bextOriginator.prop );
+ chunkUsed = true;
+ }
+ // bextOriginatorRef ----------------------------------------------
+ if ( xmp->GetProperty( bextOriginatorRef.ns , bextOriginatorRef.prop, &value, kXMP_NoOptions ) )
+ {
+ setBextField( &value, (XMP_Uns8*) buffer, 256+32, 32 );
+ xmp->DeleteProperty( bextOriginatorRef.ns , bextOriginatorRef.prop );
+ chunkUsed = true;
+ }
+ // bextOriginationDate --------------------------------------------
+ if ( xmp->GetProperty( bextOriginationDate.ns , bextOriginationDate.prop, &value, kXMP_NoOptions ) )
+ {
+ setBextField( &value, (XMP_Uns8*) buffer, 256+32+32, 10 );
+ xmp->DeleteProperty( bextOriginationDate.ns , bextOriginationDate.prop );
+ chunkUsed = true;
+ }
+ // bextOriginationTime --------------------------------------------
+ if ( xmp->GetProperty( bextOriginationTime.ns , bextOriginationTime.prop, &value, kXMP_NoOptions ) )
+ {
+ setBextField( &value, (XMP_Uns8*) buffer, 256+32+32+10, 8 );
+ xmp->DeleteProperty( bextOriginationTime.ns , bextOriginationTime.prop );
+ chunkUsed = true;
+ }
+ // bextTimeReference ----------------------------------------------
+ // thanx to friendly byte order, all 8 bytes can be written in one go:
+ if ( xmp->GetProperty( bextTimeReference.ns, bextTimeReference.prop, &value, kXMP_NoOptions ) )
+ {
+ try
+ {
+ XMP_Int64 v = SXMPUtils::ConvertToInt64( value.c_str() );
+ PutUns64LE( v, &(buffer[256+32+32+10+8] ));
+ chunkUsed = true;
+ }
+ catch (XMP_Error& e)
+ {
+ if ( e.GetID() != kXMPErr_BadParam )
+ throw e; // re-throw on any other error
+ } // 'else' tolerate ( time reference remains 0x00000000 )
+ // valid or not, do not store redundantly:
+ xmp->DeleteProperty( bextTimeReference.ns, bextTimeReference.prop );
}
- // =============================================================================================
+ // bextVersion ----------------------------------------------------
+ // set version=1, no matter what.
+ PutUns16LE( 1, &(buffer[256+32+32+10+8+8]) );
+ xmp->DeleteProperty( bextVersion.ns, bextVersion.prop );
- bool FindChunk ( RiffState & inOutRiffState, long tagID, long parentID, long subtypeID,
- long * startTagIndex, UInt32 * len, UInt64 * pos)
+ // bextUMID -------------------------------------------------------
+ if ( xmp->GetProperty( bextUMID.ns, bextUMID.prop, &value, kXMP_NoOptions ) )
{
- std::vector<RiffTag>::iterator iter = inOutRiffState.tags.begin();
- std::vector<RiffTag>::iterator endIter = inOutRiffState.tags.end();
-
- // If we're using the next index, skip the iterator.
- if ( startTagIndex != 0 ) iter += *startTagIndex;
+ std::string rawStr;
- for ( ; iter != endIter ; ++iter ) {
+ if ( !DecodeFromHexString( value.data(), (XMP_StringLen) value.size(), &rawStr ) )
+ {
+ delete [] buffer; // important.
+ XMP_Throw ( "EncodeFromHexString: illegal umid string. Must contain an even number of 0-9 and uppercase A-F chars.", kXMPErr_BadParam );
+ }
- if ( startTagIndex != 0 ) *startTagIndex += 1;
-
- if ( (parentID!= 0) && (iter->parentID != parentID) ) continue;
- if ( (tagID != 0) && (iter->tagID != tagID) ) continue;
- if ( (subtypeID != 0) && (iter->subtypeID != subtypeID) ) continue;
-
- if ( len != 0 ) *len = iter->len;
- if ( pos != 0 ) *pos = iter->pos;
-
- return true;
+ // if UMID is smaller/longer than 64 byte for any reason,
+ // truncate/do a partial write (just like for any other bext property)
- }
-
- return false;
+ memcpy( (char*) &(buffer[256+32+32+10+8+8+2]), rawStr.data(), MIN( 64, rawStr.size() ) );
+ xmp->DeleteProperty( bextUMID.ns, bextUMID.prop );
+ chunkUsed = true;
}
- // =============================================================================================
-
- static bool ReadChunk ( LFA_FileRef inFileRef, UInt64 & pos, UInt32 len, char * outBuffer )
+ // bextCodingHistory ----------------------------------------------
+ if ( xmp->GetProperty( bextCodingHistory.ns, bextCodingHistory.prop, &value, kXMP_NoOptions ) )
{
-
- if ( (inFileRef == 0) || (outBuffer == 0) ) return false;
-
- LFA_Seek (inFileRef, pos, SEEK_SET );
- UInt32 bytesRead = LFA_Read ( inFileRef, outBuffer, len );
- if ( bytesRead != len ) return false;
-
- return true;
-
+ std::string ascii;
+ convertToASCII( value.data(), (XMP_StringLen) value.size() , &ascii, (XMP_StringLen) value.size() );
+ strncpy( (char*) &(buffer[MIN_BEXT_SIZE-8]), ascii.data(), ascii.size() );
+ xmp->DeleteProperty( bextCodingHistory.ns, bextCodingHistory.prop );
+ chunkUsed = true;
}
-} // namespace RIFF_Support
-
-// =================================================================================================
-
-// *** Could be moved to a separate header
-
-#pragma pack(push,1)
-
-// [TODO] Can we switch to using just a full path here?
-struct FSSpecLegacy
-{
- short vRefNum;
- long parID;
- char name[260]; // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/fileio/fs/naming_a_file.asp -- 260 is "old school", 32000 is "new school".
-};
-
-struct CR8R_CreatorAtom
-{
- unsigned long magicLu;
-
- long atom_sizeL; // Size of this structure.
- short atom_vers_majorS; // Major atom version.
- short atom_vers_minorS; // Minor atom version.
-
- // mac
- unsigned long creator_codeLu; // Application code on MacOS.
- unsigned long creator_eventLu; // Invocation appleEvent.
-
- // windows
- char creator_extAC[16]; // Extension allowing registry search to app.
- char creator_flagAC[16]; // Flag passed to app at invocation time.
-
- char creator_nameAC[32]; // Name of the creator application.
-};
-
-typedef CR8R_CreatorAtom** CR8R_CreatorAtomHandle;
+ // always delete old, recreate if needed
+ if ( *bextChunk != 0 )
+ {
+ (*bextChunk)->parent->replaceChildWithJunk( *bextChunk );
+ (*bextChunk) = 0; // clear direct Chunk pointer
+ }
-#define PR_PROJECT_LINK_MAGIC 0x600DF00D // GoodFood
+ if ( chunkUsed)
+ *bextChunk = new ValueChunk( handler->riffChunks.at(0), std::string( (char*)buffer, bextBufferSize ), kChunk_bext );
-typedef enum
-{
- Embed_ExportTypeMovie = 0,
- Embed_ExportTypeStill,
- Embed_ExportTypeAudio,
- Embed_ExportTypeCustom
+ delete [] buffer; // important.
}
-Embed_ExportType;
-
-struct Embed_ProjectLinkAtom
+static inline void SetBufferedString ( char * dest, const std::string source, size_t limit )
{
- unsigned long magicLu;
- long atom_sizeL;
- short atom_vers_apiS;
- short atom_vers_codeS;
- unsigned long exportType; // See enum. The type of export that generated the file
- FSSpecLegacy fullPath; // Full path of the project file
-};
-
-#pragma pack(pop)
-
-// -------------------------------------------------------------------------------------------------
-
-#define kCreatorTool "CreatorTool"
-#define AdobeCreatorAtomVersion_Major 1
-#define AdobeCreatorAtomVersion_Minor 0
-#define AdobeCreatorAtom_Magic 0xBEEFCAFE
-
-#define myCreatorAtom MakeFourCC ( 'C','r','8','r' )
+ memset ( dest, 0, limit );
+ size_t count = source.size();
+ if ( count >= limit ) count = limit - 1; // Ensure a terminating nul.
+ memcpy ( dest, source.c_str(), count );
+}
-static void CreatorAtom_Initialize ( CR8R_CreatorAtom& creatorAtom )
+static void exportXMPtoCr8rChunk ( RIFF_MetaHandler* handler, ValueChunk** cr8rChunk )
{
- memset ( &creatorAtom, 0, sizeof(CR8R_CreatorAtom) );
- creatorAtom.magicLu = AdobeCreatorAtom_Magic;
- creatorAtom.atom_vers_majorS = AdobeCreatorAtomVersion_Major;
- creatorAtom.atom_vers_minorS = AdobeCreatorAtomVersion_Minor;
- creatorAtom.atom_sizeL = sizeof(CR8R_CreatorAtom);
-}
+ const SXMPMeta & xmp = handler->xmpObj;
+
+ // Make sure an existing Cr8r chunk has the proper fixed length.
+ bool haveOldCr8r = (*cr8rChunk != 0);
+ if ( haveOldCr8r && ((*cr8rChunk)->oldSize != sizeof(Cr8rBoxContent)+8) ) {
+ (*cr8rChunk)->parent->replaceChildWithJunk ( *cr8rChunk ); // Wrong length, the existing chunk must be bad.
+ (*cr8rChunk) = 0;
+ haveOldCr8r = false;
+ }
-// -------------------------------------------------------------------------------------------------
+ bool haveNewCr8r = false;
+ std::string creatorCode, appleEvent, fileExt, appOptions, appName;
+
+ haveNewCr8r |= xmp.GetStructField ( kXMP_NS_CreatorAtom, "macAtom", kXMP_NS_CreatorAtom, "applicationCode", &creatorCode, 0 );
+ haveNewCr8r |= xmp.GetStructField ( kXMP_NS_CreatorAtom, "macAtom", kXMP_NS_CreatorAtom, "invocationAppleEvent", &appleEvent, 0 );
+ haveNewCr8r |= xmp.GetStructField ( kXMP_NS_CreatorAtom, "windowsAtom", kXMP_NS_CreatorAtom, "extension", &fileExt, 0 );
+ haveNewCr8r |= xmp.GetStructField ( kXMP_NS_CreatorAtom, "windowsAtom", kXMP_NS_CreatorAtom, "invocationFlags", &appOptions, 0 );
+ haveNewCr8r |= xmp.GetProperty ( kXMP_NS_XMP, "CreatorTool", &appName, 0 );
-static void CreatorAtom_MakeValid ( CR8R_CreatorAtom * creator_atomP )
-{
- // If already valid, no conversion is needed.
- if ( creator_atomP->magicLu == AdobeCreatorAtom_Magic ) return;
+ if ( ! haveNewCr8r ) { // Get rid of an existing Cr8r chunk if there is no new XMP.
+ if ( haveOldCr8r ) {
+ (*cr8rChunk)->parent->replaceChildWithJunk ( *cr8rChunk );
+ *cr8rChunk = 0;
+ }
+ return;
+ }
+
+ if ( ! haveOldCr8r ) {
+ *cr8rChunk = new ValueChunk ( handler->lastChunk, std::string(), kChunk_Cr8r );
+ }
+
+ std::string strValue;
+ strValue.assign ( (sizeof(Cr8rBoxContent) - 1), '\0' ); // ! Use size-1 because SetValue appends a trailing 0 byte.
+ (*cr8rChunk)->SetValue ( strValue ); // ! Just get the space available.
+ XMP_Assert ( (*cr8rChunk)->newValue.size() == sizeof(Cr8rBoxContent) );
+ (*cr8rChunk)->hasChange = true;
- Flip4 ( &creator_atomP->magicLu );
- Flip2 ( &creator_atomP->atom_vers_majorS );
- Flip2 ( &creator_atomP->atom_vers_minorS );
+ Cr8rBoxContent * newCr8r = (Cr8rBoxContent*) (*cr8rChunk)->newValue.data();
+
+ if ( ! haveOldCr8r ) {
- Flip4 ( &creator_atomP->atom_sizeL );
- Flip4 ( &creator_atomP->creator_codeLu );
- Flip4 ( &creator_atomP->creator_eventLu );
+ newCr8r->magic = MakeUns32LE ( 0xBEEFCAFE );
+ newCr8r->size = MakeUns32LE ( sizeof(Cr8rBoxContent) );
+ newCr8r->majorVer = MakeUns16LE ( 1 );
- XMP_Assert ( creator_atomP->magicLu == AdobeCreatorAtom_Magic );
-}
+ } else {
-// -------------------------------------------------------------------------------------------------
+ const Cr8rBoxContent * oldCr8r = (Cr8rBoxContent*) (*cr8rChunk)->oldValue.data();
+ memcpy ( newCr8r, oldCr8r, sizeof(Cr8rBoxContent) );
+ if ( GetUns32LE ( &newCr8r->magic ) != 0xBEEFCAFE ) { // Make sure we write LE numbers.
+ Flip4 ( &newCr8r->magic );
+ Flip4 ( &newCr8r->size );
+ Flip2 ( &newCr8r->majorVer );
+ Flip2 ( &newCr8r->minorVer );
+ Flip4 ( &newCr8r->creatorCode );
+ Flip4 ( &newCr8r->appleEvent );
+ }
-static void CreatorAtom_ToBE ( CR8R_CreatorAtom * creator_atomP )
-{
- creator_atomP->atom_vers_majorS = MakeUns16BE ( creator_atomP->atom_vers_majorS );
- creator_atomP->atom_vers_minorS = MakeUns16BE ( creator_atomP->atom_vers_minorS );
+ }
+
+ if ( ! creatorCode.empty() ) {
+ newCr8r->creatorCode = MakeUns32LE ( (XMP_Uns32) strtoul ( creatorCode.c_str(), 0, 0 ) );
+ }
+
+ if ( ! appleEvent.empty() ) {
+ newCr8r->appleEvent = MakeUns32LE ( (XMP_Uns32) strtoul ( appleEvent.c_str(), 0, 0 ) );
+ }
+
+ if ( ! fileExt.empty() ) SetBufferedString ( newCr8r->fileExt, fileExt, sizeof ( newCr8r->fileExt ) );
+ if ( ! appOptions.empty() ) SetBufferedString ( newCr8r->appOptions, appOptions, sizeof ( newCr8r->appOptions ) );
+ if ( ! appName.empty() ) SetBufferedString ( newCr8r->appName, appName, sizeof ( newCr8r->appName ) );
- creator_atomP->magicLu = MakeUns32BE ( creator_atomP->magicLu );
- creator_atomP->atom_sizeL = MakeUns32BE ( creator_atomP->atom_sizeL );
- creator_atomP->creator_codeLu = MakeUns32BE ( creator_atomP->creator_codeLu );
- creator_atomP->creator_eventLu = MakeUns32BE ( creator_atomP->creator_eventLu );
}
-// -------------------------------------------------------------------------------------------------
-
-static void ProjectLinkAtom_MakeValid ( Embed_ProjectLinkAtom * link_atomP )
+static void exportXMPtoListChunk( XMP_Uns32 id, XMP_Uns32 containerType,
+ RIFF_MetaHandler* handler, ContainerChunk** listChunk, Mapping mapping[])
{
- // If already valid, no conversion is needed.
- if ( link_atomP->magicLu == PR_PROJECT_LINK_MAGIC ) return;
-
- // do the header
- Flip4 ( &link_atomP->magicLu );
- Flip4 ( &link_atomP->atom_sizeL );
- Flip2 ( &link_atomP->atom_vers_apiS );
- Flip2 ( &link_atomP->atom_vers_codeS );
+ // note: ContainerChunk**: adress of pointer to allow changing the pointer itself (i.e. chunk creation)
+ SXMPMeta* xmp = &handler->xmpObj;
+ bool listChunkIsNeeded = false; // assume for now
+
+ // ! The NUL is optional in WAV to avoid a parsing bug in Audition 3 - can't handle implicit pad byte.
+ bool optionalNUL = (handler->parent->format == kXMP_WAVFile);
- // do the FSSpec data
- Flip2 ( &link_atomP->fullPath.vRefNum );
- Flip4 ( &link_atomP->fullPath.parID );
+ for ( int p=0; mapping[p].chunkID != 0; ++p ) { // go through all potential property mappings
- XMP_Assert ( link_atomP->magicLu == PR_PROJECT_LINK_MAGIC );
-}
+ bool propExists = false;
+ std::string value, actualLang;
-// -------------------------------------------------------------------------------------------------
+ switch ( mapping[p].propType ) {
-static std::string CharsToString ( const char* buffer, int maxBuffer )
-{
- // convert possibly non-zero terminated char buffer to std::string
- std::string result;
+ // get property. if existing, remove from XMP (to avoid redundant storage)
+ case prop_TIMEVALUE:
+ propExists = xmp->GetStructField ( mapping[p].ns, mapping[p].prop, kXMP_NS_DM, "timeValue", &value, 0 );
+ break;
- char bufferz[256];
- XMP_Assert ( maxBuffer < 256 );
- if ( maxBuffer >= 256 ) return result;
+ case prop_LOCALIZED_TEXT:
+ propExists = xmp->GetLocalizedText ( mapping[p].ns, mapping[p].prop, "", "x-default", &actualLang, &value, 0);
+ if ( actualLang != "x-default" ) propExists = false; // no "x-default" => nothing to reconcile !
+ break;
- memcpy ( bufferz, buffer, maxBuffer );
- bufferz[maxBuffer] = 0;
+ case prop_ARRAYITEM:
+ propExists = xmp->GetArrayItem ( mapping[p].ns, mapping[p].prop, 1, &value, 0 );
+ break;
- result = bufferz;
- return result;
+ case prop_SIMPLE:
+ propExists = xmp->GetProperty ( mapping[p].ns, mapping[p].prop, &value, 0 );
+ break;
-}
+ default:
+ XMP_Throw ( "internal error", kXMPErr_InternalFailure );
-// -------------------------------------------------------------------------------------------------
+ }
-bool CreatorAtom::Import ( SXMPMeta& xmpObj,
- LFA_FileRef fileRef,
- RIFF_Support::RiffState& riffState )
-{
- static const long myProjectLink = MakeFourCC ( 'P','r','m','L' );
-
- unsigned long projectLinkSize;
- bool ok = RIFF_Support::GetRIFFChunk ( fileRef, riffState, myProjectLink, 0, 0, 0, &projectLinkSize );
- if ( ok ) {
+ if ( ! propExists ) {
- Embed_ProjectLinkAtom epla;
+ if ( *listChunk != 0 ) (*listChunk)->removeValue ( mapping[p].chunkID );
- std::string projectPathString;
- RIFF_Support::GetRIFFChunk ( fileRef, riffState, myProjectLink, 0, 0, (char*) &epla, &projectLinkSize );
- if ( ok ) {
- ProjectLinkAtom_MakeValid ( &epla );
- projectPathString = epla.fullPath.name;
- }
+ } else {
- if ( ! projectPathString.empty() ) {
+ listChunkIsNeeded = true;
+ if ( *listChunk == 0 ) *listChunk = new ContainerChunk ( handler->riffChunks[0], id, containerType );
- if ( projectPathString[0] == '/' ) {
- xmpObj.SetStructField ( kXMP_NS_CreatorAtom, "macAtom",
- kXMP_NS_CreatorAtom, "posixProjectPath", projectPathString, 0 );
- } else if ( projectPathString.substr(0,4) == std::string("\\\\?\\") ) {
- xmpObj.SetStructField ( kXMP_NS_CreatorAtom, "windowsAtom",
- kXMP_NS_CreatorAtom, "uncProjectPath", projectPathString, 0 );
- }
+ valueMap* cm = &(*listChunk)->childmap;
+ valueMapIter result = cm->find( mapping[p].chunkID );
+ ValueChunk* propChunk = 0;
- std::string projectTypeString;
- switch ( epla.exportType ) {
- case Embed_ExportTypeMovie : projectTypeString = "movie"; break;
- case Embed_ExportTypeStill : projectTypeString = "still"; break;
- case Embed_ExportTypeAudio : projectTypeString = "audio"; break;
- case Embed_ExportTypeCustom : projectTypeString = "custom"; break;
+ if ( result != cm->end() ) {
+ propChunk = result->second;
+ } else {
+ propChunk = new ValueChunk ( *listChunk, std::string(), mapping[p].chunkID );
}
- if ( ! projectTypeString.empty() ) {
- xmpObj.SetStructField ( kXMP_NS_DM, "projectRef", kXMP_NS_DM, "type", projectTypeString.c_str() );
- }
+ propChunk->SetValue ( value.c_str(), optionalNUL );
}
- }
-
- unsigned long creatorAtomSize = 0;
- ok = RIFF_Support::GetRIFFChunk ( fileRef, riffState, myCreatorAtom, 0, 0, 0, &creatorAtomSize );
- if ( ok ) {
-
- CR8R_CreatorAtom creatorAtom;
- ok = RIFF_Support::GetRIFFChunk ( fileRef, riffState, myCreatorAtom, 0, 0, (char*) &creatorAtom, &creatorAtomSize );
+ } // for each property
- if ( ok ) {
-
- CreatorAtom_MakeValid ( &creatorAtom );
-
- char buffer[256];
- std::string xmpString;
-
- sprintf ( buffer, "%d", creatorAtom.creator_codeLu );
- xmpString = buffer;
- xmpObj.SetStructField ( kXMP_NS_CreatorAtom, "macAtom", kXMP_NS_CreatorAtom, "applicationCode", xmpString, 0 );
-
- sprintf ( buffer, "%d", creatorAtom.creator_eventLu );
- xmpString = buffer;
- xmpObj.SetStructField ( kXMP_NS_CreatorAtom, "macAtom", kXMP_NS_CreatorAtom, "invocationAppleEvent", xmpString, 0 );
+ if ( (! listChunkIsNeeded) && (*listChunk != 0) && ((*listChunk)->children.size() == 0) ) {
+ (*listChunk)->parent->replaceChildWithJunk ( *listChunk );
+ (*listChunk) = 0; // reset direct Chunk pointer
+ }
- xmpString = CharsToString ( creatorAtom.creator_extAC, sizeof(creatorAtom.creator_extAC) );
- xmpObj.SetStructField ( kXMP_NS_CreatorAtom, "windowsAtom", kXMP_NS_CreatorAtom, "extension", xmpString, 0 );
+}
- xmpString = CharsToString ( creatorAtom.creator_flagAC, sizeof(creatorAtom.creator_flagAC) );
- xmpObj.SetStructField ( kXMP_NS_CreatorAtom, "windowsAtom", kXMP_NS_CreatorAtom, "invocationFlags", xmpString, 0 );
+void exportAndRemoveProperties ( RIFF_MetaHandler* handler )
+{
+ SXMPMeta xmpObj = handler->xmpObj;
- xmpString = CharsToString ( creatorAtom.creator_nameAC, sizeof(creatorAtom.creator_nameAC) );
- xmpObj.SetProperty ( kXMP_NS_XMP, "CreatorTool", xmpString, 0 );
-
- }
+ exportXMPtoCr8rChunk ( handler, &handler->cr8rChunk );
+ // 1/4 BWF Bext extension chunk -----------------------------------------------
+ if ( handler->parent->format == kXMP_WAVFile ) { // applies only to WAV
+ exportXMPtoBextChunk ( handler, &handler->bextChunk );
}
- return ok;
-
-}
+ // 2/4 DISP chunk
+ if ( handler->parent->format == kXMP_WAVFile ) { // create for WAVE only
-// -------------------------------------------------------------------------------------------------
+ std::string actualLang, xmpValue;
+ bool r = xmpObj.GetLocalizedText ( kXMP_NS_DC, "title", "" , "x-default" , &actualLang, &xmpValue, 0 );
-// *** Not in C library:
-#ifndef min
- #define min(a,b) ( (a < b) ? a : b )
-#endif
+ if ( r && ( actualLang == "x-default" ) ) { // prop exists?
-#define EnsureFinalNul(buffer) buffer [ sizeof(buffer) - 1 ] = 0
+ // the 'right' DISP is lead by a 32 bit low endian 0x0001
+ std::string dispValue = std::string( "\x1\0\0\0", 4 );
+ dispValue.append ( xmpValue );
-bool CreatorAtom::Update ( SXMPMeta& xmpObj,
- LFA_FileRef fileRef,
- long riffType,
- RIFF_Support::RiffState& riffState )
-{
+ if ( handler->dispChunk == 0 ) {
+ handler->dispChunk = new RIFF::ValueChunk ( handler->riffChunks.at(0), std::string(), kChunk_DISP );
+ }
- // Creator Atom related values.
- bool found = false;
- std::string posixPathString, uncPathString;
- if ( xmpObj.GetStructField ( kXMP_NS_CreatorAtom, "macAtom", kXMP_NS_CreatorAtom, "posixProjectPath", &posixPathString, 0 ) ) found = true;
- if ( xmpObj.GetStructField ( kXMP_NS_CreatorAtom, "windowsAtom", kXMP_NS_CreatorAtom, "uncProjectPath", &uncPathString, 0 ) ) found = true;
+ // ! The NUL is optional in WAV to avoid a parsing bug in Audition 3 - can't handle implicit pad byte.
+ handler->dispChunk->SetValue ( dispValue, ValueChunk::kNULisOptional );
- std::string applicationCodeString, invocationAppleEventString, extensionString, invocationFlagsString, creatorToolString;
- if ( xmpObj.GetStructField ( kXMP_NS_CreatorAtom, "macAtom", kXMP_NS_CreatorAtom, "applicationCode", &applicationCodeString, 0 ) ) found = true;
- if ( xmpObj.GetStructField ( kXMP_NS_CreatorAtom, "macAtom", kXMP_NS_CreatorAtom, "invocationAppleEvent", &invocationAppleEventString, 0 ) ) found = true;
- if ( xmpObj.GetStructField ( kXMP_NS_CreatorAtom, "windowsAtom", kXMP_NS_CreatorAtom, "extension", &extensionString, 0 ) ) found = true;
- if ( xmpObj.GetStructField ( kXMP_NS_CreatorAtom, "windowsAtom", kXMP_NS_CreatorAtom, "invocationFlags", &invocationFlagsString, 0 ) ) found = true;
- if ( xmpObj.GetProperty ( kXMP_NS_XMP, "CreatorTool", &creatorToolString, 0 ) ) found = true;
+ } else { // remove Disp Chunk..
- // No Creator Atom information present.
- if ( ! found ) return true;
+ if ( handler->dispChunk != 0 ) { // ..if existing
+ ContainerChunk* mainChunk = handler->riffChunks.at(0);
+ Chunk* needle = handler->dispChunk;
+ chunkVectIter iter = mainChunk->getChild ( needle );
+ if ( iter != mainChunk->children.end() ) {
+ mainChunk->replaceChildWithJunk ( *iter );
+ handler->dispChunk = 0;
+ mainChunk->hasChange = true;
+ }
+ }
- // Read Legacy Creator Atom.
- unsigned long creatorAtomSize = 0;
- CR8R_CreatorAtom creatorAtomLegacy;
- CreatorAtom_Initialize ( creatorAtomLegacy );
- bool ok = RIFF_Support::GetRIFFChunk ( fileRef, riffState, myCreatorAtom, 0, 0, 0, &creatorAtomSize );
- if ( ok ) {
- XMP_Assert ( creatorAtomSize == sizeof(CR8R_CreatorAtom) );
- ok = RIFF_Support::GetRIFFChunk ( fileRef, riffState, myCreatorAtom, 0, 0, (char*) &creatorAtomLegacy, &creatorAtomSize );
- CreatorAtom_MakeValid ( &creatorAtomLegacy );
- }
+ }
- // Generate new Creator Atom from XMP.
- CR8R_CreatorAtom creatorAtomViaXMP;
- CreatorAtom_Initialize ( creatorAtomViaXMP );
- if ( ! applicationCodeString.empty() ) {
- creatorAtomViaXMP.creator_codeLu = strtoul ( applicationCodeString.c_str(), 0, 0 );
- }
- if ( ! invocationAppleEventString.empty() ) {
- creatorAtomViaXMP.creator_eventLu = strtoul ( invocationAppleEventString.c_str(), 0, 0 );
- }
- if ( ! extensionString.empty() ) {
- strncpy ( creatorAtomViaXMP.creator_extAC, extensionString.c_str(), sizeof(creatorAtomViaXMP.creator_extAC) );
- EnsureFinalNul ( creatorAtomViaXMP.creator_extAC );
- }
- if ( ! invocationFlagsString.empty() ) {
- strncpy ( creatorAtomViaXMP.creator_flagAC, invocationFlagsString.c_str(), sizeof(creatorAtomViaXMP.creator_flagAC) );
- EnsureFinalNul ( creatorAtomViaXMP.creator_flagAC );
- }
- if ( ! creatorToolString.empty() ) {
- strncpy ( creatorAtomViaXMP.creator_nameAC, creatorToolString.c_str(), sizeof(creatorAtomViaXMP.creator_nameAC) );
- EnsureFinalNul ( creatorAtomViaXMP.creator_nameAC );
}
- // Write new Creator Atom, if necessary.
- if ( memcmp ( &creatorAtomViaXMP, &creatorAtomLegacy, sizeof(CR8R_CreatorAtom) ) != 0 ) {
- CreatorAtom_ToBE ( &creatorAtomViaXMP );
- ok = RIFF_Support::PutChunk ( fileRef, riffState, riffType, myCreatorAtom, (char*)&creatorAtomViaXMP, sizeof(CR8R_CreatorAtom) );
- }
+ // 3/4 LIST:INFO
+ exportXMPtoListChunk ( kChunk_LIST, kType_INFO, handler, &handler->listInfoChunk, listInfoProps );
- return ok;
+ // 4/4 LIST:Tdat
+ exportXMPtoListChunk ( kChunk_LIST, kType_Tdat, handler, &handler->listTdatChunk, listTdatProps );
}
+
+} // namespace RIFF
diff --git a/source/XMPFiles/FormatSupport/RIFF_Support.hpp b/source/XMPFiles/FormatSupport/RIFF_Support.hpp
index 8065e38..a0b972b 100644
--- a/source/XMPFiles/FormatSupport/RIFF_Support.hpp
+++ b/source/XMPFiles/FormatSupport/RIFF_Support.hpp
@@ -3,7 +3,7 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2007 Adobe Systems Incorporated
+// Copyright 2009 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -11,183 +11,32 @@
// =================================================================================================
#include "XMP_Environment.h" // ! This must be the first include.
-
#include <vector>
-
#include "XMPFiles_Impl.hpp"
-#define MakeFourCC(a,b,c,d) ((long)a | ((long)b << 8) | ((long)c << 16) | ((long)d << 24))
-
-#if XMP_WinBuild
- #include <vfw.h>
-#else
- #ifndef FOURCC_RIFF
- #define FOURCC_RIFF MakeFourCC ('R', 'I', 'F', 'F')
- #endif
- #ifndef FOURCC_LIST
- #define FOURCC_LIST MakeFourCC ('L', 'I', 'S', 'T')
- #endif
- #ifndef listtypeAVIMOVIE
- #define listtypeAVIMOVIE MakeFourCC ('m', 'o', 'v', 'i')
- #endif
-#endif
-
-namespace RIFF_Support
-{
- // Some types, if not already defined
- #ifndef UInt64
- typedef unsigned long long UInt64;
- #endif
- #ifndef UInt32
- typedef unsigned long UInt32;
- #endif
+// ahead declaration:
+class RIFF_MetaHandler;
- /**
- ** Obtain the meta-data for the tagID provided.
- ** Returns true for success
- */
- bool GetMetaData ( LFA_FileRef inFileRef, long tagID, char * outBuffer, unsigned long * outBufferSize );
+namespace RIFF {
- /**
- ** Write the meta-data for the tagID provided.
- ** Returns true for success
- */
- bool SetMetaData ( LFA_FileRef inFileRef, long riffType, long tagID, const char * inBuffer, unsigned long inBufferSize );
-
-
-
- /**
- ** A class to hold the information
- ** about a particular chunk.
- */
- class RiffTag {
- public:
+ // declare ahead
+ class Chunk;
+ class ContainerChunk;
+ class ValueChunk;
+ class XMPChunk;
- RiffTag() : pos(0), tagID(0), len(0), parent(0), parentID(0), subtypeID(0) {}
- virtual ~RiffTag() {}
-
- UInt64 pos; /* file offset of chunk data */
- long tagID; /* ckid of chunk */
- UInt32 len; /* length of chunk data */
- long parent; /* chunk# of parent */
- long parentID; /* FOURCC of parent */
- long subtypeID; /* Subtype of the tag (aka LIST ID) */
-
- };
-
- typedef std::vector<RiffTag> RiffVector;
- typedef RiffVector::iterator RiffIterator;
-
- /**
- ** A class to hold a table of the parsed
- ** chunks from a file. Its validity
- ** expires when new chunks are added.
- */
- class RiffState {
- public:
-
- RiffState() : riffpos(0), rifflen(0), next(0) {}
- virtual ~RiffState() {}
-
- UInt64 riffpos; /* file offset of current RIFF */
- long rifflen; /* length of RIFF incl. header */
- long next; /* next one to search */
- RiffVector tags; /* vector of chunks */
-
- };
-
- struct ltag {
- long id;
- UInt32 len;
- long subid;
- };
-
- /**
- ** Read from the RIFF file, and build a table of the chunks
- ** in the RIFFState class provided.
- ** Returns the number of chunks found.
- */
- long OpenRIFF ( LFA_FileRef inFileRef, RiffState & inOutRiffState );
+ /* This rountines imports the properties found into the
+ xmp packet. Use after parsing. */
+ void importProperties( RIFF_MetaHandler* handler );
- /**
- ** Get a chunk from an existing RIFFState, obtained from
- ** a call to OpenRIFF.
- ** If NULL is passed for the outBuffer, the outBufferSize parameter
- ** will contain the field size if true if returned.
- **
- ** Returns true if the chunk is found.
- **
- ** position of chunk _contents_ is returned in postPtr
- */
- bool GetRIFFChunk ( LFA_FileRef inFileRef, RiffState & inOutRiffState, long tagID, long parentID,
- long subtypeID, char * outBuffer, unsigned long * outBufferSize, UInt64* posPtr = 0);
+ /* This rountines exports XMP properties to the respective Chunks,
+ creating those if needed. No writing to file here. */
+ void exportAndRemoveProperties( RIFF_MetaHandler* handler );
+ /* will relocated a wrongly placed chunk (one of XMP, LIST:Info, LIST:Tdat=
+ from RIFF::avix back to main chunk. Chunk itself not touched. */
+ void relocateWronglyPlacedXMPChunk( RIFF_MetaHandler* handler );
- /**
- ** The routine finds an existing list and tags it as Padding
- **
- ** Returns true if success
- */
- bool MarkChunkAsPadding ( LFA_FileRef inFileRef, RiffState & inOutRiffState, long riffType, long tagID, long subtypeID );
-
-
- /**
- ** The routine finds an existing location to put the chunk into if
- ** available, otherwise it creates a new chunk and writes to it.
- **
- ** Returns true if success
- */
- bool PutChunk ( LFA_FileRef inFileRef, RiffState & inOutRiffState, long riffType, long tagID, const char * inBuffer, UInt32 inBufferSize );
-
- /**
- ** Locates the position of a chunk.
- ** All parameters except the RiffState are optional.
- **
- ** Return if found.
- */
- bool FindChunk ( RiffState & inOutRiffState, long tagID, long parentID, long subtypeID, long * starttag, UInt32 * len, UInt64 * pos );
-
- /**
- ** Low level routine to write a chunk.
- **
- ** Returns true if write succeeded.
- */
- bool WriteChunk ( LFA_FileRef inFileRef, long tagID, const char * data, UInt32 len );
-
- /**
- ** Rewrites data into an existing chunk, not writing the header like WriteChunk
- **
- ** Returns true if found and write succeeded.
- */
- bool RewriteChunk ( LFA_FileRef inFileRef, RiffState & inOutRiffState, long tagID, long parentID, const char * inData );
-
- /**
- ** Attempts to find a location to write a chunk, and if not found, prepares a chunk
- ** at the end of the file.
- **
- ** Returns true if successful.
- */
- bool MakeChunk ( LFA_FileRef inFileRef, RiffState & inOutRiffState, long riffType, UInt32 len );
-
-} // namespace RIFF_Support
-
-// =================================================================================================
-
-// *** Could be moved to a separate header
-
-namespace CreatorAtom {
-
- bool Import ( SXMPMeta& xmpObj,
- LFA_FileRef fileRef,
- RIFF_Support::RiffState& riffState );
-
- bool Update ( SXMPMeta& xmpObj,
- LFA_FileRef fileRef,
- long riffType,
- RIFF_Support::RiffState& riffState );
-
-}
-
-// =================================================================================================
+} // namespace RIFF
#endif // __RIFF_Support_hpp__
diff --git a/source/XMPFiles/FormatSupport/ReconcileIPTC.cpp b/source/XMPFiles/FormatSupport/ReconcileIPTC.cpp
index a81e8af..bef1b11 100644
--- a/source/XMPFiles/FormatSupport/ReconcileIPTC.cpp
+++ b/source/XMPFiles/FormatSupport/ReconcileIPTC.cpp
@@ -1,6 +1,6 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2006-2007 Adobe Systems Incorporated
+// Copyright 2006 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -32,11 +32,11 @@ static inline void NormalizeToCR ( std::string * value )
{
char * strPtr = (char*) value->data();
char * strEnd = strPtr + value->size();
-
+
for ( ; strPtr < strEnd; ++strPtr ) {
if ( *strPtr == kLF ) *strPtr = kCR;
}
-
+
} // NormalizeToCR
// =================================================================================================
@@ -47,11 +47,11 @@ static inline void NormalizeToLF ( std::string * value )
{
char * strPtr = (char*) value->data();
char * strEnd = strPtr + value->size();
-
+
for ( ; strPtr < strEnd; ++strPtr ) {
if ( *strPtr == kCR ) *strPtr = kLF;
}
-
+
} // NormalizeToLF
// =================================================================================================
@@ -60,70 +60,52 @@ static inline void NormalizeToLF ( std::string * value )
//
// Compute a 128 bit (16 byte) MD5 digest of the full IPTC block.
-static inline void ComputeIPTCDigest ( IPTC_Manager * iptc, MD5_Digest * digest )
+static inline void ComputeIPTCDigest ( const void * iptcPtr, const XMP_Uns32 iptcLen, MD5_Digest * digest )
{
- MD5_CTX context;
- void * iptcData;
- XMP_Uns32 iptcLen;
+ MD5_CTX context;
- iptcLen = iptc->UpdateMemoryDataSets ( &iptcData );
-
MD5Init ( &context );
- MD5Update ( &context, (XMP_Uns8*)iptcData, iptcLen );
+ MD5Update ( &context, (XMP_Uns8*)iptcPtr, iptcLen );
MD5Final ( *digest, &context );
} // ComputeIPTCDigest;
// =================================================================================================
-// ReconcileUtils::CheckIPTCDigest
+// PhotoDataUtils::CheckIPTCDigest
// ===============================
-int ReconcileUtils::CheckIPTCDigest ( IPTC_Manager * iptc, const PSIR_Manager & psir )
+int PhotoDataUtils::CheckIPTCDigest ( const void * newPtr, const XMP_Uns32 newLen, const void * oldDigest )
{
MD5_Digest newDigest;
- PSIR_Manager::ImgRsrcInfo ir1061;
-
- ComputeIPTCDigest ( iptc, &newDigest );
- bool found = psir.GetImgRsrc ( kPSIR_IPTCDigest, &ir1061 );
-
- if ( ! found ) return kDigestMissing;
- if ( ir1061.dataLen != 16 ) return kDigestMissing;
-
- if ( memcmp ( newDigest, ir1061.dataPtr, 16 ) == 0 ) return kDigestMatches;
+ ComputeIPTCDigest ( newPtr, newLen, &newDigest );
+ if ( memcmp ( &newDigest, oldDigest, 16 ) == 0 ) return kDigestMatches;
return kDigestDiffers;
-
-} // ReconcileUtils::CheckIPTCDigest
+
+} // PhotoDataUtils::CheckIPTCDigest
// =================================================================================================
-// ReconcileUtils::SetIPTCDigest
-// ===============================
+// PhotoDataUtils::SetIPTCDigest
+// =============================
-void ReconcileUtils::SetIPTCDigest ( IPTC_Manager * iptc, PSIR_Manager * psir )
+void PhotoDataUtils::SetIPTCDigest ( void * iptcPtr, XMP_Uns32 iptcLen, PSIR_Manager * psir )
{
MD5_Digest newDigest;
-
- ComputeIPTCDigest ( iptc, &newDigest );
+
+ ComputeIPTCDigest ( iptcPtr, iptcLen, &newDigest );
psir->SetImgRsrc ( kPSIR_IPTCDigest, &newDigest, sizeof(newDigest) );
-
-} // ReconcileUtils::SetIPTCDigest
+
+} // PhotoDataUtils::SetIPTCDigest
// =================================================================================================
// =================================================================================================
// =================================================================================================
-// ImportIPTC_Simple
-// =================
+// PhotoDataUtils::ImportIPTC_Simple
+// =================================
-static void ImportIPTC_Simple ( const IPTC_Manager & iptc, SXMPMeta * xmp, int digestState,
- XMP_Uns8 id, const char * xmpNS, const char * xmpProp )
+void PhotoDataUtils::ImportIPTC_Simple ( const IPTC_Manager & iptc, SXMPMeta * xmp,
+ XMP_Uns8 id, const char * xmpNS, const char * xmpProp )
{
- if ( digestState == kDigestDiffers ) {
- xmp->DeleteProperty ( xmpNS, xmpProp );
- } else {
- XMP_Assert ( digestState == kDigestMissing );
- if ( xmp->DoesPropertyExist ( xmpNS, xmpProp ) ) return;
- }
-
std::string utf8Str;
size_t count = iptc.GetDataSet_UTF8 ( id, &utf8Str );
@@ -132,59 +114,183 @@ static void ImportIPTC_Simple ( const IPTC_Manager & iptc, SXMPMeta * xmp, int d
xmp->SetProperty ( xmpNS, xmpProp, utf8Str.c_str() );
}
-} // ImportIPTC_Simple
+} // PhotoDataUtils::ImportIPTC_Simple
// =================================================================================================
-// ImportIPTC_LangAlt
-// ==================
+// PhotoDataUtils::ImportIPTC_LangAlt
+// ==================================
-static void ImportIPTC_LangAlt ( const IPTC_Manager & iptc, SXMPMeta * xmp, int digestState,
- XMP_Uns8 id, const char * xmpNS, const char * xmpProp )
+void PhotoDataUtils::ImportIPTC_LangAlt ( const IPTC_Manager & iptc, SXMPMeta * xmp,
+ XMP_Uns8 id, const char * xmpNS, const char * xmpProp )
{
- if ( digestState == kDigestDiffers ) {
- std::string xdItemPath = xmpProp; // Delete just the x-default item, not the whole array.
- xdItemPath += "[?xml:lang='x-default']";
- xmp->DeleteProperty ( xmpNS, xdItemPath.c_str() );
- } else {
- XMP_Assert ( digestState == kDigestMissing );
- if ( xmp->DoesPropertyExist ( xmpNS, xmpProp ) ) return; // Check the entire array here.
- }
-
std::string utf8Str;
-
size_t count = iptc.GetDataSet_UTF8 ( id, &utf8Str );
-
+
if ( count != 0 ) {
NormalizeToLF ( &utf8Str );
xmp->SetLocalizedText ( xmpNS, xmpProp, "", "x-default", utf8Str.c_str() );
}
-} // ImportIPTC_LangAlt
+} // PhotoDataUtils::ImportIPTC_LangAlt
// =================================================================================================
-// ImportIPTC_Array
-// ================
+// PhotoDataUtils::ImportIPTC_Array
+// ================================
-static void ImportIPTC_Array ( const IPTC_Manager & iptc, SXMPMeta * xmp, int digestState,
- XMP_Uns8 id, const char * xmpNS, const char * xmpProp )
+void PhotoDataUtils::ImportIPTC_Array ( const IPTC_Manager & iptc, SXMPMeta * xmp,
+ XMP_Uns8 id, const char * xmpNS, const char * xmpProp )
{
- if ( digestState == kDigestDiffers ) {
- xmp->DeleteProperty ( xmpNS, xmpProp );
- } else {
- XMP_Assert ( digestState == kDigestMissing );
- if ( xmp->DoesPropertyExist ( xmpNS, xmpProp ) ) return;
- }
-
std::string utf8Str;
size_t count = iptc.GetDataSet ( id, 0 );
+ xmp->DeleteProperty ( xmpNS, xmpProp );
+
+ XMP_OptionBits arrayForm = kXMP_PropArrayIsUnordered;
+ if ( XMP_LitMatch ( xmpNS, kXMP_NS_DC ) && XMP_LitMatch ( xmpProp, "creator" ) ) arrayForm = kXMP_PropArrayIsOrdered;
+
for ( size_t ds = 0; ds < count; ++ds ) {
(void) iptc.GetDataSet_UTF8 ( id, &utf8Str, ds );
NormalizeToLF ( &utf8Str );
- xmp->AppendArrayItem ( xmpNS, xmpProp, kXMP_PropArrayIsUnordered, utf8Str.c_str() );
+ xmp->AppendArrayItem ( xmpNS, xmpProp, arrayForm, utf8Str.c_str() );
}
-} // ImportIPTC_Array
+} // PhotoDataUtils::ImportIPTC_Array
+
+// =================================================================================================
+// PhotoDataUtils::ImportIPTC_Date
+// ===============================
+//
+// An IPTC (IIM) date is 8 characters, YYYYMMDD. Include the time portion if it is present. The IPTC
+// time is HHMMSSxHHMM, where 'x' is '+' or '-'. Be tolerant of some ill-formed dates and times.
+// Apparently some non-Adobe apps put strings like "YYYY-MM-DD" or "HH:MM:SSxHH:MM" in the IPTC.
+// Allow a missing time zone portion.
+
+// *** The date/time handling differs from the MWG 1.0.1 policy, following a proposed tweak to MWG:
+// *** Exif DateTimeOriginal <-> XMP exif:DateTimeOriginal
+// *** IPTC DateCreated <-> XMP photoshop:DateCreated
+// *** Exif DateTimeDigitized <-> IPTC DigitalCreateDate <-> XMP xmp:CreateDate
+
+void PhotoDataUtils::ImportIPTC_Date ( XMP_Uns8 dateID, const IPTC_Manager & iptc, SXMPMeta * xmp )
+{
+ XMP_Uns8 timeID;
+ XMP_StringPtr xmpNS, xmpProp;
+
+ if ( dateID == kIPTC_DateCreated ) {
+ timeID = kIPTC_TimeCreated;
+ xmpNS = kXMP_NS_Photoshop;
+ xmpProp = "DateCreated";
+ } else if ( dateID == kIPTC_DigitalCreateDate ) {
+ timeID = kIPTC_DigitalCreateTime;
+ xmpNS = kXMP_NS_XMP;
+ xmpProp = "CreateDate";
+ } else {
+ XMP_Throw ( "Unrecognized dateID", kXMPErr_BadParam );
+ }
+
+ // First gather the date portion.
+
+ IPTC_Manager::DataSetInfo dsInfo;
+ size_t count = iptc.GetDataSet ( dateID, &dsInfo );
+ if ( count == 0 ) return;
+
+ size_t chPos, digits;
+ XMP_DateTime xmpDate;
+ memset ( &xmpDate, 0, sizeof(xmpDate) );
+
+ chPos = 0;
+ for ( digits = 0; digits < 4; ++digits, ++chPos ) {
+ if ( (chPos >= dsInfo.dataLen) || (dsInfo.dataPtr[chPos] < '0') || (dsInfo.dataPtr[chPos] > '9') ) break;
+ xmpDate.year = (xmpDate.year * 10) + (dsInfo.dataPtr[chPos] - '0');
+ }
+
+ if ( dsInfo.dataPtr[chPos] == '-' ) ++chPos;
+ for ( digits = 0; digits < 2; ++digits, ++chPos ) {
+ if ( (chPos >= dsInfo.dataLen) || (dsInfo.dataPtr[chPos] < '0') || (dsInfo.dataPtr[chPos] > '9') ) break;
+ xmpDate.month = (xmpDate.month * 10) + (dsInfo.dataPtr[chPos] - '0');
+ }
+ if ( xmpDate.month < 1 ) xmpDate.month = 1;
+ if ( xmpDate.month > 12 ) xmpDate.month = 12;
+
+ if ( dsInfo.dataPtr[chPos] == '-' ) ++chPos;
+ for ( digits = 0; digits < 2; ++digits, ++chPos ) {
+ if ( (chPos >= dsInfo.dataLen) || (dsInfo.dataPtr[chPos] < '0') || (dsInfo.dataPtr[chPos] > '9') ) break;
+ xmpDate.day = (xmpDate.day * 10) + (dsInfo.dataPtr[chPos] - '0');
+ }
+ if ( xmpDate.day < 1 ) xmpDate.day = 1;
+ if ( xmpDate.day > 31 ) xmpDate.day = 28; // Close enough.
+
+ if ( chPos != dsInfo.dataLen ) return; // The DataSet is ill-formed.
+ xmpDate.hasDate = true;
+
+ // Now add the time portion if present.
+
+ count = iptc.GetDataSet ( timeID, &dsInfo );
+ if ( count != 0 ) {
+
+ chPos = 0;
+ for ( digits = 0; digits < 2; ++digits, ++chPos ) {
+ if ( (chPos >= dsInfo.dataLen) || (dsInfo.dataPtr[chPos] < '0') || (dsInfo.dataPtr[chPos] > '9') ) break;
+ xmpDate.hour = (xmpDate.hour * 10) + (dsInfo.dataPtr[chPos] - '0');
+ }
+ if ( xmpDate.hour < 0 ) xmpDate.hour = 0;
+ if ( xmpDate.hour > 23 ) xmpDate.hour = 23;
+
+ if ( dsInfo.dataPtr[chPos] == ':' ) ++chPos;
+ for ( digits = 0; digits < 2; ++digits, ++chPos ) {
+ if ( (chPos >= dsInfo.dataLen) || (dsInfo.dataPtr[chPos] < '0') || (dsInfo.dataPtr[chPos] > '9') ) break;
+ xmpDate.minute = (xmpDate.minute * 10) + (dsInfo.dataPtr[chPos] - '0');
+ }
+ if ( xmpDate.minute < 0 ) xmpDate.minute = 0;
+ if ( xmpDate.minute > 59 ) xmpDate.minute = 59;
+
+ if ( dsInfo.dataPtr[chPos] == ':' ) ++chPos;
+ for ( digits = 0; digits < 2; ++digits, ++chPos ) {
+ if ( (chPos >= dsInfo.dataLen) || (dsInfo.dataPtr[chPos] < '0') || (dsInfo.dataPtr[chPos] > '9') ) break;
+ xmpDate.second = (xmpDate.second * 10) + (dsInfo.dataPtr[chPos] - '0');
+ }
+ if ( xmpDate.second < 0 ) xmpDate.second = 0;
+ if ( xmpDate.second > 59 ) xmpDate.second = 59;
+
+ xmpDate.hasTime = true;
+
+ if ( (dsInfo.dataPtr[chPos] != ' ') && (dsInfo.dataPtr[chPos] != 0) ) { // Tolerate a missing TZ.
+
+ if ( dsInfo.dataPtr[chPos] == '+' ) {
+ xmpDate.tzSign = kXMP_TimeEastOfUTC;
+ } else if ( dsInfo.dataPtr[chPos] == '-' ) {
+ xmpDate.tzSign = kXMP_TimeWestOfUTC;
+ } else if ( chPos != dsInfo.dataLen ) {
+ return; // The DataSet is ill-formed.
+ }
+
+ ++chPos; // Move past the time zone sign.
+ for ( digits = 0; digits < 2; ++digits, ++chPos ) {
+ if ( (chPos >= dsInfo.dataLen) || (dsInfo.dataPtr[chPos] < '0') || (dsInfo.dataPtr[chPos] > '9') ) break;
+ xmpDate.tzHour = (xmpDate.tzHour * 10) + (dsInfo.dataPtr[chPos] - '0');
+ }
+ if ( xmpDate.tzHour < 0 ) xmpDate.tzHour = 0;
+ if ( xmpDate.tzHour > 23 ) xmpDate.tzHour = 23;
+
+ if ( dsInfo.dataPtr[chPos] == ':' ) ++chPos;
+ for ( digits = 0; digits < 2; ++digits, ++chPos ) {
+ if ( (chPos >= dsInfo.dataLen) || (dsInfo.dataPtr[chPos] < '0') || (dsInfo.dataPtr[chPos] > '9') ) break;
+ xmpDate.tzMinute = (xmpDate.tzMinute * 10) + (dsInfo.dataPtr[chPos] - '0');
+ }
+ if ( xmpDate.tzMinute < 0 ) xmpDate.tzMinute = 0;
+ if ( xmpDate.tzMinute > 59 ) xmpDate.tzMinute = 59;
+
+ if ( chPos != dsInfo.dataLen ) return; // The DataSet is ill-formed.
+ xmpDate.hasTimeZone = true;
+
+ }
+
+ }
+
+ // Finally, set the XMP property.
+
+ xmp->SetProperty_Date ( xmpNS, xmpProp, xmpDate );
+
+} // PhotoDataUtils::ImportIPTC_Date
// =================================================================================================
// ImportIPTC_IntellectualGenre
@@ -195,24 +301,16 @@ static void ImportIPTC_Array ( const IPTC_Manager & iptc, SXMPMeta * xmp, int di
// XMP and the number is dropped. Also, even though IIMv4.1 says that 2:04 is repeatable, the XMP
// property to which it is mapped is simple.
-static void ImportIPTC_IntellectualGenre ( const IPTC_Manager & iptc, SXMPMeta * xmp, int digestState,
- const char * xmpNS, const char * xmpProp )
+static void ImportIPTC_IntellectualGenre ( const IPTC_Manager & iptc, SXMPMeta * xmp )
{
- if ( digestState == kDigestDiffers ) {
- xmp->DeleteProperty ( xmpNS, xmpProp );
- } else {
- XMP_Assert ( digestState == kDigestMissing );
- if ( xmp->DoesPropertyExist ( xmpNS, xmpProp ) ) return;
- }
-
std::string utf8Str;
size_t count = iptc.GetDataSet_UTF8 ( kIPTC_IntellectualGenre, &utf8Str );
if ( count == 0 ) return;
NormalizeToLF ( &utf8Str );
-
+
XMP_StringPtr namePtr = utf8Str.c_str() + 4;
-
+
if ( utf8Str.size() <= 4 ) {
// No name in the IIM. Look up the number in our list of known genres.
int i;
@@ -224,7 +322,7 @@ static void ImportIPTC_IntellectualGenre ( const IPTC_Manager & iptc, SXMPMeta *
namePtr = kIntellectualGenreMappings[i].name;
}
- xmp->SetProperty ( xmpNS, xmpProp, namePtr );
+ xmp->SetProperty ( kXMP_NS_IPTCCore, "IntellectualGenre", namePtr );
} // ImportIPTC_IntellectualGenre
@@ -237,19 +335,11 @@ static void ImportIPTC_IntellectualGenre ( const IPTC_Manager & iptc, SXMPMeta *
// levels of the reference number hierarchy. The IPTC4XMP mapping rule is that only the reference
// number is imported to XMP.
-static void ImportIPTC_SubjectCode ( const IPTC_Manager & iptc, SXMPMeta * xmp, int digestState,
- const char * xmpNS, const char * xmpProp )
+static void ImportIPTC_SubjectCode ( const IPTC_Manager & iptc, SXMPMeta * xmp )
{
- if ( digestState == kDigestDiffers ) {
- xmp->DeleteProperty ( xmpNS, xmpProp );
- } else {
- XMP_Assert ( digestState == kDigestMissing );
- if ( xmp->DoesPropertyExist ( xmpNS, xmpProp ) ) return;
- }
-
std::string utf8Str;
size_t count = iptc.GetDataSet_UTF8 ( kIPTC_SubjectCode, 0 );
-
+
for ( size_t ds = 0; ds < count; ++ds ) {
(void) iptc.GetDataSet_UTF8 ( kIPTC_SubjectCode, &utf8Str, ds );
@@ -263,179 +353,92 @@ static void ImportIPTC_SubjectCode ( const IPTC_Manager & iptc, SXMPMeta * xmp,
if ( (refNumEnd - refNumPtr) != 8 ) continue; // This DataSet is ill-formed.
*refNumEnd = 0; // Ensure a terminating nul for the reference number portion.
- xmp->AppendArrayItem ( xmpNS, xmpProp, kXMP_PropArrayIsUnordered, refNumPtr );
+ xmp->AppendArrayItem ( kXMP_NS_IPTCCore, "SubjectCode", kXMP_PropArrayIsUnordered, refNumPtr );
}
} // ImportIPTC_SubjectCode
// =================================================================================================
-// ImportIPTC_DateCreated
-// ======================
-//
-// An IPTC (IIM) date is 8 charcters YYYYMMDD. Include the time portion from 2:60 if it is present.
-// The IPTC time is HHMMSSxHHMM, where 'x' is '+' or '-'. Be tolerant of some ill-formed dates and
-// times. Apparently some non-Adobe apps put strings like "YYYY-MM-DD" or "HH:MM:SSxHH:MM" in the
-// IPTC. Allow a missing time zone portion to mean UTC.
+// PhotoDataUtils::Import2WayIPTC
+// ==============================
-static void ImportIPTC_DateCreated ( const IPTC_Manager & iptc, SXMPMeta * xmp, int digestState,
- const char * xmpNS, const char * xmpProp )
+void PhotoDataUtils::Import2WayIPTC ( const IPTC_Manager & iptc, SXMPMeta * xmp, int iptcDigestState )
{
- if ( digestState == kDigestDiffers ) {
- xmp->DeleteProperty ( xmpNS, xmpProp );
- } else {
- XMP_Assert ( digestState == kDigestMissing );
- if ( xmp->DoesPropertyExist ( xmpNS, xmpProp ) ) return;
- }
+ if ( iptcDigestState == kDigestMatches ) return; // Ignore the IPTC if the digest matches.
- // First gather the date portion.
-
- IPTC_Manager::DataSetInfo dsInfo;
- size_t count = iptc.GetDataSet ( kIPTC_DateCreated, &dsInfo );
- if ( count == 0 ) return;
-
- size_t chPos, digits;
- XMP_DateTime xmpDate;
- memset ( &xmpDate, 0, sizeof(xmpDate) );
-
- for ( chPos = 0, digits = 0; digits < 4; ++digits, ++chPos ) {
- if ( (chPos >= dsInfo.dataLen) || (dsInfo.dataPtr[chPos] < '0') || (dsInfo.dataPtr[chPos] > '9') ) break;
- xmpDate.year = (xmpDate.year * 10) + (dsInfo.dataPtr[chPos] - '0');
+ std::string oldStr, newStr;
+ IPTC_Writer oldIPTC;
+
+ if ( iptcDigestState == kDigestDiffers ) {
+ PhotoDataUtils::ExportIPTC ( *xmp, &oldIPTC ); // Predict old IPTC DataSets based on the existing XMP.
}
- if ( dsInfo.dataPtr[chPos] == '-' ) ++chPos;
- for ( digits = 0; digits < 2; ++digits, ++chPos ) {
- if ( (chPos >= dsInfo.dataLen) || (dsInfo.dataPtr[chPos] < '0') || (dsInfo.dataPtr[chPos] > '9') ) break;
- xmpDate.month = (xmpDate.month * 10) + (dsInfo.dataPtr[chPos] - '0');
- }
- if ( xmpDate.month < 1 ) xmpDate.month = 1;
- if ( xmpDate.month > 12 ) xmpDate.month = 12;
+ size_t newCount;
+ IPTC_Manager::DataSetInfo newInfo, oldInfo;
- if ( dsInfo.dataPtr[chPos] == '-' ) ++chPos;
- for ( digits = 0; digits < 2; ++digits, ++chPos ) {
- if ( (chPos >= dsInfo.dataLen) || (dsInfo.dataPtr[chPos] < '0') || (dsInfo.dataPtr[chPos] > '9') ) break;
- xmpDate.day = (xmpDate.day * 10) + (dsInfo.dataPtr[chPos] - '0');
- }
- if ( xmpDate.day < 1 ) xmpDate.day = 1;
- if ( xmpDate.day > 31 ) xmpDate.day = 28; // Close enough.
-
- if ( chPos != dsInfo.dataLen ) return; // The DataSet is ill-formed.
+ for ( size_t i = 0; kKnownDataSets[i].id != 255; ++i ) {
- // Now add the time portion if present.
-
- count = iptc.GetDataSet ( kIPTC_TimeCreated, &dsInfo );
- if ( count != 0 ) {
-
- for ( chPos = 0, digits = 0; digits < 2; ++digits, ++chPos ) {
- if ( (chPos >= dsInfo.dataLen) || (dsInfo.dataPtr[chPos] < '0') || (dsInfo.dataPtr[chPos] > '9') ) break;
- xmpDate.hour = (xmpDate.hour * 10) + (dsInfo.dataPtr[chPos] - '0');
- }
- if ( xmpDate.hour < 0 ) xmpDate.hour = 0;
- if ( xmpDate.hour > 23 ) xmpDate.hour = 23;
-
- if ( dsInfo.dataPtr[chPos] == ':' ) ++chPos;
- for ( digits = 0; digits < 2; ++digits, ++chPos ) {
- if ( (chPos >= dsInfo.dataLen) || (dsInfo.dataPtr[chPos] < '0') || (dsInfo.dataPtr[chPos] > '9') ) break;
- xmpDate.minute = (xmpDate.minute * 10) + (dsInfo.dataPtr[chPos] - '0');
- }
- if ( xmpDate.minute < 0 ) xmpDate.minute = 0;
- if ( xmpDate.minute > 59 ) xmpDate.minute = 59;
+ const DataSetCharacteristics & thisDS = kKnownDataSets[i];
+ if ( thisDS.mapForm >= kIPTC_Map3Way ) continue; // The mapping is handled elsewhere, or not at all.
- if ( dsInfo.dataPtr[chPos] == ':' ) ++chPos;
- for ( digits = 0; digits < 2; ++digits, ++chPos ) {
- if ( (chPos >= dsInfo.dataLen) || (dsInfo.dataPtr[chPos] < '0') || (dsInfo.dataPtr[chPos] > '9') ) break;
- xmpDate.second = (xmpDate.second * 10) + (dsInfo.dataPtr[chPos] - '0');
- }
- if ( xmpDate.second < 0 ) xmpDate.second = 0;
- if ( xmpDate.second > 59 ) xmpDate.second = 59;
+ bool haveXMP = xmp->DoesPropertyExist ( thisDS.xmpNS, thisDS.xmpProp );
+ newCount = PhotoDataUtils::GetNativeInfo ( iptc, thisDS.id, iptcDigestState, haveXMP, &newInfo );
+ if ( newCount == 0 ) continue; // GetNativeInfo returns 0 for ignored local text.
- if ( dsInfo.dataPtr[chPos] == '+' ) {
- xmpDate.tzSign = kXMP_TimeEastOfUTC;
- } else if ( dsInfo.dataPtr[chPos] == '-' ) {
- xmpDate.tzSign = kXMP_TimeWestOfUTC;
- } else if ( chPos != dsInfo.dataLen ) {
- return; // The DataSet is ill-formed.
+ if ( iptcDigestState == kDigestMissing ) {
+ if ( haveXMP ) continue; // Keep the existing XMP.
+ } else if ( ! PhotoDataUtils::IsValueDifferent ( iptc, oldIPTC, thisDS.id ) ) {
+ continue; // Don't import values that match the previous export.
}
- ++chPos; // Move past the time zone sign.
- for ( chPos = 0, digits = 0; digits < 2; ++digits, ++chPos ) {
- if ( (chPos >= dsInfo.dataLen) || (dsInfo.dataPtr[chPos] < '0') || (dsInfo.dataPtr[chPos] > '9') ) break;
- xmpDate.tzHour = (xmpDate.tzHour * 10) + (dsInfo.dataPtr[chPos] - '0');
- }
- if ( xmpDate.tzHour < 0 ) xmpDate.tzHour = 0;
- if ( xmpDate.tzHour > 23 ) xmpDate.tzHour = 23;
+ // The IPTC wins. Delete any existing XMP and import the DataSet.
- if ( dsInfo.dataPtr[chPos] == ':' ) ++chPos;
- for ( digits = 0; digits < 2; ++digits, ++chPos ) {
- if ( (chPos >= dsInfo.dataLen) || (dsInfo.dataPtr[chPos] < '0') || (dsInfo.dataPtr[chPos] > '9') ) break;
- xmpDate.tzMinute = (xmpDate.tzMinute * 10) + (dsInfo.dataPtr[chPos] - '0');
- }
- if ( xmpDate.tzMinute < 0 ) xmpDate.tzMinute = 0;
- if ( xmpDate.tzMinute > 59 ) xmpDate.tzMinute = 59;
-
- if ( chPos != dsInfo.dataLen ) return; // The DataSet is ill-formed.
-
- }
-
- // Finally, set the XMP property.
-
- xmp->SetProperty_Date ( xmpNS, xmpProp, xmpDate );
-
-} // ImportIPTC_DateCreated
-
-// =================================================================================================
-// ReconcileUtils::ImportIPTC
-// ==========================
-
-void ReconcileUtils::ImportIPTC ( const IPTC_Manager & iptc, SXMPMeta * xmp, int digestState )
-{
- if ( digestState == kDigestMatches ) return;
-
- for ( size_t i = 0; kKnownDataSets[i].id != 255; ++i ) {
-
- const DataSetCharacteristics & thisDS = kKnownDataSets[i];
+ xmp->DeleteProperty ( thisDS.xmpNS, thisDS.xmpProp );
try { // Don't let errors with one stop the others.
-
+
switch ( thisDS.mapForm ) {
-
+
case kIPTC_MapSimple :
- ImportIPTC_Simple ( iptc, xmp, digestState, thisDS.id, thisDS.xmpNS, thisDS.xmpProp );
+ ImportIPTC_Simple ( iptc, xmp, thisDS.id, thisDS.xmpNS, thisDS.xmpProp );
break;
-
+
case kIPTC_MapLangAlt :
- ImportIPTC_LangAlt ( iptc, xmp, digestState, thisDS.id, thisDS.xmpNS, thisDS.xmpProp );
+ ImportIPTC_LangAlt ( iptc, xmp, thisDS.id, thisDS.xmpNS, thisDS.xmpProp );
break;
-
+
case kIPTC_MapArray :
- ImportIPTC_Array ( iptc, xmp, digestState, thisDS.id, thisDS.xmpNS, thisDS.xmpProp );
+ ImportIPTC_Array ( iptc, xmp, thisDS.id, thisDS.xmpNS, thisDS.xmpProp );
break;
-
+
case kIPTC_MapSpecial :
- if ( thisDS.id == kIPTC_IntellectualGenre ) {
- ImportIPTC_IntellectualGenre ( iptc, xmp, digestState, thisDS.xmpNS, thisDS.xmpProp );
+ if ( thisDS.id == kIPTC_DateCreated ) {
+ PhotoDataUtils::ImportIPTC_Date ( thisDS.id, iptc, xmp );
+ } else if ( thisDS.id == kIPTC_IntellectualGenre ) {
+ ImportIPTC_IntellectualGenre ( iptc, xmp );
} else if ( thisDS.id == kIPTC_SubjectCode ) {
- ImportIPTC_SubjectCode ( iptc, xmp, digestState, thisDS.xmpNS, thisDS.xmpProp );
- } else if ( thisDS.id == kIPTC_DateCreated ) {
- ImportIPTC_DateCreated ( iptc, xmp, digestState, thisDS.xmpNS, thisDS.xmpProp );
- }
+ ImportIPTC_SubjectCode ( iptc, xmp );
+ } else {
+ XMP_Assert ( false ); // Catch mapping errors.
+ }
break;
-
+
}
} catch ( ... ) {
-
+
// Do nothing, let other imports proceed.
// ? Notify client?
-
+
}
}
-
-} // ReconcileUtils::ImportIPTC;
+
+} // PhotoDataUtils::Import2WayIPTC
// =================================================================================================
-// ReconcileUtils::ImportPSIR
+// PhotoDataUtils::ImportPSIR
// ==========================
//
// There are only 2 standalone Photoshop image resources for XMP properties:
@@ -446,21 +449,13 @@ void ReconcileUtils::ImportIPTC ( const IPTC_Manager & iptc, SXMPMeta * xmp, int
// ! yes/don't-know model when importing. A missing or 0 value for PSIR 1034 cause xmpRights:Marked
// ! to be deleted.
-// **** What about 1008 and 1020?
-
-void ReconcileUtils::ImportPSIR ( const PSIR_Manager & psir, SXMPMeta * xmp, int digestState )
+void PhotoDataUtils::ImportPSIR ( const PSIR_Manager & psir, SXMPMeta * xmp, int iptcDigestState )
{
PSIR_Manager::ImgRsrcInfo rsrcInfo;
bool import;
-
- if ( digestState == kDigestMatches ) return;
-
- if ( digestState == kDigestDiffers ) {
- // Delete the mapped XMP. This forces replacement and catches legacy deletions.
- xmp->DeleteProperty ( kXMP_NS_XMP_Rights, "Marked" );
- xmp->DeleteProperty ( kXMP_NS_XMP_Rights, "WebStatement" );
- }
-
+
+ if ( iptcDigestState == kDigestMatches ) return;
+
try { // Don't let errors with one stop the others.
import = psir.GetImgRsrc ( kPSIR_CopyrightFlag, &rsrcInfo );
if ( import ) import = (! xmp->DoesPropertyExist ( kXMP_NS_XMP_Rights, "Marked" ));
@@ -471,29 +466,27 @@ void ReconcileUtils::ImportPSIR ( const PSIR_Manager & psir, SXMPMeta * xmp, int
// Do nothing, let other imports proceed.
// ? Notify client?
}
-
+
try { // Don't let errors with one stop the others.
import = psir.GetImgRsrc ( kPSIR_CopyrightURL, &rsrcInfo );
if ( import ) import = (! xmp->DoesPropertyExist ( kXMP_NS_XMP_Rights, "WebStatement" ));
if ( import ) {
- #if ! XMP_UNIXBuild
- std::string utf8;
+ std::string utf8;
+ if ( ReconcileUtils::IsUTF8 ( rsrcInfo.dataPtr, rsrcInfo.dataLen ) ) {
+ utf8.assign ( (char*)rsrcInfo.dataPtr, rsrcInfo.dataLen );
+ } else if ( ! ignoreLocalText ) {
ReconcileUtils::LocalToUTF8 ( rsrcInfo.dataPtr, rsrcInfo.dataLen, &utf8 );
- xmp->SetProperty ( kXMP_NS_XMP_Rights, "WebStatement", utf8.c_str() );
- #else
- // ! Hack until legacy-as-local issues are resolved for generic UNIX.
- if ( ReconcileUtils::IsUTF8 ( rsrcInfo.dataPtr, rsrcInfo.dataLen ) ) {
- std::string utf8 ( (char*)rsrcInfo.dataPtr, rsrcInfo.dataLen );
- xmp->SetProperty ( kXMP_NS_XMP_Rights, "WebStatement", utf8.c_str() );
- }
- #endif
+ } else {
+ import = false; // Inhibit the SetProperty call.
+ }
+ if ( import ) xmp->SetProperty ( kXMP_NS_XMP_Rights, "WebStatement", utf8.c_str() );
}
} catch ( ... ) {
// Do nothing, let other imports proceed.
// ? Notify client?
}
-
-} // ReconcileUtils::ImportPSIR;
+
+} // PhotoDataUtils::ImportPSIR;
// =================================================================================================
// =================================================================================================
@@ -502,22 +495,22 @@ void ReconcileUtils::ImportPSIR ( const PSIR_Manager & psir, SXMPMeta * xmp, int
// ExportIPTC_Simple
// =================
-static void ExportIPTC_Simple ( SXMPMeta * xmp, IPTC_Manager * iptc,
+static void ExportIPTC_Simple ( const SXMPMeta & xmp, IPTC_Manager * iptc,
const char * xmpNS, const char * xmpProp, XMP_Uns8 id )
{
std::string value;
XMP_OptionBits xmpFlags;
- bool found = xmp->GetProperty ( xmpNS, xmpProp, &value, &xmpFlags );
+ bool found = xmp.GetProperty ( xmpNS, xmpProp, &value, &xmpFlags );
if ( ! found ) {
iptc->DeleteDataSet ( id );
return;
}
-
+
if ( ! XMP_PropIsSimple ( xmpFlags ) ) return; // ? Complain? Delete the DataSet?
-
+
NormalizeToCR ( &value );
-
+
size_t iptcCount = iptc->GetDataSet ( id, 0 );
if ( iptcCount > 1 ) iptc->DeleteDataSet ( id );
@@ -529,21 +522,21 @@ static void ExportIPTC_Simple ( SXMPMeta * xmp, IPTC_Manager * iptc,
// ExportIPTC_LangAlt
// ==================
-static void ExportIPTC_LangAlt ( SXMPMeta * xmp, IPTC_Manager * iptc,
+static void ExportIPTC_LangAlt ( const SXMPMeta & xmp, IPTC_Manager * iptc,
const char * xmpNS, const char * xmpProp, XMP_Uns8 id )
{
std::string value;
XMP_OptionBits xmpFlags;
- bool found = xmp->GetProperty ( xmpNS, xmpProp, 0, &xmpFlags );
+ bool found = xmp.GetProperty ( xmpNS, xmpProp, 0, &xmpFlags );
if ( ! found ) {
iptc->DeleteDataSet ( id );
return;
}
if ( ! XMP_ArrayIsAltText ( xmpFlags ) ) return; // ? Complain? Delete the DataSet?
-
- found = xmp->GetLocalizedText ( xmpNS, xmpProp, "", "x-default", 0, &value, 0 );
+
+ found = xmp.GetLocalizedText ( xmpNS, xmpProp, "", "x-default", 0, &value, 0 );
if ( ! found ) {
iptc->DeleteDataSet ( id );
return;
@@ -566,13 +559,13 @@ static void ExportIPTC_LangAlt ( SXMPMeta * xmp, IPTC_Manager * iptc,
// XMP and IPTC array sizes differ, delete the entire IPTC and append all new values. If they match,
// set the individual values in order - which lets SetDataSet apply its no-change optimization.
-static void ExportIPTC_Array ( SXMPMeta * xmp, IPTC_Manager * iptc,
+static void ExportIPTC_Array ( const SXMPMeta & xmp, IPTC_Manager * iptc,
const char * xmpNS, const char * xmpProp, XMP_Uns8 id )
{
std::string value;
XMP_OptionBits xmpFlags;
- bool found = xmp->GetProperty ( xmpNS, xmpProp, 0, &xmpFlags );
+ bool found = xmp.GetProperty ( xmpNS, xmpProp, 0, &xmpFlags );
if ( ! found ) {
iptc->DeleteDataSet ( id );
return;
@@ -580,14 +573,14 @@ static void ExportIPTC_Array ( SXMPMeta * xmp, IPTC_Manager * iptc,
if ( ! XMP_PropIsArray ( xmpFlags ) ) return; // ? Complain? Delete the DataSet?
- XMP_Index xmpCount = xmp->CountArrayItems ( xmpNS, xmpProp );
+ XMP_Index xmpCount = xmp.CountArrayItems ( xmpNS, xmpProp );
XMP_Index iptcCount = (XMP_Index) iptc->GetDataSet ( id, 0 );
-
+
if ( xmpCount != iptcCount ) iptc->DeleteDataSet ( id );
for ( XMP_Index ds = 0; ds < xmpCount; ++ds ) { // ! XMP arrays are indexed from 1, IPTC from 0.
- (void) xmp->GetArrayItem ( xmpNS, xmpProp, ds+1, &value, &xmpFlags );
+ (void) xmp.GetArrayItem ( xmpNS, xmpProp, ds+1, &value, &xmpFlags );
if ( ! XMP_PropIsSimple ( xmpFlags ) ) continue; // ? Complain?
NormalizeToCR ( &value );
@@ -607,20 +600,19 @@ static void ExportIPTC_Array ( SXMPMeta * xmp, IPTC_Manager * iptc,
// number is dropped. Also, even though IIMv4.1 says that 2:04 is repeatable, the XMP property to
// which it is mapped is simple. Look up the XMP value in a list of known genres to get the number.
-static void ExportIPTC_IntellectualGenre ( SXMPMeta * xmp, IPTC_Manager * iptc,
- const char * xmpNS, const char * xmpProp )
+static void ExportIPTC_IntellectualGenre ( const SXMPMeta & xmp, IPTC_Manager * iptc )
{
std::string xmpValue;
XMP_OptionBits xmpFlags;
- bool found = xmp->GetProperty ( xmpNS, xmpProp, &xmpValue, &xmpFlags );
+ bool found = xmp.GetProperty ( kXMP_NS_IPTCCore, "IntellectualGenre", &xmpValue, &xmpFlags );
if ( ! found ) {
iptc->DeleteDataSet ( kIPTC_IntellectualGenre );
return;
}
-
+
if ( ! XMP_PropIsSimple ( xmpFlags ) ) return; // ? Complain? Delete the DataSet?
-
+
NormalizeToCR ( &xmpValue );
int i;
@@ -629,11 +621,11 @@ static void ExportIPTC_IntellectualGenre ( SXMPMeta * xmp, IPTC_Manager * iptc,
if ( strcmp ( namePtr, kIntellectualGenreMappings[i].name ) == 0 ) break;
}
if ( kIntellectualGenreMappings[i].name == 0 ) return; // Not a known genre, don't export it.
-
+
std::string iimValue = kIntellectualGenreMappings[i].refNum;
iimValue += ':';
iimValue += xmpValue;
-
+
size_t iptcCount = iptc->GetDataSet ( kIPTC_IntellectualGenre, 0 );
if ( iptcCount > 1 ) iptc->DeleteDataSet ( kIPTC_IntellectualGenre );
@@ -650,13 +642,12 @@ static void ExportIPTC_IntellectualGenre ( SXMPMeta * xmp, IPTC_Manager * iptc,
// levels of the reference number hierarchy. The IPTC4XMP mapping rule is that only the reference
// number is imported to XMP. We export with a fixed provider of "IPTC" and no optional names.
-static void ExportIPTC_SubjectCode ( SXMPMeta * xmp, IPTC_Manager * iptc,
- const char * xmpNS, const char * xmpProp )
+static void ExportIPTC_SubjectCode ( const SXMPMeta & xmp, IPTC_Manager * iptc )
{
std::string xmpValue, iimValue;
XMP_OptionBits xmpFlags;
- bool found = xmp->GetProperty ( xmpNS, xmpProp, 0, &xmpFlags );
+ bool found = xmp.GetProperty ( kXMP_NS_IPTCCore, "SubjectCode", 0, &xmpFlags );
if ( ! found ) {
iptc->DeleteDataSet ( kIPTC_SubjectCode );
return;
@@ -664,14 +655,14 @@ static void ExportIPTC_SubjectCode ( SXMPMeta * xmp, IPTC_Manager * iptc,
if ( ! XMP_PropIsArray ( xmpFlags ) ) return; // ? Complain? Delete the DataSet?
- XMP_Index xmpCount = xmp->CountArrayItems ( xmpNS, xmpProp );
+ XMP_Index xmpCount = xmp.CountArrayItems ( kXMP_NS_IPTCCore, "SubjectCode" );
XMP_Index iptcCount = (XMP_Index) iptc->GetDataSet ( kIPTC_SubjectCode, 0 );
-
+
if ( xmpCount != iptcCount ) iptc->DeleteDataSet ( kIPTC_SubjectCode );
for ( XMP_Index ds = 0; ds < xmpCount; ++ds ) { // ! XMP arrays are indexed from 1, IPTC from 0.
- (void) xmp->GetArrayItem ( xmpNS, xmpProp, ds+1, &xmpValue, &xmpFlags );
+ (void) xmp.GetArrayItem ( kXMP_NS_IPTCCore, "SubjectCode", ds+1, &xmpValue, &xmpFlags );
if ( ! XMP_PropIsSimple ( xmpFlags ) ) continue; // ? Complain?
if ( xmpValue.size() != 8 ) continue; // ? Complain?
@@ -686,119 +677,136 @@ static void ExportIPTC_SubjectCode ( SXMPMeta * xmp, IPTC_Manager * iptc,
} // ExportIPTC_SubjectCode
// =================================================================================================
-// ExportIPTC_DateCreated
-// ======================
+// ExportIPTC_Date
+// ===============
//
// The IPTC date and time are "YYYYMMDD" and "HHMMSSxHHMM" where 'x' is '+' or '-'. Export the IPTC
// time only if already present, or if the XMP has a time portion.
-static void ExportIPTC_DateCreated ( SXMPMeta * xmp, IPTC_Manager * iptc,
- const char * xmpNS, const char * xmpProp )
-{
- std::string xmpStr;
- XMP_DateTime xmpValue;
- XMP_OptionBits xmpFlags;
+// *** The date/time handling differs from the MWG 1.0 policy, following a proposed tweak to MWG:
+// *** Exif DateTimeOriginal <-> IPTC DateCreated <-> XMP photoshop:DateCreated
+// *** Exif DateTimeDigitized <-> IPTC DigitalCreateDate <-> XMP xmp:CreateDate
- bool xmpHasTime = false;
-
- bool found = xmp->GetProperty ( xmpNS, xmpProp, &xmpStr, &xmpFlags );
- if ( found ) {
- SXMPUtils::ConvertToDate ( xmpStr.c_str(), &xmpValue );
- if ( xmpStr.size() > 10 ) xmpHasTime = true; // Date-only values are up to "YYYY-MM-DD".
+static void ExportIPTC_Date ( XMP_Uns8 dateID, const SXMPMeta & xmp, IPTC_Manager * iptc )
+{
+ XMP_Uns8 timeID;
+ XMP_StringPtr xmpNS, xmpProp;
+
+ if ( dateID == kIPTC_DateCreated ) {
+ timeID = kIPTC_TimeCreated;
+ xmpNS = kXMP_NS_Photoshop;
+ xmpProp = "DateCreated";
+ } else if ( dateID == kIPTC_DigitalCreateDate ) {
+ timeID = kIPTC_DigitalCreateTime;
+ xmpNS = kXMP_NS_XMP;
+ xmpProp = "CreateDate";
} else {
- iptc->DeleteDataSet ( kIPTC_DateCreated );
- iptc->DeleteDataSet ( kIPTC_TimeCreated );
- return;
+ XMP_Throw ( "Unrecognized dateID", kXMPErr_BadParam );
}
- char iimValue[16];
-
- // Set the IIM date portion.
+ iptc->DeleteDataSet ( dateID ); // ! Either the XMP does not exist and we want to
+ iptc->DeleteDataSet ( timeID ); // ! delete the IPTC, or we're replacing the IPTC.
+
+ XMP_DateTime xmpValue;
+ bool found = xmp.GetProperty_Date ( xmpNS, xmpProp, &xmpValue, 0 );
+ if ( ! found ) return;
+
+ char iimValue[16]; // AUDIT: Big enough for "YYYYMMDD" (8) and "HHMMSS+HHMM" (11).
+
+ // Set the IIM date portion as YYYYMMDD with zeroes for unknown parts.
- snprintf ( iimValue, sizeof(iimValue), "%.4d%.2d%.2d", // AUDIT: Use of sizeof(iimValue) is safe.
+ snprintf ( iimValue, sizeof(iimValue), "%04d%02d%02d", // AUDIT: Use of sizeof(iimValue) is safe.
xmpValue.year, xmpValue.month, xmpValue.day );
- if ( iimValue[8] != 0 ) return; // ? Complain? Delete the DataSet?
-
- size_t iptcCount = iptc->GetDataSet ( kIPTC_DateCreated, 0 );
- if ( iptcCount > 1 ) iptc->DeleteDataSet ( kIPTC_DateCreated );
- iptc->SetDataSet_UTF8 ( kIPTC_DateCreated, iimValue, 8, 0 ); // ! Don't append a 2nd DataSet!
-
- // Set the IIM time portion.
+ iptc->SetDataSet_UTF8 ( dateID, iimValue, 8 );
- iptcCount = iptc->GetDataSet ( kIPTC_TimeCreated, 0 );
-
- if ( (iptcCount > 0) || xmpHasTime ) {
-
- snprintf ( iimValue, sizeof(iimValue), "%.2d%.2d%.2d%c%.2d%.2d", // AUDIT: Use of sizeof(iimValue) is safe.
+ // Set the IIM time portion as HHMMSS+HHMM (or -HHMM). Allow a missing time zone.
+
+ if ( xmpValue.hasTimeZone ) {
+ snprintf ( iimValue, sizeof(iimValue), "%02d%02d%02d%c%02d%02d", // AUDIT: Use of sizeof(iimValue) is safe.
xmpValue.hour, xmpValue.minute, xmpValue.second,
((xmpValue.tzSign == kXMP_TimeWestOfUTC) ? '-' : '+'), xmpValue.tzHour, xmpValue.tzMinute );
- if ( iimValue[11] != 0 ) return; // ? Complain? Delete the DataSet?
-
- if ( iptcCount > 1 ) iptc->DeleteDataSet ( kIPTC_TimeCreated );
-
- iptc->SetDataSet_UTF8 ( kIPTC_TimeCreated, iimValue, 11, 0 ); // ! Don't append a 2nd DataSet!
-
+ iptc->SetDataSet_UTF8 ( timeID, iimValue, 11 );
+ } else if ( xmpValue.hasTime ) {
+ snprintf ( iimValue, sizeof(iimValue), "%02d%02d%02d", // AUDIT: Use of sizeof(iimValue) is safe.
+ xmpValue.hour, xmpValue.minute, xmpValue.second );
+ iptc->SetDataSet_UTF8 ( timeID, iimValue, 6 );
+ } else {
+ iptc->DeleteDataSet ( timeID );
}
-} // ExportIPTC_DateCreated
+} // ExportIPTC_Date
// =================================================================================================
-// ReconcileUtils::ExportIPTC
+// PhotoDataUtils::ExportIPTC
// ==========================
-void ReconcileUtils::ExportIPTC ( SXMPMeta * xmp, IPTC_Manager * iptc )
+void PhotoDataUtils::ExportIPTC ( const SXMPMeta & xmp, IPTC_Manager * iptc )
{
- #if XMP_UNIXBuild
- return; // ! Hack until the legacy-as-local issues are resolved for generic UNIX.
- #endif
-
for ( size_t i = 0; kKnownDataSets[i].id != 255; ++i ) {
-
+
try { // Don't let errors with one stop the others.
-
+
const DataSetCharacteristics & thisDS = kKnownDataSets[i];
-
+ if ( thisDS.mapForm >= kIPTC_UnmappedText ) continue;
+
switch ( thisDS.mapForm ) {
-
+
case kIPTC_MapSimple :
ExportIPTC_Simple ( xmp, iptc, thisDS.xmpNS, thisDS.xmpProp, thisDS.id );
break;
-
+
case kIPTC_MapLangAlt :
ExportIPTC_LangAlt ( xmp, iptc, thisDS.xmpNS, thisDS.xmpProp, thisDS.id );
break;
-
+
case kIPTC_MapArray :
ExportIPTC_Array ( xmp, iptc, thisDS.xmpNS, thisDS.xmpProp, thisDS.id );
break;
-
+
case kIPTC_MapSpecial :
- if ( thisDS.id == kIPTC_IntellectualGenre ) {
- ExportIPTC_IntellectualGenre ( xmp, iptc, thisDS.xmpNS, thisDS.xmpProp );
+ if ( thisDS.id == kIPTC_DateCreated ) {
+ ExportIPTC_Date ( thisDS.id, xmp, iptc );
+ } else if ( thisDS.id == kIPTC_IntellectualGenre ) {
+ ExportIPTC_IntellectualGenre ( xmp, iptc );
} else if ( thisDS.id == kIPTC_SubjectCode ) {
- ExportIPTC_SubjectCode ( xmp, iptc, thisDS.xmpNS, thisDS.xmpProp );
- } else if ( thisDS.id == kIPTC_DateCreated ) {
- ExportIPTC_DateCreated ( xmp, iptc, thisDS.xmpNS, thisDS.xmpProp );
- }
+ ExportIPTC_SubjectCode ( xmp, iptc );
+ } else {
+ XMP_Assert ( false ); // Catch mapping errors.
+ }
break;
-
+
+ case kIPTC_Map3Way : // The 3 way case is special for import, not for export.
+ if ( thisDS.id == kIPTC_DigitalCreateDate ) {
+ // ! Special case: Don't create IIM DigitalCreateDate. This can avoid PSD
+ // ! full rewrite due to new mapping from xmp:CreateDate.
+ if ( iptc->GetDataSet ( thisDS.id, 0 ) > 0 ) ExportIPTC_Date ( thisDS.id, xmp, iptc );
+ } else if ( thisDS.id == kIPTC_Creator ) {
+ ExportIPTC_Array ( xmp, iptc, kXMP_NS_DC, "creator", kIPTC_Creator );
+ } else if ( thisDS.id == kIPTC_CopyrightNotice ) {
+ ExportIPTC_LangAlt ( xmp, iptc, kXMP_NS_DC, "rights", kIPTC_CopyrightNotice );
+ } else if ( thisDS.id == kIPTC_Description ) {
+ ExportIPTC_LangAlt ( xmp, iptc, kXMP_NS_DC, "description", kIPTC_Description );
+ } else {
+ XMP_Assert ( false ); // Catch mapping errors.
+ }
+
}
} catch ( ... ) {
-
+
// Do nothing, let other exports proceed.
// ? Notify client?
-
+
}
}
-
-} // ReconcileUtils::ExportIPTC;
+
+} // PhotoDataUtils::ExportIPTC;
// =================================================================================================
-// ReconcileUtils::ExportPSIR
+// PhotoDataUtils::ExportPSIR
// ==========================
//
// There are only 2 standalone Photoshop image resources for XMP properties:
@@ -811,11 +819,11 @@ void ReconcileUtils::ExportIPTC ( SXMPMeta * xmp, IPTC_Manager * iptc )
// ! We don't bother with the CR<->LF normalization for xmpRights:WebStatement. Very little chance
// ! of having a raw CR character in a URI.
-void ReconcileUtils::ExportPSIR ( const SXMPMeta & xmp, PSIR_Manager * psir )
+void PhotoDataUtils::ExportPSIR ( const SXMPMeta & xmp, PSIR_Manager * psir )
{
bool found;
std::string utf8Value;
-
+
try { // Don't let errors with one stop the others.
bool copyrighted = false;
found = xmp.GetProperty ( kXMP_NS_XMP_Rights, "Marked", &utf8Value, 0 );
@@ -825,24 +833,23 @@ void ReconcileUtils::ExportPSIR ( const SXMPMeta & xmp, PSIR_Manager * psir )
// Do nothing, let other exports proceed.
// ? Notify client?
}
-
+
try { // Don't let errors with one stop the others.
found = xmp.GetProperty ( kXMP_NS_XMP_Rights, "WebStatement", &utf8Value, 0 );
if ( ! found ) {
psir->DeleteImgRsrc ( kPSIR_CopyrightURL );
+ } else if ( ! ignoreLocalText ) {
+ std::string localValue;
+ ReconcileUtils::UTF8ToLocal ( utf8Value.c_str(), utf8Value.size(), &localValue );
+ psir->SetImgRsrc ( kPSIR_CopyrightURL, localValue.c_str(), (XMP_Uns32)localValue.size() );
+ } else if ( ReconcileUtils::IsASCII ( utf8Value.c_str(), utf8Value.size() ) ) {
+ psir->SetImgRsrc ( kPSIR_CopyrightURL, utf8Value.c_str(), (XMP_Uns32)utf8Value.size() );
} else {
- #if ! XMP_UNIXBuild
- std::string localValue;
- ReconcileUtils::UTF8ToLocal ( utf8Value.c_str(), utf8Value.size(), &localValue );
- psir->SetImgRsrc ( kPSIR_CopyrightURL, localValue.c_str(), (XMP_Uns32)localValue.size() );
- #else
- // ! Hack until legacy-as-local issues are resolved for generic UNIX.
- psir->DeleteImgRsrc ( kPSIR_CopyrightURL );
- #endif
+ psir->DeleteImgRsrc ( kPSIR_CopyrightURL );
}
} catch ( ... ) {
// Do nothing, let other exports proceed.
// ? Notify client?
}
-} // ReconcileUtils::ExportPSIR;
+} // PhotoDataUtils::ExportPSIR;
diff --git a/source/XMPFiles/FormatSupport/ReconcileLegacy.cpp b/source/XMPFiles/FormatSupport/ReconcileLegacy.cpp
index 7c9f1f4..78eeaa4 100644
--- a/source/XMPFiles/FormatSupport/ReconcileLegacy.cpp
+++ b/source/XMPFiles/FormatSupport/ReconcileLegacy.cpp
@@ -1,6 +1,6 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2006-2007 Adobe Systems Incorporated
+// Copyright 2006 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -14,201 +14,172 @@
// =================================================================================================
/// \file ReconcileLegacy.cpp
-/// \brief Top level parts of utilities to reconcile between XMP and legacy metadata forms such as
+/// \brief Top level parts of utilities to reconcile between XMP and legacy metadata forms such as
/// TIFF/Exif and IPTC.
///
// =================================================================================================
// =================================================================================================
-// ImportJTPtoXMP
-// ==============
+// ImportPhotoData
+// ===============
//
// Import legacy metadata for JPEG, TIFF, and Photoshop files into the XMP. The caller must have
// already done the file specific processing to select the appropriate sources of the TIFF stream,
// the Photoshop image resources, and the IPTC.
-// ! Note that kLegacyJTP_None does not literally mean no legacy. It means no IPTC-like legacy, i.e.
-// ! stuff that Photoshop pre-7 would reconcile into the IPTC and thus affect the import order below.
-
-void ImportJTPtoXMP ( XMP_FileFormat srcFormat,
- RecJTP_LegacyPriority lastLegacy,
- TIFF_Manager * tiff, // ! Need for UserComment and RelatedSoundFile hack.
- const PSIR_Manager & psir,
- IPTC_Manager * iptc, // ! Need to call UpdateDataSets.
- SXMPMeta * xmp,
- XMP_OptionBits options /* = 0 */ )
+#define SaveExifTag(ns,prop) \
+ if ( xmp->DoesPropertyExist ( ns, prop ) ) SXMPUtils::DuplicateSubtree ( *xmp, &savedExif, ns, prop )
+#define RestoreExifTag(ns,prop) \
+ if ( savedExif.DoesPropertyExist ( ns, prop ) ) SXMPUtils::DuplicateSubtree ( savedExif, xmp, ns, prop )
+
+void ImportPhotoData ( const TIFF_Manager & exif,
+ const IPTC_Manager & iptc,
+ const PSIR_Manager & psir,
+ int iptcDigestState,
+ SXMPMeta * xmp,
+ XMP_OptionBits options /* = 0 */ )
{
bool haveXMP = XMP_OptionIsSet ( options, k2XMP_FileHadXMP );
- bool haveIPTC = XMP_OptionIsSet ( options, k2XMP_FileHadIPTC );
bool haveExif = XMP_OptionIsSet ( options, k2XMP_FileHadExif );
+ bool haveIPTC = XMP_OptionIsSet ( options, k2XMP_FileHadIPTC );
- int iptcDigestState = kDigestMatches; // Default is to do no imports.
- int tiffDigestState = kDigestMatches;
- int exifDigestState = kDigestMatches;
-
- if ( ! haveXMP ) {
+ // Save some new Exif writebacks that can be XMP-only from older versions, delete all of the
+ // XMP's tiff: and exif: namespaces (they should only reflect native Exif), then put back the
+ // saved writebacks (which might get replaced by the native Exif values in the Import calls).
+ // The value of exif:ISOSpeedRatings is saved for special case handling of ISO over 65535.
- // If there is no XMP then what we have differs.
- if ( haveIPTC) iptcDigestState = kDigestDiffers;
- if ( haveExif ) tiffDigestState = exifDigestState = kDigestDiffers;
+ SXMPMeta savedExif;
- } else {
-
- // If there is XMP then check the digests for what we have. No legacy at all means the XMP
- // is OK, and the CheckXyzDigest routines return true when there is no digest. This matches
- // Photoshop, and avoids importing when an app adds XMP but does not export to the legacy or
- // write a digest.
-
- if ( haveIPTC ) iptcDigestState = ReconcileUtils::CheckIPTCDigest ( iptc, psir );
- if ( iptcDigestState == kDigestMissing ) {
- // *** Temporary hack to approximate Photoshop's behavior. Need fully documented policies!
- tiffDigestState = exifDigestState = kDigestMissing;
- } else if ( haveExif ) {
- tiffDigestState = ReconcileUtils::CheckTIFFDigest ( *tiff, *xmp );
- exifDigestState = ReconcileUtils::CheckExifDigest ( *tiff, *xmp ); // ! Yes, the Exif is in the TIFF stream.
- }
-
- }
+ SaveExifTag ( kXMP_NS_EXIF, "DateTimeOriginal" );
+ SaveExifTag ( kXMP_NS_EXIF, "GPSLatitude" );
+ SaveExifTag ( kXMP_NS_EXIF, "GPSLongitude" );
+ SaveExifTag ( kXMP_NS_EXIF, "GPSTimeStamp" );
+ SaveExifTag ( kXMP_NS_EXIF, "GPSAltitude" );
+ SaveExifTag ( kXMP_NS_EXIF, "GPSAltitudeRef" );
+ SaveExifTag ( kXMP_NS_EXIF, "ISOSpeedRatings" );
- if ( lastLegacy > kLegacyJTP_TIFF_IPTC ) {
- XMP_Throw ( "Invalid JTP legacy priority", kXMPErr_InternalFailure );
- }
+ SXMPUtils::RemoveProperties ( xmp, kXMP_NS_TIFF, 0, kXMPUtil_DoAllProperties );
+ SXMPUtils::RemoveProperties ( xmp, kXMP_NS_EXIF, 0, kXMPUtil_DoAllProperties );
+
+ RestoreExifTag ( kXMP_NS_EXIF, "DateTimeOriginal" );
+ RestoreExifTag ( kXMP_NS_EXIF, "GPSLatitude" );
+ RestoreExifTag ( kXMP_NS_EXIF, "GPSLongitude" );
+ RestoreExifTag ( kXMP_NS_EXIF, "GPSTimeStamp" );
+ RestoreExifTag ( kXMP_NS_EXIF, "GPSAltitude" );
+ RestoreExifTag ( kXMP_NS_EXIF, "GPSAltitudeRef" );
+ RestoreExifTag ( kXMP_NS_EXIF, "ISOSpeedRatings" );
- // A TIFF file with tags 270, 315, or 33432 is currently the only case where the IPTC is less
- // important than the TIFF/Exif. If there is no IPTC or no TIFF/Exif then the order does not
- // matter. The order only affects collisions between those 3 TIFF tags and their IPTC counterparts.
+ // Not obvious here, but the logic in PhotoDataUtils follows the MWG reader guidelines.
- if ( lastLegacy == kLegacyJTP_TIFF_TIFF_Tags ) {
+ PhotoDataUtils::ImportPSIR ( psir, xmp, iptcDigestState );
- if ( iptcDigestState != kDigestMatches ) {
- ReconcileUtils::ImportIPTC ( *iptc, xmp, iptcDigestState );
- ReconcileUtils::ImportPSIR ( psir, xmp, iptcDigestState );
- }
- if ( tiffDigestState != kDigestMatches ) ReconcileUtils::ImportTIFF ( *tiff, xmp, tiffDigestState, srcFormat );
- if ( exifDigestState != kDigestMatches ) ReconcileUtils::ImportExif ( *tiff, xmp, exifDigestState );
+ if ( haveIPTC ) PhotoDataUtils::Import2WayIPTC ( iptc, xmp, iptcDigestState );
+ if ( haveExif ) PhotoDataUtils::Import2WayExif ( exif, xmp, iptcDigestState );
- } else {
+ if ( haveExif | haveIPTC ) PhotoDataUtils::Import3WayItems ( exif, iptc, xmp, iptcDigestState );
- if ( tiffDigestState != kDigestMatches ) ReconcileUtils::ImportTIFF ( *tiff, xmp, tiffDigestState, srcFormat );
- if ( exifDigestState != kDigestMatches ) ReconcileUtils::ImportExif ( *tiff, xmp, exifDigestState );
- if ( iptcDigestState != kDigestMatches ) {
- ReconcileUtils::ImportIPTC ( *iptc, xmp, iptcDigestState );
- ReconcileUtils::ImportPSIR ( psir, xmp, iptcDigestState );
- }
-
- }
-
- // ! Older versions of Photoshop did not import the UserComment or RelatedSoundFile tags. Note
- // ! whether the initial XMP has these tags. Don't delete them from the TIFF when saving unless
- // ! they were in the XMP to begin with. Can't do this in ReconcileUtils::ImportExif, that is
- // ! only called when the Exif is newer than the XMP.
+ // If photoshop:DateCreated does not exist try to create it from exif:DateTimeOriginal.
- tiff->xmpHadUserComment = xmp->DoesPropertyExist ( kXMP_NS_EXIF, "UserComment" );
- tiff->xmpHadRelatedSoundFile = xmp->DoesPropertyExist ( kXMP_NS_EXIF, "RelatedSoundFile" );
+ if ( ! xmp->DoesPropertyExist ( kXMP_NS_Photoshop, "DateCreated" ) ) {
+ std::string exifValue;
+ bool haveExifDTO = xmp->GetProperty ( kXMP_NS_EXIF, "DateTimeOriginal", &exifValue, 0 );
+ if ( haveExifDTO ) xmp->SetProperty ( kXMP_NS_Photoshop, "DateCreated", exifValue.c_str() );
+ }
-} // ImportJTPtoXMP
+} // ImportPhotoData
// =================================================================================================
-// ExportXMPtoJTP
-// ==============
-
-void ExportXMPtoJTP ( XMP_FileFormat destFormat,
- SXMPMeta * xmp,
- TIFF_Manager * tiff,
- PSIR_Manager * psir,
- IPTC_Manager * iptc,
- XMP_OptionBits options /* = 0 */ )
+// ExportPhotoData
+// ===============
+
+void ExportPhotoData ( XMP_FileFormat destFormat,
+ SXMPMeta * xmp,
+ TIFF_Manager * exif, // Pass 0 if not wanted.
+ IPTC_Manager * iptc, // Pass 0 if not wanted.
+ PSIR_Manager * psir, // Pass 0 if not wanted.
+ XMP_OptionBits options /* = 0 */ )
{
- XMP_Assert ( xmp != 0 );
XMP_Assert ( (destFormat == kXMP_JPEGFile) || (destFormat == kXMP_TIFFFile) || (destFormat == kXMP_PhotoshopFile) );
-
- #if XMP_UNIXBuild
- // ! Hack until the legacy-as-local issues are resolved for generic UNIX.
- iptc = 0; // Strip IIM from the file.
- if ( tiff != 0 ) tiff->DeleteTag ( kTIFF_PrimaryIFD, kTIFF_IPTC );
- if ( psir != 0 ) {
- psir->DeleteImgRsrc ( kPSIR_IPTC );
- psir->DeleteImgRsrc ( kPSIR_IPTCDigest );
- }
- #endif
- // Save the IPTC changed flag specially. SetIPTCDigest will call UpdateMemoryDataSets, which
- // will clear the IsChanged flag. Also, UpdateMemoryDataSets can be called twice, once for the
- // general IPTC-in-PSIR case and once for the IPTC-as-TIFF-tag case.
-
- bool iptcChanged = false;
-
- // Do not write legacy IPTC (IIM) or PSIR in DNG files (which are a variant of TIFF).
-
- if ( (destFormat == kXMP_TIFFFile) && (tiff != 0) &&
- tiff->GetTag ( kTIFF_PrimaryIFD, kTIFF_DNGVersion, 0 ) ) {
-
+ // Do not write IPTC-IIM or PSIR in DNG files (which are a variant of TIFF).
+
+ if ( (destFormat == kXMP_TIFFFile) && (exif != 0) &&
+ exif->GetTag ( kTIFF_PrimaryIFD, kTIFF_DNGVersion, 0 ) ) {
+
iptc = 0; // These prevent calls to ExportIPTC and ExportPSIR.
psir = 0;
-
- tiff->DeleteTag ( kTIFF_PrimaryIFD, kTIFF_IPTC ); // These remove any existing IPTC and PSIR.
- tiff->DeleteTag ( kTIFF_PrimaryIFD, kTIFF_PSIR );
-
- }
-
- // Export the individual metadata items to the legacy forms. The PSIR and IPTC must be done
- // before the TIFF and Exif. The PSIR and IPTC have side effects that can modify the XMP, and
- // thus the values written to TIFF and Exif. The side effects are the CR<->LF normalization that
- // is done to match Photoshop.
-
- if ( psir != 0) ReconcileUtils::ExportPSIR ( *xmp, psir );
- if ( iptc != 0 ) {
- ReconcileUtils::ExportIPTC ( xmp, iptc );
- iptcChanged = iptc->IsChanged(); // ! Do after calling ExportIPTC and before calling SetIPTCDigest.
- if ( psir != 0 ) ReconcileUtils::SetIPTCDigest ( iptc, psir ); // ! Do always, in case the digest was missing before.
- }
+ exif->DeleteTag ( kTIFF_PrimaryIFD, kTIFF_IPTC ); // These remove any existing IPTC and PSIR.
+ exif->DeleteTag ( kTIFF_PrimaryIFD, kTIFF_PSIR );
- if ( tiff != 0 ) {
- ReconcileUtils::ExportTIFF ( *xmp, tiff );
- ReconcileUtils::ExportExif ( *xmp, tiff );
- ReconcileUtils::SetTIFFDigest ( *tiff, xmp ); // ! Do always, in case the digest was missing before.
- ReconcileUtils::SetExifDigest ( *tiff, xmp ); // ! Do always, in case the digest was missing before.
}
+
+ // Export the individual metadata items to the non-XMP forms. Set the IPTC digest whether or not
+ // it changed, it might not have been present or correct before.
+
+ bool iptcChanged = false; // Save explicitly, internal flag is reset by UpdateMemoryDataSets.
+
+ void * iptcPtr = 0;
+ XMP_Uns32 iptcLen = 0;
- // Now update the collections of metadata, e.g. the IPTC in PSIR 1028 or XMP in TIFF tag 700.
- // - All of the formats have the IPTC in the PSIR portion.
- // - JPEG has nothing else special.
- // - PSD has the XMP and Exif in the PSIR portion.
- // - TIFF has the XMP, IPTC, and PSIR in primary IFD tags. Yes, a 2nd copy of the IPTC.
-
- if ( (iptc != 0) && (psir != 0) && iptcChanged ) {
- void* iptcPtr;
- XMP_Uns32 iptcLen = iptc->UpdateMemoryDataSets ( &iptcPtr );
- psir->SetImgRsrc ( kPSIR_IPTC, iptcPtr, iptcLen );
+ if ( iptc != 0 ) {
+ PhotoDataUtils::ExportIPTC ( *xmp, iptc );
+ iptcChanged = iptc->IsChanged();
+ if ( iptcChanged ) iptc->UpdateMemoryDataSets();
+ iptcLen = iptc->GetBlockInfo ( &iptcPtr );
+ if ( psir != 0 ) PhotoDataUtils::SetIPTCDigest ( iptcPtr, iptcLen, psir );
}
-
- if ( destFormat == kXMP_PhotoshopFile ) {
- XMP_Assert ( psir != 0 );
+ if ( exif != 0 ) PhotoDataUtils::ExportExif ( xmp, exif );
+ if ( psir != 0 ) PhotoDataUtils::ExportPSIR ( *xmp, psir );
- if ( (tiff != 0) && tiff->IsChanged() ) {
- void* exifPtr;
- XMP_Uns32 exifLen = tiff->UpdateMemoryStream ( &exifPtr );
- psir->SetImgRsrc ( kPSIR_Exif, exifPtr, exifLen );
- }
+ // Now update the non-XMP collections of metadata according to the file format. Do not update
+ // the XMP here, that is done in the file handlers after deciding if an XMP-only in-place
+ // update should be done.
+ // - JPEG has the IPTC in PSIR 1028, the Exif and PSIR are marker segments.
+ // - TIFF has the IPTC and PSIR in primary IFD tags.
+ // - PSD has everything in PSIRs.
+
+ if ( destFormat == kXMP_JPEGFile ) {
+
+ if ( iptcChanged && (psir != 0) ) psir->SetImgRsrc ( kPSIR_IPTC, iptcPtr, iptcLen );
} else if ( destFormat == kXMP_TIFFFile ) {
-
- XMP_Assert ( tiff != 0 );
- if ( (iptc != 0) && iptcChanged ) {
- void* iptcPtr;
- XMP_Uns32 iptcLen = iptc->UpdateMemoryDataSets ( &iptcPtr );
- tiff->SetTag ( kTIFF_PrimaryIFD, kTIFF_IPTC, kTIFF_UndefinedType, iptcLen, iptcPtr );
- }
+ XMP_Assert ( exif != 0 );
+
+ if ( iptcChanged ) exif->SetTag ( kTIFF_PrimaryIFD, kTIFF_IPTC, kTIFF_UndefinedType, iptcLen, iptcPtr );
if ( (psir != 0) && psir->IsChanged() ) {
void* psirPtr;
XMP_Uns32 psirLen = psir->UpdateMemoryResources ( &psirPtr );
- tiff->SetTag ( kTIFF_PrimaryIFD, kTIFF_PSIR, kTIFF_UndefinedType, psirLen, psirPtr );
+ exif->SetTag ( kTIFF_PrimaryIFD, kTIFF_PSIR, kTIFF_UndefinedType, psirLen, psirPtr );
+ }
+
+ } else if ( destFormat == kXMP_PhotoshopFile ) {
+
+ XMP_Assert ( psir != 0 );
+
+ if ( iptcChanged ) psir->SetImgRsrc ( kPSIR_IPTC, iptcPtr, iptcLen );
+
+ if ( (exif != 0) && exif->IsChanged() ) {
+ void* exifPtr;
+ XMP_Uns32 exifLen = exif->UpdateMemoryStream ( &exifPtr );
+ psir->SetImgRsrc ( kPSIR_Exif, exifPtr, exifLen );
}
-
+
}
+
+ // Strip the tiff: and exif: namespaces from the XMP, we're done with them. Save the Exif
+ // ISOSpeedRatings if any of the values are over 0xFFFF, the native tag is SHORT. Lower level
+ // code already kept or stripped the XMP form.
+
+ SXMPMeta savedExif;
+ SaveExifTag ( kXMP_NS_EXIF, "ISOSpeedRatings" );
+
+ SXMPUtils::RemoveProperties ( xmp, kXMP_NS_TIFF, 0, kXMPUtil_DoAllProperties );
+ SXMPUtils::RemoveProperties ( xmp, kXMP_NS_EXIF, 0, kXMPUtil_DoAllProperties );
+
+ RestoreExifTag ( kXMP_NS_EXIF, "ISOSpeedRatings" );
-} // ExportXMPtoJTP
+} // ExportPhotoData
diff --git a/source/XMPFiles/FormatSupport/ReconcileLegacy.hpp b/source/XMPFiles/FormatSupport/ReconcileLegacy.hpp
index 2542309..59918cf 100644
--- a/source/XMPFiles/FormatSupport/ReconcileLegacy.hpp
+++ b/source/XMPFiles/FormatSupport/ReconcileLegacy.hpp
@@ -3,7 +3,7 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2006-2007 Adobe Systems Incorporated
+// Copyright 2006 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -18,31 +18,16 @@
// =================================================================================================
/// \file ReconcileLegacy.hpp
-/// \brief Utilities to reconcile between XMP and legacy metadata forms such as TIFF/Exif and IPTC.
+/// \brief Utilities to reconcile between XMP and photo metadata forms such as TIFF/Exif and IPTC.
///
// =================================================================================================
-// ImportJTPtoXMP imports legacy metadata for JPEG, TIFF, and Photoshop files into XMP. The caller
-// must have already done the file specific processing to select the appropriate sources of the TIFF
-// stream, the Photoshop image resources, and the IPTC.
+// ImportPhotoData imports TIFF/Exif and IPTC metadata from JPEG, TIFF, and Photoshop files into
+// XMP. The caller must have already done the file specific processing to select the appropriate
+// sources of the TIFF stream, the Photoshop image resources, and the IPTC.
//
-// The reconciliation logic used here is not identical to that used in Photoshop CS2, but should be
-// similar enough. The details of both approaches are documented in LegacyReconcile.pdf. The logic
-// used by Photoshop is more processor and memory intensive. That overhead is acceptable when
-// opening a file in Photoshop. Client's like Bridge need a lighter weight approach for quick
-// read-only access to the reconciled metadata.
-
-enum { // JTP "last-seen" legacy priorities from Photoshop. Higher numbers are more important.
- kLegacyJTP_None = 0, // No legacy metadata.
- kLegacyJTP_JPEG_TIFF_Tags = 1, // A JPEG file with TIFF tags 270, 315, or 33432.
- kLegacyJTP_PSIR_IPTC = 2, // IPTC from Photoshop image resource 1028.
- kLegacyJTP_PSIR_OldCaption = 3, // Old caption from Photoshop image resource 1008 or 1020.
- kLegacyJTP_TIFF_TIFF_Tags = 4, // A TIFF file with TIFF tags 270, 315, or 33432.
- kLegacyJTP_TIFF_IPTC = 5, // A TIFF file with TIFF tag 33723.
- kLegacyJTP_Mac_pnot = 6, // KeyW and Desc items from Macintosh pnot 0 resource.
- kLegacyJTP_ANPA_IPTC = 7 // IPTC from Macintosh ANPA 10000 resource.
-};
-typedef XMP_Uns8 RecJTP_LegacyPriority;
+// The reconciliation logic used here is based on the Metadata Working Group guidelines. This is a
+// simpler approach than used previously - which was modeled after historical Photoshop behavior.
enum { // Bits for the options to ImportJTPtoXMP.
k2XMP_FileHadXMP = 0x0001, // Set if the file had an XMP packet.
@@ -50,77 +35,66 @@ enum { // Bits for the options to ImportJTPtoXMP.
k2XMP_FileHadExif = 0x0004 // Set if the file had legacy Exif.
};
-extern void ImportJTPtoXMP ( XMP_FileFormat srcFormat,
- RecJTP_LegacyPriority lastLegacy,
- TIFF_Manager * tiff, // ! Need to modify for UserComment and RelatedSoundFile hack.
- const PSIR_Manager & psir,
- IPTC_Manager * iptc, // ! Need to modify for UpdateDataSets.
- SXMPMeta * xmp,
- XMP_OptionBits options = 0 );
+extern void ImportPhotoData ( const TIFF_Manager & exif,
+ const IPTC_Manager & iptc,
+ const PSIR_Manager & psir,
+ int iptcDigestState,
+ SXMPMeta * xmp,
+ XMP_OptionBits options = 0 );
-#if 0 // Activate if we want to support the Mac pnot resource.
-extern void ImportJTPtoXMP ( XMP_FileFormat srcFormat,
- RecJTP_LegacyPriority lastLegacy,
- const TIFF_Manager & tiff,
- const PSIR_Manager & psir,
- IPTC_Manager * iptc,
- const void * macKeyW, // The STR# for pnot 0 KeyW item.
- const std::string & macDesc, // The TEXT for pnot 0 Desc item.
- SXMPMeta * xmp,
- XMP_OptionBits options = 0 );
-#endif
+// ExportPhotoData exports XMP into TIFF/Exif and IPTC metadata for JPEG, TIFF, and Photoshop files.
-// ExportXMPtoJTP exports XMP into legacy metadata for JPEG, TIFF, and Photoshop files.
+extern void ExportPhotoData ( XMP_FileFormat destFormat,
+ SXMPMeta * xmp,
+ TIFF_Manager * exif, // Pass 0 if not wanted.
+ IPTC_Manager * iptc, // Pass 0 if not wanted.
+ PSIR_Manager * psir, // Pass 0 if not wanted.
+ XMP_OptionBits options = 0 );
-extern void ExportXMPtoJTP ( XMP_FileFormat destFormat,
- SXMPMeta * xmp,
- TIFF_Manager * tiff, // Pass 0 if not wanted.
- PSIR_Manager * psir, // Pass 0 if not wanted.
- IPTC_Manager * iptc, // Pass 0 if not wanted.
- XMP_OptionBits options = 0 );
+// *** Mapping notes need revision for MWG related changes.
// =================================================================================================
// Summary of TIFF/Exif mappings to XMP
// ====================================
-//
+//
// The mapping for each tag is driven mainly by the tag ID, and secondarily by the type. E.g. there
// is no blanket rule that all ASCII tags are mapped to simple strings in XMP. Some, such as
// SubSecTime or GPSLatitudeRef, are combined with other tags; others, like Flash, are reformated.
// However, most tags are in fact mapped in an obvious manner based on their type and count.
-//
+//
// Photoshop practice has been to truncate ASCII tags at the first NUL, not supporting the TIFF
// specification's notion of multi-part ASCII values.
-//
+//
// Rational values are mapped to XMP as "num/denom".
-//
+//
// The tags of UNDEFINED type that are mapped to XMP text are either special cases like ExifVersion
// or the strings with an explicit encoding like UserComment.
-//
+//
// Latitude and logitude are mapped to XMP as "DDD,MM,SSk" or "DDD,MM.mmk"; k is N, S, E, or W.
-//
+//
// Flash struct in XMP separates the Fired, Return, Mode, Function, and RedEyeMode portions of the
// Exif value. Fired, Function, and RedEyeMode are Boolean; Return and Mode are integers.
-//
+//
// The OECF/SFR, CFA, and DeviceSettings tables are described in the XMP spec.
-//
+//
// Instead of iterating through all tags in the various IFDs, it is probably more efficient to have
// explicit processing for the tags that get special treatment, and a static table listing those
// that get mapped by type and count. The type and count processing will verify that the actual
// type and count are as expected, if not the tag is ignored.
-//
+//
// Here are the primary (0th) IFD tags that get special treatment:
-//
+//
// 270, 33432 - ASCII mapped to alt-text['x-default']
// 306 - DateTime master
// 315 - ASCII mapped to text seq[1]
-//
+//
// Here are the primary (0th) IFD tags that get mapped by type and count:
-//
+//
// 256, 257, 258, 259, 262, 271, 272, 274, 277, 282, 283, 284, 296, 301, 305, 318, 319,
// 529, 530, 531, 532
-//
+//
// Here are the Exif IFD tags that get special treatment:
-//
+//
// 34856, 41484 - OECF/SFR table
// 36864, 40960 - 4 ASCII chars to text
// 36867, 36868 - DateTime master
@@ -130,22 +104,22 @@ extern void ExportXMPtoJTP ( XMP_FileFormat destFormat,
// 41728, 41729 - UInt8 to integer
// 41730 - CFA table
// 41995 - DeviceSettings table
-//
+//
// Here are the Exif IFD tags that get mapped by type and count:
-//
+//
// 33434, 33437, 34850, 34852, 34855, 37122, 37377, 37378, 37379, 37380, 37381, 37382, 37383, 37384,
// 37386, 37396, 40961, 40962, 40963, 40964, 41483, 41486, 41487, 41488, 41492, 41493, 41495, 41985,
// 41986, 41987, 41988, 41989, 41990, 41991, 41992, 41993, 41994, 41996, 42016
-//
+//
// Here are the GPS IFD tags that get special treatment:
-//
+//
// 0 - 4 UInt8 to text "n.n.n.n"
// 2, 4, 20, 22 - Latitude or longitude master
// 7 - special DateTime master, the time part
// 27, 28 - explicitly encoded text
-//
+//
// Here are the GPS IFD tags that get mapped by type and count:
-//
+//
// 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 23, 24, 25, 26, 30
// =================================================================================================
@@ -170,7 +144,7 @@ extern void ExportXMPtoJTP ( XMP_FileFormat destFormat,
//
// General (primary and thumbnail, 0th and 1st) IFD tags
// tag TIFF type count Name XMP mapping
-//
+//
// 256 SHORTorLONG 1 ImageWidth integer
// 257 SHORTorLONG 1 ImageLength integer
// 258 SHORT 3 BitsPerSample integer seq
@@ -196,10 +170,10 @@ extern void ExportXMPtoJTP ( XMP_FileFormat destFormat,
// 531 SHORT 1 YCbCrPositioning integer
// 532 RATIONAL 6 ReferenceBlackWhite rational seq
// 33432 ASCII Any Copyright text, dc:rights['x-default']
-//
+//
// Exif IFD tags
// tag TIFF type count Name XMP mapping
-//
+//
// 33434 RATIONAL 1 ExposureTime rational
// 33437 RATIONAL 1 FNumber rational
// 34850 SHORT 1 ExposureProgram integer
@@ -255,10 +229,10 @@ extern void ExportXMPtoJTP ( XMP_FileFormat destFormat,
// 41995 UNDEFINED Any DeviceSettingDescription DeviceSettings table
// 41996 SHORT 1 SubjectDistanceRange integer
// 42016 ASCII 33 ImageUniqueID text
-//
+//
// GPS IFD tags
// tag TIFF type count Name XMP mapping
-//
+//
// 0 BYTE 4 GPSVersionID text, "n.n.n.n", Exif has 4 UInt8
// 1 ASCII 2 GPSLatitudeRef latitude, with 2
// 2 RATIONAL 3 GPSLatitude latitude, master of 2
diff --git a/source/XMPFiles/FormatSupport/ReconcileTIFF.cpp b/source/XMPFiles/FormatSupport/ReconcileTIFF.cpp
index 4ae7564..892c683 100644
--- a/source/XMPFiles/FormatSupport/ReconcileTIFF.cpp
+++ b/source/XMPFiles/FormatSupport/ReconcileTIFF.cpp
@@ -1,6 +1,6 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2006-2008 Adobe Systems Incorporated
+// Copyright 2006 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -19,6 +19,8 @@
#endif
#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'
#pragma warning ( disable : 4996 ) // '...' was declared deprecated
#endif
@@ -44,132 +46,148 @@
// ! The sentinel tag value can't be 0, that is a valid GPS Info tag, 0xFFFF is unused so far.
+enum {
+ kExport_Never = 0, // Never export.
+ kExport_Always = 1, // Add, modify, or delete.
+ kExport_NoDelete = 2, // Add or modify, do not delete if no XMP.
+ kExport_InjectOnly = 3 // Add tag if new, never modify or delete existing values.
+};
+
struct TIFF_MappingToXMP {
XMP_Uns16 id;
XMP_Uns16 type;
XMP_Uns32 count; // Zero means any.
+ XMP_Uns8 exportMode;
const char * name; // The name of the mapped XMP property. The namespace is implicit.
};
enum { kAnyCount = 0 };
-static const TIFF_MappingToXMP sPrimaryIFDMappings[] = {
- { /* 256 */ kTIFF_ImageWidth, kTIFF_ShortOrLongType, 1, "ImageWidth" },
- { /* 257 */ kTIFF_ImageLength, kTIFF_ShortOrLongType, 1, "ImageLength" },
- { /* 258 */ kTIFF_BitsPerSample, kTIFF_ShortType, 3, "BitsPerSample" },
- { /* 259 */ kTIFF_Compression, kTIFF_ShortType, 1, "Compression" },
- { /* 262 */ kTIFF_PhotometricInterpretation, kTIFF_ShortType, 1, "PhotometricInterpretation" },
- { /* 274 */ kTIFF_Orientation, kTIFF_ShortType, 1, "Orientation" },
- { /* 277 */ kTIFF_SamplesPerPixel, kTIFF_ShortType, 1, "SamplesPerPixel" },
- { /* 284 */ kTIFF_PlanarConfiguration, kTIFF_ShortType, 1, "PlanarConfiguration" },
- { /* 530 */ kTIFF_YCbCrSubSampling, kTIFF_ShortType, 2, "YCbCrSubSampling" },
- { /* 531 */ kTIFF_YCbCrPositioning, kTIFF_ShortType, 1, "YCbCrPositioning" },
- { /* 282 */ kTIFF_XResolution, kTIFF_RationalType, 1, "XResolution" },
- { /* 283 */ kTIFF_YResolution, kTIFF_RationalType, 1, "YResolution" },
- { /* 296 */ kTIFF_ResolutionUnit, kTIFF_ShortType, 1, "ResolutionUnit" },
- { /* 301 */ kTIFF_TransferFunction, kTIFF_ShortType, 3*256, "TransferFunction" },
- { /* 318 */ kTIFF_WhitePoint, kTIFF_RationalType, 2, "WhitePoint" },
- { /* 319 */ kTIFF_PrimaryChromaticities, kTIFF_RationalType, 6, "PrimaryChromaticities" },
- { /* 529 */ kTIFF_YCbCrCoefficients, kTIFF_RationalType, 3, "YCbCrCoefficients" },
- { /* 532 */ kTIFF_ReferenceBlackWhite, kTIFF_RationalType, 6, "ReferenceBlackWhite" },
- { /* 306 */ kTIFF_DateTime, kTIFF_ASCIIType, 20, "" }, // ! Has a special mapping.
- { /* 270 */ kTIFF_ImageDescription, kTIFF_ASCIIType, kAnyCount, "" }, // ! Has a special mapping.
- { /* 271 */ kTIFF_Make, kTIFF_ASCIIType, kAnyCount, "Make" },
- { /* 272 */ kTIFF_Model, kTIFF_ASCIIType, kAnyCount, "Model" },
- { /* 305 */ kTIFF_Software, kTIFF_ASCIIType, kAnyCount, "Software" }, // Has alias to xmp:CreatorTool.
- { /* 315 */ kTIFF_Artist, kTIFF_ASCIIType, kAnyCount, "" }, // ! Has a special mapping.
- { /* 33432 */ kTIFF_Copyright, kTIFF_ASCIIType, kAnyCount, "" },
+static const TIFF_MappingToXMP sPrimaryIFDMappings[] = { // A blank name indicates a special mapping.
+ { /* 256 */ kTIFF_ImageWidth, kTIFF_ShortOrLongType, 1, kExport_Never, "ImageWidth" },
+ { /* 257 */ kTIFF_ImageLength, kTIFF_ShortOrLongType, 1, kExport_Never, "ImageLength" },
+ { /* 258 */ kTIFF_BitsPerSample, kTIFF_ShortType, 3, kExport_Never, "BitsPerSample" },
+ { /* 259 */ kTIFF_Compression, kTIFF_ShortType, 1, kExport_Never, "Compression" },
+ { /* 262 */ kTIFF_PhotometricInterpretation, kTIFF_ShortType, 1, kExport_Never, "PhotometricInterpretation" },
+ { /* 274 */ kTIFF_Orientation, kTIFF_ShortType, 1, kExport_NoDelete, "Orientation" },
+ { /* 277 */ kTIFF_SamplesPerPixel, kTIFF_ShortType, 1, kExport_Never, "SamplesPerPixel" },
+ { /* 284 */ kTIFF_PlanarConfiguration, kTIFF_ShortType, 1, kExport_Never, "PlanarConfiguration" },
+ { /* 530 */ kTIFF_YCbCrSubSampling, kTIFF_ShortType, 2, kExport_Never, "YCbCrSubSampling" },
+ { /* 531 */ kTIFF_YCbCrPositioning, kTIFF_ShortType, 1, kExport_Never, "YCbCrPositioning" },
+ { /* 282 */ kTIFF_XResolution, kTIFF_RationalType, 1, kExport_NoDelete, "XResolution" },
+ { /* 283 */ kTIFF_YResolution, kTIFF_RationalType, 1, kExport_NoDelete, "YResolution" },
+ { /* 296 */ kTIFF_ResolutionUnit, kTIFF_ShortType, 1, kExport_NoDelete, "ResolutionUnit" },
+ { /* 301 */ kTIFF_TransferFunction, kTIFF_ShortType, 3*256, kExport_Never, "TransferFunction" },
+ { /* 318 */ kTIFF_WhitePoint, kTIFF_RationalType, 2, kExport_Never, "WhitePoint" },
+ { /* 319 */ kTIFF_PrimaryChromaticities, kTIFF_RationalType, 6, kExport_Never, "PrimaryChromaticities" },
+ { /* 529 */ kTIFF_YCbCrCoefficients, kTIFF_RationalType, 3, kExport_Never, "YCbCrCoefficients" },
+ { /* 532 */ kTIFF_ReferenceBlackWhite, kTIFF_RationalType, 6, kExport_Never, "ReferenceBlackWhite" },
+ { /* 306 */ kTIFF_DateTime, kTIFF_ASCIIType, 20, kExport_Always, "" }, // ! Has a special mapping.
+ { /* 270 */ kTIFF_ImageDescription, kTIFF_ASCIIType, kAnyCount, kExport_Always, "" }, // ! Has a special mapping.
+ { /* 271 */ kTIFF_Make, kTIFF_ASCIIType, kAnyCount, kExport_InjectOnly, "Make" },
+ { /* 272 */ kTIFF_Model, kTIFF_ASCIIType, kAnyCount, kExport_InjectOnly, "Model" },
+ { /* 305 */ kTIFF_Software, kTIFF_ASCIIType, kAnyCount, kExport_Always, "Software" }, // Has alias to xmp:CreatorTool.
+ { /* 315 */ kTIFF_Artist, kTIFF_ASCIIType, kAnyCount, kExport_Always, "" }, // ! Has a special mapping.
+ { /* 33432 */ kTIFF_Copyright, kTIFF_ASCIIType, kAnyCount, kExport_Always, "" }, // ! Has a special mapping.
{ 0xFFFF, 0, 0, 0 } // ! Must end with sentinel.
};
+// ! A special need, easier than looking up the entry in sExifIFDMappings:
+static const TIFF_MappingToXMP kISOSpeedMapping = { kTIFF_ISOSpeedRatings, kTIFF_ShortType, kAnyCount, kExport_InjectOnly, "ISOSpeedRatings" };
+
static const TIFF_MappingToXMP sExifIFDMappings[] = {
- { /* 36864 */ kTIFF_ExifVersion, kTIFF_UndefinedType, 4, "" }, // ! Has a special mapping.
- { /* 40960 */ kTIFF_FlashpixVersion, kTIFF_UndefinedType, 4, "" }, // ! Has a special mapping.
- { /* 40961 */ kTIFF_ColorSpace, kTIFF_ShortType, 1, "ColorSpace" },
- { /* 37121 */ kTIFF_ComponentsConfiguration, kTIFF_UndefinedType, 4, "" }, // ! Has a special mapping.
- { /* 37122 */ kTIFF_CompressedBitsPerPixel, kTIFF_RationalType, 1, "CompressedBitsPerPixel" },
- { /* 40962 */ kTIFF_PixelXDimension, kTIFF_ShortOrLongType, 1, "PixelXDimension" },
- { /* 40963 */ kTIFF_PixelYDimension, kTIFF_ShortOrLongType, 1, "PixelYDimension" },
- { /* 37510 */ kTIFF_UserComment, kTIFF_UndefinedType, kAnyCount, "" }, // ! Has a special mapping.
- { /* 40964 */ kTIFF_RelatedSoundFile, kTIFF_ASCIIType, 13, "RelatedSoundFile" },
- { /* 36867 */ kTIFF_DateTimeOriginal, kTIFF_ASCIIType, 20, "" }, // ! Has a special mapping.
- { /* 36868 */ kTIFF_DateTimeDigitized, kTIFF_ASCIIType, 20, "" }, // ! Has a special mapping.
- { /* 33434 */ kTIFF_ExposureTime, kTIFF_RationalType, 1, "ExposureTime" },
- { /* 33437 */ kTIFF_FNumber, kTIFF_RationalType, 1, "FNumber" },
- { /* 34850 */ kTIFF_ExposureProgram, kTIFF_ShortType, 1, "ExposureProgram" },
- { /* 34852 */ kTIFF_SpectralSensitivity, kTIFF_ASCIIType, kAnyCount, "SpectralSensitivity" },
- { /* 34855 */ kTIFF_ISOSpeedRatings, kTIFF_ShortType, kAnyCount, "ISOSpeedRatings" },
- { /* 34856 */ kTIFF_OECF, kTIFF_UndefinedType, kAnyCount, "" }, // ! Has a special mapping.
- { /* 37377 */ kTIFF_ShutterSpeedValue, kTIFF_SRationalType, 1, "ShutterSpeedValue" },
- { /* 37378 */ kTIFF_ApertureValue, kTIFF_RationalType, 1, "ApertureValue" },
- { /* 37379 */ kTIFF_BrightnessValue, kTIFF_SRationalType, 1, "BrightnessValue" },
- { /* 37380 */ kTIFF_ExposureBiasValue, kTIFF_SRationalType, 1, "ExposureBiasValue" },
- { /* 37381 */ kTIFF_MaxApertureValue, kTIFF_RationalType, 1, "MaxApertureValue" },
- { /* 37382 */ kTIFF_SubjectDistance, kTIFF_RationalType, 1, "SubjectDistance" },
- { /* 37383 */ kTIFF_MeteringMode, kTIFF_ShortType, 1, "MeteringMode" },
- { /* 37384 */ kTIFF_LightSource, kTIFF_ShortType, 1, "LightSource" },
- { /* 37385 */ kTIFF_Flash, kTIFF_ShortType, 1, "" }, // ! Has a special mapping.
- { /* 37386 */ kTIFF_FocalLength, kTIFF_RationalType, 1, "FocalLength" },
- { /* 37396 */ kTIFF_SubjectArea, kTIFF_ShortType, kAnyCount, "SubjectArea" }, // ! Actually 2..4.
- { /* 41483 */ kTIFF_FlashEnergy, kTIFF_RationalType, 1, "FlashEnergy" },
- { /* 41484 */ kTIFF_SpatialFrequencyResponse, kTIFF_UndefinedType, kAnyCount, "" }, // ! Has a special mapping.
- { /* 41486 */ kTIFF_FocalPlaneXResolution, kTIFF_RationalType, 1, "FocalPlaneXResolution" },
- { /* 41487 */ kTIFF_FocalPlaneYResolution, kTIFF_RationalType, 1, "FocalPlaneYResolution" },
- { /* 41488 */ kTIFF_FocalPlaneResolutionUnit, kTIFF_ShortType, 1, "FocalPlaneResolutionUnit" },
- { /* 41492 */ kTIFF_SubjectLocation, kTIFF_ShortType, 2, "SubjectLocation" },
- { /* 41493 */ kTIFF_ExposureIndex, kTIFF_RationalType, 1, "ExposureIndex" },
- { /* 41495 */ kTIFF_SensingMethod, kTIFF_ShortType, 1, "SensingMethod" },
- { /* 41728 */ kTIFF_FileSource, kTIFF_UndefinedType, 1, "" }, // ! Has a special mapping.
- { /* 41729 */ kTIFF_SceneType, kTIFF_UndefinedType, 1, "" }, // ! Has a special mapping.
- { /* 41730 */ kTIFF_CFAPattern, kTIFF_UndefinedType, kAnyCount, "" }, // ! Has a special mapping.
- { /* 41985 */ kTIFF_CustomRendered, kTIFF_ShortType, 1, "CustomRendered" },
- { /* 41986 */ kTIFF_ExposureMode, kTIFF_ShortType, 1, "ExposureMode" },
- { /* 41987 */ kTIFF_WhiteBalance, kTIFF_ShortType, 1, "WhiteBalance" },
- { /* 41988 */ kTIFF_DigitalZoomRatio, kTIFF_RationalType, 1, "DigitalZoomRatio" },
- { /* 41989 */ kTIFF_FocalLengthIn35mmFilm, kTIFF_ShortType, 1, "FocalLengthIn35mmFilm" },
- { /* 41990 */ kTIFF_SceneCaptureType, kTIFF_ShortType, 1, "SceneCaptureType" },
- { /* 41991 */ kTIFF_GainControl, kTIFF_ShortType, 1, "GainControl" },
- { /* 41992 */ kTIFF_Contrast, kTIFF_ShortType, 1, "Contrast" },
- { /* 41993 */ kTIFF_Saturation, kTIFF_ShortType, 1, "Saturation" },
- { /* 41994 */ kTIFF_Sharpness, kTIFF_ShortType, 1, "Sharpness" },
- { /* 41995 */ kTIFF_DeviceSettingDescription, kTIFF_UndefinedType, kAnyCount, "" }, // ! Has a special mapping.
- { /* 41996 */ kTIFF_SubjectDistanceRange, kTIFF_ShortType, 1, "SubjectDistanceRange" },
- { /* 42016 */ kTIFF_ImageUniqueID, kTIFF_ASCIIType, 33, "ImageUniqueID" },
+ { /* 36864 */ kTIFF_ExifVersion, kTIFF_UndefinedType, 4, kExport_InjectOnly, "" }, // ! Has a special mapping.
+ { /* 40960 */ kTIFF_FlashpixVersion, kTIFF_UndefinedType, 4, kExport_Never, "" }, // ! Has a special mapping.
+ { /* 40961 */ kTIFF_ColorSpace, kTIFF_ShortType, 1, kExport_InjectOnly, "ColorSpace" },
+ { /* 37121 */ kTIFF_ComponentsConfiguration, kTIFF_UndefinedType, 4, kExport_InjectOnly, "" }, // ! Has a special mapping.
+ { /* 37122 */ kTIFF_CompressedBitsPerPixel, kTIFF_RationalType, 1, kExport_InjectOnly, "CompressedBitsPerPixel" },
+ { /* 40962 */ kTIFF_PixelXDimension, kTIFF_ShortOrLongType, 1, kExport_InjectOnly, "PixelXDimension" },
+ { /* 40963 */ kTIFF_PixelYDimension, kTIFF_ShortOrLongType, 1, kExport_InjectOnly, "PixelYDimension" },
+ { /* 37510 */ kTIFF_UserComment, kTIFF_UndefinedType, kAnyCount, kExport_Always, "" }, // ! Has a special mapping.
+ { /* 40964 */ kTIFF_RelatedSoundFile, kTIFF_ASCIIType, kAnyCount, kExport_Always, "RelatedSoundFile" }, // ! Exif spec says count of 13.
+ { /* 36867 */ kTIFF_DateTimeOriginal, kTIFF_ASCIIType, 20, kExport_Always, "" }, // ! Has a special mapping.
+ { /* 36868 */ kTIFF_DateTimeDigitized, kTIFF_ASCIIType, 20, kExport_Always, "" }, // ! Has a special mapping.
+ { /* 33434 */ kTIFF_ExposureTime, kTIFF_RationalType, 1, kExport_InjectOnly, "ExposureTime" },
+ { /* 33437 */ kTIFF_FNumber, kTIFF_RationalType, 1, kExport_InjectOnly, "FNumber" },
+ { /* 34850 */ kTIFF_ExposureProgram, kTIFF_ShortType, 1, kExport_InjectOnly, "ExposureProgram" },
+ { /* 34852 */ kTIFF_SpectralSensitivity, kTIFF_ASCIIType, kAnyCount, kExport_InjectOnly, "SpectralSensitivity" },
+ { /* 34855 */ kTIFF_ISOSpeedRatings, kTIFF_ShortType, kAnyCount, kExport_InjectOnly, "" }, // ! Has a special mapping.
+ { /* 34856 */ kTIFF_OECF, kTIFF_UndefinedType, kAnyCount, kExport_Never, "" }, // ! Has a special mapping.
+ { /* 37377 */ kTIFF_ShutterSpeedValue, kTIFF_SRationalType, 1, kExport_InjectOnly, "ShutterSpeedValue" },
+ { /* 37378 */ kTIFF_ApertureValue, kTIFF_RationalType, 1, kExport_InjectOnly, "ApertureValue" },
+ { /* 37379 */ kTIFF_BrightnessValue, kTIFF_SRationalType, 1, kExport_InjectOnly, "BrightnessValue" },
+ { /* 37380 */ kTIFF_ExposureBiasValue, kTIFF_SRationalType, 1, kExport_InjectOnly, "ExposureBiasValue" },
+ { /* 37381 */ kTIFF_MaxApertureValue, kTIFF_RationalType, 1, kExport_InjectOnly, "MaxApertureValue" },
+ { /* 37382 */ kTIFF_SubjectDistance, kTIFF_RationalType, 1, kExport_InjectOnly, "SubjectDistance" },
+ { /* 37383 */ kTIFF_MeteringMode, kTIFF_ShortType, 1, kExport_InjectOnly, "MeteringMode" },
+ { /* 37384 */ kTIFF_LightSource, kTIFF_ShortType, 1, kExport_InjectOnly, "LightSource" },
+ { /* 37385 */ kTIFF_Flash, kTIFF_ShortType, 1, kExport_InjectOnly, "" }, // ! Has a special mapping.
+ { /* 37386 */ kTIFF_FocalLength, kTIFF_RationalType, 1, kExport_InjectOnly, "FocalLength" },
+ { /* 37396 */ kTIFF_SubjectArea, kTIFF_ShortType, kAnyCount, kExport_Never, "SubjectArea" }, // ! Actually 2..4.
+ { /* 41483 */ kTIFF_FlashEnergy, kTIFF_RationalType, 1, kExport_InjectOnly, "FlashEnergy" },
+ { /* 41484 */ kTIFF_SpatialFrequencyResponse, kTIFF_UndefinedType, kAnyCount, kExport_InjectOnly, "" }, // ! Has a special mapping.
+ { /* 41486 */ kTIFF_FocalPlaneXResolution, kTIFF_RationalType, 1, kExport_InjectOnly, "FocalPlaneXResolution" },
+ { /* 41487 */ kTIFF_FocalPlaneYResolution, kTIFF_RationalType, 1, kExport_InjectOnly, "FocalPlaneYResolution" },
+ { /* 41488 */ kTIFF_FocalPlaneResolutionUnit, kTIFF_ShortType, 1, kExport_InjectOnly, "FocalPlaneResolutionUnit" },
+ { /* 41492 */ kTIFF_SubjectLocation, kTIFF_ShortType, 2, kExport_Never, "SubjectLocation" },
+ { /* 41493 */ kTIFF_ExposureIndex, kTIFF_RationalType, 1, kExport_InjectOnly, "ExposureIndex" },
+ { /* 41495 */ kTIFF_SensingMethod, kTIFF_ShortType, 1, kExport_InjectOnly, "SensingMethod" },
+ { /* 41728 */ kTIFF_FileSource, kTIFF_UndefinedType, 1, kExport_InjectOnly, "" }, // ! Has a special mapping.
+ { /* 41729 */ kTIFF_SceneType, kTIFF_UndefinedType, 1, kExport_InjectOnly, "" }, // ! Has a special mapping.
+ { /* 41730 */ kTIFF_CFAPattern, kTIFF_UndefinedType, kAnyCount, kExport_InjectOnly, "" }, // ! Has a special mapping.
+ { /* 41985 */ kTIFF_CustomRendered, kTIFF_ShortType, 1, kExport_Never, "CustomRendered" },
+ { /* 41986 */ kTIFF_ExposureMode, kTIFF_ShortType, 1, kExport_InjectOnly, "ExposureMode" },
+ { /* 41987 */ kTIFF_WhiteBalance, kTIFF_ShortType, 1, kExport_InjectOnly, "WhiteBalance" },
+ { /* 41988 */ kTIFF_DigitalZoomRatio, kTIFF_RationalType, 1, kExport_InjectOnly, "DigitalZoomRatio" },
+ { /* 41989 */ kTIFF_FocalLengthIn35mmFilm, kTIFF_ShortType, 1, kExport_InjectOnly, "FocalLengthIn35mmFilm" },
+ { /* 41990 */ kTIFF_SceneCaptureType, kTIFF_ShortType, 1, kExport_InjectOnly, "SceneCaptureType" },
+ { /* 41991 */ kTIFF_GainControl, kTIFF_ShortType, 1, kExport_InjectOnly, "GainControl" },
+ { /* 41992 */ kTIFF_Contrast, kTIFF_ShortType, 1, kExport_InjectOnly, "Contrast" },
+ { /* 41993 */ kTIFF_Saturation, kTIFF_ShortType, 1, kExport_InjectOnly, "Saturation" },
+ { /* 41994 */ kTIFF_Sharpness, kTIFF_ShortType, 1, kExport_InjectOnly, "Sharpness" },
+ { /* 41995 */ kTIFF_DeviceSettingDescription, kTIFF_UndefinedType, kAnyCount, kExport_InjectOnly, "" }, // ! Has a special mapping.
+ { /* 41996 */ kTIFF_SubjectDistanceRange, kTIFF_ShortType, 1, kExport_InjectOnly, "SubjectDistanceRange" },
+ { /* 42016 */ kTIFF_ImageUniqueID, kTIFF_ASCIIType, 33, kExport_InjectOnly, "ImageUniqueID" },
{ 0xFFFF, 0, 0, 0 } // ! Must end with sentinel.
};
static const TIFF_MappingToXMP sGPSInfoIFDMappings[] = {
- { /* 0 */ kTIFF_GPSVersionID, kTIFF_ByteType, 4, "" }, // ! Has a special mapping.
- { /* 2 */ kTIFF_GPSLatitude, kTIFF_RationalType, 3, "" }, // ! Has a special mapping.
- { /* 4 */ kTIFF_GPSLongitude, kTIFF_RationalType, 3, "" }, // ! Has a special mapping.
- { /* 5 */ kTIFF_GPSAltitudeRef, kTIFF_ByteType, 1, "GPSAltitudeRef" },
- { /* 6 */ kTIFF_GPSAltitude, kTIFF_RationalType, 1, "GPSAltitude" },
- { /* 7 */ kTIFF_GPSTimeStamp, kTIFF_RationalType, 3, "" }, // ! Has a special mapping.
- { /* 8 */ kTIFF_GPSSatellites, kTIFF_ASCIIType, kAnyCount, "GPSSatellites" },
- { /* 9 */ kTIFF_GPSStatus, kTIFF_ASCIIType, 2, "GPSStatus" },
- { /* 10 */ kTIFF_GPSMeasureMode, kTIFF_ASCIIType, 2, "GPSMeasureMode" },
- { /* 11 */ kTIFF_GPSDOP, kTIFF_RationalType, 1, "GPSDOP" },
- { /* 12 */ kTIFF_GPSSpeedRef, kTIFF_ASCIIType, 2, "GPSSpeedRef" },
- { /* 13 */ kTIFF_GPSSpeed, kTIFF_RationalType, 1, "GPSSpeed" },
- { /* 14 */ kTIFF_GPSTrackRef, kTIFF_ASCIIType, 2, "GPSTrackRef" },
- { /* 15 */ kTIFF_GPSTrack, kTIFF_RationalType, 1, "GPSTrack" },
- { /* 16 */ kTIFF_GPSImgDirectionRef, kTIFF_ASCIIType, 2, "GPSImgDirectionRef" },
- { /* 17 */ kTIFF_GPSImgDirection, kTIFF_RationalType, 1, "GPSImgDirection" },
- { /* 18 */ kTIFF_GPSMapDatum, kTIFF_ASCIIType, kAnyCount, "GPSMapDatum" },
- { /* 20 */ kTIFF_GPSDestLatitude, kTIFF_RationalType, 3, "" }, // ! Has a special mapping.
- { /* 22 */ kTIFF_GPSDestLongitude, kTIFF_RationalType, 3, "" }, // ! Has a special mapping.
- { /* 23 */ kTIFF_GPSDestBearingRef, kTIFF_ASCIIType, 2, "GPSDestBearingRef" },
- { /* 24 */ kTIFF_GPSDestBearing, kTIFF_RationalType, 1, "GPSDestBearing" },
- { /* 25 */ kTIFF_GPSDestDistanceRef, kTIFF_ASCIIType, 2, "GPSDestDistanceRef" },
- { /* 26 */ kTIFF_GPSDestDistance, kTIFF_RationalType, 1, "GPSDestDistance" },
- { /* 27 */ kTIFF_GPSProcessingMethod, kTIFF_UndefinedType, kAnyCount, "" }, // ! Has a special mapping.
- { /* 28 */ kTIFF_GPSAreaInformation, kTIFF_UndefinedType, kAnyCount, "" }, // ! Has a special mapping.
- { /* 30 */ kTIFF_GPSDifferential, kTIFF_ShortType, 1, "GPSDifferential" },
+ { /* 0 */ kTIFF_GPSVersionID, kTIFF_ByteType, 4, kExport_InjectOnly, "" }, // ! Has a special mapping.
+ { /* 2 */ kTIFF_GPSLatitude, kTIFF_RationalType, 3, kExport_Always, "" }, // ! Has a special mapping.
+ { /* 4 */ kTIFF_GPSLongitude, kTIFF_RationalType, 3, kExport_Always, "" }, // ! Has a special mapping.
+ { /* 5 */ kTIFF_GPSAltitudeRef, kTIFF_ByteType, 1, kExport_Always, "GPSAltitudeRef" },
+ { /* 6 */ kTIFF_GPSAltitude, kTIFF_RationalType, 1, kExport_Always, "GPSAltitude" },
+ { /* 7 */ kTIFF_GPSTimeStamp, kTIFF_RationalType, 3, kExport_Always, "" }, // ! Has a special mapping.
+ { /* 8 */ kTIFF_GPSSatellites, kTIFF_ASCIIType, kAnyCount, kExport_InjectOnly, "GPSSatellites" },
+ { /* 9 */ kTIFF_GPSStatus, kTIFF_ASCIIType, 2, kExport_InjectOnly, "GPSStatus" },
+ { /* 10 */ kTIFF_GPSMeasureMode, kTIFF_ASCIIType, 2, kExport_InjectOnly, "GPSMeasureMode" },
+ { /* 11 */ kTIFF_GPSDOP, kTIFF_RationalType, 1, kExport_InjectOnly, "GPSDOP" },
+ { /* 12 */ kTIFF_GPSSpeedRef, kTIFF_ASCIIType, 2, kExport_InjectOnly, "GPSSpeedRef" },
+ { /* 13 */ kTIFF_GPSSpeed, kTIFF_RationalType, 1, kExport_InjectOnly, "GPSSpeed" },
+ { /* 14 */ kTIFF_GPSTrackRef, kTIFF_ASCIIType, 2, kExport_InjectOnly, "GPSTrackRef" },
+ { /* 15 */ kTIFF_GPSTrack, kTIFF_RationalType, 1, kExport_InjectOnly, "GPSTrack" },
+ { /* 16 */ kTIFF_GPSImgDirectionRef, kTIFF_ASCIIType, 2, kExport_InjectOnly, "GPSImgDirectionRef" },
+ { /* 17 */ kTIFF_GPSImgDirection, kTIFF_RationalType, 1, kExport_InjectOnly, "GPSImgDirection" },
+ { /* 18 */ kTIFF_GPSMapDatum, kTIFF_ASCIIType, kAnyCount, kExport_InjectOnly, "GPSMapDatum" },
+ { /* 20 */ kTIFF_GPSDestLatitude, kTIFF_RationalType, 3, kExport_InjectOnly, "" }, // ! Has a special mapping.
+ { /* 22 */ kTIFF_GPSDestLongitude, kTIFF_RationalType, 3, kExport_InjectOnly, "" }, // ! Has a special mapping.
+ { /* 23 */ kTIFF_GPSDestBearingRef, kTIFF_ASCIIType, 2, kExport_InjectOnly, "GPSDestBearingRef" },
+ { /* 24 */ kTIFF_GPSDestBearing, kTIFF_RationalType, 1, kExport_InjectOnly, "GPSDestBearing" },
+ { /* 25 */ kTIFF_GPSDestDistanceRef, kTIFF_ASCIIType, 2, kExport_InjectOnly, "GPSDestDistanceRef" },
+ { /* 26 */ kTIFF_GPSDestDistance, kTIFF_RationalType, 1, kExport_InjectOnly, "GPSDestDistance" },
+ { /* 27 */ kTIFF_GPSProcessingMethod, kTIFF_UndefinedType, kAnyCount, kExport_InjectOnly, "" }, // ! Has a special mapping.
+ { /* 28 */ kTIFF_GPSAreaInformation, kTIFF_UndefinedType, kAnyCount, kExport_InjectOnly, "" }, // ! Has a special mapping.
+ { /* 30 */ kTIFF_GPSDifferential, kTIFF_ShortType, 1, kExport_InjectOnly, "GPSDifferential" },
{ 0xFFFF, 0, 0, 0 } // ! Must end with sentinel.
};
// =================================================================================================
+static void // ! Needed by Import2WayExif
+ExportTIFF_Date ( const SXMPMeta & xmp, const char * xmpNS, const char * xmpProp, TIFF_Manager * tiff, XMP_Uns16 mainID );
+
+// =================================================================================================
+
static XMP_Uns32 GatherInt ( const char * strPtr, size_t count )
{
XMP_Uns32 value = 0;
@@ -184,178 +202,142 @@ static XMP_Uns32 GatherInt ( const char * strPtr, size_t count )
return value;
-}
+} // GatherInt
// =================================================================================================
-// =================================================================================================
-
-// =================================================================================================
-// ComputeTIFFDigest
-// =================
-//
-// Compute a 128 bit (16 byte) MD5 digest of the mapped TIFF tags and format it as a string like:
-// 256,257,...;A0FCE844924381619820B6F7117C8B83
-// The first portion is a decimal list of the tags from sPrimaryIFDMappings, the last part is the
-// MD5 digest as 32 hex digits using capital A-F.
-
-// ! The order of listing for the tags is crucial for the change comparisons to work!
-static void
-ComputeTIFFDigest ( const TIFF_Manager & tiff, std::string * digestStr )
+static void TrimTrailingSpaces ( TIFF_Manager::TagInfo * info )
{
- MD5_CTX context;
- MD5_Digest digest;
- char buffer[40];
- size_t in, out;
+ if ( info->dataLen == 0 ) return;
+ XMP_Assert ( info->dataPtr != 0 );
- TIFF_Manager::TagInfo tagInfo;
-
- MD5Init ( &context );
- digestStr->clear();
- digestStr->reserve ( 160 ); // The current length is 134.
-
- for ( size_t i = 0; sPrimaryIFDMappings[i].id != 0xFFFF; ++i ) {
- snprintf ( buffer, sizeof(buffer), "%d,", sPrimaryIFDMappings[i].id ); // AUDIT: Use of sizeof(buffer) is safe.
- digestStr->append ( buffer );
- bool found = tiff.GetTag ( kTIFF_PrimaryIFD, sPrimaryIFDMappings[i].id, &tagInfo );
- if ( found ) MD5Update ( &context, (XMP_Uns8*)tagInfo.dataPtr, tagInfo.dataLen );
- }
-
- size_t endPos = digestStr->size() - 1;
- (*digestStr)[endPos] = ';';
-
- MD5Final ( digest, &context );
-
- for ( in = 0, out = 0; in < 16; in += 1, out += 2 ) {
- XMP_Uns8 byte = digest[in];
- buffer[out] = ReconcileUtils::kHexDigits [ byte >> 4 ];
- buffer[out+1] = ReconcileUtils::kHexDigits [ byte & 0xF ];
- }
- buffer[32] = 0;
+ char * firstChar = (char*)info->dataPtr;
+ char * lastChar = firstChar + info->dataLen - 1;
+
+ if ( (*lastChar != ' ') && (*lastChar != 0) ) return; // Nothing to do.
+
+ while ( (firstChar <= lastChar) && ((*lastChar == ' ') || (*lastChar == 0)) ) --lastChar;
+
+ XMP_Assert ( (lastChar == firstChar-1) ||
+ ((lastChar >= firstChar) && (*lastChar != ' ') && (*lastChar != 0)) );
+
+ ++lastChar;
+ XMP_Uns32 newLen = (XMP_Uns32)(lastChar - firstChar) + 1;
+ XMP_Assert ( newLen <= info->dataLen );
- digestStr->append ( buffer );
+ *lastChar = 0;
+ info->dataLen = newLen;
-} // ComputeTIFFDigest;
+} // TrimTrailingSpaces
// =================================================================================================
-// ComputeExifDigest
-// =================
-//
-// Compute a 128 bit (16 byte) MD5 digest of the mapped Exif andf GPS tags and format it as a string like:
-// 36864,40960,...;A0FCE844924381619820B6F7117C8B83
-// The first portion is a decimal list of the tags, the last part is the MD5 digest as 32 hex
-// digits using capital A-F. The listed tags are those from sExifIFDMappings followed by those from
-// sGPSInfoIFDMappings.
-
-// ! The order of listing for the tags is crucial for the change comparisons to work!
-static void
-ComputeExifDigest ( const TIFF_Manager & exif, std::string * digestStr )
+bool PhotoDataUtils::GetNativeInfo ( const TIFF_Manager & exif, XMP_Uns8 ifd, XMP_Uns16 id, TIFF_Manager::TagInfo * info )
{
- MD5_CTX context;
- MD5_Digest digest;
- char buffer[40];
- size_t in, out;
+ bool haveExif = exif.GetTag ( ifd, id, info );
- TIFF_Manager::TagInfo tagInfo;
-
- MD5Init ( &context );
- digestStr->clear();
- digestStr->reserve ( 440 ); // The current length is 414.
+ if ( haveExif ) {
- for ( size_t i = 0; sExifIFDMappings[i].id != 0xFFFF; ++i ) {
- snprintf ( buffer, sizeof(buffer), "%d,", sExifIFDMappings[i].id ); // AUDIT: Use of sizeof(buffer) is safe.
- digestStr->append ( buffer );
- bool found = exif.GetTag ( kTIFF_ExifIFD, sExifIFDMappings[i].id, &tagInfo );
- if ( found ) MD5Update ( &context, (XMP_Uns8*)tagInfo.dataPtr, tagInfo.dataLen );
- }
+ XMP_Uns32 i;
+ char * chPtr;
+
+ XMP_Assert ( (info->dataPtr != 0) || (info->dataLen == 0) ); // Null pointer requires zero length.
- for ( size_t i = 0; sGPSInfoIFDMappings[i].id != 0xFFFF; ++i ) {
- snprintf ( buffer, sizeof(buffer), "%d,", sGPSInfoIFDMappings[i].id ); // AUDIT: Use of sizeof(buffer) is safe.
- digestStr->append ( buffer );
- bool found = exif.GetTag ( kTIFF_GPSInfoIFD, sGPSInfoIFDMappings[i].id, &tagInfo );
- if ( found ) MD5Update ( &context, (XMP_Uns8*)tagInfo.dataPtr, tagInfo.dataLen );
- }
+ bool isDate = ((id == kTIFF_DateTime) || (id == kTIFF_DateTimeOriginal) || (id == kTIFF_DateTimeOriginal));
- size_t endPos = digestStr->size() - 1;
- (*digestStr)[endPos] = ';';
+ for ( i = 0, chPtr = (char*)info->dataPtr; i < info->dataLen; ++i, ++chPtr ) {
+ if ( isDate && (*chPtr == ':') ) continue; // Ignore colons, empty dates have spaces and colons.
+ if ( (*chPtr != ' ') && (*chPtr != 0) ) break; // Break if the Exif value is non-empty.
+ }
- MD5Final ( digest, &context );
+ if ( i == info->dataLen ) {
+ haveExif = false; // Ignore empty Exif.
+ } else {
+ TrimTrailingSpaces ( info );
+ if ( info->dataLen == 0 ) haveExif = false;
+ }
- for ( in = 0, out = 0; in < 16; in += 1, out += 2 ) {
- XMP_Uns8 byte = digest[in];
- buffer[out] = ReconcileUtils::kHexDigits [ byte >> 4 ];
- buffer[out+1] = ReconcileUtils::kHexDigits [ byte & 0xF ];
}
- buffer[32] = 0;
- digestStr->append ( buffer );
+ return haveExif;
-} // ComputeExifDigest;
+} // PhotoDataUtils::GetNativeInfo
// =================================================================================================
-// ReconcileUtils::CheckTIFFDigest
-// ===============================
-int
-ReconcileUtils::CheckTIFFDigest ( const TIFF_Manager & tiff, const SXMPMeta & xmp )
+size_t PhotoDataUtils::GetNativeInfo ( const IPTC_Manager & iptc, XMP_Uns8 id, int digestState, bool haveXMP, IPTC_Manager::DataSetInfo * info )
{
- std::string newDigest, oldDigest;
+ size_t iptcCount = 0;
- ComputeTIFFDigest ( tiff, &newDigest );
- bool found = xmp.GetProperty ( kXMP_NS_TIFF, "NativeDigest", &oldDigest, 0 );
+ if ( (digestState == kDigestDiffers) || ((digestState == kDigestMissing) && (! haveXMP)) ) {
+ iptcCount = iptc.GetDataSet ( id, info );
+ }
+
+ if ( ignoreLocalText && (iptcCount > 0) && (! iptc.UsingUTF8()) ) {
+ // Check to see if the new value(s) should be ignored.
+ size_t i;
+ IPTC_Manager::DataSetInfo tmpInfo;
+ for ( i = 0; i < iptcCount; ++i ) {
+ (void) iptc.GetDataSet ( id, &tmpInfo, i );
+ if ( ReconcileUtils::IsASCII ( tmpInfo.dataPtr, tmpInfo.dataLen ) ) break;
+ }
+ if ( i == iptcCount ) iptcCount = 0; // Return 0 if value(s) should be ignored.
+ }
- if ( ! found ) return kDigestMissing;
- if ( newDigest == oldDigest ) return kDigestMatches;
- return kDigestDiffers;
+ return iptcCount;
-} // ReconcileUtils::CheckTIFFDigest;
+} // PhotoDataUtils::GetNativeInfo
// =================================================================================================
-// ReconcileUtils::CheckExifDigest
-// ===============================
-int
-ReconcileUtils::CheckExifDigest ( const TIFF_Manager & tiff, const SXMPMeta & xmp )
+bool PhotoDataUtils::IsValueDifferent ( const TIFF_Manager::TagInfo & exifInfo, const std::string & xmpValue, std::string * exifValue )
{
- std::string newDigest, oldDigest;
+ if ( exifInfo.dataLen == 0 ) return false; // Ignore empty Exif values.
- ComputeExifDigest ( tiff, &newDigest );
- bool found = xmp.GetProperty ( kXMP_NS_EXIF, "NativeDigest", &oldDigest, 0 );
+ if ( ReconcileUtils::IsUTF8 ( exifInfo.dataPtr, exifInfo.dataLen ) ) { // ! Note that ASCII is UTF-8.
+ exifValue->assign ( (char*)exifInfo.dataPtr, exifInfo.dataLen );
+ } else {
+ if ( ignoreLocalText ) return false;
+ ReconcileUtils::LocalToUTF8 ( exifInfo.dataPtr, exifInfo.dataLen, exifValue );
+ }
- if ( ! found ) return kDigestMissing;
- if ( newDigest == oldDigest ) return kDigestMatches;
- return kDigestDiffers;
+ return (*exifValue != xmpValue);
-} // ReconcileUtils::CheckExifDigest;
+} // PhotoDataUtils::IsValueDifferent
// =================================================================================================
-// ReconcileUtils::SetTIFFDigest
-// =============================
-void
-ReconcileUtils::SetTIFFDigest ( const TIFF_Manager & tiff, SXMPMeta * xmp )
+bool PhotoDataUtils::IsValueDifferent ( const IPTC_Manager & newIPTC, const IPTC_Manager & oldIPTC, XMP_Uns8 id )
{
- std::string newDigest;
+ IPTC_Manager::DataSetInfo newInfo;
+ size_t newCount = newIPTC.GetDataSet ( id, &newInfo );
+ if ( newCount == 0 ) return false; // Ignore missing new IPTC values.
- ComputeTIFFDigest ( tiff, &newDigest );
- xmp->SetProperty ( kXMP_NS_TIFF, "NativeDigest", newDigest.c_str() );
+ IPTC_Manager::DataSetInfo oldInfo;
+ size_t oldCount = oldIPTC.GetDataSet ( id, &oldInfo );
+ if ( oldCount == 0 ) return true; // Missing old IPTC values differ.
+
+ if ( newCount != oldCount ) return true;
-} // ReconcileUtils::SetTIFFDigest;
+ std::string oldStr, newStr;
-// =================================================================================================
-// ReconcileUtils::SetExifDigest
-// =============================
+ for ( newCount = 0; newCount < oldCount; ++newCount ) {
-void
-ReconcileUtils::SetExifDigest ( const TIFF_Manager & tiff, SXMPMeta * xmp )
-{
- std::string newDigest;
+ if ( ignoreLocalText & (! newIPTC.UsingUTF8()) ) { // Check to see if the new value should be ignored.
+ (void) newIPTC.GetDataSet ( id, &newInfo, newCount );
+ if ( ! ReconcileUtils::IsASCII ( newInfo.dataPtr, newInfo.dataLen ) ) continue;
+ }
+
+ (void) newIPTC.GetDataSet_UTF8 ( id, &newStr, newCount );
+ (void) oldIPTC.GetDataSet_UTF8 ( id, &oldStr, newCount );
+ if ( newStr.size() == 0 ) continue; // Ignore empty new IPTC.
+ if ( newStr != oldStr ) break;
- ComputeExifDigest ( tiff, &newDigest );
- xmp->SetProperty ( kXMP_NS_EXIF, "NativeDigest", newDigest.c_str() );
+ }
-} // ReconcileUtils::SetExifDigest;
+ return ( newCount != oldCount ); // Not different if all values matched.
+
+} // PhotoDataUtils::IsValueDifferent
// =================================================================================================
// =================================================================================================
@@ -372,10 +354,10 @@ ImportSingleTIFF_Short ( const TIFF_Manager::TagInfo & tagInfo, const bool nativ
XMP_Uns16 binValue = *((XMP_Uns16*)tagInfo.dataPtr);
if ( ! nativeEndian ) binValue = Flip2 ( binValue );
-
+
char strValue[20];
snprintf ( strValue, sizeof(strValue), "%hu", binValue ); // AUDIT: Using sizeof(strValue) is safe.
-
+
xmp->SetProperty ( xmpNS, xmpProp, strValue );
} catch ( ... ) {
@@ -397,10 +379,10 @@ ImportSingleTIFF_Long ( const TIFF_Manager::TagInfo & tagInfo, const bool native
XMP_Uns32 binValue = *((XMP_Uns32*)tagInfo.dataPtr);
if ( ! nativeEndian ) binValue = Flip4 ( binValue );
-
+
char strValue[20];
- snprintf ( strValue, sizeof(strValue), "%lu", binValue ); // AUDIT: Using sizeof(strValue) is safe.
-
+ snprintf ( strValue, sizeof(strValue), "%lu", (unsigned long)binValue ); // AUDIT: Using sizeof(strValue) is safe.
+
xmp->SetProperty ( xmpNS, xmpProp, strValue );
} catch ( ... ) {
@@ -427,10 +409,10 @@ ImportSingleTIFF_Rational ( const TIFF_Manager::TagInfo & tagInfo, const bool na
binNum = Flip4 ( binNum );
binDenom = Flip4 ( binDenom );
}
-
+
char strValue[40];
- snprintf ( strValue, sizeof(strValue), "%lu/%lu", binNum, binDenom ); // AUDIT: Using sizeof(strValue) is safe.
-
+ snprintf ( strValue, sizeof(strValue), "%lu/%lu", (unsigned long)binNum, (unsigned long)binDenom ); // AUDIT: Using sizeof(strValue) is safe.
+
xmp->SetProperty ( xmpNS, xmpProp, strValue );
} catch ( ... ) {
@@ -457,10 +439,10 @@ ImportSingleTIFF_SRational ( const TIFF_Manager::TagInfo & tagInfo, const bool n
Flip4 ( &binNum );
Flip4 ( &binDenom );
}
-
+
char strValue[40];
- snprintf ( strValue, sizeof(strValue), "%ld/%ld", binNum, binDenom ); // AUDIT: Using sizeof(strValue) is safe.
-
+ snprintf ( strValue, sizeof(strValue), "%ld/%ld", (unsigned long)binNum, (unsigned long)binDenom ); // AUDIT: Using sizeof(strValue) is safe.
+
xmp->SetProperty ( xmpNS, xmpProp, strValue );
} catch ( ... ) {
@@ -479,11 +461,14 @@ ImportSingleTIFF_ASCII ( const TIFF_Manager::TagInfo & tagInfo,
SXMPMeta * xmp, const char * xmpNS, const char * xmpProp )
{
try { // Don't let errors with one stop the others.
+
+ TrimTrailingSpaces ( (TIFF_Manager::TagInfo*) &tagInfo );
+ if ( tagInfo.dataLen == 0 ) return; // Ignore empty tags.
const char * chPtr = (const char *)tagInfo.dataPtr;
const bool hasNul = (chPtr[tagInfo.dataLen-1] == 0);
const bool isUTF8 = ReconcileUtils::IsUTF8 ( chPtr, tagInfo.dataLen );
-
+
if ( isUTF8 && hasNul ) {
xmp->SetProperty ( xmpNS, xmpProp, chPtr );
} else {
@@ -491,11 +476,8 @@ ImportSingleTIFF_ASCII ( const TIFF_Manager::TagInfo & tagInfo,
if ( isUTF8 ) {
strValue.assign ( chPtr, tagInfo.dataLen );
} else {
- #if ! XMP_UNIXBuild
- ReconcileUtils::LocalToUTF8 ( chPtr, tagInfo.dataLen, &strValue );
- #else
- return; // ! Hack until legacy-as-local issues are resolved for generic UNIX.
- #endif
+ if ( ignoreLocalText ) return;
+ ReconcileUtils::LocalToUTF8 ( chPtr, tagInfo.dataLen, &strValue );
}
xmp->SetProperty ( xmpNS, xmpProp, strValue.c_str() );
}
@@ -518,10 +500,10 @@ ImportSingleTIFF_Byte ( const TIFF_Manager::TagInfo & tagInfo,
try { // Don't let errors with one stop the others.
XMP_Uns8 binValue = *((XMP_Uns8*)tagInfo.dataPtr);
-
+
char strValue[20];
snprintf ( strValue, sizeof(strValue), "%hu", binValue ); // AUDIT: Using sizeof(strValue) is safe.
-
+
xmp->SetProperty ( xmpNS, xmpProp, strValue );
} catch ( ... ) {
@@ -542,10 +524,10 @@ ImportSingleTIFF_SByte ( const TIFF_Manager::TagInfo & tagInfo,
try { // Don't let errors with one stop the others.
XMP_Int8 binValue = *((XMP_Int8*)tagInfo.dataPtr);
-
+
char strValue[20];
snprintf ( strValue, sizeof(strValue), "%hd", binValue ); // AUDIT: Using sizeof(strValue) is safe.
-
+
xmp->SetProperty ( xmpNS, xmpProp, strValue );
} catch ( ... ) {
@@ -567,10 +549,10 @@ ImportSingleTIFF_SShort ( const TIFF_Manager::TagInfo & tagInfo, const bool nati
XMP_Int16 binValue = *((XMP_Int16*)tagInfo.dataPtr);
if ( ! nativeEndian ) Flip2 ( &binValue );
-
+
char strValue[20];
snprintf ( strValue, sizeof(strValue), "%hd", binValue ); // AUDIT: Using sizeof(strValue) is safe.
-
+
xmp->SetProperty ( xmpNS, xmpProp, strValue );
} catch ( ... ) {
@@ -592,10 +574,10 @@ ImportSingleTIFF_SLong ( const TIFF_Manager::TagInfo & tagInfo, const bool nativ
XMP_Int32 binValue = *((XMP_Int32*)tagInfo.dataPtr);
if ( ! nativeEndian ) Flip4 ( &binValue );
-
+
char strValue[20];
- snprintf ( strValue, sizeof(strValue), "%ld", binValue ); // AUDIT: Using sizeof(strValue) is safe.
-
+ snprintf ( strValue, sizeof(strValue), "%ld", (long)binValue ); // AUDIT: Using sizeof(strValue) is safe.
+
xmp->SetProperty ( xmpNS, xmpProp, strValue );
} catch ( ... ) {
@@ -617,7 +599,7 @@ ImportSingleTIFF_Float ( const TIFF_Manager::TagInfo & tagInfo, const bool nativ
float binValue = *((float*)tagInfo.dataPtr);
if ( ! nativeEndian ) Flip4 ( &binValue );
-
+
xmp->SetProperty_Float ( xmpNS, xmpProp, binValue );
} catch ( ... ) {
@@ -639,7 +621,7 @@ ImportSingleTIFF_Double ( const TIFF_Manager::TagInfo & tagInfo, const bool nati
double binValue = *((double*)tagInfo.dataPtr);
if ( ! nativeEndian ) Flip8 ( &binValue );
-
+
xmp->SetProperty_Float ( xmpNS, xmpProp, binValue ); // ! Yes, SetProperty_Float.
} catch ( ... ) {
@@ -727,17 +709,19 @@ ImportArrayTIFF_Short ( const TIFF_Manager::TagInfo & tagInfo, const bool native
try { // Don't let errors with one stop the others.
XMP_Uns16 * binPtr = (XMP_Uns16*)tagInfo.dataPtr;
-
+
+ xmp->DeleteProperty ( xmpNS, xmpProp ); // ! Don't keep appending, create a new array.
+
for ( size_t i = 0; i < tagInfo.count; ++i, ++binPtr ) {
-
+
XMP_Uns16 binValue = *binPtr;
if ( ! nativeEndian ) binValue = Flip2 ( binValue );
-
+
char strValue[20];
snprintf ( strValue, sizeof(strValue), "%hu", binValue ); // AUDIT: Using sizeof(strValue) is safe.
-
+
xmp->AppendArrayItem ( xmpNS, xmpProp, kXMP_PropArrayIsOrdered, strValue );
-
+
}
} catch ( ... ) {
@@ -758,17 +742,19 @@ ImportArrayTIFF_Long ( const TIFF_Manager::TagInfo & tagInfo, const bool nativeE
try { // Don't let errors with one stop the others.
XMP_Uns32 * binPtr = (XMP_Uns32*)tagInfo.dataPtr;
-
+
+ xmp->DeleteProperty ( xmpNS, xmpProp ); // ! Don't keep appending, create a new array.
+
for ( size_t i = 0; i < tagInfo.count; ++i, ++binPtr ) {
-
+
XMP_Uns32 binValue = *binPtr;
if ( ! nativeEndian ) binValue = Flip4 ( binValue );
-
+
char strValue[20];
- snprintf ( strValue, sizeof(strValue), "%lu", binValue ); // AUDIT: Using sizeof(strValue) is safe.
-
+ snprintf ( strValue, sizeof(strValue), "%lu", (unsigned long)binValue ); // AUDIT: Using sizeof(strValue) is safe.
+
xmp->AppendArrayItem ( xmpNS, xmpProp, kXMP_PropArrayIsOrdered, strValue );
-
+
}
} catch ( ... ) {
@@ -789,21 +775,23 @@ ImportArrayTIFF_Rational ( const TIFF_Manager::TagInfo & tagInfo, const bool nat
try { // Don't let errors with one stop the others.
XMP_Uns32 * binPtr = (XMP_Uns32*)tagInfo.dataPtr;
-
+
+ xmp->DeleteProperty ( xmpNS, xmpProp ); // ! Don't keep appending, create a new array.
+
for ( size_t i = 0; i < tagInfo.count; ++i, binPtr += 2 ) {
-
+
XMP_Uns32 binNum = binPtr[0];
XMP_Uns32 binDenom = binPtr[1];
if ( ! nativeEndian ) {
binNum = Flip4 ( binNum );
binDenom = Flip4 ( binDenom );
}
-
+
char strValue[40];
- snprintf ( strValue, sizeof(strValue), "%lu/%lu", binNum, binDenom ); // AUDIT: Using sizeof(strValue) is safe.
-
+ snprintf ( strValue, sizeof(strValue), "%lu/%lu", (unsigned long)binNum, (unsigned long)binDenom ); // AUDIT: Using sizeof(strValue) is safe.
+
xmp->AppendArrayItem ( xmpNS, xmpProp, kXMP_PropArrayIsOrdered, strValue );
-
+
}
} catch ( ... ) {
@@ -824,21 +812,23 @@ ImportArrayTIFF_SRational ( const TIFF_Manager::TagInfo & tagInfo, const bool na
try { // Don't let errors with one stop the others.
XMP_Int32 * binPtr = (XMP_Int32*)tagInfo.dataPtr;
-
+
+ xmp->DeleteProperty ( xmpNS, xmpProp ); // ! Don't keep appending, create a new array.
+
for ( size_t i = 0; i < tagInfo.count; ++i, binPtr += 2 ) {
-
+
XMP_Int32 binNum = binPtr[0];
XMP_Int32 binDenom = binPtr[1];
if ( ! nativeEndian ) {
Flip4 ( &binNum );
Flip4 ( &binDenom );
}
-
+
char strValue[40];
- snprintf ( strValue, sizeof(strValue), "%ld/%ld", binNum, binDenom ); // AUDIT: Using sizeof(strValue) is safe.
-
+ snprintf ( strValue, sizeof(strValue), "%ld/%ld", (long)binNum, (long)binDenom ); // AUDIT: Using sizeof(strValue) is safe.
+
xmp->AppendArrayItem ( xmpNS, xmpProp, kXMP_PropArrayIsOrdered, strValue );
-
+
}
} catch ( ... ) {
@@ -857,28 +847,30 @@ ImportArrayTIFF_ASCII ( const TIFF_Manager::TagInfo & tagInfo,
SXMPMeta * xmp, const char * xmpNS, const char * xmpProp )
{
try { // Don't let errors with one stop the others.
+
+ TrimTrailingSpaces ( (TIFF_Manager::TagInfo*) &tagInfo );
+ if ( tagInfo.dataLen == 0 ) return; // Ignore empty tags.
const char * chPtr = (const char *)tagInfo.dataPtr;
const char * chEnd = chPtr + tagInfo.dataLen;
const bool hasNul = (chPtr[tagInfo.dataLen-1] == 0);
const bool isUTF8 = ReconcileUtils::IsUTF8 ( chPtr, tagInfo.dataLen );
-
+
std::string strValue;
-
+
if ( (! isUTF8) || (! hasNul) ) {
if ( isUTF8 ) {
strValue.assign ( chPtr, tagInfo.dataLen );
} else {
- #if ! XMP_UNIXBuild
- ReconcileUtils::LocalToUTF8 ( chPtr, tagInfo.dataLen, &strValue );
- #else
- return; // ! Hack until legacy-as-local issues are resolved for generic UNIX.
- #endif
+ if ( ignoreLocalText ) return;
+ ReconcileUtils::LocalToUTF8 ( chPtr, tagInfo.dataLen, &strValue );
}
chPtr = strValue.c_str();
chEnd = chPtr + strValue.size();
}
-
+
+ xmp->DeleteProperty ( xmpNS, xmpProp ); // ! Don't keep appending, create a new array.
+
for ( ; chPtr < chEnd; chPtr += (strlen(chPtr) + 1) ) {
xmp->AppendArrayItem ( xmpNS, xmpProp, kXMP_PropArrayIsOrdered, chPtr );
}
@@ -901,16 +893,18 @@ ImportArrayTIFF_Byte ( const TIFF_Manager::TagInfo & tagInfo,
try { // Don't let errors with one stop the others.
XMP_Uns8 * binPtr = (XMP_Uns8*)tagInfo.dataPtr;
-
+
+ xmp->DeleteProperty ( xmpNS, xmpProp ); // ! Don't keep appending, create a new array.
+
for ( size_t i = 0; i < tagInfo.count; ++i, ++binPtr ) {
-
+
XMP_Uns8 binValue = *binPtr;
-
+
char strValue[20];
snprintf ( strValue, sizeof(strValue), "%hu", binValue ); // AUDIT: Using sizeof(strValue) is safe.
-
+
xmp->AppendArrayItem ( xmpNS, xmpProp, kXMP_PropArrayIsOrdered, strValue );
-
+
}
} catch ( ... ) {
@@ -931,16 +925,18 @@ ImportArrayTIFF_SByte ( const TIFF_Manager::TagInfo & tagInfo,
try { // Don't let errors with one stop the others.
XMP_Int8 * binPtr = (XMP_Int8*)tagInfo.dataPtr;
-
+
+ xmp->DeleteProperty ( xmpNS, xmpProp ); // ! Don't keep appending, create a new array.
+
for ( size_t i = 0; i < tagInfo.count; ++i, ++binPtr ) {
-
+
XMP_Int8 binValue = *binPtr;
-
+
char strValue[20];
snprintf ( strValue, sizeof(strValue), "%hd", binValue ); // AUDIT: Using sizeof(strValue) is safe.
-
+
xmp->AppendArrayItem ( xmpNS, xmpProp, kXMP_PropArrayIsOrdered, strValue );
-
+
}
} catch ( ... ) {
@@ -961,17 +957,19 @@ ImportArrayTIFF_SShort ( const TIFF_Manager::TagInfo & tagInfo, const bool nativ
try { // Don't let errors with one stop the others.
XMP_Int16 * binPtr = (XMP_Int16*)tagInfo.dataPtr;
-
+
+ xmp->DeleteProperty ( xmpNS, xmpProp ); // ! Don't keep appending, create a new array.
+
for ( size_t i = 0; i < tagInfo.count; ++i, ++binPtr ) {
-
+
XMP_Int16 binValue = *binPtr;
if ( ! nativeEndian ) Flip2 ( &binValue );
-
+
char strValue[20];
snprintf ( strValue, sizeof(strValue), "%hd", binValue ); // AUDIT: Using sizeof(strValue) is safe.
-
+
xmp->AppendArrayItem ( xmpNS, xmpProp, kXMP_PropArrayIsOrdered, strValue );
-
+
}
} catch ( ... ) {
@@ -992,17 +990,19 @@ ImportArrayTIFF_SLong ( const TIFF_Manager::TagInfo & tagInfo, const bool native
try { // Don't let errors with one stop the others.
XMP_Int32 * binPtr = (XMP_Int32*)tagInfo.dataPtr;
-
+
+ xmp->DeleteProperty ( xmpNS, xmpProp ); // ! Don't keep appending, create a new array.
+
for ( size_t i = 0; i < tagInfo.count; ++i, ++binPtr ) {
-
+
XMP_Int32 binValue = *binPtr;
if ( ! nativeEndian ) Flip4 ( &binValue );
-
+
char strValue[20];
- snprintf ( strValue, sizeof(strValue), "%ld", binValue ); // AUDIT: Using sizeof(strValue) is safe.
-
+ snprintf ( strValue, sizeof(strValue), "%ld", (long)binValue ); // AUDIT: Using sizeof(strValue) is safe.
+
xmp->AppendArrayItem ( xmpNS, xmpProp, kXMP_PropArrayIsOrdered, strValue );
-
+
}
} catch ( ... ) {
@@ -1023,17 +1023,19 @@ ImportArrayTIFF_Float ( const TIFF_Manager::TagInfo & tagInfo, const bool native
try { // Don't let errors with one stop the others.
float * binPtr = (float*)tagInfo.dataPtr;
-
+
+ xmp->DeleteProperty ( xmpNS, xmpProp ); // ! Don't keep appending, create a new array.
+
for ( size_t i = 0; i < tagInfo.count; ++i, ++binPtr ) {
-
+
float binValue = *binPtr;
if ( ! nativeEndian ) Flip4 ( &binValue );
-
+
std::string strValue;
SXMPUtils::ConvertFromFloat ( binValue, "", &strValue );
-
+
xmp->AppendArrayItem ( xmpNS, xmpProp, kXMP_PropArrayIsOrdered, strValue.c_str() );
-
+
}
} catch ( ... ) {
@@ -1054,17 +1056,19 @@ ImportArrayTIFF_Double ( const TIFF_Manager::TagInfo & tagInfo, const bool nativ
try { // Don't let errors with one stop the others.
double * binPtr = (double*)tagInfo.dataPtr;
-
+
+ xmp->DeleteProperty ( xmpNS, xmpProp ); // ! Don't keep appending, create a new array.
+
for ( size_t i = 0; i < tagInfo.count; ++i, ++binPtr ) {
-
+
double binValue = *binPtr;
if ( ! nativeEndian ) Flip8 ( &binValue );
-
+
std::string strValue;
SXMPUtils::ConvertFromFloat ( binValue, "", &strValue ); // ! Yes, ConvertFromFloat.
-
+
xmp->AppendArrayItem ( xmpNS, xmpProp, kXMP_PropArrayIsOrdered, strValue.c_str() );
-
+
}
} catch ( ... ) {
@@ -1139,45 +1143,11 @@ ImportArrayTIFF ( const TIFF_Manager::TagInfo & tagInfo, const bool nativeEndian
} // ImportArrayTIFF
// =================================================================================================
-// ImportTIFF_VerifyImport
-// =======================
-//
-// Decide whether to proceed with the import based on the digest state and presence of the legacy
-// and XMP. Will also delete existing XMP if appropriate.
-
-static bool
-ImportTIFF_VerifyImport ( const TIFF_Manager & tiff, SXMPMeta * xmp, int digestState,
- XMP_Uns8 tiffIFD, XMP_Uns16 tiffID, const char * xmpNS, const char * xmpProp,
- TIFF_Manager::TagInfo * tagInfo )
-{
- bool found = false;
-
- try { // Don't let errors with one stop the others.
-
- if ( digestState == kDigestDiffers ) {
- xmp->DeleteProperty ( xmpNS, xmpProp );
- } else {
- XMP_Assert ( digestState == kDigestMissing );
- if ( xmp->DoesPropertyExist ( xmpNS, xmpProp ) ) return false;
- }
-
- found = tiff.GetTag ( tiffIFD, tiffID, tagInfo );
-
- } catch ( ... ) {
- found = false;
- }
-
- return found;
-
-} // ImportTIFF_VerifyImport
-
-// =================================================================================================
// ImportTIFF_CheckStandardMapping
// ===============================
static bool
-ImportTIFF_CheckStandardMapping ( const TIFF_Manager::TagInfo & tagInfo,
- const TIFF_MappingToXMP & mapInfo )
+ImportTIFF_CheckStandardMapping ( const TIFF_Manager::TagInfo & tagInfo, const TIFF_MappingToXMP & mapInfo )
{
XMP_Assert ( (kTIFF_ByteType <= tagInfo.type) && (tagInfo.type <= kTIFF_LastType) );
XMP_Assert ( mapInfo.type <= kTIFF_LastType );
@@ -1189,7 +1159,9 @@ ImportTIFF_CheckStandardMapping ( const TIFF_Manager::TagInfo & tagInfo,
if ( (tagInfo.type != kTIFF_ShortType) && (tagInfo.type != kTIFF_LongType) ) return false;
}
- if ( (tagInfo.count != mapInfo.count) && (mapInfo.count != kAnyCount) ) return false;
+ if ( (tagInfo.count != mapInfo.count) && // Maybe there is a problem because the counts don't match.
+ // (mapInfo.count != kAnyCount) && ... don't need this because of the new check below ...
+ (mapInfo.count == 1) ) return false; // Be tolerant of mismatch in expected array size.
return true;
@@ -1200,7 +1172,7 @@ ImportTIFF_CheckStandardMapping ( const TIFF_Manager::TagInfo & tagInfo,
// ===========================
static void
-ImportTIFF_StandardMappings ( XMP_Uns8 ifd, const TIFF_Manager & tiff, SXMPMeta * xmp, int digestState )
+ImportTIFF_StandardMappings ( XMP_Uns8 ifd, const TIFF_Manager & tiff, SXMPMeta * xmp )
{
const bool nativeEndian = tiff.IsNativeEndian();
TIFF_Manager::TagInfo tagInfo;
@@ -1227,22 +1199,16 @@ ImportTIFF_StandardMappings ( XMP_Uns8 ifd, const TIFF_Manager & tiff, SXMPMeta
const TIFF_MappingToXMP & mapInfo = mappings[i];
const bool mapSingle = ((mapInfo.count == 1) || (mapInfo.type == kTIFF_ASCIIType));
-
- // Skip tags that have special mappings, they are handled individually later. Delete any
- // existing XMP property before going further. But after the special mapping check since we
- // don't have the XMP property name for those. This lets legacy deletions propagate and
- // eliminates any problems with existing XMP property form. Make sure the actual tag has
- // the expected type and count, ignore it (pretend it is not present) if not.
-
- if ( mapInfo.name[0] == 0 ) continue; // Skip special mappings.
-
- bool ok = ImportTIFF_VerifyImport ( tiff, xmp, digestState, ifd, mapInfo.id, xmpNS, mapInfo.name, &tagInfo );
- if (! ok ) continue;
-
- XMP_Assert ( tagInfo.type != kTIFF_UndefinedType ); // These have a special mapping.
+
+ if ( mapInfo.name[0] == 0 ) continue; // Skip special mappings, handled higher up.
+
+ bool found = tiff.GetTag ( ifd, mapInfo.id, &tagInfo );
+ if ( ! found ) continue;
+
+ XMP_Assert ( tagInfo.type != kTIFF_UndefinedType ); // These must have a special mapping.
if ( tagInfo.type == kTIFF_UndefinedType ) continue;
if ( ! ImportTIFF_CheckStandardMapping ( tagInfo, mapInfo ) ) continue;
-
+
if ( mapSingle ) {
ImportSingleTIFF ( tagInfo, nativeEndian, xmp, xmpNS, mapInfo.name );
} else {
@@ -1250,10 +1216,10 @@ ImportTIFF_StandardMappings ( XMP_Uns8 ifd, const TIFF_Manager & tiff, SXMPMeta
}
} catch ( ... ) {
-
+
// Do nothing, let other imports proceed.
// ? Notify client?
-
+
}
}
@@ -1274,38 +1240,55 @@ ImportTIFF_StandardMappings ( XMP_Uns8 ifd, const TIFF_Manager & tiff, SXMPMeta
// part, the digits that would be to the right of the decimal point.
static void
-ImportTIFF_Date ( const TIFF_Manager & tiff, const TIFF_Manager::TagInfo & dateInfo, XMP_Uns16 secID,
+ImportTIFF_Date ( const TIFF_Manager & tiff, const TIFF_Manager::TagInfo & dateInfo,
SXMPMeta * xmp, const char * xmpNS, const char * xmpProp )
{
+ XMP_Uns16 secID;
+ switch ( dateInfo.id ) {
+ case kTIFF_DateTime : secID = kTIFF_SubSecTime; break;
+ case kTIFF_DateTimeOriginal : secID = kTIFF_SubSecTimeOriginal; break;
+ case kTIFF_DateTimeDigitized : secID = kTIFF_SubSecTimeDigitized; break;
+ }
+
try { // Don't let errors with one stop the others.
+
+ if ( (dateInfo.type != kTIFF_ASCIIType) || (dateInfo.count != 20) ) return;
const char * dateStr = (const char *) dateInfo.dataPtr;
if ( (dateStr[4] != ':') || (dateStr[7] != ':') ||
(dateStr[10] != ' ') || (dateStr[13] != ':') || (dateStr[16] != ':') ) return;
-
+
XMP_DateTime binValue;
-
+
binValue.year = GatherInt ( &dateStr[0], 4 );
binValue.month = GatherInt ( &dateStr[5], 2 );
binValue.day = GatherInt ( &dateStr[8], 2 );
+ if ( (binValue.year != 0) | (binValue.month != 0) | (binValue.day != 0) ) binValue.hasDate = true;
+
binValue.hour = GatherInt ( &dateStr[11], 2 );
binValue.minute = GatherInt ( &dateStr[14], 2 );
binValue.second = GatherInt ( &dateStr[17], 2 );
binValue.nanoSecond = 0; // Get the fractional seconds later.
- binValue.tzSign = binValue.tzHour = binValue.tzMinute = 0;
- SXMPUtils::SetTimeZone ( &binValue ); // Assume local time.
-
+ if ( (binValue.hour != 0) | (binValue.minute != 0) | (binValue.second != 0) ) binValue.hasTime = true;
+
+ binValue.tzSign = 0; // ! Separate assignment, avoid VS integer truncation warning.
+ binValue.tzHour = binValue.tzMinute = 0;
+ binValue.hasTimeZone = false; // Exif times have no zone.
+
+ // *** Consider looking at the TIFF/EP TimeZoneOffset tag?
+
TIFF_Manager::TagInfo secInfo;
- bool found = tiff.GetTag ( kTIFF_ExifIFD, secID, &secInfo );
-
+ bool found = tiff.GetTag ( kTIFF_ExifIFD, secID, &secInfo ); // ! Subseconds are all in the Exif IFD.
+
if ( found && (secInfo.type == kTIFF_ASCIIType) ) {
const char * fracPtr = (const char *) secInfo.dataPtr;
binValue.nanoSecond = GatherInt ( fracPtr, secInfo.dataLen );
size_t digits = 0;
for ( ; (('0' <= *fracPtr) && (*fracPtr <= '9')); ++fracPtr ) ++digits;
for ( ; digits < 9; ++digits ) binValue.nanoSecond *= 10;
+ if ( binValue.nanoSecond != 0 ) binValue.hasTime = true;
}
-
+
xmp->SetProperty_Date ( xmpNS, xmpProp, binValue );
} catch ( ... ) {
@@ -1326,14 +1309,14 @@ ImportTIFF_LocTextASCII ( const TIFF_Manager & tiff, XMP_Uns8 ifd, XMP_Uns16 tag
try { // Don't let errors with one stop the others.
TIFF_Manager::TagInfo tagInfo;
-
+
bool found = tiff.GetTag ( ifd, tagID, &tagInfo );
if ( (! found) || (tagInfo.type != kTIFF_ASCIIType) ) return;
-
+
const char * chPtr = (const char *)tagInfo.dataPtr;
const bool hasNul = (chPtr[tagInfo.dataLen-1] == 0);
const bool isUTF8 = ReconcileUtils::IsUTF8 ( chPtr, tagInfo.dataLen );
-
+
if ( isUTF8 && hasNul ) {
xmp->SetLocalizedText ( xmpNS, xmpProp, "", "x-default", chPtr );
} else {
@@ -1341,11 +1324,8 @@ ImportTIFF_LocTextASCII ( const TIFF_Manager & tiff, XMP_Uns8 ifd, XMP_Uns16 tag
if ( isUTF8 ) {
strValue.assign ( chPtr, tagInfo.dataLen );
} else {
- #if ! XMP_UNIXBuild
- ReconcileUtils::LocalToUTF8 ( chPtr, tagInfo.dataLen, &strValue );
- #else
- return; // ! Hack until legacy-as-local issues are resolved for generic UNIX.
- #endif
+ if ( ignoreLocalText ) return;
+ ReconcileUtils::LocalToUTF8 ( chPtr, tagInfo.dataLen, &strValue );
}
xmp->SetLocalizedText ( xmpNS, xmpProp, "", "x-default", strValue.c_str() );
}
@@ -1368,7 +1348,7 @@ ImportTIFF_EncodedString ( const TIFF_Manager & tiff, const TIFF_Manager::TagInf
try { // Don't let errors with one stop the others.
std::string strValue;
-
+
bool ok = tiff.DecodeString ( tagInfo.dataPtr, tagInfo.dataLen, &strValue );
if ( ! ok ) return;
@@ -1382,7 +1362,7 @@ ImportTIFF_EncodedString ( const TIFF_Manager & tiff, const TIFF_Manager::TagInf
// Do nothing, let other imports proceed.
// ? Notify client?
}
-
+
} // ImportTIFF_EncodedString
// =================================================================================================
@@ -1397,15 +1377,15 @@ ImportTIFF_Flash ( const TIFF_Manager::TagInfo & tagInfo, bool nativeEndian,
XMP_Uns16 binValue = *((XMP_Uns16*)tagInfo.dataPtr);
if ( ! nativeEndian ) binValue = Flip2 ( binValue );
-
+
bool fired = (bool)(binValue & 1); // Avoid implicit 0/1 conversion.
int rtrn = (binValue >> 1) & 3;
int mode = (binValue >> 3) & 3;
bool function = (bool)((binValue >> 5) & 1);
bool redEye = (bool)((binValue >> 6) & 1);
-
+
static const char * sTwoBits[] = { "0", "1", "2", "3" };
-
+
xmp->SetStructField ( kXMP_NS_EXIF, "Flash", kXMP_NS_EXIF, "Fired", (fired ? kXMP_TrueStr : kXMP_FalseStr) );
xmp->SetStructField ( kXMP_NS_EXIF, "Flash", kXMP_NS_EXIF, "Return", sTwoBits[rtrn] );
xmp->SetStructField ( kXMP_NS_EXIF, "Flash", kXMP_NS_EXIF, "Mode", sTwoBits[mode] );
@@ -1422,7 +1402,7 @@ ImportTIFF_Flash ( const TIFF_Manager::TagInfo & tagInfo, bool nativeEndian,
// =================================================================================================
// ImportTIFF_OECFTable
// ====================
-//
+//
// Although the XMP for the OECF and SFR tables is the same, the Exif is not. The OECF table has
// signed rational values and the SFR table has unsigned.
@@ -1434,25 +1414,25 @@ ImportTIFF_OECFTable ( const TIFF_Manager::TagInfo & tagInfo, bool nativeEndian,
const XMP_Uns8 * bytePtr = (XMP_Uns8*)tagInfo.dataPtr;
const XMP_Uns8 * byteEnd = bytePtr + tagInfo.dataLen;
-
- XMP_Uns16 columns = *((XMP_Uns16*)bytePtr);
+
+ XMP_Uns16 columns = *((XMP_Uns16*)bytePtr);
XMP_Uns16 rows = *((XMP_Uns16*)(bytePtr+2));
if ( ! nativeEndian ) {
columns = Flip2 ( columns );
rows = Flip2 ( rows );
}
-
+
char buffer[40];
-
+
snprintf ( buffer, sizeof(buffer), "%d", columns ); // AUDIT: Use of sizeof(buffer) is safe.
xmp->SetStructField ( xmpNS, xmpProp, kXMP_NS_EXIF, "Columns", buffer );
snprintf ( buffer, sizeof(buffer), "%d", rows ); // AUDIT: Use of sizeof(buffer) is safe.
xmp->SetStructField ( xmpNS, xmpProp, kXMP_NS_EXIF, "Rows", buffer );
-
+
std::string arrayPath;
-
+
SXMPUtils::ComposeStructFieldPath ( xmpNS, xmpProp, kXMP_NS_EXIF, "Names", &arrayPath );
-
+
bytePtr += 4; // Move to the list of names.
for ( size_t i = columns; i > 0; --i ) {
size_t nameLen = strlen((XMP_StringPtr)bytePtr) + 1; // ! Include the terminating nul.
@@ -1460,26 +1440,26 @@ ImportTIFF_OECFTable ( const TIFF_Manager::TagInfo & tagInfo, bool nativeEndian,
xmp->AppendArrayItem ( xmpNS, arrayPath.c_str(), kXMP_PropArrayIsOrdered, (XMP_StringPtr)bytePtr );
bytePtr += nameLen;
}
-
+
if ( (byteEnd - bytePtr) != (8 * columns * rows) ) { xmp->DeleteProperty ( xmpNS, xmpProp ); return; }; // Make sure the values are present.
SXMPUtils::ComposeStructFieldPath ( xmpNS, xmpProp, kXMP_NS_EXIF, "Values", &arrayPath );
-
+
XMP_Int32 * binPtr = (XMP_Int32*)bytePtr;
for ( size_t i = (columns * rows); i > 0; --i, binPtr += 2 ) {
-
+
XMP_Int32 binNum = binPtr[0];
XMP_Int32 binDenom = binPtr[1];
if ( ! nativeEndian ) {
Flip4 ( &binNum );
Flip4 ( &binDenom );
}
-
- snprintf ( buffer, sizeof(buffer), "%ld/%ld", binNum, binDenom ); // AUDIT: Use of sizeof(buffer) is safe.
-
+
+ snprintf ( buffer, sizeof(buffer), "%ld/%ld", (long)binNum, (long)binDenom ); // AUDIT: Use of sizeof(buffer) is safe.
+
xmp->AppendArrayItem ( xmpNS, arrayPath.c_str(), kXMP_PropArrayIsOrdered, buffer );
-
+
}
-
+
return;
} catch ( ... ) {
@@ -1492,7 +1472,7 @@ ImportTIFF_OECFTable ( const TIFF_Manager::TagInfo & tagInfo, bool nativeEndian,
// =================================================================================================
// ImportTIFF_SFRTable
// ===================
-//
+//
// Although the XMP for the OECF and SFR tables is the same, the Exif is not. The OECF table has
// signed rational values and the SFR table has unsigned.
@@ -1504,25 +1484,25 @@ ImportTIFF_SFRTable ( const TIFF_Manager::TagInfo & tagInfo, bool nativeEndian,
const XMP_Uns8 * bytePtr = (XMP_Uns8*)tagInfo.dataPtr;
const XMP_Uns8 * byteEnd = bytePtr + tagInfo.dataLen;
-
- XMP_Uns16 columns = *((XMP_Uns16*)bytePtr);
+
+ XMP_Uns16 columns = *((XMP_Uns16*)bytePtr);
XMP_Uns16 rows = *((XMP_Uns16*)(bytePtr+2));
if ( ! nativeEndian ) {
columns = Flip2 ( columns );
rows = Flip2 ( rows );
}
-
+
char buffer[40];
-
+
snprintf ( buffer, sizeof(buffer), "%d", columns ); // AUDIT: Use of sizeof(buffer) is safe.
xmp->SetStructField ( xmpNS, xmpProp, kXMP_NS_EXIF, "Columns", buffer );
snprintf ( buffer, sizeof(buffer), "%d", rows ); // AUDIT: Use of sizeof(buffer) is safe.
xmp->SetStructField ( xmpNS, xmpProp, kXMP_NS_EXIF, "Rows", buffer );
-
+
std::string arrayPath;
-
+
SXMPUtils::ComposeStructFieldPath ( xmpNS, xmpProp, kXMP_NS_EXIF, "Names", &arrayPath );
-
+
bytePtr += 4; // Move to the list of names.
for ( size_t i = columns; i > 0; --i ) {
size_t nameLen = strlen((XMP_StringPtr)bytePtr) + 1; // ! Include the terminating nul.
@@ -1530,26 +1510,26 @@ ImportTIFF_SFRTable ( const TIFF_Manager::TagInfo & tagInfo, bool nativeEndian,
xmp->AppendArrayItem ( xmpNS, arrayPath.c_str(), kXMP_PropArrayIsOrdered, (XMP_StringPtr)bytePtr );
bytePtr += nameLen;
}
-
+
if ( (byteEnd - bytePtr) != (8 * columns * rows) ) { xmp->DeleteProperty ( xmpNS, xmpProp ); return; }; // Make sure the values are present.
SXMPUtils::ComposeStructFieldPath ( xmpNS, xmpProp, kXMP_NS_EXIF, "Values", &arrayPath );
-
+
XMP_Uns32 * binPtr = (XMP_Uns32*)bytePtr;
for ( size_t i = (columns * rows); i > 0; --i, binPtr += 2 ) {
-
+
XMP_Uns32 binNum = binPtr[0];
XMP_Uns32 binDenom = binPtr[1];
if ( ! nativeEndian ) {
binNum = Flip4 ( binNum );
binDenom = Flip4 ( binDenom );
}
-
- snprintf ( buffer, sizeof(buffer), "%lu/%lu", binNum, binDenom ); // AUDIT: Use of sizeof(buffer) is safe.
-
+
+ snprintf ( buffer, sizeof(buffer), "%lu/%lu", (unsigned long)binNum, (unsigned long)binDenom ); // AUDIT: Use of sizeof(buffer) is safe.
+
xmp->AppendArrayItem ( xmpNS, arrayPath.c_str(), kXMP_PropArrayIsOrdered, buffer );
-
+
}
-
+
return;
} catch ( ... ) {
@@ -1571,34 +1551,34 @@ ImportTIFF_CFATable ( const TIFF_Manager::TagInfo & tagInfo, bool nativeEndian,
const XMP_Uns8 * bytePtr = (XMP_Uns8*)tagInfo.dataPtr;
const XMP_Uns8 * byteEnd = bytePtr + tagInfo.dataLen;
-
- XMP_Uns16 columns = *((XMP_Uns16*)bytePtr);
+
+ XMP_Uns16 columns = *((XMP_Uns16*)bytePtr);
XMP_Uns16 rows = *((XMP_Uns16*)(bytePtr+2));
if ( ! nativeEndian ) {
columns = Flip2 ( columns );
rows = Flip2 ( rows );
}
-
+
char buffer[20];
std::string arrayPath;
-
+
snprintf ( buffer, sizeof(buffer), "%d", columns ); // AUDIT: Use of sizeof(buffer) is safe.
xmp->SetStructField ( xmpNS, xmpProp, kXMP_NS_EXIF, "Columns", buffer );
snprintf ( buffer, sizeof(buffer), "%d", rows ); // AUDIT: Use of sizeof(buffer) is safe.
xmp->SetStructField ( xmpNS, xmpProp, kXMP_NS_EXIF, "Rows", buffer );
-
+
bytePtr += 4; // Move to the matrix of values.
if ( (byteEnd - bytePtr) != (columns * rows) ) goto BadExif; // Make sure the values are present.
-
+
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.
xmp->AppendArrayItem ( xmpNS, arrayPath.c_str(), kXMP_PropArrayIsOrdered, buffer );
}
-
+
return;
-
+
BadExif: // Ignore the tag if the table is ill-formed.
xmp->DeleteProperty ( xmpNS, xmpProp );
return;
@@ -1622,55 +1602,55 @@ ImportTIFF_DSDTable ( const TIFF_Manager & tiff, const TIFF_Manager::TagInfo & t
const XMP_Uns8 * bytePtr = (XMP_Uns8*)tagInfo.dataPtr;
const XMP_Uns8 * byteEnd = bytePtr + tagInfo.dataLen;
-
- XMP_Uns16 columns = *((XMP_Uns16*)bytePtr);
+
+ XMP_Uns16 columns = *((XMP_Uns16*)bytePtr);
XMP_Uns16 rows = *((XMP_Uns16*)(bytePtr+2));
if ( ! tiff.IsNativeEndian() ) {
columns = Flip2 ( columns );
rows = Flip2 ( rows );
}
-
+
char buffer[20];
-
+
snprintf ( buffer, sizeof(buffer), "%d", columns ); // AUDIT: Use of sizeof(buffer) is safe.
xmp->SetStructField ( xmpNS, xmpProp, kXMP_NS_EXIF, "Columns", buffer );
snprintf ( buffer, sizeof(buffer), "%d", rows ); // AUDIT: Use of sizeof(buffer) is safe.
xmp->SetStructField ( xmpNS, xmpProp, kXMP_NS_EXIF, "Rows", buffer );
-
+
std::string arrayPath;
SXMPUtils::ComposeStructFieldPath ( xmpNS, xmpProp, kXMP_NS_EXIF, "Settings", &arrayPath );
-
+
bytePtr += 4; // Move to the list of settings.
UTF16Unit * utf16Ptr = (UTF16Unit*)bytePtr;
UTF16Unit * utf16End = (UTF16Unit*)byteEnd;
-
+
std::string utf8;
-
+
// Figure 17 in the Exif 2.2 spec is unclear. It has counts for rows and columns, but the
// settings are listed as 1..n, not as a rectangular matrix. So, ignore the counts and copy
// strings until the end of the Exif value.
-
+
while ( utf16Ptr < utf16End ) {
-
+
size_t nameLen = 0;
while ( utf16Ptr[nameLen] != 0 ) ++nameLen;
++nameLen; // ! Include the terminating nul.
if ( (utf16Ptr + nameLen) > utf16End ) goto BadExif;
-
+
try {
FromUTF16 ( utf16Ptr, nameLen, &utf8, tiff.IsBigEndian() );
} catch ( ... ) {
goto BadExif; // Ignore the tag if there are conversion errors.
}
-
+
xmp->AppendArrayItem ( xmpNS, arrayPath.c_str(), kXMP_PropArrayIsOrdered, utf8.c_str() );
-
+
utf16Ptr += nameLen;
-
+
}
-
+
return;
-
+
BadExif: // Ignore the tag if the table is ill-formed.
xmp->DeleteProperty ( xmpNS, xmpProp );
return;
@@ -1693,13 +1673,13 @@ ImportTIFF_GPSCoordinate ( const TIFF_Manager & tiff, const TIFF_Manager::TagInf
try { // Don't let errors with one stop the others.
const bool nativeEndian = tiff.IsNativeEndian();
-
+
XMP_Uns16 refID = posInfo.id - 1; // ! The GPS refs and coordinates are all tag n and n+1.
TIFF_Manager::TagInfo refInfo;
bool found = tiff.GetTag ( kTIFF_GPSInfoIFD, refID, &refInfo );
if ( (! found) || (refInfo.type != kTIFF_ASCIIType) || (refInfo.count != 2) ) return;
char ref = *((char*)refInfo.dataPtr);
-
+
XMP_Uns32 * binPtr = (XMP_Uns32*)posInfo.dataPtr;
XMP_Uns32 degNum = binPtr[0];
XMP_Uns32 degDenom = binPtr[1];
@@ -1715,32 +1695,32 @@ ImportTIFF_GPSCoordinate ( const TIFF_Manager & tiff, const TIFF_Manager::TagInf
secNum = Flip4 ( secNum );
secDenom = Flip4 ( secDenom );
}
-
+
char buffer[40];
-
+
if ( (degDenom == 1) && (minDenom == 1) && (secDenom == 1) ) {
-
- snprintf ( buffer, sizeof(buffer), "%lu,%lu,%lu%c", degNum, minNum, secNum, ref ); // AUDIT: Using sizeof(buffer is safe.
-
+
+ snprintf ( buffer, sizeof(buffer), "%lu,%lu,%lu%c", (unsigned long)degNum, (unsigned long)minNum, (unsigned long)secNum, ref ); // AUDIT: Using sizeof(buffer is safe.
+
} else {
-
+
XMP_Uns32 maxDenom = degDenom;
if ( minDenom > degDenom ) maxDenom = minDenom;
if ( secDenom > degDenom ) maxDenom = secDenom;
-
+
int fracDigits = 1;
while ( maxDenom > 10 ) { ++fracDigits; maxDenom = maxDenom/10; }
-
+
double temp = (double)degNum / (double)degDenom;
double degrees = (double)((XMP_Uns32)temp); // Just the integral number of degrees.
double minutes = ((temp - degrees) * 60.0) +
((double)minNum / (double)minDenom) +
(((double)secNum / (double)secDenom) / 60.0);
-
+
snprintf ( buffer, sizeof(buffer), "%.0f,%.*f%c", degrees, fracDigits, minutes, ref ); // AUDIT: Using sizeof(buffer is safe.
-
+
}
-
+
xmp->SetProperty ( xmpNS, xmpProp, buffer );
} catch ( ... ) {
@@ -1761,18 +1741,18 @@ ImportTIFF_GPSTimeStamp ( const TIFF_Manager & tiff, const TIFF_Manager::TagInfo
try { // Don't let errors with one stop the others.
const bool nativeEndian = tiff.IsNativeEndian();
-
+
bool haveDate;
TIFF_Manager::TagInfo dateInfo;
haveDate = tiff.GetTag ( kTIFF_GPSInfoIFD, kTIFF_GPSDateStamp, &dateInfo );
if ( ! haveDate ) haveDate = tiff.GetTag ( kTIFF_ExifIFD, kTIFF_DateTimeOriginal, &dateInfo );
if ( ! haveDate ) haveDate = tiff.GetTag ( kTIFF_ExifIFD, kTIFF_DateTimeDigitized, &dateInfo );
if ( ! haveDate ) return;
-
+
const char * dateStr = (const char *) dateInfo.dataPtr;
if ( (dateStr[4] != ':') || (dateStr[7] != ':') ) return;
if ( (dateStr[10] != 0) && (dateStr[10] != ' ') ) return;
-
+
XMP_Uns32 * binPtr = (XMP_Uns32*)timeInfo.dataPtr;
XMP_Uns32 hourNum = binPtr[0];
XMP_Uns32 hourDenom = binPtr[1];
@@ -1788,8 +1768,8 @@ ImportTIFF_GPSTimeStamp ( const TIFF_Manager & tiff, const TIFF_Manager::TagInfo
secNum = Flip4 ( secNum );
secDenom = Flip4 ( secDenom );
}
-
- double fHour, fMin, fSec, fNano, temp;
+
+ double fHour, fMin, fSec, fNano, temp;
fSec = (double)secNum / (double)secDenom;
temp = (double)minNum / (double)minDenom;
fMin = (double)((XMP_Uns32)temp);
@@ -1798,20 +1778,21 @@ ImportTIFF_GPSTimeStamp ( const TIFF_Manager & tiff, const TIFF_Manager::TagInfo
fHour = (double)((XMP_Uns32)temp);
fSec += (temp - fHour) * 3600.0;
temp = (double)((XMP_Uns32)fSec);
- fNano = (fSec - temp) * (1000.0*1000.0*1000.0);
+ fNano = ((fSec - temp) * (1000.0*1000.0*1000.0)) + 0.5; // Try to avoid n999... problems.
fSec = temp;
-
+
XMP_DateTime binStamp;
- binStamp.tzSign = kXMP_TimeIsUTC;
- binStamp.tzHour = binStamp.tzMinute = 0;
binStamp.year = GatherInt ( dateStr, 4 );
binStamp.month = GatherInt ( dateStr+5, 2 );
binStamp.day = GatherInt ( dateStr+8, 2 );
binStamp.hour = (XMP_Int32)fHour;
binStamp.minute = (XMP_Int32)fMin;
binStamp.second = (XMP_Int32)fSec;
- binStamp.nanoSecond = (XMP_Int32)fNano;
-
+ binStamp.nanoSecond = (XMP_Int32)fNano;
+ binStamp.hasTimeZone = true; // Exif GPS TimeStamp is implicitly UTC.
+ binStamp.tzSign = kXMP_TimeIsUTC;
+ binStamp.tzHour = binStamp.tzMinute = 0;
+
xmp->SetProperty_Date ( xmpNS, xmpProp, binStamp );
} catch ( ... ) {
@@ -1825,76 +1806,99 @@ ImportTIFF_GPSTimeStamp ( const TIFF_Manager & tiff, const TIFF_Manager::TagInfo
// =================================================================================================
// =================================================================================================
-// ReconcileUtils::ImportTIFF
-// ==========================
+// PhotoDataUtils::Import2WayExif
+// ==============================
+//
+// Import the TIFF/Exif tags that have 2 way mappings to XMP, i.e. no correspondence to IPTC.
+// These are always imported for the tiff: and exif: namespaces, but not for others.
void
-ReconcileUtils::ImportTIFF ( const TIFF_Manager & tiff, SXMPMeta * xmp, int digestState, XMP_FileFormat srcFormat )
+PhotoDataUtils::Import2WayExif ( const TIFF_Manager & exif, SXMPMeta * xmp, int iptcDigestState )
{
+ const bool nativeEndian = exif.IsNativeEndian();
+
+ bool found, foundFromXMP;
TIFF_Manager::TagInfo tagInfo;
- bool ok;
- ImportTIFF_StandardMappings ( kTIFF_PrimaryIFD, tiff, xmp, digestState );
+ ImportTIFF_StandardMappings ( kTIFF_PrimaryIFD, exif, xmp );
+ ImportTIFF_StandardMappings ( kTIFF_ExifIFD, exif, xmp );
+ ImportTIFF_StandardMappings ( kTIFF_GPSInfoIFD, exif, xmp );
+
+ // -----------------------------------------------------------------
+ // Fixup erroneous files that have a negative value for GPSAltitude.
- // 306 DateTime is a date master with 37520 SubSecTime and is mapped to xmp:ModifyDate.
- ok = ImportTIFF_VerifyImport ( tiff, xmp, digestState, kTIFF_PrimaryIFD, kTIFF_DateTime,
- kXMP_NS_XMP, "ModifyDate", &tagInfo );
- if ( ok && (tagInfo.type == kTIFF_ASCIIType) && (tagInfo.count == 20) ) {
- ImportTIFF_Date ( tiff, tagInfo, kTIFF_SubSecTime, xmp, kXMP_NS_XMP, "ModifyDate" );
- }
+ found = exif.GetTag ( kTIFF_GPSInfoIFD, kTIFF_GPSAltitude, &tagInfo );
+ if ( found && (tagInfo.type == kTIFF_RationalType) && (tagInfo.count == 1) ) {
- if ( srcFormat != kXMP_PhotoshopFile ) {
-
- // ! TIFF tags 270, 315, and 33432 are ignored for Photoshop files.
-
- XMP_Assert ( (srcFormat == kXMP_JPEGFile) || (srcFormat == kXMP_TIFFFile) );
-
- // 270 ImageDescription is an ASCII tag and is mapped to dc:description["x-default"].
- // Call ImportTIFF_VerifyImport using the x-default item path, don't delete the whole array.
- ok = ImportTIFF_VerifyImport ( tiff, xmp, digestState, kTIFF_PrimaryIFD, kTIFF_ImageDescription,
- kXMP_NS_DC, "description[?xml:lang='x-default']", &tagInfo );
- if ( ok ) ImportTIFF_LocTextASCII ( tiff, kTIFF_PrimaryIFD, kTIFF_ImageDescription,
- xmp, kXMP_NS_DC, "description" );
-
- // 315 Artist is an ASCII tag and is mapped to dc:creator[*].
- ok = ImportTIFF_VerifyImport ( tiff, xmp, digestState, kTIFF_PrimaryIFD, kTIFF_Artist,
- kXMP_NS_DC, "creator", &tagInfo );
- if ( ok && (tagInfo.type == kTIFF_ASCIIType) ) {
- ImportArrayTIFF_ASCII ( tagInfo, xmp, kXMP_NS_DC, "creator" );
+ XMP_Uns32 num = exif.GetUns32 ( tagInfo.dataPtr );
+ XMP_Uns32 denom = exif.GetUns32 ( (XMP_Uns8*)tagInfo.dataPtr + 4 );
+ bool numNeg = num >> 31;
+ bool denomNeg = denom >> 31;
+
+ if ( (numNeg != denomNeg) || numNeg ) { // Does the GPSAltitude look negative?
+ if ( denomNeg ) {
+ denom = -denom;
+ num = -num;
+ numNeg = num >> 31;
+ }
+ if ( numNeg ) {
+ char buffer [32];
+ num = -num;
+ snprintf ( buffer, sizeof(buffer), "%lu/%lu", (unsigned long) num, (unsigned long) denom ); // AUDIT: Using sizeof(buffer) is safe.
+ xmp->SetProperty ( kXMP_NS_EXIF, "GPSAltitude", buffer );
+ xmp->SetProperty ( kXMP_NS_EXIF, "GPSAltitudeRef", "1" );
+ }
}
- // 33432 Copyright is mapped to dc:rights["x-default"].
- // Call ImportTIFF_VerifyImport using the x-default item path, don't delete the whole array.
- ok = ImportTIFF_VerifyImport ( tiff, xmp, digestState, kTIFF_PrimaryIFD, kTIFF_Copyright,
- kXMP_NS_DC, "rights[?xml:lang='x-default']", &tagInfo );
- if ( ok ) ImportTIFF_LocTextASCII ( tiff, kTIFF_PrimaryIFD, kTIFF_Copyright, xmp, kXMP_NS_DC, "rights" );
-
}
+
+ // ---------------------------------------------------------------
+ // Import DateTimeOriginal and DateTime if the XMP doss not exist.
-} // ReconcileUtils::ImportTIFF;
+ found = exif.GetTag ( kTIFF_ExifIFD, kTIFF_DateTimeOriginal, &tagInfo );
+ foundFromXMP = xmp->DoesPropertyExist ( kXMP_NS_EXIF, "DateTimeOriginal" );
+
+ if ( found && (! foundFromXMP) && (tagInfo.type == kTIFF_ASCIIType) ) {
+ ImportTIFF_Date ( exif, tagInfo, xmp, kXMP_NS_EXIF, "DateTimeOriginal" );
+ }
-// =================================================================================================
-// ReconcileUtils::ImportExif
-// ==========================
+ found = exif.GetTag ( kTIFF_PrimaryIFD, kTIFF_DateTime, &tagInfo );
+ foundFromXMP = xmp->DoesPropertyExist ( kXMP_NS_XMP, "ModifyDate" );
+
+ if ( found && (! foundFromXMP) && (tagInfo.type == kTIFF_ASCIIType) ) {
+ ImportTIFF_Date ( exif, tagInfo, xmp, kXMP_NS_XMP, "ModifyDate" );
+ }
-void
-ReconcileUtils::ImportExif ( const TIFF_Manager & tiff, SXMPMeta * xmp, int digestState )
-{
- const bool nativeEndian = tiff.IsNativeEndian();
+ // ----------------------------------------------------
+ // Import the Exif IFD tags that have special mappings.
+
+ // 34855 ISOSpeedRatings has special cases for ISO over 65535. The tag is SHORT, some cameras
+ // omit the tag and some write 65535, all put the real ISO in MakerNote - which ACR might
+ // extract and leave in the XMP. There are 3 import cases:
+ // 1. No native tag: Leave existing XMP.
+ // 2. All of the native values are under 65535: Clear any XMP and import.
+ // 3. One or more of the native values are 65535: Leave existing XMP, else import.
+
+ found = exif.GetTag ( kTIFF_ExifIFD, kTIFF_ISOSpeedRatings, &tagInfo );
+ if ( found && (tagInfo.type == kTIFF_ShortType) && (tagInfo.count > 0) ) {
- TIFF_Manager::TagInfo tagInfo;
- bool ok;
+ bool keepXMP = false;
+ XMP_Uns16 * itemPtr = (XMP_Uns16*) tagInfo.dataPtr;
+ for ( XMP_Uns32 i = 0; i < tagInfo.count; ++i, ++itemPtr ) {
+ if ( *itemPtr == 0xFFFF ) { keepXMP = true; break; } // ! Don't care about BE or LF, same either way.
+ }
- ImportTIFF_StandardMappings ( kTIFF_ExifIFD, tiff, xmp, digestState );
- ImportTIFF_StandardMappings ( kTIFF_GPSInfoIFD, tiff, xmp, digestState );
+ if ( ! keepXMP ) xmp->DeleteProperty ( kXMP_NS_EXIF, "ISOSpeedRatings" );
+
+ if ( ! xmp->DoesPropertyExist ( kXMP_NS_EXIF, "ISOSpeedRatings" ) ) {
+ ImportArrayTIFF ( tagInfo, exif.IsNativeEndian(), xmp, kXMP_NS_EXIF, "ISOSpeedRatings" );
+ }
- // ------------------------------------------------------
- // Here are the Exif IFD tags that have special mappings:
+ }
// 36864 ExifVersion is 4 "undefined" ASCII characters.
- ok = ImportTIFF_VerifyImport ( tiff, xmp, digestState, kTIFF_ExifIFD, kTIFF_ExifVersion,
- kXMP_NS_EXIF, "ExifVersion", &tagInfo );
- if ( ok && (tagInfo.type == kTIFF_UndefinedType) && (tagInfo.count == 4) ) {
+ 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);
str[4] = 0;
@@ -1902,9 +1906,8 @@ ReconcileUtils::ImportExif ( const TIFF_Manager & tiff, SXMPMeta * xmp, int dige
}
// 40960 FlashpixVersion is 4 "undefined" ASCII characters.
- ok = ImportTIFF_VerifyImport ( tiff, xmp, digestState, kTIFF_ExifIFD, kTIFF_FlashpixVersion,
- kXMP_NS_EXIF, "FlashpixVersion", &tagInfo );
- if ( ok && (tagInfo.type == kTIFF_UndefinedType) && (tagInfo.count == 4) ) {
+ 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);
str[4] = 0;
@@ -1912,94 +1915,65 @@ ReconcileUtils::ImportExif ( const TIFF_Manager & tiff, SXMPMeta * xmp, int dige
}
// 37121 ComponentsConfiguration is an array of 4 "undefined" UInt8 bytes.
- ok = ImportTIFF_VerifyImport ( tiff, xmp, digestState, kTIFF_ExifIFD, kTIFF_ComponentsConfiguration,
- kXMP_NS_EXIF, "ComponentsConfiguration", &tagInfo );
- if ( ok && (tagInfo.type == kTIFF_UndefinedType) && (tagInfo.count == 4) ) {
+ found = exif.GetTag ( kTIFF_ExifIFD, kTIFF_ComponentsConfiguration, &tagInfo );
+ if ( found && (tagInfo.type == kTIFF_UndefinedType) && (tagInfo.count == 4) ) {
ImportArrayTIFF_Byte ( tagInfo, xmp, kXMP_NS_EXIF, "ComponentsConfiguration" );
}
// 37510 UserComment is a string with explicit encoding.
- ok = ImportTIFF_VerifyImport ( tiff, xmp, digestState, kTIFF_ExifIFD, kTIFF_UserComment,
- kXMP_NS_EXIF, "UserComment", &tagInfo );
- if ( ok ) {
- ImportTIFF_EncodedString ( tiff, tagInfo, xmp, kXMP_NS_EXIF, "UserComment", true /* isLangAlt */ );
- }
-
- // 36867 DateTimeOriginal is a date master with 37521 SubSecTimeOriginal.
- ok = ImportTIFF_VerifyImport ( tiff, xmp, digestState, kTIFF_ExifIFD, kTIFF_DateTimeOriginal,
- kXMP_NS_EXIF, "DateTimeOriginal", &tagInfo );
- if ( ok && (tagInfo.type == kTIFF_ASCIIType) && (tagInfo.count == 20) ) {
- ImportTIFF_Date ( tiff, tagInfo, kTIFF_SubSecTimeOriginal, xmp, kXMP_NS_EXIF, "DateTimeOriginal" );
- }
- if ( ! xmp->DoesPropertyExist ( kXMP_NS_XMP, "CreateDate" ) ) {
- std::string exifDate;
- ok = xmp->GetProperty ( kXMP_NS_EXIF, "DateTimeOriginal", &exifDate, 0 );
- if ( ok ) xmp->SetProperty ( kXMP_NS_XMP, "CreateDate", exifDate.c_str() );
- }
-
- // 36868 DateTimeDigitized is a date master with 37522 SubSecTimeDigitized.
- ok = ImportTIFF_VerifyImport ( tiff, xmp, digestState, kTIFF_ExifIFD, kTIFF_DateTimeDigitized,
- kXMP_NS_EXIF, "DateTimeDigitized", &tagInfo );
- if ( ok && (tagInfo.type == kTIFF_ASCIIType) && (tagInfo.count == 20) ) {
- ImportTIFF_Date ( tiff, tagInfo, kTIFF_SubSecTimeDigitized, xmp, kXMP_NS_EXIF, "DateTimeDigitized" );
+ found = exif.GetTag ( kTIFF_ExifIFD, kTIFF_UserComment, &tagInfo );
+ if ( found ) {
+ ImportTIFF_EncodedString ( exif, tagInfo, xmp, kXMP_NS_EXIF, "UserComment", true /* isLangAlt */ );
}
// 34856 OECF is an OECF/SFR table.
- ok = ImportTIFF_VerifyImport ( tiff, xmp, digestState, kTIFF_ExifIFD, kTIFF_OECF,
- kXMP_NS_EXIF, "OECF", &tagInfo );
- if ( ok ) {
+ found = exif.GetTag ( kTIFF_ExifIFD, kTIFF_OECF, &tagInfo );
+ if ( found ) {
ImportTIFF_OECFTable ( tagInfo, nativeEndian, xmp, kXMP_NS_EXIF, "OECF" );
}
// 37385 Flash is a UInt16 collection of bit fields and is mapped to a struct in XMP.
- ok = ImportTIFF_VerifyImport ( tiff, xmp, digestState, kTIFF_ExifIFD, kTIFF_Flash,
- kXMP_NS_EXIF, "Flash", &tagInfo );
- if ( ok && (tagInfo.type == kTIFF_ShortType) && (tagInfo.count == 1) ) {
+ found = exif.GetTag ( kTIFF_ExifIFD, kTIFF_Flash, &tagInfo );
+ if ( found && (tagInfo.type == kTIFF_ShortType) && (tagInfo.count == 1) ) {
ImportTIFF_Flash ( tagInfo, nativeEndian, xmp, kXMP_NS_EXIF, "Flash" );
}
// 41484 SpatialFrequencyResponse is an OECF/SFR table.
- ok = ImportTIFF_VerifyImport ( tiff, xmp, digestState, kTIFF_ExifIFD, kTIFF_SpatialFrequencyResponse,
- kXMP_NS_EXIF, "SpatialFrequencyResponse", &tagInfo );
- if ( ok ) {
+ found = exif.GetTag ( kTIFF_ExifIFD, kTIFF_SpatialFrequencyResponse, &tagInfo );
+ if ( found ) {
ImportTIFF_SFRTable ( tagInfo, nativeEndian, xmp, kXMP_NS_EXIF, "SpatialFrequencyResponse" );
}
// 41728 FileSource is an "undefined" UInt8.
- ok = ImportTIFF_VerifyImport ( tiff, xmp, digestState, kTIFF_ExifIFD, kTIFF_FileSource,
- kXMP_NS_EXIF, "FileSource", &tagInfo );
- if ( ok && (tagInfo.type == kTIFF_UndefinedType) && (tagInfo.count == 1) ) {
+ found = exif.GetTag ( kTIFF_ExifIFD, kTIFF_FileSource, &tagInfo );
+ if ( found && (tagInfo.type == kTIFF_UndefinedType) && (tagInfo.count == 1) ) {
ImportSingleTIFF_Byte ( tagInfo, xmp, kXMP_NS_EXIF, "FileSource" );
}
// 41729 SceneType is an "undefined" UInt8.
- ok = ImportTIFF_VerifyImport ( tiff, xmp, digestState, kTIFF_ExifIFD, kTIFF_SceneType,
- kXMP_NS_EXIF, "SceneType", &tagInfo );
- if ( ok && (tagInfo.type == kTIFF_UndefinedType) && (tagInfo.count == 1) ) {
+ found = exif.GetTag ( kTIFF_ExifIFD, kTIFF_SceneType, &tagInfo );
+ if ( found && (tagInfo.type == kTIFF_UndefinedType) && (tagInfo.count == 1) ) {
ImportSingleTIFF_Byte ( tagInfo, xmp, kXMP_NS_EXIF, "SceneType" );
}
// 41730 CFAPattern is a custom table.
- ok = ImportTIFF_VerifyImport ( tiff, xmp, digestState, kTIFF_ExifIFD, kTIFF_CFAPattern,
- kXMP_NS_EXIF, "CFAPattern", &tagInfo );
- if ( ok ) {
+ found = exif.GetTag ( kTIFF_ExifIFD, kTIFF_CFAPattern, &tagInfo );
+ if ( found ) {
ImportTIFF_CFATable ( tagInfo, nativeEndian, xmp, kXMP_NS_EXIF, "CFAPattern" );
}
// 41995 DeviceSettingDescription is a custom table.
- ok = ImportTIFF_VerifyImport ( tiff, xmp, digestState, kTIFF_ExifIFD, kTIFF_DeviceSettingDescription,
- kXMP_NS_EXIF, "DeviceSettingDescription", &tagInfo );
- if ( ok ) {
- ImportTIFF_DSDTable ( tiff, tagInfo, xmp, kXMP_NS_EXIF, "DeviceSettingDescription" );
+ found = exif.GetTag ( kTIFF_ExifIFD, kTIFF_DeviceSettingDescription, &tagInfo );
+ if ( found ) {
+ ImportTIFF_DSDTable ( exif, tagInfo, xmp, kXMP_NS_EXIF, "DeviceSettingDescription" );
}
- // ----------------------------------------------------------
- // Here are the GPS Info IFD tags that have special mappings:
+ // --------------------------------------------------------
+ // Import the GPS Info IFD tags that have special mappings.
// 0 GPSVersionID is 4 UInt8 bytes and mapped as "n.n.n.n".
- ok = ImportTIFF_VerifyImport ( tiff, xmp, digestState, kTIFF_GPSInfoIFD, kTIFF_GPSVersionID,
- kXMP_NS_EXIF, "GPSVersionID", &tagInfo );
- if ( ok && (tagInfo.type == kTIFF_ByteType) && (tagInfo.count == 4) ) {
+ found = exif.GetTag ( kTIFF_GPSInfoIFD, kTIFF_GPSVersionID, &tagInfo );
+ if ( found && (tagInfo.type == kTIFF_ByteType) && (tagInfo.count == 4) ) {
const char * strIn = (const char *) tagInfo.dataPtr;
char strOut[8];
strOut[0] = strIn[0];
@@ -2012,213 +1986,418 @@ ReconcileUtils::ImportExif ( const TIFF_Manager & tiff, SXMPMeta * xmp, int dige
}
// 2 GPSLatitude is a GPS coordinate master.
- ok = ImportTIFF_VerifyImport ( tiff, xmp, digestState, kTIFF_GPSInfoIFD, kTIFF_GPSLatitude,
- kXMP_NS_EXIF, "GPSLatitude", &tagInfo );
- if ( ok ) {
- ImportTIFF_GPSCoordinate ( tiff, tagInfo, xmp, kXMP_NS_EXIF, "GPSLatitude" );
+ found = exif.GetTag ( kTIFF_GPSInfoIFD, kTIFF_GPSLatitude, &tagInfo );
+ if ( found ) {
+ ImportTIFF_GPSCoordinate ( exif, tagInfo, xmp, kXMP_NS_EXIF, "GPSLatitude" );
}
// 4 GPSLongitude is a GPS coordinate master.
- ok = ImportTIFF_VerifyImport ( tiff, xmp, digestState, kTIFF_GPSInfoIFD, kTIFF_GPSLongitude,
- kXMP_NS_EXIF, "GPSLongitude", &tagInfo );
- if ( ok ) {
- ImportTIFF_GPSCoordinate ( tiff, tagInfo, xmp, kXMP_NS_EXIF, "GPSLongitude" );
+ found = exif.GetTag ( kTIFF_GPSInfoIFD, kTIFF_GPSLongitude, &tagInfo );
+ if ( found ) {
+ ImportTIFF_GPSCoordinate ( exif, tagInfo, xmp, kXMP_NS_EXIF, "GPSLongitude" );
}
// 7 GPSTimeStamp is a UTC time as 3 rationals, mated with the optional GPSDateStamp.
- ok = ImportTIFF_VerifyImport ( tiff, xmp, digestState, kTIFF_GPSInfoIFD, kTIFF_GPSTimeStamp,
- kXMP_NS_EXIF, "GPSTimeStamp", &tagInfo );
- if ( ok && (tagInfo.type == kTIFF_RationalType) && (tagInfo.count == 3) ) {
- ImportTIFF_GPSTimeStamp ( tiff, tagInfo, xmp, kXMP_NS_EXIF, "GPSTimeStamp" );
+ found = exif.GetTag ( kTIFF_GPSInfoIFD, kTIFF_GPSTimeStamp, &tagInfo );
+ if ( found && (tagInfo.type == kTIFF_RationalType) && (tagInfo.count == 3) ) {
+ ImportTIFF_GPSTimeStamp ( exif, tagInfo, xmp, kXMP_NS_EXIF, "GPSTimeStamp" );
}
// 20 GPSDestLatitude is a GPS coordinate master.
- ok = ImportTIFF_VerifyImport ( tiff, xmp, digestState, kTIFF_GPSInfoIFD, kTIFF_GPSDestLatitude,
- kXMP_NS_EXIF, "GPSDestLatitude", &tagInfo );
- if ( ok ) {
- ImportTIFF_GPSCoordinate ( tiff, tagInfo, xmp, kXMP_NS_EXIF, "GPSDestLatitude" );
+ found = exif.GetTag ( kTIFF_GPSInfoIFD, kTIFF_GPSDestLatitude, &tagInfo );
+ if ( found ) {
+ ImportTIFF_GPSCoordinate ( exif, tagInfo, xmp, kXMP_NS_EXIF, "GPSDestLatitude" );
}
// 22 GPSDestLongitude is a GPS coordinate master.
- ok = ImportTIFF_VerifyImport ( tiff, xmp, digestState, kTIFF_GPSInfoIFD, kTIFF_GPSDestLongitude,
- kXMP_NS_EXIF, "GPSDestLongitude", &tagInfo );
- if ( ok ) {
- ImportTIFF_GPSCoordinate ( tiff, tagInfo, xmp, kXMP_NS_EXIF, "GPSDestLongitude" );
+ found = exif.GetTag ( kTIFF_GPSInfoIFD, kTIFF_GPSDestLongitude, &tagInfo );
+ if ( found ) {
+ ImportTIFF_GPSCoordinate ( exif, tagInfo, xmp, kXMP_NS_EXIF, "GPSDestLongitude" );
}
// 27 GPSProcessingMethod is a string with explicit encoding.
- ok = ImportTIFF_VerifyImport ( tiff, xmp, digestState, kTIFF_GPSInfoIFD, kTIFF_GPSProcessingMethod,
- kXMP_NS_EXIF, "GPSProcessingMethod", &tagInfo );
- if ( ok ) {
- ImportTIFF_EncodedString ( tiff, tagInfo, xmp, kXMP_NS_EXIF, "GPSProcessingMethod" );
+ found = exif.GetTag ( kTIFF_GPSInfoIFD, kTIFF_GPSProcessingMethod, &tagInfo );
+ if ( found ) {
+ ImportTIFF_EncodedString ( exif, tagInfo, xmp, kXMP_NS_EXIF, "GPSProcessingMethod" );
}
// 28 GPSAreaInformation is a string with explicit encoding.
- ok = ImportTIFF_VerifyImport ( tiff, xmp, digestState, kTIFF_GPSInfoIFD, kTIFF_GPSAreaInformation,
- kXMP_NS_EXIF, "GPSAreaInformation", &tagInfo );
- if ( ok ) {
- ImportTIFF_EncodedString ( tiff, tagInfo, xmp, kXMP_NS_EXIF, "GPSAreaInformation" );
+ found = exif.GetTag ( kTIFF_GPSInfoIFD, kTIFF_GPSAreaInformation, &tagInfo );
+ if ( found ) {
+ ImportTIFF_EncodedString ( exif, tagInfo, xmp, kXMP_NS_EXIF, "GPSAreaInformation" );
}
-} // ReconcileUtils::ImportExif;
+} // PhotoDataUtils::Import2WayExif
// =================================================================================================
-// =================================================================================================
+// Import3WayDateTime
+// ==================
+
+static void Import3WayDateTime ( XMP_Uns16 exifTag, const TIFF_Manager & exif, const IPTC_Manager & iptc,
+ SXMPMeta * xmp, int iptcDigestState, const IPTC_Manager & oldIPTC )
+{
+ XMP_Uns8 iptcDS;
+ XMP_StringPtr xmpNS, xmpProp;
+
+ if ( exifTag == kTIFF_DateTimeOriginal ) {
+ iptcDS = kIPTC_DateCreated;
+ xmpNS = kXMP_NS_Photoshop;
+ xmpProp = "DateCreated";
+ } else if ( exifTag == kTIFF_DateTimeDigitized ) {
+ iptcDS = kIPTC_DigitalCreateDate;
+ xmpNS = kXMP_NS_XMP;
+ xmpProp = "CreateDate";
+ } else {
+ XMP_Throw ( "Unrecognized dateID", kXMPErr_BadParam );
+ }
+
+ size_t iptcCount;
+ bool haveXMP, haveExif, haveIPTC; // ! These are manipulated to simplify MWG-compliant logic.
+ std::string xmpValue, exifValue, iptcValue;
+ TIFF_Manager::TagInfo exifInfo;
+ IPTC_Manager::DataSetInfo iptcInfo;
+
+ // Get the basic info about available values.
+ haveXMP = xmp->GetProperty ( xmpNS, xmpProp, &xmpValue, 0 );
+ iptcCount = PhotoDataUtils::GetNativeInfo ( iptc, iptcDS, iptcDigestState, haveXMP, &iptcInfo );
+ haveIPTC = (iptcCount > 0);
+ XMP_Assert ( (iptcDigestState == kDigestMatches) ? (! haveIPTC) : true );
+ haveExif = (! haveXMP) && (! haveIPTC) && PhotoDataUtils::GetNativeInfo ( exif, kTIFF_ExifIFD, exifTag, &exifInfo );
+ XMP_Assert ( (! (haveExif & haveXMP)) & (! (haveExif & haveIPTC)) );
+
+ if ( haveIPTC ) {
+
+ PhotoDataUtils::ImportIPTC_Date ( iptcDS, iptc, xmp );
+
+ } else if ( haveExif && (exifInfo.type == kTIFF_ASCIIType) ) {
+
+ // Only import the Exif form if the non-TZ information differs from the XMP.
+
+ TIFF_FileWriter exifFromXMP;
+ TIFF_Manager::TagInfo infoFromXMP;
+
+ ExportTIFF_Date ( *xmp, xmpNS, xmpProp, &exifFromXMP, exifTag );
+ bool foundFromXMP = exifFromXMP.GetTag ( kTIFF_ExifIFD, exifTag, &infoFromXMP );
+
+ if ( (! foundFromXMP) || (exifInfo.dataLen != infoFromXMP.dataLen) ||
+ (! XMP_LitNMatch ( (char*)exifInfo.dataPtr, (char*)infoFromXMP.dataPtr, exifInfo.dataLen )) ) {
+ ImportTIFF_Date ( exif, exifInfo, xmp, xmpNS, xmpProp );
+ }
+
+ }
+
+} // Import3WayDateTime
// =================================================================================================
-// ExportSingleTIFF_Short
-// ======================
+// PhotoDataUtils::Import3WayItems
+// ===============================
+//
+// Handle the imports that involve all 3 of Exif, IPTC, and XMP. There are only 4 properties with
+// 3-way mappings, copyright, description, creator, and date/time. Following the MWG guidelines,
+// this general policy is applied separately to each:
+//
+// If the new IPTC digest differs from the stored digest (favor IPTC over Exif and XMP)
+// If the IPTC value differs from the predicted old IPTC value
+// Import the IPTC value, including deleting the XMP
+// Else if the Exif is non-empty and differs from the XMP
+// Import the Exif value (does not delete existing XMP)
+// Else if the stored IPTC digest is missing (favor Exif over IPTC, or IPTC over missing XMP)
+// If the Exif is non-empty and differs from the XMP
+// Import the Exif value (does not delete existing XMP)
+// Else if the XMP is missing and the Exif is missing or empty
+// Import the IPTC value
+// Else (the new IPTC digest matches the stored digest - ignore the IPTC)
+// If the Exif is non-empty and differs from the XMP
+// Import the Exif value (does not delete existing XMP)
+//
+// Note that missing or empty Exif will never cause existing XMP to be deleted. This is a pragmatic
+// choice to improve compatibility with pre-MWG software. There are few Exif-only editors for these
+// 3-way properties, there are important existing IPTC-only editors.
-static void
-ExportSingleTIFF_Short ( const SXMPMeta & xmp, const char * xmpNS, const char * xmpProp,
- TIFF_Manager * tiff, XMP_Uns8 ifd, XMP_Uns16 id )
+// -------------------------------------------------------------------------------------------------
+
+void PhotoDataUtils::Import3WayItems ( const TIFF_Manager & exif, const IPTC_Manager & iptc, SXMPMeta * xmp, int iptcDigestState )
{
- try { // Don't let errors with one stop the others.
+ size_t iptcCount;
- long xmpValue;
+ bool haveXMP, haveExif, haveIPTC; // ! These are manipulated to simplify MWG-compliant logic.
+ std::string xmpValue, exifValue, iptcValue;
- bool foundXMP = xmp.GetProperty_Int ( xmpNS, xmpProp, &xmpValue, 0 );
- if ( ! foundXMP ) {
- tiff->DeleteTag ( ifd, id );
- return;
- }
+ TIFF_Manager::TagInfo exifInfo;
+ IPTC_Manager::DataSetInfo iptcInfo;
- if ( (xmpValue < 0) || (xmpValue > 0xFFFF) ) return; // ? Complain? Peg to limit? Delete the tag?
+ IPTC_Writer oldIPTC;
+ if ( iptcDigestState == kDigestDiffers ) {
+ PhotoDataUtils::ExportIPTC ( *xmp, &oldIPTC ); // Predict old IPTC DataSets based on the existing XMP.
+ }
+
+ // ---------------------------------------------------------------------------------
+ // Process the copyright. Replace internal nuls in the Exif to "merge" the portions.
+
+ // Get the basic info about available values.
+ haveXMP = xmp->GetLocalizedText ( kXMP_NS_DC, "rights", "", "x-default", 0, &xmpValue, 0 );
+ iptcCount = PhotoDataUtils::GetNativeInfo ( iptc, kIPTC_CopyrightNotice, iptcDigestState, haveXMP, &iptcInfo );
+ haveIPTC = (iptcCount > 0);
+ XMP_Assert ( (iptcDigestState == kDigestMatches) ? (! haveIPTC) : true );
+ haveExif = (! haveXMP) && (! haveIPTC) && PhotoDataUtils::GetNativeInfo ( exif, kTIFF_PrimaryIFD, kTIFF_Copyright, &exifInfo );
+ XMP_Assert ( (! (haveExif & haveXMP)) & (! (haveExif & haveIPTC)) );
- tiff->SetTag_Short ( ifd, id, (XMP_Uns16)xmpValue );
+ if ( haveExif && (exifInfo.dataLen > 1) ) { // Replace internal nul characters with linefeed.
+ for ( XMP_Uns32 i = 0; i < exifInfo.dataLen-1; ++i ) {
+ if ( ((char*)exifInfo.dataPtr)[i] == 0 ) ((char*)exifInfo.dataPtr)[i] = 0x0A;
+ }
+ }
+
+ if ( haveIPTC ) {
+ PhotoDataUtils::ImportIPTC_LangAlt ( iptc, xmp, kIPTC_CopyrightNotice, kXMP_NS_DC, "rights" );
+ } else if ( haveExif && PhotoDataUtils::IsValueDifferent ( exifInfo, xmpValue, &exifValue ) ) {
+ xmp->SetLocalizedText ( kXMP_NS_DC, "rights", "", "x-default", exifValue.c_str() );
+ }
+
+ // ------------------------
+ // Process the description.
+
+ // Get the basic info about available values.
+ haveXMP = xmp->GetLocalizedText ( kXMP_NS_DC, "description", "", "x-default", 0, &xmpValue, 0 );
+ iptcCount = PhotoDataUtils::GetNativeInfo ( iptc, kIPTC_Description, iptcDigestState, haveXMP, &iptcInfo );
+ haveIPTC = (iptcCount > 0);
+ XMP_Assert ( (iptcDigestState == kDigestMatches) ? (! haveIPTC) : true );
+ haveExif = (! haveXMP) && (! haveIPTC) && PhotoDataUtils::GetNativeInfo ( exif, kTIFF_PrimaryIFD, kTIFF_ImageDescription, &exifInfo );
+ XMP_Assert ( (! (haveExif & haveXMP)) & (! (haveExif & haveIPTC)) );
+
+ if ( haveIPTC ) {
+ PhotoDataUtils::ImportIPTC_LangAlt ( iptc, xmp, kIPTC_Description, kXMP_NS_DC, "description" );
+ } else if ( haveExif && PhotoDataUtils::IsValueDifferent ( exifInfo, xmpValue, &exifValue ) ) {
+ xmp->SetLocalizedText ( kXMP_NS_DC, "description", "", "x-default", exifValue.c_str() );
+ }
- } catch ( ... ) {
- // Do nothing, let other exports proceed.
- // ? Notify client?
+ // -------------------------------------------------------------------------------------------
+ // Process the creator. The XMP and IPTC are arrays, the Exif is a semicolon separated string.
+
+ // Get the basic info about available values.
+ haveXMP = xmp->DoesPropertyExist ( kXMP_NS_DC, "creator" );
+ haveExif = PhotoDataUtils::GetNativeInfo ( exif, kTIFF_PrimaryIFD, kTIFF_Artist, &exifInfo );
+ iptcCount = PhotoDataUtils::GetNativeInfo ( iptc, kIPTC_Creator, iptcDigestState, haveXMP, &iptcInfo );
+ haveIPTC = (iptcCount > 0);
+ XMP_Assert ( (iptcDigestState == kDigestMatches) ? (! haveIPTC) : true );
+ haveExif = (! haveXMP) && (! haveIPTC) && PhotoDataUtils::GetNativeInfo ( exif, kTIFF_PrimaryIFD, kTIFF_Artist, &exifInfo );
+ XMP_Assert ( (! (haveExif & haveXMP)) & (! (haveExif & haveIPTC)) );
+
+ if ( haveIPTC ) {
+ PhotoDataUtils::ImportIPTC_Array ( iptc, xmp, kIPTC_Creator, kXMP_NS_DC, "creator" );
+ } else if ( haveExif && PhotoDataUtils::IsValueDifferent ( exifInfo, xmpValue, &exifValue ) ) {
+ SXMPUtils::SeparateArrayItems ( xmp, kXMP_NS_DC, "creator", kXMP_PropArrayIsOrdered, exifValue );
}
-} // ExportSingleTIFF_Short
+ // ------------------------------------------------------------------------------
+ // Process DateTimeDigitized; DateTimeOriginal and DateTime are 2-way.
+ // *** Exif DateTimeOriginal <-> XMP exif:DateTimeOriginal
+ // *** IPTC DateCreated <-> XMP photoshop:DateCreated
+ // *** Exif DateTimeDigitized <-> IPTC DigitalCreateDate <-> XMP xmp:CreateDate
+ // *** TIFF DateTime <-> XMP xmp:ModifyDate
+
+ Import3WayDateTime ( kTIFF_DateTimeDigitized, exif, iptc, xmp, iptcDigestState, oldIPTC );
+
+} // PhotoDataUtils::Import3WayItems
// =================================================================================================
-// ExportSingleTIFF_Rational
-// =========================
+// =================================================================================================
+
+// =================================================================================================
+// ExportSingleTIFF
+// ================
//
-// An XMP (unsigned) rational is supposed to be written as a string "num/denom".
+// This is only called when the XMP exists and will be exported. And only for standard mappings.
+
+// ! Only implemented for the types known to be needed.
static void
-ExportSingleTIFF_Rational ( const SXMPMeta & xmp, const char * xmpNS, const char * xmpProp,
- TIFF_Manager * tiff, XMP_Uns8 ifd, XMP_Uns16 id )
+ExportSingleTIFF ( TIFF_Manager * tiff, XMP_Uns8 ifd, const TIFF_MappingToXMP & mapInfo,
+ bool nativeEndian, const std::string & xmpValue )
{
- try { // Don't let errors with one stop the others.
+ XMP_Assert ( (mapInfo.count == 1) || (mapInfo.type == kTIFF_ASCIIType) );
+ XMP_Assert ( mapInfo.name[0] != 0 ); // Must be a standard mapping.
+
+ char nextChar; // Used to make sure sscanf consumes all of the string.
+
+ switch ( mapInfo.type ) {
- std::string strValue;
- XMP_OptionBits xmpFlags;
+ case kTIFF_ByteType : {
+ unsigned short binValue;
+ int items = sscanf ( xmpValue.c_str(), "%hu%c", &binValue, &nextChar ); // AUDIT: Using xmpValue.c_str() is safe.
+ if ( items != 1 ) return; // ? complain? notify client?
+ tiff->SetTag_Byte ( ifd, mapInfo.id, (XMP_Uns8)binValue );
+ break;
+ }
- bool foundXMP = xmp.GetProperty ( xmpNS, xmpProp, &strValue, &xmpFlags );
- if ( ! foundXMP ) {
- tiff->DeleteTag ( ifd, id );
- return;
+ case kTIFF_ShortType : {
+ unsigned long binValue;
+ int items = sscanf ( xmpValue.c_str(), "%lu%c", &binValue, &nextChar ); // AUDIT: Using xmpValue.c_str() is safe.
+ if ( items != 1 ) return; // ? complain? notify client?
+ tiff->SetTag_Short ( ifd, mapInfo.id, (XMP_Uns16)binValue );
+ break;
}
-
- if ( ! XMP_PropIsSimple ( xmpFlags ) ) return; // ? Complain? Delete the tag?
-
- XMP_Uns32 newNum, newDenom;
- const char* partPtr;
- size_t partLen;
-
- partPtr = strValue.c_str();
- for ( partLen = 0; partPtr[partLen] != 0; ++partLen ) {
- if ( (partPtr[partLen] < '0') || (partPtr[partLen] > '9') ) break;
+
+ case kTIFF_ShortOrLongType : {
+ unsigned long binValue;
+ int items = sscanf ( xmpValue.c_str(), "%lu%c", &binValue, &nextChar ); // AUDIT: Using xmpValue.c_str() is safe.
+ if ( items != 1 ) return; // ? complain? notify client?
+ if ( binValue <= 0xFFFF ) {
+ tiff->SetTag_Short ( ifd, mapInfo.id, (XMP_Uns16)binValue );
+ } else {
+ tiff->SetTag_Long ( ifd, mapInfo.id, (XMP_Uns32)binValue );
+ }
+ break;
}
- if ( partLen == 0 ) return; // ? Complain? Delete the tag?
- newNum = GatherInt ( partPtr, partLen );
-
- if ( partPtr[partLen] == 0 ) {
- newDenom = 1; // Tolerate bad XMP that just has the numerator.
- } else if ( partPtr[partLen] != '/' ) {
- return; // ? Complain? Delete the tag?
- } else {
- partPtr += partLen+1;
- for ( partLen = 0; partPtr[partLen] != 0; ++partLen ) {
- if ( (partPtr[partLen] < '0') || (partPtr[partLen] > '9') ) break;
+
+ case kTIFF_RationalType : { // The XMP is formatted as "num/denom".
+ unsigned long num, denom;
+ int items = sscanf ( xmpValue.c_str(), "%lu/%lu%c", &num, &denom, &nextChar ); // AUDIT: Using xmpValue.c_str() is safe.
+ if ( items != 2 ) {
+ if ( items != 1 ) return; // ? complain? notify client?
+ denom = 1; // The XMP was just an integer, assume a denominator of 1.
+ }
+ tiff->SetTag_Rational ( ifd, mapInfo.id, (XMP_Uns32)num, (XMP_Uns32)denom );
+ break;
+ }
+
+ case kTIFF_SRationalType : { // The XMP is formatted as "num/denom".
+ signed long num, denom;
+ int items = sscanf ( xmpValue.c_str(), "%ld/%ld%c", &num, &denom, &nextChar ); // AUDIT: Using xmpValue.c_str() is safe.
+ if ( items != 2 ) {
+ if ( items != 1 ) return; // ? complain? notify client?
+ denom = 1; // The XMP was just an integer, assume a denominator of 1.
}
- if ( (partLen == 0) || (partPtr[partLen] != 0) ) return; // ? Complain? Delete the tag?
- newDenom = GatherInt ( partPtr, partLen );
+ tiff->SetTag_SRational ( ifd, mapInfo.id, (XMP_Int32)num, (XMP_Int32)denom );
+ break;
}
+
+ case kTIFF_ASCIIType :
+ tiff->SetTag ( ifd, mapInfo.id, kTIFF_ASCIIType, (XMP_Uns32)(xmpValue.size()+1), xmpValue.c_str() );
+ break;
- tiff->SetTag_Rational ( ifd, id, newNum, newDenom );
+ default:
+ XMP_Assert ( false ); // Force a debug assert for unexpected types.
- } catch ( ... ) {
- // Do nothing, let other exports proceed.
- // ? Notify client?
}
-
-} // ExportSingleTIFF_Rational
+
+} // ExportSingleTIFF
// =================================================================================================
-// ExportSingleTIFF_ASCII
-// ======================
+// ExportArrayTIFF
+// ================
+//
+// This is only called when the XMP exists and will be exported. And only for standard mappings.
+
+// ! Only implemented for the types known to be needed.
static void
-ExportSingleTIFF_ASCII ( const SXMPMeta & xmp, const char * xmpNS, const char * xmpProp,
- TIFF_Manager * tiff, XMP_Uns8 ifd, XMP_Uns16 id )
+ExportArrayTIFF ( TIFF_Manager * tiff, XMP_Uns8 ifd, const TIFF_MappingToXMP & mapInfo, bool nativeEndian,
+ const SXMPMeta & xmp, const char * xmpNS, const char * xmpArray )
{
- try { // Don't let errors with one stop the others.
-
- std::string xmpValue;
- XMP_OptionBits xmpFlags;
-
- bool foundXMP = xmp.GetProperty ( xmpNS, xmpProp, &xmpValue, &xmpFlags );
- if ( ! foundXMP ) {
- tiff->DeleteTag ( ifd, id );
- return;
- }
-
- if ( ! XMP_PropIsSimple ( xmpFlags ) ) return; // ? Complain? Delete the tag?
-
- tiff->SetTag ( ifd, id, kTIFF_ASCIIType, (XMP_Uns32)( xmpValue.size()+1 ), xmpValue.c_str() );
-
- } catch ( ... ) {
- // Do nothing, let other exports proceed.
- // ? Notify client?
+ XMP_Assert ( (mapInfo.count != 1) && (mapInfo.type != kTIFF_ASCIIType) );
+ XMP_Assert ( mapInfo.name[0] != 0 ); // Must be a standard mapping.
+ XMP_Assert ( mapInfo.type == kTIFF_ShortType ); // ! So far ISOSpeedRatings is the only standard array export.
+ XMP_Assert ( xmp.DoesPropertyExist ( xmpNS, xmpArray ) );
+
+ if ( mapInfo.type != kTIFF_ShortType ) return; // ! So far ISOSpeedRatings is the only standard array export.
+
+ size_t arraySize = xmp.CountArrayItems ( xmpNS, xmpArray );
+ if ( arraySize == 0 ) {
+ tiff->DeleteTag ( ifd, mapInfo.id );
+ return;
}
-} // ExportSingleTIFF_ASCII
+ std::vector<XMP_Uns16> vecValue;
+ vecValue.assign ( arraySize, 0 );
+ XMP_Uns16 * binPtr = (XMP_Uns16*) &vecValue[0];
+
+ std::string itemPath;
+ XMP_Int32 int32;
+ XMP_Uns16 uns16;
+ for ( size_t i = 1; i <= arraySize; ++i, ++binPtr ) {
+ SXMPUtils::ComposeArrayItemPath ( xmpNS, xmpArray, (XMP_Index)i, &itemPath );
+ xmp.GetProperty_Int ( xmpNS, itemPath.c_str(), &int32, 0 );
+ uns16 = (XMP_Uns16)int32;
+ if ( ! nativeEndian ) uns16 = Flip2 ( uns16 );
+ *binPtr = uns16;
+ }
+
+ tiff->SetTag ( ifd, mapInfo.id, kTIFF_ShortType, (XMP_Uns32)arraySize, &vecValue[0] );
+
+} // ExportArrayTIFF
// =================================================================================================
-// ExportArrayTIFF_ASCII
-// =====================
-//
-// Catenate all of the XMP array values into a string with separating nul characters.
+// ExportTIFF_StandardMappings
+// ===========================
static void
-ExportArrayTIFF_ASCII ( const SXMPMeta & xmp, const char * xmpNS, const char * xmpProp,
- TIFF_Manager * tiff, XMP_Uns8 ifd, XMP_Uns16 id )
+ExportTIFF_StandardMappings ( XMP_Uns8 ifd, TIFF_Manager * tiff, const SXMPMeta & xmp )
{
- try { // Don't let errors with one stop the others.
+ const bool nativeEndian = tiff->IsNativeEndian();
+ TIFF_Manager::TagInfo tagInfo;
+ std::string xmpValue;
+ XMP_OptionBits xmpForm;
- std::string itemValue, fullValue;
- XMP_OptionBits xmpFlags;
+ const TIFF_MappingToXMP * mappings = 0;
+ const char * xmpNS = 0;
- bool foundXMP = xmp.GetProperty ( xmpNS, xmpProp, 0, &xmpFlags );
- if ( ! foundXMP ) {
- tiff->DeleteTag ( ifd, id );
- return;
- }
+ if ( ifd == kTIFF_PrimaryIFD ) {
+ mappings = sPrimaryIFDMappings;
+ xmpNS = kXMP_NS_TIFF;
+ } else if ( ifd == kTIFF_ExifIFD ) {
+ mappings = sExifIFDMappings;
+ xmpNS = kXMP_NS_EXIF;
+ } else if ( ifd == kTIFF_GPSInfoIFD ) {
+ mappings = sGPSInfoIFDMappings;
+ xmpNS = kXMP_NS_EXIF; // ! Yes, the GPS Info tags are in the exif: namespace.
+ } else {
+ XMP_Throw ( "Invalid IFD for standard mappings", kXMPErr_InternalFailure );
+ }
+
+ for ( size_t i = 0; mappings[i].id != 0xFFFF; ++i ) {
+
+ try { // Don't let errors with one stop the others.
+
+ const TIFF_MappingToXMP & mapInfo = mappings[i];
+
+ if ( mapInfo.exportMode == kExport_Never ) continue;
+ if ( mapInfo.name[0] == 0 ) continue; // Skip special mappings, handled higher up.
+
+ bool haveTIFF = tiff->GetTag ( ifd, mapInfo.id, &tagInfo );
+ if ( haveTIFF && (mapInfo.exportMode == kExport_InjectOnly) ) continue;
+
+ bool haveXMP = xmp.GetProperty ( xmpNS, mapInfo.name, &xmpValue, &xmpForm );
+ if ( ! haveXMP ) {
+
+ if ( haveTIFF && (mapInfo.exportMode == kExport_Always) ) tiff->DeleteTag ( ifd, mapInfo.id );
+
+ } else {
+
+ XMP_Assert ( tagInfo.type != kTIFF_UndefinedType ); // These must have a special mapping.
+ if ( tagInfo.type == kTIFF_UndefinedType ) continue;
+
+ const bool mapSingle = ((mapInfo.count == 1) || (mapInfo.type == kTIFF_ASCIIType));
+ if ( mapSingle ) {
+ if ( ! XMP_PropIsSimple ( xmpForm ) ) continue; // ? Notify client?
+ ExportSingleTIFF ( tiff, ifd, mapInfo, nativeEndian, xmpValue );
+ } else {
+ if ( ! XMP_PropIsArray ( xmpForm ) ) continue; // ? Notify client?
+ ExportArrayTIFF ( tiff, ifd, mapInfo, nativeEndian, xmp, xmpNS, mapInfo.name );
+ }
+
+ }
+
+ } catch ( ... ) {
+
+ // Do nothing, let other imports proceed.
+ // ? Notify client?
- if ( ! XMP_PropIsArray ( xmpFlags ) ) return; // ? Complain? Delete the tag?
-
- size_t count = xmp.CountArrayItems ( xmpNS, xmpProp );
- for ( size_t i = 1; i <= count; ++i ) { // ! XMP arrays are indexed from 1.
- (void) xmp.GetArrayItem ( xmpNS, xmpProp, (XMP_Index)i, &itemValue, &xmpFlags );
- if ( ! XMP_PropIsSimple ( xmpFlags ) ) continue; // ? Complain?
- fullValue.append ( itemValue );
- fullValue.append ( 1, '\x0' );
}
-
- tiff->SetTag ( ifd, id, kTIFF_ASCIIType, (XMP_Uns32)fullValue.size(), fullValue.c_str() ); // ! Already have trailing nul.
- } catch ( ... ) {
- // Do nothing, let other exports proceed.
- // ? Notify client?
}
-
-} // ExportArrayTIFF_ASCII
+
+} // ExportTIFF_StandardMappings
// =================================================================================================
// ExportTIFF_Date
@@ -2231,35 +2410,77 @@ ExportArrayTIFF_ASCII ( const SXMPMeta & xmp, const char * xmpNS, const char * x
// decimal point.
static void
-ExportTIFF_Date ( const SXMPMeta & xmp, const char * xmpNS, const char * xmpProp,
- TIFF_Manager * tiff, XMP_Uns8 mainIFD, XMP_Uns16 mainID, XMP_Uns8 fracIFD, XMP_Uns16 fracID )
+ExportTIFF_Date ( const SXMPMeta & xmp, const char * xmpNS, const char * xmpProp, TIFF_Manager * tiff, XMP_Uns16 mainID )
{
- try { // Don't let errors with one stop the others.
+ XMP_Uns8 mainIFD = kTIFF_ExifIFD;
+ XMP_Uns16 fracID;
+ switch ( mainID ) {
+ case kTIFF_DateTime : mainIFD = kTIFF_PrimaryIFD; fracID = kTIFF_SubSecTime; break;
+ case kTIFF_DateTimeOriginal : fracID = kTIFF_SubSecTimeOriginal; break;
+ case kTIFF_DateTimeDigitized : fracID = kTIFF_SubSecTimeDigitized; break;
+ }
- XMP_DateTime xmpValue;
+ try { // Don't let errors with one stop the others.
- bool foundXMP = xmp.GetProperty_Date ( xmpNS, xmpProp, &xmpValue, 0 );
+ std::string xmpStr;
+ bool foundXMP = xmp.GetProperty ( xmpNS, xmpProp, &xmpStr, 0 );
if ( ! foundXMP ) {
tiff->DeleteTag ( mainIFD, mainID );
- tiff->DeleteTag ( fracIFD, fracID );
+ tiff->DeleteTag ( kTIFF_ExifIFD, fracID ); // ! The subseconds are always in the Exif IFD.
return;
}
+
+ // Format using all of the numbers. Then overwrite blanks for missing fields. The fields
+ // missing from the XMP are detected with length checks: YYYY-MM-DDThh:mm:ss
+ // < 18 - no seconds
+ // < 15 - no minutes
+ // < 12 - no hours
+ // < 9 - no day
+ // < 6 - no month
+ // < 1 - no year
+
+ XMP_DateTime xmpBin;
+ SXMPUtils::ConvertToDate ( xmpStr.c_str(), &xmpBin );
char buffer[24];
- snprintf ( buffer, sizeof(buffer), "%.4d:%.2d:%.2d %.2d:%.2d:%.2d", // AUDIT: Use of sizeof(buffer) is safe.
- xmpValue.year, xmpValue.month, xmpValue.day, xmpValue.hour, xmpValue.minute, xmpValue.second );
-
+ snprintf ( buffer, sizeof(buffer), "%04d:%02d:%02d %02d:%02d:%02d", // AUDIT: Use of sizeof(buffer) is safe.
+ xmpBin.year, xmpBin.month, xmpBin.day, xmpBin.hour, xmpBin.minute, xmpBin.second );
+
+ size_t xmpLen = xmpStr.size();
+ if ( xmpLen < 18 ) {
+ buffer[17] = buffer[18] = ' ';
+ if ( xmpLen < 15 ) {
+ buffer[14] = buffer[15] = ' ';
+ if ( xmpLen < 12 ) {
+ buffer[11] = buffer[12] = ' ';
+ if ( xmpLen < 9 ) {
+ buffer[8] = buffer[9] = ' ';
+ if ( xmpLen < 6 ) {
+ buffer[5] = buffer[6] = ' ';
+ if ( xmpLen < 1 ) {
+ buffer[0] = buffer[1] = buffer[2] = buffer[3] = ' ';
+ }
+ }
+ }
+ }
+ }
+ }
+
tiff->SetTag_ASCII ( mainIFD, mainID, buffer );
+
+ if ( xmpBin.nanoSecond == 0 ) {
+
+ tiff->DeleteTag ( kTIFF_ExifIFD, fracID );
- if ( xmpValue.nanoSecond != 0 ) {
+ } else {
- snprintf ( buffer, sizeof(buffer), "%09d", xmpValue.nanoSecond ); // AUDIT: Use of sizeof(buffer) is safe.
+ snprintf ( buffer, sizeof(buffer), "%09d", xmpBin.nanoSecond ); // AUDIT: Use of sizeof(buffer) is safe.
for ( size_t i = strlen(buffer)-1; i > 0; --i ) {
if ( buffer[i] != '0' ) break;
buffer[i] = 0; // Strip trailing zero digits.
}
- tiff->SetTag_ASCII ( fracIFD, fracID, buffer );
+ tiff->SetTag_ASCII ( kTIFF_ExifIFD, fracID, buffer ); // ! The subseconds are always in the Exif IFD.
}
@@ -2267,10 +2488,55 @@ ExportTIFF_Date ( const SXMPMeta & xmp, const char * xmpNS, const char * xmpProp
// Do nothing, let other exports proceed.
// ? Notify client?
}
-
+
} // ExportTIFF_Date
// =================================================================================================
+// ExportTIFF_ArrayASCII
+// =====================
+//
+// Catenate all of the XMP array values into a string. Use a "; " separator for Artist, nul for others.
+
+static void
+ExportTIFF_ArrayASCII ( const SXMPMeta & xmp, const char * xmpNS, const char * xmpProp,
+ TIFF_Manager * tiff, XMP_Uns8 ifd, XMP_Uns16 id )
+{
+ try { // Don't let errors with one stop the others.
+
+ std::string itemValue, fullValue;
+ XMP_OptionBits xmpFlags;
+
+ bool foundXMP = xmp.GetProperty ( xmpNS, xmpProp, 0, &xmpFlags );
+ if ( ! foundXMP ) {
+ tiff->DeleteTag ( ifd, id );
+ return;
+ }
+
+ if ( ! XMP_PropIsArray ( xmpFlags ) ) return; // ? Complain? Delete the tag?
+
+ if ( id == kTIFF_Artist ) {
+ SXMPUtils::CatenateArrayItems ( xmp, xmpNS, xmpProp, 0, 0, kXMP_PropArrayIsOrdered, &fullValue );
+ fullValue += '\x0'; // ! Need explicit final nul for SetTag below.
+ } else {
+ size_t count = xmp.CountArrayItems ( xmpNS, xmpProp );
+ for ( size_t i = 1; i <= count; ++i ) { // ! XMP arrays are indexed from 1.
+ (void) xmp.GetArrayItem ( xmpNS, xmpProp, (XMP_Index)i, &itemValue, &xmpFlags );
+ if ( ! XMP_PropIsSimple ( xmpFlags ) ) continue; // ? Complain?
+ fullValue.append ( itemValue );
+ fullValue.append ( 1, '\x0' );
+ }
+ }
+
+ tiff->SetTag ( ifd, id, kTIFF_ASCIIType, (XMP_Uns32)fullValue.size(), fullValue.c_str() ); // ! Already have trailing nul.
+
+ } catch ( ... ) {
+ // Do nothing, let other exports proceed.
+ // ? Notify client?
+ }
+
+} // ExportTIFF_ArrayASCII
+
+// =================================================================================================
// ExportTIFF_LocTextASCII
// ======================
@@ -2287,14 +2553,14 @@ ExportTIFF_LocTextASCII ( const SXMPMeta & xmp, const char * xmpNS, const char *
tiff->DeleteTag ( ifd, id );
return;
}
-
+
tiff->SetTag ( ifd, id, kTIFF_ASCIIType, (XMP_Uns32)( xmpValue.size()+1 ), xmpValue.c_str() );
} catch ( ... ) {
// Do nothing, let other exports proceed.
// ? Notify client?
}
-
+
} // ExportTIFF_LocTextASCII
// =================================================================================================
@@ -2323,22 +2589,22 @@ ExportTIFF_EncodedString ( const SXMPMeta & xmp, const char * xmpNS, const char
bool ok = xmp.GetLocalizedText ( xmpNS, xmpProp, "", "x-default", 0, &xmpValue, 0 );
if ( ! ok ) return; // ? Complain? Delete the tag?
}
-
+
XMP_Uns8 encoding = kTIFF_EncodeASCII;
for ( size_t i = 0; i < xmpValue.size(); ++i ) {
- if ( xmpValue[i] >= 0x80 ) {
+ if ( (XMP_Uns8)xmpValue[i] >= 0x80 ) {
encoding = kTIFF_EncodeUnicode;
break;
}
}
-
+
tiff->SetTag_EncodedString ( ifd, id, xmpValue.c_str(), encoding );
} catch ( ... ) {
// Do nothing, let other exports proceed.
// ? Notify client?
}
-
+
} // ExportTIFF_EncodedString
// =================================================================================================
@@ -2358,9 +2624,9 @@ ExportTIFF_GPSCoordinate ( const SXMPMeta & xmp, const char * xmpNS, const char
{
XMP_Uns16 refID = _id-1; // ! The GPS refs and locations are all tag N-1 and N pairs.
XMP_Uns16 locID = _id;
-
+
XMP_Assert ( (locID & 1) == 0 );
-
+
try { // Don't let errors with one stop the others.
std::string xmpValue;
@@ -2372,20 +2638,20 @@ ExportTIFF_GPSCoordinate ( const SXMPMeta & xmp, const char * xmpNS, const char
tiff->DeleteTag ( ifd, locID );
return;
}
-
+
if ( ! XMP_PropIsSimple ( xmpFlags ) ) return;
-
+
const char * chPtr = xmpValue.c_str();
-
+
XMP_Uns32 deg=0, minNum=0, minDenom=1, sec=0;
-
+
for ( ; ('0' <= *chPtr) && (*chPtr <= '9'); ++chPtr ) deg = deg*10 + (*chPtr - '0');
if ( *chPtr != ',' ) return; // Bad XMP string.
++chPtr; // Skip the comma.
-
+
for ( ; ('0' <= *chPtr) && (*chPtr <= '9'); ++chPtr ) minNum = minNum*10 + (*chPtr - '0');
if ( (*chPtr != ',') && (*chPtr != '.') ) return; // Bad XMP string.
-
+
if ( *chPtr == ',' ) {
++chPtr; // Skip the comma.
@@ -2402,15 +2668,15 @@ ExportTIFF_GPSCoordinate ( const SXMPMeta & xmp, const char * xmpNS, const char
}
}
-
+
if ( *(chPtr+1) != 0 ) return; // Bad XMP string.
-
+
char ref[2];
ref[0] = *chPtr;
ref[1] = 0;
-
+
tiff->SetTag ( ifd, refID, kTIFF_ASCIIType, 2, &ref[0] );
-
+
XMP_Uns32 loc[6];
tiff->PutUns32 ( deg, &loc[0] );
tiff->PutUns32 ( 1, &loc[1] );
@@ -2418,120 +2684,251 @@ ExportTIFF_GPSCoordinate ( const SXMPMeta & xmp, const char * xmpNS, const char
tiff->PutUns32 ( minDenom, &loc[3] );
tiff->PutUns32 ( sec, &loc[4] );
tiff->PutUns32 ( 1, &loc[5] );
-
+
tiff->SetTag ( ifd, locID, kTIFF_RationalType, 3, &loc[0] );
} catch ( ... ) {
// Do nothing, let other exports proceed.
// ? Notify client?
}
-
-} // ExportTIFF_GPSCoordinate
-// =================================================================================================
-// =================================================================================================
+} // ExportTIFF_GPSCoordinate
// =================================================================================================
-// ReconcileUtils::ExportTIFF
-// ==========================
+// ExportTIFF_GPSTimeStamp
+// =======================
//
-// Only a few tags are written back from XMP to the primary IFD, they are each handled explicitly.
-// The writeback tags are:
-// 270 - ImageDescription
-// 274 - Orientation
-// 282 - XResolution
-// 283 - YResolution
-// 296 - ResolutionUnit
-// 305 - Software
-// 306 - DateTime
-// 315 - Artist
-// 33432 - Copyright
-
-// *** need to determine if the XMP has changed - only export when necessary
+// The Exif is in 2 tags, GPSTimeStamp and GPSDateStamp. The time is 3 rationals for the hour, minute,
+// and second in UTC. The date is a nul terminated string "YYYY:MM:DD".
-void
-ReconcileUtils::ExportTIFF ( const SXMPMeta & xmp, TIFF_Manager * tiff )
+static const double kBillion = 1000.0*1000.0*1000.0;
+static const double mMaxSec = 4.0*kBillion - 1.0;
+
+static void
+ExportTIFF_GPSTimeStamp ( const SXMPMeta & xmp, const char * xmpNS, const char * xmpProp, TIFF_Manager * tiff )
{
- ExportTIFF_LocTextASCII ( xmp, kXMP_NS_DC, "description",
- tiff, kTIFF_PrimaryIFD, kTIFF_ImageDescription );
-
- ExportSingleTIFF_Short ( xmp, kXMP_NS_TIFF, "Orientation",
- tiff, kTIFF_PrimaryIFD, kTIFF_Orientation );
-
- ExportSingleTIFF_Rational ( xmp, kXMP_NS_TIFF, "XResolution",
- tiff, kTIFF_PrimaryIFD, kTIFF_XResolution );
+ try { // Don't let errors with one stop the others.
- ExportSingleTIFF_Rational ( xmp, kXMP_NS_TIFF, "YResolution",
- tiff, kTIFF_PrimaryIFD, kTIFF_YResolution );
+ XMP_DateTime binXMP;
+ bool foundXMP = xmp.GetProperty_Date ( xmpNS, xmpProp, &binXMP, 0 );
+ if ( ! foundXMP ) {
+ tiff->DeleteTag ( kTIFF_GPSInfoIFD, kTIFF_GPSTimeStamp );
+ tiff->DeleteTag ( kTIFF_GPSInfoIFD, kTIFF_GPSDateStamp );
+ return;
+ }
+
+ SXMPUtils::ConvertToUTCTime ( &binXMP );
- ExportSingleTIFF_Short ( xmp, kXMP_NS_TIFF, "ResolutionUnit",
- tiff, kTIFF_PrimaryIFD, kTIFF_ResolutionUnit );
+ XMP_Uns32 exifTime[6];
+ tiff->PutUns32 ( binXMP.hour, &exifTime[0] );
+ tiff->PutUns32 ( 1, &exifTime[1] );
+ tiff->PutUns32 ( binXMP.minute, &exifTime[2] );
+ tiff->PutUns32 ( 1, &exifTime[3] );
+ if ( binXMP.nanoSecond == 0 ) {
+ tiff->PutUns32 ( binXMP.second, &exifTime[4] );
+ tiff->PutUns32 ( 1, &exifTime[5] );
+ } else {
+ double fSec = (double)binXMP.second + ((double)binXMP.nanoSecond / kBillion );
+ XMP_Uns32 denom = 1000*1000; // Choose microsecond resolution by default.
+ TIFF_Manager::TagInfo oldInfo;
+ bool hadExif = tiff->GetTag ( kTIFF_GPSInfoIFD, kTIFF_GPSTimeStamp, &oldInfo );
+ if ( hadExif && (oldInfo.type == kTIFF_RationalType) && (oldInfo.count == 3) ) {
+ XMP_Uns32 oldDenom = tiff->GetUns32 ( &(((XMP_Uns32*)oldInfo.dataPtr)[5]) );
+ if ( oldDenom != 1 ) denom = oldDenom;
+ }
+ fSec *= denom;
+ while ( fSec > mMaxSec ) { fSec /= 10; denom /= 10; }
+ tiff->PutUns32 ( (XMP_Uns32)fSec, &exifTime[4] );
+ tiff->PutUns32 ( denom, &exifTime[5] );
+ }
+ tiff->SetTag ( kTIFF_GPSInfoIFD, kTIFF_GPSTimeStamp, kTIFF_RationalType, 3, &exifTime[0] );
- ExportSingleTIFF_ASCII ( xmp, kXMP_NS_XMP, "CreatorTool",
- tiff, kTIFF_PrimaryIFD, kTIFF_Software );
+ char exifDate[16]; // AUDIT: Long enough, only need 11.
+ snprintf ( exifDate, 12, "%04d:%02d:%02d", binXMP.year, binXMP.month, binXMP.day );
+ if ( exifDate[10] == 0 ) { // Make sure there is no value overflow.
+ tiff->SetTag ( kTIFF_GPSInfoIFD, kTIFF_GPSDateStamp, kTIFF_ASCIIType, 11, exifDate );
+ }
- ExportTIFF_Date ( xmp, kXMP_NS_XMP, "ModifyDate",
- tiff, kTIFF_PrimaryIFD, kTIFF_DateTime, kTIFF_ExifIFD, kTIFF_SubSecTime );
-
- ExportArrayTIFF_ASCII ( xmp, kXMP_NS_DC, "creator",
- tiff, kTIFF_PrimaryIFD, kTIFF_Artist );
+ } catch ( ... ) {
+ // Do nothing, let other exports proceed.
+ // ? Notify client?
+ }
- ExportTIFF_LocTextASCII ( xmp, kXMP_NS_DC, "rights",
- tiff, kTIFF_PrimaryIFD, kTIFF_Copyright );
+} // ExportTIFF_GPSTimeStamp
-} // ReconcileUtils::ExportTIFF;
+// =================================================================================================
+// =================================================================================================
// =================================================================================================
-// ReconcileUtils::ExportExif
+// PhotoDataUtils::ExportExif
// ==========================
-//
-// Only a few tags are written back from XMP to the Exif and GPS IFDs, they are each handled
-// explicitly. The Exif writeback tags are:
-// 36867 - DateTimeOriginal (plus 37521 SubSecTimeOriginal)
-// 36868 - DateTimeDigitized (plus 37522 SubSecTimeDigitized)
-// 37510 - UserComment
-// 40964 - RelatedSoundFile
-// The GPS writeback tags are:
-// 1 - GPSLatitudeRef
-// 2 - GPSLatitude
-// 3 - GPSLongitudeRef
-// 4 - GPSLongitude
-
-// ! Older versions of Photoshop did not import the UserComment or RelatedSoundFile tags. Don't
-// ! export the current XMP unless the original XMP had the tag or the current XMP has the tag.
-// ! That is, don't delete the Exif tag if the XMP never had the property.
void
-ReconcileUtils::ExportExif ( const SXMPMeta & xmp, TIFF_Manager * tiff )
+PhotoDataUtils::ExportExif ( SXMPMeta * xmp, TIFF_Manager * exif )
{
+ bool haveXMP, haveExif;
+ std::string xmpValue;
+ XMP_Int32 int32;
+ XMP_Uns8 uns8;
+
+ // Do all of the table driven standard exports.
+
+ ExportTIFF_StandardMappings ( kTIFF_PrimaryIFD, exif, *xmp );
+ ExportTIFF_StandardMappings ( kTIFF_ExifIFD, exif, *xmp );
+ ExportTIFF_StandardMappings ( kTIFF_GPSInfoIFD, exif, *xmp );
+
+ // Export dc:description to TIFF ImageDescription, and exif:UserComment to EXIF UserComment.
+
+ // *** This is not following the MWG guidelines. The policy here tries to be more backward compatible.
+
+ ExportTIFF_LocTextASCII ( *xmp, kXMP_NS_DC, "description",
+ exif, kTIFF_PrimaryIFD, kTIFF_ImageDescription );
+
+ ExportTIFF_EncodedString ( *xmp, kXMP_NS_EXIF, "UserComment",
+ exif, kTIFF_ExifIFD, kTIFF_UserComment, true /* isLangAlt */ );
+
+ // Export all of the date/time tags.
+ // ! Special case: Don't create Exif DateTimeDigitized. This can avoid PSD full rewrite due to
+ // ! new mapping from xmp:CreateDate.
- if ( xmp.DoesPropertyExist ( kXMP_NS_EXIF, "DateTimeOriginal" ) ) {
- ExportTIFF_Date ( xmp, kXMP_NS_EXIF, "DateTimeOriginal",
- tiff, kTIFF_ExifIFD, kTIFF_DateTimeOriginal, kTIFF_ExifIFD, kTIFF_SubSecTimeOriginal );
+ if ( exif->GetTag ( kTIFF_ExifIFD, kTIFF_DateTimeDigitized, 0 ) ) {
+ ExportTIFF_Date ( *xmp, kXMP_NS_XMP, "CreateDate", exif, kTIFF_DateTimeDigitized );
}
- if ( xmp.DoesPropertyExist ( kXMP_NS_EXIF, "DateTimeDigitized" ) ) {
- ExportTIFF_Date ( xmp, kXMP_NS_EXIF, "DateTimeDigitized",
- tiff, kTIFF_ExifIFD, kTIFF_DateTimeDigitized, kTIFF_ExifIFD, kTIFF_SubSecTimeDigitized );
+ ExportTIFF_Date ( *xmp, kXMP_NS_EXIF, "DateTimeOriginal", exif, kTIFF_DateTimeOriginal );
+ ExportTIFF_Date ( *xmp, kXMP_NS_XMP, "ModifyDate", exif, kTIFF_DateTime );
+
+ // 34855 ISOSpeedRatings has special cases for ISO over 65535. The tag is SHORT, some cameras
+ // omit the tag and some write 65535, all put the real ISO in MakerNote - which ACR might
+ // extract and leave in the XMP. There are 2 export cases:
+ // 1. No XMP property, or one or more of the XMP values are over 65535:
+ // Leave both the XMP and native tag alone.
+ // 1. Have XMP property and all of the XMP values are under 65535:
+ // Leave existing native tag, else export; strip the XMP either way.
+
+ haveXMP = xmp->DoesPropertyExist ( kXMP_NS_EXIF, "ISOSpeedRatings" );
+ if ( haveXMP ) {
+
+ XMP_Index i, count;
+ std::string isoValue;
+ bool haveHighISO = false;
+
+ for ( i = 1, count = xmp->CountArrayItems ( kXMP_NS_EXIF, "ISOSpeedRatings" ); i <= count; ++i ) {
+ xmp->GetArrayItem ( kXMP_NS_EXIF, "ISOSpeedRatings", i, &isoValue, 0 );
+ if ( SXMPUtils::ConvertToInt ( isoValue.c_str() ) > 0xFFFF ) { haveHighISO = true; break; }
+ }
+
+ if ( ! haveHighISO ) {
+ haveExif = exif->GetTag ( kTIFF_ExifIFD, kTIFF_ISOSpeedRatings, 0 );
+ if ( ! haveExif ) { // ISOSpeedRatings has an inject-only mapping.
+ ExportArrayTIFF ( exif, kTIFF_ExifIFD, kISOSpeedMapping, exif->IsNativeEndian(), *xmp, kXMP_NS_EXIF, "ISOSpeedRatings" );
+ }
+ xmp->DeleteProperty ( kXMP_NS_EXIF, "ISOSpeedRatings");
+ }
+
}
+
+ // Export the remaining TIFF, Exif, and GPS IFD tags.
+
+ ExportTIFF_ArrayASCII ( *xmp, kXMP_NS_DC, "creator", exif, kTIFF_PrimaryIFD, kTIFF_Artist );
- if ( tiff->xmpHadUserComment || xmp.DoesPropertyExist ( kXMP_NS_EXIF, "UserComment" ) ) {
- ExportTIFF_EncodedString ( xmp, kXMP_NS_EXIF, "UserComment",
- tiff, kTIFF_ExifIFD, kTIFF_UserComment, true /* isLangAlt */ );
+ ExportTIFF_LocTextASCII ( *xmp, kXMP_NS_DC, "rights", exif, kTIFF_PrimaryIFD, kTIFF_Copyright );
+
+ haveXMP = xmp->GetProperty ( kXMP_NS_EXIF, "ExifVersion", &xmpValue, 0 );
+ if ( haveXMP && (xmpValue.size() == 4) && (! exif->GetTag ( kTIFF_ExifIFD, kTIFF_ExifVersion, 0 )) ) {
+ // 36864 ExifVersion is 4 "undefined" ASCII characters.
+ exif->SetTag ( kTIFF_ExifIFD, kTIFF_ExifVersion, kTIFF_UndefinedType, 4, xmpValue.data() );
+ }
+
+ haveXMP = xmp->DoesPropertyExist ( kXMP_NS_EXIF, "ComponentsConfiguration" );
+ if ( haveXMP && (xmp->CountArrayItems ( kXMP_NS_EXIF, "ComponentsConfiguration" ) == 4) &&
+ (! exif->GetTag ( kTIFF_ExifIFD, kTIFF_ComponentsConfiguration, 0 )) ) {
+ // 37121 ComponentsConfiguration is an array of 4 "undefined" UInt8 bytes.
+ XMP_Uns8 compConfig[4];
+ xmp->GetProperty_Int ( kXMP_NS_EXIF, "ComponentsConfiguration[1]", &int32, 0 );
+ compConfig[0] = (XMP_Uns8)int32;
+ xmp->GetProperty_Int ( kXMP_NS_EXIF, "ComponentsConfiguration[2]", &int32, 0 );
+ compConfig[1] = (XMP_Uns8)int32;
+ xmp->GetProperty_Int ( kXMP_NS_EXIF, "ComponentsConfiguration[3]", &int32, 0 );
+ compConfig[2] = (XMP_Uns8)int32;
+ xmp->GetProperty_Int ( kXMP_NS_EXIF, "ComponentsConfiguration[4]", &int32, 0 );
+ compConfig[3] = (XMP_Uns8)int32;
+ exif->SetTag ( kTIFF_ExifIFD, kTIFF_ComponentsConfiguration, kTIFF_UndefinedType, 4, &compConfig[0] );
+ }
+
+ haveXMP = xmp->DoesPropertyExist ( kXMP_NS_EXIF, "Flash" );
+ if ( haveXMP && (! exif->GetTag ( kTIFF_ExifIFD, kTIFF_Flash, 0 )) ) {
+ // 37385 Flash is a UInt16 collection of bit fields and is mapped to a struct in XMP.
+ XMP_Uns16 binFlash = 0;
+ bool field;
+ haveXMP = xmp->GetProperty_Bool ( kXMP_NS_EXIF, "Flash/exif:Fired", &field, 0 );
+ if ( haveXMP & field ) binFlash |= 0x0001;
+ haveXMP = xmp->GetProperty_Int ( kXMP_NS_EXIF, "Flash/exif:Return", &int32, 0 );
+ if ( haveXMP ) binFlash |= (int32 & 3) << 1;
+ haveXMP = xmp->GetProperty_Int ( kXMP_NS_EXIF, "Flash/exif:Mode", &int32, 0 );
+ if ( haveXMP ) binFlash |= (int32 & 3) << 3;
+ haveXMP = xmp->GetProperty_Bool ( kXMP_NS_EXIF, "Flash/exif:Function", &field, 0 );
+ if ( haveXMP & field ) binFlash |= 0x0020;
+ haveXMP = xmp->GetProperty_Bool ( kXMP_NS_EXIF, "Flash/exif:RedEyeMode", &field, 0 );
+ if ( haveXMP & field ) binFlash |= 0x0040;
+ exif->SetTag_Short ( kTIFF_ExifIFD, kTIFF_Flash, binFlash );
+ }
+
+ haveXMP = xmp->GetProperty_Int ( kXMP_NS_EXIF, "FileSource", &int32, 0 );
+ if ( haveXMP && (! exif->GetTag ( kTIFF_ExifIFD, kTIFF_FileSource, 0 )) ) {
+ // 41728 FileSource is an "undefined" UInt8.
+ uns8 = (XMP_Uns8)int32;
+ exif->SetTag ( kTIFF_ExifIFD, kTIFF_FileSource, kTIFF_UndefinedType, 1, &uns8 );
}
+
+ haveXMP = xmp->GetProperty_Int ( kXMP_NS_EXIF, "SceneType", &int32, 0 );
+ if ( haveXMP && (! exif->GetTag ( kTIFF_ExifIFD, kTIFF_SceneType, 0 )) ) {
+ // 41729 SceneType is an "undefined" UInt8.
+ uns8 = (XMP_Uns8)int32;
+ exif->SetTag ( kTIFF_ExifIFD, kTIFF_SceneType, kTIFF_UndefinedType, 1, &uns8 );
+ }
+
+ // *** Deferred inject-only tags: SpatialFrequencyResponse, DeviceSettingDescription, CFAPattern
- if ( tiff->xmpHadRelatedSoundFile || xmp.DoesPropertyExist ( kXMP_NS_EXIF, "RelatedSoundFile" ) ) {
- ExportSingleTIFF_ASCII ( xmp, kXMP_NS_EXIF, "RelatedSoundFile",
- tiff, kTIFF_ExifIFD, kTIFF_RelatedSoundFile );
+ haveXMP = xmp->GetProperty ( kXMP_NS_EXIF, "GPSVersionID", &xmpValue, 0 ); // This is inject-only.
+ if ( haveXMP && (xmpValue.size() == 7) && (! exif->GetTag ( kTIFF_GPSInfoIFD, kTIFF_GPSVersionID, 0 )) ) {
+ char gpsID[4]; // 0 GPSVersionID is 4 UInt8 bytes and mapped in XMP as "n.n.n.n".
+ gpsID[0] = xmpValue[0];
+ gpsID[1] = xmpValue[2];
+ gpsID[2] = xmpValue[4];
+ gpsID[3] = xmpValue[6];
+ exif->SetTag ( kTIFF_GPSInfoIFD, kTIFF_GPSVersionID, kTIFF_ByteType, 4, &gpsID[0] );
}
+
+ ExportTIFF_GPSCoordinate ( *xmp, kXMP_NS_EXIF, "GPSLatitude", exif, kTIFF_GPSInfoIFD, kTIFF_GPSLatitude );
+
+ ExportTIFF_GPSCoordinate ( *xmp, kXMP_NS_EXIF, "GPSLongitude", exif, kTIFF_GPSInfoIFD, kTIFF_GPSLongitude );
+
+ ExportTIFF_GPSTimeStamp ( *xmp, kXMP_NS_EXIF, "GPSTimeStamp", exif );
- if ( xmp.DoesPropertyExist ( kXMP_NS_EXIF, "GPSLatitude" ) ) {
- ExportTIFF_GPSCoordinate ( xmp, kXMP_NS_EXIF, "GPSLatitude", tiff, kTIFF_GPSInfoIFD, kTIFF_GPSLatitude );
+ // The following GPS tags are inject-only.
+
+ haveXMP = xmp->DoesPropertyExist ( kXMP_NS_EXIF, "GPSDestLatitude" );
+ if ( haveXMP && (! exif->GetTag ( kTIFF_GPSInfoIFD, kTIFF_GPSDestLatitude, 0 )) ) {
+ ExportTIFF_GPSCoordinate ( *xmp, kXMP_NS_EXIF, "GPSDestLatitude", exif, kTIFF_GPSInfoIFD, kTIFF_GPSDestLatitude );
+ }
+
+ haveXMP = xmp->DoesPropertyExist ( kXMP_NS_EXIF, "GPSDestLongitude" );
+ if ( haveXMP && (! exif->GetTag ( kTIFF_GPSInfoIFD, kTIFF_GPSDestLongitude, 0 )) ) {
+ ExportTIFF_GPSCoordinate ( *xmp, kXMP_NS_EXIF, "GPSDestLongitude", exif, kTIFF_GPSInfoIFD, kTIFF_GPSDestLongitude );
}
- if ( xmp.DoesPropertyExist ( kXMP_NS_EXIF, "GPSLongitude" ) ) {
- ExportTIFF_GPSCoordinate ( xmp, kXMP_NS_EXIF, "GPSLongitude", tiff, kTIFF_GPSInfoIFD, kTIFF_GPSLongitude );
+ haveXMP = xmp->GetProperty ( kXMP_NS_EXIF, "GPSProcessingMethod", &xmpValue, 0 );
+ if ( haveXMP && (! xmpValue.empty()) && (! exif->GetTag ( kTIFF_GPSInfoIFD, kTIFF_GPSProcessingMethod, 0 )) ) {
+ // 27 GPSProcessingMethod is a string with explicit encoding.
+ ExportTIFF_EncodedString ( *xmp, kXMP_NS_EXIF, "GPSProcessingMethod", exif, kTIFF_GPSInfoIFD, kTIFF_GPSProcessingMethod );
}
-} // ReconcileUtils::ExportExif;
+ haveXMP = xmp->GetProperty ( kXMP_NS_EXIF, "GPSAreaInformation", &xmpValue, 0 );
+ if ( haveXMP && (! xmpValue.empty()) && (! exif->GetTag ( kTIFF_GPSInfoIFD, kTIFF_GPSAreaInformation, 0 )) ) {
+ // 28 GPSAreaInformation is a string with explicit encoding.
+ ExportTIFF_EncodedString ( *xmp, kXMP_NS_EXIF, "GPSAreaInformation", exif, kTIFF_GPSInfoIFD, kTIFF_GPSAreaInformation );
+ }
+
+} // PhotoDataUtils::ExportExif
diff --git a/source/XMPFiles/FormatSupport/Reconcile_Impl.cpp b/source/XMPFiles/FormatSupport/Reconcile_Impl.cpp
index 1f06083..7d27769 100644
--- a/source/XMPFiles/FormatSupport/Reconcile_Impl.cpp
+++ b/source/XMPFiles/FormatSupport/Reconcile_Impl.cpp
@@ -1,6 +1,6 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2006-2007 Adobe Systems Incorporated
+// Copyright 2006 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -20,26 +20,26 @@
// =================================================================================================
/// \file Reconcile_Impl.cpp
-/// \brief Implementation utilities for the legacy metadata reconciliation support.
+/// \brief Implementation utilities for the photo metadata reconciliation support.
///
// =================================================================================================
// =================================================================================================
-// IsASCII
-// =======
+// ReconcileUtils::IsASCII
+// =======================
//
// See if a string is 7 bit ASCII.
-static inline bool IsASCII ( const void * strPtr, size_t strLen )
+bool ReconcileUtils::IsASCII ( const void * textPtr, size_t textLen )
{
- for ( const XMP_Uns8 * strPos = (XMP_Uns8*)strPtr; strLen > 0; --strLen, ++strPos ) {
- if ( *strPos >= 0x80 ) return false;
+ for ( const XMP_Uns8 * textPos = (XMP_Uns8*)textPtr; textLen > 0; --textLen, ++textPos ) {
+ if ( *textPos >= 0x80 ) return false;
}
return true;
-} // IsASCII
+} // ReconcileUtils::IsASCII
// =================================================================================================
// ReconcileUtils::IsUTF8
@@ -49,16 +49,16 @@ static inline bool IsASCII ( const void * strPtr, size_t strLen )
// strings. We don't use CodePoint_from_UTF8_Multi in UnicodeConversions because it throws an
// exception for non-Unicode and we don't need to actually compute the code points.
-bool ReconcileUtils::IsUTF8 ( const void * utf8Ptr, size_t utf8Len )
+bool ReconcileUtils::IsUTF8 ( const void * textPtr, size_t textLen )
{
- const XMP_Uns8 * utf8Pos = (XMP_Uns8*)utf8Ptr;
- const XMP_Uns8 * utf8End = utf8Pos + utf8Len;
+ const XMP_Uns8 * textPos = (XMP_Uns8*)textPtr;
+ const XMP_Uns8 * textEnd = textPos + textLen;
- while ( utf8Pos < utf8End ) {
+ while ( textPos < textEnd ) {
- if ( *utf8Pos < 0x80 ) {
+ if ( *textPos < 0x80 ) {
- ++utf8Pos; // ASCII is UTF-8, tolerate nuls.
+ ++textPos; // ASCII is UTF-8, tolerate nuls.
} else {
@@ -68,26 +68,26 @@ bool ReconcileUtils::IsUTF8 ( const void * utf8Ptr, size_t utf8Len )
#if 0 // *** This might be a more effcient way to count the bytes.
static XMP_Uns8 kByteCounts[16] = { 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 2, 2, 3, 4 };
- size_t bytesNeeded = kByteCounts [ *utf8Pos >> 4 ];
- if ( (bytesNeeded < 2) || ((bytesNeeded == 4) && ((*utf8Pos & 0x08) != 0)) ) return false;
- if ( (utf8Pos + bytesNeeded) > utf8End ) return false;
+ size_t bytesNeeded = kByteCounts [ *textPos >> 4 ];
+ if ( (bytesNeeded < 2) || ((bytesNeeded == 4) && ((*textPos & 0x08) != 0)) ) return false;
+ if ( (textPos + bytesNeeded) > textEnd ) return false;
#endif
size_t bytesNeeded = 0; // Count the high order 1 bits in the first byte.
- for ( XMP_Uns8 temp = *utf8Pos; temp > 0x7F; temp = temp << 1 ) ++bytesNeeded;
+ for ( XMP_Uns8 temp = *textPos; temp > 0x7F; temp = temp << 1 ) ++bytesNeeded;
// *** Consider CPU-specific assembly inline, e.g. cntlzw on PowerPC.
- if ( (bytesNeeded < 2) || (bytesNeeded > 4) || ((utf8Pos+bytesNeeded) > utf8End) ) return false;
+ if ( (bytesNeeded < 2) || (bytesNeeded > 4) || ((textPos+bytesNeeded) > textEnd) ) return false;
- for ( --bytesNeeded, ++utf8Pos; bytesNeeded > 0; --bytesNeeded, ++utf8Pos ) {
- if ( (*utf8Pos >> 6) != 2 ) return false;
+ for ( --bytesNeeded, ++textPos; bytesNeeded > 0; --bytesNeeded, ++textPos ) {
+ if ( (*textPos >> 6) != 2 ) return false;
}
}
}
- return true;
+ return true; // ! Returns true for empty strings.
} // ReconcileUtils::IsUTF8
@@ -97,8 +97,7 @@ bool ReconcileUtils::IsUTF8 ( const void * utf8Ptr, size_t utf8Len )
#if XMP_WinBuild
- static void UTF8ToWinEncoding ( UINT codePage,
- const XMP_Uns8 * utf8Ptr, size_t utf8Len, std::string * host )
+ void ReconcileUtils::UTF8ToWinEncoding ( UINT codePage, const XMP_Uns8 * utf8Ptr, size_t utf8Len, std::string * host )
{
std::string utf16; // WideCharToMultiByte wants native UTF-16.
@@ -117,11 +116,15 @@ bool ReconcileUtils::IsUTF8 ( const void * utf8Ptr, size_t utf8Len )
#elif XMP_MacBuild
- static void UTF8ToMacEncoding ( TextEncoding & destEncoding,
- const XMP_Uns8 * utf8Ptr, size_t utf8Len, std::string * host )
+ void ReconcileUtils::UTF8ToMacEncoding ( XMP_Uns16 macScript, XMP_Uns16 macLang, const XMP_Uns8 * utf8Ptr, size_t utf8Len, std::string * host )
{
OSStatus err;
+ TextEncoding destEncoding;
+ if ( macLang == langUnspecified ) macLang = kTextLanguageDontCare;
+ err = UpgradeScriptInfoToTextEncoding ( macScript, macLang, kTextRegionDontCare, 0, &destEncoding );
+ if ( err != noErr ) XMP_Throw ( "UpgradeScriptInfoToTextEncoding failed", kXMPErr_ExternalFailure );
+
UnicodeMapping mappingInfo;
mappingInfo.mappingVersion = kUnicodeUseLatestMapping;
mappingInfo.otherEncoding = GetTextEncodingBase ( destEncoding );
@@ -167,8 +170,7 @@ bool ReconcileUtils::IsUTF8 ( const void * utf8Ptr, size_t utf8Len )
#elif XMP_UNIXBuild
- // ! Does not exist, must not be called, for Generic UNIX builds. It is not clear at this time
- // ! what notion of local encoding should be used for generic UNIX, especially in a server product.
+ // ! Does not exist, must not be called, for Generic UNIX builds.
#endif
@@ -176,17 +178,13 @@ bool ReconcileUtils::IsUTF8 ( const void * utf8Ptr, size_t utf8Len )
// ReconcileUtils::UTF8ToLocal
// ===========================
-#if ! XMP_UNIXBuild
-// ! Does not exist, must not be called, for Generic UNIX builds. It is not clear at this time
-// ! what notion of local encoding should be used for generic UNIX, especially in a server product.
-
void ReconcileUtils::UTF8ToLocal ( const void * _utf8Ptr, size_t utf8Len, std::string * local )
{
const XMP_Uns8* utf8Ptr = (XMP_Uns8*)_utf8Ptr;
local->erase();
- if ( IsASCII ( utf8Ptr, utf8Len ) ) {
+ if ( ReconcileUtils::IsASCII ( utf8Ptr, utf8Len ) ) {
local->assign ( (const char *)utf8Ptr, utf8Len );
return;
}
@@ -197,76 +195,87 @@ void ReconcileUtils::UTF8ToLocal ( const void * _utf8Ptr, size_t utf8Len, std::s
#elif XMP_MacBuild
- OSStatus err;
-
- TextEncoding localEncoding;
- err = UpgradeScriptInfoToTextEncoding ( smSystemScript,
- kTextLanguageDontCare, kTextRegionDontCare, 0, &localEncoding );
- if ( err != noErr ) XMP_Throw ( "UpgradeScriptInfoToTextEncoding failed", kXMPErr_ExternalFailure );
-
- UTF8ToMacEncoding ( localEncoding, utf8Ptr, utf8Len, local );
+ UTF8ToMacEncoding ( smSystemScript, kTextLanguageDontCare, utf8Ptr, utf8Len, local );
#elif XMP_UNIXBuild
- #error "No generic UNIX implementation"
+ XMP_Throw ( "Generic UNIX does not have conversions between local and Unicode", kXMPErr_Unavailable );
#endif
} // ReconcileUtils::UTF8ToLocal
-#endif
-
// =================================================================================================
// ReconcileUtils::UTF8ToLatin1
// ============================
-//
-// Actually to the Windows code page 1252 superset of 8859-1.
-
-#if ! XMP_UNIXBuild
-// ! Does not exist, must not be called, for Generic UNIX builds. At some point we could consider
-// ! creating our own private implementation. So far only needed for the ID3 legacy in MP3 files.
void ReconcileUtils::UTF8ToLatin1 ( const void * _utf8Ptr, size_t utf8Len, std::string * latin1 )
{
const XMP_Uns8* utf8Ptr = (XMP_Uns8*)_utf8Ptr;
+ const XMP_Uns8* utf8End = utf8Ptr + utf8Len;
latin1->erase();
+ latin1->reserve ( utf8Len ); // As good a guess as any, at least enough, exact for ASCII.
- if ( IsASCII ( utf8Ptr, utf8Len ) ) {
- latin1->assign ( (const char *)utf8Ptr, utf8Len );
- return;
- }
-
- #if XMP_WinBuild
-
- UTF8ToWinEncoding ( 1252, utf8Ptr, utf8Len, latin1 );
+ bool inBadRun = false;
- #elif XMP_MacBuild
-
- TextEncoding latin1Encoding;
- latin1Encoding = CreateTextEncoding ( kTextEncodingWindowsLatin1,
- kTextEncodingDefaultVariant, kTextEncodingDefaultFormat );
-
- UTF8ToMacEncoding ( latin1Encoding, utf8Ptr, utf8Len, latin1 );
+ while ( utf8Ptr < utf8End ) {
- #elif XMP_UNIXBuild
+ if ( *utf8Ptr <= 0x7F ) {
+
+ (*latin1) += (char)*utf8Ptr; // Have an ASCII character.
+ inBadRun = false;
+ ++utf8Ptr;
+
+ } else if ( utf8Ptr == (utf8End - 1) ) {
+
+ inBadRun = false;
+ ++utf8Ptr; // Ignore a bad end to the UTF-8.
+
+ } else {
+
+ XMP_Assert ( (utf8End - utf8Ptr) >= 2 );
+ XMP_Uns16 ch16 = GetUns16BE ( utf8Ptr ); // A Latin-1 80..FF is 2 UTF-8 bytes.
+
+ if ( (0xC280 <= ch16) && (ch16 <= 0xC2BF) ) {
+
+ (*latin1) += (char)(ch16 & 0xFF); // UTF-8 C280..C2BF are Latin-1 80..BF.
+ inBadRun = false;
+ utf8Ptr += 2;
+
+ } else if ( (0xC380 <= ch16) && (ch16 <= 0xC3BF) ) {
+
+ (*latin1) += (char)((ch16 & 0xFF) + 0x40); // UTF-8 C380..C3BF are Latin-1 C0..FF.
+ inBadRun = false;
+ utf8Ptr += 2;
+
+ } else {
+
+ if ( ! inBadRun ) {
+ inBadRun = true;
+ (*latin1) += "(?)"; // Mark the run of out of scope UTF-8.
+ }
+
+ ++utf8Ptr; // Skip the presumably well-formed UTF-8 character.
+ while ( (utf8Ptr < utf8End) && ((*utf8Ptr & 0xC0) == 0x80) ) ++utf8Ptr;
+
+ }
+
+ }
- #error "No generic UNIX implementation"
+ }
- #endif
+ XMP_Assert ( utf8Ptr == utf8End );
} // ReconcileUtils::UTF8ToLatin1
-#endif
-
// =================================================================================================
// HostEncodingToUTF8
// ==================
#if XMP_WinBuild
- static void WinEncodingToUTF8 ( UINT codePage,
- const XMP_Uns8 * hostPtr, size_t hostLen, std::string * utf8 )
+ void ReconcileUtils::WinEncodingToUTF8 ( UINT codePage, const XMP_Uns8 * hostPtr, size_t hostLen, std::string * utf8 )
{
int utf16Len = MultiByteToWideChar ( codePage, 0, (LPCSTR)hostPtr, (int)hostLen, 0, 0 );
@@ -279,11 +288,15 @@ void ReconcileUtils::UTF8ToLatin1 ( const void * _utf8Ptr, size_t utf8Len, std::
#elif XMP_MacBuild
- static void MacEncodingToUTF8 ( TextEncoding & srcEncoding,
- const XMP_Uns8 * hostPtr, size_t hostLen, std::string * utf8 )
+ void ReconcileUtils::MacEncodingToUTF8 ( XMP_Uns16 macScript, XMP_Uns16 macLang, const XMP_Uns8 * hostPtr, size_t hostLen, std::string * utf8 )
{
OSStatus err;
+ TextEncoding srcEncoding;
+ if ( macLang == langUnspecified ) macLang = kTextLanguageDontCare;
+ err = UpgradeScriptInfoToTextEncoding ( macScript, macLang, kTextRegionDontCare, 0, &srcEncoding );
+ if ( err != noErr ) XMP_Throw ( "UpgradeScriptInfoToTextEncoding failed", kXMPErr_ExternalFailure );
+
UnicodeMapping mappingInfo;
mappingInfo.mappingVersion = kUnicodeUseLatestMapping;
mappingInfo.otherEncoding = GetTextEncodingBase ( srcEncoding );
@@ -327,8 +340,7 @@ void ReconcileUtils::UTF8ToLatin1 ( const void * _utf8Ptr, size_t utf8Len, std::
#elif XMP_UNIXBuild
- // ! Does not exist, must not be called, for Generic UNIX builds. It is not clear at this time
- // ! what notion of local encoding should be used for generic UNIX, especially in a server product.
+ // ! Does not exist, must not be called, for Generic UNIX builds.
#endif
@@ -336,17 +348,13 @@ void ReconcileUtils::UTF8ToLatin1 ( const void * _utf8Ptr, size_t utf8Len, std::
// ReconcileUtils::LocalToUTF8
// ===========================
-#if ! XMP_UNIXBuild
-// ! Does not exist, must not be called, for Generic UNIX builds. It is not clear at this time
-// ! what notion of local encoding should be used for generic UNIX, especially in a server product.
-
void ReconcileUtils::LocalToUTF8 ( const void * _localPtr, size_t localLen, std::string * utf8 )
{
const XMP_Uns8* localPtr = (XMP_Uns8*)_localPtr;
utf8->erase();
- if ( IsASCII ( localPtr, localLen ) ) {
+ if ( ReconcileUtils::IsASCII ( localPtr, localLen ) ) {
utf8->assign ( (const char *)localPtr, localLen );
return;
}
@@ -357,63 +365,42 @@ void ReconcileUtils::LocalToUTF8 ( const void * _localPtr, size_t localLen, std:
#elif XMP_MacBuild
- OSStatus err;
-
- TextEncoding localEncoding;
- err = UpgradeScriptInfoToTextEncoding ( smSystemScript, kTextLanguageDontCare, kTextRegionDontCare, 0, &localEncoding );
- if ( err != noErr ) XMP_Throw ( "UpgradeScriptInfoToTextEncoding failed", kXMPErr_ExternalFailure );
-
- MacEncodingToUTF8 ( localEncoding, localPtr, localLen, utf8 );
+ MacEncodingToUTF8 ( smSystemScript, kTextLanguageDontCare, localPtr, localLen, utf8 );
#elif XMP_UNIXBuild
- #error "No generic UNIX implementation"
+ XMP_Throw ( "Generic UNIX does not have conversions between local and Unicode", kXMPErr_Unavailable );
#endif
} // ReconcileUtils::LocalToUTF8
-#endif
-
// =================================================================================================
// ReconcileUtils::Latin1ToUTF8
// ============================
-//
-// Actually from the Windows code page 1252 superset of 8859-1.
-
-#if ! XMP_UNIXBuild
-// ! Does not exist, must not be called, for Generic UNIX builds. At some point we could consider
-// ! creating our own private implementation. So far only needed for the ID3 legacy in MP3 files.
void ReconcileUtils::Latin1ToUTF8 ( const void * _latin1Ptr, size_t latin1Len, std::string * utf8 )
{
const XMP_Uns8* latin1Ptr = (XMP_Uns8*)_latin1Ptr;
+ const XMP_Uns8* latin1End = latin1Ptr + latin1Len;
utf8->erase();
+ utf8->reserve ( latin1Len ); // As good a guess as any, exact for ASCII.
- if ( IsASCII ( latin1Ptr, latin1Len ) ) {
- utf8->assign ( (const char *)latin1Ptr, latin1Len );
- return;
- }
-
- #if XMP_WinBuild
+ for ( ; latin1Ptr < latin1End; ++latin1Ptr ) {
- WinEncodingToUTF8 ( 1252, latin1Ptr, latin1Len, utf8 );
+ XMP_Uns8 ch8 = *latin1Ptr;
- #elif XMP_MacBuild
-
- TextEncoding latin1Encoding;
- latin1Encoding = CreateTextEncoding ( kTextEncodingWindowsLatin1,
- kTextEncodingDefaultVariant, kTextEncodingDefaultFormat );
-
- MacEncodingToUTF8 ( latin1Encoding, latin1Ptr, latin1Len, utf8 );
-
- #elif XMP_UNIXBuild
+ if ( ch8 <= 0x7F ) {
+ (*utf8) += (char)ch8; // Have an ASCII character.
+ } else if ( ch8 <= 0xBF ) {
+ (*utf8) += 0xC2; // Latin-1 80..BF are UTF-8 C280..C2BF.
+ (*utf8) += (char)ch8;
+ } else {
+ (*utf8) += 0xC3; // Latin-1 C0..FF are UTF-8 C380..C3BF.
+ (*utf8) += (char)(ch8 - 0x40);
+ }
- #error "No generic UNIX implementation"
+ }
- #endif
-
} // ReconcileUtils::Latin1ToUTF8
-
-#endif
diff --git a/source/XMPFiles/FormatSupport/Reconcile_Impl.hpp b/source/XMPFiles/FormatSupport/Reconcile_Impl.hpp
index 5fe59a7..19ea865 100644
--- a/source/XMPFiles/FormatSupport/Reconcile_Impl.hpp
+++ b/source/XMPFiles/FormatSupport/Reconcile_Impl.hpp
@@ -3,7 +3,7 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2006-2007 Adobe Systems Incorporated
+// Copyright 2006 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -30,37 +30,66 @@ enum {
};
namespace ReconcileUtils {
-
+
+ // *** These ought to be with the Unicode conversions.
+
static const char * kHexDigits = "0123456789ABCDEF";
+
+ bool IsASCII ( const void * _textPtr, size_t textLen );
+ bool IsUTF8 ( const void * _textPtr, size_t textLen );
+
+ void UTF8ToLocal ( const void * _utf8Ptr, size_t utf8Len, std::string * local );
+ void UTF8ToLatin1 ( const void * _utf8Ptr, size_t utf8Len, std::string * latin1 );
+ void LocalToUTF8 ( const void * _localPtr, size_t localLen, std::string * utf8 );
+ void Latin1ToUTF8 ( const void * _latin1Ptr, size_t latin1Len, std::string * utf8 );
- bool IsUTF8 ( const void * _utf8Ptr, size_t utf8Len );
-
- #if ! XMP_UNIXBuild // Remove from generic UNIX until legacy-as-local issues are resolved.
- void UTF8ToLocal ( const void * _utf8Ptr, size_t utf8Len, std::string * local );
- void UTF8ToLatin1 ( const void * _utf8Ptr, size_t utf8Len, std::string * latin1 );
- void LocalToUTF8 ( const void * _localPtr, size_t localLen, std::string * utf8 );
- void Latin1ToUTF8 ( const void * _latin1Ptr, size_t latin1Len, std::string * utf8 );
- // *** These ought to be with the Unicode conversions.
+ #if XMP_WinBuild
+ void UTF8ToWinEncoding ( UINT codePage, const XMP_Uns8 * utf8Ptr, size_t utf8Len, std::string * host );
+ void WinEncodingToUTF8 ( UINT codePage, const XMP_Uns8 * hostPtr, size_t hostLen, std::string * utf8 );
+ #elif XMP_MacBuild
+ void UTF8ToMacEncoding ( XMP_Uns16 macScript, XMP_Uns16 macLang, const XMP_Uns8 * utf8Ptr, size_t utf8Len, std::string * host );
+ void MacEncodingToUTF8 ( XMP_Uns16 macScript, XMP_Uns16 macLang, const XMP_Uns8 * hostPtr, size_t hostLen, std::string * utf8 );
#endif
- int CheckIPTCDigest ( IPTC_Manager * iptc, const PSIR_Manager & psir );
- int CheckTIFFDigest ( const TIFF_Manager & tiff, const SXMPMeta & xmp );
- int CheckExifDigest ( const TIFF_Manager & tiff, const SXMPMeta & xmp );
+}; // ReconcileUtils
- void SetIPTCDigest ( IPTC_Manager * iptc, PSIR_Manager * psir );
- void SetTIFFDigest ( const TIFF_Manager & tiff, SXMPMeta * xmp );
- void SetExifDigest ( const TIFF_Manager & tiff, SXMPMeta * xmp );
-
- void ImportIPTC ( const IPTC_Manager & iptc, SXMPMeta * xmp, int digestState );
- void ImportPSIR ( const PSIR_Manager & psir, SXMPMeta * xmp, int digestState );
- void ImportTIFF ( const TIFF_Manager & tiff, SXMPMeta * xmp, int digestState, XMP_FileFormat srcFormat );
- void ImportExif ( const TIFF_Manager & tiff, SXMPMeta * xmp, int digestState );
+namespace PhotoDataUtils {
+
+ int CheckIPTCDigest ( const void * newPtr, const XMP_Uns32 newLen, const void * oldDigest );
+ void SetIPTCDigest ( void * iptcPtr, XMP_Uns32 iptcLen, PSIR_Manager * psir );
+
+ bool GetNativeInfo ( const TIFF_Manager & exif, XMP_Uns8 ifd, XMP_Uns16 id, TIFF_Manager::TagInfo * info );
+ size_t GetNativeInfo ( const IPTC_Manager & iptc, XMP_Uns8 id, int digestState,
+ bool haveXMP, IPTC_Manager::DataSetInfo * info );
- void ExportIPTC ( SXMPMeta * xmp, IPTC_Manager * iptc ); // ! Has XMP side effects!
+ bool IsValueDifferent ( const TIFF_Manager::TagInfo & exifInfo,
+ const std::string & xmpValue, std::string * exifValue );
+ bool IsValueDifferent ( const IPTC_Manager & newIPTC, const IPTC_Manager & oldIPTC, XMP_Uns8 id );
+
+ void ImportPSIR ( const PSIR_Manager & psir, SXMPMeta * xmp, int iptcDigestState );
+
+ void Import2WayIPTC ( const IPTC_Manager & iptc, SXMPMeta * xmp, int iptcDigestState );
+ void Import2WayExif ( const TIFF_Manager & exif, SXMPMeta * xmp, int iptcDigestState );
+
+ void Import3WayItems ( const TIFF_Manager & exif, const IPTC_Manager & iptc, SXMPMeta * xmp, int iptcDigestState );
+
void ExportPSIR ( const SXMPMeta & xmp, PSIR_Manager * psir );
- void ExportTIFF ( const SXMPMeta & xmp, TIFF_Manager * tiff );
- void ExportExif ( const SXMPMeta & xmp, TIFF_Manager * tiff );
+ void ExportIPTC ( const SXMPMeta & xmp, IPTC_Manager * iptc );
+ void ExportExif ( SXMPMeta * xmp, TIFF_Manager * exif );
+
+ // These need to be exposed for use in Import3WayItem:
-}; // ReconcileUtils
+ void ImportIPTC_Simple ( const IPTC_Manager & iptc, SXMPMeta * xmp,
+ XMP_Uns8 id, const char * xmpNS, const char * xmpProp );
+
+ void ImportIPTC_LangAlt ( const IPTC_Manager & iptc, SXMPMeta * xmp,
+ XMP_Uns8 id, const char * xmpNS, const char * xmpProp );
+
+ void ImportIPTC_Array ( const IPTC_Manager & iptc, SXMPMeta * xmp,
+ XMP_Uns8 id, const char * xmpNS, const char * xmpProp );
+
+ void ImportIPTC_Date ( XMP_Uns8 dateID, const IPTC_Manager & iptc, SXMPMeta * xmp );
+
+}; // PhotoDataUtils
#endif // __Reconcile_Impl_hpp__
diff --git a/source/XMPFiles/FormatSupport/SWF_Support.cpp b/source/XMPFiles/FormatSupport/SWF_Support.cpp
index 0a22cf0..d9b8115 100644
--- a/source/XMPFiles/FormatSupport/SWF_Support.cpp
+++ b/source/XMPFiles/FormatSupport/SWF_Support.cpp
@@ -1,6 +1,6 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2007 Adobe Systems Incorporated
+// Copyright 2007 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -16,7 +16,7 @@ namespace SWF_Support
// =============================================================================================
- int CalcHeaderSize ( IO::InputStream* inputStream )
+ static int CalcHeaderSize ( IO::InputStream* inputStream )
{
int size = 0;
@@ -44,7 +44,7 @@ namespace SWF_Support
// =============================================================================================
- unsigned long CheckTag ( IO::InputStream* inputStream, TagState& inOutTagState, TagData& inOutTagData )
+ static unsigned long CheckTag ( IO::InputStream* inputStream, TagState& inOutTagState, TagData& inOutTagData )
{
unsigned long ret = 0;
XMP_Uns8 * buffer = 0;
diff --git a/source/XMPFiles/FormatSupport/SWF_Support.hpp b/source/XMPFiles/FormatSupport/SWF_Support.hpp
index ca301b9..db20ab2 100644
--- a/source/XMPFiles/FormatSupport/SWF_Support.hpp
+++ b/source/XMPFiles/FormatSupport/SWF_Support.hpp
@@ -3,7 +3,7 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2008 Adobe Systems Incorporated
+// Copyright 2008 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
diff --git a/source/XMPFiles/FormatSupport/TIFF_FileWriter.cpp b/source/XMPFiles/FormatSupport/TIFF_FileWriter.cpp
index 05372b8..3eb6eb2 100644
--- a/source/XMPFiles/FormatSupport/TIFF_FileWriter.cpp
+++ b/source/XMPFiles/FormatSupport/TIFF_FileWriter.cpp
@@ -1,6 +1,6 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2006-2008 Adobe Systems Incorporated
+// Copyright 2006 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -33,15 +33,15 @@ TIFF_FileWriter::TIFF_FileWriter() : changed(false), legacyDeleted(false), memPa
{
XMP_Uns8 bogusTIFF [kEmptyTIFFLength];
-
+
bogusTIFF[0] = 0x4D;
bogusTIFF[1] = 0x4D;
bogusTIFF[2] = 0x00;
bogusTIFF[3] = 0x2A;
bogusTIFF[4] = bogusTIFF[5] = bogusTIFF[6] = bogusTIFF[7] = 0x00;
-
+
(void) this->CheckTIFFHeader ( bogusTIFF, sizeof ( bogusTIFF ) );
-
+
} // TIFF_FileWriter::TIFF_FileWriter
// =================================================================================================
@@ -52,7 +52,6 @@ TIFF_FileWriter::~TIFF_FileWriter()
{
XMP_Assert ( ! (this->memParsed && this->fileParsed) );
- if ( this->fileParsed && (this->jpegTNailPtr != 0) ) free ( this->jpegTNailPtr );
if ( this->ownedStream ) {
XMP_Assert ( this->memStream != 0 );
free ( this->memStream );
@@ -117,17 +116,17 @@ const TIFF_FileWriter::InternalTagInfo* TIFF_FileWriter::FindTagInIFD ( XMP_Uns8
// TIFF_FileWriter::GetIFD
// =======================
-bool TIFF_FileWriter::GetIFD ( XMP_Uns8 ifd, TagInfoMap* ifdMap ) const
+bool TIFF_FileWriter::GetIFD ( XMP_Uns8 ifd, TagInfoMap* ifdMap ) const
{
if ( ifd > kTIFF_LastRealIFD ) XMP_Throw ( "Invalid IFD number", kXMPErr_BadParam );
const InternalTagMap& currIFD = this->containedIFDs[ifd].tagMap;
InternalTagMap::const_iterator tagPos = currIFD.begin();
InternalTagMap::const_iterator tagEnd = currIFD.end();
-
+
if ( ifdMap != 0 ) ifdMap->clear();
if ( tagPos == tagEnd ) return false; // Empty IFD.
-
+
if ( ifdMap != 0 ) {
for ( ; tagPos != tagEnd; ++tagPos ) {
const InternalTagInfo& intInfo = tagPos->second;
@@ -135,7 +134,7 @@ bool TIFF_FileWriter::GetIFD ( XMP_Uns8 ifd, TagInfoMap* ifdMap ) const
(*ifdMap)[intInfo.id] = extInfo;
}
}
-
+
return true;
} // TIFF_FileWriter::GetIFD
@@ -148,20 +147,20 @@ XMP_Uns32 TIFF_FileWriter::GetValueOffset ( XMP_Uns8 ifd, XMP_Uns16 id ) const
{
const InternalTagInfo* thisTag = this->FindTagInIFD ( ifd, id );
if ( (thisTag == 0) || (thisTag->origDataLen == 0) ) return 0;
-
+
return thisTag->origDataOffset;
-
+
} // TIFF_FileWriter::GetValueOffset
// =================================================================================================
// TIFF_FileWriter::GetTag
// =======================
-bool TIFF_FileWriter::GetTag ( XMP_Uns8 ifd, XMP_Uns16 id, TagInfo* info ) const
+bool TIFF_FileWriter::GetTag ( XMP_Uns8 ifd, XMP_Uns16 id, TagInfo* info ) const
{
const InternalTagInfo* thisTag = this->FindTagInIFD ( ifd, id );
if ( thisTag == 0 ) return false;
-
+
if ( info != 0 ) {
info->id = thisTag->id;
@@ -171,21 +170,21 @@ bool TIFF_FileWriter::GetTag ( XMP_Uns8 ifd, XMP_Uns16 id, TagInfo* info ) const
info->dataPtr = (const void*)(thisTag->dataPtr);
}
-
+
return true;
-
+
} // TIFF_FileWriter::GetTag
// =================================================================================================
// TIFF_FileWriter::SetTag
// =======================
-void TIFF_FileWriter::SetTag ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns16 type, XMP_Uns32 count, const void* clientPtr )
+void TIFF_FileWriter::SetTag ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns16 type, XMP_Uns32 count, const void* clientPtr )
{
if ( (type < kTIFF_ByteType) || (type > kTIFF_LastType) ) XMP_Throw ( "Invalid TIFF tag type", kXMPErr_BadParam );
size_t typeSize = kTIFF_TypeSizes[type];
size_t fullSize = count * typeSize;
-
+
ifd = PickIFD ( ifd, id );
InternalTagMap& currIFD = this->containedIFDs[ifd].tagMap;
@@ -210,7 +209,7 @@ void TIFF_FileWriter::SetTag ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns16 type, XMP_U
}
tagPtr->FreeData(); // Release any existing data allocation.
-
+
tagPtr->type = type; // These might be changing also.
tagPtr->count = count;
@@ -218,7 +217,7 @@ void TIFF_FileWriter::SetTag ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns16 type, XMP_U
tagPtr->changed = true;
tagPtr->dataLen = (XMP_Uns32)fullSize;
-
+
if ( fullSize <= 4 ) {
// The data is less than 4 bytes, store it in the smallValue field using native endianness.
tagPtr->dataPtr = (XMP_Uns8*) &tagPtr->smallValue;
@@ -228,7 +227,7 @@ void TIFF_FileWriter::SetTag ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns16 type, XMP_U
if ( tagPtr->dataPtr == 0 ) XMP_Throw ( "Out of memory", kXMPErr_NoMemory );
}
memcpy ( tagPtr->dataPtr, clientPtr, fullSize ); // AUDIT: Safe, space guaranteed to be fullSize.
-
+
if ( ! this->nativeEndian ) {
if ( typeSize == 2 ) {
XMP_Uns16* flipPtr = (XMP_Uns16*) tagPtr->dataPtr;
@@ -241,7 +240,7 @@ void TIFF_FileWriter::SetTag ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns16 type, XMP_U
for ( XMP_Uns32 i = 0; i < count; ++i ) Flip8 ( flipPtr[i] );
}
}
-
+
this->containedIFDs[ifd].changed = true;
this->changed = true;
@@ -251,11 +250,11 @@ void TIFF_FileWriter::SetTag ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns16 type, XMP_U
// TIFF_FileWriter::DeleteTag
// ==========================
-void TIFF_FileWriter::DeleteTag ( XMP_Uns8 ifd, XMP_Uns16 id )
+void TIFF_FileWriter::DeleteTag ( XMP_Uns8 ifd, XMP_Uns16 id )
{
ifd = PickIFD ( ifd, id );
InternalTagMap& currIFD = this->containedIFDs[ifd].tagMap;
-
+
InternalTagMap::iterator tagPos = currIFD.find ( id );
if ( tagPos == currIFD.end() ) return; // ! Don't set the changed flags if the tag didn't exist.
@@ -270,15 +269,15 @@ void TIFF_FileWriter::DeleteTag ( XMP_Uns8 ifd, XMP_Uns16 id )
// TIFF_FileWriter::GetTag_Integer
// ===============================
-bool TIFF_FileWriter::GetTag_Integer ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns32* data ) const
+bool TIFF_FileWriter::GetTag_Integer ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns32* data ) const
{
const InternalTagInfo* thisTag = this->FindTagInIFD ( ifd, id );
if ( thisTag == 0 ) return false;
if ( thisTag->count != 1 ) return false;
-
+
static XMP_Uns32 voidValue;
if ( data == 0 ) data = &voidValue;
-
+
if ( thisTag->type == kTIFF_ShortType ) {
*data = this->GetUns16 ( thisTag->dataPtr );
} else if ( thisTag->type == kTIFF_LongType ) {
@@ -286,7 +285,7 @@ bool TIFF_FileWriter::GetTag_Integer ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns32* da
} else {
return false;
}
-
+
return true;
} // TIFF_FileWriter::GetTag_Integer
@@ -300,7 +299,7 @@ bool TIFF_FileWriter::GetTag_Byte ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns8* data )
const InternalTagInfo* thisTag = this->FindTagInIFD ( ifd, id );
if ( thisTag == 0 ) return false;
if ( (thisTag->type != kTIFF_ByteType) || (thisTag->dataLen != 1) ) return false;
-
+
if ( data != 0 ) *data = *thisTag->dataPtr;
return true;
@@ -315,7 +314,7 @@ bool TIFF_FileWriter::GetTag_SByte ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Int8* data
const InternalTagInfo* thisTag = this->FindTagInIFD ( ifd, id );
if ( thisTag == 0 ) return false;
if ( (thisTag->type != kTIFF_SByteType) || (thisTag->dataLen != 1) ) return false;
-
+
if ( data != 0 ) *data = *thisTag->dataPtr;
return true;
@@ -330,7 +329,7 @@ bool TIFF_FileWriter::GetTag_Short ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns16* data
const InternalTagInfo* thisTag = this->FindTagInIFD ( ifd, id );
if ( thisTag == 0 ) return false;
if ( (thisTag->type != kTIFF_ShortType) || (thisTag->dataLen != 2) ) return false;
-
+
if ( data != 0 ) *data = this->GetUns16 ( thisTag->dataPtr );
return true;
@@ -345,7 +344,7 @@ bool TIFF_FileWriter::GetTag_SShort ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Int16* dat
const InternalTagInfo* thisTag = this->FindTagInIFD ( ifd, id );
if ( thisTag == 0 ) return false;
if ( (thisTag->type != kTIFF_SShortType) || (thisTag->dataLen != 2) ) return false;
-
+
if ( data != 0 ) *data = (XMP_Int16) this->GetUns16 ( thisTag->dataPtr );
return true;
@@ -360,7 +359,7 @@ bool TIFF_FileWriter::GetTag_Long ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns32* data
const InternalTagInfo* thisTag = this->FindTagInIFD ( ifd, id );
if ( thisTag == 0 ) return false;
if ( (thisTag->type != kTIFF_LongType) || (thisTag->dataLen != 4) ) return false;
-
+
if ( data != 0 ) *data = this->GetUns32 ( thisTag->dataPtr );
return true;
@@ -375,7 +374,7 @@ bool TIFF_FileWriter::GetTag_SLong ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Int32* data
const InternalTagInfo* thisTag = this->FindTagInIFD ( ifd, id );
if ( thisTag == 0 ) return false;
if ( (thisTag->type != kTIFF_SLongType) || (thisTag->dataLen != 4) ) return false;
-
+
if ( data != 0 ) *data = (XMP_Int32) this->GetUns32 ( thisTag->dataPtr );
return true;
@@ -390,13 +389,13 @@ bool TIFF_FileWriter::GetTag_Rational ( XMP_Uns8 ifd, XMP_Uns16 id, Rational* da
const InternalTagInfo* thisTag = this->FindTagInIFD ( ifd, id );
if ( (thisTag == 0) || (thisTag->dataPtr == 0) ) return false;
if ( (thisTag->type != kTIFF_RationalType) || (thisTag->dataLen != 8) ) return false;
-
+
if ( data != 0 ) {
XMP_Uns32* dataPtr = (XMP_Uns32*)thisTag->dataPtr;
data->num = this->GetUns32 ( dataPtr );
data->denom = this->GetUns32 ( dataPtr+1 );
}
-
+
return true;
} // TIFF_FileWriter::GetTag_Rational
@@ -410,13 +409,13 @@ bool TIFF_FileWriter::GetTag_SRational ( XMP_Uns8 ifd, XMP_Uns16 id, SRational*
const InternalTagInfo* thisTag = this->FindTagInIFD ( ifd, id );
if ( (thisTag == 0) || (thisTag->dataPtr == 0) ) return false;
if ( (thisTag->type != kTIFF_SRationalType) || (thisTag->dataLen != 8) ) return false;
-
+
if ( data != 0 ) {
XMP_Uns32* dataPtr = (XMP_Uns32*)thisTag->dataPtr;
data->num = (XMP_Int32) this->GetUns32 ( dataPtr );
data->denom = (XMP_Int32) this->GetUns32 ( dataPtr+1 );
}
-
+
return true;
} // TIFF_FileWriter::GetTag_SRational
@@ -430,8 +429,8 @@ bool TIFF_FileWriter::GetTag_Float ( XMP_Uns8 ifd, XMP_Uns16 id, float* data ) c
const InternalTagInfo* thisTag = this->FindTagInIFD ( ifd, id );
if ( thisTag == 0 ) return false;
if ( (thisTag->type != kTIFF_FloatType) || (thisTag->dataLen != 4) ) return false;
-
- if ( data != 0 ) *data = this->GetFloat ( thisTag->dataPtr );
+
+ if ( data != 0 ) *data = this->GetFloat ( thisTag->dataPtr );
return true;
} // TIFF_FileWriter::GetTag_Float
@@ -445,8 +444,8 @@ bool TIFF_FileWriter::GetTag_Double ( XMP_Uns8 ifd, XMP_Uns16 id, double* data )
const InternalTagInfo* thisTag = this->FindTagInIFD ( ifd, id );
if ( (thisTag == 0) || (thisTag->dataPtr == 0) ) return false;
if ( (thisTag->type != kTIFF_DoubleType) || (thisTag->dataLen != 8) ) return false;
-
- if ( data != 0 ) *data = this->GetDouble ( thisTag->dataPtr );
+
+ if ( data != 0 ) *data = this->GetDouble ( thisTag->dataPtr );
return true;
} // TIFF_FileWriter::GetTag_Double
@@ -461,10 +460,10 @@ bool TIFF_FileWriter::GetTag_ASCII ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_StringPtr*
if ( thisTag == 0 ) return false;
if ( (thisTag->dataLen > 4) && (thisTag->dataPtr == 0) ) return false;
if ( thisTag->type != kTIFF_ASCIIType ) return false;
-
+
if ( dataPtr != 0 ) *dataPtr = (XMP_StringPtr)thisTag->dataPtr;
if ( dataLen != 0 ) *dataLen = thisTag->dataLen;
-
+
return true;
} // TIFF_FileWriter::GetTag_ASCII
@@ -478,9 +477,9 @@ bool TIFF_FileWriter::GetTag_EncodedString ( XMP_Uns8 ifd, XMP_Uns16 id, std::st
const InternalTagInfo* thisTag = this->FindTagInIFD ( ifd, id );
if ( thisTag == 0 ) return false;
if ( thisTag->type != kTIFF_UndefinedType ) return false;
-
+
if ( utf8Str == 0 ) return true; // Return true if the converted string is not wanted.
-
+
bool ok = this->DecodeString ( thisTag->dataPtr, thisTag->dataLen, utf8Str );
return ok;
@@ -492,8 +491,10 @@ bool TIFF_FileWriter::GetTag_EncodedString ( XMP_Uns8 ifd, XMP_Uns16 id, std::st
void TIFF_FileWriter::SetTag_EncodedString ( XMP_Uns8 ifd, XMP_Uns16 id, const std::string& utf8Str, XMP_Uns8 encoding )
{
+ std::string encodedStr;
- XMP_Throw ( "Not yet implemented", kXMPErr_Unimplemented );
+ this->EncodeString ( utf8Str, encoding, &encodedStr );
+ this->SetTag ( ifd, id, kTIFF_UndefinedType, (XMP_Uns32)encodedStr.size(), encodedStr.c_str() );
} // TIFF_FileWriter::SetTag_EncodedString
@@ -506,22 +507,22 @@ bool TIFF_FileWriter::IsLegacyChanged()
if ( ! this->changed ) return false;
if ( this->legacyDeleted ) return true;
-
+
for ( int ifd = 0; ifd < kTIFF_KnownIFDCount; ++ifd ) {
InternalIFDInfo & thisIFD = this->containedIFDs[ifd];
if ( ! thisIFD.changed ) continue;
-
+
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.id != kTIFF_XMP) ) return true;
}
}
-
+
return false; // Can get here if the XMP tag is the only one changed.
} // TIFF_FileWriter::IsLegacyChanged
@@ -530,14 +531,14 @@ bool TIFF_FileWriter::IsLegacyChanged()
// TIFF_FileWriter::ParseMemoryStream
// ==================================
-void TIFF_FileWriter::ParseMemoryStream ( const void* data, XMP_Uns32 length, bool copyData /* = true */ )
+void TIFF_FileWriter::ParseMemoryStream ( const void* data, XMP_Uns32 length, bool copyData /* = true */ )
{
this->DeleteExistingInfo();
this->memParsed = true;
if ( length == 0 ) return;
// Allocate space for the full in-memory stream and copy it.
-
+
if ( ! copyData ) {
XMP_Assert ( ! this->ownedStream );
this->memStream = (XMP_Uns8*) data;
@@ -551,11 +552,10 @@ void TIFF_FileWriter::ParseMemoryStream ( const void* data, XMP_Uns32 length, bo
this->tiffLength = length;
// Find and process the primary, Exif, GPS, and Interoperability IFDs.
-
+
XMP_Uns32 primaryIFDOffset = this->CheckTIFFHeader ( this->memStream, length );
- XMP_Uns32 tnailIFDOffset = 0;
-
- if ( primaryIFDOffset != 0 ) tnailIFDOffset = this->ProcessMemoryIFD ( primaryIFDOffset, kTIFF_PrimaryIFD );
+
+ if ( primaryIFDOffset != 0 ) (void) this->ProcessMemoryIFD ( primaryIFDOffset, kTIFF_PrimaryIFD );
const InternalTagInfo* exifIFDTag = this->FindTagInIFD ( kTIFF_PrimaryIFD, kTIFF_ExifIFDPointer );
if ( (exifIFDTag != 0) && (exifIFDTag->type == kTIFF_LongType) && (exifIFDTag->dataLen == 4) ) {
@@ -574,19 +574,7 @@ void TIFF_FileWriter::ParseMemoryStream ( const void* data, XMP_Uns32 length, bo
XMP_Uns32 interopOffset = this->GetUns32 ( interopIFDTag->dataPtr );
(void) this->ProcessMemoryIFD ( interopOffset, kTIFF_InteropIFD );
}
-
- // Process the thumbnail IFD. We only do this for Exif-compliant TIFF streams. Extract the
- // JPEG thumbnail image pointer (tag 513) for later use by GetTNailInfo.
-
- if ( (tnailIFDOffset != 0) && (! this->containedIFDs[kTIFF_ExifIFD].tagMap.empty()) ) {
- (void) this->ProcessMemoryIFD ( tnailIFDOffset, kTIFF_TNailIFD );
- const InternalTagInfo* jpegInfo = FindTagInIFD ( kTIFF_TNailIFD, kTIFF_JPEGInterchangeFormat );
- if ( jpegInfo != 0 ) {
- XMP_Uns32 tnailImageOffset = this->GetUns32 ( jpegInfo->dataPtr );
- this->jpegTNailPtr = (XMP_Uns8*)this->memStream + tnailImageOffset;
- }
- }
-
+
#if 0
{
printf ( "\nExiting TIFF_FileWriter::ParseMemoryStream\n" );
@@ -616,27 +604,27 @@ void TIFF_FileWriter::ParseMemoryStream ( const void* data, XMP_Uns32 length, bo
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_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 ( (ifdOffset + 2 + tagCount*12 + 4) > this->tiffLength ) XMP_Throw ( "Out of bounds IFD", kXMPErr_BadTIFF );
-
+
ifdInfo.origIFDOffset = ifdOffset;
ifdInfo.origCount = tagCount;
-
+
for ( size_t i = 0; i < tagCount; ++i ) {
-
+
RawIFDEntry* rawTag = &ifdEntries[i];
XMP_Uns16 tagType = this->GetUns16 ( &rawTag->type );
if ( (tagType < kTIFF_ByteType) || (tagType > kTIFF_LastType) ) continue; // Bad type, skip this tag.
-
+
XMP_Uns16 tagID = this->GetUns16 ( &rawTag->id );
XMP_Uns32 tagCount = this->GetUns32 ( &rawTag->count );
@@ -654,64 +642,17 @@ XMP_Uns32 TIFF_FileWriter::ProcessMemoryIFD ( XMP_Uns32 ifdOffset, XMP_Uns8 ifd
// printf ( "FW_ProcessMemoryIFD tag %d large value @ %.8X\n", mapTag.id, mapTag.dataPtr );
}
mapTag.dataPtr = this->memStream + mapTag.origDataOffset;
-
+
}
-
+
ifdPtr += (2 + tagCount*12);
ifdInfo.origNextIFD = this->GetUns32 ( ifdPtr );
-
+
return ifdInfo.origNextIFD;
} // TIFF_FileWriter::ProcessMemoryIFD
// =================================================================================================
-// CaptureJPEGTNail
-// ================
-//
-// Capture the JPEG image stream for an Exif compressed thumbnail.
-
-static XMP_Uns8* CaptureJPEGTNail ( LFA_FileRef fileRef, IOBuffer* ioBuf, const TIFF_Manager& tiff )
-{
- bool ok;
- XMP_Uns8* jpegPtr = 0;
- XMP_Uns32 jpegOffset, jpegLen;
-
- ok = tiff.GetTag_Integer ( kTIFF_TNailIFD, kTIFF_JPEGInterchangeFormat, &jpegOffset );
- if ( ok ) ok = tiff.GetTag_Integer ( kTIFF_TNailIFD, kTIFF_JPEGInterchangeFormatLength, &jpegLen );
- if ( ! ok ) return 0;
-
- if ( jpegLen > 1024*1024 ) return 0; // ? XMP_Throw ( "Outrageous JPEG TNail length", kXMPErr_BadTIFF );
-
- jpegPtr = (XMP_Uns8*) malloc ( jpegLen );
- if ( jpegPtr == 0 ) XMP_Throw ( "Out of memory", kXMPErr_NoMemory );
-
- try {
-
- if ( jpegLen > kIOBufferSize ) {
- // This value is bigger than the I/O buffer, read it directly and restore the file position.
- LFA_Seek ( fileRef, jpegOffset, SEEK_SET );
- LFA_Read ( fileRef, jpegPtr, jpegLen, kLFA_RequireAll );
- LFA_Seek ( fileRef, (ioBuf->filePos + ioBuf->len), SEEK_SET );
- } else {
- // This value can fit in the I/O buffer, so use that.
- MoveToOffset ( fileRef, jpegOffset, ioBuf );
- ok = CheckFileSpace ( fileRef, ioBuf, jpegLen );
- if ( ! ok ) XMP_Throw ( "EOF in data block", kXMPErr_BadTIFF );
- memcpy ( jpegPtr, ioBuf->ptr, jpegLen ); // AUDIT: Safe, malloc'ed jpegLen bytes above.
- }
-
- } catch ( ... ) {
-
- free ( jpegPtr );
- throw;
-
- }
-
- return jpegPtr;
-
-} // CaptureJPEGTNail
-
-// =================================================================================================
// TIFF_FileWriter::ParseFileStream
// ================================
//
@@ -720,7 +661,7 @@ static XMP_Uns8* CaptureJPEGTNail ( LFA_FileRef fileRef, IOBuffer* ioBuf, const
// and all of their interesting tag values within the first 64K of the file. Well, at least before
// we get around to our edit-by-append approach.
-void TIFF_FileWriter::ParseFileStream ( LFA_FileRef fileRef )
+void TIFF_FileWriter::ParseFileStream ( LFA_FileRef fileRef )
{
bool ok;
IOBuffer ioBuf;
@@ -729,17 +670,16 @@ void TIFF_FileWriter::ParseFileStream ( LFA_FileRef fileRef )
this->fileParsed = true;
this->tiffLength = (XMP_Uns32) LFA_Measure ( fileRef );
if ( this->tiffLength == 0 ) return;
-
+
// Find and process the primary, Exif, GPS, and Interoperability IFDs.
-
+
ioBuf.filePos = LFA_Seek ( fileRef, 0, SEEK_SET );
ok = CheckFileSpace ( fileRef, &ioBuf, 8 );
if ( ! ok ) XMP_Throw ( "TIFF too small", kXMPErr_BadTIFF );
-
+
XMP_Uns32 primaryIFDOffset = this->CheckTIFFHeader ( ioBuf.ptr, this->tiffLength );
- XMP_Uns32 tnailIFDOffset = 0;
-
- if ( primaryIFDOffset != 0 ) tnailIFDOffset = this->ProcessFileIFD ( kTIFF_PrimaryIFD, primaryIFDOffset, fileRef, &ioBuf );
+
+ if ( primaryIFDOffset != 0 ) (void) this->ProcessFileIFD ( kTIFF_PrimaryIFD, primaryIFDOffset, fileRef, &ioBuf );
const InternalTagInfo* exifIFDTag = this->FindTagInIFD ( kTIFF_PrimaryIFD, kTIFF_ExifIFDPointer );
if ( (exifIFDTag != 0) && (exifIFDTag->type == kTIFF_LongType) && (exifIFDTag->count == 1) ) {
@@ -758,15 +698,7 @@ void TIFF_FileWriter::ParseFileStream ( LFA_FileRef fileRef )
XMP_Uns32 interopOffset = this->GetUns32 ( interopIFDTag->dataPtr );
(void) this->ProcessFileIFD ( kTIFF_InteropIFD, interopOffset, fileRef, &ioBuf );
}
-
- // Process the thumbnail IFD. We only do this for Exif-compliant TIFF streams. Do this after
- // the others since they are often within the first 64K of the file and the thumbnail is not.
- if ( (tnailIFDOffset != 0) && (! this->containedIFDs[kTIFF_ExifIFD].tagMap.empty()) ) {
- (void) this->ProcessFileIFD ( kTIFF_TNailIFD, tnailIFDOffset, fileRef, &ioBuf );
- this->jpegTNailPtr = CaptureJPEGTNail ( fileRef, &ioBuf, *this );
- }
-
#if 0
{
printf ( "\nExiting TIFF_FileWriter::ParseFileStream\n" );
@@ -793,22 +725,22 @@ void TIFF_FileWriter::ParseFileStream ( LFA_FileRef fileRef )
// TIFF_FileWriter::ProcessFileIFD
// ===============================
-XMP_Uns32 TIFF_FileWriter::ProcessFileIFD ( XMP_Uns8 ifd, XMP_Uns32 ifdOffset, LFA_FileRef fileRef, IOBuffer* ioBuf )
+XMP_Uns32 TIFF_FileWriter::ProcessFileIFD ( XMP_Uns8 ifd, XMP_Uns32 ifdOffset, LFA_FileRef fileRef, IOBuffer* ioBuf )
{
InternalIFDInfo& ifdInfo ( this->containedIFDs[ifd] );
-
+
MoveToOffset ( fileRef, ifdOffset, ioBuf ); // Move to the start of the IFD.
-
+
bool ok = CheckFileSpace ( fileRef, ioBuf, 2 );
if ( ! ok ) XMP_Throw ( "IFD count missing", kXMPErr_BadTIFF );
XMP_Uns16 tagCount = this->GetUns16 ( ioBuf->ptr );
if ( tagCount >= 0x8000 ) XMP_Throw ( "Outrageous IFD count", kXMPErr_BadTIFF );
if ( (ifdOffset + 2 + tagCount*12 + 4) > this->tiffLength ) XMP_Throw ( "Out of bounds IFD", kXMPErr_BadTIFF );
-
+
ifdInfo.origIFDOffset = ifdOffset;
ifdInfo.origCount = tagCount;
-
+
// ---------------------------------------------------------------------------------------------
// First create all of the IFD map entries, capturing short values, and get the next IFD offset.
// We're using a std::map for storage, it automatically eliminates duplicates and provides
@@ -816,15 +748,15 @@ XMP_Uns32 TIFF_FileWriter::ProcessFileIFD ( XMP_Uns8 ifd, XMP_Uns32 ifdOffset, L
// value, following Photoshop's behavior.
ioBuf->ptr += 2; // Move to the first IFD entry.
-
+
for ( XMP_Uns16 i = 0; i < tagCount; ++i, ioBuf->ptr += 12 ) {
-
+
if ( ! CheckFileSpace ( fileRef, ioBuf, 12 ) ) XMP_Throw ( "EOF within IFD", kXMPErr_BadTIFF );
-
+
RawIFDEntry* rawTag = (RawIFDEntry*)ioBuf->ptr;
XMP_Uns16 tagType = this->GetUns16 ( &rawTag->type );
if ( (tagType < kTIFF_ByteType) || (tagType > kTIFF_LastType) ) continue; // Bad type, skip this tag.
-
+
XMP_Uns16 tagID = this->GetUns16 ( &rawTag->id );
XMP_Uns32 tagCount = this->GetUns32 ( &rawTag->count );
@@ -841,37 +773,37 @@ XMP_Uns32 TIFF_FileWriter::ProcessFileIFD ( XMP_Uns8 ifd, XMP_Uns32 ifdOffset, L
} else {
mapTag.origDataOffset = this->GetUns32 ( &rawTag->dataOrOffset ); // Extract the data offset.
}
-
+
}
-
+
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 );
-
+
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;
if ( currTag->dataLen <= 4 ) continue; // Short values are already in the smallValue field.
while ( *knownTagPtr < currTag->id ) ++knownTagPtr;
if ( *knownTagPtr != currTag->id ) continue; // Skip unrecognized tags.
if ( currTag->dataLen > 1024*1024 ) XMP_Throw ( "Outrageous data length", kXMPErr_BadTIFF );
-
+
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 );
@@ -879,18 +811,18 @@ XMP_Uns32 TIFF_FileWriter::ProcessFileIFD ( XMP_Uns8 ifd, XMP_Uns32 ifdOffset, L
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?
@@ -913,11 +845,11 @@ XMP_Uns32 TIFF_FileWriter::ProcessFileIFD ( XMP_Uns8 ifd, XMP_Uns32 ifdOffset, L
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.
}
-
+
}
-
+
// Done, return the next IFD offset.
-
+
return ifdInfo.origNextIFD;
} // TIFF_FileWriter::ProcessFileIFD
@@ -928,13 +860,12 @@ XMP_Uns32 TIFF_FileWriter::ProcessFileIFD ( XMP_Uns8 ifd, XMP_Uns32 ifdOffset, L
//
// See comments for ProcessPShop6IFD.
-void TIFF_FileWriter::IntegrateFromPShop6 ( const void * buriedPtr, size_t buriedLen )
+void TIFF_FileWriter::IntegrateFromPShop6 ( const void * buriedPtr, size_t buriedLen )
{
TIFF_MemoryReader buriedExif;
buriedExif.ParseMemoryStream ( buriedPtr, (XMP_Uns32) buriedLen );
-
+
this->ProcessPShop6IFD ( buriedExif, kTIFF_PrimaryIFD );
- this->ProcessPShop6IFD ( buriedExif, kTIFF_TNailIFD );
this->ProcessPShop6IFD ( buriedExif, kTIFF_ExifIFD );
this->ProcessPShop6IFD ( buriedExif, kTIFF_GPSInfoIFD );
@@ -955,7 +886,7 @@ void* TIFF_FileWriter::CopyTagToMasterIFD ( const TagInfo & ps6Tag, InternalIFDI
InternalTagInfo& newTag = newPos->second;
newTag.dataLen = ps6Tag.dataLen;
-
+
if ( newTag.dataLen <= 4 ) {
newTag.dataPtr = (XMP_Uns8*) &newTag.smallValue;
newTag.smallValue = *((XMP_Uns32*)ps6Tag.dataPtr);
@@ -967,9 +898,9 @@ void* TIFF_FileWriter::CopyTagToMasterIFD ( const TagInfo & ps6Tag, InternalIFDI
newTag.changed = true; // ! See comments with ProcessPShop6IFD.
XMP_Assert ( (newTag.origDataLen == 0) && (newTag.origDataOffset == 0) );
-
+
masterIFD->changed = true;
-
+
return newPos->second.dataPtr; // ! Return the address within the map entry for small values.
} // TIFF_FileWriter::CopyTagToMasterIFD
@@ -983,17 +914,17 @@ void* TIFF_FileWriter::CopyTagToMasterIFD ( const TagInfo & ps6Tag, InternalIFDI
static bool FlipCFATable ( void* voidPtr, XMP_Uns32 tagLen, GetUns16_Proc GetUns16 )
{
if ( tagLen < 4 ) return false;
-
+
XMP_Uns16* u16Ptr = (XMP_Uns16*)voidPtr;
Flip2 ( &u16Ptr[0] ); // Flip the counts to match the master TIFF.
Flip2 ( &u16Ptr[1] );
-
+
XMP_Uns16 columns = GetUns16 ( &u16Ptr[0] ); // Fetch using the master TIFF's routine.
XMP_Uns16 rows = GetUns16 ( &u16Ptr[1] );
-
+
if ( tagLen != (XMP_Uns32)(4 + columns*rows) ) return false;
-
+
return true;
} // FlipCFATable
@@ -1010,12 +941,12 @@ static bool FlipCFATable ( void* voidPtr, XMP_Uns32 tagLen, GetUns16_Proc GetUns
static bool FlipDSDTable ( void* voidPtr, XMP_Uns32 tagLen, GetUns16_Proc GetUns16 )
{
if ( tagLen < 4 ) return false;
-
+
XMP_Uns16* u16Ptr = (XMP_Uns16*)voidPtr;
for ( size_t i = tagLen/2; i > 0; --i, ++u16Ptr ) Flip2 ( u16Ptr );
-
+
return true;
-
+
} // FlipDSDTable
// =================================================================================================
@@ -1033,20 +964,20 @@ static bool FlipOECFSFRTable ( void* voidPtr, XMP_Uns32 tagLen, GetUns16_Proc Ge
Flip2 ( &u16Ptr[0] ); // Flip the data to match the master TIFF.
Flip2 ( &u16Ptr[1] );
-
+
XMP_Uns16 columns = GetUns16 ( &u16Ptr[0] ); // Fetch using the master TIFF's routine.
XMP_Uns16 rows = GetUns16 ( &u16Ptr[1] );
-
+
XMP_Uns32 minLen = 4 + columns + (8 * columns * rows); // Minimum legit tag size.
if ( tagLen < minLen ) return false;
-
+
// Compute the start of the rationals from the end of value. No need to walk through the names.
XMP_Uns32* u32Ptr = (XMP_Uns32*) ((XMP_Uns8*)voidPtr + tagLen - (8 * columns * rows));
for ( size_t i = 2*columns*rows; i > 0; --i, ++u32Ptr ) Flip4 ( u32Ptr );
-
+
return true;
-
+
} // FlipOECFSFRTable
// =================================================================================================
@@ -1058,7 +989,7 @@ static bool FlipOECFSFRTable ( void* voidPtr, XMP_Uns32 tagLen, GetUns16_Proc Ge
// tags up to the parent file. Existing tags are not replaced.
//
// While it is tempting to try to directly use the TIFF_MemoryReader's tweaked IFD info, making that
-// visible would compromise implementation separation. Better to pay the modest runtime cost of
+// visible would compromise implementation separation. Better to pay the modest runtime cost of
// using the official GetIFD method, letting it build the map.
//
// The tags that get moved are marked as being changed, as is the IFD they are moved into, but the
@@ -1075,41 +1006,41 @@ void TIFF_FileWriter::ProcessPShop6IFD ( const TIFF_MemoryReader& buriedExif, XM
{
bool ok, found;
TagInfoMap ps6IFD;
-
+
found = buriedExif.GetIFD ( ifd, &ps6IFD );
if ( ! found ) return;
-
+
bool needsFlipping = (this->bigEndian != buriedExif.IsBigEndian());
-
+
InternalIFDInfo* masterIFD = &this->containedIFDs[ifd];
-
+
TagInfoMap::const_iterator ps6Pos = ps6IFD.begin();
TagInfoMap::const_iterator ps6End = ps6IFD.end();
-
+
for ( ; ps6Pos != ps6End; ++ps6Pos ) {
-
+
// Copy buried tags to the master IFD if they don't already exist there.
-
+
const TagInfo& ps6Tag = ps6Pos->second;
-
+
if ( this->FindTagInIFD ( ifd, ps6Tag.id ) != 0 ) continue; // Keep existing master tags.
if ( needsFlipping && (ps6Tag.id == 37500) ) continue; // Don't copy an unflipped MakerNote.
if ( (ps6Tag.id == kTIFF_ExifIFDPointer) || // Skip the tags that are explicit offsets.
(ps6Tag.id == kTIFF_GPSInfoIFDPointer) ||
(ps6Tag.id == kTIFF_JPEGInterchangeFormat) ||
(ps6Tag.id == kTIFF_InteroperabilityIFDPointer) ) continue;
-
+
void* voidPtr = this->CopyTagToMasterIFD ( ps6Tag, masterIFD );
-
+
if ( needsFlipping ) {
switch ( ps6Tag.type ) {
-
+
case kTIFF_ByteType:
case kTIFF_SByteType:
case kTIFF_ASCIIType:
// Nothing more to do.
break;
-
+
case kTIFF_ShortType:
case kTIFF_SShortType:
{
@@ -1117,7 +1048,7 @@ void TIFF_FileWriter::ProcessPShop6IFD ( const TIFF_MemoryReader& buriedExif, XM
for ( size_t i = ps6Tag.count; i > 0; --i, ++u16Ptr ) Flip2 ( u16Ptr );
}
break;
-
+
case kTIFF_LongType:
case kTIFF_SLongType:
case kTIFF_FloatType:
@@ -1126,7 +1057,7 @@ void TIFF_FileWriter::ProcessPShop6IFD ( const TIFF_MemoryReader& buriedExif, XM
for ( size_t i = ps6Tag.count; i > 0; --i, ++u32Ptr ) Flip4 ( u32Ptr );
}
break;
-
+
case kTIFF_RationalType:
case kTIFF_SRationalType:
{
@@ -1134,14 +1065,14 @@ void TIFF_FileWriter::ProcessPShop6IFD ( const TIFF_MemoryReader& buriedExif, XM
for ( size_t i = (2 * ps6Tag.count); i > 0; --i, ++ratPtr ) Flip4 ( ratPtr );
}
break;
-
+
case kTIFF_DoubleType:
{
XMP_Uns64* u64Ptr = (XMP_Uns64*)voidPtr;
for ( size_t i = ps6Tag.count; i > 0; --i, ++u64Ptr ) Flip8 ( u64Ptr );
}
break;
-
+
case kTIFF_UndefinedType:
// Fix up the few kinds of special tables that Exif 2.2 defines.
ok = true; // Keep everything that isn't a special table.
@@ -1154,20 +1085,92 @@ void TIFF_FileWriter::ProcessPShop6IFD ( const TIFF_MemoryReader& buriedExif, XM
}
if ( ! ok ) this->DeleteTag ( ifd, ps6Tag.id );
break;
-
+
default:
// ? XMP_Throw ( "Unexpected tag type", kXMPErr_InternalFailure );
this->DeleteTag ( ifd, ps6Tag.id );
break;
-
+
}
}
-
+
}
-
+
} // TIFF_FileWriter::ProcessPShop6IFD
// =================================================================================================
+// TIFF_FileWriter::PreflightIFDLinkage
+// ====================================
+//
+// Preflight special cases for the linkage between IFDs. Three of the IFDs are found through an
+// explicit tag, the Exif, GPS, and Interop IFDs. The presence or absence of those IFDs affects the
+// presence or absence of the linkage tag, which can affect the IFD containing the linkage tag. The
+// thumbnail IFD is chained from the primary IFD, so if the thumbnail IFD is present we make sure
+// that the primary IFD isn't empty.
+
+void TIFF_FileWriter::PreflightIFDLinkage()
+{
+
+ // Do the tag-linked IFDs bottom up, Interop then GPS then Exif.
+
+ if ( this->containedIFDs[kTIFF_InteropIFD].tagMap.empty() ) {
+ this->DeleteTag ( kTIFF_ExifIFD, kTIFF_InteroperabilityIFDPointer );
+ } else if ( ! this->GetTag ( kTIFF_ExifIFD, kTIFF_InteroperabilityIFDPointer, 0 ) ) {
+ this->SetTag_Long ( kTIFF_ExifIFD, kTIFF_InteroperabilityIFDPointer, 0xABADABAD );
+ }
+
+ if ( this->containedIFDs[kTIFF_GPSInfoIFD].tagMap.empty() ) {
+ this->DeleteTag ( kTIFF_PrimaryIFD, kTIFF_GPSInfoIFDPointer );
+ } else if ( ! this->GetTag ( kTIFF_PrimaryIFD, kTIFF_GPSInfoIFDPointer, 0 ) ) {
+ this->SetTag_Long ( kTIFF_PrimaryIFD, kTIFF_GPSInfoIFDPointer, 0xABADABAD );
+ }
+
+ if ( this->containedIFDs[kTIFF_ExifIFD].tagMap.empty() ) {
+ this->DeleteTag ( kTIFF_PrimaryIFD, kTIFF_ExifIFDPointer );
+ } else if ( ! this->GetTag ( kTIFF_PrimaryIFD, kTIFF_ExifIFDPointer, 0 ) ) {
+ this->SetTag_Long ( kTIFF_PrimaryIFD, kTIFF_ExifIFDPointer, 0xABADABAD );
+ }
+
+ // Make sure that the primary IFD is not empty if the thumbnail IFD is not empty.
+
+ if ( this->containedIFDs[kTIFF_PrimaryIFD].tagMap.empty() &&
+ (! this->containedIFDs[kTIFF_TNailIFD].tagMap.empty()) ) {
+ this->SetTag_Short ( kTIFF_PrimaryIFD, kTIFF_ResolutionUnit, 2 ); // Set Resolution unit to inches.
+ }
+
+} // TIFF_FileWriter::PreflightIFDLinkage
+
+// =================================================================================================
+// TIFF_FileWriter::DetermineVisibleLength
+// =======================================
+
+XMP_Uns32 TIFF_FileWriter::DetermineVisibleLength()
+{
+ XMP_Uns32 visibleLength = 8; // Start with the TIFF header size.
+
+ for ( int ifd = 0; ifd < kTIFF_KnownIFDCount; ++ifd ) {
+
+ InternalIFDInfo& ifdInfo ( this->containedIFDs[ifd] );
+ size_t tagCount = ifdInfo.tagMap.size();
+ if ( tagCount == 0 ) continue;
+
+ visibleLength += (XMP_Uns32)( 6 + (12 * tagCount) );
+
+ InternalTagMap::iterator tagPos = ifdInfo.tagMap.begin();
+ InternalTagMap::iterator tagEnd = ifdInfo.tagMap.end();
+
+ for ( ; tagPos != tagEnd; ++tagPos ) {
+ InternalTagInfo & currTag ( tagPos->second );
+ if ( currTag.dataLen > 4 ) visibleLength += ((currTag.dataLen + 1) & 0xFFFFFFFE); // ! Round to even lengths.
+ }
+
+ }
+
+ return visibleLength;
+
+} // TIFF_FileWriter::DetermineVisibleLength
+
+// =================================================================================================
// TIFF_FileWriter::DetermineAppendInfo
// ====================================
@@ -1182,7 +1185,7 @@ XMP_Uns32 TIFF_FileWriter::DetermineAppendInfo ( XMP_Uns32 appendedOrigin,
{
XMP_Uns32 appendedLength = 0;
XMP_Assert ( (appendedOrigin & 1) == 0 ); // Make sure it is even.
-
+
#if Trace_DetermineAppendInfo
{
printf ( "\nEntering TIFF_FileWriter::DetermineAppendInfo%s\n", (appendAll ? ", append all" : "") );
@@ -1208,59 +1211,56 @@ XMP_Uns32 TIFF_FileWriter::DetermineAppendInfo ( XMP_Uns32 appendedOrigin,
printf ( "\n" );
}
#endif
-
+
// Determine which of the IFDs will be appended. If the Exif, GPS, or Interoperability IFDs are
// appended, set dummy values for their offsets in the "owning" IFD. This must be done first
// since this might cause the owning IFD to grow.
-
+
if ( ! appendAll ) {
for ( int i = 0; i < kTIFF_KnownIFDCount ;++i ) appendedIFDs[i] = false;
} else {
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());
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());
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());
if ( appendedIFDs[kTIFF_ExifIFD] ) {
this->SetTag_Long ( kTIFF_PrimaryIFD, kTIFF_ExifIFDPointer, 0xABADABAD );
}
-
- appendedIFDs[kTIFF_TNailIFD] |= (this->containedIFDs[kTIFF_TNailIFD].origCount <
- this->containedIFDs[kTIFF_TNailIFD].tagMap.size());
-
+
appendedIFDs[kTIFF_PrimaryIFD] |= (this->containedIFDs[kTIFF_PrimaryIFD].origCount <
this->containedIFDs[kTIFF_PrimaryIFD].tagMap.size());
// 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.
+
for ( int ifd = 0; ifd < kTIFF_KnownIFDCount ;++ifd ) {
-
+
InternalIFDInfo& ifdInfo ( this->containedIFDs[ifd] );
size_t tagCount = ifdInfo.tagMap.size();
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) );
}
-
+
InternalTagMap::iterator tagPos = ifdInfo.tagMap.begin();
InternalTagMap::iterator tagEnd = ifdInfo.tagMap.end();
@@ -1277,11 +1277,11 @@ XMP_Uns32 TIFF_FileWriter::DetermineAppendInfo ( XMP_Uns32 appendedOrigin,
}
}
-
+
}
-
+
// If the Exif, GPS, or Interoperability IFDs get appended, update the tag values for their new offsets.
-
+
if ( appendedIFDs[kTIFF_ExifIFD] ) {
this->SetTag_Long ( kTIFF_PrimaryIFD, kTIFF_ExifIFDPointer, newIFDOffsets[kTIFF_ExifIFD] );
}
@@ -1291,7 +1291,7 @@ XMP_Uns32 TIFF_FileWriter::DetermineAppendInfo ( XMP_Uns32 appendedOrigin,
if ( appendedIFDs[kTIFF_InteropIFD] ) {
this->SetTag_Long ( kTIFF_ExifIFD, kTIFF_InteroperabilityIFDPointer, newIFDOffsets[kTIFF_InteropIFD] );
}
-
+
#if Trace_DetermineAppendInfo
{
printf ( "Exiting TIFF_FileWriter::DetermineAppendInfo\n" );
@@ -1320,9 +1320,9 @@ XMP_Uns32 TIFF_FileWriter::DetermineAppendInfo ( XMP_Uns32 appendedOrigin,
printf ( "\n" );
}
#endif
-
+
return appendedLength;
-
+
} // TIFF_FileWriter::DetermineAppendInfo
// =================================================================================================
@@ -1354,7 +1354,7 @@ void TIFF_FileWriter::UpdateMemByAppend ( XMP_Uns8** newStream_out, XMP_Uns32* n
// Allocate the new block of memory for the full stream. Copy the original stream. Write the
// modified IFDs and values. Finally rebuild the internal IFD info and tag map.
-
+
XMP_Uns32 newLength = appendedOrigin + appendedLength;
XMP_Uns8* newStream = (XMP_Uns8*) malloc ( newLength + extraSpace );
if ( newStream == 0 ) XMP_Throw ( "Out of memory", kXMPErr_NoMemory );
@@ -1364,16 +1364,16 @@ void TIFF_FileWriter::UpdateMemByAppend ( XMP_Uns8** newStream_out, XMP_Uns32* n
XMP_Assert ( appendedOrigin == (this->tiffLength + 1) );
newStream[this->tiffLength] = 0; // Clear the pad byte.
}
-
+
try { // We might get exceptions from the next part and must delete newStream on the way out.
-
+
// Write the modified IFDs and values. Rewrite the full IFD from scratch to make sure the
// tags are now unique and sorted. Copy large changed values to their appropriate location.
-
+
XMP_Uns32 appendedOffset = appendedOrigin;
-
+
for ( int ifd = 0; ifd < kTIFF_KnownIFDCount; ++ifd ) {
-
+
InternalIFDInfo& ifdInfo ( this->containedIFDs[ifd] );
size_t tagCount = ifdInfo.tagMap.size();
@@ -1381,12 +1381,12 @@ void TIFF_FileWriter::UpdateMemByAppend ( XMP_Uns8** newStream_out, XMP_Uns32* n
if ( tagCount == 0 ) continue;
XMP_Uns8* ifdPtr = newStream + newIFDOffsets[ifd];
-
+
if ( appendedIFDs[ifd] ) {
XMP_Assert ( newIFDOffsets[ifd] == appendedOffset );
appendedOffset += (XMP_Uns32)( 6 + (12 * tagCount) );
}
-
+
this->PutUns16 ( (XMP_Uns16)tagCount, ifdPtr );
ifdPtr += 2;
@@ -1426,67 +1426,31 @@ void TIFF_FileWriter::UpdateMemByAppend ( XMP_Uns8** newStream_out, XMP_Uns32* n
ifdPtr += 4;
}
-
+
this->PutUns32 ( ifdInfo.origNextIFD, ifdPtr );
ifdPtr += 4;
-
+
}
-
+
XMP_Assert ( appendedOffset == newLength );
-
+
// Back fill the offsets for the primary and thumnbail IFDs, if they are now appended.
-
+
if ( appendedIFDs[kTIFF_PrimaryIFD] ) {
this->PutUns32 ( newIFDOffsets[kTIFF_PrimaryIFD], (newStream + 4) );
}
-
- if ( appendedIFDs[kTIFF_TNailIFD] ) {
- size_t primaryIFDCount = this->containedIFDs[kTIFF_PrimaryIFD].tagMap.size();
- XMP_Uns32 tnailRefOffset = newIFDOffsets[kTIFF_PrimaryIFD] + 2 + (12 * (XMP_Uns32)primaryIFDCount);
- this->PutUns32 ( newIFDOffsets[kTIFF_TNailIFD], (newStream + tnailRefOffset) );
- }
-
+
} catch ( ... ) {
-
+
free ( newStream );
throw;
-
+
}
-
+
*newStream_out = newStream;
*newLength_out = newLength;
-
-} // TIFF_FileWriter::UpdateMemByAppend
-// =================================================================================================
-// TIFF_FileWriter::DetermineVisibleLength
-// =======================================
-
-XMP_Uns32 TIFF_FileWriter::DetermineVisibleLength()
-{
- XMP_Uns32 visibleLength = 8; // Start with the TIFF header size.
-
- for ( int ifd = 0; ifd < kTIFF_KnownIFDCount; ++ifd ) {
-
- InternalIFDInfo& ifdInfo ( this->containedIFDs[ifd] );
- size_t tagCount = ifdInfo.tagMap.size();
- if ( tagCount == 0 ) continue;
-
- visibleLength += (XMP_Uns32)( 6 + (12 * tagCount) );
-
- InternalTagMap::iterator tagPos = ifdInfo.tagMap.begin();
- InternalTagMap::iterator tagEnd = ifdInfo.tagMap.end();
-
- for ( ; tagPos != tagEnd; ++tagPos ) {
- InternalTagInfo & currTag ( tagPos->second );
- if ( currTag.dataLen > 4 ) visibleLength += ((currTag.dataLen + 1) & 0xFFFFFFFE); // ! Round to even lengths.
- }
-
- }
-
- return visibleLength;
-
-} // TIFF_FileWriter::DetermineVisibleLength
+} // TIFF_FileWriter::UpdateMemByAppend
// =================================================================================================
// TIFF_FileWriter::UpdateMemByRewrite
@@ -1582,54 +1546,39 @@ static const SimpleHiddenContentInfo kSimpleHiddenContentInfo [kSimpleHiddenCont
// -------------------------------------------------------------------------------------------------
-void TIFF_FileWriter::UpdateMemByRewrite ( XMP_Uns8** newStream_out, XMP_Uns32* newLength_out )
+void TIFF_FileWriter::UpdateMemByRewrite ( XMP_Uns8** newStream_out, XMP_Uns32* newLength_out )
{
const InternalTagInfo* tagInfo;
-
+
// Check for tags that we don't tolerate because they have data we can't (or refuse to) find.
-
+
for ( XMP_Uns8 ifd = 0; ifd < kTIFF_KnownIFDCount; ++ifd ) {
for ( int i = 0; kNoGoTags[i] != 0xFFFF; ++i ) {
tagInfo = this->FindTagInIFD ( ifd, kNoGoTags[i] );
if ( tagInfo != 0 ) XMP_Throw ( "Tag not tolerated for TIFF rewrite", kXMPErr_Unimplemented );
}
}
-
- // Delete unwanted tags.
-
+
+ // Delete unwanted tags.
+
for ( XMP_Uns8 ifd = 0; ifd < kTIFF_KnownIFDCount; ++ifd ) {
for ( int i = 0; kBanishedTags[i] != 0xFFFF; ++i ) {
this->DeleteTag ( ifd, kBanishedTags[i] );
}
}
-
- // Make sure the "pointer" tags for the Exif, GPS, and Interop IFDs exist. The order is
- // important, adding the Interop pointer can cause the Exif IFD to exist.
-
- if ( ! this->containedIFDs[kTIFF_InteropIFD].tagMap.empty() ) {
- this->SetTag_Long ( kTIFF_ExifIFD, kTIFF_InteroperabilityIFDPointer, 0xABADABAD );
- }
-
- if ( ! this->containedIFDs[kTIFF_GPSInfoIFD].tagMap.empty() ) {
- this->SetTag_Long ( kTIFF_PrimaryIFD, kTIFF_GPSInfoIFDPointer, 0xABADABAD );
- }
-
- if ( ! this->containedIFDs[kTIFF_ExifIFD].tagMap.empty() ) {
- this->SetTag_Long ( kTIFF_PrimaryIFD, kTIFF_ExifIFDPointer, 0xABADABAD );
- }
// Determine the offsets and additional size for the hidden offset content. Set the offset tags
// to the new offset.
-
+
XMP_Uns32 hiddenContentLength = 0;
XMP_Uns32 hiddenContentOrigin = this->DetermineVisibleLength();
-
+
SimpleHiddenContentLocations hiddenLocations [kSimpleHiddenContentCount];
-
+
for ( int i = 0; i < kSimpleHiddenContentCount; ++i ) {
const SimpleHiddenContentInfo & hiddenInfo ( kSimpleHiddenContentInfo[i] );
-
+
bool haveLength = this->GetTag_Integer ( hiddenInfo.ifd, hiddenInfo.lengthTag, &hiddenLocations[i].length );
bool haveOffset = this->GetTag_Integer ( hiddenInfo.ifd, hiddenInfo.offsetTag, &hiddenLocations[i].oldOffset );
if ( haveLength != haveOffset ) XMP_Throw ( "Unpaired simple hidden content tag", kXMPErr_BadTIFF );
@@ -1640,7 +1589,7 @@ void TIFF_FileWriter::UpdateMemByRewrite ( XMP_Uns8** newStream_out, XMP_Uns32*
hiddenContentLength += ((hiddenLocations[i].length + 1) & 0xFFFFFFFE); // ! Round up for even offsets.
}
-
+
// Save any old memory stream for the content behind hidden offsets. Setup a bare TIFF header.
XMP_Uns8* oldStream = this->memStream;
@@ -1652,20 +1601,20 @@ void TIFF_FileWriter::UpdateMemByRewrite ( XMP_Uns8** newStream_out, XMP_Uns32*
bareTIFF[0] = 0x49; bareTIFF[1] = 0x49; bareTIFF[2] = 0x2A; bareTIFF[3] = 0x00;
}
*((XMP_Uns32*)&bareTIFF[4]) = 0;
-
+
this->memStream = &bareTIFF[0];
this->tiffLength = sizeof ( bareTIFF );
this->ownedStream = false;
// Call UpdateMemByAppend to write the new stream, telling it to append everything.
-
+
this->UpdateMemByAppend ( newStream_out, newLength_out, true, hiddenContentLength );
// Copy the hidden content and update the output stream length;
XMP_Assert ( *newLength_out == hiddenContentOrigin );
*newLength_out += hiddenContentLength;
-
+
for ( int i = 0; i < kSimpleHiddenContentCount; ++i ) {
if ( hiddenLocations[i].length == 0 ) continue;
@@ -1675,7 +1624,7 @@ void TIFF_FileWriter::UpdateMemByRewrite ( XMP_Uns8** newStream_out, XMP_Uns32*
memcpy ( destPtr, srcPtr, hiddenLocations[i].length ); // AUDIT: Safe copy, not user data, computed length.
}
-
+
} // TIFF_FileWriter::UpdateMemByRewrite
// =================================================================================================
@@ -1692,15 +1641,17 @@ void TIFF_FileWriter::UpdateMemByRewrite ( XMP_Uns8** newStream_out, XMP_Uns32*
// will discard any MakerNote tags and risks breaking offsets that are hidden. This can be necessary
// though to try to make the TIFF fit in a JPEG file.
-XMP_Uns32 TIFF_FileWriter::UpdateMemoryStream ( void** dataPtr, bool condenseStream /* = false */ )
+XMP_Uns32 TIFF_FileWriter::UpdateMemoryStream ( void** dataPtr, bool condenseStream /* = false */ )
{
if ( this->fileParsed ) XMP_Throw ( "Not memory based", kXMPErr_EnforceFailure );
-
+
if ( ! this->changed ) {
if ( dataPtr != 0 ) *dataPtr = this->memStream;
return this->tiffLength;
}
-
+
+ this->PreflightIFDLinkage();
+
bool nowEmpty = true;
for ( size_t i = 0; i < kTIFF_KnownIFDCount; ++i ) {
if ( ! this->containedIFDs[i].tagMap.empty() ) {
@@ -1708,39 +1659,39 @@ XMP_Uns32 TIFF_FileWriter::UpdateMemoryStream ( void** dataPtr, bool condenseStr
break;
}
}
-
+
XMP_Uns8* newStream = 0;
XMP_Uns32 newLength = 0;
-
+
if ( nowEmpty ) {
-
+
this->DeleteExistingInfo(); // Prepare for an empty reparse.
-
+
} else {
if ( this->tiffLength == 0 ) { // ! An empty parse does set this->memParsed.
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 {
this->UpdateMemByAppend ( &newStream, &newLength );
}
-
+
}
-
+
// Parse the revised stream. This is the cleanest way to rebuild the tag map.
-
+
this->ParseMemoryStream ( newStream, newLength, kDoNotCopyData );
XMP_Assert ( this->tiffLength == newLength );
this->ownedStream = (newLength > 0); // ! We really do own the new stream, if not empty.
-
+
if ( dataPtr != 0 ) *dataPtr = this->memStream;
return newLength;
-
+
} // TIFF_FileWriter::UpdateMemoryStream
// =================================================================================================
@@ -1769,17 +1720,17 @@ XMP_Uns32 TIFF_FileWriter::UpdateMemoryStream ( void** dataPtr, bool condenseStr
#define Trace_UpdateFileStream 0
#endif
-void TIFF_FileWriter::UpdateFileStream ( LFA_FileRef fileRef )
+void TIFF_FileWriter::UpdateFileStream ( LFA_FileRef fileRef )
{
if ( this->memParsed ) XMP_Throw ( "Not file based", kXMPErr_EnforceFailure );
if ( ! this->changed ) return;
-
+
XMP_Int64 origDataLength = LFA_Measure ( fileRef );
if ( (origDataLength >> 32) != 0 ) XMP_Throw ( "TIFF files can't exceed 4GB", kXMPErr_BadTIFF );
-
+
bool appendedIFDs[kTIFF_KnownIFDCount];
XMP_Uns32 newIFDOffsets[kTIFF_KnownIFDCount];
-
+
#if Trace_UpdateFileStream
printf ( "\nStarting update of TIFF file stream\n" );
#endif
@@ -1791,15 +1742,17 @@ void TIFF_FileWriter::UpdateFileStream ( LFA_FileRef fileRef )
LFA_Write ( fileRef, "\0", 1 );
}
+ this->PreflightIFDLinkage();
+
XMP_Uns32 appendedLength = DetermineAppendInfo ( appendedOrigin, appendedIFDs, newIFDOffsets );
if ( appendedLength > (0xFFFFFFFFUL - appendedOrigin) ) XMP_Throw ( "TIFF files can't exceed 4GB", kXMPErr_BadTIFF );
// 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.
-
+
// *** This might benefit from a map of the in-place updates. This would allow use of a possibly
// *** more efficient sequential I/O model. Could even incorporate the safe update file copying.
-
+
for ( int ifd = 0; ifd < kTIFF_KnownIFDCount; ++ifd ) {
InternalIFDInfo & thisIFD = this->containedIFDs[ifd];
@@ -1807,7 +1760,7 @@ void TIFF_FileWriter::UpdateFileStream ( LFA_FileRef fileRef )
// In order to get a little bit of locality, write the IFD first then the changed tags that
// have large values and fit in-place.
-
+
if ( ! appendedIFDs[ifd] ) {
#if Trace_UpdateFileStream
printf ( " Updating IFD %d in-place at offset %d (0x%X)\n", ifd, thisIFD.origIFDOffset, thisIFD.origIFDOffset );
@@ -1815,10 +1768,10 @@ void TIFF_FileWriter::UpdateFileStream ( LFA_FileRef fileRef )
LFA_Seek ( fileRef, thisIFD.origIFDOffset, SEEK_SET );
this->WriteFileIFD ( fileRef, thisIFD );
}
-
+
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) || (thisTag.dataLen > thisTag.origDataLen) ) continue;
@@ -1835,12 +1788,12 @@ void TIFF_FileWriter::UpdateFileStream ( LFA_FileRef fileRef )
XMP_Int64 fileEnd = LFA_Seek ( fileRef, 0, SEEK_END );
XMP_Assert ( fileEnd == appendedOrigin );
-
+
for ( int ifd = 0; ifd < kTIFF_KnownIFDCount; ++ifd ) {
InternalIFDInfo & thisIFD = this->containedIFDs[ifd];
if ( ! thisIFD.changed ) continue;
-
+
if ( appendedIFDs[ifd] ) {
#if Trace_UpdateFileStream
printf ( " Updating IFD %d by append at offset %d (0x%X)\n", ifd, newIFDOffsets[ifd], newIFDOffsets[ifd] );
@@ -1848,10 +1801,10 @@ void TIFF_FileWriter::UpdateFileStream ( LFA_FileRef fileRef )
XMP_Assert ( newIFDOffsets[ifd] == LFA_Measure(fileRef) );
this->WriteFileIFD ( fileRef, thisIFD );
}
-
+
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) || (thisTag.dataLen <= thisTag.origDataLen) ) continue;
@@ -1866,10 +1819,10 @@ void TIFF_FileWriter::UpdateFileStream ( LFA_FileRef fileRef )
}
- // Back-fill the offsets for the primary and thumnbail IFDs, if they are now appended.
-
+ // Back-fill the offset for the primary IFD, if it is now appended.
+
XMP_Uns32 newOffset;
-
+
if ( appendedIFDs[kTIFF_PrimaryIFD] ) {
this->PutUns32 ( newIFDOffsets[kTIFF_PrimaryIFD], &newOffset );
#if TraceUpdateFileStream
@@ -1878,30 +1831,10 @@ void TIFF_FileWriter::UpdateFileStream ( LFA_FileRef fileRef )
LFA_Seek ( fileRef, 4, SEEK_SET );
LFA_Write ( fileRef, &newOffset, 4 );
}
-
- InternalIFDInfo & primaryIFD = this->containedIFDs[kTIFF_PrimaryIFD];
- InternalIFDInfo & tnailIFD = this->containedIFDs[kTIFF_TNailIFD];
-
- if ( appendedIFDs[kTIFF_TNailIFD] && (primaryIFD.origNextIFD == tnailIFD.origIFDOffset) ) {
-
- size_t primaryIFDCount = primaryIFD.tagMap.size();
- XMP_Uns32 tnailRefOffset = newIFDOffsets[kTIFF_PrimaryIFD] + 2 + (12 * (XMP_Uns32)primaryIFDCount);
-
- this->PutUns32 ( newIFDOffsets[kTIFF_TNailIFD], &newOffset );
- #if TraceUpdateFileStream
- printf ( " Back-filling offset of thumbnail IFD, offset at %d (0x%X), pointing to %d (0x%X)\n",
- tnailRefOffset, tnailRefOffset, newOffset, newOffset );
- #endif
- LFA_Seek ( fileRef, tnailRefOffset, SEEK_SET );
- LFA_Write ( fileRef, &newOffset, 4 );
-
- primaryIFD.origNextIFD = newIFDOffsets[kTIFF_TNailIFD]; // ! Ought to be below, easier here.
- }
-
// Reset the changed flags and original length/offset values. This simulates a reparse of the
// updated file.
-
+
for ( int ifd = 0; ifd < kTIFF_KnownIFDCount; ++ifd ) {
InternalIFDInfo & thisIFD = this->containedIFDs[ifd];
@@ -1910,10 +1843,10 @@ void TIFF_FileWriter::UpdateFileStream ( LFA_FileRef fileRef )
thisIFD.changed = false;
thisIFD.origCount = (XMP_Uns16)( thisIFD.tagMap.size() );
thisIFD.origIFDOffset = newIFDOffsets[ifd];
-
+
InternalTagMap::iterator tagPos;
InternalTagMap::iterator tagEnd = thisIFD.tagMap.end();
-
+
for ( tagPos = thisIFD.tagMap.begin(); tagPos != tagEnd; ++tagPos ) {
InternalTagInfo & thisTag = tagPos->second;
if ( ! thisTag.changed ) continue;
@@ -1926,7 +1859,7 @@ void TIFF_FileWriter::UpdateFileStream ( LFA_FileRef fileRef )
this->tiffLength = (XMP_Uns32) LFA_Measure ( fileRef );
LFA_Seek ( fileRef, 0, SEEK_END ); // Can't hurt.
-
+
#if Trace_UpdateFileStream
printf ( "\nFinished update of TIFF file stream\n" );
#endif
@@ -1942,7 +1875,7 @@ void TIFF_FileWriter::WriteFileIFD ( LFA_FileRef fileRef, InternalIFDInfo & this
XMP_Uns16 tagCount;
this->PutUns16 ( (XMP_Uns16)thisIFD.tagMap.size(), &tagCount );
LFA_Write ( fileRef, &tagCount, 2 );
-
+
InternalTagMap::iterator tagPos;
InternalTagMap::iterator tagEnd = thisIFD.tagMap.end();
@@ -1960,7 +1893,7 @@ void TIFF_FileWriter::WriteFileIFD ( LFA_FileRef fileRef, InternalIFDInfo & this
XMP_Assert ( sizeof(ifdEntry) == 12 );
}
-
+
XMP_Uns32 nextIFD;
this->PutUns32 ( thisIFD.origNextIFD, &nextIFD );
LFA_Write ( fileRef, &nextIFD, 4 );
diff --git a/source/XMPFiles/FormatSupport/TIFF_MemoryReader.cpp b/source/XMPFiles/FormatSupport/TIFF_MemoryReader.cpp
index 4ca9cac..316cea0 100644
--- a/source/XMPFiles/FormatSupport/TIFF_MemoryReader.cpp
+++ b/source/XMPFiles/FormatSupport/TIFF_MemoryReader.cpp
@@ -1,6 +1,6 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2006-2007 Adobe Systems Incorporated
+// Copyright 2006 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -14,7 +14,7 @@
/// \brief Implementation of the memory-based read-only TIFF_Manager.
///
/// The read-only forms of TIFF_Manager are derived from TIFF_Reader. The GetTag methods are common
-/// implementations in TIFF_Reader. The parsing code is different in the TIFF_MemoryReader and
+/// implementations in TIFF_Reader. The parsing code is different in the TIFF_MemoryReader and
/// TIFF_FileReader constructors. There are also separate destructors to release captured info.
///
/// The read-only implementations use runtime data that is simple tweaks on the stored form. The
@@ -52,9 +52,9 @@ void TIFF_MemoryReader::SortIFD ( TweakedIFDInfo* thisIFD )
XMP_Uns16 tagCount = thisIFD->count;
TweakedIFDEntry* ifdEntries = thisIFD->entries;
XMP_Uns16 prevTag = ifdEntries[0].id;
-
+
for ( size_t i = 1; i < tagCount; ++i ) {
-
+
XMP_Uns16 thisTag = ifdEntries[i].id;
if ( thisTag > prevTag ) {
@@ -76,7 +76,7 @@ void TIFF_MemoryReader::SortIFD ( TweakedIFDInfo* thisIFD )
for ( j = (XMP_Int32)i-1; j >= 0; --j ) {
if ( ifdEntries[j].id <= thisTag ) break;
}
-
+
if ( (j >= 0) && (ifdEntries[j].id == thisTag) ) {
// Out of order duplicate, move it to position j, move the tail of the array up.
@@ -96,11 +96,11 @@ void TIFF_MemoryReader::SortIFD ( TweakedIFDInfo* thisIFD )
}
}
-
+
}
-
+
thisIFD->count = tagCount; // Save the final count.
-
+
} // TIFF_MemoryReader::SortIFD
// =================================================================================================
@@ -111,15 +111,16 @@ bool TIFF_MemoryReader::GetIFD ( XMP_Uns8 ifd, TagInfoMap* ifdMap ) const
{
if ( ifd > kTIFF_LastRealIFD ) XMP_Throw ( "Invalid IFD requested", kXMPErr_InternalFailure );
const TweakedIFDInfo* thisIFD = &containedIFDs[ifd];
-
+
if ( ifdMap != 0 ) ifdMap->clear();
if ( thisIFD->count == 0 ) return false;
-
+
if ( ifdMap != 0 ) {
-
+
for ( size_t i = 0; i < thisIFD->count; ++i ) {
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 );
info.count = info.dataLen / (XMP_Uns32)kTIFF_TypeSizes[info.type];
@@ -128,9 +129,9 @@ bool TIFF_MemoryReader::GetIFD ( XMP_Uns8 ifd, TagInfoMap* ifdMap ) const
(*ifdMap)[info.id] = info;
}
-
+
}
-
+
return true;
} // TIFF_MemoryReader::GetIFD
@@ -144,23 +145,23 @@ const TIFF_MemoryReader::TweakedIFDEntry* TIFF_MemoryReader::FindTagInIFD ( XMP_
if ( ifd == kTIFF_KnownIFD ) {
// ... lookup the tag in the known tag map
}
-
+
if ( ifd > kTIFF_LastRealIFD ) XMP_Throw ( "Invalid IFD requested", kXMPErr_InternalFailure );
const TweakedIFDInfo* thisIFD = &containedIFDs[ifd];
-
+
if ( thisIFD->count == 0 ) return 0;
-
+
XMP_Uns32 spanLength = thisIFD->count;
const TweakedIFDEntry* spanBegin = &(thisIFD->entries[0]);
-
+
while ( spanLength > 1 ) {
XMP_Uns32 halfLength = spanLength >> 1; // Since spanLength > 1, halfLength > 0.
const TweakedIFDEntry* spanMiddle = spanBegin + halfLength;
-
+
// 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 ) {
spanBegin = spanMiddle;
break;
@@ -170,9 +171,9 @@ const TIFF_MemoryReader::TweakedIFDEntry* TIFF_MemoryReader::FindTagInIFD ( XMP_
spanBegin = spanMiddle; // Keep a valid spanBegin for the return check, don't use spanMiddle+1.
spanLength -= halfLength;
}
-
+
}
-
+
if ( spanBegin->id != id ) spanBegin = 0;
return spanBegin;
@@ -186,11 +187,11 @@ XMP_Uns32 TIFF_MemoryReader::GetValueOffset ( XMP_Uns8 ifd, XMP_Uns16 id ) const
{
const TweakedIFDEntry* thisTag = this->FindTagInIFD ( ifd, id );
if ( thisTag == 0 ) return 0;
-
+
XMP_Uns8 * valuePtr = (XMP_Uns8*) this->GetDataPtr ( thisTag );
-
+
return (XMP_Uns32)(valuePtr - this->tiffStream); // ! TIFF streams can't exceed 4GB.
-
+
} // TIFF_MemoryReader::GetValueOffset
// =================================================================================================
@@ -201,20 +202,21 @@ 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.
+
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->dataPtr = this->GetDataPtr ( thisTag );
}
-
+
return true;
-
+
} // TIFF_MemoryReader::GetTag
// =================================================================================================
@@ -225,7 +227,7 @@ bool TIFF_MemoryReader::GetTag_Integer ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns32*
{
const TweakedIFDEntry* thisTag = this->FindTagInIFD ( ifd, id );
if ( thisTag == 0 ) return false;
-
+
if ( data != 0 ) {
if ( thisTag->type == kTIFF_ShortType ) {
if ( thisTag->bytes != 2 ) return false; // Wrong count.
@@ -237,7 +239,7 @@ bool TIFF_MemoryReader::GetTag_Integer ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns32*
return false;
}
}
-
+
return true;
} // TIFF_MemoryReader::GetTag_Integer
@@ -251,11 +253,11 @@ bool TIFF_MemoryReader::GetTag_Byte ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns8* data
const TweakedIFDEntry* thisTag = this->FindTagInIFD ( ifd, id );
if ( thisTag == 0 ) return false;
if ( (thisTag->type != kTIFF_ByteType) || (thisTag->bytes != 1) ) return false;
-
+
if ( data != 0 ) {
*data = * ( (XMP_Uns8*) this->GetDataPtr ( thisTag ) );
}
-
+
return true;
} // TIFF_MemoryReader::GetTag_Byte
@@ -269,11 +271,11 @@ bool TIFF_MemoryReader::GetTag_SByte ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Int8* dat
const TweakedIFDEntry* thisTag = this->FindTagInIFD ( ifd, id );
if ( thisTag == 0 ) return false;
if ( (thisTag->type != kTIFF_SByteType) || (thisTag->bytes != 1) ) return false;
-
+
if ( data != 0 ) {
*data = * ( (XMP_Int8*) this->GetDataPtr ( thisTag ) );
}
-
+
return true;
} // TIFF_MemoryReader::GetTag_SByte
@@ -287,11 +289,11 @@ bool TIFF_MemoryReader::GetTag_Short ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns16* da
const TweakedIFDEntry* thisTag = this->FindTagInIFD ( ifd, id );
if ( thisTag == 0 ) return false;
if ( (thisTag->type != kTIFF_ShortType) || (thisTag->bytes != 2) ) return false;
-
+
if ( data != 0 ) {
*data = this->GetUns16 ( this->GetDataPtr ( thisTag ) );
}
-
+
return true;
} // TIFF_MemoryReader::GetTag_Short
@@ -305,11 +307,11 @@ bool TIFF_MemoryReader::GetTag_SShort ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Int16* d
const TweakedIFDEntry* thisTag = this->FindTagInIFD ( ifd, id );
if ( thisTag == 0 ) return false;
if ( (thisTag->type != kTIFF_SShortType) || (thisTag->bytes != 2) ) return false;
-
+
if ( data != 0 ) {
*data = (XMP_Int16) this->GetUns16 ( this->GetDataPtr ( thisTag ) );
}
-
+
return true;
} // TIFF_MemoryReader::GetTag_SShort
@@ -323,11 +325,11 @@ bool TIFF_MemoryReader::GetTag_Long ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns32* dat
const TweakedIFDEntry* thisTag = this->FindTagInIFD ( ifd, id );
if ( thisTag == 0 ) return false;
if ( (thisTag->type != kTIFF_LongType) || (thisTag->bytes != 4) ) return false;
-
+
if ( data != 0 ) {
*data = this->GetUns32 ( this->GetDataPtr ( thisTag ) );
}
-
+
return true;
} // TIFF_MemoryReader::GetTag_Long
@@ -341,11 +343,11 @@ bool TIFF_MemoryReader::GetTag_SLong ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Int32* da
const TweakedIFDEntry* thisTag = this->FindTagInIFD ( ifd, id );
if ( thisTag == 0 ) return false;
if ( (thisTag->type != kTIFF_SLongType) || (thisTag->bytes != 4) ) return false;
-
+
if ( data != 0 ) {
*data = (XMP_Int32) this->GetUns32 ( this->GetDataPtr ( thisTag ) );
}
-
+
return true;
} // TIFF_MemoryReader::GetTag_SLong
@@ -359,13 +361,13 @@ bool TIFF_MemoryReader::GetTag_Rational ( XMP_Uns8 ifd, XMP_Uns16 id, Rational*
const TweakedIFDEntry* thisTag = this->FindTagInIFD ( ifd, id );
if ( thisTag == 0 ) return false;
if ( (thisTag->type != kTIFF_RationalType) || (thisTag->bytes != 8) ) return false;
-
+
if ( data != 0 ) {
XMP_Uns32* dataPtr = (XMP_Uns32*) this->GetDataPtr ( thisTag );
data->num = this->GetUns32 ( dataPtr );
data->denom = this->GetUns32 ( dataPtr+1 );
}
-
+
return true;
} // TIFF_MemoryReader::GetTag_Rational
@@ -379,13 +381,13 @@ bool TIFF_MemoryReader::GetTag_SRational ( XMP_Uns8 ifd, XMP_Uns16 id, SRational
const TweakedIFDEntry* thisTag = this->FindTagInIFD ( ifd, id );
if ( thisTag == 0 ) return false;
if ( (thisTag->type != kTIFF_SRationalType) || (thisTag->bytes != 8) ) return false;
-
+
if ( data != 0 ) {
XMP_Uns32* dataPtr = (XMP_Uns32*) this->GetDataPtr ( thisTag );
data->num = (XMP_Int32) this->GetUns32 ( dataPtr );
data->denom = (XMP_Int32) this->GetUns32 ( dataPtr+1 );
}
-
+
return true;
} // TIFF_MemoryReader::GetTag_SRational
@@ -399,11 +401,11 @@ bool TIFF_MemoryReader::GetTag_Float ( XMP_Uns8 ifd, XMP_Uns16 id, float* data )
const TweakedIFDEntry* thisTag = this->FindTagInIFD ( ifd, id );
if ( thisTag == 0 ) return false;
if ( (thisTag->type != kTIFF_FloatType) || (thisTag->bytes != 4) ) return false;
-
+
if ( data != 0 ) {
*data = this->GetFloat ( this->GetDataPtr ( thisTag ) );
}
-
+
return true;
} // TIFF_MemoryReader::GetTag_Float
@@ -417,12 +419,12 @@ bool TIFF_MemoryReader::GetTag_Double ( XMP_Uns8 ifd, XMP_Uns16 id, double* data
const TweakedIFDEntry* thisTag = this->FindTagInIFD ( ifd, id );
if ( thisTag == 0 ) return false;
if ( (thisTag->type != kTIFF_DoubleType) || (thisTag->bytes != 8) ) return false;
-
+
if ( data != 0 ) {
double* dataPtr = (double*) this->GetDataPtr ( thisTag );
*data = this->GetDouble ( dataPtr );
}
-
+
return true;
} // TIFF_MemoryReader::GetTag_Double
@@ -436,13 +438,13 @@ bool TIFF_MemoryReader::GetTag_ASCII ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_StringPtr
const TweakedIFDEntry* thisTag = this->FindTagInIFD ( ifd, id );
if ( thisTag == 0 ) return false;
if ( thisTag->type != kTIFF_ASCIIType ) return false;
-
+
if ( dataPtr != 0 ) {
*dataPtr = (XMP_StringPtr) this->GetDataPtr ( thisTag );
}
-
+
if ( dataLen != 0 ) *dataLen = thisTag->bytes;
-
+
return true;
} // TIFF_MemoryReader::GetTag_ASCII
@@ -456,9 +458,9 @@ bool TIFF_MemoryReader::GetTag_EncodedString ( XMP_Uns8 ifd, XMP_Uns16 id, std::
const TweakedIFDEntry* thisTag = this->FindTagInIFD ( ifd, id );
if ( thisTag == 0 ) return false;
if ( thisTag->type != kTIFF_UndefinedType ) return false;
-
+
if ( utf8Str == 0 ) return true; // Return true if the converted string is not wanted.
-
+
bool ok = this->DecodeString ( this->GetDataPtr ( thisTag ), thisTag->bytes, utf8Str );
return ok;
@@ -473,21 +475,21 @@ bool TIFF_MemoryReader::GetTag_EncodedString ( XMP_Uns8 ifd, XMP_Uns16 id, std::
void TIFF_MemoryReader::ParseMemoryStream ( const void* data, XMP_Uns32 length, bool copyData /* = true */ )
{
// Get rid of any current TIFF.
-
+
if ( this->ownedStream ) free ( this->tiffStream );
this->ownedStream = false;
this->tiffStream = 0;
this->tiffLength = 0;
-
+
for ( size_t i = 0; i < kTIFF_KnownIFDCount; ++i ) {
this->containedIFDs[i].count = 0;
this->containedIFDs[i].entries = 0;
}
-
+
if ( length == 0 ) return;
// Allocate space for the full in-memory stream and copy it.
-
+
if ( ! copyData ) {
XMP_Assert ( ! this->ownedStream );
this->tiffStream = (XMP_Uns8*) data;
@@ -500,13 +502,16 @@ void TIFF_MemoryReader::ParseMemoryStream ( const void* data, XMP_Uns32 length,
}
this->tiffLength = length;
-
+
// Find and process the primary, Exif, GPS, and Interoperability IFDs.
-
+
XMP_Uns32 primaryIFDOffset = this->CheckTIFFHeader ( this->tiffStream, length );
XMP_Uns32 tnailIFDOffset = 0;
-
+
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 );
const TweakedIFDEntry* exifIFDTag = this->FindTagInIFD ( kTIFF_PrimaryIFD, kTIFF_ExifIFDPointer );
if ( (exifIFDTag != 0) && (exifIFDTag->type == kTIFF_LongType) && (exifIFDTag->bytes == 4) ) {
@@ -525,18 +530,6 @@ void TIFF_MemoryReader::ParseMemoryStream ( const void* data, XMP_Uns32 length,
XMP_Uns32 interopOffset = this->GetUns32 ( &interopIFDTag->dataOrPos );
(void) this->ProcessOneIFD ( interopOffset, kTIFF_InteropIFD );
}
-
- // Process the thumbnail IFD. We only do this for Exif-compliant TIFF streams. Extract the
- // JPEG thumbnail image pointer (tag 513) for later use by GetTNailInfo.
-
- if ( (tnailIFDOffset != 0) && (this->containedIFDs[kTIFF_ExifIFD].count > 0) ) {
- (void) this->ProcessOneIFD ( tnailIFDOffset, kTIFF_TNailIFD );
- const TweakedIFDEntry* jpegInfo = FindTagInIFD ( kTIFF_TNailIFD, kTIFF_JPEGInterchangeFormat );
- if ( jpegInfo != 0 ) {
- XMP_Uns32 tnailImageOffset = this->GetUns32 ( &jpegInfo->dataOrPos );
- this->jpegTNailPtr = (XMP_Uns8*)this->tiffStream + tnailImageOffset;
- }
- }
} // TIFF_MemoryReader::ParseMemoryStream
@@ -547,11 +540,11 @@ void TIFF_MemoryReader::ParseMemoryStream ( const void* data, XMP_Uns32 length,
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_Uns8* ifdPtr = this->tiffStream + ifdOffset;
XMP_Uns16 ifdCount = this->GetUns16 ( ifdPtr );
TweakedIFDEntry* ifdEntries = (TweakedIFDEntry*)(ifdPtr+2);
@@ -561,11 +554,11 @@ XMP_Uns32 TIFF_MemoryReader::ProcessOneIFD ( XMP_Uns32 ifdOffset, XMP_Uns8 ifd )
ifdInfo.count = ifdCount;
ifdInfo.entries = ifdEntries;
-
+
XMP_Int32 prevTag = -1; // ! The GPS IFD has a tag 0, so we need a signed initial value.
bool needsSorting = false;
for ( size_t i = 0; i < ifdCount; ++i ) {
-
+
TweakedIFDEntry* thisEntry = &ifdEntries[i]; // Tweak the IFD entry to be more useful.
if ( ! this->nativeEndian ) {
@@ -573,7 +566,7 @@ XMP_Uns32 TIFF_MemoryReader::ProcessOneIFD ( XMP_Uns32 ifdOffset, XMP_Uns8 ifd )
Flip2 ( &thisEntry->type );
Flip4 ( &thisEntry->bytes );
}
-
+
if ( thisEntry->id <= prevTag ) needsSorting = true;
prevTag = thisEntry->id;
@@ -586,12 +579,12 @@ XMP_Uns32 TIFF_MemoryReader::ProcessOneIFD ( XMP_Uns32 ifdOffset, XMP_Uns8 ifd )
}
}
-
+
ifdPtr += (2 + ifdCount*12);
XMP_Uns32 nextIFDOffset = this->GetUns32 ( ifdPtr );
-
+
if ( needsSorting ) SortIFD ( &ifdInfo ); // ! Don't perturb the ifdCount used to find the next IFD offset.
-
+
return nextIFDOffset;
} // TIFF_MemoryReader::ProcessOneIFD
diff --git a/source/XMPFiles/FormatSupport/TIFF_Support.cpp b/source/XMPFiles/FormatSupport/TIFF_Support.cpp
index 8aecc10..87a96c9 100644
--- a/source/XMPFiles/FormatSupport/TIFF_Support.cpp
+++ b/source/XMPFiles/FormatSupport/TIFF_Support.cpp
@@ -1,6 +1,6 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2006-2007 Adobe Systems Incorporated
+// Copyright 2006 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -25,10 +25,9 @@
static bool sFirstCTor = true;
TIFF_Manager::TIFF_Manager()
- : bigEndian(false), nativeEndian(false), jpegTNailPtr(0),
+ : bigEndian(false), nativeEndian(false),
GetUns16(0), GetUns32(0), GetFloat(0), GetDouble(0),
- PutUns16(0), PutUns32(0), PutFloat(0), PutDouble(0),
- xmpHadUserComment(false), xmpHadRelatedSoundFile(false)
+ PutUns16(0), PutUns32(0), PutFloat(0), PutDouble(0)
{
if ( sFirstCTor ) {
@@ -318,10 +317,20 @@ bool TIFF_Manager::DecodeString ( const void * encodedPtr, size_t encodedLen, st
} else if ( *typePtr == 'U' ) {
try {
+
const UTF16Unit * utf16Ptr = (const UTF16Unit *) valuePtr;
size_t utf16Len = valueLen >> 1; // The number of UTF-16 storage units, not bytes.
- UTF16_to_UTF8 ( utf16Ptr, utf16Len, this->bigEndian, utf8Str );
+ if ( utf16Len == 0 ) return false;
+ bool isBigEndian = this->bigEndian; // Default to stream endian, unless there is a BOM ...
+ if ( (*utf16Ptr == 0xFEFF) || (*utf16Ptr == 0xFFFE) ) { // Check for an explicit BOM
+ isBigEndian = (*((XMP_Uns8*)utf16Ptr) == 0xFE);
+ utf16Ptr += 1; // Don't translate the BOM.
+ utf16Len -= 1;
+ if ( utf16Len == 0 ) return false;
+ }
+ UTF16_to_UTF8 ( utf16Ptr, utf16Len, isBigEndian, utf8Str );
return true;
+
} catch ( ... ) {
return false; // Ignore the tag if there are conversion errors.
}
@@ -423,204 +432,3 @@ bool TIFF_Manager::EncodeString ( const std::string& utf8Str, XMP_Uns8 encoding,
} // TIFF_Manager::EncodeString
// =================================================================================================
-// GetJPEGDimensions
-// =================
-//
-// Get the internal dimensions for a JPEG compressed image. These are the X (width) and Y (height)
-// components of the SOFn marker segment. A 0 value for Y says that the height is in the NL
-// component of the DNL marker segment at the end of the first scan. We'll use the first SOF, in
-// the case of hierarchical JPEG which has multiple frames.
-//
-// For this logic a JPEG stream is:
-// SOI standalone marker
-// Optional marker segments
-// First frame:
-// SOFn marker segment
-// First scan:
-// Optional marker segments
-// SOS marker segment
-// Image data and RST standalone markers
-// Optional DNL marker segment
-// Optional additional scans
-// Optional additional frames
-// EOI standalone marker
-//
-// There is no explicit length for the image data portion of a scan. It ends at the first non-RST
-// marker. So we look no further than the first non-RST marker after the first SOS marker segment.
-// That is the one and only DNL marker segment, if it exists. Hopefully we stop at the first SOFn.
-//
-// The first 5 bytes of the SOFn contents are:
-// Uns8 - ignored here
-// Uns16 - Y, height, big endian
-// Uns16 - X, width, big endian
-//
-// A DNL marker segment contains just 2 bytes of data, the big endian Uns16 number of lines.
-
-static void GetJPEGDimensions ( const void * jpegStream, size_t jpegLength, XMP_Uns32 * width, XMP_Uns32 * height )
-{
- const XMP_Uns8 * jpegPtr = (const XMP_Uns8 *) jpegStream;
- const XMP_Uns8 * jpegEnd = jpegPtr + jpegLength;
-
- XMP_Uns16 marker, length;
-
- *width = *height = 0; // Assume the worst.
-
- marker = GetUns16BE ( jpegPtr );
- if ( marker != 0xFFD8 ) return; // Check for the SOI.
- jpegPtr += 2;
-
- // Scan for the first SOFn marker and extract the Y and X components.
-
- while ( jpegPtr < jpegEnd ) {
- marker = GetUns16BE ( jpegPtr );
- if ( ((marker & 0xFFF0) == 0xFFC0) &&
- (marker != 0xFFC4) && (marker != 0xFFC8) && (marker != 0xFFCC) ) break;
- jpegPtr += 2;
- if ( (jpegPtr < jpegEnd) && ((marker & 0xFFF8) != 0xFFD0) &&
- (marker != 0xFF01) && (marker != 0xFFD8) && (marker != 0xFFD9) ) {
- jpegPtr += GetUns16BE ( jpegPtr );
- }
- }
-
- if ( jpegPtr >= jpegEnd ) return; // Ran out of data.
- if ( (marker & 0xFFF0) != 0xFFC0 ) return; // Not an SOFn marker.
- jpegPtr += 2;
- length = GetUns16BE ( jpegPtr );
- if ( length < 7 ) return; // Bad length, the SOFn marker segment is too short.
-
- *height = GetUns16BE ( jpegPtr+3 );
- *width = GetUns16BE ( jpegPtr+5 );
- if ( *height != 0 ) return; // Done if the Y component is non-zero.
- jpegPtr += length;
-
- // Need to look for a DNL marker segment. Scan for the first SOS marker.
-
- while ( jpegPtr < jpegEnd ) {
- marker = GetUns16BE ( jpegPtr );
- if ( marker == 0xFFDA ) break;
- jpegPtr += 2;
- if ( (jpegPtr < jpegEnd) && ((marker & 0xFFF8) != 0xFFD0) &&
- (marker != 0xFF01) && (marker != 0xFFD8) && (marker != 0xFFD9) ) {
- jpegPtr += GetUns16BE ( jpegPtr );
- }
- }
-
- if ( jpegPtr >= jpegEnd ) return; // Ran out of data.
- if ( marker != 0xFFDA ) return; // Not an SOS marker.
- jpegPtr += 2;
- length = GetUns16BE ( jpegPtr );
- jpegPtr += length;
-
- // Now look for a non-RST marker. We're in the image data, must scan one byte at a time.
-
- while ( jpegPtr < jpegEnd ) {
- if ( *jpegPtr != 0xFF ) {
- ++jpegPtr;
- } else {
- marker = GetUns16BE ( jpegPtr );
- if ( (0xFF01 <= marker) && (marker <= 0xFFFE) && ((marker & 0xFFF8) != 0xFFD0) ) break;
- jpegPtr += 2;
- }
- }
-
- if ( jpegPtr >= jpegEnd ) return; // Ran out of data.
- if ( marker != 0xFFDC ) return; // Not a DNL marker.
- jpegPtr += 2;
- length = GetUns16BE ( jpegPtr );
- if ( length != 4 ) return; // Bad DNL marker segment length.
-
- *height = GetUns16BE ( jpegPtr+2 );
-
-} // GetJPEGDimensions
-
-// =================================================================================================
-// TIFF_Manager::GetTNailInfo
-// ==========================
-//
-// Gather the info for a native Exif thumbnail, if there is one. We only return full info for a JPEG
-// compressed thumbnail.
-// - There must be at least 2 top level IFDs, the second is the thumbnail.
-// - The Exif IFD must be present.
-// - The thumbnail IFD must have tag 259, Compression.
-// - A JPEG compressed thumbnail must have tags 513 and 514, JPEGInterchangeFormat and JPEGInterchangeFormatLength.
-//
-// Tag 259 (Compression) in the thumbnail IFD defines the thumbnail compression scheme. It is 1 for
-// uncompressed and 6 for JPEG compressed. If the thumbnail is JPEG compressed, then tag 513
-// (JPEGInterchangeFormat) in the thumbnail IFD is the offset of the thumbnail image stream (to the
-// SOI) and tag 514 (JPEGInterchangeFormatLength) is the length of the stream in bytes. Yes,
-// another stupid Exif mistake of putting an explicit offset in the TIFF info (type LONG, count 1)
-// instead of a properly typed data block!
-//
-// The full image dimensions for an Exif-compliant compressed JPEG image are in tags 40962
-// (PixelXDimension) and 40963 (PixelYDimension) of the Exif IFD.
-//
-// The dimensions of an Exif-compliant uncompressed (TIFF) thumbnail are in tags 256 (ImageWidth)
-// and 257 (ImageLength) of the thumbnail IFD. The dimensions of an Exif-compliant compressed
-// (JPEG) thumbnail are within the JPEG stream of the thumbnail. The JPEG dimensions should be in
-// the X (width) and Y (height) components of the SOF marker segment. A 0 value for Y says that the
-// height is in the NL component of the DNL marker segment at the end of the first scan.
-
-bool TIFF_Manager::GetTNailInfo ( XMP_ThumbnailInfo * tnailInfo ) const
-{
- bool found;
- XMP_Uns16 compression;
-
- enum { kUncompressedTNail = 1, kJPEGCompressedTNail = 6 };
-
- if ( tnailInfo == 0 ) return false;
-
- // Make sure the required IFDs and tags are present.
-
- if ( (! this->HasExifIFD()) || (! this->HasThumbnailIFD()) ) return false;
-
- found = this->GetTag_Short ( kTIFF_TNailIFD, kTIFF_Compression, &compression );
- if ( ! found ) return false;
- if ( (compression != kUncompressedTNail) && (compression != kJPEGCompressedTNail) ) return false;
-
- // Gather the info that depends on the thumbnail format.
-
- if ( compression == kUncompressedTNail ) {
-
- // Gather the info for an uncompressed thumbnail. Just the format, width, and height.
-
- tnailInfo->tnailFormat = kXMP_TIFFTNail;
- (void) this->GetTag_Integer ( kTIFF_TNailIFD, kTIFF_ImageWidth, &tnailInfo->tnailWidth );
- (void) this->GetTag_Integer ( kTIFF_TNailIFD, kTIFF_ImageLength, &tnailInfo->tnailHeight );
-
- } else {
-
- // Gather the info for a JPEG compressed thumbnail. The JPEG stream pointer is special, the
- // type/count of tag 513 is LONG/1 - thank once again Exif! The pointer was set when parsing
- // the TIFF stream. That is when we have to capture the stream for file-based TIFF.
-
- XMP_Uns32 jpegOffset, jpegLength;
- found = this->GetTag_Long ( kTIFF_TNailIFD, kTIFF_JPEGInterchangeFormat, &jpegOffset );
- if ( ! found ) return false;
- found = this->GetTag_Long ( kTIFF_TNailIFD, kTIFF_JPEGInterchangeFormatLength, &jpegLength );
- if ( ! found ) return false;
-
- XMP_Assert ( this->jpegTNailPtr != 0 );
-
- tnailInfo->tnailFormat = kXMP_JPEGTNail;
- tnailInfo->tnailImage = this->jpegTNailPtr;
- tnailInfo->tnailSize = jpegLength;
-
- GetJPEGDimensions ( tnailInfo->tnailImage, tnailInfo->tnailSize,
- &tnailInfo->tnailWidth, &tnailInfo->tnailHeight );
-
- }
-
- // If we get here there is a thumbnail of some sort. Gether remaining common info.
-
- (void) this->GetTag_Integer ( kTIFF_ExifIFD, kTIFF_PixelXDimension, &tnailInfo->fullWidth );
- (void) this->GetTag_Integer ( kTIFF_ExifIFD, kTIFF_PixelYDimension, &tnailInfo->fullHeight );
- (void) this->GetTag_Short ( kTIFF_PrimaryIFD, kTIFF_Orientation, &tnailInfo->fullOrientation );
-
- found = this->GetTag_Short ( kTIFF_TNailIFD, kTIFF_Orientation, &tnailInfo->tnailOrientation );
- if ( ! found ) tnailInfo->tnailOrientation = tnailInfo->fullOrientation;
-
- return true;
-
-} // TIFF_Manager::GetTNailInfo
-
-// =================================================================================================
diff --git a/source/XMPFiles/FormatSupport/TIFF_Support.hpp b/source/XMPFiles/FormatSupport/TIFF_Support.hpp
index 1394b99..94f6198 100644
--- a/source/XMPFiles/FormatSupport/TIFF_Support.hpp
+++ b/source/XMPFiles/FormatSupport/TIFF_Support.hpp
@@ -3,7 +3,7 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2006-2008 Adobe Systems Incorporated
+// Copyright 2006 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -38,15 +38,12 @@
/// entirely in memory. Think of TIFF_FileWriter as "file-based OR read-write". TIFF_FileWriter only
/// maintains information for tags of interest as metadata.
///
-/// The needs of XMPFiles are well defined metadata access. Only 5 IFDs are recognized:
+/// The needs of XMPFiles are well defined metadata access. Only 4 IFDs are processed:
/// \li The 0th IFD, for the primary image, the first one in the outer list of IFDs.
-/// \li The 1st IFD, for an Exif thumbnail, the second one in the outer list of IFDs.
/// \li The Exif general metadata IFD, from tag 34665 in the primary image IFD.
/// \li The Exif GPS Info metadata IFD, from tag 34853 in the primary image IFD.
/// \li The Exif Interoperability IFD, from tag 40965 in the Exif general metadata IFD.
///
-/// \note In the future we should add support for the non-Exif thumbnails in DNG (TIFF/EP) files.
-///
/// \note These classes are for use only when directly compiled and linked. They should not be
/// packaged in a DLL by themselves. They do not provide any form of C++ ABI protection.
// =================================================================================================
@@ -60,7 +57,7 @@
enum { // Constants for the recognized IFDs.
kTIFF_PrimaryIFD = 0, // The primary image IFD, also called the 0th IFD.
- kTIFF_TNailIFD = 1, // The thumbnail image IFD also called the 1st IFD.
+ kTIFF_TNailIFD = 1, // The thumbnail image IFD also called the 1st IFD. (not used)
kTIFF_ExifIFD = 2, // The Exif general metadata IFD.
kTIFF_GPSInfoIFD = 3, // The Exif GPS Info IFD.
kTIFF_InteropIFD = 4, // The Exif Interoperability IFD.
@@ -143,7 +140,7 @@ enum {
kTIFF_GPSInfoIFDPointer = 34853,
kTIFF_DNGVersion = 50706,
kTIFF_DNGBackwardVersion = 50707,
-
+
// Additional thumbnail IFD tags. We also care about 256, 257, and 259 in thumbnails.
kTIFF_JPEGInterchangeFormat = 513,
kTIFF_JPEGInterchangeFormatLength = 514,
@@ -218,7 +215,7 @@ enum {
kTIFF_DeviceSettingDescription = 41995,
kTIFF_SubjectDistanceRange = 41996,
kTIFF_ImageUniqueID = 42016,
-
+
kTIFF_MakerNote = 37500, // Gets deleted when rewriting memory-based TIFF.
// GPS IFD tags.
@@ -254,7 +251,7 @@ enum {
kTIFF_GPSAreaInformation = 28,
kTIFF_GPSDateStamp = 29,
kTIFF_GPSDifferential = 30
-
+
};
// ------------------------------------------------------------------
@@ -437,7 +434,7 @@ public:
static const size_t kEmptyTIFFLength = 8; // Just the header.
static const size_t kEmptyIFDLength = 2 + 4; // Entry count and next-IFD offset.
static const size_t kIFDEntryLength = 12;
-
+
struct TagInfo {
XMP_Uns16 id;
XMP_Uns16 type;
@@ -448,7 +445,7 @@ public:
TagInfo ( XMP_Uns16 _id, XMP_Uns16 _type, XMP_Uns32 _count, const void* _dataPtr, XMP_Uns32 _dataLen )
: id(_id), type(_type), count(_count), dataPtr(_dataPtr), dataLen(_dataLen) {};
};
-
+
typedef std::map<XMP_Uns16,TagInfo> TagInfoMap;
struct Rational { XMP_Uns32 num, denom; };
@@ -458,15 +455,14 @@ public:
// The IsXyzEndian methods return the external endianness of the original parsed TIFF stream.
// The \c GetTag methods return native endian values, the \c SetTag methods take native values.
// The original endianness is preserved in output.
-
+
bool IsBigEndian() const { return this->bigEndian; };
bool IsLittleEndian() const { return (! this->bigEndian); };
bool IsNativeEndian() const { return this->nativeEndian; };
-
+
// ---------------------------------------------------------------------------------------------
- // The TIFF_Manager only keeps explicit knowledge of up to 5 IFDs:
+ // The TIFF_Manager only keeps explicit knowledge of up to 4 IFDs:
// - The primary image IFD, also known as the 0th IFD. This must be present.
- // - A possible thumbnail IFD, also known as the 1st IFD, chained from the primary image IFD.
// - A possible Exif general metadata IFD, found from tag 34665 in the primary image IFD.
// - A possible Exif GPS metadata IFD, found from tag 34853 in the primary image IFD.
// - A possible Exif Interoperability IFD, found from tag 40965 in the Exif general metadata IFD.
@@ -476,11 +472,10 @@ public:
// removed. Parsing will sort the tags into ascending order, AppendTIFF and ComposeTIFF will
// preserve the sorted order. These fixes do not cause IsChanged to return true, that only
// happens if the client makes explicit changes using SetTag or DeleteTag.
-
- virtual bool HasThumbnailIFD() const = 0;
+
virtual bool HasExifIFD() const = 0;
virtual bool HasGPSInfoIFD() const = 0;
-
+
// ---------------------------------------------------------------------------------------------
// These are the basic methods to get a map of all of the tags in an IFD, to get or set a tag,
// or to delete a tag. The dataPtr returned by \c GetTag is consided read-only, the client must
@@ -491,17 +486,17 @@ public:
// \c SetTag replaces an existing tag regardless of type or count. \c DeleteTag deletes a tag,
// it is a no-op if the tag does not exist. \c GetValueOffset returns the offset within the
// parsed stream of the tag's value. It returns 0 if the tag was not in the parsed input.
-
+
virtual bool GetIFD ( XMP_Uns8 ifd, TagInfoMap* ifdMap ) const = 0;
-
+
virtual bool GetTag ( XMP_Uns8 ifd, XMP_Uns16 id, TagInfo* info ) const = 0;
virtual void SetTag ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns16 type, XMP_Uns32 count, const void* dataPtr ) = 0;
-
+
virtual void DeleteTag ( XMP_Uns8 ifd, XMP_Uns16 id ) = 0;
-
+
virtual XMP_Uns32 GetValueOffset ( XMP_Uns8 ifd, XMP_Uns16 id ) const = 0;
-
+
// ---------------------------------------------------------------------------------------------
// These methods are for tags whose type can be short or long, depending on the actual value.
// \c GetTag_Integer returns false if an existing tag's type is not short, or long, or if the
@@ -511,7 +506,7 @@ public:
virtual bool GetTag_Integer ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns32* data ) const = 0;
void SetTag_Integer ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns32 data );
-
+
// ---------------------------------------------------------------------------------------------
// These are customized forms of GetTag that verify the type and return a typed value. False is
// returned if the type does not match or if the count is not 1.
@@ -532,7 +527,7 @@ public:
virtual bool GetTag_Double ( XMP_Uns8 ifd, XMP_Uns16 id, double* data ) const = 0;
virtual bool GetTag_ASCII ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_StringPtr* dataPtr, XMP_StringLen* dataLen ) const = 0;
-
+
// ---------------------------------------------------------------------------------------------
void SetTag_Byte ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns8 data );
@@ -549,18 +544,14 @@ public:
void SetTag_Double ( XMP_Uns8 ifd, XMP_Uns16 id, double data );
void SetTag_ASCII ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_StringPtr dataPtr );
-
+
// ---------------------------------------------------------------------------------------------
virtual bool GetTag_EncodedString ( XMP_Uns8 ifd, XMP_Uns16 id, std::string* utf8Str ) const = 0;
virtual void SetTag_EncodedString ( XMP_Uns8 ifd, XMP_Uns16 id, const std::string& utf8Str, XMP_Uns8 encoding ) = 0;
-
+
bool DecodeString ( const void * encodedPtr, size_t encodedLen, std::string* utf8Str ) const;
bool EncodeString ( const std::string& utf8Str, XMP_Uns8 encoding, std::string* encodedStr );
-
- // ---------------------------------------------------------------------------------------------
-
- bool GetTNailInfo ( XMP_ThumbnailInfo * tnailInfo ) const;
// ---------------------------------------------------------------------------------------------
// \c IsChanged returns true if a read-write stream has changes that need to be saved. This is
@@ -596,17 +587,17 @@ public:
// The condenseStream parameter to UpdateMemoryStream can be used to rewrite the full stream
// instead of appending. This will discard any MakerNote tags and risks breaking offsets that
// are hidden. This can be necessary though to try to make the TIFF fit in a JPEG file.
-
+
virtual void ParseMemoryStream ( const void* data, XMP_Uns32 length, bool copyData = true ) = 0;
virtual void ParseFileStream ( LFA_FileRef fileRef ) = 0;
-
+
virtual void IntegrateFromPShop6 ( const void * buriedPtr, size_t buriedLen ) = 0;
-
+
virtual XMP_Uns32 UpdateMemoryStream ( void** dataPtr, bool condenseStream = false ) = 0;
virtual void UpdateFileStream ( LFA_FileRef fileRef ) = 0;
-
+
// ---------------------------------------------------------------------------------------------
-
+
GetUns16_Proc GetUns16; // Get values from the TIFF stream.
GetUns32_Proc GetUns32; // Always native endian on the outside, stream endian in the stream.
GetFloat_Proc GetFloat;
@@ -618,18 +609,13 @@ public:
PutDouble_Proc PutDouble;
virtual ~TIFF_Manager() {};
-
- // ! Hacks to help the reconciliation code accomodate Photoshop behavior:
- bool xmpHadUserComment, xmpHadRelatedSoundFile;
protected:
bool bigEndian, nativeEndian;
-
- XMP_Uns8 * jpegTNailPtr;
XMP_Uns32 CheckTIFFHeader ( const XMP_Uns8* tiffPtr, XMP_Uns32 length );
-
+
TIFF_Manager(); // Force clients to use the reader or writer derived classes.
struct RawIFDEntry {
@@ -653,18 +639,17 @@ protected:
class TIFF_MemoryReader : public TIFF_Manager { // The derived class for memory-based read-only access.
public:
- bool HasThumbnailIFD() const { return (containedIFDs[kTIFF_TNailIFD].count != 0); };
bool HasExifIFD() const { return (containedIFDs[kTIFF_ExifIFD].count != 0); };
bool HasGPSInfoIFD() const { return (containedIFDs[kTIFF_GPSInfoIFD].count != 0); };
bool GetIFD ( XMP_Uns8 ifd, TagInfoMap* ifdMap ) const;
-
+
bool GetTag ( XMP_Uns8 ifd, XMP_Uns16 id, TagInfo* info ) const;
void SetTag ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns16 type, XMP_Uns32 count, const void* dataPtr ) { NotAppropriate(); };
-
+
void DeleteTag ( XMP_Uns8 ifd, XMP_Uns16 id ) { NotAppropriate(); };
-
+
XMP_Uns32 GetValueOffset ( XMP_Uns8 ifd, XMP_Uns16 id ) const;
bool GetTag_Integer ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns32* data ) const;
@@ -687,18 +672,18 @@ public:
bool GetTag_EncodedString ( XMP_Uns8 ifd, XMP_Uns16 id, std::string* utf8Str ) const;
void SetTag_EncodedString ( XMP_Uns8 ifd, XMP_Uns16 id, const std::string& utf8Str, XMP_Uns8 encoding ) { NotAppropriate(); };
-
+
bool IsChanged() { return false; };
bool IsLegacyChanged() { return false; };
-
+
void ParseMemoryStream ( const void* data, XMP_Uns32 length, bool copyData = true );
void ParseFileStream ( LFA_FileRef fileRef ) { NotAppropriate(); };
-
+
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 ( LFA_FileRef fileRef ) { NotAppropriate(); };
-
+
TIFF_MemoryReader() : ownedStream(false), tiffStream(0), tiffLength(0) {};
virtual ~TIFF_MemoryReader() { if ( this->ownedStream ) free ( this->tiffStream ); };
@@ -722,26 +707,26 @@ private:
XMP_Uns32 dataOrPos;
TweakedIFDEntry() : id(0), type(0), bytes(0), dataOrPos(0) {};
};
-
+
struct TweakedIFDInfo {
XMP_Uns16 count;
TweakedIFDEntry* entries;
TweakedIFDInfo() : count(0), entries(0) {};
};
-
+
TweakedIFDInfo containedIFDs[kTIFF_KnownIFDCount];
static void SortIFD ( TweakedIFDInfo* thisIFD );
XMP_Uns32 ProcessOneIFD ( XMP_Uns32 ifdOffset, XMP_Uns8 ifd );
-
+
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); };
static inline void NotAppropriate() { XMP_Throw ( "Not appropriate for TIFF_Reader", kXMPErr_InternalFailure ); };
-
+
}; // TIFF_MemoryReader
@@ -756,18 +741,17 @@ private:
class TIFF_FileWriter : public TIFF_Manager { // The derived class for file-based or read-write access.
public:
- bool HasThumbnailIFD() const { return this->containedIFDs[kTIFF_TNailIFD].tagMap.size() != 0; };
bool HasExifIFD() const { return this->containedIFDs[kTIFF_ExifIFD].tagMap.size() != 0; };
bool HasGPSInfoIFD() const { return this->containedIFDs[kTIFF_GPSInfoIFD].tagMap.size() != 0; };
bool GetIFD ( XMP_Uns8 ifd, TagInfoMap* ifdMap ) const;
-
+
bool GetTag ( XMP_Uns8 ifd, XMP_Uns16 id, TagInfo* info ) const;
void SetTag ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns16 type, XMP_Uns32 count, const void* dataPtr );
-
+
void DeleteTag ( XMP_Uns8 ifd, XMP_Uns16 id );
-
+
XMP_Uns32 GetValueOffset ( XMP_Uns8 ifd, XMP_Uns16 id ) const;
bool GetTag_Integer ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns32* data ) const;
@@ -790,13 +774,13 @@ public:
bool GetTag_EncodedString ( XMP_Uns8 ifd, XMP_Uns16 id, std::string* utf8Str ) const;
void SetTag_EncodedString ( XMP_Uns8 ifd, XMP_Uns16 id, const std::string& utf8Str, XMP_Uns8 encoding );
-
+
bool IsChanged() { return this->changed; };
-
+
bool IsLegacyChanged();
-
+
enum { kDoNotCopyData = false };
-
+
void ParseMemoryStream ( const void* data, XMP_Uns32 length, bool copyData = true );
void ParseFileStream ( LFA_FileRef fileRef );
@@ -824,13 +808,13 @@ private:
// the smallValue field for small values. This is also the usage when a tag is changed (for both
// memory and file cases), the dataPtr is a separate allocation for large values (over 4 bytes),
// and points to the smallValue field for small values.
-
+
// ! The working data values are always stream endian, no matter where stored. They are flipped
// ! as necessary by GetTag and SetTag.
-
+
static const bool kIsFileBased = true; // For use in the InternalTagInfo constructor.
static const bool kIsMemoryBased = false;
-
+
class InternalTagInfo {
public:
@@ -875,9 +859,9 @@ private:
origDataLen(0), origDataOffset(0), changed(false), fileBased(false) {};
};
-
+
typedef std::map<XMP_Uns16,InternalTagInfo> InternalTagMap;
-
+
struct InternalIFDInfo {
bool changed;
XMP_Uns16 origCount; // Original number of IFD entries.
@@ -893,12 +877,12 @@ private:
this->tagMap.clear();
};
};
-
+
InternalIFDInfo containedIFDs[kTIFF_KnownIFDCount];
-
+
static XMP_Uns8 PickIFD ( XMP_Uns8 ifd, XMP_Uns16 id );
const InternalTagInfo* FindTagInIFD ( XMP_Uns8 ifd, XMP_Uns16 id ) const;
-
+
void DeleteExistingInfo();
XMP_Uns32 ProcessMemoryIFD ( XMP_Uns32 ifdOffset, XMP_Uns8 ifd );
@@ -908,10 +892,8 @@ private:
void* CopyTagToMasterIFD ( const TagInfo& ps6Tag, InternalIFDInfo* masterIFD );
- void UpdateMemByAppend ( XMP_Uns8** newStream_out, XMP_Uns32* newLength_out,
- bool appendAll = false, XMP_Uns32 extraSpace = 0 );
- void UpdateMemByRewrite ( XMP_Uns8** newStream_out, XMP_Uns32* newLength_out );
-
+ void PreflightIFDLinkage();
+
XMP_Uns32 DetermineVisibleLength();
XMP_Uns32 DetermineAppendInfo ( XMP_Uns32 appendedOrigin,
@@ -919,8 +901,12 @@ private:
XMP_Uns32 newIFDOffsets[kTIFF_KnownIFDCount],
bool appendAll = false );
+ void UpdateMemByAppend ( XMP_Uns8** newStream_out, XMP_Uns32* newLength_out,
+ bool appendAll = false, XMP_Uns32 extraSpace = 0 );
+ void UpdateMemByRewrite ( XMP_Uns8** newStream_out, XMP_Uns32* newLength_out );
+
void WriteFileIFD ( LFA_FileRef fileRef, InternalIFDInfo & thisIFD );
-
+
}; // TIFF_FileWriter
diff --git a/source/XMPFiles/FormatSupport/XDCAM_Support.cpp b/source/XMPFiles/FormatSupport/XDCAM_Support.cpp
index ac0fe1e..b86e69e 100644
--- a/source/XMPFiles/FormatSupport/XDCAM_Support.cpp
+++ b/source/XMPFiles/FormatSupport/XDCAM_Support.cpp
@@ -1,6 +1,6 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2006-2008 Adobe Systems Incorporated
+// Copyright 2008 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -94,7 +94,7 @@ bool GetLegacyMetaData ( SXMPMeta * xmpObjPtr,
}
// Modify Date
- if ( digestFound || (! xmpObjPtr->DoesPropertyExist ( kXMP_NS_XMP, "LastUpdate" )) ) {
+ if ( digestFound || (! xmpObjPtr->DoesPropertyExist ( kXMP_NS_XMP, "ModifyDate" )) ) {
legacyProp = rootElem->GetNamedElement ( legacyNS, "LastUpdate" );
if ( (legacyProp != 0) && legacyProp->IsEmptyLeafNode() ) {
XMP_StringPtr legacyValue = legacyProp->GetAttrValue ( "value" );
@@ -106,7 +106,7 @@ bool GetLegacyMetaData ( SXMPMeta * xmpObjPtr,
}
// Metadata Modify Date
- if ( digestFound || (! xmpObjPtr->DoesPropertyExist ( kXMP_NS_XMP, "lastUpdate" )) ) {
+ if ( digestFound || (! xmpObjPtr->DoesPropertyExist ( kXMP_NS_XMP, "MetadataDate" )) ) {
legacyProp = rootElem->GetNamedElement ( legacyNS, "lastUpdate" );
if ( (legacyProp != 0) && legacyProp->IsEmptyLeafNode() ) {
XMP_StringPtr legacyValue = legacyProp->GetAttrValue ( "value" );
@@ -117,6 +117,18 @@ bool GetLegacyMetaData ( SXMPMeta * xmpObjPtr,
}
}
+ // Description
+ if ( digestFound || (! xmpObjPtr->DoesPropertyExist ( kXMP_NS_DC, "description" )) ) {
+ legacyProp = rootElem->GetNamedElement ( legacyNS, "Description" );
+ if ( (legacyProp != 0) && legacyProp->IsLeafContentNode() ) {
+ XMP_StringPtr legacyValue = legacyProp->GetLeafContentValue();
+ if ( legacyValue != 0 ) {
+ xmpObjPtr->SetLocalizedText ( kXMP_NS_DC, "description", "", "x-default", legacyValue, kXMP_DeleteExisting );
+ containsXMP = true;
+ }
+ }
+ }
+
legacyContext = rootElem->GetNamedElement ( legacyNS, "VideoFormat" );
if ( legacyContext != 0 ) {
@@ -228,6 +240,54 @@ bool GetLegacyMetaData ( SXMPMeta * xmpObjPtr,
}
+ legacyContext = rootElem->GetNamedElement ( legacyNS, "Device" );
+ if ( legacyContext != 0 ) {
+
+ std::string model;
+
+ // manufacturer string
+ XMP_StringPtr manufacturer = legacyContext->GetAttrValue ( "manufacturer" );
+ if ( manufacturer != 0 ) {
+ model += manufacturer;
+ }
+
+ // model string
+ XMP_StringPtr modelName = legacyContext->GetAttrValue ( "modelName" );
+ if ( modelName != 0 ) {
+ if ( model.size() > 0 ) {
+ model += " ";
+ }
+ model += modelName;
+ }
+
+
+ // For the dm::cameraModel property, concat the make and model.
+ if ( digestFound || (! xmpObjPtr->DoesPropertyExist ( kXMP_NS_DM, "cameraModel" )) ) {
+ if ( model.size() != 0 ) {
+ xmpObjPtr->SetProperty ( kXMP_NS_DM, "cameraModel", model, kXMP_DeleteExisting );
+ containsXMP = true;
+ }
+ }
+
+ // EXIF Model
+ if ( digestFound || (! xmpObjPtr->DoesPropertyExist ( kXMP_NS_TIFF, "Model" )) ) {
+ xmpObjPtr->SetProperty ( kXMP_NS_TIFF, "Model", modelName, kXMP_DeleteExisting );
+ }
+
+ // EXIF Make
+ if ( digestFound || (! xmpObjPtr->DoesPropertyExist ( kXMP_NS_TIFF, "Make" )) ) {
+ xmpObjPtr->SetProperty ( kXMP_NS_TIFF, "Make", manufacturer, kXMP_DeleteExisting );
+ }
+
+ // EXIF-AUX Serial number
+ XMP_StringPtr serialNumber = legacyContext->GetAttrValue ( "serialNo" );
+ if ( serialNumber != 0 && (digestFound || (! xmpObjPtr->DoesPropertyExist ( kXMP_NS_EXIF_Aux, "SerialNumber" ))) ) {
+ xmpObjPtr->SetProperty ( kXMP_NS_EXIF_Aux, "SerialNumber", serialNumber, kXMP_DeleteExisting );
+ }
+
+ }
+
+
return containsXMP;
}
diff --git a/source/XMPFiles/FormatSupport/XDCAM_Support.hpp b/source/XMPFiles/FormatSupport/XDCAM_Support.hpp
index 3fe1e65..51811c7 100644
--- a/source/XMPFiles/FormatSupport/XDCAM_Support.hpp
+++ b/source/XMPFiles/FormatSupport/XDCAM_Support.hpp
@@ -3,7 +3,7 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2006-2007 Adobe Systems Incorporated
+// Copyright 2008 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
diff --git a/source/XMPFiles/FormatSupport/XMPScanner.cpp b/source/XMPFiles/FormatSupport/XMPScanner.cpp
index 4396a25..a67c2c6 100644
--- a/source/XMPFiles/FormatSupport/XMPScanner.cpp
+++ b/source/XMPFiles/FormatSupport/XMPScanner.cpp
@@ -1,5 +1,5 @@
// =================================================================================================
-// Copyright 2002-2007 Adobe Systems Incorporated
+// Copyright 2004 Adobe Systems Incorporated
// All Rights Reserved.
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
diff --git a/source/XMPFiles/FormatSupport/XMPScanner.hpp b/source/XMPFiles/FormatSupport/XMPScanner.hpp
index 2c8b6fa..472a43e 100644
--- a/source/XMPFiles/FormatSupport/XMPScanner.hpp
+++ b/source/XMPFiles/FormatSupport/XMPScanner.hpp
@@ -2,7 +2,7 @@
#define __XMPScanner_hpp__
// =================================================================================================
-// Copyright 2002-2007 Adobe Systems Incorporated
+// Copyright 2004 Adobe Systems Incorporated
// All Rights Reserved.
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
diff --git a/source/XMPFiles/WXMPFiles.cpp b/source/XMPFiles/WXMPFiles.cpp
index 5976b48..4e32607 100644
--- a/source/XMPFiles/WXMPFiles.cpp
+++ b/source/XMPFiles/WXMPFiles.cpp
@@ -1,6 +1,6 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2007 Adobe Systems Incorporated
+// Copyright 2004 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -34,33 +34,23 @@ static WXMP_Result voidResult; // Used for functions that don't use the normal
void WXMPFiles_GetVersionInfo_1 ( XMP_VersionInfo * versionInfo )
{
WXMP_Result * wResult = &voidResult; // ! Needed to "fool" the EnterWrapper macro.
- XMP_ENTER_WRAPPER_NO_LOCK ( "WXMPFiles_GetVersionInfo_1" )
+ XMP_ENTER_NoLock ( "WXMPFiles_GetVersionInfo_1" )
XMPFiles::GetVersionInfo ( versionInfo );
- XMP_EXIT_WRAPPER_NO_THROW
+ XMP_EXIT_NoThrow
}
// -------------------------------------------------------------------------------------------------
-void WXMPFiles_Initialize_1 ( WXMP_Result * wResult )
+void WXMPFiles_Initialize_1 ( XMP_OptionBits options,
+ WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER_NO_LOCK ( "WXMPFiles_Initialize_1" )
-
- wResult->int32Result = XMPFiles::Initialize ( 0 );
-
- XMP_EXIT_WRAPPER
-}
-
-// -------------------------------------------------------------------------------------------------
-
-void WXMPFiles_Initialize_2 ( XMP_OptionBits options, WXMP_Result * wResult )
-{
- XMP_ENTER_WRAPPER_NO_LOCK ( "WXMPFiles_Initialize_1" )
+ XMP_ENTER_NoLock ( "WXMPFiles_Initialize_1" )
wResult->int32Result = XMPFiles::Initialize ( options );
- XMP_EXIT_WRAPPER
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
@@ -68,79 +58,55 @@ void WXMPFiles_Initialize_2 ( XMP_OptionBits options, WXMP_Result * wResult )
void WXMPFiles_Terminate_1()
{
WXMP_Result * wResult = &voidResult; // ! Needed to "fool" the EnterWrapper macro.
- XMP_ENTER_WRAPPER_NO_LOCK ( "WXMPFiles_Terminate_1" )
+ XMP_ENTER_NoLock ( "WXMPFiles_Terminate_1" )
XMPFiles::Terminate();
- XMP_EXIT_WRAPPER_NO_THROW
+ XMP_EXIT_NoThrow
}
// =================================================================================================
void WXMPFiles_CTor_1 ( WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER ( "WXMPFiles_CTor_1" )
+ XMP_ENTER_Static ( "WXMPFiles_CTor_1" ) // No lib object yet, use the static entry.
XMPFiles * newObj = new XMPFiles();
++newObj->clientRefs;
XMP_Assert ( newObj->clientRefs == 1 );
wResult->ptrResult = newObj;
- XMP_EXIT_WRAPPER
-}
-
-// -------------------------------------------------------------------------------------------------
-
-void WXMPFiles_UnlockLib_1()
-{
- WXMP_Result * wResult = &voidResult; // ! Needed to fool the EnterWrapper macro.
- XMP_ENTER_WRAPPER_NO_LOCK ( "WXMPFiles_UnlockLib_1" )
-
- XMPFiles::UnlockLib();
-
- XMP_EXIT_WRAPPER_NO_THROW
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
-void WXMPFiles_UnlockObj_1 ( XMPFilesRef xmpFilesRef )
-{
- WXMP_Result * wResult = &voidResult; // ! Needed to fool the EnterWrapper macro.
- XMP_ENTER_WRAPPER_NO_LOCK ( "WXMPFiles_UnlockObj_1" )
-
- XMPFiles * thiz = (XMPFiles*)xmpFilesRef;
- thiz->UnlockObj();
-
- XMP_EXIT_WRAPPER_NO_THROW
-}
-
-// -------------------------------------------------------------------------------------------------
-
-void WXMPFiles_IncrementRefCount_1 ( XMPFilesRef xmpFilesRef )
+void WXMPFiles_IncrementRefCount_1 ( XMPFilesRef xmpObjRef )
{
WXMP_Result * wResult = &voidResult; // ! Needed to "fool" the EnterWrapper macro.
- XMP_ENTER_WRAPPER ( "WXMPFiles_IncrementRefCount_1" )
+ XMP_ENTER_ObjWrite ( XMPFiles, "WXMPFiles_IncrementRefCount_1" )
- XMPFiles * thiz = (XMPFiles*)xmpFilesRef;
++thiz->clientRefs;
XMP_Assert ( thiz->clientRefs > 0 );
- XMP_EXIT_WRAPPER_NO_THROW
+ XMP_EXIT_NoThrow
}
// -------------------------------------------------------------------------------------------------
-void WXMPFiles_DecrementRefCount_1 ( XMPFilesRef xmpFilesRef )
+void WXMPFiles_DecrementRefCount_1 ( XMPFilesRef xmpObjRef )
{
WXMP_Result * wResult = &voidResult; // ! Needed to "fool" the EnterWrapper macro.
- XMP_ENTER_WRAPPER ( "WXMPFiles_DecrementRefCount_1" )
+ XMP_ENTER_ObjWrite ( XMPFiles, "WXMPFiles_DecrementRefCount_1" )
- XMPFiles * thiz = (XMPFiles*)xmpFilesRef;
XMP_Assert ( thiz->clientRefs > 0 );
--thiz->clientRefs;
- if ( thiz->clientRefs <= 0 ) delete ( thiz );
+ if ( thiz->clientRefs <= 0 ) {
+ objLock.Release();
+ delete ( thiz );
+ }
- XMP_EXIT_WRAPPER_NO_THROW
+ XMP_EXIT_NoThrow
}
// =================================================================================================
@@ -149,11 +115,11 @@ void WXMPFiles_GetFormatInfo_1 ( XMP_FileFormat format,
XMP_OptionBits * flags,
WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER ( "WXMPFiles_GetFormatInfo_1" )
+ XMP_ENTER_Static ( "WXMPFiles_GetFormatInfo_1" )
wResult->int32Result = XMPFiles::GetFormatInfo ( format, flags );
- XMP_EXIT_WRAPPER
+ XMP_EXIT
}
// =================================================================================================
@@ -161,11 +127,11 @@ void WXMPFiles_GetFormatInfo_1 ( XMP_FileFormat format,
void WXMPFiles_CheckFileFormat_1 ( XMP_StringPtr filePath,
WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER ( "WXMPFiles_CheckFileFormat_1" )
+ XMP_ENTER_Static ( "WXMPFiles_CheckFileFormat_1" )
wResult->int32Result = XMPFiles::CheckFileFormat ( filePath );
- XMP_EXIT_WRAPPER
+ XMP_EXIT
}
// =================================================================================================
@@ -173,138 +139,123 @@ void WXMPFiles_CheckFileFormat_1 ( XMP_StringPtr filePath,
void WXMPFiles_CheckPackageFormat_1 ( XMP_StringPtr folderPath,
WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER ( "WXMPFiles_CheckPackageFormat_1" )
+ XMP_ENTER_Static ( "WXMPFiles_CheckPackageFormat_1" )
wResult->int32Result = XMPFiles::CheckPackageFormat ( folderPath );
- XMP_EXIT_WRAPPER
+ XMP_EXIT
}
// =================================================================================================
-void WXMPFiles_OpenFile_1 ( XMPFilesRef xmpFilesRef,
+void WXMPFiles_OpenFile_1 ( XMPFilesRef xmpObjRef,
XMP_StringPtr filePath,
XMP_FileFormat format,
XMP_OptionBits openFlags,
WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER ( "WXMPFiles_OpenFile_1" )
+ XMP_ENTER_ObjWrite ( XMPFiles, "WXMPFiles_OpenFile_1" )
StartPerfCheck ( kAPIPerf_OpenFile, filePath );
- XMPFiles * thiz = (XMPFiles*)xmpFilesRef;
bool ok = thiz->OpenFile ( filePath, format, openFlags );
wResult->int32Result = ok;
EndPerfCheck ( kAPIPerf_OpenFile );
- XMP_EXIT_WRAPPER
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
-void WXMPFiles_CloseFile_1 ( XMPFilesRef xmpFilesRef,
+void WXMPFiles_CloseFile_1 ( XMPFilesRef xmpObjRef,
XMP_OptionBits closeFlags,
WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER ( "WXMPFiles_CloseFile_1" )
+ XMP_ENTER_ObjWrite ( XMPFiles, "WXMPFiles_CloseFile_1" )
StartPerfCheck ( kAPIPerf_CloseFile, "" );
- XMPFiles * thiz = (XMPFiles*)xmpFilesRef;
thiz->CloseFile ( closeFlags );
EndPerfCheck ( kAPIPerf_CloseFile );
- XMP_EXIT_WRAPPER
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
-void WXMPFiles_GetFileInfo_1 ( XMPFilesRef xmpFilesRef,
- XMP_StringPtr * filePath,
- XMP_StringLen * filePathLen,
+void WXMPFiles_GetFileInfo_1 ( XMPFilesRef xmpObjRef,
+ void * clientPath,
XMP_OptionBits * openFlags,
XMP_FileFormat * format,
XMP_OptionBits * handlerFlags,
+ SetClientStringProc SetClientString,
WXMP_Result * wResult )
{
- bool isOpen = false;
- XMP_ENTER_WRAPPER ( "WXMPFiles_GetFileInfo_1" )
+ XMP_ENTER_ObjRead ( XMPFiles, "WXMPFiles_GetFileInfo_1" )
- XMPFiles * thiz = (XMPFiles*)xmpFilesRef;
- isOpen = thiz->GetFileInfo ( filePath, filePathLen, openFlags, format, handlerFlags );
+ XMP_StringPtr pathStr;
+ XMP_StringLen pathLen;
+
+ bool isOpen = thiz.GetFileInfo ( &pathStr, &pathLen, openFlags, format, handlerFlags );
+ if ( isOpen && (clientPath != 0) ) (*SetClientString) ( clientPath, pathStr, pathLen );
wResult->int32Result = isOpen;
- XMP_EXIT_WRAPPER_KEEP_LOCK ( isOpen )
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
-void WXMPFiles_SetAbortProc_1 ( XMPFilesRef xmpFilesRef,
+void WXMPFiles_SetAbortProc_1 ( XMPFilesRef xmpObjRef,
XMP_AbortProc abortProc,
void * abortArg,
WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER ( "WXMPFiles_SetAbortProc_1" )
+ XMP_ENTER_ObjWrite ( XMPFiles, "WXMPFiles_SetAbortProc_1" )
- XMPFiles * thiz = (XMPFiles*)xmpFilesRef;
thiz->SetAbortProc ( abortProc, abortArg );
- XMP_EXIT_WRAPPER
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
-void WXMPFiles_GetXMP_1 ( XMPFilesRef xmpFilesRef,
+void WXMPFiles_GetXMP_1 ( XMPFilesRef xmpObjRef,
XMPMetaRef xmpRef,
- XMP_StringPtr * xmpPacket,
- XMP_StringLen * xmpPacketLen,
+ void * clientPacket,
XMP_PacketInfo * packetInfo,
+ SetClientStringProc SetClientString,
WXMP_Result * wResult )
{
- bool hasXMP = false;
- XMP_ENTER_WRAPPER ( "WXMPFiles_GetXMP_1" )
+ XMP_ENTER_ObjWrite ( XMPFiles, "WXMPFiles_GetXMP_1" )
StartPerfCheck ( kAPIPerf_GetXMP, "" );
- XMPFiles * thiz = (XMPFiles*)xmpFilesRef;
+ bool hasXMP = false;
+ XMP_StringPtr packetStr;
+ XMP_StringLen packetLen;
+
if ( xmpRef == 0 ) {
- hasXMP = thiz->GetXMP ( 0, xmpPacket, xmpPacketLen, packetInfo );
+ hasXMP = thiz->GetXMP ( 0, &packetStr, &packetLen, packetInfo );
} else {
SXMPMeta xmpObj ( xmpRef );
- hasXMP = thiz->GetXMP ( &xmpObj, xmpPacket, xmpPacketLen, packetInfo );
+ hasXMP = thiz->GetXMP ( &xmpObj, &packetStr, &packetLen, packetInfo );
}
+
+ if ( hasXMP && (clientPacket != 0) ) (*SetClientString) ( clientPacket, packetStr, packetLen );
wResult->int32Result = hasXMP;
EndPerfCheck ( kAPIPerf_GetXMP );
- XMP_EXIT_WRAPPER_KEEP_LOCK ( hasXMP )
-}
-
-// -------------------------------------------------------------------------------------------------
-
-void WXMPFiles_GetThumbnail_1 ( XMPFilesRef xmpFilesRef,
- XMP_ThumbnailInfo * tnailInfo, // ! Can be null.
- WXMP_Result * wResult )
-{
- XMP_ENTER_WRAPPER ( "WXMPFiles_GetThumbnail_1" )
- StartPerfCheck ( kAPIPerf_GetThumbnail, "" );
-
- XMPFiles * thiz = (XMPFiles*)xmpFilesRef;
- bool hasTNail = thiz->GetThumbnail ( tnailInfo );
- wResult->int32Result = hasTNail;
-
- EndPerfCheck ( kAPIPerf_GetThumbnail );
- XMP_EXIT_WRAPPER // ! No need to keep the lock, the tnail info won't change.
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
-void WXMPFiles_PutXMP_1 ( XMPFilesRef xmpFilesRef,
+void WXMPFiles_PutXMP_1 ( XMPFilesRef xmpObjRef,
XMPMetaRef xmpRef, // ! Only one of the XMP object or packet are passed.
XMP_StringPtr xmpPacket,
XMP_StringLen xmpPacketLen,
WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER ( "WXMPFiles_PutXMP_1" )
+ XMP_ENTER_ObjWrite ( XMPFiles, "WXMPFiles_PutXMP_1" )
StartPerfCheck ( kAPIPerf_PutXMP, "" );
- XMPFiles * thiz = (XMPFiles*)xmpFilesRef;
if ( xmpRef != 0 ) {
thiz->PutXMP ( xmpRef );
} else {
@@ -312,21 +263,20 @@ void WXMPFiles_PutXMP_1 ( XMPFilesRef xmpFilesRef,
}
EndPerfCheck ( kAPIPerf_PutXMP );
- XMP_EXIT_WRAPPER
+ XMP_EXIT
}
// -------------------------------------------------------------------------------------------------
-void WXMPFiles_CanPutXMP_1 ( XMPFilesRef xmpFilesRef,
+void WXMPFiles_CanPutXMP_1 ( XMPFilesRef xmpObjRef,
XMPMetaRef xmpRef, // ! Only one of the XMP object or packet are passed.
XMP_StringPtr xmpPacket,
XMP_StringLen xmpPacketLen,
WXMP_Result * wResult )
{
- XMP_ENTER_WRAPPER ( "WXMPFiles_CanPutXMP_1" )
+ XMP_ENTER_ObjWrite ( XMPFiles, "WXMPFiles_CanPutXMP_1" )
StartPerfCheck ( kAPIPerf_CanPutXMP, "" );
- XMPFiles * thiz = (XMPFiles*)xmpFilesRef;
if ( xmpRef != 0 ) {
wResult->int32Result = thiz->CanPutXMP ( xmpRef );
} else {
@@ -334,7 +284,7 @@ void WXMPFiles_CanPutXMP_1 ( XMPFilesRef xmpFilesRef,
}
EndPerfCheck ( kAPIPerf_CanPutXMP );
- XMP_EXIT_WRAPPER
+ XMP_EXIT
}
// =================================================================================================
diff --git a/source/XMPFiles/XMPFiles.cpp b/source/XMPFiles/XMPFiles.cpp
index 8471675..ba16e3f 100644
--- a/source/XMPFiles/XMPFiles.cpp
+++ b/source/XMPFiles/XMPFiles.cpp
@@ -1,6 +1,6 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2008 Adobe Systems Incorporated
+// Copyright 2004 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -22,8 +22,7 @@
#include "FileHandlers/Scanner_Handler.hpp"
#include "FileHandlers/MPEG2_Handler.hpp"
#include "FileHandlers/PNG_Handler.hpp"
-#include "FileHandlers/AVI_Handler.hpp"
-#include "FileHandlers/WAV_Handler.hpp"
+#include "FileHandlers/RIFF_Handler.hpp"
#include "FileHandlers/MP3_Handler.hpp"
#include "FileHandlers/SWF_Handler.hpp"
#include "FileHandlers/UCF_Handler.hpp"
@@ -36,11 +35,6 @@
#include "FileHandlers/AVCHD_Handler.hpp"
#include "FileHandlers/ASF_Handler.hpp"
-#if ! (XMP_64 || XMP_UNIXBuild)
- #include "QuickTime_Support.hpp"
- #include "FileHandlers/MOV_Handler.hpp" //old MOV handler
-#endif
-
// =================================================================================================
/// \file XMPFiles.cpp
/// \brief High level support to access metadata in files of interest to Adobe applications.
@@ -51,7 +45,7 @@
// =================================================================================================
-long sXMPFilesInitCount = 0;
+XMP_Int32 sXMPFilesInitCount = 0;
#if GatherPerformanceData
APIPerfCollection* sAPIPerf = 0;
@@ -236,7 +230,7 @@ CheckParentFolderNames ( const std::string & rootPath, const std::string & gpN
// P2 .../MyMovie/CONTENTS/<group>/<file>.<ext> - check CONTENTS and <group>
if ( (gpName == "CONTENTS") && CheckP2ContentChild ( parentName ) ) return kXMP_P2File;
- // XDCAMEX .../MyMovie/BPAV/CLPR/<clip>/<file>.<ext> - check for BPAV/CLPR
+ // XDCAM-EX .../MyMovie/BPAV/CLPR/<clip>/<file>.<ext> - check for BPAV/CLPR
// ! This must be checked before XDCAM-SAM because both have a "CLPR" grandparent.
if ( gpName == "CLPR" ) {
std::string tempPath, greatGP;
@@ -247,6 +241,7 @@ CheckParentFolderNames ( const std::string & rootPath, const std::string & gpN
}
// XDCAM-FAM .../MyMovie/<group>/<file>.<ext> - check that <group> is CLIP, or EDIT, or SUB
+ // ! The standard says Clip/Edit/Sub, but the caller has already shifted to upper case.
if ( (parentName == "CLIP") || (parentName == "EDIT") || (parentName == "SUB") ) return kXMP_XDCAM_FAMFile;
// XDCAM-SAM .../MyMovie/PROAV/<group>/<clip>/<file>.<ext> - check for PROAV and CLPR or EDTR
@@ -292,8 +287,8 @@ CheckTopFolderName ( const std::string & rootPath )
if ( GetFileMode ( childPath.c_str() ) == kFMode_IsFolder ) return kXMP_P2File;
childPath.erase ( baseLen );
- // XDCAM-FAM .../MyMovie/<group>/... - only check for CLIP and MEDIAPRO.XML
- childPath += "CLIP";
+ // XDCAM-FAM .../MyMovie/<group>/... - only check for Clip and MEDIAPRO.XML
+ childPath += "Clip"; // ! Yes, mixed case.
if ( GetFileMode ( childPath.c_str() ) == kFMode_IsFolder ) {
childPath.erase ( baseLen );
childPath += "MEDIAPRO.XML";
@@ -340,15 +335,12 @@ TryFolderHandlers ( XMP_FileFormat format,
const std::string & rootPath,
const std::string & gpName,
const std::string & parentName,
- const std::string & _leafName,
+ const std::string & leafName,
XMPFiles * parentObj )
{
bool foundHandler = false;
XMPFileHandlerInfo * handlerInfo = 0;
XMPFileHandlerTablePos handlerPos;
-
- std::string leafName ( _leafName );
- MakeUpperCase ( &leafName );
// We know we're in a possible context for a folder-oriented handler, so try them.
@@ -360,7 +352,7 @@ TryFolderHandlers ( XMP_FileFormat format,
handlerInfo = &handlerPos->second;
CheckFolderFormatProc CheckProc = (CheckFolderFormatProc) (handlerInfo->checkProc);
foundHandler = CheckProc ( handlerInfo->format, rootPath, gpName, parentName, leafName, parentObj );
- XMP_Assert ( foundHandler || (parentObj->handlerTemp == 0) );
+ XMP_Assert ( foundHandler || (parentObj->tempPtr == 0) );
}
} else {
@@ -370,7 +362,7 @@ TryFolderHandlers ( XMP_FileFormat format,
handlerInfo = &handlerPos->second;
CheckFolderFormatProc CheckProc = (CheckFolderFormatProc) (handlerInfo->checkProc);
foundHandler = CheckProc ( handlerInfo->format, rootPath, gpName, parentName, leafName, parentObj );
- XMP_Assert ( foundHandler || (parentObj->handlerTemp == 0) );
+ XMP_Assert ( foundHandler || (parentObj->tempPtr == 0) );
if ( foundHandler ) break; // ! Exit before incrementing handlerPos.
}
@@ -501,11 +493,11 @@ SelectSmartHandler ( XMPFiles * thiz, XMP_StringPtr clientPath, XMP_FileFormat f
thiz->fileRef = LFA_Open ( clientPath, openMode );
XMP_Assert ( thiz->fileRef != 0 ); // LFA_Open must either succeed or throw.
}
- thiz->format = handlerInfo->format; // ! Hack to tell the CheckProc thiz is an initial call.
+ thiz->format = format; // ! Hack to tell the CheckProc thiz is an initial call.
if ( ! (handlerInfo->flags & kXMPFiles_FolderBasedFormat) ) {
CheckFileFormatProc CheckProc = (CheckFileFormatProc) (handlerInfo->checkProc);
- foundHandler = CheckProc ( handlerInfo->format, clientPath, thiz->fileRef, thiz );
+ foundHandler = CheckProc ( format, clientPath, thiz->fileRef, thiz );
} else {
// *** Don't try here yet. These are messy, needing existence checking and path processing.
// *** CheckFolderFormatProc CheckProc = (CheckFolderFormatProc) (handlerInfo->checkProc);
@@ -514,7 +506,7 @@ SelectSmartHandler ( XMPFiles * thiz, XMP_StringPtr clientPath, XMP_FileFormat f
if ( openFlags & kXMPFiles_OpenStrictly ) openFlags ^= kXMPFiles_OpenStrictly;
}
- XMP_Assert ( foundHandler || (thiz->handlerTemp == 0) );
+ XMP_Assert ( foundHandler || (thiz->tempPtr == 0) );
if ( foundHandler ) return handlerInfo;
handlerInfo = 0; // ! Clear again for later use.
@@ -539,7 +531,7 @@ SelectSmartHandler ( XMPFiles * thiz, XMP_StringPtr clientPath, XMP_FileFormat f
// 3c. Make sure the root folder has a viable top level child folder.
// ! This does "return 0" on failure, the file does not exist so a normal file handler can't apply.
-
+
if ( GetFileMode ( rootPath.c_str() ) != kFMode_IsFolder ) return 0;
thiz->format = CheckTopFolderName ( rootPath );
if ( thiz->format == kXMP_UnknownFile ) return 0;
@@ -569,6 +561,7 @@ SelectSmartHandler ( XMPFiles * thiz, XMP_StringPtr clientPath, XMP_FileFormat f
if ( (thiz->format == kXMP_XDCAM_FAMFile) &&
((parentName == "CLIP") || (parentName == "EDIT") || (parentName == "SUB")) ) {
+ // ! The standard says Clip/Edit/Sub, but we just shifted to upper case.
gpName = origGPName; // ! XDCAM-FAM has just 1 level of inner folder, preserve the "MyMovie" case.
}
@@ -585,11 +578,14 @@ SelectSmartHandler ( XMPFiles * thiz, XMP_StringPtr clientPath, XMP_FileFormat f
if ( (thiz->fileRef == 0) && (! (handlerInfo->flags & kXMPFiles_HandlerOwnsFile)) ) {
thiz->fileRef = LFA_Open ( clientPath, openMode );
XMP_Assert ( thiz->fileRef != 0 ); // LFA_Open must either succeed or throw.
+ } else if ( (thiz->fileRef != 0) && (handlerInfo->flags & kXMPFiles_HandlerOwnsFile) ) {
+ LFA_Close ( thiz->fileRef );
+ thiz->fileRef = 0;
}
thiz->format = handlerInfo->format; // ! Hack to tell the CheckProc thiz is an initial call.
CheckFileFormatProc CheckProc = (CheckFileFormatProc) (handlerInfo->checkProc);
foundHandler = CheckProc ( handlerInfo->format, clientPath, thiz->fileRef, thiz );
- XMP_Assert ( foundHandler || (thiz->handlerTemp == 0) );
+ XMP_Assert ( foundHandler || (thiz->tempPtr == 0) );
if ( foundHandler ) return handlerInfo;
}
@@ -604,7 +600,7 @@ SelectSmartHandler ( XMPFiles * thiz, XMP_StringPtr clientPath, XMP_FileFormat f
handlerInfo = &handlerPos->second;
CheckFileFormatProc CheckProc = (CheckFileFormatProc) (handlerInfo->checkProc);
foundHandler = CheckProc ( handlerInfo->format, clientPath, thiz->fileRef, thiz );
- XMP_Assert ( foundHandler || (thiz->handlerTemp == 0) );
+ XMP_Assert ( foundHandler || (thiz->tempPtr == 0) );
if ( foundHandler ) return handlerInfo;
}
@@ -619,7 +615,7 @@ SelectSmartHandler ( XMPFiles * thiz, XMP_StringPtr clientPath, XMP_FileFormat f
handlerInfo = &handlerPos->second;
CheckFileFormatProc CheckProc = (CheckFileFormatProc) (handlerInfo->checkProc);
foundHandler = CheckProc ( handlerInfo->format, clientPath, thiz->fileRef, thiz );
- XMP_Assert ( foundHandler || (thiz->handlerTemp == 0) );
+ XMP_Assert ( foundHandler || (thiz->tempPtr == 0) );
if ( foundHandler ) return handlerInfo;
}
@@ -649,8 +645,12 @@ XMPFiles::GetVersionInfo ( XMP_VersionInfo * info )
// =================================================================================================
-#if ! (XMP_64 || XMP_UNIXBuild)
- static bool sIgnoreQuickTime = false; // Not vital, but helps catching missing excludes elsewhere.
+#if XMP_TraceFilesCalls
+ FILE * xmpFilesLog = stderr;
+#endif
+
+#if UseGlobalLibraryLock & (! XMP_StaticBuild )
+ XMP_BasicMutex sLibraryLock; // ! Handled in XMPMeta for static builds.
#endif
/* class static */
@@ -660,13 +660,22 @@ XMPFiles::Initialize ( XMP_OptionBits options /* = 0 */ )
++sXMPFilesInitCount;
if ( sXMPFilesInitCount > 1 ) return true;
+ #if XMP_TraceFilesCallsToFile
+ xmpFilesLog = fopen ( "XMPFilesLog.txt", "w" );
+ if ( xmpFilesLog == 0 ) xmpFilesLog = stderr;
+ #endif
+
+ #if UseGlobalLibraryLock & (! XMP_StaticBuild )
+ InitializeBasicMutex ( sLibraryLock ); // ! Handled in XMPMeta for static builds.
+ #endif
+
SXMPMeta::Initialize(); // Just in case the client does not.
+ if ( ! Initialize_LibUtils() ) return false;
+
#if GatherPerformanceData
sAPIPerf = new APIPerfCollection;
#endif
-
- XMP_InitMutex ( &sXMPFilesLock );
XMP_Uns16 endianInt = 0x00FF;
XMP_Uns8 endianByte = *((XMP_Uns8*)&endianInt);
@@ -683,22 +692,13 @@ XMPFiles::Initialize ( XMP_OptionBits options /* = 0 */ )
sNormalHandlers = new XMPFileHandlerTable;
sOwningHandlers = new XMPFileHandlerTable;
- sXMPFilesExceptionMessage = new XMP_VarString;
-
InitializeUnicodeConversions();
- #if ! (XMP_64 || XMP_UNIXBuild)
- sIgnoreQuickTime = XMP_OptionIsSet ( options, kXMPFiles_NoQuickTimeInit );
- (void) QuickTime_Support::MainInitialize ( sIgnoreQuickTime ); // Don't worry about failure, the MOV handler checks that.
+ ignoreLocalText = XMP_OptionIsSet ( options, kXMPFiles_IgnoreLocalText );
+ #if XMP_UNIXBuild
+ if ( ! ignoreLocalText ) XMP_Throw ( "Generic UNIX clients must pass kXMPFiles_IgnoreLocalText", kXMPErr_EnforceFailure );
#endif
-#if XMP_UNIXBuild
-
- // *** For the time being only allow the JPEG smart handler for generic UNIX, not even packet scanning.
- RegisterNormalHandler ( kXMP_JPEGFile, kJPEG_HandlerFlags, JPEG_CheckFormat, JPEG_MetaHandlerCTor );
-
-#else
-
// -----------------------------------------
// Register the directory-oriented handlers.
@@ -723,11 +723,13 @@ XMPFiles::Initialize ( XMP_OptionBits options /* = 0 */ )
RegisterNormalHandler ( kXMP_PostScriptFile, kPostScript_HandlerFlags, PostScript_CheckFormat, PostScript_MetaHandlerCTor );
RegisterNormalHandler ( kXMP_WMAVFile, kASF_HandlerFlags, ASF_CheckFormat, ASF_MetaHandlerCTor );
RegisterNormalHandler ( kXMP_MP3File, kMP3_HandlerFlags, MP3_CheckFormat, MP3_MetaHandlerCTor );
- RegisterNormalHandler ( kXMP_WAVFile, kWAV_HandlerFlags, WAV_CheckFormat, WAV_MetaHandlerCTor );
- RegisterNormalHandler ( kXMP_AVIFile, kAVI_HandlerFlags, AVI_CheckFormat, AVI_MetaHandlerCTor );
+ RegisterNormalHandler ( kXMP_WAVFile, kRIFF_HandlerFlags, RIFF_CheckFormat, RIFF_MetaHandlerCTor );
+ RegisterNormalHandler ( kXMP_AVIFile, kRIFF_HandlerFlags, RIFF_CheckFormat, RIFF_MetaHandlerCTor );
+
RegisterNormalHandler ( kXMP_SWFFile, kSWF_HandlerFlags, SWF_CheckFormat, SWF_MetaHandlerCTor );
RegisterNormalHandler ( kXMP_UCFFile, kUCF_HandlerFlags, UCF_CheckFormat, UCF_MetaHandlerCTor );
RegisterNormalHandler ( kXMP_MPEG4File, kMPEG4_HandlerFlags, MPEG4_CheckFormat, MPEG4_MetaHandlerCTor );
+ RegisterNormalHandler ( kXMP_MOVFile, kMPEG4_HandlerFlags, MPEG4_CheckFormat, MPEG4_MetaHandlerCTor ); // ! Yes, MPEG-4 includes MOV.
RegisterNormalHandler ( kXMP_FLVFile, kFLV_HandlerFlags, FLV_CheckFormat, FLV_MetaHandlerCTor );
// ---------------------------------------------------------------------------------------
@@ -736,14 +738,18 @@ XMPFiles::Initialize ( XMP_OptionBits options /* = 0 */ )
RegisterOwningHandler ( kXMP_MPEGFile, kMPEG2_HandlerFlags, MPEG2_CheckFormat, MPEG2_MetaHandlerCTor );
RegisterOwningHandler ( kXMP_MPEG2File, kMPEG2_HandlerFlags, MPEG2_CheckFormat, MPEG2_MetaHandlerCTor );
- #if ! (XMP_64 || XMP_UNIXBuild)
- RegisterOwningHandler ( kXMP_MOVFile, kMOV_HandlerFlags, MOV_CheckFormat, MOV_MetaHandlerCTor );
- #endif
-
-#endif // XMP_UNIXBuild, temporary exclusions
-
// Make sure the embedded info strings are referenced and kept.
if ( (kXMPFiles_EmbeddedVersion[0] == 0) || (kXMPFiles_EmbeddedCopyright[0] == 0) ) return false;
+ // Verify critical type sizes.
+ XMP_Assert ( sizeof(XMP_Int8) == 1 );
+ XMP_Assert ( sizeof(XMP_Int16) == 2 );
+ XMP_Assert ( sizeof(XMP_Int32) == 4 );
+ XMP_Assert ( sizeof(XMP_Int64) == 8 );
+ XMP_Assert ( sizeof(XMP_Uns8) == 1 );
+ XMP_Assert ( sizeof(XMP_Uns16) == 2 );
+ XMP_Assert ( sizeof(XMP_Uns32) == 4 );
+ XMP_Assert ( sizeof(XMP_Uns64) == 8 );
+
return true;
} // XMPFiles::Initialize
@@ -825,18 +831,12 @@ static void ReportPerformanceData()
// =================================================================================================
-#define EliminateGlobal(g) delete ( g ); g = 0
-
/* class static */
void
XMPFiles::Terminate()
{
--sXMPFilesInitCount;
- if ( sXMPFilesInitCount != 0 ) return;
-
- #if ! (XMP_64 || XMP_UNIXBuild)
- QuickTime_Support::MainTerminate ( sIgnoreQuickTime );
- #endif
+ if ( sXMPFilesInitCount != 0 ) return; // Not ready to terminate, or already terminated.
#if GatherPerformanceData
ReportPerformanceData();
@@ -846,13 +846,20 @@ XMPFiles::Terminate()
EliminateGlobal ( sFolderHandlers );
EliminateGlobal ( sNormalHandlers );
EliminateGlobal ( sOwningHandlers );
-
- EliminateGlobal ( sXMPFilesExceptionMessage );
-
- XMP_TermMutex ( sXMPFilesLock );
SXMPMeta::Terminate(); // Just in case the client does not.
+ Terminate_LibUtils();
+
+ #if UseGlobalLibraryLock & (! XMP_StaticBuild )
+ TerminateBasicMutex ( sLibraryLock ); // ! Handled in XMPMeta for static builds.
+ #endif
+
+ #if XMP_TraceFilesCallsToFile
+ if ( xmpFilesLog != stderr ) fclose ( xmpFilesLog );
+ xmpFilesLog = stderr;
+ #endif
+
} // XMPFiles::Terminate
// =================================================================================================
@@ -865,7 +872,8 @@ XMPFiles::XMPFiles() :
abortProc(0),
abortArg(0),
handler(0),
- handlerTemp(0)
+ tempPtr(0),
+ tempUI32(0)
{
// Nothing more to do, clientRefs is incremented in wrapper.
@@ -887,46 +895,13 @@ XMPFiles::~XMPFiles()
this->fileRef = 0;
}
- if ( this->handlerTemp != 0 ) free ( this->handlerTemp ); // ! Must have been malloc-ed!
+ if ( this->tempPtr != 0 ) free ( this->tempPtr ); // ! Must have been malloc-ed!
} // XMPFiles::~XMPFiles
// =================================================================================================
/* class static */
-void
-XMPFiles::UnlockLib()
-{
-
- // *** Would be better to have the count in an object with the mutex.
- #if TraceXMPLocking
- fprintf ( xmpOut, " Unlocking XMPFiles, count = %d\n", sXMPFilesLockCount ); fflush ( xmpOut );
- #endif
- --sXMPFilesLockCount;
- XMP_Assert ( sXMPFilesLockCount == 0 );
- XMP_ExitCriticalRegion ( sXMPFilesLock );
-
-} // XMPFiles::UnlockLib
-
-// =================================================================================================
-
-void
-XMPFiles::UnlockObj()
-{
-
- // *** Would be better to have the count in an object with the mutex.
- #if TraceXMPLocking
- fprintf ( xmpOut, " Unlocking XMPFiles, count = %d\n", sXMPFilesLockCount ); fflush ( xmpOut );
- #endif
- --sXMPFilesLockCount;
- XMP_Assert ( sXMPFilesLockCount == 0 );
- XMP_ExitCriticalRegion ( sXMPFilesLock );
-
-} // XMPFiles::UnlockObj
-
-// =================================================================================================
-
-/* class static */
bool
XMPFiles::GetFormatInfo ( XMP_FileFormat format,
XMP_OptionBits * flags /* = 0 */ )
@@ -998,12 +973,6 @@ XMPFiles::OpenFile ( XMP_StringPtr clientPath,
XMP_FileFormat format /* = kXMP_UnknownFile */,
XMP_OptionBits openFlags /* = 0 */ )
{
-#if XMP_UNIXBuild
- // *** For the time being only allow the JPEG smart handler for generic UNIX, not even packet scanning.
- format = kXMP_JPEGFile;
- openFlags |= (kXMPFiles_OpenUseSmartHandler | kXMPFiles_OpenStrictly);
-#endif
-
if ( this->handler != 0 ) XMP_Throw ( "File already open", kXMPErr_BadParam );
if ( this->fileRef != 0 ) { // ! Sanity check to prevent open file leaks.
LFA_Close ( this->fileRef );
@@ -1071,7 +1040,6 @@ XMPFiles::OpenFile ( XMP_StringPtr clientPath,
}
XMP_Assert ( handlerInfo != 0 );
- format = handlerInfo->format;
handlerCTor = handlerInfo->handlerCTor;
handlerFlags = handlerInfo->flags;
@@ -1081,8 +1049,8 @@ XMPFiles::OpenFile ( XMP_StringPtr clientPath,
XMP_Assert ( handlerFlags == handler->handlerFlags );
this->handler = handler;
- if ( this->format == kXMP_UnknownFile ) this->format = format; // ! The CheckProc might have set it.
-
+ if ( this->format == kXMP_UnknownFile ) this->format = handlerInfo->format; // ! The CheckProc might have set it.
+
try {
handler->CacheFileData();
} catch ( ... ) {
@@ -1095,11 +1063,6 @@ XMPFiles::OpenFile ( XMP_StringPtr clientPath,
throw;
}
- if ( ! (openFlags & kXMPFiles_OpenCacheTNail) ) {
- handler->containsTNail = false; // Make sure GetThumbnail will cleanly return false.
- handler->processedTNail = true;
- }
-
if ( handler->containsXMP ) FillPacketInfo ( handler->xmpPacket, &handler->packetInfo );
if ( (! (openFlags & kXMPFiles_OpenForUpdate)) && (! (handlerFlags & kXMPFiles_HandlerOwnsFile)) ) {
@@ -1156,11 +1119,13 @@ XMPFiles::CloseFile ( XMP_OptionBits closeFlags /* = 0 */ )
// Close the file without doing common crash-safe writing. The handler might do it.
- #if GatherPerformanceData
- if ( needsUpdate ) sAPIPerf->back().extraInfo += ", direct update";
- #endif
-
- if ( needsUpdate ) this->handler->UpdateFile ( doSafeUpdate );
+ if ( needsUpdate ) {
+ #if GatherPerformanceData
+ sAPIPerf->back().extraInfo += ", direct update";
+ #endif
+ this->handler->UpdateFile ( doSafeUpdate );
+ }
+
delete this->handler;
this->handler = 0;
if ( this->fileRef != 0 ) LFA_Close ( this->fileRef );
@@ -1185,19 +1150,22 @@ XMPFiles::CloseFile ( XMP_OptionBits closeFlags /* = 0 */ )
XMP_Assert ( tempFileRef == 0 );
tempFileRef = LFA_Open ( tempFilePath.c_str(), 'w' );
this->fileRef = tempFileRef;
+ tempFileRef = 0;
this->filePath = tempFilePath;
this->handler->WriteFile ( origFileRef, origFilePath );
} else {
- // The handler can only update an existing file. Do a little dance so the final file
- // is the original, thus preserving ownership, permissions, etc. This does have the
- // risk that the interim copy under the original name has "current" ownership and
- // permissions. The dance steps:
- // - Copy the original file to a temp name.
- // - Rename the original file to a different temp name.
- // - Rename the copy file back to the original name.
- // - Call the handler's UpdateFile method for the "original as temp" file.
+ // The handler can only update an existing file. Do a little dance so that the final
+ // file is the updated original, thus preserving ownership, permissions, etc. This
+ // does have the risk that the interim copy under the original name has "current"
+ // ownership and permissions. The dance steps:
+ // - Copy the original file to a temp name, the copyFile.
+ // - Rename the original file to a different temp name, the tempFile.
+ // - Rename the copyFile back to the original name.
+ // - Call the handler's UpdateFile method for the tempFile.
+ // A failure inside the handler's UpdateFile method will leave the copied file under
+ // the original name.
// *** A user abort might leave the copy file under the original name! Need better
// *** duplicate code that handles all parts of a file, and for CreateTempFile to
@@ -1216,31 +1184,34 @@ XMPFiles::CloseFile ( XMP_OptionBits closeFlags /* = 0 */ )
LFA_Copy ( origFileRef, copyFileRef, fileSize, this->abortProc, this->abortArg );
LFA_Close ( origFileRef );
- origFileRef = this->fileRef = 0;
LFA_Close ( copyFileRef );
- copyFileRef = 0;
+ copyFileRef = origFileRef = this->fileRef = 0;
CreateTempFile ( origFilePath, &tempFilePath );
LFA_Delete ( tempFilePath.c_str() ); // ! Slight risk of name being grabbed before rename.
LFA_Rename ( origFilePath.c_str(), tempFilePath.c_str() );
+ LFA_Rename ( copyFilePath.c_str(), origFilePath.c_str() );
+ copyFilePath.clear();
+
XMP_Assert ( tempFileRef == 0 );
tempFileRef = LFA_Open ( tempFilePath.c_str(), 'w' );
this->fileRef = tempFileRef;
+ tempFileRef = 0;
+ this->filePath = tempFilePath;
try {
- LFA_Rename ( copyFilePath.c_str(), origFilePath.c_str() );
+ this->handler->UpdateFile ( false ); // We're doing the safe update, not the handler.
} catch ( ... ) {
this->fileRef = 0;
+ this->filePath = origFilePath; // This is really the copied file.
LFA_Close ( tempFileRef );
- LFA_Rename ( tempFilePath.c_str(), origFilePath.c_str() );
+ LFA_Delete ( tempFilePath.c_str() );
+ tempFileRef = 0;
+ tempFilePath.clear();
throw;
}
- XMP_Assert ( (tempFileRef != 0) && (tempFileRef == this->fileRef) );
- this->filePath = tempFilePath;
- this->handler->UpdateFile ( false ); // We're doing the safe update, not the handler.
-
}
delete this->handler;
@@ -1255,6 +1226,7 @@ XMPFiles::CloseFile ( XMP_OptionBits closeFlags /* = 0 */ )
LFA_Delete ( origFilePath.c_str() );
LFA_Rename ( tempFilePath.c_str(), origFilePath.c_str() );
+ tempFilePath.clear();
}
@@ -1264,25 +1236,35 @@ XMPFiles::CloseFile ( XMP_OptionBits closeFlags /* = 0 */ )
try {
if ( this->fileRef != 0 ) LFA_Close ( this->fileRef );
- } catch ( ... ) { /*Do nothing, throw the outer exception later. */ }
+ } catch ( ... ) { /* Do nothing, throw the outer exception later. */ }
try {
if ( origFileRef != 0 ) LFA_Close ( origFileRef );
- } catch ( ... ) { /*Do nothing, throw the outer exception later. */ }
+ } catch ( ... ) { /* Do nothing, throw the outer exception later. */ }
try {
if ( tempFileRef != 0 ) LFA_Close ( tempFileRef );
- } catch ( ... ) { /*Do nothing, throw the outer exception later. */ }
+ } catch ( ... ) { /* Do nothing, throw the outer exception later. */ }
+ try {
+ if ( ! tempFilePath.empty() ) LFA_Delete ( tempFilePath.c_str() );
+ } catch ( ... ) { /* Do nothing, throw the outer exception later. */ }
try {
if ( copyFileRef != 0 ) LFA_Close ( copyFileRef );
- } catch ( ... ) { /*Do nothing, throw the outer exception later. */ }
+ } catch ( ... ) { /* Do nothing, throw the outer exception later. */ }
+ try {
+ if ( ! copyFilePath.empty() ) LFA_Delete ( copyFilePath.c_str() );
+ } catch ( ... ) { /* Do nothing, throw the outer exception later. */ }
try {
if ( this->handler != 0 ) delete this->handler;
- } catch ( ... ) { /*Do nothing, throw the outer exception later. */ }
+ } catch ( ... ) { /* Do nothing, throw the outer exception later. */ }
this->handler = 0;
this->format = kXMP_UnknownFile;
this->fileRef = 0;
this->filePath.clear();
this->openFlags = 0;
+
+ if ( this->tempPtr != 0 ) free ( this->tempPtr ); // ! Must have been malloc-ed!
+ this->tempPtr = 0;
+ this->tempUI32 = 0;
throw;
@@ -1296,6 +1278,10 @@ XMPFiles::CloseFile ( XMP_OptionBits closeFlags /* = 0 */ )
this->filePath.clear();
this->openFlags = 0;
+ if ( this->tempPtr != 0 ) free ( this->tempPtr ); // ! Must have been malloc-ed!
+ this->tempPtr = 0;
+ this->tempUI32 = 0;
+
} // XMPFiles::CloseFile
// =================================================================================================
@@ -1305,7 +1291,7 @@ XMPFiles::GetFileInfo ( XMP_StringPtr * filePath /* = 0 */,
XMP_StringLen * pathLen /* = 0 */,
XMP_OptionBits * openFlags /* = 0 */,
XMP_FileFormat * format /* = 0 */,
- XMP_OptionBits * handlerFlags /* = 0 */ )
+ XMP_OptionBits * handlerFlags /* = 0 */ ) const
{
if ( this->handler == 0 ) return false;
XMPFileHandler * handler = this->handler;
@@ -1373,6 +1359,8 @@ XMPFiles::GetXMP ( SXMPMeta * xmpObj /* = 0 */,
XMP_PacketInfo * packetInfo /* = 0 */ )
{
if ( this->handler == 0 ) XMP_Throw ( "XMPFiles::GetXMP - No open file", kXMPErr_BadObject );
+
+ XMP_OptionBits applyTemplateFlags = kXMPTemplate_AddNewProperties | kXMPTemplate_IncludeInternalProperties;
if ( ! this->handler->processedXMP ) {
try {
@@ -1380,8 +1368,9 @@ XMPFiles::GetXMP ( SXMPMeta * xmpObj /* = 0 */,
} catch ( ... ) {
// Return the outputs then rethrow the exception.
if ( xmpObj != 0 ) {
- SXMPUtils::RemoveProperties ( xmpObj, 0, 0, kXMPUtil_DoAllProperties );
- SXMPUtils::AppendProperties ( this->handler->xmpObj, xmpObj, kXMPUtil_DoAllProperties );
+ // ! Don't use Clone, that replaces the internal ref in the local xmpObj, leaving the client unchanged!
+ xmpObj->Erase();
+ SXMPUtils::ApplyTemplate ( xmpObj, this->handler->xmpObj, applyTemplateFlags );
}
if ( xmpPacket != 0 ) *xmpPacket = this->handler->xmpPacket.c_str();
if ( xmpPacketLen != 0 ) *xmpPacketLen = (XMP_StringLen) this->handler->xmpPacket.size();
@@ -1397,8 +1386,9 @@ XMPFiles::GetXMP ( SXMPMeta * xmpObj /* = 0 */,
if ( xmpObj != 0 ) *xmpObj = this->handler->xmpObj.Clone();
#else
if ( xmpObj != 0 ) {
- SXMPUtils::RemoveProperties ( xmpObj, 0, 0, kXMPUtil_DoAllProperties );
- SXMPUtils::AppendProperties ( this->handler->xmpObj, xmpObj, kXMPUtil_DoAllProperties );
+ // ! Don't use Clone, that replaces the internal ref in the local xmpObj, leaving the client unchanged!
+ xmpObj->Erase();
+ SXMPUtils::ApplyTemplate ( xmpObj, this->handler->xmpObj, applyTemplateFlags );
}
#endif
@@ -1413,23 +1403,6 @@ XMPFiles::GetXMP ( SXMPMeta * xmpObj /* = 0 */,
// =================================================================================================
-bool
-XMPFiles::GetThumbnail ( XMP_ThumbnailInfo * tnailInfo )
-{
- if ( this->handler == 0 ) XMP_Throw ( "XMPFiles::GetThumbnail - No open file", kXMPErr_BadObject );
-
- if ( ! (this->handler->handlerFlags & kXMPFiles_ReturnsTNail) ) return false;
-
- if ( ! this->handler->processedTNail ) this->handler->ProcessTNail();
- if ( ! this->handler->containsTNail ) return false;
- if ( tnailInfo != 0 ) *tnailInfo = this->handler->tnailInfo;
-
- return true;
-
-} // XMPFiles::GetThumbnail
-
-// =================================================================================================
-
static bool
DoPutXMP ( XMPFiles * thiz, const SXMPMeta & xmpObj, const bool doIt )
{
diff --git a/source/XMPFiles/XMPFiles.hpp b/source/XMPFiles/XMPFiles.hpp
index b76de70..8744a31 100644
--- a/source/XMPFiles/XMPFiles.hpp
+++ b/source/XMPFiles/XMPFiles.hpp
@@ -3,7 +3,7 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2007 Adobe Systems Incorporated
+// Copyright 2004 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -15,7 +15,12 @@
#define TXMP_STRING_TYPE std::string
#include "XMP.hpp"
-typedef void * LFA_FileRef;
+#if ! UNIX_ENV
+ typedef void * LFA_FileRef;
+#else
+ typedef XMP_Int32 LFA_FileRef;
+#endif
+
class XMPFileHandler;
// =================================================================================================
@@ -167,9 +172,6 @@ public:
XMPFiles();
virtual ~XMPFiles();
- static void UnlockLib();
- void UnlockObj();
-
static bool GetFormatInfo ( XMP_FileFormat format,
XMP_OptionBits * flags = 0 );
@@ -186,7 +188,7 @@ public:
XMP_StringLen * filePathLen = 0,
XMP_OptionBits * openFlags = 0,
XMP_FileFormat * format = 0,
- XMP_OptionBits * handlerFlags = 0 );
+ XMP_OptionBits * handlerFlags = 0 ) const;
void SetAbortProc ( XMP_AbortProc abortProc,
void * abortArg );
@@ -195,8 +197,6 @@ public:
XMP_StringPtr * xmpPacket = 0,
XMP_StringLen * xmpPacketLen = 0,
XMP_PacketInfo * packetInfo = 0 );
-
- bool GetThumbnail ( XMP_ThumbnailInfo * tnailInfo );
void PutXMP ( const SXMPMeta & xmpObj );
@@ -211,13 +211,16 @@ public:
// Leave this data public so file handlers can see it.
XMP_Int32 clientRefs; // ! Must be signed to allow decrement from zero.
+ XMP_ReadWriteLock lock;
XMP_FileFormat format;
LFA_FileRef fileRef; // Non-zero if a file is open.
std::string filePath;
XMP_OptionBits openFlags;
XMPFileHandler * handler; // Non-null if a file is open.
- void * handlerTemp; // For use between the CheckProc and handler creation.
+
+ void * tempPtr; // For use between the CheckProc and handler creation.
+ XMP_Uns32 tempUI32;
XMP_AbortProc abortProc;
void * abortArg;
diff --git a/source/XMPFiles/XMPFiles_Impl.cpp b/source/XMPFiles/XMPFiles_Impl.cpp
index c07a82f..1677141 100644
--- a/source/XMPFiles/XMPFiles_Impl.cpp
+++ b/source/XMPFiles/XMPFiles_Impl.cpp
@@ -1,6 +1,6 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2002-2007 Adobe Systems Incorporated
+// Copyright 2004 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -43,35 +43,9 @@ using namespace std;
#pragma warning ( disable : 4800 ) // forcing value to bool 'true' or 'false' (performance warning)
#endif
-XMP_FileFormat voidFileFormat = 0; // Used as sink for unwanted output parameters.
-XMP_Mutex sXMPFilesLock;
-int sXMPFilesLockCount = 0;
-std::string * sXMPFilesExceptionMessage = 0;
-
-#if TraceXMPCalls
- FILE * xmpFilesOut = stderr;
-#endif
-
-//only define in one non-public-source, non-header(.cpp) place
-void LFA_Throw ( const char* msg, int id )
-{
- switch(id)
- {
- case kLFAErr_InternalFailure:
- XMP_Throw(msg,kXMPErr_InternalFailure);
- break;
- case kLFAErr_ExternalFailure:
- XMP_Throw(msg,kXMPErr_ExternalFailure);
- break;
- case kLFAErr_UserAbort:
- XMP_Throw(msg,kXMPErr_UserAbort);
- break;
- default:
- XMP_Throw(msg,kXMPErr_UnknownException);
- break;
- }
-}
+bool ignoreLocalText = false;
+XMP_FileFormat voidFileFormat = 0; // Used as sink for unwanted output parameters.
// =================================================================================================
// Add all known mappings, multiple mappings (tif, tiff) are OK.
@@ -145,6 +119,8 @@ const FileExtMapping kFileExtMap[] =
{ "pdfxml", kXMP_UCFFile },
{ "mars", kXMP_UCFFile },
{ "idml", kXMP_UCFFile },
+ { "idap", kXMP_UCFFile },
+ { "icap", kXMP_UCFFile },
{ "", 0 } }; // ! Must be last as a sentinel.
// Files known to contain XMP but have no smart handling, here or elsewhere.
@@ -177,15 +153,32 @@ const char * kKnownRejectedFiles[] =
// RAW files
"cr2", "erf", "fff", "dcr", "kdc", "mos", "mfw", "mef",
"raw", "nef", "orf", "pef", "arw", "sr2", "srf", "sti",
- "3fr",
- // not supported UCF subformats
+ "3fr", "rwl", "crw", "sraw", "mos", "mrw", "nrw", "rw2",
+ "c3f",
+ // UCF subformats
"air",
+ // Others
+ "r3d",
0 }; // ! Keep a 0 sentinel at the end.
// =================================================================================================
// =================================================================================================
+void LFA_Throw ( const char* msg, int id )
+{
+ switch ( id ) {
+ case kLFAErr_InternalFailure:
+ XMP_Throw ( msg, kXMPErr_InternalFailure );
+ case kLFAErr_ExternalFailure:
+ XMP_Throw ( msg, kXMPErr_ExternalFailure );
+ case kLFAErr_UserAbort:
+ XMP_Throw ( msg, kXMPErr_UserAbort );
+ default:
+ XMP_Throw ( msg, kXMPErr_UnknownException );
+ }
+}
+
// =================================================================================================
#if XMP_MacBuild | XMP_UNIXBuild
@@ -198,6 +191,7 @@ const char * kKnownRejectedFiles[] =
}
#endif
+// =================================================================================================
static bool CreateNewFile ( const char * newPath, const char * origPath, size_t filePos, bool copyMacRsrc )
{
@@ -748,17 +742,6 @@ void ReadXMPPacket ( XMPFileHandler * handler )
} // ReadXMPPacket
// =================================================================================================
-// XMPFileHandler::ProcessTNail
-// ============================
-
-void XMPFileHandler::ProcessTNail()
-{
-
- this->processedTNail = true; // ! Must be overridden by handlers that support thumbnails.
-
-} // XMPFileHandler::ProcessTNail
-
-// =================================================================================================
// XMPFileHandler::ProcessXMP
// ==========================
//
diff --git a/source/XMPFiles/XMPFiles_Impl.hpp b/source/XMPFiles/XMPFiles_Impl.hpp
index 5ba0f14..294ca5f 100644
--- a/source/XMPFiles/XMPFiles_Impl.hpp
+++ b/source/XMPFiles/XMPFiles_Impl.hpp
@@ -3,7 +3,7 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2004-2008 Adobe Systems Incorporated
+// Copyright 2004 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -13,6 +13,7 @@
#include "XMP_Environment.h" // ! Must be the first #include!
#include "XMP_Const.h"
#include "XMP_BuildInfo.h"
+#include "XMP_LibUtils.hpp"
#include "EndianUtils.hpp"
#include <string>
@@ -30,14 +31,12 @@
#include <cassert>
#if XMP_WinBuild
- #include <Windows.h>
#define snprintf _snprintf
#else
#if XMP_MacBuild
#include <Files.h>
#endif
// POSIX headers for both Mac and generic UNIX.
- #include <pthread.h>
#include <fcntl.h>
#include <unistd.h>
#include <dirent.h>
@@ -48,7 +47,9 @@
// =================================================================================================
// General global variables and macros
-extern long sXMPFilesInitCount;
+extern bool ignoreLocalText;
+
+extern XMP_Int32 sXMPFilesInitCount;
#ifndef GatherPerformanceData
#define GatherPerformanceData 0
@@ -67,14 +68,13 @@ extern long sXMPFilesInitCount;
kAPIPerf_OpenFile,
kAPIPerf_CloseFile,
kAPIPerf_GetXMP,
- kAPIPerf_GetThumbnail,
kAPIPerf_PutXMP,
kAPIPerf_CanPutXMP,
kAPIPerfProcCount // Last, count of the procs.
};
static const char* kAPIPerfNames[] =
- { "OpenFile", "CloseFile", "GetXMP", "GetThumbnail", "PutXMP", "CanPutXMP", 0 };
+ { "OpenFile", "CloseFile", "GetXMP", "PutXMP", "CanPutXMP", 0 };
struct APIPerfItem {
XMP_Uns8 whichProc;
@@ -133,10 +133,6 @@ extern const char * kKnownRejectedFiles[];
#define Uns8Ptr(p) ((XMP_Uns8 *) (p))
-#define kTab ((char)0x09)
-#define kLF ((char)0x0A)
-#define kCR ((char)0x0D)
-
#define IsNewline( ch ) ( ((ch) == kLF) || ((ch) == kCR) )
#define IsSpaceOrTab( ch ) ( ((ch) == ' ') || ((ch) == kTab) )
#define IsWhitespace( ch ) ( IsSpaceOrTab ( ch ) || IsNewline ( ch ) )
@@ -160,31 +156,40 @@ static inline void MakeUpperCase ( std::string * str )
#define XMP_LitMatch(s,l) (std::strcmp((s),(l)) == 0)
#define XMP_LitNMatch(s,l,n) (std::strncmp((s),(l),(n)) == 0)
-#define IgnoreParam(p) voidVoidPtr = (void*)&p
-
// =================================================================================================
-// Support for asserts
+// Support for call tracing
-#define _MakeStr(p) #p
-#define _NotifyMsg(n,c,f,l) #n " failed: " #c " in " f " at line " _MakeStr(l)
-#define _NotifyMsg2(msg,c,e) #e " " #msg ": " #c
-
-#if ! XMP_DebugBuild
- #define XMP_Assert(c) ((void) 0)
-#else
- #define XMP_Assert(c) assert ( c )
+#ifndef XMP_TraceFilesCalls
+ #define XMP_TraceFilesCalls 0
+ #define XMP_TraceFilesCallsToFile 0
#endif
- #define XMP_Enforce(c) \
- if ( ! (c) ) { \
- const char * assert_msg = _NotifyMsg ( XMP_Enforce, (c), __FILE__, __LINE__ ); \
- XMP_Throw ( assert_msg , kXMPErr_EnforceFailure ); \
- }
-#define XMP_Validate(c,msg,e) \
- if ( ! (c) ) { \
- const char * enforce_msg = _NotifyMsg2(msg,c,e); \
- XMP_Throw ( enforce_msg , e ); \
- }
+#if XMP_TraceFilesCalls
+
+ #undef AnnounceThrow
+ #undef AnnounceCatch
+
+ #undef AnnounceEntry
+ #undef AnnounceNoLock
+ #undef AnnounceExit
+
+ extern FILE * xmpFilesLog;
+
+ #define AnnounceThrow(msg) \
+ fprintf ( xmpFilesLog, "XMP_Throw: %s\n", msg ); fflush ( xmpFilesLog )
+ #define AnnounceCatch(msg) \
+ fprintf ( xmpFilesLog, "Catch in %s: %s\n", procName, msg ); fflush ( xmpFilesLog )
+
+ #define AnnounceEntry(proc) \
+ const char * procName = proc; \
+ fprintf ( xmpFilesLog, "Entering %s\n", procName ); fflush ( xmpFilesLog )
+ #define AnnounceNoLock(proc) \
+ const char * procName = proc; \
+ fprintf ( xmpFilesLog, "Entering %s (no lock)\n", procName ); fflush ( xmpFilesLog )
+ #define AnnounceExit() \
+ fprintf ( xmpFilesLog, "Exiting %s\n", procName ); fflush ( xmpFilesLog )
+
+#endif
// =================================================================================================
// Support for memory leak tracking
@@ -214,148 +219,6 @@ static inline void MakeUpperCase ( std::string * str )
#endif
// =================================================================================================
-// Support for exceptions and thread locking
-
-// *** Local copies of threading and exception macros from XMP_Impl.hpp. XMPFiles needs to use a
-// *** separate thread lock from XMPCore. Eventually this could benefit from being recast into an
-// *** XMPToolkit_Impl that supports separate locks.
-
-typedef std::string XMP_VarString;
-
-#ifndef TraceXMPCalls
- #define TraceXMPCalls 0
-#endif
-
-#if ! TraceXMPCalls
-
- #define AnnounceThrow(msg) /* Do nothing. */
- #define AnnounceCatch(msg) /* Do nothing. */
-
- #define AnnounceEntry(proc) /* Do nothing. */
- #define AnnounceNoLock(proc) /* Do nothing. */
- #define AnnounceExit() /* Do nothing. */
-
- #define ReportLock() ++sXMPFilesLockCount
- #define ReportUnlock() --sXMPFilesLockCount
- #define ReportKeepLock() /* Do nothing. */
-
-#else
-
- extern FILE * xmpFilesOut;
-
- #define AnnounceThrow(msg) \
- fprintf ( xmpFilesOut, "XMP_Throw: %s\n", msg ); fflush ( xmpFilesOut )
- #define AnnounceCatch(msg) \
- fprintf ( xmpFilesOut, "Catch in %s: %s\n", procName, msg ); fflush ( xmpFilesOut )
-
- #define AnnounceEntry(proc) \
- const char * procName = proc; \
- fprintf ( xmpFilesOut, "Entering %s\n", procName ); fflush ( xmpFilesOut )
- #define AnnounceNoLock(proc) \
- const char * procName = proc; \
- fprintf ( xmpFilesOut, "Entering %s (no lock)\n", procName ); fflush ( xmpFilesOut )
- #define AnnounceExit() \
- fprintf ( xmpFilesOut, "Exiting %s\n", procName ); fflush ( xmpFilesOut )
-
- #define ReportLock() \
- ++sXMPFilesLockCount; fprintf ( xmpFilesOut, " Auto lock, count = %d\n", sXMPFilesLockCount ); fflush ( xmpFilesOut )
- #define ReportUnlock() \
- --sXMPFilesLockCount; fprintf ( xmpFilesOut, " Auto unlock, count = %d\n", sXMPFilesLockCount ); fflush ( xmpFilesOut )
- #define ReportKeepLock() \
- fprintf ( xmpFilesOut, " Keeping lock, count = %d\n", sXMPFilesLockCount ); fflush ( xmpFilesOut )
-
-#endif
-
-#define XMP_Throw(msg,id) { AnnounceThrow ( msg ); throw XMP_Error ( id, msg ); }
-
-// -------------------------------------------------------------------------------------------------
-
-#if XMP_WinBuild
- typedef CRITICAL_SECTION XMP_Mutex;
-#else
- // Use pthread for both Mac and generic UNIX.
- typedef pthread_mutex_t XMP_Mutex;
-#endif
-
-extern XMP_Mutex sXMPFilesLock;
-extern int sXMPFilesLockCount; // Keep signed to catch unlock errors.
-extern XMP_VarString * sXMPFilesExceptionMessage;
-
-extern bool XMP_InitMutex ( XMP_Mutex * mutex );
-extern void XMP_TermMutex ( XMP_Mutex & mutex );
-
-extern void XMP_EnterCriticalRegion ( XMP_Mutex & mutex );
-extern void XMP_ExitCriticalRegion ( XMP_Mutex & mutex );
-
-class XMPFiles_AutoMutex {
-public:
- XMPFiles_AutoMutex() : mutex(&sXMPFilesLock) { XMP_EnterCriticalRegion ( *mutex ); ReportLock(); };
- ~XMPFiles_AutoMutex() { if ( mutex != 0 ) { ReportUnlock(); XMP_ExitCriticalRegion ( *mutex ); mutex = 0; } };
- void KeepLock() { ReportKeepLock(); mutex = 0; };
-private:
- XMP_Mutex * mutex;
-};
-
-// *** Switch to XMPEnterObjectWrapper & XMPEnterStaticWrapper, to allow for per-object locks.
-
-// ! Don't do the initialization check (sXMP_InitCount > 0) for the no-lock case. That macro is used
-// ! by WXMPMeta_Initialize_1.
-
-#define XMP_ENTER_WRAPPER_NO_LOCK(proc) \
- AnnounceNoLock ( proc ); \
- XMP_Assert ( (0 <= sXMPFilesLockCount) && (sXMPFilesLockCount <= 1) ); \
- try { \
- wResult->errMessage = 0;
-
-#define XMP_ENTER_WRAPPER(proc) \
- AnnounceEntry ( proc ); \
- XMP_Assert ( sXMPFilesInitCount > 0 ); \
- XMP_Assert ( (0 <= sXMPFilesLockCount) && (sXMPFilesLockCount <= 1) ); \
- try { \
- XMPFiles_AutoMutex mutex; \
- wResult->errMessage = 0;
-
-#define XMP_EXIT_WRAPPER \
- XMP_CATCH_EXCEPTIONS \
- AnnounceExit();
-
-#define XMP_EXIT_WRAPPER_KEEP_LOCK(keep) \
- if ( keep ) mutex.KeepLock(); \
- XMP_CATCH_EXCEPTIONS \
- AnnounceExit();
-
-#define XMP_EXIT_WRAPPER_NO_THROW \
- } catch ( ... ) { \
- AnnounceCatch ( "no-throw catch-all" ); \
- /* Do nothing. */ \
- } \
- AnnounceExit();
-
-#define XMP_CATCH_EXCEPTIONS \
- } catch ( XMP_Error & xmpErr ) { \
- wResult->int32Result = xmpErr.GetID(); \
- wResult->ptrResult = (void*)"XMP"; \
- wResult->errMessage = xmpErr.GetErrMsg(); \
- if ( wResult->errMessage == 0 ) wResult->errMessage = ""; \
- AnnounceCatch ( wResult->errMessage ); \
- } catch ( std::exception & stdErr ) { \
- wResult->int32Result = kXMPErr_StdException; \
- wResult->errMessage = stdErr.what(); \
- if ( wResult->errMessage == 0 ) wResult->errMessage = ""; \
- AnnounceCatch ( wResult->errMessage ); \
- } catch ( ... ) { \
- wResult->int32Result = kXMPErr_UnknownException; \
- wResult->errMessage = "Caught unknown exception"; \
- AnnounceCatch ( wResult->errMessage ); \
- }
-
-#if XMP_DebugBuild
- #define RELEASE_NO_THROW /* empty */
-#else
- #define RELEASE_NO_THROW throw()
-#endif
-
-// =================================================================================================
// FileHandler declarations
extern void ReadXMPPacket ( XMPFileHandler * handler );
@@ -367,7 +230,6 @@ public:
#define DefaultCTorPresets \
handlerFlags(0), stdCharForm(kXMP_CharUnknown), \
- containsTNail(false), processedTNail(false), \
containsXMP(false), processedXMP(false), needsUpdate(false)
XMPFileHandler() : parent(0), DefaultCTorPresets {};
@@ -376,7 +238,6 @@ public:
virtual ~XMPFileHandler() {}; // ! The specific handler is responsible for tnailInfo.tnailImage.
virtual void CacheFileData() = 0;
- virtual void ProcessTNail(); // The default implementation just sets processedTNail to true.
virtual void ProcessXMP(); // The default implementation just parses the XMP.
virtual XMP_OptionBits GetSerializeOptions(); // The default is compact.
@@ -390,8 +251,6 @@ public:
XMP_OptionBits handlerFlags; // Capabilities of this handler.
XMP_Uns8 stdCharForm; // The standard character form for output.
- bool containsTNail; // True if the file has a native thumbnail.
- bool processedTNail; // True if the cached thumbnail data has been processed.
bool containsXMP; // True if the file has XMP or PutXMP has been called.
bool processedXMP; // True if the XMP is parsed and reconciled.
bool needsUpdate; // True if the file needs to be updated.
@@ -400,8 +259,6 @@ public:
std::string xmpPacket; // ! This is the current XMP, updated by XMPFiles::PutXMP.
SXMPMeta xmpObj;
- XMP_ThumbnailInfo tnailInfo;
-
}; // XMPFileHandler
typedef XMPFileHandler * (* XMPFileHandlerCTor) ( XMPFiles * parent );
diff --git a/source/common/EndianUtils.hpp b/source/common/EndianUtils.hpp
index e3c9b23..b2dae7c 100644
--- a/source/common/EndianUtils.hpp
+++ b/source/common/EndianUtils.hpp
@@ -3,7 +3,7 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2006-2007 Adobe Systems Incorporated
+// Copyright 2006 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
diff --git a/source/common/ExpatAdapter.hpp b/source/common/ExpatAdapter.hpp
index 9d6c9ec..781d8c1 100644
--- a/source/common/ExpatAdapter.hpp
+++ b/source/common/ExpatAdapter.hpp
@@ -2,7 +2,7 @@
#define __ExpatAdapter_hpp__
// =================================================================================================
-// Copyright 2005-2008 Adobe Systems Incorporated
+// Copyright 2005 Adobe Systems Incorporated
// All Rights Reserved.
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -27,6 +27,7 @@ class ExpatAdapter : public XMLParserAdapter {
public:
XML_Parser parser;
+ XMP_NamespaceTable * registeredNamespaces;
#if BanAllEntityUsage
bool isAborted;
@@ -36,14 +37,21 @@ public:
size_t elemNesting;
#endif
- ExpatAdapter();
+ static const bool kUseGlobalNamespaces = true;
+ static const bool kUseLocalNamespaces = false;
+
+ ExpatAdapter ( bool useGlobalNamespaces );
virtual ~ExpatAdapter();
void ParseBuffer ( const void * buffer, size_t length, bool last = true );
+private:
+
+ ExpatAdapter() : registeredNamespaces(0) {}; // ! Force use of constructor with namespace parameter.
+
};
-extern "C" ExpatAdapter * XMP_NewExpatAdapter();
+extern "C" ExpatAdapter * XMP_NewExpatAdapter ( bool useGlobalNamespaces );
// =================================================================================================
diff --git a/source/common/LargeFileAccess.cpp b/source/common/LargeFileAccess.cpp
index 81e9557..61aa0fc 100644
--- a/source/common/LargeFileAccess.cpp
+++ b/source/common/LargeFileAccess.cpp
@@ -1,16 +1,14 @@
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
-// Copyright 2004-2008 Adobe Systems Incorporated
+// Copyright 2008 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.
+// of the Adobe license agreement accompanying it.
// =================================================================================================
#include "LargeFileAccess.hpp"
-extern void LFA_Throw ( const char* msg, int id );
-
// =================================================================================================
// LFA implementations for Macintosh
// =================================
@@ -37,7 +35,11 @@ extern void LFA_Throw ( const char* msg, int id );
FSRef fileRef;
SInt8 perm = ( (mode == 'r') ? fsRdPerm : fsRdWrPerm );
HFSUniStr255 dataForkName;
+#if __LP64__
+ FSIORefNum refNum;
+#else
SInt16 refNum;
+#endif
OSErr err = FSGetDataForkName ( &dataForkName );
if ( err != noErr ) LFA_Throw ( "LFA_Open: FSGetDataForkName failure", kLFAErr_ExternalFailure );
@@ -98,7 +100,11 @@ extern void LFA_Throw ( const char* msg, int id );
FSRef fileRef;
SInt8 perm = ( (mode == 'r') ? fsRdPerm : fsRdWrPerm );
HFSUniStr255 rsrcForkName;
+#if __LP64__
+ FSIORefNum refNum;
+#else
SInt16 refNum;
+#endif
OSErr err = FSGetResourceForkName ( &rsrcForkName );
if ( err != noErr ) LFA_Throw ( "LFA_OpenRsrc: FSGetResourceForkName failure", kLFAErr_ExternalFailure );
@@ -523,9 +529,23 @@ extern void LFA_Throw ( const char* msg, int id );
int flags = ((mode == 'r') ? O_RDONLY : O_RDWR); // *** Include O_EXLOCK?
+
+
int descr = open ( filePath, flags, ( S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP ) );
- if ( descr == -1 ) LFA_Throw ( "LFA_Open: open failure", kLFAErr_ExternalFailure );
+ if ( descr == -1 )
+ LFA_Throw ( "LFA_Open: open failure", kLFAErr_ExternalFailure );
+
+ struct stat info;
+ if ( fstat(descr,&info) == -1 )
+ LFA_Throw( "LFA_Open: fstat failed.", kLFAErr_ExternalFailure );
+ // troublesome issue:
+ // a root user might be able to open a write-protected file w/o complaint
+ // although we should (to stay in sync with Mac/Win behaviour)
+ // reject write access (i.e. OpenForUpdate) to write-protected files:
+ if ( (mode == 'w') && ( 0 == (info.st_mode & S_IWUSR) ))
+ LFA_Throw( "LFA_Open:file is write proected", kLFAErr_ExternalFailure );
+
return (LFA_FileRef)descr;
} // LFA_Open
@@ -766,7 +786,13 @@ void LFA_Move ( LFA_FileRef srcFile, XMP_Int64 srcOffset,
XMP_Int64 LFA_Tell ( LFA_FileRef file )
{
- return LFA_Seek( file, 0 , SEEK_CUR );
+ return LFA_Seek( file, 0 , SEEK_CUR ); // _CUR !
+}
+
+// plain convenience
+XMP_Int64 LFA_Rewind( LFA_FileRef file)
+{
+ return LFA_Seek( file, 0 , SEEK_SET ); // _SET !
}
//*** kind of a hack, TOTEST
@@ -800,8 +826,13 @@ bool LFA_isEof( LFA_FileRef file )
#endif
#if XMP_UNIXBuild
- LFA_Throw ( "LFA_isEof: not implemented for Unix yet", kLFAErr_ExternalFailure );
- return 0;
+ int descr = (int)file;
+
+ struct stat info;
+ if (fstat(descr,&info) == -1)
+ LFA_Throw ( "LFA_isEof: fstat failed.", kLFAErr_ExternalFailure );
+
+ return LFA_Tell(file) == info.st_size;
#endif
}
diff --git a/source/common/LargeFileAccess.hpp b/source/common/LargeFileAccess.hpp
index bf7f894..0467478 100644
--- a/source/common/LargeFileAccess.hpp
+++ b/source/common/LargeFileAccess.hpp
@@ -2,38 +2,18 @@
#define __LargeFileAccess_hpp__ 1
// =================================================================================================
-// Copyright 2006 Adobe Systems Incorporated
+// Copyright 2008 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 "XMP_Environment.h" // ! This must be the first include.
+
#include <stdio.h> // The assert macro needs printf.
-#include "XMP_Environment.h"
-#include "XMP_Const.h"
#include <string>
-#include "EndianUtils.hpp"
-
-typedef void * LFA_FileRef;
-
-enum { // used by LFA_Throw, re-route to whatever you need
- kLFAErr_InternalFailure = 1,
- kLFAErr_ExternalFailure = 2,
- kLFAErr_UserAbort = 3
- };
-
-#define TXMP_STRING_TYPE std::string
-#define XMP_INCLUDE_XMPFILES 1
-#include "XMP.hpp"
-
-// to use these routines, user must define LFA_Throw, kLFAErr_ExternalFailure, kLFAErr_UserAbort
-// Note:LFA_Throw must be a function, not a macro (it may very well wrap a macro, i.e.
-//LFA_Throw(msg,id) { throw std::logic_error ( msg ); }
-
-using namespace std;
-
#if XMP_WinBuild
#include <Windows.h>
#define snprintf _snprintf
@@ -50,6 +30,27 @@ using namespace std;
#include <sys/types.h>
#endif
+#include "XMP_Const.h"
+#include "EndianUtils.hpp"
+
+using namespace std;
+
+// =================================================================================================
+
+enum { // Used by LFA_Throw, re-route to whatever you need.
+ kLFAErr_InternalFailure = 1,
+ kLFAErr_ExternalFailure = 2,
+ kLFAErr_UserAbort = 3
+};
+
+extern void LFA_Throw ( const char* msg, int id ); // Function needed for reuse of LFA code in various projects.
+
+#if ! XMP_UNIXBuild
+ typedef void * LFA_FileRef;
+#else
+ typedef XMP_Int32 LFA_FileRef;
+#endif
+
// *** Change the model of the LFA functions to not throw for "normal" open/create errors.
// *** Make sure the semantics of open/create/rename are consistent, e.g. about create of existing.
@@ -59,6 +60,10 @@ extern void LFA_Delete ( const char* fileName );
extern void LFA_Rename ( const char* oldName, const char * newName );
extern void LFA_Close ( LFA_FileRef file );
+#if XMP_MacBuild
+ extern LFA_FileRef LFA_OpenRsrc ( const char * filePath, char mode );
+#endif
+
// NOTE: unlike the fseek() 'original' LFA_Seek returns the new file position,
// *NOT* 0 to indicate everything's fine
extern XMP_Int64 LFA_Seek ( LFA_FileRef file, XMP_Int64 offset, int seekMode, bool* okPtr = 0 );
@@ -66,6 +71,7 @@ extern XMP_Int32 LFA_Read ( LFA_FileRef file, void* buffer, XMP_Int32 bytes,
extern void LFA_Write ( LFA_FileRef file, const void* buffer, XMP_Int32 bytes );
extern void LFA_Flush ( LFA_FileRef file );
extern XMP_Int64 LFA_Tell ( LFA_FileRef file );
+extern XMP_Int64 LFA_Rewind ( LFA_FileRef file );
extern XMP_Int64 LFA_Measure ( LFA_FileRef file );
extern void LFA_Extend ( LFA_FileRef file, XMP_Int64 length );
extern void LFA_Truncate ( LFA_FileRef file, XMP_Int64 length );
@@ -84,6 +90,12 @@ enum { kLFA_RequireAll = true }; // Used for requireAll to LFA_Read.
// =================================================================================================
// Read and convert endianess in one go:
+static inline XMP_Uns8 LFA_ReadUns8 ( LFA_FileRef file )
+{
+ XMP_Uns8 value;
+ LFA_Read ( file, &value, 1, kLFA_RequireAll );
+ return value;
+}
static inline XMP_Uns16 LFA_ReadUns16_BE ( LFA_FileRef file )
{
@@ -135,5 +147,144 @@ static inline XMP_Uns64 LFA_ReadUns64_LE ( LFA_FileRef file )
#define LFA_ReadInt64_BE(file) ((XMP_Int64) LFA_ReadUns64_BE ( file ))
#define LFA_ReadInt64_LE(file) ((XMP_Int64) LFA_ReadUns64_LE ( file ))
+// peek functions /////////////////////////////////////////////
+// peek functions, to see what's next:
+static inline XMP_Uns8 LFA_PeekUns8(LFA_FileRef file)
+{
+ XMP_Uns8 value = LFA_ReadUns8( file );
+ LFA_Seek( file, -1, SEEK_CUR );
+ return value;
+}
+
+static inline XMP_Uns16 LFA_PeekUns16_BE(LFA_FileRef file)
+{
+ XMP_Uns16 value = LFA_ReadUns16_BE( file );
+ LFA_Seek( file, -2, SEEK_CUR );
+ return value;
+}
+
+static inline XMP_Uns16 LFA_PeekUns16_LE(LFA_FileRef file)
+{
+ XMP_Uns16 value = LFA_ReadUns16_LE( file );
+ LFA_Seek( file, -2, SEEK_CUR );
+ return value;
+}
+
+static inline XMP_Uns32 LFA_PeekUns32_BE(LFA_FileRef file)
+{
+ XMP_Uns32 value = LFA_ReadUns32_BE( file );
+ LFA_Seek( file, -4, SEEK_CUR );
+ return value;
+}
+
+static inline XMP_Uns32 LFA_PeekUns32_LE(LFA_FileRef file)
+{
+ XMP_Uns32 value = LFA_ReadUns32_LE( file );
+ LFA_Seek( file, -4, SEEK_CUR );
+ return value;
+}
+
+static inline XMP_Uns64 LFA_PeekUns64_BE(LFA_FileRef file)
+{
+ XMP_Uns64 value = LFA_ReadUns64_BE( file );
+ LFA_Seek( file, -8, SEEK_CUR );
+ return value;
+}
+
+static inline XMP_Uns64 LFA_PeekUns64_LE(LFA_FileRef file)
+{
+ XMP_Uns64 value = LFA_ReadUns64_LE( file );
+ LFA_Seek( file, -8, SEEK_CUR );
+ return value;
+}
+
+
+#define LFA_PeekInt16_BE(file) ((XMP_Int16) LFA_PeekUns16_BE ( file ))
+#define LFA_PeekInt16_LE(file) ((XMP_Int16) LFA_PeekUns16_LE ( file ))
+#define LFA_PeekInt32_BE(file) ((XMP_Int32) LFA_PeekUns32_BE ( file ))
+#define LFA_PeekInt32_LE(file) ((XMP_Int32) LFA_PeekUns32_LE ( file ))
+#define LFA_PeekInt64_BE(file) ((XMP_Int64) LFA_PeekUns64_BE ( file ))
+#define LFA_PeekInt64_LE(file) ((XMP_Int64) LFA_PeekUns64_LE ( file ))
+
+// write functions /////////////////////////////////////////////
+static inline void LFA_WriteUns8 ( LFA_FileRef file, XMP_Uns8 value )
+{
+ LFA_Write ( file, &value, 1 );
+}
+
+static inline void LFA_WriteUns16_LE ( LFA_FileRef file, XMP_Uns16 value )
+{
+ XMP_Uns16 v = MakeUns16LE(value);
+ LFA_Write ( file, &v, 2 );
+}
+
+static inline void LFA_WriteUns16_BE ( LFA_FileRef file, XMP_Uns16 value )
+{
+ XMP_Uns16 v = MakeUns16BE(value);
+ LFA_Write ( file, &v, 2 );
+}
+
+static inline void LFA_WriteUns32_LE ( LFA_FileRef file, XMP_Uns32 value )
+{
+ XMP_Uns32 v = MakeUns32LE(value);
+ LFA_Write ( file, &v, 4 );
+}
+
+static inline void LFA_WriteUns32_BE ( LFA_FileRef file, XMP_Uns32 value )
+{
+ XMP_Uns32 v = MakeUns32BE(value);
+ LFA_Write ( file, &v, 4 );
+}
+
+static inline void LFA_WriteUns64_LE ( LFA_FileRef file, XMP_Uns64 value )
+{
+ XMP_Uns64 v = MakeUns64LE(value);
+ LFA_Write ( file, &v, 8 );
+}
+
+static inline void LFA_WriteUns64_BE ( LFA_FileRef file, XMP_Uns64 value )
+{
+ XMP_Uns64 v = MakeUns64BE(value);
+ LFA_Write ( file, &v, 8 );
+}
+
+////////////////////////////////////////
+
+static inline void LFA_WriteInt16_LE ( LFA_FileRef file, XMP_Int16 value )
+{
+ XMP_Uns16 v = MakeUns16LE((XMP_Uns16)value);
+ LFA_Write ( file, &v, 2 );
+}
+
+static inline void LFA_WriteInt16_BE ( LFA_FileRef file, XMP_Int16 value )
+{
+ XMP_Uns16 v = MakeUns16BE((XMP_Uns16)value);
+ LFA_Write ( file, &v, 2 );
+}
+
+static inline void LFA_WriteInt32_LE ( LFA_FileRef file, XMP_Int32 value )
+{
+ XMP_Uns32 v = MakeUns32LE((XMP_Uns32)value);
+ LFA_Write ( file, &v, 4 );
+}
+
+static inline void LFA_WriteInt32_BE ( LFA_FileRef file, XMP_Int32 value )
+{
+ XMP_Uns32 v = MakeUns32BE((XMP_Uns32)value);
+ LFA_Write ( file, &v, 4 );
+}
+
+static inline void LFA_WriteInt64_LE ( LFA_FileRef file, XMP_Int64 value )
+{
+ XMP_Uns64 v = MakeUns64LE((XMP_Uns64)value);
+ LFA_Write ( file, &v, 8 );
+}
+
+static inline void LFA_WriteInt64_BE ( LFA_FileRef file, XMP_Int64 value )
+{
+ XMP_Uns64 v = MakeUns64BE((XMP_Uns64)value);
+ LFA_Write ( file, &v, 8 );
+}
+
#endif // __LargeFileAccess_hpp__
diff --git a/source/common/UnicodeConversions.cpp b/source/common/UnicodeConversions.cpp
index 123c502..3156e6e 100644
--- a/source/common/UnicodeConversions.cpp
+++ b/source/common/UnicodeConversions.cpp
@@ -1,5 +1,5 @@
// =================================================================================================
-// Copyright 2004-2007 Adobe Systems Incorporated
+// Copyright 2004 Adobe Systems Incorporated
// All Rights Reserved.
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -221,41 +221,29 @@ void InitializeUnicodeConversions()
// =================================================================================================
-#if XMP_MacBuild && __MWERKS__
-
- #define UTF16InSwap(inPtr) UTF16Unit ( __lhbrx ( (void*)(inPtr), 0 ) )
- #define UTF32InSwap(inPtr) UTF32Unit ( __lwbrx ( (void*)(inPtr), 0 ) )
-
- #define UTF16OutSwap(outPtr,value) __sthbrx ( value, (void*)(outPtr), 0 )
- #define UTF32OutSwap(outPtr,value) __stwbrx ( value, (void*)(outPtr), 0 )
-
-#else
-
- static inline UTF16Unit UTF16InSwap ( const UTF16Unit * inPtr )
- {
- UTF16Unit inUnit = *inPtr;
- return (inUnit << 8) | (inUnit >> 8);
- }
-
- static inline UTF32Unit UTF32InSwap ( const UTF32Unit * inPtr )
- {
- UTF32Unit inUnit = *inPtr;
- return (inUnit << 24) | ((inUnit << 8) & 0x00FF0000) | ((inUnit >> 8) & 0x0000FF00) | (inUnit >> 24);
- }
+static inline UTF16Unit UTF16InSwap ( const UTF16Unit * inPtr )
+{
+ UTF16Unit inUnit = *inPtr;
+ return (inUnit << 8) | (inUnit >> 8);
+}
- static inline void UTF16OutSwap ( UTF16Unit * outPtr, const UTF16Unit value )
- {
- UTF16Unit outUnit = (value << 8) | (value >> 8);
- *outPtr = outUnit;
- }
+static inline UTF32Unit UTF32InSwap ( const UTF32Unit * inPtr )
+{
+ UTF32Unit inUnit = *inPtr;
+ return (inUnit << 24) | ((inUnit << 8) & 0x00FF0000) | ((inUnit >> 8) & 0x0000FF00) | (inUnit >> 24);
+}
- static inline void UTF32OutSwap ( UTF32Unit * outPtr, const UTF32Unit value )
- {
- UTF32Unit outUnit = (value << 24) | ((value << 8) & 0x00FF0000) | ((value >> 8) & 0x0000FF00) | (value >> 24);
- *outPtr = outUnit;
- }
+static inline void UTF16OutSwap ( UTF16Unit * outPtr, const UTF16Unit value )
+{
+ UTF16Unit outUnit = (value << 8) | (value >> 8);
+ *outPtr = outUnit;
+}
-#endif
+static inline void UTF32OutSwap ( UTF32Unit * outPtr, const UTF32Unit value )
+{
+ UTF32Unit outUnit = (value << 24) | ((value << 8) & 0x00FF0000) | ((value >> 8) & 0x0000FF00) | (value >> 24);
+ *outPtr = outUnit;
+}
// =================================================================================================
diff --git a/source/common/UnicodeConversions.hpp b/source/common/UnicodeConversions.hpp
index 0888300..acbff69 100644
--- a/source/common/UnicodeConversions.hpp
+++ b/source/common/UnicodeConversions.hpp
@@ -2,7 +2,7 @@
#define __UnicodeConversions_h__
// =================================================================================================
-// Copyright 2004-2007 Adobe Systems Incorporated
+// Copyright 2004 Adobe Systems Incorporated
// All Rights Reserved.
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -14,9 +14,10 @@
// =================================================================================================
#if UnicodeTestBuild
- typedef unsigned char UTF8Unit;
- typedef unsigned short UTF16Unit;
- typedef unsigned long UTF32Unit;
+ #error "Never used."
+ //typedef unsigned char UTF8Unit;
+ //typedef unsigned short UTF16Unit;
+ //typedef unsigned long UTF32Unit;
#else
typedef XMP_Uns8 UTF8Unit;
typedef XMP_Uns16 UTF16Unit;
diff --git a/source/common/UnicodeInlines.incl_cpp b/source/common/UnicodeInlines.incl_cpp
index 8d55bad..d96d370 100644
--- a/source/common/UnicodeInlines.incl_cpp
+++ b/source/common/UnicodeInlines.incl_cpp
@@ -2,7 +2,7 @@
#define __UnicodeInlines_incl_cpp__
// =================================================================================================
-// Copyright 2002-2007 Adobe Systems Incorporated
+// Copyright 2004 Adobe Systems Incorporated
// All Rights Reserved.
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
diff --git a/source/common/XMLParserAdapter.hpp b/source/common/XMLParserAdapter.hpp
index a21771f..c0fca52 100644
--- a/source/common/XMLParserAdapter.hpp
+++ b/source/common/XMLParserAdapter.hpp
@@ -2,7 +2,7 @@
#define __XMLParserAdapter_hpp__
// =================================================================================================
-// Copyright 2005-2007 Adobe Systems Incorporated
+// Copyright 2005 Adobe Systems Incorporated
// All Rights Reserved.
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
diff --git a/source/common/XML_Node.cpp b/source/common/XML_Node.cpp
index 9163fff..29854b4 100644
--- a/source/common/XML_Node.cpp
+++ b/source/common/XML_Node.cpp
@@ -1,5 +1,5 @@
// =================================================================================================
-// Copyright 2005-2007 Adobe Systems Incorporated
+// Copyright 2007 Adobe Systems Incorporated
// All Rights Reserved.
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
@@ -212,7 +212,7 @@ static void DumpNodeList ( std::string * buffer, const XML_NodeVector & list, in
if ( node->nsPrefixLen != 0 ) {
*buffer += ", prefixLen=";
char numBuf [20];
- snprintf ( numBuf, sizeof(numBuf), "%d", node->nsPrefixLen );
+ snprintf ( numBuf, sizeof(numBuf), "%d", (int)node->nsPrefixLen );
*buffer += numBuf;
}
*buffer += "\n";
diff --git a/source/common/XMP_LibUtils.cpp b/source/common/XMP_LibUtils.cpp
new file mode 100644
index 0000000..afe15ac
--- /dev/null
+++ b/source/common/XMP_LibUtils.cpp
@@ -0,0 +1,592 @@
+// =================================================================================================
+// Copyright 2009 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 "XMP_Environment.h"
+
+#include "XMP_LibUtils.hpp"
+
+#include "UnicodeInlines.incl_cpp"
+
+// =================================================================================================
+
+#ifndef TraceThreadLocks
+ #define TraceThreadLocks 0
+#endif
+
+// -------------------------------------------------------------------------------------------------
+
+extern "C" bool Initialize_LibUtils()
+{
+ return true;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+extern "C" void Terminate_LibUtils(){
+ // Nothing to do.
+}
+
+// =================================================================================================
+// Thread synchronization locks
+// =================================================================================================
+
+XMP_ReadWriteLock::XMP_ReadWriteLock() : beingWritten(false)
+{
+ #if XMP_DebugBuild && HaveAtomicIncrDecr
+ this->lockCount = 0;
+ // Atomic counter must be 32 or 64 bits and naturally aligned.
+ size_t counterSize = sizeof ( XMP_AtomicCounter );
+ size_t counterOffset = XMP_OffsetOf ( XMP_ReadWriteLock, lockCount );
+ XMP_Assert ( (counterSize == 4) || (counterSize == 8) ); // Counter must be 32 or 64 bits.
+ XMP_Assert ( (counterOffset & (counterSize-1)) == 0 ); // Counter must be naturally aligned.
+ #endif
+ XMP_BasicRWLock_Initialize ( this->lock );
+ #if TraceThreadLocks
+ fprintf ( stderr, "Created lock %.8X\n", this );
+ #endif
+}
+
+// ---------------------------------------------------------------------------------------------
+
+XMP_ReadWriteLock::~XMP_ReadWriteLock()
+{
+ #if TraceThreadLocks
+ fprintf ( stderr, "Deleting lock %.8X\n", this );
+ #endif
+ #if XMP_DebugBuild && HaveAtomicIncrDecr
+ XMP_Assert ( this->lockCount == 0 );
+ #endif
+ XMP_BasicRWLock_Terminate ( this->lock );
+}
+
+// ---------------------------------------------------------------------------------------------
+
+void XMP_ReadWriteLock::Acquire ( bool forWriting )
+{
+ #if TraceThreadLocks
+ fprintf ( stderr, "Acquiring lock %.8X for %s, count %d%s\n",
+ this, (forWriting ? "writing" : "reading"), this->lockCount, (this->beingWritten ? ", being written" : "") );
+ #endif
+
+ if ( forWriting ) {
+ XMP_BasicRWLock_AcquireForWrite ( this->lock );
+ #if XMP_DebugBuild && HaveAtomicIncrDecr
+ XMP_Assert ( this->lockCount == 0 );
+ #endif
+ } else {
+ XMP_BasicRWLock_AcquireForRead ( this->lock );
+ XMP_Assert ( ! this->beingWritten );
+ }
+ #if XMP_DebugBuild && HaveAtomicIncrDecr
+ XMP_AtomicIncrement ( this->lockCount );
+ #endif
+ this->beingWritten = forWriting;
+
+ #if TraceThreadLocks
+ fprintf ( stderr, "Acquired lock %.8X for %s, count %d%s\n",
+ this, (forWriting ? "writing" : "reading"), this->lockCount, (this->beingWritten ? ", being written" : "") );
+ #endif
+}
+
+// ---------------------------------------------------------------------------------------------
+
+void XMP_ReadWriteLock::Release()
+{
+ #if TraceThreadLocks
+ fprintf ( stderr, "Releasing lock %.8X, count %d%s\n", this, this->lockCount, (this->beingWritten ? ", being written" : "") );
+ #endif
+
+ #if XMP_DebugBuild && HaveAtomicIncrDecr
+ XMP_Assert ( this->lockCount > 0 );
+ XMP_AtomicDecrement ( this->lockCount ); // ! Do these before unlocking, that might release a waiting thread.
+ #endif
+ bool forWriting = this->beingWritten;
+ this->beingWritten = false;
+
+ if ( forWriting ) {
+ XMP_BasicRWLock_ReleaseFromWrite ( this->lock );
+ } else {
+ XMP_BasicRWLock_ReleaseFromRead ( this->lock );
+ }
+
+ #if TraceThreadLocks
+ fprintf ( stderr, "Released lock %.8X, count %d%s\n", this, this->lockCount, (this->beingWritten ? ", being written" : "") );
+ #endif
+}
+
+// =================================================================================================
+
+#if UseHomeGrownLock
+
+ #if XMP_MacBuild | XMP_UNIXBuild
+
+ // -----------------------------------------------------------------------------------------
+
+ // About pthread mutexes and conditions:
+ //
+ // The mutex protecting the condition must be locked before waiting for the condition. A
+ // thread can wait for a condition to be signaled by calling the pthread_cond_wait
+ // subroutine. The subroutine atomically unlocks the mutex and blocks the calling thread
+ // until the condition is signaled. When the call returns, the mutex is locked again.
+
+ #define InitializeBasicMutex(mutex) { int err = pthread_mutex_init ( &mutex, 0 ); XMP_Enforce ( err == 0 ); }
+ #define TerminateBasicMutex(mutex) { int err = pthread_mutex_destroy ( &mutex ); XMP_Enforce ( err == 0 ); }
+
+ #define AcquireBasicMutex(mutex) { int err = pthread_mutex_lock ( &mutex ); XMP_Enforce ( err == 0 ); }
+ #define ReleaseBasicMutex(mutex) { int err = pthread_mutex_unlock ( &mutex ); XMP_Enforce ( err == 0 ); }
+
+ #define InitializeBasicQueue(queue) { int err = pthread_cond_init ( &queue, 0 ); XMP_Enforce ( err == 0 ); }
+ #define TerminateBasicQueue(queue) { int err = pthread_cond_destroy ( &queue ); XMP_Enforce ( err == 0 ); }
+
+ #define WaitOnBasicQueue(queue,mutex) { int err = pthread_cond_wait ( &queue, &mutex ); XMP_Enforce ( err == 0 ); }
+ #define ReleaseOneBasicQueue(queue) { int err = pthread_cond_signal ( &queue ); XMP_Enforce ( err == 0 ); }
+ #define ReleaseAllBasicQueue(queue) { int err = pthread_cond_broadcast ( &queue ); XMP_Enforce ( err == 0 ); }
+
+ // -----------------------------------------------------------------------------------------
+
+ #elif XMP_WinBuild
+
+ // -----------------------------------------------------------------------------------------
+
+ #define InitializeBasicMutex(mutex) { InitializeCriticalSection ( &mutex ); }
+ #define TerminateBasicMutex(mutex) { DeleteCriticalSection ( &mutex ); }
+
+ #define AcquireBasicMutex(mutex) { EnterCriticalSection ( &mutex ); }
+ #define ReleaseBasicMutex(mutex) { LeaveCriticalSection ( &mutex ); }
+
+ #if ! BuildLocksForWinXP
+
+ // About Win32 condition variables (not on XP):
+ //
+ // Condition variables enable threads to atomically release a lock and enter the
+ // sleeping state. They can be used with critical sections or slim reader/writer (SRW)
+ // locks. Condition variables support operations that "wake one" or "wake all" waiting
+ // threads. After a thread is woken, it re-acquires the lock it released when the thread
+ // entered the sleeping state.
+
+ #define InitializeBasicQueue(queue) { InitializeConditionVariable ( &queue ); }
+ #define TerminateBasicQueue(queue) /* Do nothing. */
+
+ #define WaitOnBasicQueue(queue,mutex) \
+ { BOOL ok = SleepConditionVariableCS ( &queue, &mutex, INFINITE /* timeout */ ); XMP_Enforce ( ok ); }
+
+ #define ReleaseOneBasicQueue(queue) { WakeConditionVariable ( &queue ); }
+ #define ReleaseAllBasicQueue(queue) { WakeAllConditionVariable ( &queue ); }
+
+ #else
+
+ // Need to create our own queue for Windows XP. This is not a general queue, it depends
+ // on the usage inside XMP_HomeGrownLock where the queueMutex guarantees that the
+ // queueing operations are done single threaded.
+
+ #define InitializeBasicQueue(queue) /* Do nothing. */
+ #define TerminateBasicQueue(queue) /* Do nothing. */
+
+ #define WaitOnBasicQueue(queue,mutex) { queue.Wait ( mutex ); }
+ #define ReleaseOneBasicQueue(queue) { queue.ReleaseOne(); }
+ #define ReleaseAllBasicQueue(queue) { queue.ReleaseAll(); }
+
+ // -------------------------------------------------------------------------------------
+
+ XMP_WinXP_HGQueue::XMP_WinXP_HGQueue() : queueEvent(0), waitCount(0), releaseAll(false)
+ {
+ this->queueEvent = CreateEvent ( NULL, FALSE, TRUE, NULL ); // Auto reset, initially clear.
+ XMP_Enforce ( this->queueEvent != 0 );
+ }
+
+ // -------------------------------------------------------------------------------------
+
+ XMP_WinXP_HGQueue::~XMP_WinXP_HGQueue()
+ {
+ CloseHandle ( this->queueEvent );
+ }
+
+ // -------------------------------------------------------------------------------------
+
+ void XMP_WinXP_HGQueue::Wait ( XMP_BasicMutex & queueMutex )
+ {
+ ++this->waitCount; // ! Does not need atomic increment, protected by queue mutex.
+ ReleaseBasicMutex ( queueMutex );
+ DWORD status = WaitForSingleObject ( this->queueEvent, INFINITE );
+ if ( status != WAIT_OBJECT_0 ) XMP_Throw ( "Failure from WaitForSingleObject", kXMPErr_ExternalFailure );
+ AcquireBasicMutex ( queueMutex );
+ --this->waitCount; // ! Does not need atomic decrement, protected by queue mutex.
+
+ if ( this->releaseAll ) {
+ if ( this->waitCount == 0 ) {
+ this->releaseAll = false;
+ } else {
+ BOOL ok = SetEvent ( this->queueEvent );
+ if ( ! ok ) XMP_Throw ( "Failure from SetEvent", kXMPErr_ExternalFailure );
+ }
+ }
+ }
+
+ // -------------------------------------------------------------------------------------
+
+ void XMP_WinXP_HGQueue::ReleaseOne()
+ {
+ XMP_Assert ( ! this->releaseAll );
+ BOOL ok = SetEvent ( this->queueEvent );
+ if ( ! ok ) XMP_Throw ( "Failure from SetEvent", kXMPErr_ExternalFailure );
+ }
+
+ // -------------------------------------------------------------------------------------
+
+ void XMP_WinXP_HGQueue::ReleaseAll()
+ {
+ this->releaseAll = true;
+ BOOL ok = SetEvent ( this->queueEvent );
+ if ( ! ok ) XMP_Throw ( "Failure from SetEvent", kXMPErr_ExternalFailure );
+ }
+
+ #endif
+
+ // -----------------------------------------------------------------------------------------
+
+ #endif
+
+ // =============================================================================================
+
+ XMP_HomeGrownLock::XMP_HomeGrownLock() : lockCount(0), readersWaiting(0), writersWaiting(0), beingWritten(false)
+ {
+ InitializeBasicMutex ( this->queueMutex );
+ InitializeBasicQueue ( this->writerQueue );
+ InitializeBasicQueue ( this->readerQueue );
+ }
+
+ // =============================================================================================
+
+ XMP_HomeGrownLock::~XMP_HomeGrownLock()
+ {
+ TerminateBasicMutex ( this->queueMutex );
+ TerminateBasicQueue ( this->writerQueue );
+ TerminateBasicQueue ( this->readerQueue );
+ }
+
+ // =============================================================================================
+
+ void XMP_HomeGrownLock::AcquireForRead()
+ {
+ XMP_AutoMutex autoMutex ( &this->queueMutex );
+
+ ++this->readersWaiting; // ! Does not need atomic increment, protected by queue mutex.
+ while ( (this->beingWritten) || (this->writersWaiting > 0) ) {
+ // Don't allow more readers if writers are waiting.
+ WaitOnBasicQueue ( this->readerQueue, this->queueMutex );
+ }
+ --this->readersWaiting; // ! Does not need atomic decrement, protected by queue mutex.
+ XMP_Assert ( ! this->beingWritten );
+
+ ++this->lockCount; // ! Does not need atomic increment, protected by queue mutex.
+ }
+
+ // =============================================================================================
+
+ void XMP_HomeGrownLock::AcquireForWrite()
+ {
+ XMP_AutoMutex autoMutex ( &this->queueMutex );
+
+ ++this->writersWaiting; // ! Does not need atomic increment, protected by queue mutex.
+ while ( this->lockCount > 0 ) {
+ WaitOnBasicQueue ( this->writerQueue, this->queueMutex );
+ }
+ --this->writersWaiting; // ! Does not need atomic decrement, protected by queue mutex.
+ XMP_Assert ( (! this->beingWritten) && (this->lockCount == 0) );
+
+ ++this->lockCount; // ! Does not need atomic increment, protected by queue mutex.
+ this->beingWritten = true;
+ }
+
+ // =============================================================================================
+
+ void XMP_HomeGrownLock::ReleaseFromRead()
+ {
+ XMP_AutoMutex autoMutex ( &this->queueMutex );
+
+ XMP_Assert ( (! this->beingWritten) && (this->lockCount > 0) );
+ --this->lockCount; // ! Does not need atomic decrement, protected by queue mutex.
+
+ if ( this->writersWaiting > 0 ) {
+ ReleaseOneBasicQueue ( this->writerQueue );
+ } else if ( this->readersWaiting > 0 ) {
+ ReleaseAllBasicQueue ( this->readerQueue );
+ }
+
+ }
+
+ // =============================================================================================
+
+ void XMP_HomeGrownLock::ReleaseFromWrite()
+ {
+ XMP_AutoMutex autoMutex ( &this->queueMutex );
+
+ XMP_Assert ( this->beingWritten && (this->lockCount == 1) );
+ --this->lockCount; // ! Does not need atomic decrement, protected by queue mutex.
+ this->beingWritten = false;
+
+ if ( this->writersWaiting > 0 ) {
+ ReleaseOneBasicQueue ( this->writerQueue );
+ } else if ( this->readersWaiting > 0 ) {
+ ReleaseAllBasicQueue ( this->readerQueue );
+ }
+ }
+
+ // =============================================================================================
+
+#endif
+
+// =================================================================================================
+// Data structure dumping utilities
+// ================================
+
+void
+DumpClearString ( const XMP_VarString & value, XMP_TextOutputProc outProc, void * refCon )
+{
+
+ char buffer [20];
+ bool prevNormal;
+ XMP_Status status = 0;
+
+ XMP_StringPtr spanStart, spanEnd;
+ XMP_StringPtr valueEnd = &value[0] + value.size();
+
+ spanStart = &value[0];
+ while ( spanStart < valueEnd ) {
+
+ // Output the next span of regular characters.
+ for ( spanEnd = spanStart; spanEnd < valueEnd; ++spanEnd ) {
+ if ( *spanEnd > 0x7F ) break;
+ if ( (*spanEnd < 0x20) && (*spanEnd != kTab) && (*spanEnd != kLF) ) break;
+ }
+ if ( spanStart != spanEnd ) status = (*outProc) ( refCon, spanStart, (XMP_StringLen)(spanEnd-spanStart) );
+ if ( status != 0 ) break;
+ spanStart = spanEnd;
+
+ // Output the next span of irregular characters.
+ prevNormal = true;
+ for ( spanEnd = spanStart; spanEnd < valueEnd; ++spanEnd ) {
+ if ( ((0x20 <= *spanEnd) && (*spanEnd <= 0x7F)) || (*spanEnd == kTab) || (*spanEnd == kLF) ) break;
+ char space = ' ';
+ if ( prevNormal ) space = '<';
+ status = (*outProc) ( refCon, &space, 1 );
+ if ( status != 0 ) break;
+ OutProcHexByte ( *spanEnd );
+ prevNormal = false;
+ }
+ if ( ! prevNormal ) {
+ status = (*outProc) ( refCon, ">", 1 );
+ if ( status != 0 ) return;
+ }
+ spanStart = spanEnd;
+
+ }
+
+} // DumpClearString
+
+// -------------------------------------------------------------------------------------------------
+
+static void
+DumpStringMap ( const XMP_StringMap & map, XMP_StringPtr label, XMP_TextOutputProc outProc, void * refCon )
+{
+ XMP_cStringMapPos currPos;
+ XMP_cStringMapPos endPos = map.end();
+
+ size_t maxLen = 0;
+ for ( currPos = map.begin(); currPos != endPos; ++currPos ) {
+ size_t currLen = currPos->first.size();
+ if ( currLen > maxLen ) maxLen = currLen;
+ }
+
+ OutProcNewline();
+ OutProcLiteral ( label );
+ OutProcNewline();
+
+ for ( currPos = map.begin(); currPos != endPos; ++currPos ) {
+ OutProcNChars ( " ", 2 );
+ DumpClearString ( currPos->first, outProc, refCon );
+ OutProcPadding ( maxLen - currPos->first.size() );
+ OutProcNChars ( " => ", 4 );
+ DumpClearString ( currPos->second, outProc, refCon );
+ OutProcNewline();
+ }
+
+} // DumpStringMap
+
+// =================================================================================================
+// Namespace Tables
+// =================================================================================================
+
+XMP_NamespaceTable::XMP_NamespaceTable ( const XMP_NamespaceTable & presets )
+{
+ XMP_AutoLock presetLock ( &presets.lock, kXMP_ReadLock );
+
+ this->uriToPrefixMap = presets.uriToPrefixMap;
+ this->prefixToURIMap = presets.prefixToURIMap;
+
+} // XMP_NamespaceTable::XMP_NamespaceTable
+
+// =================================================================================================
+
+bool XMP_NamespaceTable::Define ( XMP_StringPtr _uri, XMP_StringPtr _suggPrefix,
+ XMP_StringPtr * prefixPtr, XMP_StringLen * prefixLen )
+{
+ XMP_AutoLock tableLock ( &this->lock, kXMP_WriteLock );
+ bool prefixMatches = false;
+
+ XMP_Assert ( (_uri != 0) && (*_uri != 0) && (_suggPrefix != 0) && (*_suggPrefix != 0) );
+
+ XMP_VarString uri ( _uri );
+ XMP_VarString suggPrefix ( _suggPrefix );
+ if ( suggPrefix[suggPrefix.size()-1] != ':' ) suggPrefix += ':';
+ VerifySimpleXMLName ( _suggPrefix, _suggPrefix+suggPrefix.size()-1 ); // Exclude the colon.
+
+ XMP_StringMapPos uriPos = this->uriToPrefixMap.find ( uri );
+
+ if ( uriPos == this->uriToPrefixMap.end() ) {
+
+ // The URI is not yet registered, make sure we use a unique prefix.
+
+ XMP_VarString uniqPrefix ( suggPrefix );
+ int suffix = 0;
+ char buffer [32]; // AUDIT: Plenty of room for the "_%d_" suffix.
+
+ while ( true ) {
+ if ( this->prefixToURIMap.find ( uniqPrefix ) == this->prefixToURIMap.end() ) break;
+ ++suffix;
+ snprintf ( buffer, sizeof(buffer), "_%d_:", suffix ); // AUDIT: Using sizeof for snprintf length is safe.
+ uniqPrefix = suggPrefix;
+ uniqPrefix.erase ( uniqPrefix.size()-1 ); // ! Remove the trailing ':'.
+ uniqPrefix += buffer;
+ }
+
+ // Add the new namespace to both maps.
+
+ XMP_StringPair newNS ( uri, uniqPrefix );
+ uriPos = this->uriToPrefixMap.insert ( this->uriToPrefixMap.end(), newNS );
+
+ newNS.first.swap ( newNS.second );
+ (void) this->prefixToURIMap.insert ( this->prefixToURIMap.end(), newNS );
+
+ }
+
+ // Return the actual prefix and see if it matches the suggested prefix.
+
+ if ( prefixPtr != 0 ) *prefixPtr = uriPos->second.c_str();
+ if ( prefixLen != 0 ) *prefixLen = (XMP_StringLen)uriPos->second.size();
+
+ prefixMatches = ( uriPos->second == suggPrefix );
+ return prefixMatches;
+
+} // XMP_NamespaceTable::Define
+
+// =================================================================================================
+
+bool XMP_NamespaceTable::GetPrefix ( XMP_StringPtr _uri, XMP_StringPtr * prefixPtr, XMP_StringLen * prefixLen ) const
+{
+ XMP_AutoLock tableLock ( &this->lock, kXMP_ReadLock );
+ bool found = false;
+
+ XMP_Assert ( (_uri != 0) && (*_uri != 0) );
+
+ XMP_VarString uri ( _uri );
+ XMP_cStringMapPos uriPos = this->uriToPrefixMap.find ( uri );
+
+ if ( uriPos != this->uriToPrefixMap.end() ) {
+ if ( prefixPtr != 0 ) *prefixPtr = uriPos->second.c_str();
+ if ( prefixLen != 0 ) *prefixLen = (XMP_StringLen)uriPos->second.size();
+ found = true;
+ }
+
+ return found;
+
+} // XMP_NamespaceTable::GetPrefix
+
+// =================================================================================================
+
+bool XMP_NamespaceTable::GetURI ( XMP_StringPtr _prefix, XMP_StringPtr * uriPtr, XMP_StringLen * uriLen ) const
+{
+ XMP_AutoLock tableLock ( &this->lock, kXMP_ReadLock );
+
+ bool found = false;
+
+ XMP_Assert ( (_prefix != 0) && (*_prefix != 0) );
+
+ XMP_VarString prefix ( _prefix );
+ if ( prefix[prefix.size()-1] != ':' ) prefix += ':';
+ XMP_cStringMapPos prefixPos = this->prefixToURIMap.find ( prefix );
+
+ if ( prefixPos != this->prefixToURIMap.end() ) {
+ if ( uriPtr != 0 ) *uriPtr = prefixPos->second.c_str();
+ if ( uriLen != 0 ) *uriLen = (XMP_StringLen)prefixPos->second.size();
+ found = true;
+ }
+
+ return found;
+
+} // XMP_NamespaceTable::GetURI
+
+// =================================================================================================
+
+void XMP_NamespaceTable::Dump ( XMP_TextOutputProc outProc, void * refCon ) const
+{
+ XMP_AutoLock tableLock ( &this->lock, kXMP_ReadLock );
+
+ XMP_cStringMapPos p2uEnd = this->prefixToURIMap.end(); // ! Move up to avoid gcc complaints.
+ XMP_cStringMapPos u2pEnd = this->uriToPrefixMap.end();
+
+ DumpStringMap ( this->prefixToURIMap, "Dumping namespace prefix to URI map", outProc, refCon );
+
+ if ( this->prefixToURIMap.size() != this->uriToPrefixMap.size() ) {
+ OutProcLiteral ( "** bad namespace map sizes **" );
+ XMP_Throw ( "Fatal namespace map problem", kXMPErr_InternalFailure );
+ }
+
+ for ( XMP_cStringMapPos nsLeft = this->prefixToURIMap.begin(); nsLeft != p2uEnd; ++nsLeft ) {
+
+ XMP_cStringMapPos nsOther = this->uriToPrefixMap.find ( nsLeft->second );
+ if ( (nsOther == u2pEnd) || (nsLeft != this->prefixToURIMap.find ( nsOther->second )) ) {
+ OutProcLiteral ( " ** bad namespace URI ** " );
+ DumpClearString ( nsLeft->second, outProc, refCon );
+ break;
+ }
+
+ for ( XMP_cStringMapPos nsRight = nsLeft; nsRight != p2uEnd; ++nsRight ) {
+ if ( nsRight == nsLeft ) continue; // ! Can't start at nsLeft+1, no operator+!
+ if ( nsLeft->second == nsRight->second ) {
+ OutProcLiteral ( " ** duplicate namespace URI ** " );
+ DumpClearString ( nsLeft->second, outProc, refCon );
+ break;
+ }
+ }
+
+ }
+
+ for ( XMP_cStringMapPos nsLeft = this->uriToPrefixMap.begin(); nsLeft != u2pEnd; ++nsLeft ) {
+
+ XMP_cStringMapPos nsOther = this->prefixToURIMap.find ( nsLeft->second );
+ if ( (nsOther == p2uEnd) || (nsLeft != this->uriToPrefixMap.find ( nsOther->second )) ) {
+ OutProcLiteral ( " ** bad namespace prefix ** " );
+ DumpClearString ( nsLeft->second, outProc, refCon );
+ break;
+ }
+
+ for ( XMP_cStringMapPos nsRight = nsLeft; nsRight != u2pEnd; ++nsRight ) {
+ if ( nsRight == nsLeft ) continue; // ! Can't start at nsLeft+1, no operator+!
+ if ( nsLeft->second == nsRight->second ) {
+ OutProcLiteral ( " ** duplicate namespace prefix ** " );
+ DumpClearString ( nsLeft->second, outProc, refCon );
+ break;
+ }
+ }
+
+ }
+
+} // XMP_NamespaceTable::Dump
+
+// =================================================================================================
diff --git a/source/common/XMP_LibUtils.hpp b/source/common/XMP_LibUtils.hpp
new file mode 100644
index 0000000..7697de3
--- /dev/null
+++ b/source/common/XMP_LibUtils.hpp
@@ -0,0 +1,520 @@
+#ifndef __XMP_LibUtils_hpp__
+#define __XMP_LibUtils_hpp__ 1
+
+// =================================================================================================
+// Copyright 2009 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 "XMP_Environment.h" // ! Must be the first include.
+#include "XMP_Const.h"
+
+#include <map>
+#include <string>
+
+#if XMP_DebugBuild
+ #include <cassert>
+#endif
+
+#if XMP_WinBuild
+ #ifndef snprintf
+ #define snprintf _snprintf
+ #endif
+#endif
+
+// =================================================================================================
+// Basic types, constants
+// ======================
+
+#define kTab ((char)0x09)
+#define kLF ((char)0x0A)
+#define kCR ((char)0x0D)
+
+typedef std::string XMP_VarString;
+
+#define EliminateGlobal(g) delete ( g ); g = 0
+
+extern "C" bool Initialize_LibUtils();
+extern "C" void Terminate_LibUtils();
+
+static void * ignorePtr = 0;
+#define IgnoreParam(p) ignorePtr = (void*)&p
+
+// The builtin offsetof macro sometimes violates C++ data member rules.
+#define XMP_OffsetOf(struct,field) ( (char*)(&((struct*)0x100)->field) - (char*)0x100 )
+
+// =================================================================================================
+// Support for exceptions and asserts
+// ==================================
+
+#define AnnounceThrow(msg) /* Do nothing. */
+#define AnnounceCatch(msg) /* Do nothing. */
+
+#define XMP_Throw(msg,id) { AnnounceThrow ( msg ); throw XMP_Error ( id, msg ); }
+
+// -------------------------------------------------------------------------------------------------
+
+#define _MakeStr(p) #p
+#define _NotifyMsg(n,c,f,l) #n " failed: " #c " in " f " at line " _MakeStr(l)
+#define _ExplicitMsg(msg,c,e) #e " " #msg ": " #c
+
+#define XMP_Validate(c,msg,e) \
+ if ( ! (c) ) { \
+ const char * validate_msg = _ExplicitMsg ( msg, c, e ); \
+ XMP_Throw ( validate_msg, e ); \
+ }
+
+#if ! XMP_DebugBuild
+ #define XMP_Assert(c) ((void) 0)
+#else
+ #define XMP_Assert(c) assert ( c )
+#endif
+
+ #define XMP_Enforce(c) \
+ if ( ! (c) ) { \
+ const char * assert_msg = _NotifyMsg ( XMP_Enforce, (c), __FILE__, __LINE__ ); \
+ XMP_Throw ( assert_msg , kXMPErr_EnforceFailure ); \
+ }
+// =================================================================================================
+// Thread synchronization locks
+// ============================
+
+// About XMP and thread synchronization
+//
+// A variety of choices are provided for thread synchronization. Exactly one method must be chosen
+// by defining the appropriate symbol to 1.
+//
+// * UseNoLock - This choice turns the turns the synchronization functions into no-ops. It must only
+// be used by single threaded clients, or clients providing their own control at a higher level.
+//
+// * UseGlobalLibraryLock - This choice uses a single per-library lock. The result is thread safe
+// but unfriendly behavior, no true concurrency. This should only be used as a debugging fallback.
+//
+// * UseBoostLock - This choice uses the Boost shared_mutex mechanism. It has the advantage of being
+// robust and being available on pretty much all platforms. It has the disadvantage of requiring the
+// developer to download, integrate, and build the Boost thread library.
+//
+// * UsePThreadLock - This choice uses the POSIX pthread rwlock mechanism. It has the advantage of
+// being robust and being available on any modern UNIX platform, including Mac OS X.
+//
+// * UseWinSlimLock - This choice uses the Windows slim reader/writer mechanism. It is robust but
+// only available on Vista and newer versions of Windows, it is not available on XP.
+//
+// * UseHomeGrownLock - This choice uses local code plus lower level synchronization primitives. It
+// has the advantage of being usable on all platforms, and having exposed and tunable policy. It
+// has the disadvantage of possibly being less robust than Boost or the O/S provided mechanisms.
+// The lower level synchronization primitives are pthread mutex and condition for UNIX (including
+// Mac OS X). For WIndows there is a choice of critical section and condition variable for Vista
+// and newer; or critical section, event and semaphore for XP and newer.
+
+#define UseHomeGrownLock 1
+
+// -------------------------------------------------------------------------------------------------
+// A basic exclusive access mutex and atomic increment/decrement operations.
+
+#if XMP_WinBuild
+
+ #include <Windows.h>
+
+ #define HaveAtomicIncrDecr 1
+ typedef LONG XMP_AtomicCounter;
+
+ #define XMP_AtomicIncrement(x) InterlockedIncrement ( &(x) )
+ #define XMP_AtomicDecrement(x) InterlockedDecrement ( &(x) )
+
+ typedef CRITICAL_SECTION XMP_BasicMutex;
+
+ #define InitializeBasicMutex(mutex) { InitializeCriticalSection ( &mutex ); }
+ #define TerminateBasicMutex(mutex) { DeleteCriticalSection ( &mutex ); }
+ #define AcquireBasicMutex(mutex) { EnterCriticalSection ( &mutex ); }
+ #define ReleaseBasicMutex(mutex) { LeaveCriticalSection ( &mutex ); }
+
+#elif XMP_MacBuild
+
+ #include <pthread.h>
+ #include <libkern/OSAtomic.h>
+
+ #define HaveAtomicIncrDecr 1
+ typedef int32_t XMP_AtomicCounter;
+
+ #define XMP_AtomicIncrement(x) OSAtomicIncrement32 ( &(x) )
+ #define XMP_AtomicDecrement(x) OSAtomicDecrement32 ( &(x) )
+
+ typedef pthread_mutex_t XMP_BasicMutex;
+
+ #define InitializeBasicMutex(mutex) { int err = pthread_mutex_init ( &mutex, 0 ); XMP_Enforce ( err == 0 ); }
+ #define TerminateBasicMutex(mutex) { int err = pthread_mutex_destroy ( &mutex ); XMP_Enforce ( err == 0 ); }
+ #define AcquireBasicMutex(mutex) { int err = pthread_mutex_lock ( &mutex ); XMP_Enforce ( err == 0 ); }
+ #define ReleaseBasicMutex(mutex) { int err = pthread_mutex_unlock ( &mutex ); XMP_Enforce ( err == 0 ); }
+
+#elif XMP_UNIXBuild
+
+ #include <pthread.h>
+
+ // Atomic increment/decrement intrinsics should be in gcc 4.1, but Solaris seems to lack them.
+ #ifndef HaveAtomicIncrDecr
+ #define HaveAtomicIncrDecr 1
+ #endif
+ #if HaveAtomicIncrDecr
+ typedef XMP_Uns32 XMP_AtomicCounter;
+ #define XMP_AtomicIncrement(x) __sync_add_and_fetch ( &(x), 1 )
+ #define XMP_AtomicDecrement(x) __sync_sub_and_fetch ( &(x), 1 )
+ #endif
+
+ typedef pthread_mutex_t XMP_BasicMutex;
+
+ #define InitializeBasicMutex(mutex) { int err = pthread_mutex_init ( &mutex, 0 ); XMP_Enforce ( err == 0 ); }
+ #define TerminateBasicMutex(mutex) { int err = pthread_mutex_destroy ( &mutex ); XMP_Enforce ( err == 0 ); }
+ #define AcquireBasicMutex(mutex) { int err = pthread_mutex_lock ( &mutex ); XMP_Enforce ( err == 0 ); }
+ #define ReleaseBasicMutex(mutex) { int err = pthread_mutex_unlock ( &mutex ); XMP_Enforce ( err == 0 ); }
+
+#endif
+
+class XMP_AutoMutex {
+public:
+ XMP_AutoMutex ( XMP_BasicMutex * _mutex ) : mutex(_mutex) { AcquireBasicMutex ( *this->mutex ); }
+ ~XMP_AutoMutex() { this->Release(); }
+ void Release() { if ( this->mutex != 0 ) ReleaseBasicMutex ( *this->mutex ); this->mutex = 0; }
+private:
+ XMP_BasicMutex * mutex;
+ XMP_AutoMutex() {}; // ! Must not be used.
+};
+
+// -------------------------------------------------------------------------------------------------
+// Details for the various locking mechanisms.
+
+#if UseNoLock
+
+ typedef void* XMP_BasicRWLock; // For single threaded clients that want maximum performance.
+
+ #define XMP_BasicRWLock_Initialize(lck) /* Do nothing. */
+ #define XMP_BasicRWLock_Terminate(lck) /* Do nothing. */
+
+ #define XMP_BasicRWLock_AcquireForRead(lck) /* Do nothing. */
+ #define XMP_BasicRWLock_AcquireForWrite(lck) /* Do nothing. */
+
+ #define XMP_BasicRWLock_ReleaseFromRead(lck) /* Do nothing. */
+ #define XMP_BasicRWLock_ReleaseFromWrite(lck) /* Do nothing. */
+
+#elif UseGlobalLibraryLock
+
+ extern XMP_BasicMutex sLibraryLock;
+
+ typedef void* XMP_BasicRWLock; // Use the old thread-unfriendly per-DLL mutex.
+
+ #define XMP_BasicRWLock_Initialize(lck) /* Do nothing. */
+ #define XMP_BasicRWLock_Terminate(lck) /* Do nothing. */
+
+ #define XMP_BasicRWLock_AcquireForRead(lck) /* Do nothing. */
+ #define XMP_BasicRWLock_AcquireForWrite(lck) /* Do nothing. */
+
+ #define XMP_BasicRWLock_ReleaseFromRead(lck) /* Do nothing. */
+ #define XMP_BasicRWLock_ReleaseFromWrite(lck) /* Do nothing. */
+
+#elif UseBoostLock
+
+ #include <boost/thread/shared_mutex.hpp>
+ typedef boost::shared_mutex XMP_BasicRWLock;
+
+ #define XMP_BasicRWLock_Initialize(lck) /* Do nothing. */
+ #define XMP_BasicRWLock_Terminate(lck) /* Do nothing. */
+
+ #define XMP_BasicRWLock_AcquireForRead(lck) lck.lock_shared()
+ #define XMP_BasicRWLock_AcquireForWrite(lck) lck.lock()
+
+ #define XMP_BasicRWLock_ReleaseFromRead(lck) lck.unlock_shared()
+ #define XMP_BasicRWLock_ReleaseFromWrite(lck) lck.unlock()
+
+#elif UsePThreadLock
+
+ #include <pthread.h>
+ typedef pthread_rwlock_t XMP_BasicRWLock;
+
+ #define XMP_BasicRWLock_Initialize(lck) \
+ { int err = pthread_rwlock_init ( &lck, 0 ); \
+ if ( err != 0 ) XMP_Throw ( "Initialize pthread rwlock failed", kXMPErr_ExternalFailure ); }
+ #define XMP_BasicRWLock_Terminate(lck) \
+ { int err = pthread_rwlock_destroy ( &lck ); XMP_Assert ( err == 0 ); }
+
+ #define XMP_BasicRWLock_AcquireForRead(lck) \
+ { int err = pthread_rwlock_rdlock ( &lck ); \
+ if ( err != 0 ) XMP_Throw ( "Acquire pthread read lock failed", kXMPErr_ExternalFailure ); }
+ #define XMP_BasicRWLock_AcquireForWrite(lck) \
+ { int err = pthread_rwlock_wrlock ( &lck ); \
+ if ( err != 0 ) XMP_Throw ( "Acquire pthread write lock failed", kXMPErr_ExternalFailure ); }
+
+ #define XMP_BasicRWLock_ReleaseFromRead(lck) \
+ { int err = pthread_rwlock_unlock ( &lck ); \
+ if ( err != 0 ) XMP_Throw ( "Release pthread read lock failed", kXMPErr_ExternalFailure ); }
+ #define XMP_BasicRWLock_ReleaseFromWrite(lck) \
+ { int err = pthread_rwlock_unlock ( &lck ); \
+ if ( err != 0 ) XMP_Throw ( "Release pthread write lock failed", kXMPErr_ExternalFailure ); }
+
+#elif UseWinSlimLock
+
+ #include <Windows.h>
+ typedef SRWLOCK XMP_BasicRWLock;
+
+ #define XMP_BasicRWLock_Initialize(lck) InitializeSRWLock ( &lck )
+ #define XMP_BasicRWLock_Terminate(lck) /* Do nothing. */
+
+ #define XMP_BasicRWLock_AcquireForRead(lck) AcquireSRWLockShared ( &lck )
+ #define XMP_BasicRWLock_AcquireForWrite(lck) AcquireSRWLockExclusive ( &lck )
+
+ #define XMP_BasicRWLock_ReleaseFromRead(lck) ReleaseSRWLockShared ( &lck )
+ #define XMP_BasicRWLock_ReleaseFromWrite(lck) ReleaseSRWLockExclusive ( &lck )
+
+#elif UseHomeGrownLock
+
+ class XMP_HomeGrownLock;
+ typedef XMP_HomeGrownLock XMP_BasicRWLock;
+
+ #define XMP_BasicRWLock_Initialize(lck) /* Do nothing. */
+ #define XMP_BasicRWLock_Terminate(lck) /* Do nothing. */
+ #define XMP_BasicRWLock_AcquireForRead(lck) lck.AcquireForRead()
+ #define XMP_BasicRWLock_AcquireForWrite(lck) lck.AcquireForWrite()
+ #define XMP_BasicRWLock_ReleaseFromRead(lck) lck.ReleaseFromRead()
+ #define XMP_BasicRWLock_ReleaseFromWrite(lck) lck.ReleaseFromWrite()
+
+ #if XMP_MacBuild | XMP_UNIXBuild
+
+ #include <pthread.h>
+
+ typedef pthread_cond_t XMP_BasicQueue;
+
+ #elif XMP_WinBuild
+
+ #include <Windows.h>
+ #ifndef BuildLocksForWinXP
+ #define BuildLocksForWinXP 1
+ #endif
+
+ #if ! BuildLocksForWinXP
+ typedef CONDITION_VARIABLE XMP_BasicQueue; // ! Requires Vista or newer.
+ #else
+ class XMP_WinXP_HGQueue {
+ public:
+ XMP_WinXP_HGQueue();
+ ~XMP_WinXP_HGQueue();
+ void Wait ( XMP_BasicMutex & queueMutex );
+ void ReleaseOne();
+ void ReleaseAll();
+ private:
+ HANDLE queueEvent;
+ volatile XMP_Uns32 waitCount; // ! Does not need to be XMP_AtomicCounter.
+ volatile bool releaseAll;
+ };
+ typedef XMP_WinXP_HGQueue XMP_BasicQueue;
+ #endif
+
+ #endif
+
+ class XMP_HomeGrownLock {
+ public:
+ XMP_HomeGrownLock();
+ ~XMP_HomeGrownLock();
+ void AcquireForRead();
+ void AcquireForWrite();
+ void ReleaseFromRead();
+ void ReleaseFromWrite();
+ private:
+ XMP_BasicMutex queueMutex; // Used to protect queueing operations.
+ XMP_BasicQueue readerQueue, writerQueue;
+ volatile XMP_Uns32 lockCount, readersWaiting, writersWaiting; // ! Does not need to be XMP_AtomicCounter.
+ volatile bool beingWritten;
+ };
+
+#else
+
+ #error "No locking mechanism chosen"
+
+#endif
+
+class XMP_ReadWriteLock { // For the lock objects, use XMP_AutoLock to do the locking.
+public:
+ XMP_ReadWriteLock();
+ ~XMP_ReadWriteLock();
+ void Acquire ( bool forWriting );
+ void Release();
+private:
+ XMP_BasicRWLock lock;
+ #if XMP_DebugBuild && HaveAtomicIncrDecr
+ volatile XMP_AtomicCounter lockCount; // ! Only for debug checks, must be XMP_AtomicCounter.
+ #endif
+ volatile bool beingWritten;
+};
+
+#define kXMP_ReadLock false
+#define kXMP_WriteLock true
+
+class XMP_AutoLock {
+public:
+ XMP_AutoLock ( const XMP_ReadWriteLock * _lock, bool forWriting, bool cond = true ) : lock(0)
+ {
+ if ( cond ) {
+ // The cast below is needed because the _lock parameter might come from something
+ // like "const XMPMeta &", which would make the lock itself const. But we need to
+ // modify the lock (to acquire and release) even if the owning object is const.
+ this->lock = (XMP_ReadWriteLock*)_lock;
+ this->lock->Acquire ( forWriting );
+ }
+ }
+ ~XMP_AutoLock() { this->Release(); }
+ void Release() { if ( this->lock != 0 ) this->lock->Release(); this->lock = 0; }
+private:
+ XMP_ReadWriteLock * lock;
+ XMP_AutoLock() {}; // ! Must not be used.
+};
+
+// =================================================================================================
+// Support for wrappers
+// ====================
+
+#define AnnounceStaticEntry(proc) /* Do nothing. */
+#define AnnounceObjectEntry(proc,rwMode) /* Do nothing. */
+
+#define AnnounceExit() /* Do nothing. */
+
+// -------------------------------------------------------------------------------------------------
+
+#if UseGlobalLibraryLock
+ #define AcquireLibraryLock(lck) XMP_AutoMutex libLock ( &lck )
+#else
+ #define AcquireLibraryLock(lck) /* nothing */
+#endif
+
+#define XMP_ENTER_NoLock(Proc) \
+ AnnounceStaticEntry ( Proc ); \
+ try { \
+ wResult->errMessage = 0;
+
+#define XMP_ENTER_Static(Proc) \
+ AnnounceStaticEntry ( Proc ); \
+ AcquireLibraryLock ( sLibraryLock ); \
+ try { \
+ wResult->errMessage = 0;
+
+#define XMP_ENTER_ObjRead(XMPClass,Proc) \
+ AnnounceObjectEntry ( Proc, "reader" ); \
+ AcquireLibraryLock ( sLibraryLock ); \
+ const XMPClass & thiz = *((XMPClass*)xmpObjRef); \
+ XMP_AutoLock objLock ( &thiz.lock, kXMP_ReadLock ); \
+ try { \
+ wResult->errMessage = 0;
+
+#define XMP_ENTER_ObjWrite(XMPClass,Proc) \
+ AnnounceObjectEntry ( Proc, "writer" ); \
+ AcquireLibraryLock ( sLibraryLock ); \
+ XMPClass * thiz = (XMPClass*)xmpObjRef; \
+ XMP_AutoLock objLock ( &thiz->lock, kXMP_WriteLock ); \
+ try { \
+ wResult->errMessage = 0;
+
+#define XMP_EXIT \
+ XMP_CATCH_EXCEPTIONS \
+ AnnounceExit();
+
+#define XMP_EXIT_NoThrow \
+ } catch ( ... ) { \
+ AnnounceCatch ( "no-throw catch-all" ); \
+ /* Do nothing. */ \
+ } \
+ AnnounceExit();
+
+#define XMP_CATCH_EXCEPTIONS \
+ } catch ( XMP_Error & xmpErr ) { \
+ wResult->int32Result = xmpErr.GetID(); \
+ wResult->ptrResult = (void*)"XMP"; \
+ wResult->errMessage = xmpErr.GetErrMsg(); \
+ if ( wResult->errMessage == 0 ) wResult->errMessage = ""; \
+ AnnounceCatch ( wResult->errMessage ); \
+ } catch ( std::exception & stdErr ) { \
+ wResult->int32Result = kXMPErr_StdException; \
+ wResult->errMessage = stdErr.what(); \
+ if ( wResult->errMessage == 0 ) wResult->errMessage = ""; \
+ AnnounceCatch ( wResult->errMessage ); \
+ } catch ( ... ) { \
+ wResult->int32Result = kXMPErr_UnknownException; \
+ wResult->errMessage = "Caught unknown exception"; \
+ AnnounceCatch ( wResult->errMessage ); \
+ }
+
+#if XMP_DebugBuild
+ #define RELEASE_NO_THROW /* empty */
+#else
+ #define RELEASE_NO_THROW throw()
+#endif
+
+// =================================================================================================
+// Data structure dumping utilities
+// ================================
+
+#define IsHexDigit(ch) ( (('0' <= (ch)) && ((ch) <= '9')) || (('A' <= (ch)) && ((ch) <= 'F')) )
+#define HexDigitValue(ch) ( (((ch) - '0') < 10) ? ((ch) - '0') : ((ch) - 'A' + 10) )
+
+static const char * kTenSpaces = " ";
+#define OutProcPadding(pad) { size_t padLen = (pad); \
+ for ( ; padLen >= 10; padLen -= 10 ) OutProcNChars ( kTenSpaces, 10 ); \
+ for ( ; padLen > 0; padLen -= 1 ) OutProcNChars ( " ", 1 ); }
+
+
+#define OutProcNewline() { XMP_Status status = (*outProc) ( refCon, "\n", 1 ); if ( status != 0 ) return; }
+
+#define OutProcNChars(p,n) { XMP_Status status = (*outProc) ( refCon, (p), (n) ); if ( status != 0 ) return; }
+
+#define OutProcLiteral(lit) { XMP_Status status = (*outProc) ( refCon, (lit), (XMP_StringLen)strlen(lit) ); if ( status != 0 ) return; }
+
+#define OutProcString(str) { XMP_Status status = (*outProc) ( refCon, (str).c_str(), (XMP_StringLen)(str).size() ); if ( status != 0 ) return; }
+
+#define OutProcDecInt(num) { snprintf ( buffer, sizeof(buffer), "%ld", (long)(num) ); /* AUDIT: Using sizeof for snprintf length is safe */ \
+ XMP_Status status = (*outProc) ( refCon, buffer, (XMP_StringLen)strlen(buffer) ); if ( status != 0 ) return; }
+
+#define OutProcHexInt(num) { snprintf ( buffer, sizeof(buffer), "%lX", (long)(num) ); /* AUDIT: Using sizeof for snprintf length is safe */ \
+ XMP_Status status = (*outProc) ( refCon, buffer, (XMP_StringLen)strlen(buffer) ); if ( status != 0 ) return; }
+
+#define OutProcHexByte(num) { snprintf ( buffer, sizeof(buffer), "%.2X", (num) ); /* AUDIT: Using sizeof for snprintf length is safe */ \
+ XMP_Status status = (*outProc) ( refCon, buffer, (XMP_StringLen)strlen(buffer) ); if ( status != 0 ) return; }
+
+static const char * kIndent = " ";
+#define OutProcIndent(lev) { for ( size_t i = 0; i < (lev); ++i ) OutProcNChars ( kIndent, 3 ); }
+
+void DumpClearString ( const XMP_VarString & value, XMP_TextOutputProc outProc, void * refCon );
+
+// =================================================================================================
+// Namespace Tables
+// ================
+
+typedef std::pair < XMP_VarString, XMP_VarString > XMP_StringPair;
+
+typedef std::map < XMP_VarString, XMP_VarString > XMP_StringMap;
+typedef XMP_StringMap::iterator XMP_StringMapPos;
+typedef XMP_StringMap::const_iterator XMP_cStringMapPos;
+
+class XMP_NamespaceTable {
+public:
+
+ XMP_NamespaceTable() {};
+ XMP_NamespaceTable ( const XMP_NamespaceTable & presets );
+ virtual ~XMP_NamespaceTable() {};
+
+ bool Define ( XMP_StringPtr uri, XMP_StringPtr suggPrefix,
+ XMP_StringPtr * prefixPtr, XMP_StringLen * prefixLen);
+
+ bool GetPrefix ( XMP_StringPtr uri, XMP_StringPtr * prefixPtr, XMP_StringLen * prefixLen ) const;
+ bool GetURI ( XMP_StringPtr prefix, XMP_StringPtr * uriPtr, XMP_StringLen * uriLen ) const;
+
+ void Dump ( XMP_TextOutputProc outProc, void * refCon ) const;
+
+private:
+
+ XMP_ReadWriteLock lock;
+ XMP_StringMap uriToPrefixMap, prefixToURIMap;
+
+};
+
+// =================================================================================================
+
+#endif // __XMP_LibUtils_hpp__