diff options
author | Hubert Figuière <hub@figuiere.net> | 2016-12-07 00:25:52 -0500 |
---|---|---|
committer | Hubert Figuière <hub@figuiere.net> | 2016-12-07 00:29:32 -0500 |
commit | 1696cff2133f0eab1fea45c164cb771a6e5e4526 (patch) | |
tree | d526dab08731fd8f6a4978b67df6e788979b567c /XMPFiles | |
parent | 93571bfffb172a92f602de38a615902c52da111a (diff) | |
parent | 6071af09b5e263b63e57b28ab8a78484bc65e3fe (diff) |
Merge branch 'adobe-sdk' into integration
Merge Adobe SDK CC 2016.07
Diffstat (limited to 'XMPFiles')
69 files changed, 5299 insertions, 1501 deletions
diff --git a/XMPFiles/build/CMakeLists.txt b/XMPFiles/build/CMakeLists.txt index 76bdae0..2bd632d 100644 --- a/XMPFiles/build/CMakeLists.txt +++ b/XMPFiles/build/CMakeLists.txt @@ -9,7 +9,7 @@ # ============================================================================== # define minimum cmake version -cmake_minimum_required(VERSION 2.8.6) +cmake_minimum_required(VERSION 3.5.2) # Enable folder grouping of projects in IDEs set_property(GLOBAL PROPERTY USE_FOLDERS ON) @@ -41,6 +41,7 @@ include(${CMAKE_CURRENT_SOURCE_DIR}/${XMP_THIS_PROJECT_RELATIVEPATH}/build/XMP_C set(TP_ZUID_PATH "${XMPROOT_DIR}/third-party/zuid/interfaces") +set(TP_EXPAT_PATH "${PROJECT_ROOT}/../../third-party/expat/lib/") set(LIB_ADOBEXMP XMPCore) # ============================================================================== diff --git a/XMPFiles/build/CMakeListsCommon.txt b/XMPFiles/build/CMakeListsCommon.txt index 2f116d0..234d0d0 100644 --- a/XMPFiles/build/CMakeListsCommon.txt +++ b/XMPFiles/build/CMakeListsCommon.txt @@ -22,7 +22,6 @@ source_group("Header Files\\Internal Headers\\Common Code" FILES ${INTERNAL_HEAD file (GLOB INTERNAL_HEADER_FILEHANDLERS ${SOURCE_ROOT}/FileHandlers/*.hpp) list (REMOVE_ITEM INTERNAL_HEADER_FILEHANDLERS ${SOURCE_ROOT}/FileHandlers/AIFF_Handler.hpp - ${SOURCE_ROOT}/FileHandlers/GIF_Handler.hpp ) source_group("Header Files\\Internal Headers\\File Handlers" FILES ${INTERNAL_HEADER_FILEHANDLERS}) @@ -36,9 +35,7 @@ file (GLOB INTERNAL_HEADER_FORMATSUPPORT_WAVE ${SOURCE_ROOT}/FormatSupport/WAVE/ source_group("Header Files\\Internal Headers\\Format Support\\WAVE" FILES ${INTERNAL_HEADER_FORMATSUPPORT_WAVE}) file (GLOB INTERNAL_HEADER_FORMATSUPPORT ${SOURCE_ROOT}/FormatSupport/*.hpp) -list (REMOVE_ITEM INTERNAL_HEADER_FORMATSUPPORT - ${SOURCE_ROOT}/FormatSupport/GIF_Support.hpp - ) + list (REMOVE_ITEM INTERNAL_HEADER_FORMATSUPPORT # ${SOURCE_ROOT}/source/MD5.cpp ${XMPROOT_DIR}/source/UnicodeConversions.cpp @@ -74,6 +71,14 @@ list (REMOVE_ITEM HEADERFILES_THIRDPARTY_ZLIB ) source_group("Header Files\\ThirdParty\\zlib" FILES ${HEADERFILES_THIRDPARTY_ZLIB}) +list (APPEND FILES_THIRDPARTY_EXPAT + ${TP_EXPAT_PATH}/xmlparse.c + ${TP_EXPAT_PATH}/xmlrole.c + ${TP_EXPAT_PATH}/xmltok.c + ) +list (APPEND FILES_THIRDPARTY_EXPAT ${RESOURCE_ROOT}/../../XMPCore/resource/${XMP_PLATFORM_SHORT}/expat_config.h) +source_group("Source Files\\ThirdParty\\expat" FILES ${FILES_THIRDPARTY_EXPAT}) + list (APPEND HEADERFILES ${XMPROOT_DIR}/source/Host_IO.hpp ${XMPROOT_DIR}/source/XIO.hpp @@ -120,9 +125,7 @@ file (GLOB SOURCEFILES_FILEHANDLERS ${SOURCE_ROOT}/FileHandlers/*.cpp) list (APPEND SOURCEFILES_FILEHANDLERS ${SOURCE_ROOT}/FileHandlers/AIFF_Handler.hpp ) -list (REMOVE_ITEM SOURCEFILES_FILEHANDLERS - ${SOURCE_ROOT}/FileHandlers/GIF_Handler.cpp - ) + source_group("Source Files\\File Handlers" FILES ${SOURCEFILES_FILEHANDLERS}) file (GLOB SOURCEFILES_FORMATSUPPORT_AIFF ${SOURCE_ROOT}/FormatSupport/AIFF/*.cpp) @@ -135,9 +138,7 @@ file (GLOB SOURCEFILES_FORMATSUPPORT_WAVE ${SOURCE_ROOT}/FormatSupport/WAVE/*.cp source_group("Source Files\\Format Support\\WAVE" FILES ${SOURCEFILES_FORMATSUPPORT_WAVE}) file (GLOB SOURCEFILES_FORMATSUPPORT ${SOURCE_ROOT}/FormatSupport/*.cpp) -list (REMOVE_ITEM SOURCEFILES_FORMATSUPPORT - ${SOURCE_ROOT}/FormatSupport/GIF_Support.cpp - ) + source_group("Source Files\\Format Support" FILES ${SOURCEFILES_FORMATSUPPORT}) if (NOT APPLE_IOS) @@ -160,6 +161,13 @@ if (NOT APPLE_IOS) source_group("Source Files\\PluginHandler" FILES ${SOURCEFILES_PLUGINHANDLER}) endif() +#Core & Common public source files +file (GLOB PUBLIC_XMPCOMMON_CLIENTGLUE_FILES ${XMPROOT_DIR}/public/include/XMPCommon/source/*.*) +source_group("Source Files\\Public\\XMPCommon" FILES ${PUBLIC_XMPCOMMON_CLIENTGLUE_FILES}) + +file (GLOB PUBLIC_XMPCORE_CLIENTGLUE_FILES ${XMPROOT_DIR}/public/include/XMPCore/source/*.*) +source_group("Source Files\\Public\\XMPCore" FILES ${PUBLIC_XMPCORE_CLIENTGLUE_FILES}) + list (APPEND HEADERFILES_THIRDPARTY_ZLIB ${XMPROOT_DIR}/third-party/zlib/adler32.c ${XMPROOT_DIR}/third-party/zlib/compress.c @@ -197,13 +205,17 @@ list(APPEND SOURCE_FILES ${SOURCEFILES_FORMATSUPPORT_WAVE} ${SOURCEFILES_FORMATSUPPORT} ${SOURCEFILES_PLUGINHANDLER} + ${PUBLIC_XMPCOMMON_CLIENTGLUE_FILES} + ${PUBLIC_XMPCORE_CLIENTGLUE_FILES} ${HEADERFILES_THIRDPARTY_ZLIB} + ${FILES_THIRDPARTY_EXPAT} ) # include directories include_directories(${XMPROOT_DIR}) include_directories(${XMPROOT_DIR}/public/include) -include_directories(${XMPROOT_DIR}/third-party/expat/zlib) +include_directories(${TP_EXPAT_PATH}) +include_directories(${XMPROOT_DIR}/third-party/expat/public/lib) include_directories(${XMPROOT_DIR}/XMPFilesPlugins/api/source) include_directories(${RESOURCE_ROOT}/${XMP_PLATFORM_SHORT}) @@ -214,25 +226,47 @@ link_directories(${OUTPUT_DIR}) # Define what to do, lib, exe, etc if (UNIX AND APPLE AND NOT ${XMP_BUILD_STATIC}) # preprocess Info.plist - add_custom_target(${TARGET_NAME}InfoPlist - COMMAND mkdir -p ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR} - COMMAND if [ $(CONFIGURATION) != Debug ]; then - ${GCCTOOL} -E -P -x c ${RESOURCE_ROOT}/${XMP_PLATFORM_SHORT}/${TARGET_NAME}.plist - -F${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/ - -DPRODUCT_NAME=${TARGET_NAME} -DMAC_ENV=1 -DNDEBUG=1 - -include ${RESOURCE_ROOT}/${XMP_PLATFORM_SHORT}/${TARGET_NAME}PList.h - -o ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/Info.plist - \; else - ${GCCTOOL} -E -P -x c ${RESOURCE_ROOT}/${XMP_PLATFORM_SHORT}/${TARGET_NAME}.plist - -F${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/ - -DPRODUCT_NAME=${TARGET_NAME} -DMAC_ENV=1 -DDEBUG=1 - -include ${RESOURCE_ROOT}/${XMP_PLATFORM_SHORT}/${TARGET_NAME}PList.h - -o ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/Info.plist - \; fi - COMMAND rm -f ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/../CMakeFiles/${TARGET_NAME}.dir/Info.plist - COMMAND cp ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/Info.plist ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/../CMakeFiles/${TARGET_NAME}.dir/Info.plist - COMMENT "Preprocessing Info-plist" - ) + if(NOT APPLE_IOS) + add_custom_target(${TARGET_NAME}InfoPlist + COMMAND mkdir -p ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR} + COMMAND if [ $(CONFIGURATION) != Debug ]; then + ${GCCTOOL} -E -P -x c ${RESOURCE_ROOT}/${XMP_PLATFORM_SHORT}/${TARGET_NAME}.plist + -F${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/ + -DPRODUCT_NAME=${TARGET_NAME} -DMAC_ENV=1 -DNDEBUG=1 + -include ${RESOURCE_ROOT}/${XMP_PLATFORM_SHORT}/${TARGET_NAME}PList.h + -o ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/Info.plist + \; else + ${GCCTOOL} -E -P -x c ${RESOURCE_ROOT}/${XMP_PLATFORM_SHORT}/${TARGET_NAME}.plist + -F${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/ + -DPRODUCT_NAME=${TARGET_NAME} -DMAC_ENV=1 -DDEBUG=1 + -include ${RESOURCE_ROOT}/${XMP_PLATFORM_SHORT}/${TARGET_NAME}PList.h + -o ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/Info.plist + \; fi + COMMAND rm -f ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/../CMakeFiles/${TARGET_NAME}.dir/Info.plist + COMMAND cp ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/Info.plist ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/../CMakeFiles/${TARGET_NAME}.dir/Info.plist + COMMENT "Preprocessing Info-plist" + ) + else() + add_custom_target(${TARGET_NAME}InfoPlist + COMMAND mkdir -p ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR} + COMMAND if [ $(CONFIGURATION) != Debug ]; then + ${GCCTOOL} -E -P -x c ${RESOURCE_ROOT}/ios/${TARGET_NAME}.plist + -F${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/ + -DPRODUCT_NAME=${TARGET_NAME} -DIOS_ENV=1 -DNDEBUG=1 + -include ${RESOURCE_ROOT}/ios/${TARGET_NAME}PList.h + -o ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/Info.plist + \; else + ${GCCTOOL} -E -P -x c ${RESOURCE_ROOT}/ios/${TARGET_NAME}.plist + -F${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/ + -DPRODUCT_NAME=${TARGET_NAME} -DIOS_ENV=1 -DDEBUG=1 + -include ${RESOURCE_ROOT}/ios/${TARGET_NAME}PList.h + -o ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/Info.plist + \; fi + COMMAND rm -f ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/../CMakeFiles/${TARGET_NAME}.dir/Info.plist + COMMAND cp ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/Info.plist ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/../CMakeFiles/${TARGET_NAME}.dir/Info.plist + COMMENT "Preprocessing Info-plist" + ) + endif() set(DEPENDENCY_LIST "ALL:${TARGET_NAME}InfoPlist" "DLL:XMPCore") else () set(DEPENDENCY_LIST "DLL:XMPCore") @@ -258,13 +292,17 @@ else(WIN32) endif() endif() -set(FRAMEWORK_LIST "Mac:CoreFoundation" "Mac:CoreServices" "Mac:${LIB_ADOBEXMP}" "Mac:${XMP_PLATFORM_LINK}") +set(FRAMEWORK_LIST "ALL:CoreFoundation" "Mac:CoreServices" "ALL:${LIB_ADOBEXMP}" "ALL:${XMP_PLATFORM_LINK}") AddMacFramework(${TARGET_NAME} FRAMEWORK_LIST) if(UNIX) if (NOT APPLE) SetPlatformLinkFlags(${TARGET_NAME} "-Xlinker --version-script -Xlinker \"${RESOURCE_ROOT}/${XMP_PLATFORM_SHORT}/${TARGET_NAME}.exp\"" "") else() + if(APPLE_IOS AND NOT XMP_BUILD_STATIC) + set_target_properties(${TARGET_NAME} PROPERTIES XCODE_ATTRIBUTE_DYLIB_INSTALL_NAME_BASE "@rpath") + set_target_properties(${TARGET_NAME} PROPERTIES XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "com.adobe.Adobe${TARGET_NAME}") + endif() set_target_properties(${TARGET_NAME} PROPERTIES BUILD_WITH_INSTALL_RPATH ON INSTALL_NAME_DIR "@executable_path/../Frameworks") SetPlatformLinkFlags(${TARGET_NAME} "-exported_symbols_list \"${RESOURCE_ROOT}/${XMP_PLATFORM_SHORT}/${TARGET_NAME}.exp\"" "${XMPFILES_LIB}") endif() diff --git a/XMPFiles/resource/ios/expat_config.h b/XMPFiles/resource/ios/expat_config.h new file mode 100644 index 0000000..a21de6e --- /dev/null +++ b/XMPFiles/resource/ios/expat_config.h @@ -0,0 +1,107 @@ +/* expat_config.h. Generated by configure. */ +/* expat_config.h.in. Generated from configure.in by autoheader. */ + +/* *** Tweaked by hand for 32 bit Xcode builds on PowerPC and x86 */ + +#if __BIG_ENDIAN__ + + /* 1234 = LIL_ENDIAN, 4321 = BIGENDIAN */ + #define BYTEORDER 4321 + + /* whether byteorder is bigendian */ + #define WORDS_BIGENDIAN 1 + +#else + + /* 1234 = LIL_ENDIAN, 4321 = BIGENDIAN */ + #define BYTEORDER 1234 + + /* whether byteorder is bigendian */ + /* #define WORDS_BIGENDIAN 1 */ + +#endif + +/* Define to 1 if you have the `bcopy' function. */ +#define HAVE_BCOPY 1 + +/* Define to 1 if you have the <check.h> header file. */ +/* #undef HAVE_CHECK_H */ + +/* Define to 1 if you have the <dlfcn.h> header file. */ +#define HAVE_DLFCN_H 1 + +/* Define to 1 if you have the <fcntl.h> header file. */ +#define HAVE_FCNTL_H 1 + +/* Define to 1 if you have the `getpagesize' function. */ +#define HAVE_GETPAGESIZE 1 + +/* Define to 1 if you have the <inttypes.h> header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the `memmove' function. */ +#define HAVE_MEMMOVE 1 + +/* Define to 1 if you have the <memory.h> header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have a working `mmap' system call. */ +#define HAVE_MMAP 1 + +/* Define to 1 if you have the <stdint.h> header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the <stdlib.h> header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the <strings.h> header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the <string.h> header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the <sys/stat.h> header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the <sys/types.h> header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the <unistd.h> header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "expat-bugs@mail.libexpat.org" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "expat" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "expat 1.95.8" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "expat" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "1.95.8" + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define to specify how much context to retain around the current parse + point. */ +#define XML_CONTEXT_BYTES 1024 + +/* Define to make parameter entity parsing functionality available. */ +/* #define XML_DTD 1 */ + +/* Define to make XML Namespaces functionality available. */ +#define XML_NS 1 + +/* Define to empty if `const' does not conform to ANSI C. */ +/* #undef const */ + +/* Define to `long' if <sys/types.h> does not define. */ +/* #undef off_t */ + +/* Define to `unsigned' if <sys/types.h> does not define. */ +/* #undef size_t */ diff --git a/XMPFiles/resource/linux/expat_config.h b/XMPFiles/resource/linux/expat_config.h new file mode 100644 index 0000000..7d147e7 --- /dev/null +++ b/XMPFiles/resource/linux/expat_config.h @@ -0,0 +1,120 @@ +/* expat_config.h. Generated by configure. */ +/* expat_config.h.in. Generated from configure.in by autoheader. */ + +/* *** Tweaked by hand for generic UNIX builds **** */ + +#ifdef WORDS_BIGENDIAN + #error "WORDS_BIGENDIAN must be initially undefined" +#endif + +#if TargetOS == i80386linux + /* #undef WORDS_BIGENDIAN */ + #define HAVE_MMAP 1 + #define HAVE_STDINT_H 1 + /* #undef const */ +#elif TargetOS == sparcsolaris + #define WORDS_BIGENDIAN 1 + #define HAVE_MMAP 1 + /* #undef HAVE_STDINT_H */ + /* #undef const */ +#elif TargetOS == rs60000aix + #define WORDS_BIGENDIAN 1 + /* #undef HAVE_MMAP */ + /* #undef HAVE_STDINT_H */ + /* #undef const */ +#elif TargetOS == hppahpux + #define WORDS_BIGENDIAN 1 + /* #undef HAVE_MMAP */ + /* #undef HAVE_STDINT_H */ + #define const +#elif TargetOS == ia64hpux + #define WORDS_BIGENDIAN 1 + /* #undef HAVE_MMAP */ + /* #undef HAVE_STDINT_H */ + #define const +#else + #error "Unknown target OS" +#endif + +/* 1234 = LIL_ENDIAN, 4321 = BIGENDIAN */ +#if WORDS_BIGENDIAN + #define BYTEORDER 4321 +#else + #define BYTEORDER 1234 +#endif + +/* Define to 1 if you have the `bcopy' function. */ +#define HAVE_BCOPY 1 + +/* Define to 1 if you have the <check.h> header file. */ +/* #undef HAVE_CHECK_H */ + +/* Define to 1 if you have the <dlfcn.h> header file. */ +#define HAVE_DLFCN_H 1 + +/* Define to 1 if you have the <fcntl.h> header file. */ +#define HAVE_FCNTL_H 1 + +/* Define to 1 if you have the `getpagesize' function. */ +#define HAVE_GETPAGESIZE 1 + +/* Define to 1 if you have the <inttypes.h> header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the `memmove' function. */ +#define HAVE_MEMMOVE 1 + +/* Define to 1 if you have the <memory.h> header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the <stdlib.h> header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the <strings.h> header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the <string.h> header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the <sys/stat.h> header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the <sys/types.h> header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the <unistd.h> header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "expat-bugs@mail.libexpat.org" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "expat" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "expat 1.95.8" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "expat" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "1.95.8" + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define to specify how much context to retain around the current parse + point. */ +#define XML_CONTEXT_BYTES 1024 + +/* Define to make parameter entity parsing functionality available. */ +/* #define XML_DTD 1 */ + +/* Define to make XML Namespaces functionality available. */ +#define XML_NS 1 + +/* Define to `long' if <sys/types.h> does not define. */ +/* #undef off_t */ + +/* Define to `unsigned' if <sys/types.h> does not define. */ +/* #undef size_t */ diff --git a/XMPFiles/resource/mac/expat_config.h b/XMPFiles/resource/mac/expat_config.h new file mode 100644 index 0000000..a21de6e --- /dev/null +++ b/XMPFiles/resource/mac/expat_config.h @@ -0,0 +1,107 @@ +/* expat_config.h. Generated by configure. */ +/* expat_config.h.in. Generated from configure.in by autoheader. */ + +/* *** Tweaked by hand for 32 bit Xcode builds on PowerPC and x86 */ + +#if __BIG_ENDIAN__ + + /* 1234 = LIL_ENDIAN, 4321 = BIGENDIAN */ + #define BYTEORDER 4321 + + /* whether byteorder is bigendian */ + #define WORDS_BIGENDIAN 1 + +#else + + /* 1234 = LIL_ENDIAN, 4321 = BIGENDIAN */ + #define BYTEORDER 1234 + + /* whether byteorder is bigendian */ + /* #define WORDS_BIGENDIAN 1 */ + +#endif + +/* Define to 1 if you have the `bcopy' function. */ +#define HAVE_BCOPY 1 + +/* Define to 1 if you have the <check.h> header file. */ +/* #undef HAVE_CHECK_H */ + +/* Define to 1 if you have the <dlfcn.h> header file. */ +#define HAVE_DLFCN_H 1 + +/* Define to 1 if you have the <fcntl.h> header file. */ +#define HAVE_FCNTL_H 1 + +/* Define to 1 if you have the `getpagesize' function. */ +#define HAVE_GETPAGESIZE 1 + +/* Define to 1 if you have the <inttypes.h> header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the `memmove' function. */ +#define HAVE_MEMMOVE 1 + +/* Define to 1 if you have the <memory.h> header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have a working `mmap' system call. */ +#define HAVE_MMAP 1 + +/* Define to 1 if you have the <stdint.h> header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the <stdlib.h> header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the <strings.h> header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the <string.h> header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the <sys/stat.h> header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the <sys/types.h> header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the <unistd.h> header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "expat-bugs@mail.libexpat.org" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "expat" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "expat 1.95.8" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "expat" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "1.95.8" + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define to specify how much context to retain around the current parse + point. */ +#define XML_CONTEXT_BYTES 1024 + +/* Define to make parameter entity parsing functionality available. */ +/* #define XML_DTD 1 */ + +/* Define to make XML Namespaces functionality available. */ +#define XML_NS 1 + +/* Define to empty if `const' does not conform to ANSI C. */ +/* #undef const */ + +/* Define to `long' if <sys/types.h> does not define. */ +/* #undef off_t */ + +/* Define to `unsigned' if <sys/types.h> does not define. */ +/* #undef size_t */ diff --git a/XMPFiles/resource/win/expat_config.h b/XMPFiles/resource/win/expat_config.h new file mode 100644 index 0000000..e6d2f63 --- /dev/null +++ b/XMPFiles/resource/win/expat_config.h @@ -0,0 +1,99 @@ +#if WIN_ENV + #pragma warning ( disable : 4244 ) // possible loss of data (temporary for 64 bit builds) +#endif + +/* expat_config.h. Generated by configure. */ +/* expat_config.h.in. Generated from configure.in by autoheader. */ + +/* *** Tweaked by hand for 32 bit Windows builds */ + +/* 1234 = LIL_ENDIAN, 4321 = BIGENDIAN */ +#define BYTEORDER 1234 + +/* Define to 1 if you have the `bcopy' function. */ +/* #define HAVE_BCOPY 1 */ + +/* Define to 1 if you have the <check.h> header file. */ +/* #undef HAVE_CHECK_H */ + +/* Define to 1 if you have the <dlfcn.h> header file. */ +/* #define HAVE_DLFCN_H 1 */ + +/* Define to 1 if you have the <fcntl.h> header file. */ +/* #define HAVE_FCNTL_H 1 */ + +/* Define to 1 if you have the `getpagesize' function. */ +/* #define HAVE_GETPAGESIZE 1 */ + +/* Define to 1 if you have the <inttypes.h> header file. */ +/* #define HAVE_INTTYPES_H 1 */ + +/* Define to 1 if you have the `memmove' function. */ +#define HAVE_MEMMOVE 1 + +/* Define to 1 if you have the <memory.h> header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have a working `mmap' system call. */ +/* #define HAVE_MMAP 1 */ + +/* Define to 1 if you have the <stdint.h> header file. */ +/* #define HAVE_STDINT_H 1 */ + +/* Define to 1 if you have the <stdlib.h> header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the <strings.h> header file. */ +/* #define HAVE_STRINGS_H 1 */ + +/* Define to 1 if you have the <string.h> header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the <sys/stat.h> header file. */ +/* #define HAVE_SYS_STAT_H 1 */ + +/* Define to 1 if you have the <sys/types.h> header file. */ +/* #define HAVE_SYS_TYPES_H 1 */ + +/* Define to 1 if you have the <unistd.h> header file. */ +/* #define HAVE_UNISTD_H 1 */ + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "expat-bugs@mail.libexpat.org" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "expat" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "expat 1.95.8" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "expat" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "1.95.8" + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* whether byteorder is bigendian */ +/* #define WORDS_BIGENDIAN 1 */ + +/* Define to specify how much context to retain around the current parse + point. */ +#define XML_CONTEXT_BYTES 1024 + +/* Define to make parameter entity parsing functionality available. */ +/* #define XML_DTD 1 */ + +/* Define to make XML Namespaces functionality available. */ +#define XML_NS 1 + +/* Define to empty if `const' does not conform to ANSI C. */ +/* #undef const */ + +/* Define to `long' if <sys/types.h> does not define. */ +/* #undef off_t */ + +/* Define to `unsigned' if <sys/types.h> does not define. */ +/* #undef size_t */ diff --git a/XMPFiles/source/FileHandlers/GIF_Handler.cpp b/XMPFiles/source/FileHandlers/GIF_Handler.cpp index 39090ea..ded9954 100644 --- a/XMPFiles/source/FileHandlers/GIF_Handler.cpp +++ b/XMPFiles/source/FileHandlers/GIF_Handler.cpp @@ -1,20 +1,23 @@ // ================================================================================================= // 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 // of the Adobe license agreement accompanying it. +// +// This file includes implementation of GIF file metadata, according to GIF89a Specification. +// https://www.w3.org/Graphics/GIF/spec-gif89a.txt +// The Graphics Interchange Format(c) is the Copyright property of CompuServe Incorporated. +// GIF(sm) is a Service Mark property of CompuServe Incorporated. +// All Rights Reserved . http://www.w3.org/Consortium/Legal // -// Derived from PNG_Handler.cpp by Ian Jacobi // ================================================================================================= -#include "GIF_Handler.hpp" +#include "public/include/XMP_Environment.h" // ! Must be the first #include! +#include "XMPFiles/source/FileHandlers/GIF_Handler.hpp" #include "source/XIO.hpp" -#include "XMPFiles/source/FormatSupport/GIF_Support.hpp" - -using namespace std; // ================================================================================================= /// \file GIF_Handler.hpp @@ -34,24 +37,32 @@ XMPFileHandler * GIF_MetaHandlerCTor ( XMPFiles * parent ) } // GIF_MetaHandlerCTor +#define GIF_89_Header_LEN 6 +#define GIF_89_Header_DATA "\x47\x49\x46\x38\x39\x61" // must be GIF89a, nothing else as XMP is supported only in 89a version + +#define APP_ID_LEN 11 +#define XMP_APP_ID_DATA "\x58\x4D\x50\x20\x44\x61\x74\x61\x58\x4D\x50" + +#define MAGIC_TRAILER_LEN 258 + // ================================================================================================= // GIF_CheckFormat // =============== bool GIF_CheckFormat ( XMP_FileFormat format, XMP_StringPtr filePath, - XMP_IO* fileRef, + XMP_IO* fileRef, XMPFiles * parent ) { IgnoreParam(format); IgnoreParam(fileRef); IgnoreParam(parent); XMP_Assert ( format == kXMP_GIFFile ); - IOBuffer ioBuf; + if ( fileRef->Length() < GIF_89_Header_LEN ) return false; + XMP_Uns8 buffer[ GIF_89_Header_LEN ]; fileRef->Rewind(); - if ( ! CheckFileSpace ( fileRef, &ioBuf, GIF_SIGNATURE_LEN ) ) return false; // We need at least 3, so the buffer is not filled. - - if ( ! CheckBytes ( ioBuf.ptr, GIF_SIGNATURE_DATA, GIF_SIGNATURE_LEN ) ) return false; + fileRef->Read( buffer, GIF_89_Header_LEN ); + if ( !CheckBytes( buffer, GIF_89_Header_DATA, GIF_89_Header_LEN ) ) return false; return true; @@ -61,12 +72,12 @@ bool GIF_CheckFormat ( XMP_FileFormat format, // GIF_MetaHandler::GIF_MetaHandler // ================================== -GIF_MetaHandler::GIF_MetaHandler ( XMPFiles * _parent ) +GIF_MetaHandler::GIF_MetaHandler( XMPFiles * _parent ) : XMPPacketOffset( 0 ), XMPPacketLength( 0 ), trailerOffset( 0 ) { this->parent = _parent; this->handlerFlags = kGIF_HandlerFlags; // It MUST be UTF-8. - this->stdCharForm = kXMP_Char8Bit; + this->stdCharForm = kXMP_Char8Bit; } @@ -84,52 +95,31 @@ GIF_MetaHandler::~GIF_MetaHandler() void GIF_MetaHandler::CacheFileData() { - this->containsXMP = false; - XMP_IO* fileRef ( this->parent->ioRef ); - if ( fileRef == 0) return; - - // We try to navigate through the blocks to find the XMP block. - GIF_Support::BlockState blockState; - long numBlocks = GIF_Support::OpenGIF ( fileRef, blockState ); - if ( numBlocks == 0 ) return; + XMP_IO * fileRef = this->parent->ioRef; - if (blockState.xmpLen != 0) + // Try to navigate through the blocks to find the XMP block. + if ( this->ParseGIFBlocks( fileRef ) ) { - // XMP present + // XMP packet present + this->xmpPacket.assign( XMPPacketLength, ' ' ); - this->xmpPacket.reserve(blockState.xmpLen); - this->xmpPacket.assign(blockState.xmpLen, ' '); + // 13 bytes for the block size and 2 bytes for Extension ID and Label + this->SeekFile( fileRef, XMPPacketOffset, kXMP_SeekFromStart ); + fileRef->ReadAll( ( void* )this->xmpPacket.data(), XMPPacketLength ); - if (GIF_Support::ReadBuffer ( fileRef, blockState.xmpPos, blockState.xmpLen, const_cast<char *>(this->xmpPacket.data()) )) - { - this->packetInfo.offset = blockState.xmpPos; - this->packetInfo.length = blockState.xmpLen; - this->containsXMP = true; - } - } - else - { - // no XMP + this->packetInfo.offset = XMPPacketOffset; + this->packetInfo.length = XMPPacketLength; + this->containsXMP = true; } + // else no XMP } // GIF_MetaHandler::CacheFileData // ================================================================================================= -// GIF_MetaHandler::ProcessTNail -// ============================== - -void GIF_MetaHandler::ProcessTNail() -{ - - XMP_Throw ( "GIF_MetaHandler::ProcessTNail isn't implemented yet", kXMPErr_Unimplemented ); - -} // GIF_MetaHandler::ProcessTNail - -// ================================================================================================= // GIF_MetaHandler::ProcessXMP -// ============================ +// =========================== // // Process the raw XMP and legacy metadata that was previously cached. @@ -140,10 +130,10 @@ void GIF_MetaHandler::ProcessXMP() // Process the XMP packet. if ( ! this->xmpPacket.empty() ) { - + XMP_Assert ( this->containsXMP ); XMP_StringPtr packetStr = this->xmpPacket.c_str(); - XMP_StringLen packetLen = this->xmpPacket.size(); + XMP_StringLen packetLen = (XMP_StringLen) this->xmpPacket.size(); this->xmpObj.ParseFromBuffer ( packetStr, packetLen ); @@ -154,111 +144,280 @@ void GIF_MetaHandler::ProcessXMP() } // GIF_MetaHandler::ProcessXMP // ================================================================================================= -// GIF_MetaHandler::UpdateFile -// ============================ +// GIF_MetaHandler::ParseGIFBlocks +// =========================== -void GIF_MetaHandler::UpdateFile ( bool doSafeUpdate ) +bool GIF_MetaHandler::ParseGIFBlocks( XMP_IO* fileRef ) { - bool updated = false; - - if ( ! this->needsUpdate ) return; - if ( doSafeUpdate ) XMP_Throw ( "GIF_MetaHandler::UpdateFile: Safe update not supported", kXMPErr_Unavailable ); - - XMP_StringPtr packetStr = xmpPacket.c_str(); - XMP_StringLen packetLen = xmpPacket.size(); - if ( packetLen == 0 ) return; + fileRef->Rewind(); - XMP_IO* fileRef(this->parent->ioRef); - if ( fileRef == 0 ) return; + // Checking for GIF header + XMP_Uns8 buffer[ GIF_89_Header_LEN ]; - GIF_Support::BlockState blockState; - long numBlocks = GIF_Support::OpenGIF ( fileRef, blockState ); - if ( numBlocks == 0 ) return; + fileRef->Read( buffer, GIF_89_Header_LEN ); + XMP_Enforce( memcmp( buffer, GIF_89_Header_DATA, GIF_89_Header_LEN ) == 0 ); - // write/update block(s) - if (blockState.xmpLen == 0) + bool IsXMPExists = false; + bool IsTrailerExists = false; + + ReadLogicalScreenDesc( fileRef ); + + // Parsing rest of the blocks + while ( fileRef->Offset() != fileRef->Length() ) { - // no current chunk -> inject - updated = SafeWriteFile(); + XMP_Uns8 blockType; + + // Read the block type byte + fileRef->Read( &blockType, 1 ); + + if ( blockType == kXMP_block_ImageDesc ) + { + + // ImageDesc is a special case, So read data just like its structure. + long tableSize = 0; + XMP_Uns8 fields; + // Reading Dimesnions of image as + // 2 bytes = Image Left Position + // + 2 bytes = Image Right Position + // + 2 bytes = Image Width + // + 2 bytes = Image Height + // = 8 bytes + this->SeekFile( fileRef, 8, kXMP_SeekFromCurrent ); + + // Reading one byte for Packed Fields + fileRef->Read( &fields, 1 ); + + // Getting Local Table Size and skipping table size + if ( fields & 0x80 ) + { + tableSize = ( 1 << ( ( fields & 0x07 ) + 1 ) ) * 3; + this->SeekFile( fileRef, tableSize, kXMP_SeekFromCurrent ); + } + + // 1 byte LZW Minimum code size + this->SeekFile( fileRef, 1, kXMP_SeekFromCurrent ); + + XMP_Uns8 subBlockSize; + // 1 byte compressed sub-block size + fileRef->Read( &subBlockSize, 1 ); + + while ( subBlockSize != 0x00 ) + { + // Skipping compressed data sub-block + this->SeekFile( fileRef, subBlockSize, kXMP_SeekFromCurrent ); + + // 1 byte compressed sub-block size + fileRef->Read( &subBlockSize, 1 ); + } + + } + else if ( blockType == kXMP_block_Extension ) + { + XMP_Uns8 extensionLbl; + XMP_Uns32 blockSize = 0; + XMP_Uns64 blockOffset = fileRef->Offset(); + + // Extension Label + fileRef->Read( &extensionLbl, 1 ); + + // Block or Sub-Block size + fileRef->Read( &blockSize, 1 ); + + // Checking for Application Extension label and blockSize + if ( extensionLbl == 0xFF && blockSize == APP_ID_LEN ) + { + XMP_Uns8 idData[ APP_ID_LEN ]; + fileRef->Read( idData, APP_ID_LEN, true ); + + // Checking For XMP ID + if ( memcmp( idData, XMP_APP_ID_DATA, APP_ID_LEN ) == 0 ) + { + XMPPacketOffset = fileRef->Offset(); + IsXMPExists = true; + } + + // Parsing sub-blocks + XMP_Uns8 subBlockSize; + fileRef->Read( &subBlockSize, 1 ); + while ( subBlockSize != 0x00 ) + { + this->SeekFile( fileRef, subBlockSize, kXMP_SeekFromCurrent ); + fileRef->Read( &subBlockSize, 1 ); + } + if ( IsXMPExists ) + XMPPacketLength = static_cast< XMP_Uns32 >( fileRef->Offset() - XMPPacketOffset - MAGIC_TRAILER_LEN ); + } + else + { + // Extension block other than Application Extension + while ( blockSize != 0x00 ) + { + // Seeking block size or sub-block size + this->SeekFile( fileRef, blockSize, kXMP_SeekFromCurrent ); + + // Block Size + fileRef->Read( &blockSize, 1 ); + } + } + } + else if ( blockType == kXMP_block_Trailer ) + { + // 1 byte is subtracted for block type + trailerOffset = fileRef->Offset() - 1; + IsTrailerExists = true; + break; + } + else + XMP_Throw( "Invaild GIF Block", kXMPErr_BadBlockFormat ); } - else if (blockState.xmpLen >= packetLen ) + + if ( !IsTrailerExists ) + XMP_Throw( "No trailer exists for GIF file", kXMPErr_BadFileFormat ); + + return IsXMPExists; + +} // GIF_MetaHandler::ParseGIFBlocks + +// ================================================================================================= +// GIF_MetaHandler::ReadLogicalScreenDesc +// =========================== + +void GIF_MetaHandler::ReadLogicalScreenDesc( XMP_IO* fileRef ) +{ + XMP_Uns8 fields; + + // 2 bytes for Screen Width + // + 2 bytes for Screen Height + // = 4 Bytes + this->SeekFile( fileRef, 4, kXMP_SeekFromCurrent ); + + // 1 byte for Packed Fields + fileRef->Read( &fields, 1 ); + + // 1 byte for Background Color Index + // + 1 byte for Pixel Aspect Ratio + // = 2 bytes + this->SeekFile( fileRef, 2, kXMP_SeekFromCurrent ); + + // Look for Global Color Table if exists + if ( fields & 0x80 ) + { + long tableSize = ( 1 << ( ( fields & 0x07 ) + 1 ) ) * 3; + this->SeekFile( fileRef, tableSize, kXMP_SeekFromCurrent ); + } + +} // GIF_MetaHandler::ReadLogicalScreenDesc + +// ================================================================================================= +// GIF_MetaHandler::SeekFile +// =========================== + +void GIF_MetaHandler::SeekFile( XMP_IO * fileRef, XMP_Int64 offset, SeekMode mode ) +{ + if ( offset > fileRef->Length() || ( mode == kXMP_SeekFromCurrent && fileRef->Offset() + offset > fileRef->Length() ) ) { - // current chunk size is sufficient -> write and update CRC (in place update) - updated = GIF_Support::WriteBuffer(fileRef, blockState.xmpPos, packetLen, packetStr ); - // GIF doesn't have a CRC like PNG. + XMP_Throw( "Out of range seek operation", kXMPErr_InternalFailure ); } - else if (blockState.xmpLen < packetLen) + else + fileRef->Seek( offset, mode ); + +} // GIF_MetaHandler::SeekFile + +// ================================================================================================= +// GIF_MetaHandler::UpdateFile +// =========================== + +void GIF_MetaHandler::UpdateFile ( bool doSafeUpdate ) +{ + XMP_Assert( !doSafeUpdate ); // This should only be called for "unsafe" updates. + + if ( ! this->needsUpdate ) return; + + XMP_IO * fileRef = this->parent->ioRef; + + XMP_StringPtr packetStr = xmpPacket.c_str(); + XMP_StringLen newPacketLength = (XMP_StringLen)xmpPacket.size(); + + if ( newPacketLength == XMPPacketLength ) { - // XMP is too large for current chunk -> expand - updated = SafeWriteFile(); + this->SeekFile( fileRef, this->packetInfo.offset, kXMP_SeekFromStart ); + fileRef->Write( this->xmpPacket.c_str(), newPacketLength ); } + else + { + XMP_IO* tempFile = fileRef->DeriveTemp(); + if ( tempFile == 0 ) XMP_Throw( "Failure creating GIF temp file", kXMPErr_InternalFailure ); - if ( ! updated )return; // If there's an error writing the chunk, bail. + this->WriteTempFile( tempFile ); + fileRef->AbsorbTemp(); + } this->needsUpdate = false; } // GIF_MetaHandler::UpdateFile // ================================================================================================= -// GIF_MetaHandler::WriteFile -// =========================== +// GIF_MetaHandler::WriteTempFile +// ============================== void GIF_MetaHandler::WriteTempFile ( XMP_IO* tempRef ) { - XMP_IO* originalRef = this->parent->ioRef; + XMP_Assert( this->needsUpdate ); - GIF_Support::BlockState blockState; - long numBlocks = GIF_Support::OpenGIF ( originalRef, blockState ); - if ( numBlocks == 0 ) return; + XMP_IO* originalRef = this->parent->ioRef; + originalRef->Rewind(); - tempRef->Truncate( 0 ); - // LFA_Write(destRef, GIF_SIGNATURE_DATA, GIF_SIGNATURE_LEN); + tempRef->Truncate ( 0 ); + + if ( XMPPacketOffset != 0 ) + { + // Copying blocks before XMP Application Block + XIO::Copy( originalRef, tempRef, XMPPacketOffset ); - GIF_Support::BlockIterator curPos = blockState.blocks.begin(); - GIF_Support::BlockIterator endPos = blockState.blocks.end(); + // Writing XMP Packet + tempRef->Write( this->xmpPacket.c_str(), (XMP_Uns32)this->xmpPacket.size() ); - long blockCount; + // Copying Rest of the file + originalRef->Seek( XMPPacketLength, kXMP_SeekFromCurrent ); + XIO::Copy( originalRef, tempRef, originalRef->Length() - originalRef->Offset() ); - for (blockCount = 0; (curPos != endPos); ++curPos, ++blockCount) + } + else { - GIF_Support::BlockData block = *curPos; + if ( trailerOffset == 0 ) + XMP_Throw( "Not able to write XMP packet in GIF file", kXMPErr_BadFileFormat ); - // discard existing XMP block - if (block.xmp) - continue; + // Copying blocks before XMP Application Block + XIO::Copy( originalRef, tempRef, trailerOffset ); - // copy any other block - GIF_Support::CopyBlock(originalRef, tempRef, block); + // Writing Extension Introducer + XIO::WriteUns8( tempRef, kXMP_block_Extension ); - // place XMP block immediately before trailer - if (blockCount == numBlocks - 2) - { - XMP_StringPtr packetStr = xmpPacket.c_str(); - XMP_StringLen packetLen = xmpPacket.size(); + // Writing Application Extension label + XIO::WriteUns8( tempRef, 0xFF ); - GIF_Support::WriteXMPBlock(tempRef, packetLen, packetStr ); - } - } + // Writing Application Extension label + XIO::WriteUns8( tempRef, APP_ID_LEN ); -} // GIF_MetaHandler::WriteFile + // Writing Application Extension label + tempRef->Write( XMP_APP_ID_DATA, APP_ID_LEN ); -// ================================================================================================= -// GIF_MetaHandler::SafeWriteFile -// =========================== + // Writing XMP Packet + tempRef->Write( this->xmpPacket.c_str(), (XMP_Uns32)this->xmpPacket.size() ); -bool GIF_MetaHandler::SafeWriteFile () -{ - bool ret = false; + // Writing Magic trailer + XMP_Uns8 magicByte = 0x01; + tempRef->Write( &magicByte, 1 ); + for ( magicByte = 0xFF; magicByte != 0x00; --magicByte ) + tempRef->Write( &magicByte, 1 ); + tempRef->Write( &magicByte, 1 ); + tempRef->Write( &magicByte, 1 ); - XMP_IO* originalFile = this->parent->ioRef; - XMP_IO* tempFile = originalFile->DeriveTemp(); - if ( tempFile == 0 ) - XMP_Throw ( "Failure creating GIF temp file", kXMPErr_InternalFailure ); + // Copying Rest of the file + XIO::Copy( originalRef, tempRef, originalRef->Length() - originalRef->Offset() ); - this->WriteTempFile( tempFile ); - originalFile->AbsorbTemp(); + } - return true; -} // GIF_MetaHandler::SafeWriteFile +} // GIF_MetaHandler::WriteTempFile // ================================================================================================= diff --git a/XMPFiles/source/FileHandlers/GIF_Handler.hpp b/XMPFiles/source/FileHandlers/GIF_Handler.hpp index 40f7046..10ef3f9 100644 --- a/XMPFiles/source/FileHandlers/GIF_Handler.hpp +++ b/XMPFiles/source/FileHandlers/GIF_Handler.hpp @@ -3,24 +3,27 @@ // ================================================================================================= // 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 // of the Adobe license agreement accompanying it. +// +// This file includes implementation of GIF file metadata, according to GIF89a Specification. +// https://www.w3.org/Graphics/GIF/spec-gif89a.txt +// The Graphics Interchange Format(c) is the Copyright property of CompuServe Incorporated. +// GIF(sm) is a Service Mark property of CompuServe Incorporated. +// All Rights Reserved . http://www.w3.org/Consortium/Legal // -// Derived from PNG_Handler.hpp by Ian Jacobi +//Derived from PNG_Handler.hpp by Ian Jacobi // ================================================================================================= -#include "public/include/XMP_Environment.h" // ! XMP_Environment.h must be the first included header. +#include "public/include/XMP_Environment.h" // ! Must be the first #include! #include "public/include/XMP_Const.h" #include "public/include/XMP_IO.hpp" #include "XMPFiles/source/XMPFiles_Impl.hpp" -#include "source/XMPFiles_IO.hpp" - -#include "XMPFiles/source/FormatSupport/GIF_Support.hpp" // ================================================================================================= /// \file GIF_Handler.hpp @@ -35,14 +38,14 @@ extern XMPFileHandler* GIF_MetaHandlerCTor ( XMPFiles* parent ); extern bool GIF_CheckFormat ( XMP_FileFormat format, - XMP_StringPtr filePath, - XMP_IO* fileRef, + XMP_StringPtr filePath, + XMP_IO* fileRef, XMPFiles* parent ); -static const XMP_OptionBits kGIF_HandlerFlags = ( kXMPFiles_CanInjectXMP | - kXMPFiles_CanExpand | - kXMPFiles_PrefersInPlace | - kXMPFiles_AllowsOnlyXMP | +static const XMP_OptionBits kGIF_HandlerFlags = ( kXMPFiles_CanInjectXMP | + kXMPFiles_CanExpand | + kXMPFiles_PrefersInPlace | + kXMPFiles_AllowsOnlyXMP | kXMPFiles_ReturnsRawPacket | kXMPFiles_NeedsReadOnlyPacket ); @@ -52,17 +55,34 @@ class GIF_MetaHandler : public XMPFileHandler public: void CacheFileData(); - void ProcessTNail(); void ProcessXMP(); - + void UpdateFile ( bool doSafeUpdate ); - void WriteTempFile ( XMP_IO* tempRef ); + void WriteTempFile ( XMP_IO* tempRef ); bool SafeWriteFile (); GIF_MetaHandler ( XMPFiles* parent ); virtual ~GIF_MetaHandler(); +private: + + enum GIFBlockType + { + kXMP_block_ImageDesc = 0x2C, + kXMP_block_Extension = 0x21, + kXMP_block_Trailer = 0x3B, + kXMP_block_Header = 0x47 + }; + + XMP_Uns64 XMPPacketOffset; + XMP_Uns32 XMPPacketLength; + XMP_Uns64 trailerOffset; + + bool ParseGIFBlocks( XMP_IO * fileRef ); + void ReadLogicalScreenDesc( XMP_IO* fileRef ); + void SeekFile( XMP_IO * fileRef, XMP_Int64 offset, SeekMode mode ); + }; // GIF_MetaHandler // ================================================================================================= diff --git a/XMPFiles/source/FileHandlers/JPEG_Handler.cpp b/XMPFiles/source/FileHandlers/JPEG_Handler.cpp index bdc5505..a37f78e 100644 --- a/XMPFiles/source/FileHandlers/JPEG_Handler.cpp +++ b/XMPFiles/source/FileHandlers/JPEG_Handler.cpp @@ -24,6 +24,8 @@ #include "third-party/zuid/interfaces/MD5.h" +#include <algorithm> + using namespace std; // ================================================================================================= @@ -651,8 +653,7 @@ void JPEG_MetaHandler::ProcessXMP() // Process the legacy metadata. if ( haveIPTC && (! haveXMP) && (iptcDigestState == kDigestMatches) ) iptcDigestState = kDigestMissing; - bool parseIPTC = (iptcDigestState != kDigestMatches) || (! readOnly); - if ( parseIPTC ) iptc.ParseMemoryDataSets ( iptcInfo.dataPtr, iptcInfo.dataLen ); + if (iptcInfo.dataLen) iptc.ParseMemoryDataSets ( iptcInfo.dataPtr, iptcInfo.dataLen ); ImportPhotoData ( exif, iptc, psir, iptcDigestState, &this->xmpObj, options ); this->containsXMP = true; // Assume we had something for the XMP. @@ -961,7 +962,7 @@ void JPEG_MetaHandler::WriteTempFile ( XMP_IO* tempRef ) } if ( copySegment && (signatureLen == kExtXMPSignatureLength) && - CheckBytes ( &buffer[0], kExtXMPSignatureString, kExtXMPPrefixLength ) ) { + CheckBytes ( &buffer[0], kExtXMPSignatureString, kExtXMPSignatureLength ) ) { copySegment = false; } diff --git a/XMPFiles/source/FileHandlers/MP3_Handler.cpp b/XMPFiles/source/FileHandlers/MP3_Handler.cpp index 55211c9..9295a2e 100644 --- a/XMPFiles/source/FileHandlers/MP3_Handler.cpp +++ b/XMPFiles/source/FileHandlers/MP3_Handler.cpp @@ -14,11 +14,14 @@ #include "XMPFiles/source/XMPFiles_Impl.hpp" #include "XMPFiles/source/FormatSupport/Reconcile_Impl.hpp" +#include "source/UnicodeConversions.hpp" #include "source/XMPFiles_IO.hpp" #include "source/XIO.hpp" #include "XMPFiles/source/FileHandlers/MP3_Handler.hpp" +#include <sstream> + // ================================================================================================= /// \file MP3_Handler.cpp /// \brief MP3 handler class. @@ -204,7 +207,10 @@ void MP3_MetaHandler::CacheFileData() // read frames XMP_Uns32 xmpID = XMP_V23_ID; - if ( this->majorVersion == 2 ) xmpID = XMP_V22_ID; + if ( this->majorVersion == 2 ) + { + xmpID = XMP_V22_ID; + } while ( file->Offset() < this->oldTagSize ) { @@ -442,10 +448,9 @@ void MP3_MetaHandler::ProcessXMP() if ( xmpObj.GetProperty_Date ( kXMP_NS_XMP, "CreateDate", &oldDateTime, 0 ) ) { - haveNewDateTime = haveNewDateTime && - ( (newDateTime.year != oldDateTime.year) || - ( (newDateTime.month != 0 ) && ( (newDateTime.day != oldDateTime.day) || (newDateTime.month != oldDateTime.month) ) ) || - ( newDateTime.hasTime && ( (newDateTime.hour != oldDateTime.hour) || (newDateTime.minute != oldDateTime.minute) ) ) ); + haveNewDateTime = haveNewDateTime && ( (newDateTime.year != oldDateTime.year) || ( (newDateTime.month != 0 ) + && ( (newDateTime.day != oldDateTime.day) || (newDateTime.month != oldDateTime.month) ) ) + || ( newDateTime.hasTime && ( (newDateTime.hour != oldDateTime.hour) || (newDateTime.minute != oldDateTime.minute) ) ) ); } // NOTE: no further validation nessesary the function "SetProperty_Date" will care about validating date and time // any exception will be caught and block import @@ -727,3 +732,4 @@ void MP3_MetaHandler::WriteTempFile ( XMP_IO* tempRef ) IgnoreParam(tempRef); XMP_Throw ( "MP3_MetaHandler::WriteTempFile: Not supported", kXMPErr_Unimplemented ); } // MP3_MetaHandler::WriteTempFile + diff --git a/XMPFiles/source/FileHandlers/MP3_Handler.hpp b/XMPFiles/source/FileHandlers/MP3_Handler.hpp index 05345c6..6916f2e 100644 --- a/XMPFiles/source/FileHandlers/MP3_Handler.hpp +++ b/XMPFiles/source/FileHandlers/MP3_Handler.hpp @@ -23,14 +23,12 @@ extern bool MP3_CheckFormat ( XMP_FileFormat format, XMP_StringPtr filePath, XMP_IO* fileRef, XMPFiles * parent ); - static const XMP_OptionBits kMP3_HandlerFlags = (kXMPFiles_CanInjectXMP | kXMPFiles_CanExpand | kXMPFiles_PrefersInPlace | kXMPFiles_AllowsOnlyXMP | - kXMPFiles_ReturnsRawPacket| + kXMPFiles_ReturnsRawPacket | kXMPFiles_CanReconcile); - class MP3_MetaHandler : public XMPFileHandler { public: @@ -43,7 +41,6 @@ public: void WriteTempFile ( XMP_IO* tempRef ); void ProcessXMP(); - private: //////////////////////////////////////////////////////////////////////////////////// // instance vars diff --git a/XMPFiles/source/FileHandlers/MPEG4_Handler.cpp b/XMPFiles/source/FileHandlers/MPEG4_Handler.cpp index 905f19f..ee25d7a 100644 --- a/XMPFiles/source/FileHandlers/MPEG4_Handler.cpp +++ b/XMPFiles/source/FileHandlers/MPEG4_Handler.cpp @@ -179,8 +179,8 @@ static inline bool IsXMPUUID ( XMP_IO * fileRef,XMP_Uns64 contentSize, bool unmo // // 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 ISO files must begin with an 'ftyp' box containing 'mp41', 'mp42', 'f4v ', 'qt ', 'isom','3gp4', +// '3g2a','3g2b' or '3g2c' in the compatible brands. // // The general box structure is: // @@ -259,8 +259,11 @@ bool MPEG4_CheckFormat ( XMP_FileFormat format, 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) || ( brand == ISOMedia::k_avc1 ) ) { + } + else if ( ( brand == ISOMedia::k_mp41 ) || ( brand == ISOMedia::k_mp42 ) || + ( brand == ISOMedia::k_f4v ) || ( brand == ISOMedia::k_avc1 ) || ( brand == ISOMedia::k_isom ) || + ( brand == ISOMedia::k_3gp4 ) || ( brand == ISOMedia::k_3g2a ) || ( brand == ISOMedia::k_3g2b ) || + ( brand == ISOMedia::k_3g2c ) ) { haveCompatibleBrand = true; // Need to keep looking in case 'qt ' follows. } @@ -1774,7 +1777,7 @@ static void AttemptFileRepair ( XMP_IO* qtFile, XMP_Int64 fileSpace, QTErrorMode } AtomInfo info; - XMP_Int64 headerSize; + XMP_Int64 headerSize(0); // Process the top level atoms until an error is found. @@ -2062,7 +2065,7 @@ void MPEG4_MetaHandler::CacheFileData() bool xmpOnly = XMP_OptionIsSet ( openFlags, kXMPFiles_OpenOnlyXMP ); bool haveISOFile = (this->fileMode == MOOV_Manager::kFileIsNormalISO); - bool uuidFound = (! haveISOFile); // Ignore the XMP 'uuid' box for QuickTime files. + bool xmpUuidFound = (! 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; @@ -2089,24 +2092,24 @@ void MPEG4_MetaHandler::CacheFileData() this->moovBoxPos = boxPos; this->moovBoxSize = (XMP_Uns32)fullMoovSize; moovFound = true; - if ( uuidFound ) break; // Exit the loop when both are found. + if ( xmpUuidFound ) break; // Exit the loop when both are found. - } else if ( (! uuidFound) && (currBox.boxType == ISOMedia::k_uuid) && IsXMPUUID(fileRef,currBox.contentSize) ) { + } else if ( (! xmpUuidFound) && (currBox.boxType == ISOMedia::k_uuid) && ( memcmp( currBox.idUUID, ISOMedia::k_xmpUUID, 16 ) == 0 ) ) { 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->packetInfo.offset = boxPos + currBox.headerSize ; + this->packetInfo.length = (XMP_Int32) (currBox.contentSize); this->xmpPacket.assign ( this->packetInfo.length, ' ' ); fileRef->ReadAll ( (void*)this->xmpPacket.data(), this->packetInfo.length ); this->xmpBoxPos = boxPos; this->xmpBoxSize = (XMP_Uns32)fullUuidSize; - uuidFound = true; + xmpUuidFound = true; if ( moovFound ) break; // Exit the loop when both are found. } @@ -2679,7 +2682,7 @@ void MPEG4_MetaHandler::OptimizeFileLayout() needsOptimization = mdatFound; if ( xmpFound ) break; // Don't need to look further. - } else if ( currBox.boxType == ISOMedia::k_uuid && IsXMPUUID(originalFile,currBox.contentSize) ) { + } else if ( currBox.boxType == ISOMedia::k_uuid && ( memcmp( currBox.idUUID, ISOMedia::k_xmpUUID, 16 ) == 0 ) ) { xmpFound = true; xmpIndex = boxCount-1; // Need later for optimization. @@ -3009,7 +3012,7 @@ void MPEG4_MetaHandler::UpdateFile ( bool doSafeUpdate ) // 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(); + 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] ); diff --git a/XMPFiles/source/FileHandlers/P2_Handler.cpp b/XMPFiles/source/FileHandlers/P2_Handler.cpp index d99235d..1b7ad09 100644 --- a/XMPFiles/source/FileHandlers/P2_Handler.cpp +++ b/XMPFiles/source/FileHandlers/P2_Handler.cpp @@ -718,6 +718,7 @@ void P2_MetaHandler::SetStartTimecodeFromLegacyXML ( XML_NodePtr legacyVideoCont } else if ( p2FrameRate == "50p" ) { dmTimeFormat = "50Timecode"; + this->AdjustTimeCode( p2StartTimecode, false ); } else if ( ( p2FrameRate == "59.94p" ) && ( p2DropFrameFlag != 0 ) ) { @@ -726,6 +727,7 @@ void P2_MetaHandler::SetStartTimecodeFromLegacyXML ( XML_NodePtr legacyVideoCont } else if ( XMP_LitMatch ( p2DropFrameFlag, "false" ) ) { dmTimeFormat = "5994NonDropTimecode"; } + this->AdjustTimeCode( p2StartTimecode, false ); } else if ( (p2FrameRate == "59.94i") || (p2FrameRate == "29.97p") ) { @@ -767,6 +769,56 @@ void P2_MetaHandler::SetStartTimecodeFromLegacyXML ( XML_NodePtr legacyVideoCont } // P2_MetaHandler::SetStartTimecodeFromLegacyXML +// ================================================================================================= +// P2_MetaHandler::AdjustTimeCode +// =========================================== + +void P2_MetaHandler::AdjustTimeCode( std::string & p2Timecode, const XMP_Bool & isXMPtoXMLConversion ) +{ + /* + XMP is storing frame number for 50P and 59.94P as [0-49] and [0-59] respectively, + but NRT XML can store frame number for these format as [0-29]. + So, XMP need to adjust values for these cases. + */ + try + { + XMP_Int64 strLength = p2Timecode.length(); + XMP_Int64 index = strLength - 1; + for (; index > 0; --index) + if (p2Timecode.at(index) == ':') + break; + std::string FFValue; + if ( index == strLength - 2 ) // HH:MM:SS:F + FFValue = p2Timecode.substr(index + 1, 1); + else if ( index == strLength - 3 ) + FFValue = p2Timecode.substr(index + 1, 2); // HH:MM:SS:FF + else + throw; // Invalid format + stringstream timeCodeStream (FFValue); + XMP_Uns32 frameNumber; + timeCodeStream >> frameNumber; + if (isXMPtoXMLConversion) // Conversion from XMP to XML so doing half the value + { + frameNumber /= 2; + XMP_Assert(frameNumber >= 0 && frameNumber < 30); + } + else // Conversion from XML to XMP so doubling the value + { + XMP_Assert(frameNumber >= 0 && frameNumber < 30); + frameNumber *= 2; + } + timeCodeStream.clear(); + timeCodeStream << p2Timecode.substr(0, index + 1); + if (frameNumber < 10) + timeCodeStream << '0'; + timeCodeStream << frameNumber; + p2Timecode = timeCodeStream.str(); + } + catch (...) + { + XMP_Throw("P2 Invalid Timecode.", kXMPErr_InternalFailure); + } +} // P2_MetaHandler::AdjustTimeCode // ================================================================================================= // P2_MetaHandler::SetGPSPropertyFromLegacyXML @@ -1235,7 +1287,35 @@ void P2_MetaHandler::UpdateFile ( bool doSafeUpdate ) updateLegacyXML = true; } } + } + // Half the startTimeCode frame number value in XML if require so + std::string xmpStartTimeCode; + bool isTimecodeExists = this->xmpObj.GetStructField(kXMP_NS_DM, "startTimecode", kXMP_NS_DM, "timeValue", &xmpStartTimeCode, 0); + if (isTimecodeExists) + { + std::string frameFormat; + this->xmpObj.GetStructField(kXMP_NS_DM, "startTimecode", kXMP_NS_DM, "timeFormat", &frameFormat, 0); + if (frameFormat == "50Timecode" || frameFormat == "5994DropTimecode" || frameFormat == "5994NonDropTimecode") + { + p2Clip = this->p2ClipManager.GetManagedClip(); + XMP_StringPtr p2NS = p2Clip->GetP2RootNode()->ns.c_str(); + XML_NodePtr legacyVideoContext = p2Clip->GetEssenceListNode(); + if (legacyVideoContext != 0) + { + legacyVideoContext = legacyVideoContext->GetNamedElement(p2NS, "Video"); + XML_NodePtr legacyProp = legacyVideoContext->GetNamedElement(p2NS, "StartTimecode"); + if ((legacyProp != 0) && legacyProp->IsLeafContentNode()) + { + AdjustTimeCode( xmpStartTimeCode, true ); + if (xmpStartTimeCode != legacyProp->GetLeafContentValue()) + { + legacyProp->SetLeafContentValue(xmpStartTimeCode.c_str()); + updateLegacyXML = true; + } + } + } + } } std::string newDigest; diff --git a/XMPFiles/source/FileHandlers/P2_Handler.hpp b/XMPFiles/source/FileHandlers/P2_Handler.hpp index 5e492ee..8ef6da3 100644 --- a/XMPFiles/source/FileHandlers/P2_Handler.hpp +++ b/XMPFiles/source/FileHandlers/P2_Handler.hpp @@ -99,6 +99,7 @@ private: 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 ); + void AdjustTimeCode( std::string & p2Timecode, const XMP_Bool & isXMPtoXMLConversion ); XML_Node * ForceChildElement ( XML_Node * parent, XMP_StringPtr localName, XMP_Int32 indent, XMP_Bool insertAtFront ); diff --git a/XMPFiles/source/FileHandlers/PSD_Handler.cpp b/XMPFiles/source/FileHandlers/PSD_Handler.cpp index e5847e9..312e855 100644 --- a/XMPFiles/source/FileHandlers/PSD_Handler.cpp +++ b/XMPFiles/source/FileHandlers/PSD_Handler.cpp @@ -250,8 +250,7 @@ void PSD_MetaHandler::ProcessXMP() // Process the legacy metadata. if ( haveIPTC && (! haveXMP) && (iptcDigestState == kDigestMatches) ) iptcDigestState = kDigestMissing; - bool parseIPTC = (iptcDigestState != kDigestMatches) || (! readOnly); - if ( parseIPTC ) iptc.ParseMemoryDataSets ( iptcInfo.dataPtr, iptcInfo.dataLen ); + if (iptcInfo.dataLen) 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. diff --git a/XMPFiles/source/FileHandlers/PostScript_Handler.cpp b/XMPFiles/source/FileHandlers/PostScript_Handler.cpp index d21ab8d..4467794 100644 --- a/XMPFiles/source/FileHandlers/PostScript_Handler.cpp +++ b/XMPFiles/source/FileHandlers/PostScript_Handler.cpp @@ -885,7 +885,11 @@ void PostScript_MetaHandler::ParsePSFile() if (CheckBytes ( ioBuf.ptr, Uns8Ptr("iler"), 4 )) { ioBuf.ptr+=4; - while(!IsNewline(*ioBuf.ptr)) ++ioBuf.ptr; + while ( !IsNewline( *ioBuf.ptr ) ) + { + if ( !CheckFileSpace( fileRef, &ioBuf, 1 ) ) return; + ++ioBuf.ptr; + } setTokenInfo(kPS_Trailer,begStartpos,ioBuf.filePos+ioBuf.ptr-ioBuf.data-begStartpos); } } diff --git a/XMPFiles/source/FileHandlers/RIFF_Handler.cpp b/XMPFiles/source/FileHandlers/RIFF_Handler.cpp index 7d6fdb3..5884390 100644 --- a/XMPFiles/source/FileHandlers/RIFF_Handler.cpp +++ b/XMPFiles/source/FileHandlers/RIFF_Handler.cpp @@ -71,10 +71,11 @@ RIFF_MetaHandler::RIFF_MetaHandler ( XMPFiles * _parent ) this->oldFileSize = this->newFileSize = this->trailingGarbageSize = 0; this->level = 0; - this->listInfoChunk = this->listTdatChunk = 0; + this->listInfoChunk = this->listTdatChunk = this->listHdlrChunk = 0; this->dispChunk = this->bextChunk = this->cr8rChunk = this->prmlChunk = 0; this->xmpChunk = 0; this->lastChunk = 0; + this->iditChunk = 0; this->hasListInfoINAM = false; } diff --git a/XMPFiles/source/FileHandlers/RIFF_Handler.hpp b/XMPFiles/source/FileHandlers/RIFF_Handler.hpp index d8c83a2..bfa7b2a 100644 --- a/XMPFiles/source/FileHandlers/RIFF_Handler.hpp +++ b/XMPFiles/source/FileHandlers/RIFF_Handler.hpp @@ -57,11 +57,12 @@ public: // state variables, needed during parsing XMP_Uns8 level; - RIFF::ContainerChunk *listInfoChunk, *listTdatChunk; + RIFF::ContainerChunk *listInfoChunk, *listTdatChunk,*listHdlrChunk; RIFF::ValueChunk* dispChunk; RIFF::ValueChunk* bextChunk; RIFF::ValueChunk* cr8rChunk; RIFF::ValueChunk* prmlChunk; + RIFF::ValueChunk* iditChunk; RIFF::XMPChunk* xmpChunk; RIFF::ContainerChunk* lastChunk; bool hasListInfoINAM; // needs to be known for the special 3-way merge around dc:title diff --git a/XMPFiles/source/FileHandlers/SVG_Handler.cpp b/XMPFiles/source/FileHandlers/SVG_Handler.cpp new file mode 100644 index 0000000..93ef108 --- /dev/null +++ b/XMPFiles/source/FileHandlers/SVG_Handler.cpp @@ -0,0 +1,689 @@ +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2015 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. +// +// This file includes implementation of SVG metadata, according to Scalable Vector Graphics (SVG) 1.1 Specification. +// "https://www.w3.org/TR/2003/REC-SVG11-20030114/" +// Copyright © 1994-2002 World Wide Web Consortium, (Massachusetts Institute of Technology, +// Institut National de Recherche en Informatique et en Automatique, Keio University). +// All Rights Reserved . http://www.w3.org/Consortium/Legal +// +// ================================================================================================= + +#include "public/include/XMP_Environment.h" // ! XMP_Environment.h must be the first included header. + +#include "public/include/XMP_Const.h" +#include "public/include/XMP_IO.hpp" + +#include "XMPFiles/source/XMPFiles_Impl.hpp" +#include "source/XMPFiles_IO.hpp" +#include "source/XIO.hpp" + +#include "XMPFiles/source/FileHandlers/SVG_Handler.hpp" + +using namespace std; + +/* + Currently supporting only UTF-8 encoded SVG +*/ + +// ================================================================================================= +// SVG_CheckFormat +// =============== + +bool SVG_CheckFormat( XMP_FileFormat format, + XMP_StringPtr filePath, + XMP_IO * fileRef, + XMPFiles * parent ) +{ + // 8K buffer is provided just to handle maximum SVG files + // We can't check for SVG element in whole file which could take a lot of time for valid XML files + IgnoreParam( filePath ); IgnoreParam( parent ); + + XMP_Assert( format == kXMP_SVGFile ); + + fileRef->Rewind(); + + XMP_Uns8 buffer[ 1024 ]; + + // Reading 4 bytes for BOM + XMP_Uns32 bytesRead = fileRef->Read( buffer, 4 ); + if ( bytesRead != 4 ) + return false; + + // Checking for UTF-16 BOM and UTF-32 BOM + if ( ( buffer[ 0 ] == 0xFF && buffer[ 1 ] == 0xFE ) || ( buffer[ 0 ] == 0xFE && buffer[ 1 ] == 0xFF ) || ( buffer[ 0 ] == buffer[ 1 ] == 0x00 && buffer[ 2 ] == 0xFE && buffer[ 3 ] == 0xFF ) ) + { + return false; + } + + // Initially we are intersted only in "svg" element. + SVG_Adapter * svgChecker = new SVG_Adapter(); + if ( svgChecker == 0 ) + return false; + + bool isSVG = false; + + fileRef->Rewind(); + for ( XMP_Uns8 index = 0; index < 8; ++index ) + { + XMP_Int32 ioCount = fileRef->Read( buffer, sizeof( buffer ) ); + if ( ioCount == 0 ) break; + + // Checking for well formed XML + if ( !svgChecker->ParseBufferNoThrow( buffer, ioCount, false /* not the end */ ) ) + break; + + if ( svgChecker->tree.GetNamedElement( "http://www.w3.org/2000/svg", "svg" ) ) + { + isSVG = true; + break; + } + } + + if ( svgChecker ) + delete ( svgChecker ); + + return isSVG; + +} // SVG_CheckFormat + +// ================================================================================================= +// SVG_MetaHandlerCTor +// =================== + +XMPFileHandler * SVG_MetaHandlerCTor( XMPFiles * parent ) +{ + return new SVG_MetaHandler( parent ); + +} // SVG_MetaHandlerCTor + +// ================================================================================================= +// SVG_MetaHandler::SVG_MetaHandler +// ================================ + +SVG_MetaHandler::SVG_MetaHandler( XMPFiles * _parent ) : svgNode( 0 ), svgAdapter( 0 ), isTitleUpdateReq( false ), isDescUpdateReq( false ) +{ + this->parent = _parent; + this->handlerFlags = kSVG_HandlerFlags; + this->stdCharForm = kXMP_Char8Bit; + +} + +// ================================================================================================= +// SVG_MetaHandler::~SVG_MetaHandler +// ================================= + +SVG_MetaHandler::~SVG_MetaHandler() +{ + + if ( this->svgAdapter != 0 ) + { + delete ( this->svgAdapter ); + this->svgAdapter = 0; + } +} + +// ================================================================================================= +// SVG_MetaHandler::GetSerializeOptions +// =================================== +// +// Override default implementation to ensure Canonical packet. + +XMP_OptionBits SVG_MetaHandler::GetSerializeOptions() +{ + + return ( kXMP_UseCanonicalFormat ); + +} // SVG_MetaHandler::GetSerializeOptions + +// ================================================================================================= +// SVG_MetaHandler::CacheFileData +// ============================== + +void SVG_MetaHandler::CacheFileData() +{ + XMP_Assert( !this->containsXMP ); + + XMP_IO * fileRef = this->parent->ioRef; + + XMP_Uns8 marker[ 4 ]; + fileRef->Rewind(); + fileRef->Read( marker, 4 ); + + // Checking for UTF-16 BOM and UTF-32 BOM + if ( ( marker[ 0 ] == 0xFF && marker[ 1 ] == 0xFE ) || ( marker[ 0 ] == 0xFE && marker[ 1 ] == 0xFF ) || ( marker[ 0 ] == marker[ 1 ] == 0x00 && marker[ 2 ] == 0xFE && marker[ 3 ] == 0xFF ) ) + { + XMP_Error error( kXMPErr_BadXML, "Invalid SVG file" ); + this->NotifyClient( &this->parent->errorCallback, kXMPErrSev_OperationFatal, error ); + } + + // Creating a new SVG Parser + svgAdapter = new SVG_Adapter(); + if ( svgAdapter == 0 ) + XMP_Throw( "SVG_MetaHandler: Can't create SVG adapter", kXMPErr_NoMemory ); + svgAdapter->SetErrorCallback( &this->parent->errorCallback ); + + // Registering all the required tags to SVG Parser + svgAdapter->RegisterPI( "xpacket" ); + svgAdapter->RegisterElement( "metadata", "svg" ); + svgAdapter->RegisterElement( "xmpmeta", "metadata" ); + svgAdapter->RegisterElement( "RDF", "metadata" ); + svgAdapter->RegisterElement( "title", "svg" ); + svgAdapter->RegisterElement( "desc", "svg" ); + + // Parsing the whole buffer + fileRef->Rewind(); + XMP_Uns8 buffer[ 64 * 1024 ]; + while ( true ) { + XMP_Int32 ioCount = fileRef->Read( buffer, sizeof( buffer ) ); + if ( ioCount == 0 || !svgAdapter->IsParsingRequire() ) break; + svgAdapter->ParseBuffer( buffer, ioCount, false /* not the end */ ); + } + svgAdapter->ParseBuffer( 0, 0, true ); // End the parse. + + XML_Node & xmlTree = this->svgAdapter->tree; + XML_NodePtr rootElem = 0; + + for ( size_t i = 0, limit = xmlTree.content.size(); i < limit; ++i ) + { + if ( xmlTree.content[ i ]->kind == kElemNode ) { + rootElem = xmlTree.content[ i ]; + } + } + if ( rootElem == 0 ) + XMP_Throw( "Not a valid SVG File", kXMPErr_BadFileFormat ); + + XMP_StringPtr rootLocalName = rootElem->name.c_str() + rootElem->nsPrefixLen; + + if ( ! XMP_LitMatch( rootLocalName, "svg" ) ) + XMP_Throw( "Not able to parse such SVG File", kXMPErr_BadFileFormat ); + + // Making SVG node as Root Node + svgNode = rootElem; + + bool FoundPI = false; + bool FoundWrapper = false; + XML_NodePtr metadataNode = svgNode->GetNamedElement( rootElem->ns.c_str(), "metadata" ); + + // We are intersted only in the Metadata tag of outer SVG element + // XMP should be present only in metadata Node of SVG + if ( metadataNode != NULL ) + { + XMP_Int64 packetLength = -1; + XMP_Int64 packetOffset = -1; + XMP_Int64 PIOffset = svgAdapter->GetPIOffset( "xpacket", 1 ); + OffsetStruct wrapperOffset = svgAdapter->GetElementOffsets( "xmpmeta" ); + OffsetStruct rdfOffset = svgAdapter->GetElementOffsets( "RDF" ); + + // Checking XMP PI's position + if ( PIOffset != -1 ) + { + if ( wrapperOffset.startOffset != -1 && wrapperOffset.startOffset < PIOffset ) + packetOffset = wrapperOffset.startOffset; + else + { + XMP_Int64 trailerOffset = svgAdapter->GetPIOffset( "xpacket", 2 ); + XML_NodePtr trailerNode = metadataNode->GetNamedElement( "", "xpacket", 1 ); + if ( trailerOffset != -1 || trailerNode != 0 ) + { + packetLength = 2; // "<?" = 2 + packetLength += trailerNode->name.length(); // Node's name + packetLength += 1; // Empty Space after Node's name + packetLength += trailerNode->value.length(); // Value + packetLength += 2; // "?>" = 2 + packetLength += ( trailerOffset - PIOffset ); + packetOffset = PIOffset; + } + } + } + else if ( wrapperOffset.startOffset != -1 ) // XMP Wrapper is present without PI + { + XML_NodePtr wrapperNode = metadataNode->GetNamedElement( "adobe:ns:meta/", "xmpmeta" ); + if ( wrapperNode != 0 ) + { + std::string trailerWrapper = "</x:xmpmeta>"; + packetLength = trailerWrapper.length(); + packetLength += ( wrapperOffset.endOffset - wrapperOffset.startOffset ); + packetOffset = wrapperOffset.startOffset; + } + } + else // RDF packet is present without PI and wrapper + { + XML_NodePtr rdfNode = metadataNode->GetNamedElement( "http://www.w3.org/1999/02/22-rdf-syntax-ns#", "RDF" ); + if ( rdfNode != 0 ) + { + std::string rdfTrailer = "</rdf:RDF>"; + packetLength = rdfTrailer.length(); + packetLength += ( rdfOffset.endOffset - rdfOffset.startOffset ); + packetOffset = rdfOffset.startOffset; + } + } + + // Fill the necesarry information and packet with XMP data + if ( packetOffset != -1 ) + { + this->packetInfo.offset = packetOffset; + this->packetInfo.length = ( XMP_Int32 ) packetLength; + this->xmpPacket.assign( this->packetInfo.length, ' ' ); + fileRef->Seek( packetOffset, kXMP_SeekFromStart ); + fileRef->ReadAll( ( void* )this->xmpPacket.data(), this->packetInfo.length ); + FillPacketInfo( this->xmpPacket, &this->packetInfo ); + this->containsXMP = true; + return; + } + } + this->containsXMP = false; + +} // SVG_MetaHandler::CacheFileData + +// ================================================================================================= +// SVG_MetaHandler::ProcessXMP +// ============================== + +void SVG_MetaHandler::ProcessXMP() +{ + // + // Here we are intersted in Only 2 childs, title and desc + // + this->processedXMP = true; // Make sure we only come through here once. + + if ( svgNode == NULL ) + return; + + if ( !this->xmpPacket.empty() ) { + XMP_Assert( this->containsXMP ); + this->xmpObj.ParseFromBuffer( this->xmpPacket.c_str(), ( XMP_StringLen )this->xmpPacket.size() ); + } + + // Description + XML_NodePtr descNode = svgNode->GetNamedElement( svgNode->ns.c_str(), "desc" ); + if ( descNode != 0 && descNode->content.size() == 1 && descNode->content[0]->kind == kCDataNode ) + { + this->xmpObj.SetLocalizedText( kXMP_NS_DC, "description", "", "x-default", descNode->content[0]->value, kXMP_DeleteExisting ); + this->containsXMP = true; + } + + // Title + XML_NodePtr titleNode = svgNode->GetNamedElement( svgNode->ns.c_str(), "title" ); + if ( titleNode != 0 && titleNode->content.size() == 1 && titleNode->content[ 0 ]->kind == kCDataNode ) + { + this->xmpObj.SetLocalizedText( kXMP_NS_DC, "title", "", "x-default", titleNode->content[0]->value, kXMP_DeleteExisting ); + this->containsXMP = true; + } + +} // SVG_MetaHandler::ProcessXMP + +// ================================================================================================= +// SVG_MetaHandler::ProcessTitle +// =========================== +// It is handling the updation and deletion case +void SVG_MetaHandler::ProcessTitle( XMP_IO* sourceRef, XMP_IO * destRef, const std::string &value, XMP_Int64 ¤tOffset, const OffsetStruct & titleOffset ) +{ + if ( value.empty() ) + { + XIO::Copy( sourceRef, destRef, titleOffset.startOffset - currentOffset ); + sourceRef->Seek( titleOffset.nextOffset, kXMP_SeekFromStart ); + currentOffset = titleOffset.nextOffset; + } + else + { + std::string titleElement = "<title>"; + XIO::Copy( sourceRef, destRef, titleOffset.startOffset - currentOffset + titleElement.length() ); + destRef->Write( value.c_str(), static_cast< int >( value.length() ) ); + sourceRef->Seek( titleOffset.endOffset, kXMP_SeekFromStart ); + currentOffset = titleOffset.endOffset; + } +} // SVG_MetaHandler::ProcessTitle + +// ================================================================================================= +// SVG_MetaHandler::ProcessDescription +// =========================== +// It is handling the updation and deletion case +void SVG_MetaHandler::ProcessDescription( XMP_IO* sourceRef, XMP_IO * destRef, const std::string &value, XMP_Int64 ¤tOffset, const OffsetStruct & descOffset ) +{ + if ( value.empty() ) + { + XIO::Copy( sourceRef, destRef, descOffset.startOffset - currentOffset ); + sourceRef->Seek( descOffset.nextOffset, kXMP_SeekFromStart ); + currentOffset = descOffset.nextOffset; + } + else + { + std::string descElement = "<desc>"; + XIO::Copy( sourceRef, destRef, descOffset.startOffset - currentOffset + descElement.length() ); + destRef->Write( value.c_str(), static_cast< int >( value.length() ) ); + sourceRef->Seek( descOffset.endOffset, kXMP_SeekFromStart ); + currentOffset = descOffset.endOffset; + } + +} // SVG_MetaHandler::ProcessDescription + +// ================================================================================================= +// SVG_MetaHandler::InsertNewTitle +// =========================== +// It is handling the insertion case +void SVG_MetaHandler::InsertNewTitle( XMP_IO * destRef, const std::string &value ) +{ + std::string titleElement = "<title>"; + destRef->Write( titleElement.c_str(), static_cast< int >( titleElement.length() ) ); + destRef->Write( value.c_str(), static_cast< int >( value.length() ) ); + titleElement = "</title>\n"; + destRef->Write( titleElement.c_str(), static_cast< int >( titleElement.length() ) ); + +} // SVG_MetaHandler::InsertNewTitle + +// ================================================================================================= +// SVG_MetaHandler::InsertNewDescription +// =========================== +// It is handling the insertion case +void SVG_MetaHandler::InsertNewDescription( XMP_IO * destRef, const std::string &value ) +{ + std::string descElement = "<desc>"; + destRef->Write( descElement.c_str(), static_cast< int >( descElement.length() ) ); + destRef->Write( value.c_str(), static_cast< int >( value.length() ) ); + descElement = "</desc>\n"; + destRef->Write( descElement.c_str(), static_cast< int >( descElement.length() ) ); + +} // SVG_MetaHandler::InsertNewDescription + +// ================================================================================================= +// SVG_MetaHandler::InsertNewMetadata +// =========================== +// It is handling the insertion case +void SVG_MetaHandler::InsertNewMetadata( XMP_IO * destRef, const std::string &value ) +{ + + std::string metadataElement = "<metadata>"; + destRef->Write( metadataElement.c_str(), static_cast< int >( metadataElement.length() ) ); + destRef->Write( value.c_str(), static_cast< int >( value.length() ) ); + metadataElement = "</metadata>\n"; + destRef->Write( metadataElement.c_str(), static_cast< int >( metadataElement.length() ) ); + +} // SVG_MetaHandler::InsertNewMetadata + +// ================================================================================================= +// SVG_MetaHandler::UpdateFile +// =========================== + +void SVG_MetaHandler::UpdateFile( bool doSafeUpdate ) +{ + XMP_Assert( !doSafeUpdate ); // This should only be called for "unsafe" updates. + + XMP_IO* sourceRef = this->parent->ioRef; + + if ( sourceRef == NULL || svgNode == NULL ) + return; + + // Checking whether Title updation requires or not + std::string title; + XML_NodePtr titleNode = svgNode->GetNamedElement( svgNode->ns.c_str(), "title" ); + (void) this->xmpObj.GetLocalizedText( kXMP_NS_DC, "title", "", "x-default", 0, &title, 0 ); + if ( ( titleNode == NULL ) == ( title.empty() ) ) + { + if ( titleNode != NULL && titleNode->content.size() == 1 && titleNode->content[ 0 ]->kind == kCDataNode && !XMP_LitMatch( titleNode->content[ 0 ]->value.c_str(), title.c_str() ) ) + isTitleUpdateReq = true; + } + else + isTitleUpdateReq = true; + + // Checking whether Description updation requires or not + std::string description; + XML_NodePtr descNode = svgNode->GetNamedElement( svgNode->ns.c_str(), "desc" ); + ( void ) this->xmpObj.GetLocalizedText( kXMP_NS_DC, "description", "", "x-default", 0, &description, 0 ); + if ( ( descNode == NULL ) == ( description.empty() ) ) + { + if ( descNode != NULL && descNode->content.size() == 1 && descNode->content[ 0 ]->kind == kCDataNode && !XMP_LitMatch( descNode->content[ 0 ]->value.c_str(), description.c_str() ) ) + isDescUpdateReq = true; + } + else + isDescUpdateReq = true; + + // If any updation is required then don't do inplace replace + bool isUpdateRequire = isTitleUpdateReq | isDescUpdateReq | (this->packetInfo.offset == kXMPFiles_UnknownOffset); + + // Inplace Updation of XMP + if ( !isUpdateRequire && this->xmpPacket.size() == this->packetInfo.length ) + { + sourceRef->Seek( this->packetInfo.offset, kXMP_SeekFromStart ); + sourceRef->Write( this->xmpPacket.c_str(), static_cast< int >( this->xmpPacket.size() ) ); + } + else + { + // Inplace is not possibe, So perform full updation + try + { + XMP_IO* tempRef = sourceRef->DeriveTemp(); + this->WriteTempFile( tempRef ); + } + catch ( ... ) + { + sourceRef->DeleteTemp(); + throw; + } + + sourceRef->AbsorbTemp(); + } + + this->needsUpdate = false; + +} // SVG_MetaHandler::UpdateFile + +// ================================================================================================= +// SVG_MetaHandler::WriteTempFile +// ============================== +// +void SVG_MetaHandler::WriteTempFile( XMP_IO* tempRef ) +{ + XMP_Assert( this->needsUpdate ); + + XMP_IO* sourceRef = this->parent->ioRef; + if ( sourceRef == NULL || svgNode == NULL ) + return; + + tempRef->Rewind(); + sourceRef->Rewind(); + + XMP_Int64 currentOffset = svgAdapter->firstSVGElementOffset; + XIO::Copy( sourceRef, tempRef, currentOffset ); + + OffsetStruct titleOffset = svgAdapter->GetElementOffsets( "title" ); + OffsetStruct descOffset = svgAdapter->GetElementOffsets( "desc" ); + OffsetStruct metadataOffset = svgAdapter->GetElementOffsets( "metadata" ); + + std::string title; + std::string description; + + XML_NodePtr titleNode = svgNode->GetNamedElement( svgNode->ns.c_str(), "title" ); + ( void ) this->xmpObj.GetLocalizedText( kXMP_NS_DC, "title", "", "x-default", 0, &title, 0 ); + + XML_NodePtr descNode = svgNode->GetNamedElement( svgNode->ns.c_str(), "desc" ); + ( void ) this->xmpObj.GetLocalizedText( kXMP_NS_DC, "description", "", "x-default", 0, &description, 0 ); + + // Need to cover the case of both workflows + // This would have been called after inplace is not possible + // This would have called for safe update + if ( !isTitleUpdateReq ) + { + if ( ( titleNode == NULL ) == ( title.empty() ) ) + { + if ( titleNode != NULL && titleNode->content.size() == 1 && titleNode->content[ 0 ]->kind == kCDataNode && !XMP_LitMatch( titleNode->content[ 0 ]->value.c_str(), title.c_str() ) ) + isTitleUpdateReq = true; + } + else + isTitleUpdateReq = true; + } + if ( !isDescUpdateReq ) + { + if ( ( descNode == NULL ) == ( description.empty() ) ) + { + if ( descNode != NULL && descNode->content.size() == 1 && descNode->content[ 0 ]->kind == kCDataNode && !XMP_LitMatch( descNode->content[ 0 ]->value.c_str(), description.c_str() ) ) + isDescUpdateReq = true; + } + else + isDescUpdateReq = true; + } + + // Initial Insertion/Updation + + // Insert/Update Title if requires + // Don't insert/update it if Metadata or desc child comes before title child + bool isTitleWritten = !isTitleUpdateReq; + if ( isTitleUpdateReq ) + { + // Insertion Case + if ( titleNode == NULL ) + { + InsertNewTitle( tempRef, title ); + isTitleWritten = true; + } + else if ( ( descOffset.startOffset == -1 || titleOffset.startOffset < descOffset.startOffset ) // Updation/Deletion Case + && ( metadataOffset.startOffset == -1 || titleOffset.startOffset < metadataOffset.startOffset ) ) + { + ProcessTitle( sourceRef, tempRef, title, currentOffset, titleOffset ); + isTitleWritten = true; + } + } + + // Insert/Update Description if requires + // Don't insert/update it if Metadata child comes before desc child + bool isDescWritten = !isDescUpdateReq; + if ( isDescUpdateReq ) + { + if ( descNode == NULL ) + { + if ( titleOffset.nextOffset != -1 ) + { + XIO::Copy( sourceRef, tempRef, titleOffset.nextOffset - currentOffset ); + currentOffset = titleOffset.nextOffset; + } + InsertNewDescription( tempRef, description ); + isDescWritten = true; + } + else if ( metadataOffset.startOffset == -1 || descOffset.startOffset < metadataOffset.startOffset ) + { + ProcessDescription( sourceRef, tempRef, description, currentOffset, descOffset ); + isDescWritten = true; + } + } + + // Insert/Update Metadata if requires + // Don't insert/update it if case is DTM + bool isMetadataWritten = false; + if ( metadataOffset.startOffset == -1 ) + { + if ( descOffset.nextOffset != -1 ) + { + XIO::Copy( sourceRef, tempRef, descOffset.nextOffset - currentOffset ); + currentOffset = descOffset.nextOffset; + } + else if ( titleOffset.nextOffset != -1 ) + { + XIO::Copy( sourceRef, tempRef, titleOffset.nextOffset - currentOffset ); + currentOffset = titleOffset.nextOffset; + } + InsertNewMetadata( tempRef, this->xmpPacket ); + isMetadataWritten = true; + } + else if ( !( !isTitleWritten && isDescWritten && titleOffset.startOffset < metadataOffset.startOffset ) ) // Not DTM + { + // No XMP packet was present in the file + if ( this->packetInfo.offset == kXMPFiles_UnknownOffset ) + { + std::string metadataElement = "<metadata>"; + XIO::Copy( sourceRef, tempRef, metadataOffset.startOffset - currentOffset + metadataElement.length() ); + currentOffset = sourceRef->Offset(); + tempRef->Write( this->xmpPacket.c_str(), static_cast< int >( this->xmpPacket.length() ) ); + } + else // Replace XMP Packet + { + XIO::Copy( sourceRef, tempRef, this->packetInfo.offset - currentOffset ); + tempRef->Write( this->xmpPacket.c_str(), static_cast< int >( this->xmpPacket.length() ) ); + sourceRef->Seek( this->packetInfo.offset + this->packetInfo.length, kXMP_SeekFromStart ); + currentOffset = sourceRef->Offset(); + } + isMetadataWritten = true; + } + + // If simple cases was followed then copy rest file + if ( isTitleWritten && isDescWritten && isMetadataWritten ) + { + XIO::Copy( sourceRef, tempRef, ( sourceRef->Length() - currentOffset ) ); + return; + } + + // If the case is not Simple (TDM) then perform these operations + if ( isDescWritten ) // TDM, DTM, DMT + { + if ( !isTitleWritten ) // DTM, DMT + { + if ( titleOffset.startOffset < metadataOffset.startOffset ) // DTM + { + ProcessTitle( sourceRef, tempRef, title, currentOffset, titleOffset ); + isTitleWritten = true; + + if ( this->packetInfo.offset == kXMPFiles_UnknownOffset ) + { + std::string metadataElement = "<metadata>"; + XIO::Copy( sourceRef, tempRef, metadataOffset.startOffset - currentOffset + metadataElement.length() ); + currentOffset = sourceRef->Offset(); + tempRef->Write( this->xmpPacket.c_str(), static_cast< int >( this->xmpPacket.length() ) ); + } + else + { + XIO::Copy( sourceRef, tempRef, this->packetInfo.offset - currentOffset ); + tempRef->Write( this->xmpPacket.c_str(), static_cast< int >( this->xmpPacket.length() ) ); + sourceRef->Seek( this->packetInfo.offset + this->packetInfo.length, kXMP_SeekFromStart ); + currentOffset = sourceRef->Offset(); + } + isMetadataWritten = true; + + } + else // DMT + { + ProcessTitle( sourceRef, tempRef, title, currentOffset, titleOffset ); + isTitleWritten = true; + } + } + // Else + // Would have already covered this case: TDM + + } + else // TMD, MDT, MTD + { + if ( isTitleWritten ) // TMD + { + ProcessDescription( sourceRef, tempRef, description, currentOffset, descOffset ); + isDescWritten = true; + } + else // MDT or MTD + { + if ( titleOffset.startOffset < descOffset.startOffset ) // MTD + { + ProcessTitle( sourceRef, tempRef, title, currentOffset, titleOffset ); + isTitleWritten = true; + + ProcessDescription( sourceRef, tempRef, description, currentOffset, descOffset ); + isDescWritten = true; + } + else // MDT + { + ProcessDescription( sourceRef, tempRef, description, currentOffset, descOffset ); + isDescWritten = true; + + ProcessTitle( sourceRef, tempRef, title, currentOffset, titleOffset ); + isTitleWritten = true; + } + } + } + + // Finally Everything would have been written + XMP_Enforce( isTitleWritten && isDescWritten && isMetadataWritten ); + XIO::Copy( sourceRef, tempRef, ( sourceRef->Length() - currentOffset ) ); + this->needsUpdate = false; + +} // SVG_MetaHandler::WriteTempFile diff --git a/XMPFiles/source/FileHandlers/SVG_Handler.hpp b/XMPFiles/source/FileHandlers/SVG_Handler.hpp new file mode 100644 index 0000000..4790a98 --- /dev/null +++ b/XMPFiles/source/FileHandlers/SVG_Handler.hpp @@ -0,0 +1,76 @@ +#ifndef __SVG_Handler_hpp__ +#define __SVG_Handler_hpp__ 1 + +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2015 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. +// +// This file includes implementation of SVG metadata, according to Scalable Vector Graphics (SVG) 1.1 Specification. +// "https://www.w3.org/TR/2003/REC-SVG11-20030114/" +// Copyright © 1994-2002 World Wide Web Consortium, (Massachusetts Institute of Technology, +// Institut National de Recherche en Informatique et en Automatique, Keio University). +// All Rights Reserved . http://www.w3.org/Consortium/Legal +// +// ================================================================================================= + +#include "public/include/XMP_Environment.h" // ! XMP_Environment.h must be the first included header. + +#include "public/include/XMP_Const.h" +#include "public/include/XMP_IO.hpp" + +#include "XMPFiles/source/XMPFiles_Impl.hpp" +#include "XMPFiles/source/FormatSupport/SVG_Adapter.hpp" + +extern XMPFileHandler* SVG_MetaHandlerCTor( XMPFiles* parent ); + +extern bool SVG_CheckFormat( XMP_FileFormat format, + XMP_StringPtr filePath, + XMP_IO * fileRef, + XMPFiles * parent ); + +static const XMP_OptionBits kSVG_HandlerFlags = ( kXMPFiles_CanInjectXMP | + kXMPFiles_CanExpand | + kXMPFiles_CanRewrite | + kXMPFiles_PrefersInPlace | + kXMPFiles_CanReconcile | + kXMPFiles_ReturnsRawPacket | + kXMPFiles_AllowsSafeUpdate ); + +class SVG_MetaHandler : public XMPFileHandler { + +public: + + void CacheFileData(); + void ProcessXMP(); + + void UpdateFile( bool doSafeUpdate ); + void WriteTempFile( XMP_IO* tempRef ); + + XMP_OptionBits GetSerializeOptions(); + + SVG_MetaHandler( XMPFiles* parent ); + virtual ~SVG_MetaHandler(); + +private: + + SVG_MetaHandler() {}; + SVG_Adapter * svgAdapter; + XML_NodePtr svgNode; + bool isTitleUpdateReq; + bool isDescUpdateReq; + + void ProcessTitle( XMP_IO* sourceRef, XMP_IO * destRef, const std::string &value, XMP_Int64 ¤tOffset, const OffsetStruct & titleOffset ); + void ProcessDescription( XMP_IO* sourceRef, XMP_IO * destRef, const std::string &value, XMP_Int64 ¤tOffset, const OffsetStruct & descOffset ); + void InsertNewTitle( XMP_IO * destRef, const std::string &value ); + void InsertNewDescription( XMP_IO * destRef, const std::string &value ); + void InsertNewMetadata( XMP_IO * destRef, const std::string &value ); + +}; // SVG_MetaHandler + +// ================================================================================================= + +#endif /* __SVG_Handler_hpp__ */ diff --git a/XMPFiles/source/FileHandlers/TIFF_Handler.cpp b/XMPFiles/source/FileHandlers/TIFF_Handler.cpp index a09b879..66075bd 100644 --- a/XMPFiles/source/FileHandlers/TIFF_Handler.cpp +++ b/XMPFiles/source/FileHandlers/TIFF_Handler.cpp @@ -272,8 +272,7 @@ void TIFF_MetaHandler::ProcessXMP() // Process the legacy metadata. if ( haveIPTC && (! haveXMP) && (iptcDigestState == kDigestMatches) ) iptcDigestState = kDigestMissing; - bool parseIPTC = (iptcDigestState != kDigestMatches) || (! readOnly); - if ( parseIPTC ) iptc.ParseMemoryDataSets ( iptcInfo.dataPtr, iptcInfo.dataLen ); + if (iptcInfo.dataLen) 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. diff --git a/XMPFiles/source/FileHandlers/XDCAMEX_Handler.cpp b/XMPFiles/source/FileHandlers/XDCAMEX_Handler.cpp index 826cc0b..74c7c00 100644 --- a/XMPFiles/source/FileHandlers/XDCAMEX_Handler.cpp +++ b/XMPFiles/source/FileHandlers/XDCAMEX_Handler.cpp @@ -624,12 +624,18 @@ void XDCAMEX_MetaHandler::GetTakeDuration ( const std::string & takeURI, std::st std::string takeDir ( takeURI ); takeDir.erase ( 0, 1 ); // Change the leading "//" to "/", then all '/' to kDirChar. +#if XMP_MacBuild +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunreachable-code" +#endif if ( kDirChar != '/' ) { for ( size_t i = 0, limit = takeDir.size(); i < limit; ++i ) { if ( takeDir[i] == '/' ) takeDir[i] = kDirChar; } } - +#if XMP_MacBuild +#pragma clang diagnostic pop +#endif std::string takePath ( this->rootPath ); takePath += kDirChar; takePath += "BPAV"; diff --git a/XMPFiles/source/FileHandlers/XDCAMFAM_Handler.cpp b/XMPFiles/source/FileHandlers/XDCAMFAM_Handler.cpp new file mode 100644 index 0000000..ded65c4 --- /dev/null +++ b/XMPFiles/source/FileHandlers/XDCAMFAM_Handler.cpp @@ -0,0 +1,677 @@ +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2015 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. +// ================================================================================================= + +// ================================================================================================= +// +// This handler will handle FAM/FTP variant of XDCAM. +// More information could be found in XDCAM_Handler.cpp +// +// ================================================================================================= + +#include "public/include/XMP_Environment.h" // ! XMP_Environment.h must be the first included header. + +#include "public/include/XMP_Const.h" +#include "public/include/XMP_IO.hpp" + +#include "XMPFiles/source/XMPFiles_Impl.hpp" +#include "source/XMPFiles_IO.hpp" +#include "source/XIO.hpp" +#include "source/IOUtils.hpp" + +#include "XMPFiles/source/FileHandlers/XDCAMFAM_Handler.hpp" +#include "XMPFiles/source/FormatSupport/XDCAM_Support.hpp" +#include "XMPFiles/source/FormatSupport/PackageFormat_Support.hpp" + +bool XDCAMFAM_CheckFormat ( XMP_FileFormat format, + const std::string & rootPath, + const std::string & groupName, + const std::string & parentName, + const std::string & leafName, + XMPFiles * parent ) +{ + /* isXDStyle = true Means SxS Memory or XDStyle + , = false Means Professional Disk */ + bool isXDStyle = false; + if ( (format != kXMP_XDCAM_FAMFile) && (format != kXMP_UnknownFile) ) return false; + if ( groupName.empty() != parentName.empty() ) return false; + + if ( groupName.empty() && ( Host_IO::GetChildMode ( rootPath.c_str(), "PROAV" ) == Host_IO::kFMode_IsFolder ) ) return false; + + std::string tempPath = rootPath; + + if ( !parentName.empty() ) + { + // Real Absolute Path exists + if ( ! ( parentName == "CLIP" || parentName == "SUB" || parentName == "LOCAL" ) ) + return false; + tempPath += kDirChar + groupName; + } + + // Some basic Checks + if ( Host_IO::GetChildMode ( tempPath.c_str(), "DISCMETA.XML" ) != Host_IO::kFMode_IsFile ) return false; + if ( Host_IO::GetChildMode( tempPath.c_str(), "MEDIAPRO.XML" ) != Host_IO::kFMode_IsFile ) return false; + if ( ( Host_IO::GetChildMode( tempPath.c_str(), "Take" ) == Host_IO::kFMode_IsFolder ) || ( Host_IO::GetChildMode( tempPath.c_str(), "Local" ) == Host_IO::kFMode_IsFolder ) ) + isXDStyle = true; + + // XDStyle can't have INDEX.XML + if ( isXDStyle && ( Host_IO::GetChildMode( tempPath.c_str(), "INDEX.XML" ) == Host_IO::kFMode_IsFile ) ) + return false; + // XDStyle can't have ALIAS.XML + if( isXDStyle && ( Host_IO::GetChildMode( tempPath.c_str(), "ALIAS.XML" ) == Host_IO::kFMode_IsFile ) ) + return false; + // Non-XDStyle can't have CUEUP.XML file + if( ( !isXDStyle ) && ( Host_IO::GetChildMode( tempPath.c_str(), "CUEUP.XML" ) == Host_IO::kFMode_IsFile ) ) + return false; + + // We will get metadata from NRT file inside Clip folder only + tempPath += kDirChar; + tempPath += "Clip"; + tempPath += kDirChar; + + std::string clipName = leafName; + size_t length = clipName.length(); + + // Proxy file support + if ( ( parentName == "SUB" ) ) + { + if( clipName.at( length - 3 ) != 'S' || ( ! IsDigit( clipName.at( length - 2 ) ) ) || ( ! IsDigit( clipName.at( length - 1 ) ) ) ) + return false; + clipName.erase( clipName.begin() + length - 3, clipName.end() ); + } + + tempPath += clipName; + + // .MXF file Existence with case sensitive is the new check inserted + std::string mxfPath = tempPath + ".MXF"; + if ( Host_IO::GetFileMode ( mxfPath.c_str() ) != Host_IO::kFMode_IsFile ) + { + mxfPath = tempPath + ".mxf"; + if ( Host_IO::GetFileMode ( mxfPath.c_str() ) != Host_IO::kFMode_IsFile ) + return false; + } + + tempPath += "M01.XML"; + if ( Host_IO::GetFileMode ( tempPath.c_str() ) != Host_IO::kFMode_IsFile ) + return false; + return true; + +} // XDCAMFAM_CheckFormat + +XMPFileHandler * XDCAMFAM_MetaHandlerCTor ( XMPFiles * parent ) +{ + return new XDCAMFAM_MetaHandler ( parent ); + +} // XDCAM_MetaHandlerCTor + +// ================================================================================================= +// XDCAMFAM_MetaHandler::XDCAMFAM_MetaHandler +// ==================================== +XDCAMFAM_MetaHandler::XDCAMFAM_MetaHandler ( XMPFiles * _parent ) : XDCAM_MetaHandler(_parent), isXDStyle( false ) +{ + this->handlerFlags = kXDCAMFAM_HandlerFlags; + // Setting the various path variables + this->SetPathVariables ( this->parent->GetFilePath() ); +} // XDCAMFAM_MetaHandler::XDCAMFAM_MetaHandler + + +// ================================================================================================= +// XDCAMFAM_MetaHandler::SetPathVariables +// ==================================== +void XDCAMFAM_MetaHandler::SetPathVariables ( const std::string & clientPath ) +{ + // No need to check for existing or non existing as would have been done at check file format if ForceGivenHandler flag is not provided + std::string tempPath = clientPath; + std::string parentName, GroupName; + std::string ignored; + + XIO::SplitLeafName ( &tempPath, &this->clipName ); + + this->rootPath = tempPath; + + if ( ! Host_IO::Exists( clientPath.c_str() ) ) + { + // Logical Path exists + // No need to extract extension as clipname is given without extension + if ( ( Host_IO::GetChildMode( tempPath.c_str(), "INDEX.XML" ) != Host_IO::kFMode_IsFile ) ) + this->isXDStyle = true; + tempPath += kDirChar; + tempPath += "Clip"; + XMP_Assert( Host_IO::GetFileMode( tempPath.c_str() ) == Host_IO::kFMode_IsFolder ); + } + else + { + // Real Absolute Path exists + XIO::SplitFileExtension ( &this->clipName, &ignored ); + XIO::SplitLeafName ( &tempPath, &parentName ); + if ( ( Host_IO::GetChildMode( tempPath.c_str(), "INDEX.XML" ) != Host_IO::kFMode_IsFile ) ) + this->isXDStyle = true; + this->rootPath = tempPath; + + size_t length = this->clipName.length(); + + // Proxy file support + if ( parentName == "Sub" ) + { + XMP_Assert( IsDigit( clipName.at( length - 2 ) ) && IsDigit( clipName.at( length - 1 ) ) ); + XMP_Assert( this->clipName.at( length - 3 ) == 'S' ); + this->clipName.erase( this->clipName.begin() + length - 3, clipName.end() ); + tempPath += kDirChar ; + tempPath += "Clip"; + } + else + tempPath += kDirChar + parentName; + } + + // Checks for Clip folder in XDCAM + XMP_Assert ( Host_IO::GetChildMode ( rootPath.c_str(), "Clip" ) == Host_IO::kFMode_IsFolder ); + + tempPath += kDirChar; + tempPath += this->clipName; + std::string mxfPath; + + // Case sensitive Extension support to check for clipname.MXF or clipname.mxf as already cover in Checkformat + if ( !( MakeClipFilePath( &mxfPath, ".MXF", true ) || MakeClipFilePath( &mxfPath, ".mxf", true ) ) ) + { + XMP_Error error( kXMPErr_FilePathNotAFile, "Clip MXF file must be exist" ); + NotifyClient( &this->parent->errorCallback, kXMPErrSev_FileFatal, error ); + } + + // NRT file Check as already cover in checkformat + if ( ! MakeClipFilePath ( &this->mNRTFilePath, "M01.XML", true ) ) + { + XMP_Error error( kXMPErr_FilePathNotAFile, "Clip NRT XML file must be exist" ); + NotifyClient( &this->parent->errorCallback, kXMPErrSev_FileFatal, error ); + } + + // Setting correct sidecar path + if ( this->isXDStyle || (Host_IO::GetChildMode ( rootPath.c_str(), "UserData" ) == Host_IO::kFMode_IsFolder ) ) + { + if ( ! ( MakeClipFilePath( &this->sidecarPath, ".xmp", true ) || MakeClipFilePath( &this->sidecarPath, ".XMP", true ) ) ) + this->sidecarPath = mxfPath + ".xmp"; + } + else + { + if ( ! ( MakeClipFilePath( &this->sidecarPath, "M01.XMP", true ) || MakeClipFilePath( &this->sidecarPath, "M01.xmp", true ) ) ) + this->sidecarPath = tempPath + "M01.XMP"; + } + +} // XDCAMFAM_MetaHandler::SetPathVariables + +// ================================================================================================= +// XDCAMFAM_MetaHandler::FillAssociatedResources +// ==================================== +void XDCAMFAM_MetaHandler::FillAssociatedResources( std::vector<std::string> * resourceList ) +{ + //Add RootPath + std::string filePath = rootPath + kDirChar; + PackageFormat_Support::AddResourceIfExists( resourceList, filePath ); + + // Get the files present directly inside root folder. + filePath = rootPath + kDirChar + "ALIAS.XML"; + PackageFormat_Support::AddResourceIfExists(resourceList, filePath); + + // INDEX.XML doesn't exist for XDStyle + if( ! this->isXDStyle ) + { + filePath = rootPath + kDirChar + "INDEX.XML"; + PackageFormat_Support::AddResourceIfExists(resourceList, filePath); + } + + filePath = rootPath + kDirChar + "DISCMETA.XML"; + PackageFormat_Support::AddResourceIfExists(resourceList, filePath); + + filePath = rootPath + kDirChar + "MEDIAPRO.XML"; + PackageFormat_Support::AddResourceIfExists(resourceList, filePath); + filePath = rootPath + kDirChar + "MEDIAPRO.BUP"; + PackageFormat_Support::AddResourceIfExists(resourceList, filePath); + + // CUEUP.XML don't exist for Professional Disk XDCAM + if( this->isXDStyle ) + { + filePath = rootPath + kDirChar + "CUEUP.XML"; + PackageFormat_Support::AddResourceIfExists(resourceList, filePath); + filePath = rootPath + kDirChar + "CUEUP.BUP"; + PackageFormat_Support::AddResourceIfExists(resourceList, filePath); + } + + // Add the UserData folder + filePath = rootPath + kDirChar + "UserData" + kDirChar; + PackageFormat_Support::AddResourceIfExists(resourceList, filePath); + + + // Get the files present inside clip folder. + XMP_VarString clipPath = rootPath + kDirChar + "Clip" + kDirChar ; + size_t oldCount = resourceList->size(); + + XMP_VarString regExp; + XMP_StringVector regExpVec; + + regExp = "^" + clipName + ".MXF$"; + regExpVec.push_back ( regExp ); + regExp = "^" + clipName + "M\\d\\d.XML$"; + regExpVec.push_back ( regExp ); + if ( this->isXDStyle ) + { + regExp = "^" + clipName + "R\\d\\d.BIM$"; + regExpVec.push_back ( regExp ); + } + else + { + regExp = "^" + clipName + "M\\d\\d.KLV$"; + regExpVec.push_back ( regExp ); + } + IOUtils::GetMatchingChildren ( *resourceList, clipPath, regExpVec, false, true, true ); + PackageFormat_Support::AddResourceIfExists( resourceList, this->sidecarPath); + if ( resourceList->size() <= oldCount ) + { + PackageFormat_Support::AddResourceIfExists(resourceList, clipPath); + } + + //Get the files Under Sub folder + clipPath = rootPath + kDirChar + "Sub" + kDirChar ; + regExpVec.clear(); + regExp = "^" + clipName + "S\\d\\d.MXF$"; + regExpVec.push_back ( regExp ); + oldCount = resourceList->size(); + IOUtils::GetMatchingChildren ( *resourceList, clipPath, regExpVec, false, true, true ); + // Add Sub folder if no file inside this, was added. + if ( resourceList->size() <= oldCount ) + { + PackageFormat_Support::AddResourceIfExists(resourceList, clipPath); + } + + // Get the files Under Local folder if it is XDStyle + if ( isXDStyle ) + { + clipPath = rootPath + kDirChar + "Local" + kDirChar ; + regExpVec.clear(); + // ClipInfo file + regExp = "^" + clipName + "C\\d\\d.SMI$"; + regExpVec.push_back ( regExp ); + // Picture pointer file + regExp = "^" + clipName + "I\\d\\d.PPN$"; + regExpVec.push_back ( regExp ); + oldCount = resourceList->size(); + IOUtils::GetMatchingChildren ( *resourceList, clipPath, regExpVec, false, true, true ); + // Add Local folder if no file inside this, was added. + if ( resourceList->size() <= oldCount ) + { + PackageFormat_Support::AddResourceIfExists(resourceList, clipPath); + } + } + + // Add the Edit lists associated to this clip + XMP_StringVector editInfoList; + bool atLeastOneFileAdded = false; + clipPath = rootPath + kDirChar + "Edit" + kDirChar ; + if ( GetInfoFiles ( editInfoList , clipPath ) ) + { + size_t noOfEditInfoFiles = editInfoList.size() ; + for( size_t count = 0; count < noOfEditInfoFiles; count++ ) + { + atLeastOneFileAdded = PackageFormat_Support::AddResourceIfExists(resourceList, editInfoList[count]) ? true : atLeastOneFileAdded; + + XMP_VarString editNRTFile = editInfoList[count] ; + size_t filenamelen = editNRTFile.length() ; + if ( editNRTFile[ filenamelen - 7 ] == 'E' + && IsDigit( editNRTFile[ filenamelen - 6 ] ) + && IsDigit( editNRTFile[ filenamelen - 5 ] ) ) + { + editNRTFile.erase( editNRTFile.begin() + filenamelen - 7, editNRTFile.end() ) ; + } + else + { + editNRTFile.erase( editNRTFile.begin() + filenamelen - 4, editNRTFile.end() ) ; + } + + XMP_VarString fileName; + size_t pos = editNRTFile.find_last_of ( kDirChar ); + fileName = editNRTFile.substr ( pos + 1 ); + XMP_VarString regExp = "^" + fileName + "M\\d\\d.XML$"; + oldCount = resourceList->size(); + IOUtils::GetMatchingChildren ( *resourceList, clipPath, regExp, false, true, true ); + atLeastOneFileAdded = resourceList->size() > oldCount; + + } + } + // Add Edit folder if no file inside this, was added. + if ( !atLeastOneFileAdded ) + { + PackageFormat_Support::AddResourceIfExists(resourceList, clipPath); + } + + atLeastOneFileAdded = false; + + // Add the Takes associated to this clip, + // Take folder exists only for XDStyle + if( this->isXDStyle ) + { + XMP_StringVector takeList; + clipPath = rootPath + kDirChar + "Take" + kDirChar ; + if( GetInfoFiles ( takeList , clipPath ) ) + { + size_t noOfTakes = takeList.size() ; + for( size_t count = 0; count < noOfTakes; count++ ) + { + atLeastOneFileAdded = PackageFormat_Support::AddResourceIfExists(resourceList, takeList[count]) ? true : atLeastOneFileAdded; + XMP_VarString takeNRTFile = takeList[count] ; + size_t filenamelen = takeList[count].length() ; + if ( takeNRTFile[ filenamelen - 7 ] == 'U' + && IsDigit( takeNRTFile[ filenamelen - 6 ] ) + && IsDigit( takeNRTFile[ filenamelen - 5 ] ) ) + { + takeNRTFile.erase( takeNRTFile.begin() + filenamelen - 7, takeNRTFile.end() ) ; + } + else + { + takeNRTFile.erase( takeNRTFile.begin() + filenamelen - 4, takeNRTFile.end() ) ; + } + + XMP_VarString fileName; + size_t pos = takeNRTFile.find_last_of ( kDirChar ); + fileName = takeNRTFile.substr ( pos + 1 ); + XMP_VarString regExp = "^" + fileName + "M\\d\\d.XML$"; + oldCount = resourceList->size(); + IOUtils::GetMatchingChildren ( *resourceList, clipPath, regExp, false, true, true ); + atLeastOneFileAdded = resourceList->size() > oldCount; + } + } + // Add Take folder if no file inside this, was added. + if(!atLeastOneFileAdded) + { + filePath = rootPath + kDirChar + "Take" + kDirChar; + PackageFormat_Support::AddResourceIfExists(resourceList, filePath); + } + } + + // Add the Planning Metadata Files associated to this clip + // Planning Metadata exist for both SxS and Professional Disk + XMP_StringVector planList; + clipPath = rootPath + kDirChar + "General" + kDirChar + "Sony" + kDirChar+ "Planning" + kDirChar; + if( GetPlanningFiles ( planList , clipPath ) ) + { + size_t noOfPlans = planList.size() ; + for( size_t count = 0; count < noOfPlans; count++ ) + { + resourceList->push_back( planList[count] ); + } + } +} // XDCAMFAM_MetaHandler::FillAssociatedResources + +// ================================================================================================= +// XDCAMFAM_MetaHandler::MakeClipFilePath +// ==================================== +bool XDCAMFAM_MetaHandler::MakeClipFilePath ( std::string * path, XMP_StringPtr suffix, bool checkFile /* = false */ ) +{ + + *path = this->rootPath; + *path += kDirChar; + + *path += "Clip"; // ! Yes, mixed case. + + *path += kDirChar; + *path += this->clipName; + *path += suffix; + + if ( ! checkFile ) return true; + return Host_IO::Exists ( path->c_str() ); + +} // XDCAMFAM_MetaHandler::MakeClipFilePath + +// ================================================================================================= +// XDCAMFAM_MetaHandler::MakeLocalFilePath +// ==================================== +bool XDCAMFAM_MetaHandler::MakeLocalFilePath ( std::string * path, XMP_Uns8 fileType, bool checkFile /* = false */ ) +{ + + *path = this->rootPath; + *path += kDirChar; + + *path += "Local"; // ! Yes, mixed case. + + *path += kDirChar; + *path += this->clipName; + + if( fileType == k_LocalPPNFile ) + *path += "I01.PPN"; + else if ( fileType == k_LocalClipInfoFile ) + *path += "S01.SMI"; + else + return false; + + if ( ! checkFile ) return true; + return Host_IO::Exists ( path->c_str() ); + +} // XDCAMFAM_MetaHandler::MakeLocalFilePath + + +// ================================================================================================= +// XDCAMFAM_MetaHandler::GetMediaProMetadata +// ==================================== +bool XDCAMFAM_MetaHandler::GetMediaProMetadata ( SXMPMeta * xmpObjPtr, + const std::string& clipUMID, + bool digestFound ) +{ + // Build a directory string to the MEDIAPRO file. + std::string mediaproPath; + MakeMediaproPath ( &mediaproPath ); + return XDCAM_Support::GetMediaProLegacyMetadata ( xmpObjPtr, clipUMID, mediaproPath, digestFound ); +} // XDCAMFAM_MetaHandler::GetMediaProMetadata + +// ================================================================================================= +// XDCAMFAM_MetaHandler::GetPlanningFiles +// ==================================== +bool XDCAMFAM_MetaHandler::GetPlanningFiles ( std::vector<std::string> &planInfoList, std::string pathToFolder) +{ + std::string clipUmid; + bool found = false; + + if( GetClipUmid ( clipUmid ) ) + { + if ( Host_IO::Exists( pathToFolder.c_str() ) && + Host_IO::GetFileMode( pathToFolder.c_str() ) == Host_IO::kFMode_IsFolder + ) + { + Host_IO::AutoFolder planFolder; + std::string listChild; + + planFolder.folder = Host_IO::OpenFolder ( pathToFolder.c_str() ); + while ( Host_IO::GetNextChild ( planFolder.folder, &listChild ) ) { + size_t filenamelen = listChild.size(); + std::string listFilePath = pathToFolder + listChild ; + if ( ! ( filenamelen > 4 && + ( listChild.compare ( filenamelen - 4, 4 , ".XML" ) == 0 + || + listChild.compare ( filenamelen - 4, 4 , ".xml" ) == 0 + ) + && + Host_IO::GetFileMode( listFilePath.c_str() ) == Host_IO::kFMode_IsFile + ) ) continue; + if( IsClipsPlanning ( clipUmid , listFilePath.c_str() ) ) + { + found = true ; + planInfoList.push_back( listFilePath ); + } + } + } + } + return found; +} // XDCAMFAM_MetaHandler::GetPlanningFiles + +// ================================================================================================= +// XDCAMFAM_MetaHandler::IsClipsPlanning +// ==================================== +bool XDCAMFAM_MetaHandler::IsClipsPlanning ( std::string clipUmid , XMP_StringPtr planPath ) +{ + ExpatAdapter* planniingExpat = 0 ; + XMP_StringPtr nameSpace = 0 ; + try { + readXMLFile( planPath, planniingExpat ); + if ( planniingExpat != 0 ) + { + XML_Node & xmlTree = planniingExpat->tree; + XML_NodePtr rootElem = 0; + + for ( size_t i = 0, limit = xmlTree.content.size(); i < limit; ++i ) { + if ( xmlTree.content[i]->kind == kElemNode ) { + rootElem = xmlTree.content[i]; + } + } + if ( rootElem != 0 ) + { + XMP_StringPtr rootLocalName = rootElem->name.c_str() + rootElem->nsPrefixLen; + + if ( XMP_LitMatch ( rootLocalName, "PlanningMetadata" ) ) + { + nameSpace = rootElem->ns.c_str() ; + size_t noOfMaterialGroups = rootElem->CountNamedElements ( nameSpace, "MaterialGroup" ) ; + while( noOfMaterialGroups-- ) + { + XML_NodePtr mgNode = rootElem->GetNamedElement( nameSpace, "MaterialGroup" ); + size_t noOfMaterialElements = mgNode->CountNamedElements ( nameSpace, "Material" ) ; + while( noOfMaterialElements-- ) + { + XML_NodePtr materialNode = mgNode->GetNamedElement( nameSpace, "Material" ); + XMP_StringPtr materialType = materialNode->GetAttrValue ( "type" ); + if ( materialType && XMP_LitMatch( materialType , "clip" ) ) + { + XMP_StringPtr umidValue = materialNode->GetAttrValue ( "umidRef" ); + if ( umidValue != 0 && XMP_LitMatch( umidValue , clipUmid.c_str() ) ) + { + delete ( planniingExpat ) ; + return true; + } + } + + } + } + } + } + } + + } catch ( ... ) { + } + delete ( planniingExpat ) ; + return false; +} // XDCAMFAM_MetaHandler::IsClipsPlanning + +// ================================================================================================= +// XDCAMFAM_MetaHandler::GetInfoFiles +// ==================================== +bool XDCAMFAM_MetaHandler::GetInfoFiles ( std::vector<std::string> &infoList, std::string pathToFolder) +{ + std::string clipUmid; + bool found = false; + + if( GetClipUmid ( clipUmid ) ) + { + if ( Host_IO::Exists( pathToFolder.c_str() ) && + Host_IO::GetFileMode( pathToFolder.c_str() ) == Host_IO::kFMode_IsFolder + ) + { + Host_IO::AutoFolder infoFolder; + std::string listChild; + + infoFolder.folder = Host_IO::OpenFolder ( pathToFolder.c_str() ); + while ( Host_IO::GetNextChild ( infoFolder.folder, &listChild ) ) { + size_t filenamelen = listChild.size(); + std::string listFilePath = pathToFolder + listChild ; + if ( ! ( filenamelen > 7 && + listChild.compare ( filenamelen - 4, 4 , ".SMI" ) == 0 && + Host_IO::GetFileMode( listFilePath.c_str() ) == Host_IO::kFMode_IsFile + ) ) continue; + if( RefersClipUmid ( clipUmid , listFilePath.c_str() ) ) + { + found = true ; + infoList.push_back( listFilePath ); + } + } + } + } + return found; +} // XDCAMFAM_MetaHandler::GetInfoFiles + +// ================================================================================================= +// XDCAMFAM_MetaHandler::GetClipUmid +// ============================== +bool XDCAMFAM_MetaHandler::GetClipUmid ( std::string &clipUmid ) +{ + std::string clipInfoPath; + ExpatAdapter* clipInfoExpat = 0 ; + bool umidFound = false; + XMP_StringPtr nameSpace = 0; + try { + if ( this->MakeLocalFilePath ( &clipInfoPath, k_LocalClipInfoFile, true ) ) + { + readXMLFile( clipInfoPath.c_str(), clipInfoExpat ); + if ( clipInfoExpat != 0 ) + { + XML_Node & xmlTree = clipInfoExpat->tree; + XML_NodePtr rootElem = 0; + + for ( size_t i = 0, limit = xmlTree.content.size(); i < limit; ++i ) { + if ( xmlTree.content[i]->kind == kElemNode ) { + rootElem = xmlTree.content[i]; + } + } + if ( rootElem != 0 ) + { + XMP_StringPtr rootLocalName = rootElem->name.c_str() + rootElem->nsPrefixLen; + + if ( XMP_LitMatch ( rootLocalName, "smil" ) ) + { + XMP_StringPtr umidValue = rootElem->GetAttrValue ( "umid" ); + if ( umidValue != 0 ) { + clipUmid = umidValue; + umidFound = true; + } + } + } + } + } + if( ! umidFound ) + { //try to get the umid from the NRT metadata + delete ( clipInfoExpat ) ; clipInfoExpat = 0; + this->MakeClipFilePath ( &clipInfoPath, "M01.XML" ) ; + readXMLFile( clipInfoPath.c_str(), clipInfoExpat ) ; + if ( clipInfoExpat != 0 ) + { + XML_Node & xmlTree = clipInfoExpat->tree; + XML_NodePtr rootElem = 0; + for ( size_t i = 0, limit = xmlTree.content.size(); i < limit; ++i ) { + if ( xmlTree.content[i]->kind == kElemNode ) { + rootElem = xmlTree.content[i]; + } + } + if ( rootElem != 0 ) + { + XMP_StringPtr rootLocalName = rootElem->name.c_str() + rootElem->nsPrefixLen; + + if ( XMP_LitMatch ( rootLocalName, "NonRealTimeMeta" ) ) + { + nameSpace = rootElem->ns.c_str() ; + XML_NodePtr targetProp = rootElem->GetNamedElement ( nameSpace, "TargetMaterial" ); + if ( (targetProp != 0) && targetProp->IsEmptyLeafNode() ) { + XMP_StringPtr umidValue = targetProp->GetAttrValue ( "umidRef" ); + if ( umidValue != 0 ) { + clipUmid = umidValue; + umidFound = true; + } + } + } + } + } + } + } catch ( ... ) { + } + delete ( clipInfoExpat ) ; + return umidFound; +} // XDCAMFAM_MetaHandler::GetClipUmid + +// ================================================================================================= diff --git a/XMPFiles/source/FileHandlers/XDCAMFAM_Handler.hpp b/XMPFiles/source/FileHandlers/XDCAMFAM_Handler.hpp new file mode 100644 index 0000000..b53e55b --- /dev/null +++ b/XMPFiles/source/FileHandlers/XDCAMFAM_Handler.hpp @@ -0,0 +1,71 @@ +#ifndef __XDCAMFAM_Handler_hpp__ +#define __XDCAMFAM_Handler_hpp__ 1 + +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2015 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "public/include/XMP_Environment.h" // ! This must be the first include. +#include "XMPFiles/source/FileHandlers/XDCAM_Handler.hpp" + + +extern XMPFileHandler * XDCAMFAM_MetaHandlerCTor ( XMPFiles * parent ); + +extern bool XDCAMFAM_CheckFormat ( XMP_FileFormat format, + const std::string & rootPath, + const std::string & gpName, + const std::string & parentName, + const std::string & leafName, + XMPFiles * parent ); + +static const XMP_OptionBits kXDCAMFAM_HandlerFlags = (kXMPFiles_CanInjectXMP | + kXMPFiles_CanExpand | + kXMPFiles_CanRewrite | + kXMPFiles_PrefersInPlace | + kXMPFiles_CanReconcile | + kXMPFiles_AllowsOnlyXMP | + kXMPFiles_ReturnsRawPacket | + kXMPFiles_HandlerOwnsFile | + kXMPFiles_AllowsSafeUpdate | + kXMPFiles_FolderBasedFormat); + + +class XDCAMFAM_MetaHandler : public XDCAM_MetaHandler +{ + +public: + + void FillAssociatedResources ( std::vector<std::string> * resourceList ); + XDCAMFAM_MetaHandler ( XMPFiles * _parent ); + virtual ~XDCAMFAM_MetaHandler() { }; + +private: + + bool isXDStyle; + + bool MakeClipFilePath ( std::string * path, XMP_StringPtr suffix, bool checkFile = false ); + void SetPathVariables ( const std::string & clientPath ); + bool GetMediaProMetadata ( SXMPMeta * xmpObjPtr, const std::string& clipUMID, bool digestFound ); + bool GetInfoFiles ( std::vector<std::string> &infoList, std::string pathToFolder) ; + bool GetPlanningFiles ( std::vector<std::string> &planInfoList, std::string pathToFolder) ; + bool IsClipsPlanning ( std::string clipUmid , XMP_StringPtr planPath ) ; + bool GetClipUmid ( std::string &clipUmid ); + bool MakeLocalFilePath ( std::string * path, XMP_Uns8 fileType, bool checkFile = false ); + + XDCAMFAM_MetaHandler() : XDCAM_MetaHandler() {}; // Hidden on purpose. + + enum + { + k_LocalPPNFile, + k_LocalClipInfoFile + }; + +}; // XDCAMFAM_MetaHandler + +// ================================================================================================= +#endif /* __XDCAMFAM_Handler_hpp__ */ diff --git a/XMPFiles/source/FileHandlers/XDCAMSAM_Handler.cpp b/XMPFiles/source/FileHandlers/XDCAMSAM_Handler.cpp new file mode 100644 index 0000000..f0303df --- /dev/null +++ b/XMPFiles/source/FileHandlers/XDCAMSAM_Handler.cpp @@ -0,0 +1,429 @@ +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2015 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "public/include/XMP_Environment.h" // ! XMP_Environment.h must be the first included header. + +#include "public/include/XMP_Const.h" +#include "public/include/XMP_IO.hpp" + +#include "XMPFiles/source/XMPFiles_Impl.hpp" +#include "source/XMPFiles_IO.hpp" +#include "source/XIO.hpp" +#include "source/IOUtils.hpp" + +#include "XMPFiles/source/FileHandlers/XDCAMSAM_Handler.hpp" +#include "XMPFiles/source/FormatSupport/XDCAM_Support.hpp" +#include "XMPFiles/source/FormatSupport/PackageFormat_Support.hpp" + +bool XDCAMSAM_CheckFormat ( XMP_FileFormat format, + const std::string & rootPath, + const std::string & groupName, + const std::string & parentName, + const std::string & leafName, + XMPFiles * parent ) +{ + // We only support file in CLPR folder not in other folders + + if ( ( format != kXMP_XDCAM_SAMFile ) && ( format != kXMP_UnknownFile ) ) return false; + + // parentName or groupName empty means Logical path exists + if ( groupName.empty() != parentName.empty() ) return false; + + std::string tempPath = rootPath; + std::string clipName = leafName; + + if ( groupName.empty() ) + { + // Logical clip exists + tempPath += kDirChar; + tempPath += "PROAV"; + + // Simple checks to ensure presence or absence of Management files or CLPR folder + if ( Host_IO::GetChildMode ( tempPath.c_str(), "INDEX.XML" ) != Host_IO::kFMode_IsFile ) return false; + if ( Host_IO::GetChildMode ( tempPath.c_str(), "DISCMETA.XML" ) != Host_IO::kFMode_IsFile ) return false; + if ( Host_IO::GetChildMode ( tempPath.c_str(), "DISCINFO.XML" ) != Host_IO::kFMode_IsFile ) return false; + if ( Host_IO::GetChildMode ( tempPath.c_str(), "CLPR" ) != Host_IO::kFMode_IsFolder ) return false; + if ( Host_IO::GetChildMode( tempPath.c_str(), "MEDIAPRO.XML" ) == Host_IO::kFMode_IsFile ) return false; + tempPath += kDirChar; + tempPath += "CLPR"; + tempPath += kDirChar + leafName; + } + else if ( groupName == "CLPR" ) + { + // XMP provides support only to files inside CLPR + // Simple checks to ensure presence or absence of Management files + if ( Host_IO::GetChildMode ( tempPath.c_str(), "INDEX.XML" ) != Host_IO::kFMode_IsFile ) return false; + if ( Host_IO::GetChildMode ( tempPath.c_str(), "DISCMETA.XML" ) != Host_IO::kFMode_IsFile ) return false; + if ( Host_IO::GetChildMode ( tempPath.c_str(), "DISCINFO.XML" ) != Host_IO::kFMode_IsFile ) return false; + if ( ( Host_IO::GetChildMode( tempPath.c_str(), "MEDIAPRO.XML" ) == Host_IO::kFMode_IsFile ) ) return false; + + tempPath += kDirChar + groupName; + tempPath += kDirChar + parentName; + size_t length = clipName.length(); + const char fileType = clipName.at ( length - 3 ); + if ( IsDigit( clipName.at( length - 1 ) ) && IsDigit( clipName.at( length - 2 ) ) ) + { + // Last 3rd characater shows what is the file type + switch ( fileType ) + { + case 'A' : // Audio + case 'C' : // ClipInfo + case 'I' : // Picture pointer + case 'M' : // NRT + case 'R' : // Real Time + case 'S' : // Sub (Proxy) + case 'V' : // Video + break; + default: // Unknown + return false; + } + clipName.erase ( clipName.begin() + length - 3, clipName.end() ); + } + } + else + return false; + tempPath += kDirChar + clipName; + tempPath += "M01.XML"; + + // Checking for NRT file + if ( Host_IO::GetFileMode ( tempPath.c_str() ) != Host_IO::kFMode_IsFile ) + return false; + + return true; + +} // XDCAMSAM_CheckFormat + +XMPFileHandler * XDCAMSAM_MetaHandlerCTor ( XMPFiles * parent ) +{ + return new XDCAMSAM_MetaHandler ( parent ); +} // XDCAMSAM_MetaHandlerCTor + + +// ================================================================================================= +// XDCAMSAM_MetaHandler::XDCAMSAM_MetaHandler +// ==================================== +XDCAMSAM_MetaHandler::XDCAMSAM_MetaHandler ( XMPFiles * _parent ) : XDCAM_MetaHandler(_parent) +{ + this->handlerFlags = kXDCAMSAM_HandlerFlags; + // Setting the various path variables + this->SetPathVariables ( this->parent->GetFilePath() ); + +} // XDCAMSAM_MetaHandler::XDCAMSAM_MetaHandler + +// ================================================================================================= +// XDCAMSAM_MetaHandler::SetPathVariables +// ==================================== +void XDCAMSAM_MetaHandler::SetPathVariables ( const std::string & clientPath ) +{ + // No need to check for existing or non existing as would have been done at check file format if ForceGivenHandler flag is not provided + std::string tempPath = clientPath; + std::string parentName, groupName; + std::string ignored; + + std::string leafName; + XIO::SplitLeafName ( &tempPath, &leafName ); + + if ( ! Host_IO::Exists( clientPath.c_str() ) ) + { + // logical path exists + // No need to extract extension as clipname is given without extension + this->rootPath = tempPath; + tempPath += kDirChar; + tempPath += "PROAV"; + + XMP_Assert ( Host_IO::GetChildMode( tempPath.c_str(), "MEDIAPRO.XML" ) != Host_IO::kFMode_IsFile ); + + tempPath += kDirChar; + tempPath += "CLPR"; + tempPath += kDirChar + leafName; + } + else + { + // Real Absolute Path exists + XIO::SplitFileExtension ( &leafName, &ignored ); + + XIO::SplitLeafName ( &tempPath, &parentName ); + XIO::SplitLeafName ( &tempPath, &groupName ); + + std::string proav; + XIO::SplitLeafName ( &tempPath, &proav ); + XMP_Assert ( proav == "PROAV" ); + + this->rootPath = tempPath; + + + XMP_Assert ( groupName == "CLPR" ); + + // Real Path may be ..PROAV//CLPR//clipName//clipname.MXF + // So XMP check for ..PROAV//CLPR//clipName//clipNameM01.xml + // XNP sidecar file will be ..PROAV//CLPR//clipName//clipNameM01.XMP + size_t length = leafName.length(); + + XMP_Assert ( IsDigit( leafName.at( length - 2 ) ) && IsDigit( leafName.at( length - 1 ) ) ); + // Last 3rd character of file will inform us about its type + const char fileType = leafName.at( length - 3 ); + + // A = Audio, C = ClipInfo, I = Picture Pointer, M = Non-Realtime, R = Realtime, S = Sub (Proxy), V = Video + XMP_Assert ( fileType == 'A' || fileType == 'C' || fileType == 'I' || fileType == 'M' || fileType == 'R' || fileType == 'S' || fileType == 'V' ); + + leafName.erase ( leafName.begin() + length - 3, leafName.end() ); + + tempPath += kDirChar + proav; + tempPath += kDirChar + groupName; + tempPath += kDirChar + parentName; + } + + this->clipName = leafName; + + + tempPath += kDirChar; + tempPath += leafName; + + // NRT file Check as already cover in checkformat + if ( ! MakeClipFilePath( &this->mNRTFilePath, "M01.XML", true ) ) + { + XMP_Error error( kXMPErr_FilePathNotAFile, "Clip NRT XML file must be exist" ); + NotifyClient( &this->parent->errorCallback, kXMPErrSev_FileFatal, error ); + } + + // Setting correct sidecar path covering both .XMP and .xmp + if ( ! ( MakeClipFilePath( &this->sidecarPath, "M01.XMP", true ) || MakeClipFilePath( &this->sidecarPath, "M01.xmp", true ) ) ) + this->sidecarPath = tempPath + "M01.XMP"; + +} // XDCAMSAM_MetaHandler::SetPathVariables + +// ================================================================================================= +// XDCAMSAM_MetaHandler::FillAssociatedResources +// ==================================== +void XDCAMSAM_MetaHandler::FillAssociatedResources( std::vector<std::string> * resourceList ) +{ + + std::string proavPath = rootPath + kDirChar + "PROAV" + kDirChar; + std::string filePath; + + //Add RootPath + filePath = rootPath + kDirChar; + PackageFormat_Support::AddResourceIfExists( resourceList, filePath ); + + // Get the files present directly inside PROAV folder. + filePath = proavPath + "INDEX.XML"; + PackageFormat_Support::AddResourceIfExists(resourceList, filePath); + filePath = proavPath + "INDEX.BUP"; + PackageFormat_Support::AddResourceIfExists(resourceList, filePath); + + filePath = proavPath + "DISCINFO.XML"; + PackageFormat_Support::AddResourceIfExists(resourceList, filePath); + filePath = proavPath + "DISCINFO.BUP"; + PackageFormat_Support::AddResourceIfExists(resourceList, filePath); + + filePath = proavPath + "DISCMETA.XML"; + PackageFormat_Support::AddResourceIfExists(resourceList, filePath); + + // Covering files in clipname folder in CLPR folder + XMP_VarString clipPath = proavPath + "CLPR" + kDirChar + clipName + kDirChar; + XMP_VarString regExp; + XMP_StringVector regExpVec; + + // ClipInfo file + regExp = "^" + clipName + "C\\d\\d.SMI$"; + regExpVec.push_back ( regExp ); + // Non-Real time metadata file + regExp = "^" + clipName + "M\\d\\d.XML$"; + regExpVec.push_back ( regExp ); + // Video file + regExp = "^" + clipName + "V\\d\\d.MXF$"; + regExpVec.push_back ( regExp ); + // Audio File + regExp = "^" + clipName + "A\\d\\d.MXF$"; + regExpVec.push_back ( regExp ); + // Real time metadata file + regExp = "^" + clipName + "R\\d\\d.BIM$"; + regExpVec.push_back ( regExp ); + // Picture pointer file + regExp = "^" + clipName + "I\\d\\d.PPN$"; + regExpVec.push_back ( regExp ); + // Sub(Proxy) files + regExp = "^" + clipName + "S\\d\\d.MXF$"; + regExpVec.push_back ( regExp ); + IOUtils::GetMatchingChildren ( *resourceList, clipPath, regExpVec, false, true, true ); + + // Adding sidecar file if exixts + PackageFormat_Support::AddResourceIfExists(resourceList, this->sidecarPath); + + // Add the Edit lists that refer this clip + std::vector<std::string> editInfoList; + if( GetEditInfoFiles ( editInfoList ) ) + { + size_t noOfEditInfoFiles = editInfoList.size() ; + for( size_t count = 0; count < noOfEditInfoFiles; count++ ) + { + PackageFormat_Support::AddResourceIfExists(resourceList, editInfoList[count]); + std::string editNRTFile = editInfoList[count].c_str() ; + size_t filenamelen = editInfoList[count].length() ; + editNRTFile[ filenamelen - 7 ] = 'M'; + editNRTFile[ filenamelen - 3 ] = 'X'; + editNRTFile[ filenamelen - 2 ] = 'M'; + editNRTFile[ filenamelen - 1 ] = 'L'; + PackageFormat_Support::AddResourceIfExists(resourceList, editNRTFile ); + } + } +} // XDCAMSAM_MetaHandler::FillAssociatedResources + +// ================================================================================================= +// XDCAMSAM_MetaHandler::MakeClipFilePath +// ==================================== +bool XDCAMSAM_MetaHandler::MakeClipFilePath ( std::string * path, XMP_StringPtr suffix, bool checkFile /* = false */ ) +{ + *path = this->rootPath; + *path += kDirChar; + *path += "PROAV"; + *path += kDirChar; + + *path += "CLPR"; // ! Yes, mixed case. + + *path += kDirChar; + *path += this->clipName; + *path += kDirChar; + *path += this->clipName; + *path += suffix; + + if ( ! checkFile ) return true; + return Host_IO::Exists ( path->c_str() ); + +} // XDCAMSAM_MetaHandler::MakeClipFilePath + +// ================================================================================================= +// XDCAMSAM_MetaHandler::GetEditInfoFiles +// ==================================== +bool XDCAMSAM_MetaHandler::GetEditInfoFiles ( std::vector<std::string> &editInfoList ) +{ + std::string clipUmid; + bool found = false; + + if( GetClipUmid ( clipUmid ) ) + { + std::string editFolderPath = this->rootPath + kDirChar + "PROAV" + kDirChar + "EDTR" + kDirChar ; + if ( Host_IO::Exists( editFolderPath.c_str() ) && + Host_IO::GetFileMode( editFolderPath.c_str() ) == Host_IO::kFMode_IsFolder + ) + { + Host_IO::AutoFolder edtrFolder, editFolder; + std::string edtrChildName, edlistChild; + + edtrFolder.folder = Host_IO::OpenFolder ( editFolderPath.c_str() ); + while ( Host_IO::GetNextChild ( edtrFolder.folder, &edtrChildName ) ) { + size_t childLen = edtrChildName.size(); + std::string editListFolderPath = editFolderPath + edtrChildName + kDirChar ; + if ( ! ( childLen == 5 && + edtrChildName[0] == 'E' && + IsDigit( edtrChildName[1] ) && + IsDigit( edtrChildName[2] ) && + IsDigit( edtrChildName[3] ) && + IsDigit( edtrChildName[4] ) && + Host_IO::GetFileMode( editListFolderPath.c_str() ) == Host_IO::kFMode_IsFolder + ) ) continue; + + editFolder.folder = Host_IO::OpenFolder ( editListFolderPath.c_str() ); + while ( Host_IO::GetNextChild ( editFolder.folder, &edlistChild ) ) { + size_t filenamelen = edlistChild.size(); + std::string editListFilePath = editListFolderPath + edlistChild ; + if ( ! ( filenamelen == 12 && + edlistChild.compare ( filenamelen - 4, 4 , ".SMI" ) == 0 && + edlistChild.compare ( 0, edtrChildName.size(), edtrChildName ) == 0 && + Host_IO::GetFileMode( editListFilePath.c_str() ) == Host_IO::kFMode_IsFile + ) ) continue; + if( RefersClipUmid ( clipUmid , editListFilePath.c_str() ) ) + { + found = true ; + editInfoList.push_back( editListFilePath ); + } + } + } + } + } + return found; +} // XDCAMSAM_MetaHandler::GetEditInfoFiles + +// ================================================================================================= +// XDCAMSAM_MetaHandler::GetClipUmid +// ============================== +bool XDCAMSAM_MetaHandler::GetClipUmid ( std::string &clipUmid ) +{ + std::string clipInfoPath; + ExpatAdapter* clipInfoExpat = 0 ; + bool umidFound = false; + XMP_StringPtr nameSpace = 0; + try { + this->MakeClipFilePath ( &clipInfoPath, "C01.SMI" ) ; + readXMLFile( clipInfoPath.c_str(), clipInfoExpat ); + if ( clipInfoExpat != 0 ) + { + XML_Node & xmlTree = clipInfoExpat->tree; + XML_NodePtr rootElem = 0; + + for ( size_t i = 0, limit = xmlTree.content.size(); i < limit; ++i ) { + if ( xmlTree.content[i]->kind == kElemNode ) { + rootElem = xmlTree.content[i]; + } + } + if ( rootElem != 0 ) + { + XMP_StringPtr rootLocalName = rootElem->name.c_str() + rootElem->nsPrefixLen; + + if ( XMP_LitMatch ( rootLocalName, "smil" ) ) + { + XMP_StringPtr umidValue = rootElem->GetAttrValue ( "umid" ); + if ( umidValue != 0 ) { + clipUmid = umidValue; + umidFound = true; + } + } + } + } + if( ! umidFound ) + { //try to get the umid from the NRT metadata + delete ( clipInfoExpat ) ; clipInfoExpat = 0; + this->MakeClipFilePath ( &clipInfoPath, "M01.XML" ) ; + readXMLFile( clipInfoPath.c_str(), clipInfoExpat ) ; + if ( clipInfoExpat != 0 ) + { + XML_Node & xmlTree = clipInfoExpat->tree; + XML_NodePtr rootElem = 0; + for ( size_t i = 0, limit = xmlTree.content.size(); i < limit; ++i ) { + if ( xmlTree.content[i]->kind == kElemNode ) { + rootElem = xmlTree.content[i]; + } + } + if ( rootElem != 0 ) + { + XMP_StringPtr rootLocalName = rootElem->name.c_str() + rootElem->nsPrefixLen; + + if ( XMP_LitMatch ( rootLocalName, "NonRealTimeMeta" ) ) + { + nameSpace = rootElem->ns.c_str() ; + XML_NodePtr targetProp = rootElem->GetNamedElement ( nameSpace, "TargetMaterial" ); + if ( (targetProp != 0) && targetProp->IsEmptyLeafNode() ) { + XMP_StringPtr umidValue = targetProp->GetAttrValue ( "umidRef" ); + if ( umidValue != 0 ) { + clipUmid = umidValue; + umidFound = true; + } + } + } + } + } + } + } catch ( ... ) { + } + delete ( clipInfoExpat ) ; + return umidFound; +} // XDCAMSAM_MetaHandler::GetClipUmid + +// ================================================================================================= + diff --git a/XMPFiles/source/FileHandlers/XDCAMSAM_Handler.hpp b/XMPFiles/source/FileHandlers/XDCAMSAM_Handler.hpp new file mode 100644 index 0000000..b68334b --- /dev/null +++ b/XMPFiles/source/FileHandlers/XDCAMSAM_Handler.hpp @@ -0,0 +1,58 @@ +#ifndef __XDCAMSAM_Handler_hpp__ +#define __XDCAMSAM_Handler_hpp__ 1 + +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2015 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "public/include/XMP_Environment.h" // ! This must be the first include. +#include "XMPFiles/source/FileHandlers/XDCAM_Handler.hpp" + + +extern XMPFileHandler * XDCAMSAM_MetaHandlerCTor ( XMPFiles * parent ); + +extern bool XDCAMSAM_CheckFormat ( XMP_FileFormat format, + const std::string & rootPath, + const std::string & groupName, + const std::string & parentName, + const std::string & leafName, + XMPFiles * parent ); + +static const XMP_OptionBits kXDCAMSAM_HandlerFlags = (kXMPFiles_CanInjectXMP | + kXMPFiles_CanExpand | + kXMPFiles_CanRewrite | + kXMPFiles_PrefersInPlace | + kXMPFiles_CanReconcile | + kXMPFiles_AllowsOnlyXMP | + kXMPFiles_ReturnsRawPacket | + kXMPFiles_HandlerOwnsFile | + kXMPFiles_AllowsSafeUpdate | + kXMPFiles_FolderBasedFormat); + +class XDCAMSAM_MetaHandler : public XDCAM_MetaHandler +{ + +public: + + void FillAssociatedResources ( std::vector<std::string> * resourceList ); + XDCAMSAM_MetaHandler ( XMPFiles * _parent ); + virtual ~XDCAMSAM_MetaHandler() { }; + +private: + + bool MakeClipFilePath ( std::string * path, XMP_StringPtr suffix, bool checkFile = false ); + void SetPathVariables ( const std::string & clientPath ); + bool GetEditInfoFiles ( std::vector<std::string> &editInfoList ); + bool GetClipUmid ( std::string &clipUmid ); + + XDCAMSAM_MetaHandler() : XDCAM_MetaHandler() {}; // Hidden on purpose. + +}; // XDCAMSAM_MetaHandler + +// ================================================================================================= +#endif /* __XDCAMSAM_Handler_hpp__ */ diff --git a/XMPFiles/source/FileHandlers/XDCAM_Handler.cpp b/XMPFiles/source/FileHandlers/XDCAM_Handler.cpp index 2a05be9..397b049 100644 --- a/XMPFiles/source/FileHandlers/XDCAM_Handler.cpp +++ b/XMPFiles/source/FileHandlers/XDCAM_Handler.cpp @@ -194,358 +194,15 @@ using namespace std; // ! 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. -bool XDCAM_CheckFormat ( XMP_FileFormat format, - const std::string & _rootPath, - const std::string & _gpName, - const std::string & parentName, - const std::string & leafName, - XMPFiles * parent ) -{ - std::string rootPath = _rootPath; // ! Need tweaking in the existing file cases (FAM and SAM). - std::string gpName = _gpName; - - bool isFAM = false; - - std::string tempPath, childName; - - std::string clipName = leafName; - - // Do some basic checks on the root path and component names. Decide if this is FAM or SAM. - - if ( gpName.empty() != parentName.empty() ) return false; // Must be both empty or both non-empty. - - if ( gpName.empty() ) { - - // This is the logical clip path case. Just look for PROAV to see if this is FAM or SAM. - if ( Host_IO::GetChildMode ( rootPath.c_str(), "PROAV" ) != Host_IO::kFMode_IsFolder ) isFAM = true; - - } else { - - // 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; - } - - if ( isFAM ) { - - // Put the proper root path together. Clean up the clip name if needed. - - if ( ! rootPath.empty() ) rootPath += kDirChar; - rootPath += gpName; - gpName.erase(); - - // XMPilot has no ALIAS.XML, but does have a UserData folder, don't change the first - // letter of the clip name for XMPilot. - if ( (Host_IO::GetChildMode ( rootPath.c_str(), "ALIAS.XML" ) != Host_IO::kFMode_IsFile) && - (Host_IO::GetChildMode ( rootPath.c_str(), "UserData" ) != Host_IO::kFMode_IsFolder) ) { - clipName[0] = 'C'; // ! See notes above about pending bug. - } - - if ( clipName.size() > 3 ) { - size_t clipMid = clipName.size() - 3; - char c1 = clipName[clipMid]; - char c2 = clipName[clipMid+1]; - char c3 = clipName[clipMid+2]; - if ( ('A' <= c1) && (c1 <= 'Z') && - ('0' <= c2) && (c2 <= '9') && ('0' <= c3) && (c3 <= '9') ) { - clipName.erase ( clipMid ); - } - } - - } else { - - // Fix the clip name. Check for and strip the "PROAV" suffix on the root path. - - clipName = parentName; // ! We have a folder with the (almost) exact clip name. - clipName[0] = 'C'; - - std::string proav; - XIO::SplitLeafName ( &rootPath, &proav ); - MakeUpperCase ( &proav ); - if ( (rootPath.empty()) || (proav != "PROAV") ) return false; - - } - - } - - // 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 tempPtr hackery. - - if ( isFAM ) { - - if ( (format != kXMP_XDCAM_FAMFile) && (format != kXMP_UnknownFile) ) return false; - - tempPath = rootPath; - - // XMPilot does not have INDEX.XML but does have UserData. - if ( (Host_IO::GetChildMode ( tempPath.c_str(), "INDEX.XML" ) != Host_IO::kFMode_IsFile) && - !((Host_IO::GetChildMode ( rootPath.c_str(), "UserData" ) == Host_IO::kFMode_IsFolder) - // Changes introduced by Sony for XDCAM Memory SxS format in the FAM file structure are - // 1) There is no INDEX.XML in the root directory for XDCAM Memory SxS. - // 2) There is a new Take folder(similar to XDCAMEX) in the root directory. - || (Host_IO::GetChildMode ( tempPath.c_str(), "Take" ) == Host_IO::kFMode_IsFolder))) return false; - if ( Host_IO::GetChildMode ( tempPath.c_str(), "DISCMETA.XML" ) != Host_IO::kFMode_IsFile ) return false; - if ( Host_IO::GetChildMode ( tempPath.c_str(), "MEDIAPRO.XML" ) != Host_IO::kFMode_IsFile ) return false; - - tempPath += kDirChar; - tempPath += "Clip"; // ! Yes, mixed case. - tempPath += kDirChar; - tempPath += clipName; - tempPath += "M01.XML"; - if ( Host_IO::GetFileMode ( tempPath.c_str() ) != Host_IO::kFMode_IsFile ) return false; - - tempPath = rootPath; - tempPath += kDirChar; - tempPath += "FAM"; - tempPath += kDirChar; - tempPath += clipName; - - } else { - - if ( (format != kXMP_XDCAM_SAMFile) && (format != kXMP_UnknownFile) ) return false; - - // We already know about the PROAV folder, just check below it. - - tempPath = rootPath; - tempPath += kDirChar; - tempPath += "PROAV"; - - if ( Host_IO::GetChildMode ( tempPath.c_str(), "INDEX.XML" ) != Host_IO::kFMode_IsFile ) return false; - if ( Host_IO::GetChildMode ( tempPath.c_str(), "DISCMETA.XML" ) != Host_IO::kFMode_IsFile ) return false; - if ( Host_IO::GetChildMode ( tempPath.c_str(), "DISCINFO.XML" ) != Host_IO::kFMode_IsFile ) return false; - if ( Host_IO::GetChildMode ( tempPath.c_str(), "CLPR" ) != Host_IO::kFMode_IsFolder ) return false; - - tempPath += kDirChar; - tempPath += "CLPR"; - tempPath += kDirChar; - tempPath += clipName; - if ( Host_IO::GetFileMode ( tempPath.c_str() ) != Host_IO::kFMode_IsFolder ) return false; - - tempPath += kDirChar; - tempPath += clipName; - tempPath += "M01.XML"; - if ( Host_IO::GetFileMode ( tempPath.c_str() ) != Host_IO::kFMode_IsFile ) return false; - - tempPath = rootPath; - tempPath += kDirChar; - tempPath += "SAM"; - tempPath += kDirChar; - tempPath += clipName; - - } - - // Save the pseudo-path for the handler object. A bit of a hack, but the only way to get info - // from here to there. - - size_t pathLen = tempPath.size() + 1; // Include a terminating nul. - 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; - -} // XDCAM_CheckFormat - -// ================================================================================================= - -static void* CreatePseudoClipPath ( const std::string & clientPath ) { - - // Used to create the clip pseudo path when the CheckFormat function is skipped. - - std::string pseudoPath = clientPath; - std::string clipName; - bool isSAM; - - size_t pathLen; - void* tempPtr = 0; - - if ( ! Host_IO::Exists ( pseudoPath.c_str() ) ) { - - // This is the logical clip path case. Look for PROAV to see if this is FAM or SAM. - - XIO::SplitLeafName ( &pseudoPath, &clipName ); // Extract the logical clip name, no extension. - isSAM = ( Host_IO::GetChildMode ( pseudoPath.c_str(), "PROAV" ) == Host_IO::kFMode_IsFolder ); - - } else { - - // The client passed a physical path. We have separate cases for FAM and SAM. If the last - // folder, the parent of the file, is Clip, Edit, or Sub (ignoring case) then this is FAM - // and things are a bit messy. For SAM, the parent folder is the almost clip name. - - std::string parentName, ignored; - - XIO::SplitLeafName ( &pseudoPath, &clipName ); // Extract the logical clip name. - XIO::SplitFileExtension ( &clipName, &ignored ); - - XIO::SplitLeafName ( &pseudoPath, &parentName ); - MakeUpperCase ( &parentName ); - isSAM = ( (parentName != "CLIP") && (parentName != "EDIT") && (parentName != "SUB") ); - - if ( isSAM ) { - - // SAM is easy, the parent name is almost the clip name, the first letter gets coerced - // to 'C'. There are 2 other folders to remove from the path. - - clipName = parentName; - clipName[0] = 'C'; - XIO::SplitLeafName ( &pseudoPath, &ignored ); // Remove the 2 intermediate folder levels. - XIO::SplitLeafName ( &pseudoPath, &ignored ); - - } else { - - // FAM is a bit messy, study the comments and code of XDCAM_CheckFormat for details. - - if ( Host_IO::GetChildMode ( pseudoPath.c_str(), "ALIAS.XML" ) != Host_IO::kFMode_IsFile ) { - clipName[0] = 'C'; // ! See notes in XDCAM_CheckFormat about pending bug. - } - - if ( clipName.size() > 3 ) { - size_t clipMid = clipName.size() - 3; - char c1 = clipName[clipMid]; - char c2 = clipName[clipMid+1]; - char c3 = clipName[clipMid+2]; - if ( ('A' <= c1) && (c1 <= 'Z') && - ('0' <= c2) && (c2 <= '9') && ('0' <= c3) && (c3 <= '9') ) { - clipName.erase ( clipMid ); - } - } - - } - - } - - pseudoPath += kDirChar; - if ( isSAM ) { - pseudoPath += "SAM"; - } else { - pseudoPath += "FAM"; - } - pseudoPath += kDirChar; - pseudoPath += clipName; - - pathLen = pseudoPath.size() + 1; // Include a terminating nul. - tempPtr = malloc ( pathLen ); - if ( tempPtr == 0 ) XMP_Throw ( "No memory for XDCAM clip info", kXMPErr_NoMemory ); - memcpy ( tempPtr, pseudoPath.c_str(), pathLen ); - - return tempPtr; - -} // CreatePseudoClipPath - -// ================================================================================================= -// XDCAM_MetaHandlerCTor -// ===================== - -XMPFileHandler * XDCAM_MetaHandlerCTor ( XMPFiles * parent ) -{ - return new XDCAM_MetaHandler ( parent ); - -} // XDCAM_MetaHandlerCTor - - -// ================================================================================================= -// XDCAM_MetaHandler::SetSidecarPath -// ==================================== -void XDCAM_MetaHandler::SetSidecarPath() -{ - // Here, we set the appropriate sidecar name for this format. - // If, the format if XMPilot (no INDEX.XML but UserData folder present) or - // SxS (no INDEX.XML but Take folder present) then sidecar name will be - // old name used by MXFHandler i.e, {clipName}.MXF.xmp or {clipname}.mxf.xmp - // For all other cases, new side car name i.e, {clipname}M01.XMP will be used. - - try - { - if(this->isFAM && Host_IO::GetChildMode ( this->rootPath.c_str(), "INDEX.XML" ) != Host_IO::kFMode_IsFile && - (Host_IO::GetChildMode ( rootPath.c_str(), "UserData" ) == Host_IO::kFMode_IsFolder - || Host_IO::GetChildMode ( this->rootPath.c_str(), "Take" ) == Host_IO::kFMode_IsFolder) ) - { - // this is either XMPilot or SxS format. - XMP_VarString mxfFilePath; - if(MakeClipFilePath ( &mxfFilePath , ".MXF", true ) || MakeClipFilePath ( &mxfFilePath , ".mxf", true ) ) - { - Host_IO::FileRef hostRef = Host_IO::Open ( mxfFilePath.c_str(), Host_IO::openReadOnly ); - if ( hostRef != Host_IO::noFileRef ) - { - - XMPFiles_IO mxfFile ( hostRef, mxfFilePath.c_str() , Host_IO::openReadOnly ); - - if ( Host_IO::Length(hostRef) >= 16 ) - { - XMP_Uns8 buffer[16]; - Host_IO::Seek(hostRef, 0, kXMP_SeekFromStart); - XMP_Uns32 readBytes = Host_IO::Read(hostRef, buffer, 16 ); - - if ( ( readBytes == 16 ) && - ( GetUns32BE(&buffer[0]) == 0x060E2B34 ) && - ( GetUns32BE(&buffer[4]) == 0x02050101 ) && - ( GetUns32BE(&buffer[8]) == 0x0D010201 ) && - ( ( GetUns32BE(&buffer[12]) & 0xFFFF00FF ) == 0x01020000 ) - ) - { - std::string pathtomxfclip=Host_IO::GetCasePreservedName(mxfFilePath); - if ( pathtomxfclip != "" ) - { - std::string ext; - XIO::SplitFileExtension( &pathtomxfclip, &ext , false); - ext="."+ext; - MakeClipFilePath ( &mxfFilePath , ext.c_str() , false ); - this->sidecarPath = mxfFilePath + ".xmp"; - } - } - } - } - } - } - } - catch( ... ) - { - // Use new side car name. - } - if(this->sidecarPath.empty()) - { - MakeClipFilePath ( &this->sidecarPath , "M01.XMP", false ) ; - } -}// XDCAM_MetaHandler::SetSidecarPath - // ================================================================================================= // XDCAM_MetaHandler::XDCAM_MetaHandler // ==================================== - -XDCAM_MetaHandler::XDCAM_MetaHandler ( XMPFiles * _parent ) : isFAM(false), expat(0),clipMetadata(NULL) +XDCAM_MetaHandler::XDCAM_MetaHandler ( XMPFiles * _parent ) : expat(0), clipMetadata(NULL) { this->parent = _parent; // Inherited, can't set in the prefix. - this->handlerFlags = kXDCAM_HandlerFlags; this->stdCharForm = kXMP_Char8Bit; - // Extract the root path, clip name, and FAM/SAM flag from tempPtr. - - if ( this->parent->tempPtr == 0 ) { - // The CheckFormat call might have been skipped. - this->parent->tempPtr = CreatePseudoClipPath ( this->parent->GetFilePath() ); - } - - this->rootPath.assign ( (char*) this->parent->tempPtr ); - free ( this->parent->tempPtr ); - this->parent->tempPtr = 0; - - XIO::SplitLeafName ( &this->rootPath, &this->clipName ); - - std::string temp; - XIO::SplitLeafName ( &this->rootPath, &temp ); - XMP_Assert ( (temp == "FAM") || (temp == "SAM") ); - if ( temp == "FAM" ) this->isFAM = true; - // backward compatibility ensured for XMPilot Clips - // XMPilot is FAM - this->SetSidecarPath(); - XMP_Assert ( this->isFAM ? (this->parent->format == kXMP_XDCAM_FAMFile) : (this->parent->format == kXMP_XDCAM_SAMFile) ); - } // XDCAM_MetaHandler::XDCAM_MetaHandler // ================================================================================================= @@ -564,35 +221,6 @@ XDCAM_MetaHandler::~XDCAM_MetaHandler() } // XDCAM_MetaHandler::~XDCAM_MetaHandler // ================================================================================================= -// XDCAM_MetaHandler::MakeClipFilePath -// =================================== - -bool XDCAM_MetaHandler::MakeClipFilePath ( std::string * path, XMP_StringPtr suffix, bool checkFile /* = false */ ) -{ - - *path = this->rootPath; - *path += kDirChar; - - if ( this->isFAM ) { - *path += "Clip"; // ! Yes, mixed case. - } else { - *path += "PROAV"; - *path += kDirChar; - *path += "CLPR"; - *path += kDirChar; - *path += this->clipName; - } - - *path += kDirChar; - *path += this->clipName; - *path += suffix; - - if ( ! checkFile ) return true; - return Host_IO::Exists ( path->c_str() ); - -} // XDCAM_MetaHandler::MakeClipFilePath - -// ================================================================================================= // XDCAM_MetaHandler::MakeMediaproPath // =================================== @@ -616,6 +244,10 @@ bool XDCAM_MetaHandler::MakeMediaproPath ( std::string * path, bool checkFile /* #define kHexDigits "0123456789ABCDEF" +// ================================================================================================= +// XDCAM_MetaHandler::MakeLegacyDigest +// ================================ + void XDCAM_MetaHandler::MakeLegacyDigest ( std::string * digestStr ) { digestStr->erase(); @@ -664,7 +296,7 @@ void XDCAM_MetaHandler::MakeLegacyDigest ( std::string * digestStr ) } // XDCAM_MetaHandler::MakeLegacyDigest // ================================================================================================= -// P2_MetaHandler::CleanupLegacyXML +// XDCAM_MetaHandler::CleanupLegacyXML // ================================ void XDCAM_MetaHandler::CleanupLegacyXML() @@ -676,6 +308,10 @@ void XDCAM_MetaHandler::CleanupLegacyXML() } // XDCAM_MetaHandler::CleanupLegacyXML +// ================================================================================================= +// XDCAM_MetaHandler::readXMLFile +// ================================ + void XDCAM_MetaHandler::readXMLFile( XMP_StringPtr filePath, ExpatAdapter* &expat ) { Host_IO::FileRef hostRef = Host_IO::Open ( filePath, Host_IO::openReadOnly ); @@ -705,27 +341,25 @@ static inline bool operator< ( const XMP_DateTime & left, const XMP_DateTime & r return (compare < 0); } +// ================================================================================================= +// XDCAM_MetaHandler::GetFileModDate +// ================================ + bool XDCAM_MetaHandler::GetFileModDate ( XMP_DateTime * modDate ) { - // The XDCAM FAM locations of metadata: - // MEDIAPRO.XML // Has non-XMP metadata. - // Clip: - // C0001_50i_DVCAM_43_4chM01.XML // Has non-XMP metadata. - // C0001_50i_DVCAM_43_4chM01.XMP - - // The XDCAM SAM locations of metadata: - // PROAV: - // CLPR: - // C0001: - // C0001M01.XML // Has non-XMP metadata. - // C0001M01.XMP + // Modify date is found in the increasing priority order + // + // MEDIAPRO.XML + // Non-Real time metadata file + // XMP file bool ok, haveDate = false; std::string fullPath; XMP_DateTime oneDate, junkDate; if ( modDate == 0 ) modDate = &junkDate; + // MEDIAPRO.XML std::string mediaproPath; ok = MakeMediaproPath ( &mediaproPath, true /* checkFile */ ); if ( ok ) ok = Host_IO::GetModifyDate ( mediaproPath.c_str(), &oneDate ); @@ -734,15 +368,19 @@ bool XDCAM_MetaHandler::GetFileModDate ( XMP_DateTime * modDate ) haveDate = true; } - ok = this->MakeClipFilePath ( &fullPath, "M01.XML", true /* checkFile */ ); - if ( ok ) ok = Host_IO::GetModifyDate ( fullPath.c_str(), &oneDate ); + // Non-Real time metadata file + ok = Host_IO::Exists( this->mNRTFilePath.c_str() ); + //ok = this->MakeClipFilePath ( &fullPath, "M01.XML", true /* checkFile */ ); + if ( ok ) ok = Host_IO::GetModifyDate ( this->mNRTFilePath.c_str(), &oneDate ); if ( ok ) { if ( (! haveDate) || (*modDate < oneDate) ) *modDate = oneDate; haveDate = true; } - ok = this->MakeClipFilePath ( &fullPath, "M01.XMP", true /* checkFile */ ); - if ( ok ) ok = Host_IO::GetModifyDate ( fullPath.c_str(), &oneDate ); + // XMP file + ok = Host_IO::Exists( this->sidecarPath.c_str() ); + //ok = this->MakeClipFilePath ( &fullPath, "M01.XMP", true /* checkFile */ ); + if ( ok ) ok = Host_IO::GetModifyDate ( this->sidecarPath.c_str(), &oneDate ); if ( ok ) { if ( (! haveDate) || (*modDate < oneDate) ) *modDate = oneDate; haveDate = true; @@ -752,140 +390,6 @@ bool XDCAM_MetaHandler::GetFileModDate ( XMP_DateTime * modDate ) } // XDCAM_MetaHandler::GetFileModDate - -// ================================================================================================= -// XDCAM_MetaHandler::GetClipUmid -// ============================== -bool XDCAM_MetaHandler::GetClipUmid ( std::string &clipUmid ) -{ - std::string clipInfoPath; - ExpatAdapter* clipInfoExpat = 0 ; - bool umidFound = false; - XMP_StringPtr nameSpace = 0; - try { - this->MakeClipFilePath ( &clipInfoPath, "C01.SMI" ) ; - readXMLFile( clipInfoPath.c_str(), clipInfoExpat ); - if ( clipInfoExpat != 0 ) - { - XML_Node & xmlTree = clipInfoExpat->tree; - XML_NodePtr rootElem = 0; - - for ( size_t i = 0, limit = xmlTree.content.size(); i < limit; ++i ) { - if ( xmlTree.content[i]->kind == kElemNode ) { - rootElem = xmlTree.content[i]; - } - } - if ( rootElem != 0 ) - { - XMP_StringPtr rootLocalName = rootElem->name.c_str() + rootElem->nsPrefixLen; - - if ( XMP_LitMatch ( rootLocalName, "smil" ) ) - { - XMP_StringPtr umidValue = rootElem->GetAttrValue ( "umid" ); - if ( umidValue != 0 ) { - clipUmid = umidValue; - umidFound = true; - } - } - } - } - if( ! umidFound ) - { //try to get the umid from the NRT metadata - delete ( clipInfoExpat ) ; clipInfoExpat = 0; - this->MakeClipFilePath ( &clipInfoPath, "M01.XML" ) ; - readXMLFile( clipInfoPath.c_str(), clipInfoExpat ) ; - if ( clipInfoExpat != 0 ) - { - XML_Node & xmlTree = clipInfoExpat->tree; - XML_NodePtr rootElem = 0; - for ( size_t i = 0, limit = xmlTree.content.size(); i < limit; ++i ) { - if ( xmlTree.content[i]->kind == kElemNode ) { - rootElem = xmlTree.content[i]; - } - } - if ( rootElem != 0 ) - { - XMP_StringPtr rootLocalName = rootElem->name.c_str() + rootElem->nsPrefixLen; - - if ( XMP_LitMatch ( rootLocalName, "NonRealTimeMeta" ) ) - { - nameSpace = rootElem->ns.c_str() ; - XML_NodePtr targetProp = rootElem->GetNamedElement ( nameSpace, "TargetMaterial" ); - if ( (targetProp != 0) && targetProp->IsEmptyLeafNode() ) { - XMP_StringPtr umidValue = targetProp->GetAttrValue ( "umidRef" ); - if ( umidValue != 0 ) { - clipUmid = umidValue; - umidFound = true; - } - } - } - } - } - } - } catch ( ... ) { - } - delete ( clipInfoExpat ) ; - return umidFound; -}// XDCAM_MetaHandler::GetClipUmid - -// ================================================================================================= -// XDCAM_MetaHandler::IsClipsPlanning -// ================================== -bool XDCAM_MetaHandler::IsClipsPlanning ( std::string clipUmid , XMP_StringPtr planPath ) -{ - ExpatAdapter* planniingExpat = 0 ; - XMP_StringPtr nameSpace = 0 ; - try { - readXMLFile( planPath, planniingExpat ); - if ( planniingExpat != 0 ) - { - XML_Node & xmlTree = planniingExpat->tree; - XML_NodePtr rootElem = 0; - - for ( size_t i = 0, limit = xmlTree.content.size(); i < limit; ++i ) { - if ( xmlTree.content[i]->kind == kElemNode ) { - rootElem = xmlTree.content[i]; - } - } - if ( rootElem != 0 ) - { - XMP_StringPtr rootLocalName = rootElem->name.c_str() + rootElem->nsPrefixLen; - - if ( XMP_LitMatch ( rootLocalName, "PlanningMetadata" ) ) - { - nameSpace = rootElem->ns.c_str() ; - size_t noOfMaterialGroups = rootElem->CountNamedElements ( nameSpace, "MaterialGroup" ) ; - while( noOfMaterialGroups-- ) - { - XML_NodePtr mgNode = rootElem->GetNamedElement( nameSpace, "MaterialGroup" ); - size_t noOfMaterialElements = mgNode->CountNamedElements ( nameSpace, "Material" ) ; - while( noOfMaterialElements-- ) - { - XML_NodePtr materialNode = mgNode->GetNamedElement( nameSpace, "Material" ); - XMP_StringPtr materialType = materialNode->GetAttrValue ( "type" ); - if ( materialType && XMP_LitMatch( materialType , "clip" ) ) - { - XMP_StringPtr umidValue = materialNode->GetAttrValue ( "umidRef" ); - if ( umidValue != 0 && XMP_LitMatch( umidValue , clipUmid.c_str() ) ) - { - delete ( planniingExpat ) ; - return true; - } - } - - } - } - } - } - } - - } catch ( ... ) { - } - delete ( planniingExpat ) ; - return false; -} // XDCAM_MetaHandler::IsClipsPlanning - - // ================================================================================================= // XDCAM_MetaHandler::RefersClipUmid // ================================== @@ -948,144 +452,9 @@ bool XDCAM_MetaHandler::RefersClipUmid ( std::string clipUmid , XMP_StringPtr ed return false; } // XDCAM_MetaHandler::RefersClipUmid -inline bool IsDigit( char c ) -{ - return c >= '0' && c <= '9'; -} - - -// ================================================================================================= -// XDCAM_MetaHandler::GetEditInfoFilesSAM -// ====================================== -bool XDCAM_MetaHandler::GetEditInfoFilesSAM ( std::vector<std::string> &editInfoList ) -{ - std::string clipUmid; - bool found = false; - - if( GetClipUmid ( clipUmid ) ) - { - std::string editFolderPath = this->rootPath + kDirChar + "PROAV" + kDirChar + "EDTR" + kDirChar ; - if ( Host_IO::Exists( editFolderPath.c_str() ) && - Host_IO::GetFileMode( editFolderPath.c_str() ) == Host_IO::kFMode_IsFolder - ) - { - Host_IO::AutoFolder edtrFolder, editFolder; - std::string edtrChildName, edlistChild; - - edtrFolder.folder = Host_IO::OpenFolder ( editFolderPath.c_str() ); - while ( Host_IO::GetNextChild ( edtrFolder.folder, &edtrChildName ) ) { - size_t childLen = edtrChildName.size(); - std::string editListFolderPath = editFolderPath + edtrChildName + kDirChar ; - if ( ! ( childLen == 5 && - edtrChildName[0] == 'E' && - IsDigit( edtrChildName[1] ) && - IsDigit( edtrChildName[2] ) && - IsDigit( edtrChildName[3] ) && - IsDigit( edtrChildName[4] ) && - Host_IO::GetFileMode( editListFolderPath.c_str() ) == Host_IO::kFMode_IsFolder - ) ) continue; - - editFolder.folder = Host_IO::OpenFolder ( editListFolderPath.c_str() ); - while ( Host_IO::GetNextChild ( editFolder.folder, &edlistChild ) ) { - size_t filenamelen = edlistChild.size(); - std::string editListFilePath = editListFolderPath + edlistChild ; - if ( ! ( filenamelen == 12 && - edlistChild.compare ( filenamelen - 4, 4 , ".SMI" ) == 0 && - edlistChild.compare ( 0, edtrChildName.size(), edtrChildName ) == 0 && - Host_IO::GetFileMode( editListFilePath.c_str() ) == Host_IO::kFMode_IsFile - ) ) continue; - if( RefersClipUmid ( clipUmid , editListFilePath.c_str() ) ) - { - found = true ; - editInfoList.push_back( editListFilePath ); - } - } - } - } - } - return found; -} // XDCAM_MetaHandler::GetEditInfoFilesSAM - -// ================================================================================================= -// XDCAM_MetaHandler::GetInfoFilesFAM -// ================================== -bool XDCAM_MetaHandler::GetInfoFilesFAM ( std::vector<std::string> &editInfoList, std::string pathToFolder) -{ - std::string clipUmid; - bool found = false; - - if( GetClipUmid ( clipUmid ) ) - { - if ( Host_IO::Exists( pathToFolder.c_str() ) && - Host_IO::GetFileMode( pathToFolder.c_str() ) == Host_IO::kFMode_IsFolder - ) - { - Host_IO::AutoFolder editFolder; - std::string edlistChild; - - editFolder.folder = Host_IO::OpenFolder ( pathToFolder.c_str() ); - while ( Host_IO::GetNextChild ( editFolder.folder, &edlistChild ) ) { - size_t filenamelen = edlistChild.size(); - std::string editListFilePath = pathToFolder + edlistChild ; - if ( ! ( filenamelen > 7 && - edlistChild.compare ( filenamelen - 4, 4 , ".SMI" ) == 0 && - Host_IO::GetFileMode( editListFilePath.c_str() ) == Host_IO::kFMode_IsFile - ) ) continue; - if( RefersClipUmid ( clipUmid , editListFilePath.c_str() ) ) - { - found = true ; - editInfoList.push_back( editListFilePath ); - } - } - } - } - return found; -} // XDCAM_MetaHandler::GetInfoFilesFAM - -// ================================================================================================= -// XDCAM_MetaHandler::GetPlanningFilesFAM -// ====================================== -bool XDCAM_MetaHandler::GetPlanningFilesFAM ( std::vector<std::string> &planInfoList, std::string pathToFolder) -{ - std::string clipUmid; - bool found = false; - - if( GetClipUmid ( clipUmid ) ) - { - if ( Host_IO::Exists( pathToFolder.c_str() ) && - Host_IO::GetFileMode( pathToFolder.c_str() ) == Host_IO::kFMode_IsFolder - ) - { - Host_IO::AutoFolder planFolder; - std::string listChild; - - planFolder.folder = Host_IO::OpenFolder ( pathToFolder.c_str() ); - while ( Host_IO::GetNextChild ( planFolder.folder, &listChild ) ) { - size_t filenamelen = listChild.size(); - std::string listFilePath = pathToFolder + listChild ; - if ( ! ( filenamelen > 4 && - ( listChild.compare ( filenamelen - 4, 4 , ".XML" ) == 0 - || - listChild.compare ( filenamelen - 4, 4 , ".xml" ) == 0 - ) - && - Host_IO::GetFileMode( listFilePath.c_str() ) == Host_IO::kFMode_IsFile - ) ) continue; - if( IsClipsPlanning ( clipUmid , listFilePath.c_str() ) ) - { - found = true ; - planInfoList.push_back( listFilePath ); - } - } - } - } - return found; -} // XDCAM_MetaHandler::GetPlanningFilesFAM - // ================================================================================================= // XDCAM_MetaHandler::IsMetadataWritable // ======================================= - bool XDCAM_MetaHandler::IsMetadataWritable ( ) { std::vector<std::string> metadataFiles; @@ -1099,310 +468,12 @@ bool XDCAM_MetaHandler::IsMetadataWritable ( ) }// XDCAM_MetaHandler::IsMetadataWritable // ================================================================================================= -// XDCAM_MetaHandler::FillFAMAssociatedResources -// ============================================= -void XDCAM_MetaHandler::FillFAMAssociatedResources ( std::vector<std::string> * resourceList ) -{ - // The possible associated resources: - // .../MyMovie/ - // ALIAS.XML - // INDEX.XML - // DISCMETA.XML - // MEDIAPRO.XML - // MEDIAPRO.BUP - // CUEUP.XML - // CUEUP.BUP - // Clip/ - // AAAAA.MXF AAAAA is the clipname with clipserial - // XX is a counter which will start from from 01 and can go upto 99 based - // on number of files present in this folder with same extension and same clipname/editListName/Takename. - // AAAAAMXX.XML - // AAAAAMXX.XMP - // AAAAARXX.BIM - // Sub/ - // AAAAASXX.MXF - // Local/ - // AAAAACXX.SMI - // AAAAACXX.PPN - // Edit/ DDDDD is the editListName - // DDDDDEXX.SMI - // DDDDDMXX.XML - // Take/ TTTTT is the Takename - // TTTTT.SMI - // TTTTTUNN.SMI NN is a counter which goes from 01 to N-1 where N is number of media, this - // take is divided into. For Nth, TTTTT.SMI shall be picked up. - // TTTTTMXX.XML - // General/ - // Sony/ - // Planning/ AAAAA is the clipname without clipserial - // YYYYMMDDHHMISS is DateTime - // BBBBB_YYYYMMDDHHMISS.xml - // UserData/ - // - - //Add RootPath - std::string filePath = rootPath + kDirChar; - PackageFormat_Support::AddResourceIfExists( resourceList, filePath ); - - // Get the files present directly inside root folder. - filePath = rootPath + kDirChar + "ALIAS.XML"; - PackageFormat_Support::AddResourceIfExists(resourceList, filePath); - - filePath = rootPath + kDirChar + "INDEX.XML"; - PackageFormat_Support::AddResourceIfExists(resourceList, filePath); - - filePath = rootPath + kDirChar + "DISCMETA.XML"; - PackageFormat_Support::AddResourceIfExists(resourceList, filePath); - - filePath = rootPath + kDirChar + "MEDIAPRO.XML"; - PackageFormat_Support::AddResourceIfExists(resourceList, filePath); - filePath = rootPath + kDirChar + "MEDIAPRO.BUP"; - PackageFormat_Support::AddResourceIfExists(resourceList, filePath); - - filePath = rootPath + kDirChar + "CUEUP.XML"; - PackageFormat_Support::AddResourceIfExists(resourceList, filePath); - filePath = rootPath + kDirChar + "CUEUP.BUP"; - PackageFormat_Support::AddResourceIfExists(resourceList, filePath); - - // Add the UserData folder which is used to identify the format in any way - filePath = rootPath + kDirChar + "UserData" + kDirChar; - PackageFormat_Support::AddResourceIfExists(resourceList, filePath); - - XMP_VarString clipPath = rootPath + kDirChar + "Clip" + kDirChar ; - - size_t oldCount = resourceList->size(); - // Get the files present inside clip folder. - XMP_VarString regExp; - XMP_StringVector regExpVec; - - regExp = "^" + clipName + ".MXF$"; - regExpVec.push_back ( regExp ); - regExp = "^" + clipName + "M\\d\\d.XML$"; - regExpVec.push_back ( regExp ); - regExp = "^" + clipName + "R\\d\\d.BIM$"; - regExpVec.push_back ( regExp ); - IOUtils::GetMatchingChildren ( *resourceList, clipPath, regExpVec, false, true, true ); - PackageFormat_Support::AddResourceIfExists(resourceList, this->sidecarPath); - if ( resourceList->size() <= oldCount ) - { - PackageFormat_Support::AddResourceIfExists(resourceList, clipPath); - } - - //Get the files Under Sub folder - clipPath = rootPath + kDirChar + "Sub" + kDirChar ; - regExpVec.clear(); - regExp = "^" + clipName + "S\\d\\d.MXF$"; - regExpVec.push_back ( regExp ); - oldCount = resourceList->size(); - IOUtils::GetMatchingChildren ( *resourceList, clipPath, regExpVec, false, true, true ); - // Add Sub folder if no file inside this, was added. - if ( resourceList->size() <= oldCount ) - { - PackageFormat_Support::AddResourceIfExists(resourceList, clipPath); - } - - //Get the files Under Local folder - clipPath = rootPath + kDirChar + "Local" + kDirChar ; - regExpVec.clear(); - regExp = "^" + clipName + "C\\d\\d.SMI$"; - regExpVec.push_back ( regExp ); - regExp = "^" + clipName + "I\\d\\d.PPN$"; - regExpVec.push_back ( regExp ); - oldCount = resourceList->size(); - IOUtils::GetMatchingChildren ( *resourceList, clipPath, regExpVec, false, true, true ); - - //Add the Edit lists associated to this clip - XMP_StringVector editInfoList; - bool atLeastOneFileAdded = false; - clipPath = rootPath + kDirChar + "Edit" + kDirChar ; - if ( GetInfoFilesFAM ( editInfoList , clipPath ) ) - { - size_t noOfEditInfoFiles = editInfoList.size() ; - for( size_t count = 0; count < noOfEditInfoFiles; count++ ) - { - atLeastOneFileAdded = PackageFormat_Support::AddResourceIfExists(resourceList, editInfoList[count]) ? true : atLeastOneFileAdded; - std::string editNRTFile = editInfoList[count] ; - size_t filenamelen = editInfoList[count].length() ; - editNRTFile[ filenamelen - 7 ] = 'M'; - editNRTFile[ filenamelen - 3 ] = 'X'; - editNRTFile[ filenamelen - 2 ] = 'M'; - editNRTFile[ filenamelen - 1 ] = 'L'; - atLeastOneFileAdded = PackageFormat_Support::AddResourceIfExists(resourceList, editNRTFile ) ? true : atLeastOneFileAdded; - } - } - // Add Edit folder if no file inside this, was added. - if ( !atLeastOneFileAdded ) - { - PackageFormat_Support::AddResourceIfExists(resourceList, clipPath); - } - - atLeastOneFileAdded = false; - - //Add the Takes associated to this clip - XMP_StringVector takeList; - clipPath = rootPath + kDirChar + "Take" + kDirChar ; - if( GetInfoFilesFAM ( takeList , clipPath ) ) - { - size_t noOfTakes = takeList.size() ; - for( size_t count = 0; count < noOfTakes; count++ ) - { - atLeastOneFileAdded = PackageFormat_Support::AddResourceIfExists(resourceList, takeList[count]) ? true : atLeastOneFileAdded; - XMP_VarString takeNRTFile = takeList[count] ; - size_t filenamelen = takeList[count].length() ; - if ( takeNRTFile[ filenamelen - 7 ] == 'U' - && IsDigit( takeNRTFile[ filenamelen - 6 ] ) - && IsDigit( takeNRTFile[ filenamelen - 5 ] ) ) - { - takeNRTFile.erase( takeNRTFile.begin() + filenamelen - 7, takeNRTFile.end() ) ; - } - else - { - takeNRTFile.erase( takeNRTFile.begin() + filenamelen - 4, takeNRTFile.end() ) ; - } - - XMP_VarString fileName; - size_t pos = takeNRTFile.find_last_of ( kDirChar ); - fileName = takeNRTFile.substr ( pos + 1 ); - XMP_VarString regExp = "^" + fileName + "M\\d\\d.XML$"; - oldCount = resourceList->size(); - IOUtils::GetMatchingChildren ( *resourceList, clipPath, regExp, false, true, true ); - atLeastOneFileAdded = resourceList->size() > oldCount; - } - } - // Add Take folder if no file inside this, was added. - if(!atLeastOneFileAdded) - { - filePath = rootPath + kDirChar + "Take" + kDirChar; - PackageFormat_Support::AddResourceIfExists(resourceList, filePath); - } - - //Add the Planning Metadata Files associated to this clip - XMP_StringVector planList; - clipPath = rootPath + kDirChar + "General" + kDirChar + "Sony" + kDirChar+ "Planning" + kDirChar; - if( GetPlanningFilesFAM ( planList , clipPath ) ) - { - size_t noOfPlans = planList.size() ; - for( size_t count = 0; count < noOfPlans; count++ ) - { - resourceList->push_back( planList[count] ); - } - } -} // XDCAM_MetaHandler::FillFAMAssociatedResources - -// ================================================================================================= -// XDCAM_MetaHandler::FillSAMAssociatedResources -// ============================================= -void XDCAM_MetaHandler::FillSAMAssociatedResources ( std::vector<std::string> * resourceList ) -{ - // The possible associated resources: - // .../MyMovie/ - // PROAV/ - // INDEX.XML - // INDEX.BUP - // DISCMETA.XML - // DISCINFO.XML - // DISCINFO.BUP - // CLPR/ - // CXXXX/ XXXX is ClipSerial and NN is a counter which will start from from 01 and can go upto 99 based - // on number of files present in this folder with same extension. - // CXXXXCNN.SMI - // CXXXXVNN.MXF - // CXXXXANN.MXF - // CXXXXRNN.BIM - // CXXXXINN.PPN - // CXXXXMNN.XML - // CXXXXSNN.MXF - // EDTR/ - // EXXXX: - // EXXXXENN.SMI - // EXXXXMNN.XML - // - std::string proavPath = rootPath + kDirChar + "PROAV" + kDirChar; - std::string filePath; - //Add RootPath - filePath = rootPath + kDirChar; - PackageFormat_Support::AddResourceIfExists( resourceList, filePath ); - - // Get the files present directly inside PROAV folder. - filePath = proavPath + "INDEX.XML"; - PackageFormat_Support::AddResourceIfExists(resourceList, filePath); - filePath = proavPath + "INDEX.BUP"; - PackageFormat_Support::AddResourceIfExists(resourceList, filePath); - - filePath = proavPath + "DISCINFO.XML"; - PackageFormat_Support::AddResourceIfExists(resourceList, filePath); - filePath = proavPath + "DISCINFO.BUP"; - PackageFormat_Support::AddResourceIfExists(resourceList, filePath); - - filePath = proavPath + "DISCMETA.XML"; - PackageFormat_Support::AddResourceIfExists(resourceList, filePath); - - XMP_VarString clipPath = proavPath + "CLPR" + kDirChar + clipName + kDirChar; - XMP_VarString regExp; - XMP_StringVector regExpVec; - - regExp = "^" + clipName + "C\\d\\d.SMI$"; - regExpVec.push_back ( regExp ); - regExp = "^" + clipName + "M\\d\\d.XML$"; - regExpVec.push_back ( regExp ); - regExp = "^" + clipName + "V\\d\\d.MXF$"; - regExpVec.push_back ( regExp ); - regExp = "^" + clipName + "A\\d\\d.MXF$"; - regExpVec.push_back ( regExp ); - regExp = "^" + clipName + "R\\d\\d.BIM$"; - regExpVec.push_back ( regExp ); - regExp = "^" + clipName + "I\\d\\d.PPN$"; - regExpVec.push_back ( regExp ); - regExp = "^" + clipName + "S\\d\\d.MXF$"; - regExpVec.push_back ( regExp ); - IOUtils::GetMatchingChildren ( *resourceList, clipPath, regExpVec, false, true, true ); - PackageFormat_Support::AddResourceIfExists(resourceList, this->sidecarPath); - //Add the Edit lists that refer this clip - std::vector<std::string> editInfoList; - if( GetEditInfoFilesSAM ( editInfoList ) ) - { - size_t noOfEditInfoFiles = editInfoList.size() ; - for( size_t count = 0; count < noOfEditInfoFiles; count++ ) - { - PackageFormat_Support::AddResourceIfExists(resourceList, editInfoList[count]); - std::string editNRTFile = editInfoList[count].c_str() ; - size_t filenamelen = editInfoList[count].length() ; - editNRTFile[ filenamelen - 7 ] = 'M'; - editNRTFile[ filenamelen - 3 ] = 'X'; - editNRTFile[ filenamelen - 2 ] = 'M'; - editNRTFile[ filenamelen - 1 ] = 'L'; - PackageFormat_Support::AddResourceIfExists(resourceList, editNRTFile ); - } - } -}// XDCAM_MetaHandler::FillSAMAssociatedResources - -// ================================================================================================= -// XDCAM_MetaHandler::FillAssociatedResources -// ====================================== -void XDCAM_MetaHandler::FillAssociatedResources ( std::vector<std::string> * resourceList ) -{ - if( this->isFAM ) - FillFAMAssociatedResources ( resourceList ); - else - FillSAMAssociatedResources ( resourceList ); -} -// ================================================================================================= // XDCAM_MetaHandler::FillMetadataFiles // ==================================== void XDCAM_MetaHandler::FillMetadataFiles ( std::vector<std::string> * metadataFiles ) { - std::string noExtPath, filePath; - - if(this->isFAM) { - noExtPath = rootPath + kDirChar + "Clip" + kDirChar + clipName; - } else { - noExtPath = rootPath + kDirChar + "PROAV" + kDirChar + "CLPR" + - kDirChar + clipName + kDirChar + clipName; - } - - metadataFiles->push_back ( this->sidecarPath ); - filePath = noExtPath + "M01.XML"; - metadataFiles->push_back ( filePath ); + metadataFiles->push_back( this->sidecarPath ); + metadataFiles->push_back( this->mNRTFilePath ); } // XDCAM_MetaHandler::FillMetadataFiles @@ -1419,7 +490,6 @@ void XDCAM_MetaHandler::CacheFileData() } // See if the clip's .XMP file exists. - if ( ! Host_IO::Exists ( this->sidecarPath.c_str() ) ) return; // No XMP. // Read the entire .XMP file. We know the XMP exists, New_XMPFiles_IO is supposed to return 0 @@ -1428,6 +498,7 @@ void XDCAM_MetaHandler::CacheFileData() bool readOnly = XMP_OptionIsClear ( this->parent->openFlags, kXMPFiles_OpenForUpdate ); XMP_Assert ( this->parent->ioRef == 0 ); + XMPFiles_IO* xmpFile = XMPFiles_IO::New_XMPFiles_IO ( this->sidecarPath.c_str(), readOnly ); if ( xmpFile == 0 ) XMP_Throw ( "XDCAM XMP file open failure", kXMPErr_InternalFailure ); this->parent->ioRef = xmpFile; @@ -1454,20 +525,6 @@ void XDCAM_MetaHandler::CacheFileData() // XDCAM_MetaHandler::GetMediaProMetadata // ====================================== -bool XDCAM_MetaHandler::GetMediaProMetadata ( SXMPMeta * xmpObjPtr, - const std::string& clipUMID, - bool digestFound ) -{ - if (!this->isFAM) return false; - - // Build a directory string to the MEDIAPRO file. - - std::string mediaproPath; - MakeMediaproPath ( &mediaproPath ); - return XDCAM_Support::GetMediaProLegacyMetadata ( xmpObjPtr, clipUMID, mediaproPath, digestFound ); - -} - // ================================================================================================= // XDCAM_MetaHandler::ProcessXMP // ============================= @@ -1492,10 +549,10 @@ void XDCAM_MetaHandler::ProcessXMP() } // NonRealTimeMeta -> XMP by schema - std::string xmlPath, umid; - this->MakeClipFilePath ( &xmlPath, "M01.XML" ); + std::string xmlPath = this->mNRTFilePath; + std::string umid; - readXMLFile( xmlPath.c_str(),this->expat ); + readXMLFile( xmlPath.c_str(), this->expat ); if ( this->expat == 0 ) return; // The root element should be NonRealTimeMeta in some namespace. Take whatever this file uses. @@ -1590,7 +647,7 @@ void XDCAM_MetaHandler::UpdateFile ( bool doSafeUpdate ) std::string legacyXML, xmlPath; this->expat->tree.Serialize ( &legacyXML ); - this->MakeClipFilePath ( &xmlPath, "M01.XML" ); + xmlPath = this->mNRTFilePath; bool haveXML = Host_IO::Exists ( xmlPath.c_str() ); if ( ! haveXML ) Host_IO::Create ( xmlPath.c_str() ); diff --git a/XMPFiles/source/FileHandlers/XDCAM_Handler.hpp b/XMPFiles/source/FileHandlers/XDCAM_Handler.hpp index f03ada8..e70621a 100644 --- a/XMPFiles/source/FileHandlers/XDCAM_Handler.hpp +++ b/XMPFiles/source/FileHandlers/XDCAM_Handler.hpp @@ -24,25 +24,10 @@ /// // ================================================================================================= -extern XMPFileHandler * XDCAM_MetaHandlerCTor ( XMPFiles * parent ); - -extern bool XDCAM_CheckFormat ( XMP_FileFormat format, - const std::string & rootPath, - const std::string & gpName, - const std::string & parentName, - const std::string & leafName, - XMPFiles * parent ); - -static const XMP_OptionBits kXDCAM_HandlerFlags = (kXMPFiles_CanInjectXMP | - kXMPFiles_CanExpand | - kXMPFiles_CanRewrite | - kXMPFiles_PrefersInPlace | - kXMPFiles_CanReconcile | - kXMPFiles_AllowsOnlyXMP | - kXMPFiles_ReturnsRawPacket | - kXMPFiles_HandlerOwnsFile | - kXMPFiles_AllowsSafeUpdate | - kXMPFiles_FolderBasedFormat); +inline bool IsDigit( char c ) +{ + return c >= '0' && c <= '9'; +} class XDCAM_MetaHandler : public XMPFileHandler { @@ -50,8 +35,7 @@ public: bool GetFileModDate ( XMP_DateTime * modDate ); - void FillMetadataFiles ( std::vector<std::string> * metadataFiles ); - void FillAssociatedResources ( std::vector<std::string> * resourceList ); + virtual void FillAssociatedResources ( std::vector<std::string> * resourceList ) {}; bool IsMetadataWritable ( ) ; void CacheFileData(); @@ -66,31 +50,31 @@ public: XDCAM_MetaHandler ( XMPFiles * _parent ); virtual ~XDCAM_MetaHandler(); -private: +protected: - XDCAM_MetaHandler() : isFAM(false), expat(0), clipMetadata(0) {}; // Hidden on purpose. + XDCAM_MetaHandler() : expat(0), clipMetadata(0) {}; // Hidden on purpose. - bool MakeClipFilePath ( std::string * path, XMP_StringPtr suffix, bool checkFile = false ); + virtual bool MakeClipFilePath ( std::string * path, XMP_StringPtr suffix, bool checkFile = false ) { return false; } + virtual void SetPathVariables ( const std::string & clientPath ) { } + virtual bool GetMediaProMetadata ( SXMPMeta * xmpObjPtr, const std::string& clipUMID, bool digestFound ) { + return false; + } bool MakeMediaproPath ( std::string * path, bool checkFile = false ); - void MakeLegacyDigest ( std::string * digestStr ); - void CleanupLegacyXML(); - void SetSidecarPath(); - + virtual bool GetClipUmid ( std::string &clipUmid ) { return false; } void readXMLFile( XMP_StringPtr filePath,ExpatAdapter* &expat ); - bool GetClipUmid ( std::string &clipUmid ) ; - bool IsClipsPlanning ( std::string clipUmid , XMP_StringPtr planPath ) ; bool RefersClipUmid ( std::string clipUmid , XMP_StringPtr editInfoPath ) ; - bool GetInfoFilesFAM ( std::vector<std::string> &InfoList, std::string pathToFolder) ; - bool GetPlanningFilesFAM ( std::vector<std::string> &planInfoList, std::string pathToFolder) ; - bool GetEditInfoFilesSAM ( std::vector<std::string> &editInfoList ) ; - void FillFAMAssociatedResources ( std::vector<std::string> * resourceList ); - void FillSAMAssociatedResources ( std::vector<std::string> * resourceList ); + std::string rootPath, clipName, sidecarPath; + + std::string mNRTFilePath; + std::string oldSidecarPath; - bool GetMediaProMetadata ( SXMPMeta * xmpObjPtr, const std::string& clipUMID, bool digestFound ); +private: - std::string rootPath, clipName, xdcNS, legacyNS, sidecarPath; + void FillMetadataFiles ( std::vector<std::string> * metadataFiles ); + void MakeLegacyDigest ( std::string * digestStr ); + void CleanupLegacyXML(); - bool isFAM; + std::string xdcNS, legacyNS; ExpatAdapter * expat; XML_Node * clipMetadata; // ! Don't delete, points into the Expat tree. diff --git a/XMPFiles/source/FormatSupport/ID3_Support.cpp b/XMPFiles/source/FormatSupport/ID3_Support.cpp index dd19c16..528609c 100644 --- a/XMPFiles/source/FormatSupport/ID3_Support.cpp +++ b/XMPFiles/source/FormatSupport/ID3_Support.cpp @@ -468,50 +468,53 @@ void ID3v2Frame::release() // ================================================================================================= -void ID3v2Frame::setFrameValue ( const std::string& rawvalue, bool needDescriptor, - bool utf16, bool isXMPPRIVFrame, bool needEncodingByte ) +void ID3v2Frame::setFrameValue( const std::string& rawvalue, bool needDescriptor, + bool utf16, bool isXMPPRIVFrame, bool needEncodingByte, bool isAlreadyEncoded /* = false */ ) { std::string value; if ( isXMPPRIVFrame ) { - XMP_Assert ( (! needDescriptor) && (! utf16) ); + XMP_Assert( ( !needDescriptor ) && ( !utf16 ) ); - value.append ( "XMP\0", 4 ); - value.append ( rawvalue ); - value.append ( "\0", 1 ); // final zero byte + value.append( "XMP\0", 4 ); + value.append( rawvalue ); + value.append( "\0", 1 ); // final zero byte - } else { + } + else if ( !isAlreadyEncoded ) { if ( needEncodingByte ) { if ( utf16 ) { - value.append ( "\x1", 1 ); - } else { - value.append ( "\x0", 1 ); + value.append( "\x1", 1 ); + } + else { + value.append( "\x0", 1 ); } } - if ( needDescriptor ) value.append ( "eng", 3 ); + if ( needDescriptor ) value.append( "eng", 3 ); if ( utf16 ) { - if ( needDescriptor ) value.append ( "\xFF\xFE\0\0", 4 ); + if ( needDescriptor ) value.append( "\xFF\xFE\0\0", 4 ); - value.append ( "\xFF\xFE", 2 ); + 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 ); + ToUTF16( ( XMP_Uns8* ) rawvalue.c_str(), rawvalue.size(), &utf16str, false ); + value.append( utf16str ); + value.append( "\0\0", 2 ); - } else { + } + else { std::string convertedValue; - ReconcileUtils::UTF8ToLatin1 ( rawvalue.c_str(), rawvalue.size(), &convertedValue ); + ReconcileUtils::UTF8ToLatin1( rawvalue.c_str(), rawvalue.size(), &convertedValue ); - if ( needDescriptor ) value.append ( "\0", 1 ); - value.append ( convertedValue ); - value.append ( "\0", 1 ); + if ( needDescriptor ) value.append( "\0", 1 ); + value.append( convertedValue ); + value.append( "\0", 1 ); } @@ -520,10 +523,19 @@ void ID3v2Frame::setFrameValue ( const std::string& rawvalue, bool needDescripto this->changed = true; this->release(); - this->contentSize = (XMP_Int32) value.size(); - XMP_Validate ( (this->contentSize < 20*1024*1024), "XMP Property exceeds 20MB in size", kXMPErr_InternalFailure ); - this->content = new char [ this->contentSize ]; - memcpy ( this->content, value.c_str(), this->contentSize ); + if ( isAlreadyEncoded ) + { + XMP_Assert( ( !needDescriptor ) && ( !utf16 ) && value.empty() ); + this->contentSize = ( XMP_Int32 ) rawvalue.size(); + } + else + this->contentSize = ( XMP_Int32 ) value.size(); + XMP_Validate( ( this->contentSize < 20 * 1024 * 1024 ), "XMP Property exceeds 20MB in size", kXMPErr_InternalFailure ); + this->content = new char[ this->contentSize ]; + if ( isAlreadyEncoded ) + memcpy( this->content, rawvalue.c_str(), this->contentSize ); + else + memcpy( this->content, value.c_str(), this->contentSize ); } // ID3v2Frame::setFrameValue @@ -871,7 +883,7 @@ void ID3v1Tag::write ( XMP_IO* file, SXMPMeta* meta ) } - if ( meta->GetProperty ( kXMP_NS_DM, "trackNumber", &utf8, 0 ) ) { + if ( meta->GetProperty ( kXMP_NS_DM, "trackNumber", &utf8, (XMP_OptionBits *) kXMP_NoOptions ) ) { XMP_Uns8 trackNo = 0; try { diff --git a/XMPFiles/source/FormatSupport/ID3_Support.hpp b/XMPFiles/source/FormatSupport/ID3_Support.hpp index 1ca3107..dda6bfc 100644 --- a/XMPFiles/source/FormatSupport/ID3_Support.hpp +++ b/XMPFiles/source/FormatSupport/ID3_Support.hpp @@ -120,7 +120,7 @@ namespace ID3_Support { void release(); void setFrameValue ( const std::string& rawvalue, bool needDescriptor = false, - bool utf16 = false, bool isXMPPRIVFrame = false, bool needEncodingByte = true ); + bool utf16 = false, bool isXMPPRIVFrame = false, bool needEncodingByte = true, bool isAlreadyEncoded = false ); XMP_Int64 read ( XMP_IO* file, XMP_Uns8 majorVersion ); void write ( XMP_IO* file, XMP_Uns8 majorVersion ); diff --git a/XMPFiles/source/FormatSupport/IPTC_Support.cpp b/XMPFiles/source/FormatSupport/IPTC_Support.cpp index fece006..3df7726 100644 --- a/XMPFiles/source/FormatSupport/IPTC_Support.cpp +++ b/XMPFiles/source/FormatSupport/IPTC_Support.cpp @@ -250,8 +250,10 @@ void IPTC_Manager::ParseMemoryDataSets ( const void* data, XMP_Uns32 length, boo if ( (dsLen == 3) && (memcmp ( iptcPtr, "\x1B\x25\x47", 3 ) == 0) ) this->utf8Encoding = true; } - XMP_Uns16 mapID = recNum*1000 + dsNum; - DataSetInfo dsInfo ( recNum, dsNum, dsLen, iptcPtr ); + XMP_Uns16 mapID = recNum*1000 + dsNum; + DataSetInfo dsInfo( recNum, dsNum, dsLen ); + if ( dsLen != 0 ) + dsInfo.dataPtr = iptcPtr; DataSetMap::iterator dsPos = this->dataSets.find ( mapID ); bool repeatable = false; @@ -405,6 +407,10 @@ IPTC_Writer::~IPTC_Writer() void IPTC_Writer::SetDataSet_UTF8 ( XMP_Uns8 dsNum, const void* utf8Ptr, XMP_Uns32 utf8Len, long which /* = -1 */ ) { + // No need to process if data is of zero length + if ( utf8Len == 0 ) + return; + const DataSetCharacteristics* knownDS = FindKnownDataSet ( dsNum ); if ( knownDS == 0 ) XMP_Throw ( "Can only set known IPTC DataSets", kXMPErr_InternalFailure ); @@ -413,7 +419,10 @@ void IPTC_Writer::SetDataSet_UTF8 ( XMP_Uns8 dsNum, const void* utf8Ptr, XMP_Uns XMP_Uns8 * tempPtr; XMP_Uns32 dataLen; std::string localStr; - +#if XMP_MacBuild +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunreachable-code" +#endif if ( kUTF8_Mode == kUTF8_AlwaysMode ) { // Always use UTF-8. @@ -463,6 +472,9 @@ void IPTC_Writer::SetDataSet_UTF8 ( XMP_Uns8 dsNum, const void* utf8Ptr, XMP_Uns } } +#if XMP_MacBuild +#pragma clang diagnostic pop +#endif // Set the value for this DataSet, making a non-transient copy of the value. Respect UTF-8 character // boundaries when truncating. This is easy to check. If the first truncated byte has 10 in the @@ -674,15 +686,16 @@ void IPTC_Writer::ConvertToUTF8() for ( ; dsPos != dsEnd; ++dsPos ) { DataSetInfo & dsInfo = dsPos->second; - - ReconcileUtils::LocalToUTF8 ( dsInfo.dataPtr, dsInfo.dataLen, &utf8Str ); - this->DisposeLooseValue ( dsInfo ); - - 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. - + if ( dsInfo.dataLen != 0 ) + { + ReconcileUtils::LocalToUTF8( dsInfo.dataPtr, dsInfo.dataLen, &utf8Str ); + this->DisposeLooseValue( dsInfo ); + + 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. + } } this->utf8Encoding = true; @@ -706,15 +719,16 @@ void IPTC_Writer::ConvertToLocal() for ( ; dsPos != dsEnd; ++dsPos ) { DataSetInfo & dsInfo = dsPos->second; - - ReconcileUtils::UTF8ToLocal ( dsInfo.dataPtr, dsInfo.dataLen, &localStr ); - this->DisposeLooseValue ( dsInfo ); - - 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. - + if ( dsInfo.dataLen != 0 ) + { + ReconcileUtils::UTF8ToLocal( dsInfo.dataPtr, dsInfo.dataLen, &localStr ); + this->DisposeLooseValue( dsInfo ); + + 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. + } } this->utf8Encoding = false; diff --git a/XMPFiles/source/FormatSupport/IPTC_Support.hpp b/XMPFiles/source/FormatSupport/IPTC_Support.hpp index 4c2d2bc..e81f1e0 100644 --- a/XMPFiles/source/FormatSupport/IPTC_Support.hpp +++ b/XMPFiles/source/FormatSupport/IPTC_Support.hpp @@ -156,6 +156,8 @@ public: XMP_Uns32 dataLen; XMP_Uns8 * dataPtr; // ! The data is read-only. Raw data pointer, beware of character encoding. DataSetInfo() : recNum(0), dsNum(0), dataLen(0), dataPtr(0) {}; + DataSetInfo( XMP_Uns8 _recNum, XMP_Uns8 _dsNum, XMP_Uns32 _dataLen ) + : recNum( _recNum ), dsNum( _dsNum ), dataLen( _dataLen ), dataPtr( 0 ) {}; DataSetInfo ( XMP_Uns8 _recNum, XMP_Uns8 _dsNum, XMP_Uns32 _dataLen, XMP_Uns8 * _dataPtr ) : recNum(_recNum), dsNum(_dsNum), dataLen(_dataLen), dataPtr(_dataPtr) {}; }; diff --git a/XMPFiles/source/FormatSupport/ISOBaseMedia_Support.cpp b/XMPFiles/source/FormatSupport/ISOBaseMedia_Support.cpp index d4a8076..b1ee77c 100644 --- a/XMPFiles/source/FormatSupport/ISOBaseMedia_Support.cpp +++ b/XMPFiles/source/FormatSupport/ISOBaseMedia_Support.cpp @@ -55,7 +55,8 @@ const XMP_Uns8 * GetBoxInfo ( const XMP_Uns8 * boxPtr, const XMP_Uns8 * boxLimit if ( info == 0 ) info = &voidInfo; info->boxType = info->headerSize = 0; info->contentSize = 0; - + memset( info->idUUID, 0, 16 ); + if ( boxPtr >= boxLimit ) XMP_Throw ( "Bad offset to GetBoxInfo", kXMPErr_InternalFailure ); if ( (boxLimit - boxPtr) < 8 ) { // Is there enough space for a standard box header? @@ -68,8 +69,22 @@ const XMP_Uns8 * GetBoxInfo ( const XMP_Uns8 * boxPtr, const XMP_Uns8 * boxLimit info->boxType = GetUns32BE ( boxPtr+4 ); if ( u32Size >= 8 ) { - info->headerSize = 8; // Normal explicit size case. - info->contentSize = u32Size - 8; + if( info->boxType == ISOMedia::k_uuid ) + { + if ( (boxLimit - boxPtr) < 24 ) + { // Is there enough space for a uuid box header? + if ( throwErrors ) XMP_Throw ( "No space for UUID box header", kXMPErr_BadFileFormat ); + info->headerSize = (XMP_Uns32) (boxLimit - boxPtr); + return boxLimit; + } + info->headerSize = 8 + 16; // 16 for ID in UUID + memcpy( info->idUUID, boxPtr + 8, 16 ); + } + else + { + info->headerSize = 8; // Normal explicit size case. + } + info->contentSize = u32Size - info->headerSize; } else if ( u32Size == 0 ) { info->headerSize = 8; // The box goes to EoF - treat it as "to limit". info->contentSize = (boxLimit - boxPtr) - 8; @@ -115,6 +130,7 @@ XMP_Uns64 GetBoxInfo ( XMP_IO* fileRef, const XMP_Uns64 boxOffset, const XMP_Uns if ( info == 0 ) info = &voidInfo; info->boxType = info->headerSize = 0; info->contentSize = 0; + memset( info->idUUID, 0, 16 ); if ( boxOffset >= boxLimit ) XMP_Throw ( "Bad offset to GetBoxInfo", kXMPErr_InternalFailure ); @@ -131,8 +147,22 @@ XMP_Uns64 GetBoxInfo ( XMP_IO* fileRef, const XMP_Uns64 boxOffset, const XMP_Uns info->boxType = GetUns32BE ( &buffer[4] ); if ( u32Size >= 8 ) { - info->headerSize = 8; // Normal explicit size case. - info->contentSize = u32Size - 8; + if( info->boxType == ISOMedia::k_uuid ) + { + if ( (boxLimit - boxOffset) < 24 ) + { // Is there enough space for a uuid box header? + if ( throwErrors ) XMP_Throw ( "No space for UUID box header", kXMPErr_BadFileFormat ); + info->headerSize = (XMP_Uns32) (boxLimit - boxOffset); + return boxLimit; + } + info->headerSize = 8 + 16; // 16 for ID in UUID + (void) fileRef->ReadAll ( info->idUUID, 16 ); + } + else + { + info->headerSize = 8; // Normal explicit size case. + } + info->contentSize = u32Size - info->headerSize; } else if ( u32Size == 0 ) { info->headerSize = 8; // The box goes to EoF. info->contentSize = fileRef->Length() - (boxOffset + 8); diff --git a/XMPFiles/source/FormatSupport/ISOBaseMedia_Support.hpp b/XMPFiles/source/FormatSupport/ISOBaseMedia_Support.hpp index 728293f..a7c8feb 100644 --- a/XMPFiles/source/FormatSupport/ISOBaseMedia_Support.hpp +++ b/XMPFiles/source/FormatSupport/ISOBaseMedia_Support.hpp @@ -37,6 +37,11 @@ namespace ISOMedia { ISOboxType(k_f4v ,0x66347620UL)SEPARATOR \ ISOboxType(k_avc1,0x61766331UL)SEPARATOR \ ISOboxType(k_qt ,0x71742020UL)SEPARATOR \ + ISOboxType(k_isom,0x69736F6DUL)SEPARATOR \ + ISOboxType(k_3gp4,0x33677034UL)SEPARATOR \ + ISOboxType(k_3g2a,0x33673261UL)SEPARATOR \ + ISOboxType(k_3g2b,0x33673262UL)SEPARATOR \ + ISOboxType(k_3g2c,0x33673263UL)SEPARATOR \ \ ISOboxType(k_moov,0x6D6F6F76UL)SEPARATOR /* Container Box, no version/flags. */ \ ISOboxType(k_mvhd,0x6D766864UL)SEPARATOR /* Data FullBox, has version/flags. */ \ @@ -93,16 +98,17 @@ namespace ISOMedia { bool IsKnownBoxType(XMP_Uns32 boxType) ; void TerminateGlobals(); - static XMP_Uns32 k_xmpUUID [4] = { MakeUns32BE ( 0xBE7ACFCBUL ), - MakeUns32BE ( 0x97A942E8UL ), - MakeUns32BE ( 0x9C719994UL ), - MakeUns32BE ( 0x91E3AFACUL ) }; + static XMP_Uns8 k_xmpUUID [16] = { 0xBE, 0x7A, 0xCF, 0xCB, 0x97, 0xA9, 0x42, 0xE8, 0x9C, 0x71, 0x99, 0x94, 0x91, 0xE3, 0xAF, 0xAC }; 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) {}; + XMP_Uns8 idUUID[16]; // ID of the uuid atom if present + BoxInfo() : boxType(0), headerSize(0), contentSize(0) + { + memset( idUUID, 0, 16 ); + }; }; // Get basic info about a box in memory, returning a pointer to the following box. diff --git a/XMPFiles/source/FormatSupport/MOOV_Support.cpp b/XMPFiles/source/FormatSupport/MOOV_Support.cpp index 959df44..50b02eb 100644 --- a/XMPFiles/source/FormatSupport/MOOV_Support.cpp +++ b/XMPFiles/source/FormatSupport/MOOV_Support.cpp @@ -59,6 +59,8 @@ void MOOV_Manager::FillBoxInfo ( const BoxNode & node, BoxInfo * info ) const info->childCount = (XMP_Uns32)node.children.size(); info->contentSize = node.contentSize; info->content = PickContentPtr ( node ); + if( node.boxType == ISOMedia::k_uuid ) + memcpy( info->idUUID, node.idUUID, 16); } // MOOV_Manager::FillBoxInfo @@ -249,7 +251,10 @@ void MOOV_Manager::ParseNestedBoxes ( BoxNode * parentNode, const std::string & (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 ) ); + if( isoInfo.boxType == ISOMedia::k_uuid ) + parentNode->children.push_back ( BoxNode ( childOffset, isoInfo.boxType, isoInfo.headerSize, (XMP_Uns8 *)isoInfo.idUUID, (XMP_Uns32)isoInfo.contentSize ) ); + else + parentNode->children.push_back ( BoxNode ( childOffset, isoInfo.boxType, isoInfo.headerSize, (XMP_Uns32)isoInfo.contentSize ) ); BoxNode * newChild = &parentNode->children.back(); #if TraceParseMoovTree @@ -300,13 +305,17 @@ void MOOV_Manager::NoteChange() // // 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 ) +void MOOV_Manager::SetBox ( BoxRef theBox, const void* dataPtr, XMP_Uns32 size , const XMP_Uns8 * idUUID ) { XMP_Enforce ( size < moovBoxSizeLimit ); BoxNode * node = (BoxNode*)theBox; if ( node->contentSize == size ) { - + if( node->boxType == ISOMedia::k_uuid && idUUID != 0 ) + { + memcpy ( node->idUUID, idUUID, 16 ); + this->moovNode.changed = true; + } XMP_Uns8 * oldContent = PickContentPtr ( *node ); if ( memcmp ( oldContent, dataPtr, size ) == 0 ) return; // No change. memcpy ( oldContent, dataPtr, size ); // Update the old content in-place @@ -323,6 +332,8 @@ void MOOV_Manager::SetBox ( BoxRef theBox, const void* dataPtr, XMP_Uns32 size ) memcpy ( &node->changedContent[0], dataPtr, size ); node->contentSize = size; node->changed = true; + if( node->boxType == ISOMedia::k_uuid && idUUID != 0) + memcpy ( node->idUUID, idUUID, 16 ); this->moovNode.changed = true; #if TraceUpdateMoovTree @@ -342,7 +353,7 @@ void MOOV_Manager::SetBox ( BoxRef theBox, const void* dataPtr, XMP_Uns32 size ) // // Like above, but create the path to the box if necessary. -void MOOV_Manager::SetBox ( const char * boxPath, const void* dataPtr, XMP_Uns32 size ) +void MOOV_Manager::SetBox ( const char * boxPath, const void* dataPtr, XMP_Uns32 size , const XMP_Uns8 * idUUID ) { XMP_Enforce ( size < moovBoxSizeLimit ); @@ -363,11 +374,11 @@ void MOOV_Manager::SetBox ( const char * boxPath, const void* dataPtr, XMP_Uns32 parentRef = currRef; currRef = this->GetTypeChild ( parentRef, boxType, 0 ); - if ( currRef == 0 ) currRef = this->AddChildBox ( parentRef, boxType, 0, 0 ); + if ( currRef == 0 ) currRef = this->AddChildBox ( parentRef, boxType, 0, 0 , idUUID ); } - this->SetBox ( currRef, dataPtr, size ); + this->SetBox ( currRef, dataPtr, size, idUUID ); } // MOOV_Manager::SetBox @@ -375,12 +386,15 @@ void MOOV_Manager::SetBox ( const char * boxPath, const void* dataPtr, XMP_Uns32 // MOOV_Manager::AddChildBox // ========================= -MOOV_Manager::BoxRef MOOV_Manager::AddChildBox ( BoxRef parentRef, XMP_Uns32 childType, const void* dataPtr, XMP_Uns32 size ) +MOOV_Manager::BoxRef MOOV_Manager::AddChildBox ( BoxRef parentRef, XMP_Uns32 childType, const void* dataPtr, XMP_Uns32 size , const XMP_Uns8 * idUUID ) { BoxNode * parent = (BoxNode*)parentRef; XMP_Assert ( parent != 0 ); - parent->children.push_back ( BoxNode ( 0, childType, 0, 0 ) ); + if( childType == ISOMedia::k_uuid && idUUID != 0) + parent->children.push_back ( BoxNode ( 0, childType, 0, idUUID, 0 ) ); + else + parent->children.push_back ( BoxNode ( 0, childType, 0, 0 ) ); BoxNode * newNode = &parent->children.back(); this->SetBox ( newNode, dataPtr, size ); @@ -436,6 +450,8 @@ XMP_Uns32 MOOV_Manager::NewSubtreeSize ( const BoxNode & node, const std::string { XMP_Uns32 subtreeSize = 8 + node.contentSize; // All boxes will have 8 byte headers. + if( node.boxType == ISOMedia::k_uuid ) + subtreeSize += 16; // id of uuid is 16 bytes long if ( (node.boxType == ISOMedia::k_free) || (node.boxType == ISOMedia::k_wide) ) { } @@ -493,7 +509,12 @@ XMP_Uns8 * MOOV_Manager::AppendNewSubtree ( const BoxNode & node, const std::str XMP_Uns8 * boxOrigin = newPtr; // Save origin to fill in the final size. PutUns32BE ( node.boxType, (newPtr + 4) ); IncrNewPtr ( 8 ); - + if( node.boxType == ISOMedia::k_uuid ) // For uuid, additional 16 bytes is stored for ID + { + XMP_Enforce ( (XMP_Uns32)(newEnd - newPtr) >= ( 16 + node.contentSize ) ); + memcpy( newPtr, node.idUUID, 16 ); + IncrNewPtr ( 16 ); + } if ( node.contentSize != 0 ) { const XMP_Uns8 * content = PickContentPtr( node ); memcpy ( newPtr, content, node.contentSize ); diff --git a/XMPFiles/source/FormatSupport/MOOV_Support.hpp b/XMPFiles/source/FormatSupport/MOOV_Support.hpp index 7d34ca2..9dc785c 100644 --- a/XMPFiles/source/FormatSupport/MOOV_Support.hpp +++ b/XMPFiles/source/FormatSupport/MOOV_Support.hpp @@ -44,7 +44,11 @@ public: 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) {}; + XMP_Uns8 idUUID[16]; // ID of the uuid atom if present + BoxInfo() : boxType(0), childCount(0), contentSize(0), content(0) + { + memset ( idUUID ,0, 16 ); + }; }; // --------------------------------------------------------------------------------------------- @@ -73,10 +77,10 @@ public: void NoteChange(); - void SetBox ( BoxRef theBox, const void* dataPtr, XMP_Uns32 size ); - void SetBox ( const char * boxPath, const void* dataPtr, XMP_Uns32 size ); + void SetBox ( BoxRef theBox, const void* dataPtr, XMP_Uns32 size , const XMP_Uns8 * idUUID = 0 ); + void SetBox ( const char * boxPath, const void* dataPtr, XMP_Uns32 size , const XMP_Uns8 * idUUID = 0 ); - BoxRef AddChildBox ( BoxRef parentRef, XMP_Uns32 childType, const void * dataPtr, XMP_Uns32 size ); + BoxRef AddChildBox ( BoxRef parentRef, XMP_Uns32 childType, const void * dataPtr, XMP_Uns32 size , const XMP_Uns8 * idUUID = 0 ); // --------------------------------------------------------------------------------------------- // DeleteNthChild - Delete the overall n-th child, return true if there was one. @@ -195,15 +199,26 @@ private: 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. + XMP_Uns32 contentSize; // The current content size, does not include nested boxes or id. BoxList children; + XMP_Uns8 idUUID[16]; 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() : offset(0), boxType(0), headerSize(0), contentSize(0), changed(false) + { + memset ( idUUID, 0, 16 ); + }; BoxNode ( XMP_Uns32 _offset, XMP_Uns32 _boxType, XMP_Uns32 _headerSize, XMP_Uns32 _contentSize ) - : offset(_offset), boxType(_boxType), headerSize(_headerSize), contentSize(_contentSize), changed(false) {}; - + : offset(_offset), boxType(_boxType), headerSize(_headerSize), contentSize(_contentSize), changed(false) + { + memset ( idUUID, 0, 16 ); + }; + BoxNode ( XMP_Uns32 _offset, XMP_Uns32 _boxType, XMP_Uns32 _headerSize, const XMP_Uns8 * _idUUID, XMP_Uns32 _contentSize ) + : offset(_offset), boxType(_boxType), headerSize(_headerSize), contentSize(_contentSize), changed(false) + { + memcpy ( idUUID, _idUUID, 16 ); + }; }; XMP_Uns8 fileMode; diff --git a/XMPFiles/source/FormatSupport/RIFF.cpp b/XMPFiles/source/FormatSupport/RIFF.cpp index 4d9a0c1..6930190 100644 --- a/XMPFiles/source/FormatSupport/RIFF.cpp +++ b/XMPFiles/source/FormatSupport/RIFF.cpp @@ -52,21 +52,20 @@ Chunk* getChunk ( ContainerChunk* parent, RIFF_MetaHandler* handler ) return new ContainerChunk( parent, handler ); case kChunk_LIST: { - if ( level != 1 ) break; // only care on this level + if ( level != 1 ) break; // only care on this level // look further (beyond 4+4 = beyond id+size) to check on relevance file->Seek ( 8, kXMP_SeekFromCurrent ); XMP_Uns32 containerType = XIO::PeekUns32_LE ( file ); file->Seek ( -8, kXMP_SeekFromCurrent ); - bool isRelevantList = ( containerType== kType_INFO || containerType == kType_Tdat ); - if ( !isRelevantList ) break; - - return new ContainerChunk( parent, handler ); + bool isRelevantList = ( containerType== kType_INFO || containerType == kType_Tdat || containerType == kType_hdrl ); + 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 ); + 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 @@ -114,6 +113,13 @@ Chunk* getChunk ( ContainerChunk* parent, RIFF_MetaHandler* handler ) JunkChunk* r = new JunkChunk( parent, handler ); return r; } + case kChunk_IDIT: + { + if ( level != 2 ) break; // only care on this level + ValueChunk* r = new ValueChunk( parent, handler ); + handler->iditChunk = r; + return r; + } } // this "default:" section must be ouside switch bracket, to be // reachable by all those break statements above: @@ -267,7 +273,8 @@ ContainerChunk::ContainerChunk( ContainerChunk* parent, RIFF_MetaHandler* handle // 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 ) + ( this->id == kChunk_LIST && this->containerType == kType_Tdat ) || + ( this->id == kChunk_LIST && this->containerType == kType_hdrl ) ); XMP_Int64 endOfChunk = this->oldPos + this->oldSize; @@ -330,6 +337,8 @@ ContainerChunk::ContainerChunk( ContainerChunk* parent, RIFF_MetaHandler* handle handler->listInfoChunk = this; if ( level==1 && this->id==kChunk_LIST && this->containerType == kType_Tdat ) handler->listTdatChunk = this; + if ( level == 1 && this->id == kChunk_LIST && this->containerType == kType_hdrl ) + handler->listHdlrChunk = this; } else // skip non-interest container chunk { @@ -814,8 +823,6 @@ 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(); diff --git a/XMPFiles/source/FormatSupport/RIFF.hpp b/XMPFiles/source/FormatSupport/RIFF.hpp index e2451e5..4bb1f6e 100644 --- a/XMPFiles/source/FormatSupport/RIFF.hpp +++ b/XMPFiles/source/FormatSupport/RIFF.hpp @@ -70,6 +70,7 @@ namespace RIFF { // other relevant chunks const XMP_Uns32 kChunk_XMP = 0x584D505F; // "_PMX" + const XMP_Uns32 kChunk_IDIT = 0x54494449; // "TIDI" // relevant for Index Correction // LIST: diff --git a/XMPFiles/source/FormatSupport/RIFF_Support.cpp b/XMPFiles/source/FormatSupport/RIFF_Support.cpp index 152a53b..a64c98e 100644 --- a/XMPFiles/source/FormatSupport/RIFF_Support.cpp +++ b/XMPFiles/source/FormatSupport/RIFF_Support.cpp @@ -22,6 +22,8 @@ #include "XMPFiles/source/FormatSupport/RIFF_Support.hpp" #include "XMPFiles/source/FormatSupport/Reconcile_Impl.hpp" +#include <sstream> + #define MIN(a, b) ((a) < (b) ? (a) : (b)) using namespace RIFF; @@ -37,6 +39,9 @@ XMP_Int32 MAX_BEXT_SIZE = 100 * 1024 * 1024; XMP_Int32 CR8R_SIZE = 0x5C; XMP_Int32 PRML_SIZE = 0x122; +// IDIT chunk size +XMP_Int32 IDIT_SIZE = 0x1A; + static const char* sHexChars = "0123456789ABCDEF"; // Encode a string of raw data bytes into a HexString (w/o spaces, i.e. "DEADBEEF"). @@ -290,6 +295,89 @@ static void importBextChunkToXMP( RIFF_MetaHandler* handler, ValueChunk* bextChu } } // importBextChunkToXMP +static XMP_Int32 GetMonth( const char * valuePtr ) +{ + // This will check 3 characters (4,5,6) of the input and return corresponding months as 1,2,...,12 + // else it will return 0 + // Input should as follows : wda mon dd hh:mm:ss yyyy\n\0 + char firstChar = tolower( valuePtr[ 4 ] ); + char secondChar = tolower( valuePtr[ 5 ] ); + char thirdChar = tolower( valuePtr[ 6 ] ); + if ( firstChar == 'j' && secondChar == 'a' && thirdChar == 'n' ) + return 1; + if ( firstChar == 'f' && secondChar == 'e' && thirdChar == 'b' ) + return 2; + if ( firstChar == 'm' && secondChar == 'a' ) + { + if ( thirdChar == 'r' ) + return 3; + if ( thirdChar == 'y' ) + return 5; + } + if ( firstChar == 'a' && secondChar == 'p' && thirdChar == 'r' ) + return 4; + if ( firstChar == 'j' && secondChar == 'u' ) + { + if ( thirdChar == 'n' ) + return 6; + if ( thirdChar == 'l' ) + return 7; + } + if ( firstChar == 'a' && secondChar == 'u' && thirdChar == 'g' ) + return 8; + if ( firstChar == 's' && secondChar == 'e' && thirdChar == 'p' ) + return 9; + if ( firstChar == 'o' && secondChar == 'c' && thirdChar == 't' ) + return 10; + if ( firstChar == 'n' && secondChar == 'o' && thirdChar == 'v' ) + return 11; + if ( firstChar == 'd' && secondChar == 'e' && thirdChar == 'c' ) + return 12; + return 0; +} + +static XMP_Uns32 GatherUnsignedInt ( const char * strPtr, size_t count ) +{ + XMP_Uns32 value = 0; + const char * strEnd = strPtr + count; + + while ( strPtr < strEnd ) { + if ( *strPtr == ' ' ) ++strPtr; + else break; + } + + while ( strPtr < strEnd ) { + char ch = *strPtr; + if ( (ch < '0') || (ch > '9') ) break; + value = value*10 + (ch - '0'); + ++strPtr; + } + + return value; + +} // GatherUnsignedInt + +static void importIditChunkToXMP( RIFF_MetaHandler* handler, ValueChunk* iditChunk ) +{ + // if there iss a IDIT chunk, there is data... + handler->containsXMP = true; // very important for treatment on caller level + + // Size has been already checked in calling function + XMP_Enforce( iditChunk->oldSize == IDIT_SIZE + 8 ); + const char * valuePtr = iditChunk->oldValue.c_str(); + XMP_Enforce( valuePtr[ IDIT_SIZE - 2 ] == 0x0A ); + XMP_Enforce( valuePtr[ 13 ] == ':' && valuePtr[ 16 ] == ':' ); + XMP_DateTime dateTime; + dateTime.month = GetMonth( valuePtr ); + dateTime.day = GatherUnsignedInt( valuePtr + 8, 2 ); + dateTime.hour = GatherUnsignedInt( valuePtr + 11, 2 ); + dateTime.minute = GatherUnsignedInt( valuePtr + 14, 2 ); + dateTime.second = GatherUnsignedInt( valuePtr + 17, 2 ); + dateTime.year = GatherUnsignedInt( valuePtr + 20, 4 ); + handler->xmpObj.SetProperty_Date( kXMP_NS_EXIF, "DateTimeOriginal", dateTime ); + +} // importIditChunkToXMP + static void importPrmLToXMP( RIFF_MetaHandler* handler, ValueChunk* prmlChunk ) { bool haveXMP = false; @@ -540,6 +628,13 @@ void importProperties( RIFF_MetaHandler* handler ) } // if size sufficient } // handler->dispChunk + // IDIT chunk -------------------------------------------------------------- + if ( handler->parent->format == kXMP_AVIFile && // Only for AVI file + handler->iditChunk != 0 && handler->iditChunk->oldSize == IDIT_SIZE + 8 ) // Including header size i.e, ID + size + { + importIditChunkToXMP( handler, handler->iditChunk ); + } + } // importProperties //////////////////////////////////////////////////////////////////////////////// @@ -613,7 +708,7 @@ static void exportXMPtoBextChunk( RIFF_MetaHandler* handler, ValueChunk** bextCh // 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, 0 )) + if ( xmp->GetProperty( bextCodingHistory.ns, bextCodingHistory.prop, &value, (XMP_OptionBits *) kXMP_NoOptions )) { bextBufferSize += ((XMP_StringLen)value.size()) + 1 ; // add to size (and a trailing zero) } @@ -625,35 +720,35 @@ static void exportXMPtoBextChunk( RIFF_MetaHandler* handler, ValueChunk** bextCh // grab props, write into buffer, remove from XMP /////////////////////////// // bextDescription ------------------------------------------------ - if ( xmp->GetProperty( bextDescription.ns, bextDescription.prop, &value, 0 ) ) + if ( xmp->GetProperty( bextDescription.ns, bextDescription.prop, &value, (XMP_OptionBits *) kXMP_NoOptions ) ) { setBextField( &value, (XMP_Uns8*) buffer, 0, 256 ); xmp->DeleteProperty( bextDescription.ns, bextDescription.prop) ; chunkUsed = true; } // bextOriginator ------------------------------------------------- - if ( xmp->GetProperty( bextOriginator.ns , bextOriginator.prop, &value, 0 ) ) + if ( xmp->GetProperty( bextOriginator.ns , bextOriginator.prop, &value, (XMP_OptionBits *) kXMP_NoOptions ) ) { setBextField( &value, (XMP_Uns8*) buffer, 256, 32 ); xmp->DeleteProperty( bextOriginator.ns , bextOriginator.prop ); chunkUsed = true; } // bextOriginatorRef ---------------------------------------------- - if ( xmp->GetProperty( bextOriginatorRef.ns , bextOriginatorRef.prop, &value, 0 ) ) + if ( xmp->GetProperty( bextOriginatorRef.ns , bextOriginatorRef.prop, &value, (XMP_OptionBits *) 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, 0 ) ) + if ( xmp->GetProperty( bextOriginationDate.ns , bextOriginationDate.prop, &value, (XMP_OptionBits *) 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, 0 ) ) + if ( xmp->GetProperty( bextOriginationTime.ns , bextOriginationTime.prop, &value, (XMP_OptionBits *) kXMP_NoOptions ) ) { setBextField( &value, (XMP_Uns8*) buffer, 256+32+32+10, 8 ); xmp->DeleteProperty( bextOriginationTime.ns , bextOriginationTime.prop ); @@ -661,7 +756,7 @@ static void exportXMPtoBextChunk( RIFF_MetaHandler* handler, ValueChunk** bextCh } // bextTimeReference ---------------------------------------------- // thanx to friendly byte order, all 8 bytes can be written in one go: - if ( xmp->GetProperty( bextTimeReference.ns, bextTimeReference.prop, &value, 0 ) ) + if ( xmp->GetProperty( bextTimeReference.ns, bextTimeReference.prop, &value, (XMP_OptionBits *) kXMP_NoOptions ) ) { try { @@ -684,7 +779,7 @@ static void exportXMPtoBextChunk( RIFF_MetaHandler* handler, ValueChunk** bextCh xmp->DeleteProperty( bextVersion.ns, bextVersion.prop ); // bextUMID ------------------------------------------------------- - if ( xmp->GetProperty( bextUMID.ns, bextUMID.prop, &value, 0 ) ) + if ( xmp->GetProperty( bextUMID.ns, bextUMID.prop, &value, (XMP_OptionBits *) kXMP_NoOptions ) ) { std::string rawStr; @@ -703,7 +798,7 @@ static void exportXMPtoBextChunk( RIFF_MetaHandler* handler, ValueChunk** bextCh } // bextCodingHistory ---------------------------------------------- - if ( xmp->GetProperty( bextCodingHistory.ns, bextCodingHistory.prop, &value, 0 ) ) + if ( xmp->GetProperty( bextCodingHistory.ns, bextCodingHistory.prop, &value, (XMP_OptionBits *) kXMP_NoOptions ) ) { std::string ascii; convertToASCII( value.data(), (XMP_StringLen) value.size() , &ascii, (XMP_StringLen) value.size() ); @@ -809,6 +904,237 @@ static void exportXMPtoCr8rChunk ( RIFF_MetaHandler* handler, ValueChunk** cr8rC } +// Returns numbers of leap years between 1800 and provided year value. Both end values will be inclusive for getting this field. +// For leap year this will be handled later in GetIDITString() function +static XMP_Uns32 GetLeapYearsNumber( const XMP_Uns32 & year ) +{ + + XMP_Uns32 numLeapYears = ( year / 4 ); + numLeapYears -= ( year / 100 ); + numLeapYears += ( ( year + 200 ) / 400 ); // 200 is added becuase our base is 1800 which give modulas 200 by divinding with 400 + return numLeapYears; + +} //GetLeapYearsNumber + +static XMP_Uns32 GetDays( const XMP_Int32 & month, const bool &isLeapYear ) +{ + // Adding number of days as per last month of the provided year + // Leap year case is handled later + XMP_Uns32 numDays = 0; + switch ( month ) + { + case 2: + numDays = 31; + break; + case 3: + numDays = 31 + 28; + break; + case 4: + numDays = 31 + 28 + 31; + break; + case 5: + numDays = 31 + 28 + 31 + 30; + break; + case 6: + numDays = 31 + 28 + 31 + 30 + 31; + break; + case 7: + numDays = 31 + 28 + 31 + 30 + 31 + 30; + break; + case 8: + numDays = 31 + 28 + 31 + 30 + 31 + 30 + 31; + break; + case 9: + numDays = 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31; + break; + case 10: + numDays = 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30; + break; + case 11: + numDays = 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31; + break; + case 12: + numDays = 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30; + break; + default: + break; + } + + // Adding one day for leap year and month above than feb + if ( isLeapYear == true && month > 2 ) + numDays += 1; + + return numDays; + +} // GetDays + +static const std::string GetIDITString( const XMP_DateTime & targetDate ) +{ + // Date 1 Jan 1800 is treating as base date for handling this issue + // 1800 is chosen becuase of consistency in calender after 1752 + + XMP_Uns64 numOfDays = 0; // Specifies number of days after 1 jan 1800 + XMP_Uns32 year = targetDate.year - 1800; + + // 2000 was the first exception when year dividing by 100 was still a leap year + bool isLeapYear = ( year % 4 != 0 ) ? false : ( year % 100 != 0 ) ? true : ( ( year % 400 != 200 ) ? false : true ); + + // Adding days according to normal year and adjusting days for leap years + numOfDays = 365 * year; + numOfDays += GetLeapYearsNumber( year ); + + // Adding days according to the month + numOfDays += GetDays( targetDate.month, isLeapYear ); + + // GetLeapYearsNumber() function is also considering provided year for calculating number of leap numbers between provided year + // and 1800. This consideration is done by inclusive both end values. So both GetLeapYearsNumber() and GetDays() would have added + // extra day for higher end year for leap year. + // So, we need to decrease one day from number of days field + if ( isLeapYear ) + --numOfDays; + + // Adding days according to provided month + numOfDays += targetDate.day; + + // Weekday starting from Wednesday i.e., Wed will be the first day of the week. + // This day was choosen because 1 Jan 1800 was Wednesday + XMP_Uns8 weekDayNum = numOfDays % 7; + std::string weekDay; + switch ( weekDayNum ) + { + case 0: + weekDay = "Tue"; + break; + case 1: + weekDay = "Wed"; + break; + case 2: + weekDay = "Thu"; + break; + case 3: + weekDay = "Fri"; + break; + case 4: + weekDay = "Sat"; + break; + case 5: + weekDay = "Sun"; + break; + case 6: + weekDay = "Mon"; + break; + default: + break; + } + + // Stream to convert into IDIT format + std::stringstream iditStream; + iditStream << weekDay; + iditStream.put( ' ' ); + + // IDIT needs 3 character codes for month + const char * monthArray[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + iditStream << monthArray[ targetDate.month - 1 ]; + iditStream.put( ' ' ); + + if ( targetDate.day < 10 ) + iditStream.put( '0' ); + iditStream << targetDate.day; + iditStream.put( ' ' ); + + if ( targetDate.hour < 10 ) + iditStream.put( '0' ); + iditStream << targetDate.hour; + iditStream.put( ':' ); + + if ( targetDate.minute < 10 ) + iditStream.put( '0' ); + iditStream << targetDate.minute; + iditStream.put( ':' ); + + if ( targetDate.second < 10 ) + iditStream.put( '0' ); + iditStream << targetDate.second; + iditStream.put( ' ' ); + + // No need to handle casese for year 1 to 999 + /* + if ( targetDate.year < 10 ) + iditStream << " "; + else if ( targetDate.year < 100 ) + iditStream << " "; + else if ( targetDate.year < 1000 ) + iditStream << " "; + */ + // Year will be in the range of 1800-3999 + iditStream << targetDate.year; + + // Adding new line charcter for IDIT + iditStream.put( '\n' ); + + return iditStream.str(); + +} // GetIDITString + +static void exportXMPtoIDITChunk( RIFF_MetaHandler* handler ) +{ + // exif:DateTimeOriginal -> IDIT chunk + ContainerChunk * hdlrChunk = handler->listHdlrChunk; + if ( hdlrChunk == 0 ) + XMP_Throw( "Header of AVI file (hdlr chunk) must exists", kXMPErr_BadFileFormat ); + + XMP_DateTime dateTime; + bool propExists = handler->xmpObj.GetProperty_Date( kXMP_NS_EXIF, "DateTimeOriginal", &dateTime, 0 ); + if ( !propExists ) + { + if ( handler->iditChunk != 0 ) + { + // Exception would have thrown if we don't find hdlr chunk for AVI file + XMP_Assert( hdlrChunk != 0 ); + bool isSuccess = hdlrChunk->removeValue( kChunk_IDIT ); + if ( !isSuccess ) + XMP_Throw( "Removal of IDIT block fails", kXMPErr_InternalFailure ); + handler->iditChunk = 0; + hdlrChunk->hasChange = true; + } + // Else no need to do anything + } + else + { + if ( dateTime.year < 1800 || dateTime.year > 3999 ) + XMP_Throw( "For IDIT block, XMP currently supports years in between 1800 and 3999 (Both inclusive).", kXMPErr_InternalFailure ); + + /* + Conversion need to be done from XMP date time to IDIT structure. + XMP_DateTime accepts any value but IDIT needs to have weekday, month-day, month, year and time. + */ + + // Silently modifying dateTime for invalid dates. + if ( dateTime.month < 1 ) + dateTime.month = 1; + if ( dateTime.month > 12 ) + dateTime.month = 12; + if ( dateTime.day < 1 ) + dateTime.day = 1; + if ( dateTime.day > 31 ) + dateTime.day = 31; + + const std::string iditString = GetIDITString( dateTime ); + + // If no IDIT exits then create one + if ( handler->iditChunk == 0 ) + handler->iditChunk = new ValueChunk( hdlrChunk, std::string(), kChunk_IDIT ); + else if ( strncmp( iditString.c_str(), handler->iditChunk->oldValue.c_str(), IDIT_SIZE ) == 0 ) // Equal + return; + + // Setting the IDIT value + handler->iditChunk->hasChange = true; + handler->iditChunk->SetValue( iditString, true ); + + } + +} // exportXMPtoIDITChunk + static void exportXMPtoListChunk( XMP_Uns32 id, XMP_Uns32 containerType, RIFF_MetaHandler* handler, ContainerChunk** listChunk, Mapping mapping[]) { @@ -887,12 +1213,12 @@ void exportAndRemoveProperties ( RIFF_MetaHandler* handler ) exportXMPtoCr8rChunk ( handler, &handler->cr8rChunk ); - // 1/4 BWF Bext extension chunk ----------------------------------------------- + // 1/5 BWF Bext extension chunk ----------------------------------------------- if ( handler->parent->format == kXMP_WAVFile ) { // applies only to WAV exportXMPtoBextChunk ( handler, &handler->bextChunk ); } - // 2/4 DISP chunk + // 2/5 DISP chunk if ( handler->parent->format == kXMP_WAVFile ) { // create for WAVE only std::string actualLang, xmpValue; @@ -928,12 +1254,16 @@ void exportAndRemoveProperties ( RIFF_MetaHandler* handler ) } - // 3/4 LIST:INFO + // 3/5 LIST:INFO exportXMPtoListChunk ( kChunk_LIST, kType_INFO, handler, &handler->listInfoChunk, listInfoProps ); - // 4/4 LIST:Tdat + // 4/5 LIST:Tdat exportXMPtoListChunk ( kChunk_LIST, kType_Tdat, handler, &handler->listTdatChunk, listTdatProps ); + // 5/5 LIST:HDRL:IDIT + if ( handler->parent->format == kXMP_AVIFile ) + exportXMPtoIDITChunk ( handler ); + } } // namespace RIFF diff --git a/XMPFiles/source/FormatSupport/ReconcileIPTC.cpp b/XMPFiles/source/FormatSupport/ReconcileIPTC.cpp index df9d676..142cdd6 100644 --- a/XMPFiles/source/FormatSupport/ReconcileIPTC.cpp +++ b/XMPFiles/source/FormatSupport/ReconcileIPTC.cpp @@ -193,7 +193,7 @@ void PhotoDataUtils::ImportIPTC_Date ( XMP_Uns8 dateID, const IPTC_Manager & ipt IPTC_Manager::DataSetInfo dsInfo; size_t count = iptc.GetDataSet ( dateID, &dsInfo ); - if ( count == 0 ) return; + if ( count == 0 || dsInfo.dataLen == 0 ) return; size_t chPos, digits; XMP_DateTime xmpDate; @@ -205,7 +205,7 @@ void PhotoDataUtils::ImportIPTC_Date ( XMP_Uns8 dateID, const IPTC_Manager & ipt xmpDate.year = (xmpDate.year * 10) + (dsInfo.dataPtr[chPos] - '0'); } - if ( dsInfo.dataPtr[chPos] == '-' ) ++chPos; + if ( ( chPos < dsInfo.dataLen ) && 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'); @@ -213,7 +213,7 @@ void PhotoDataUtils::ImportIPTC_Date ( XMP_Uns8 dateID, const IPTC_Manager & ipt if ( xmpDate.month < 1 ) xmpDate.month = 1; if ( xmpDate.month > 12 ) xmpDate.month = 12; - if ( dsInfo.dataPtr[chPos] == '-' ) ++chPos; + if ( ( chPos < dsInfo.dataLen ) && 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'); @@ -227,7 +227,7 @@ void PhotoDataUtils::ImportIPTC_Date ( XMP_Uns8 dateID, const IPTC_Manager & ipt // Now add the time portion if present. count = iptc.GetDataSet ( timeID, &dsInfo ); - if ( count != 0 ) { + if ( count != 0 && dsInfo.dataLen > 0 ) { chPos = 0; for ( digits = 0; digits < 2; ++digits, ++chPos ) { @@ -237,7 +237,7 @@ void PhotoDataUtils::ImportIPTC_Date ( XMP_Uns8 dateID, const IPTC_Manager & ipt if ( xmpDate.hour < 0 ) xmpDate.hour = 0; if ( xmpDate.hour > 23 ) xmpDate.hour = 23; - if ( dsInfo.dataPtr[chPos] == ':' ) ++chPos; + if ( ( chPos < dsInfo.dataLen ) && 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'); @@ -245,7 +245,7 @@ void PhotoDataUtils::ImportIPTC_Date ( XMP_Uns8 dateID, const IPTC_Manager & ipt if ( xmpDate.minute < 0 ) xmpDate.minute = 0; if ( xmpDate.minute > 59 ) xmpDate.minute = 59; - if ( dsInfo.dataPtr[chPos] == ':' ) ++chPos; + if ( ( chPos < dsInfo.dataLen ) && 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'); @@ -255,11 +255,11 @@ void PhotoDataUtils::ImportIPTC_Date ( XMP_Uns8 dateID, const IPTC_Manager & ipt xmpDate.hasTime = true; - if ( (dsInfo.dataPtr[chPos] != ' ') && (dsInfo.dataPtr[chPos] != 0) ) { // Tolerate a missing TZ. + if ( ( chPos < dsInfo.dataLen ) && (dsInfo.dataPtr[chPos] != ' ') && (dsInfo.dataPtr[chPos] != 0) ) { // Tolerate a missing TZ. - if ( dsInfo.dataPtr[chPos] == '+' ) { + if ( ( chPos < dsInfo.dataLen ) && ( dsInfo.dataPtr[chPos] == '+' ) ) { xmpDate.tzSign = kXMP_TimeEastOfUTC; - } else if ( dsInfo.dataPtr[chPos] == '-' ) { + } else if ( ( chPos < dsInfo.dataLen ) && dsInfo.dataPtr[chPos] == '-' ) { xmpDate.tzSign = kXMP_TimeWestOfUTC; } else if ( chPos != dsInfo.dataLen ) { return; // The DataSet is ill-formed. @@ -273,7 +273,7 @@ void PhotoDataUtils::ImportIPTC_Date ( XMP_Uns8 dateID, const IPTC_Manager & ipt if ( xmpDate.tzHour < 0 ) xmpDate.tzHour = 0; if ( xmpDate.tzHour > 23 ) xmpDate.tzHour = 23; - if ( dsInfo.dataPtr[chPos] == ':' ) ++chPos; + if ( ( chPos < dsInfo.dataLen ) && 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'); @@ -367,8 +367,6 @@ static void ImportIPTC_SubjectCode ( const IPTC_Manager & iptc, SXMPMeta * xmp ) void PhotoDataUtils::Import2WayIPTC ( const IPTC_Manager & iptc, SXMPMeta * xmp, int iptcDigestState ) { - if ( iptcDigestState == kDigestMatches ) return; // Ignore the IPTC if the digest matches. - std::string oldStr, newStr; IPTC_Writer oldIPTC; @@ -386,9 +384,9 @@ void PhotoDataUtils::Import2WayIPTC ( const IPTC_Manager & iptc, SXMPMeta * xmp, bool haveXMP = xmp->DoesPropertyExist ( thisDS.xmpNS, thisDS.xmpProp ); newCount = PhotoDataUtils::GetNativeInfo ( iptc, thisDS.dsNum, iptcDigestState, haveXMP, &newInfo ); - if ( newCount == 0 ) continue; // GetNativeInfo returns 0 for ignored local text. - - if ( iptcDigestState == kDigestMissing ) { + if ( ( newCount == 0 ) || ( newInfo.dataLen == 0 ) ) continue; // GetNativeInfo returns 0 for ignored local text. + // For no data in dataset, don't import or delete anything + if ( iptcDigestState == kDigestMissing || iptcDigestState == kDigestMatches ) { if ( haveXMP ) continue; // Keep the existing XMP. } else if ( ! PhotoDataUtils::IsValueDifferent ( iptc, oldIPTC, thisDS.dsNum ) ) { continue; // Don't import values that match the previous export. diff --git a/XMPFiles/source/FormatSupport/ReconcileTIFF.cpp b/XMPFiles/source/FormatSupport/ReconcileTIFF.cpp index aa4aea6..ee58379 100644 --- a/XMPFiles/source/FormatSupport/ReconcileTIFF.cpp +++ b/XMPFiles/source/FormatSupport/ReconcileTIFF.cpp @@ -306,9 +306,7 @@ size_t PhotoDataUtils::GetNativeInfo ( const IPTC_Manager & iptc, XMP_Uns8 id, i { size_t iptcCount = 0; - if ( (digestState == kDigestDiffers) || ((digestState == kDigestMissing) && (! haveXMP)) ) { - iptcCount = iptc.GetDataSet ( id, info ); - } + iptcCount = iptc.GetDataSet ( id, info ); if ( ignoreLocalText && (iptcCount > 0) && (! iptc.UsingUTF8()) ) { // Check to see if the new value(s) should be ignored. @@ -2283,11 +2281,10 @@ static void Import3WayDateTime ( XMP_Uns16 exifTag, const TIFF_Manager & exif, c 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 ) { + if ( haveIPTC && ((iptcDigestState == kDigestDiffers) || (!haveXMP && !haveExif)) ) { PhotoDataUtils::ImportIPTC_Date ( iptcDS, iptc, xmp ); @@ -2360,7 +2357,6 @@ void PhotoDataUtils::Import3WayItems ( const TIFF_Manager & exif, const IPTC_Man 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)) ); @@ -2370,7 +2366,7 @@ void PhotoDataUtils::Import3WayItems ( const TIFF_Manager & exif, const IPTC_Man } } - if ( haveIPTC ) { + if ( haveIPTC && ((iptcDigestState == kDigestDiffers) || (!haveXMP && !haveExif)) ) { 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() ); @@ -2383,11 +2379,10 @@ void PhotoDataUtils::Import3WayItems ( const TIFF_Manager & exif, const IPTC_Man 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 ) { + if ( haveIPTC && ((iptcDigestState == kDigestDiffers) || (!haveXMP && !haveExif)) ) { 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() ); @@ -2401,11 +2396,10 @@ void PhotoDataUtils::Import3WayItems ( const TIFF_Manager & exif, const IPTC_Man 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 ) { + if ( haveIPTC && ((iptcDigestState == kDigestDiffers) || (!haveXMP && !haveExif)) ) { 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", diff --git a/XMPFiles/source/FormatSupport/SVG_Adapter.cpp b/XMPFiles/source/FormatSupport/SVG_Adapter.cpp new file mode 100644 index 0000000..750848d --- /dev/null +++ b/XMPFiles/source/FormatSupport/SVG_Adapter.cpp @@ -0,0 +1,464 @@ +// ================================================================================================= +// Copyright 2015 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. +// +// This file includes implementation of SVG metadata, according to Scalable Vector Graphics (SVG) 1.1 Specification. +// "https://www.w3.org/TR/2003/REC-SVG11-20030114/" +// Copyright © 1994-2002 World Wide Web Consortium, (Massachusetts Institute of Technology, +// Institut National de Recherche en Informatique et en Automatique, Keio University). +// All Rights Reserved . http://www.w3.org/Consortium/Legal +// +// ================================================================================================= + +#include "public/include/XMP_Environment.h" // ! Must be the first #include! + +#include "XMPFiles/source/FormatSupport/SVG_Adapter.hpp" + +#include "third-party/expat/lib/expat.h" +#include <string.h> + +using namespace std; + +#if XMP_WinBuild +#pragma warning ( disable : 4996 ) // '...' was declared deprecated +#endif + +#define FullNameSeparator '@' + +// ================================================================================================= + +static void StartNamespaceDeclHandler( void * userData, XMP_StringPtr prefix, XMP_StringPtr uri ); +static void EndNamespaceDeclHandler( void * userData, XMP_StringPtr prefix ); +static void StartElementHandler( void * userData, XMP_StringPtr name, XMP_StringPtr* attrs ); +static void EndElementHandler( void * userData, XMP_StringPtr name ); +static void CharacterDataHandler( void * userData, XMP_StringPtr cData, int len ); +static void ProcessingInstructionHandler( void * userData, XMP_StringPtr target, XMP_StringPtr data ); +static void DeclarationHandler( void *userData, const XML_Char *version, const XML_Char *encoding, int standalone ); + +static bool isRequireData = false; +static XMP_Uns32 reqDepth = 0; + +// Flag is provided to support behaviour like Expat Adapter +#if BanAllEntityUsage + +// For now we do this by banning DOCTYPE entirely. This is easy and consistent with what is +// available in recent Java XML parsers. Another, somewhat less drastic, approach would be to +// ban all entity declarations. We can't allow declarations and ban references, Expat does not +// call the SkippedEntityHandler for references in attribute values. + +// ! Standard entities (&, <, >, ", ', and numeric character references) are +// ! not banned. Expat handles them transparently no matter what. + +static void StartDoctypeDeclHandler( void * userData, XMP_StringPtr doctypeName, + XMP_StringPtr sysid, XMP_StringPtr pubid, int has_internal_subset ); + +#endif + +// ================================================================================================= + +SVG_Adapter::SVG_Adapter() : parser(0), registeredNamespaces(0), firstSVGElementOffset(-1), depth(0) +{ + + this->parser = XML_ParserCreateNS( 0, FullNameSeparator ); + + if ( this->parser == 0 ) { + XMP_Error error( kXMPErr_NoMemory, "Failure creating Expat parser" ); + this->NotifyClient( kXMPErrSev_ProcessFatal, error ); + } + else + { + this->registeredNamespaces = new XMP_NamespaceTable(); + + XML_SetUserData( this->parser, this ); + XML_SetNamespaceDeclHandler( this->parser, StartNamespaceDeclHandler, EndNamespaceDeclHandler ); + XML_SetElementHandler( this->parser, StartElementHandler, EndElementHandler ); + XML_SetCharacterDataHandler( this->parser, CharacterDataHandler ); + XML_SetProcessingInstructionHandler( this->parser, ProcessingInstructionHandler ); + XML_SetXmlDeclHandler( this->parser, DeclarationHandler ); + +#if BanAllEntityUsage + XML_SetStartDoctypeDeclHandler( this->parser, StartDoctypeDeclHandler ); + isAborted = false; +#endif + + this->parseStack.push_back( &this->tree ); // Push the XML root node. + } +} // SVG_Adapter::SVG_Adapter + +// ================================================================================================= + +SVG_Adapter::~SVG_Adapter() +{ + if ( this->parser != 0 ) XML_ParserFree( this->parser ); + this->parser = 0; + + if ( this->registeredNamespaces != 0 ) delete ( this->registeredNamespaces ); + this->registeredNamespaces = 0; + +} // SVG_Adapter::~SVG_Adapter + +// ================================================================================================= + +OffsetStruct SVG_Adapter::GetElementOffsets( std::string elementName ) +{ + IteratorStringOffsetStruct iterator = this->mOffsetsMap.find( elementName ); + if ( iterator != mOffsetsMap.end() ) + return iterator->second; + + return OffsetStruct(); +} // SVG_Adapter::GetElementOffset + +// ================================================================================================= + +void SVG_Adapter::RegisterElement( std::string elementName, std::string reqParent ) +{ + IteratorStringOffsetStruct iterator = this->mOffsetsMap.find( elementName ); + if ( iterator == mOffsetsMap.end() ) + { + this->mOffsetsMap.insert( iterator, std::pair<std::string, OffsetStruct>( elementName, OffsetStruct( reqParent ) ) ); + } +} // SVG_Adapter::RegisterElement + +// ================================================================================================= + +XMP_Int64 SVG_Adapter::GetPIOffset( std::string PIName, XMP_Uns32 requiredIndex /* = 1 */ ) +{ + if ( this->parser != 0 ) + { + std::pair<IteratorStringXMP_Int64, IteratorStringXMP_Int64> iterator = this->mPIWithOffsetMap.equal_range( PIName ); + if ( iterator.first != iterator.second ) + { + XMP_Uns32 index = 0; + IteratorStringXMP_Int64 indexIterator = iterator.first; + for ( ; index < ( requiredIndex - 1 ) && indexIterator != iterator.second; ++indexIterator, ++index ); + if ( index == requiredIndex - 1 ) + return indexIterator->second; + } + } + return -1; +} // SVG_Adapter::GetPIOffset + +// ================================================================================================= + +void SVG_Adapter::RegisterPI( std::string PIName ) +{ + IteratorStringXMP_Int64 iterator = this->mPIWithOffsetMap.find( PIName ); + if ( iterator == mPIWithOffsetMap.end() ) + { + this->mPIWithOffsetMap.insert( iterator, std::pair<std::string, XMP_Int64>( PIName, -1 ) ); + } +} // SVG_Adapter::RegisterPI + +// ================================================================================================= + +XMP_Bool SVG_Adapter::IsParsingRequire( ) +{ + for ( IteratorStringOffsetStruct iterator = this->mOffsetsMap.begin(); iterator != this->mOffsetsMap.end(); ++iterator ) + { + if ( iterator->second.startOffset == -1 || iterator->second.endOffset == -1 || iterator->second.nextOffset == -1 ) + return true; + } + return false; +} // SVG_Adapter::IsParsingRequire + +// ================================================================================================= + +// This version of parsing throw an error +void SVG_Adapter::ParseBuffer( const void * buffer, size_t length, bool last /* = true */ ) +{ + enum XML_Status status; + + if ( length == 0 ) { // Expat does not like empty buffers. + if ( !last ) return; + const char * kOneSpace = " "; + buffer = kOneSpace; + length = 1; + } + + status = XML_Parse( this->parser, ( const char * ) buffer, static_cast< XMP_StringLen >( length ), last ); + +#if BanAllEntityUsage + if ( this->isAborted ) { + XMP_Error error( kXMPErr_BadXML, "DOCTYPE is not allowed" ) + this->NotifyClient( kXMPErrSev_Recoverable, error ); + } +#endif + + if ( status != XML_STATUS_OK ) { + + XMP_Error error( kXMPErr_BadXML, "Invalid SVG file" ); + this->NotifyClient( kXMPErrSev_OperationFatal, error ); + + } + +} // SVG_Adapter::ParseBuffer + +// ================================================================================================= + +// This version of parsing doesn't throw error but returns false if any error is encountered +// This is required just for checkformat +XMP_Bool SVG_Adapter::ParseBufferNoThrow( const void * buffer, size_t length, bool last /* = true */ ) +{ + enum XML_Status status; + + if ( length == 0 ) { // Expat does not like empty buffers. + if ( !last ) return false; + const char * kOneSpace = " "; + buffer = kOneSpace; + length = 1; + } + + status = XML_Parse( this->parser, ( const char * ) buffer, static_cast< XMP_StringLen >( length ), last ); + +#if BanAllEntityUsage + if ( this->isAborted ) { + XMP_Error error( kXMPErr_BadXML, "DOCTYPE is not allowed" ) + this->NotifyClient( kXMPErrSev_Recoverable, error ); + } +#endif + + if ( status != XML_STATUS_OK ) + return false; + else + return true; + +} // SVG_Adapter::ParseBufferNoThrow + +// ================================================================================================= + +static void ParseFullNS( XMP_StringPtr fullName, string & NS, string &localName ) +{ + // Expat delivers the full name as a catenation of namespace URI, separator, and local name. + size_t sepPos = strlen( fullName ); + for ( --sepPos; sepPos > 0; --sepPos ) { + if ( fullName[ sepPos ] == FullNameSeparator ) break; + } + + if ( fullName[ sepPos ] == FullNameSeparator ) + { + localName = fullName + sepPos + 1; + NS.assign( fullName, sepPos ); + } + else + localName = fullName; + +} // ParseFullNS + +// ================================================================================================= + +static void StartNamespaceDeclHandler( void * userData, XMP_StringPtr prefix, XMP_StringPtr uri ) +{ + + SVG_Adapter * thiz = ( SVG_Adapter* ) userData; + + if ( prefix == 0 ) prefix = "_dflt_"; // Have default namespace. + if ( uri == 0 ) return; // Ignore, have xmlns:pre="", no URI to register. + + ( void ) thiz->registeredNamespaces->Define( uri, prefix, 0, 0 ); + +} // StartNamespaceDeclHandler + +// ================================================================================================= + +static void EndNamespaceDeclHandler( void * userData, XMP_StringPtr prefix ) +{ + IgnoreParam( userData ); + IgnoreParam( prefix ); + if ( prefix == 0 ) prefix = "_dflt_"; // Have default namespace. + +} // EndNamespaceDeclHandler + +// ================================================================================================= + +static void StartElementHandler( void * userData, XMP_StringPtr name, XMP_StringPtr* attrs ) +{ + // In case, if name is NULL then ParseBuffer would return with error status + SVG_Adapter * thiz = ( SVG_Adapter* ) userData; + thiz->depth++; + if ( thiz->depth > 3 ) + return; + else if ( thiz->firstSVGElementOffset == -1 && thiz->depth == 2 ) + thiz->firstSVGElementOffset = XML_GetCurrentByteIndex( thiz->parser ); + else + { + if ( !thiz->mPrevRequiredElement.empty() ) + { + IteratorStringOffsetStruct iterator = thiz->mOffsetsMap.find( thiz->mPrevRequiredElement ); + if ( iterator != thiz->mOffsetsMap.end() ) + iterator->second.nextOffset = XML_GetCurrentByteIndex( thiz->parser ); + thiz->mPrevRequiredElement.clear(); + } + } + + string NS, localName; + ParseFullNS( name, NS, localName ); + + IteratorStringOffsetStruct iterator = thiz->mOffsetsMap.find( localName ); + if ( iterator == thiz->mOffsetsMap.end() && localName != "svg" ) + return; + + XML_Node * parentNode = thiz->parseStack.back(); + XML_Node * elemNode = new XML_Node( parentNode, "", kElemNode ); + + if ( strncmp( localName.c_str(), name, localName.length() ) != 0 ) + { + XMP_StringPtr prefix; + XMP_StringLen prefixLen; + bool found = thiz->registeredNamespaces->GetPrefix( NS.c_str(), &prefix, &prefixLen ); + if ( !found ) { + XMP_Error error( kXMPErr_ExternalFailure, "Unknown URI in Expat full name" ); + thiz->NotifyClient( kXMPErrSev_OperationFatal, error ); + } + elemNode->ns = NS; + elemNode->nsPrefixLen = prefixLen; // ! Includes the ':'. + + if ( strcmp( prefix, "_dflt_:" ) == 0 ) + { + elemNode->name = localName; + elemNode->nsPrefixLen = 0; + } + else + { + elemNode->name = prefix; + elemNode->name += localName; + } + } + else + { + elemNode->name = localName; // The name is not in a namespace. + } + + parentNode->content.push_back( elemNode ); + thiz->parseStack.push_back( elemNode ); + + if ( iterator != thiz->mOffsetsMap.end() && iterator->second.parent == parentNode->name ) + { + reqDepth = thiz->depth; + isRequireData = true; + if ( iterator->second.startOffset == -1 ) + iterator->second.startOffset = XML_GetCurrentByteIndex( thiz->parser ); + } + else + { + isRequireData = false; + } + +} // StartElementHandler + +// ================================================================================================= + +static void EndElementHandler( void * userData, XMP_StringPtr name ) +{ + SVG_Adapter * thiz = ( SVG_Adapter* ) userData; + + thiz->depth--; + if ( thiz->depth > 2 ) + return; + + string NS, localName; + ParseFullNS( name, NS, localName ); + + IteratorStringOffsetStruct iterator = thiz->mOffsetsMap.find( localName ); + if ( iterator != thiz->mOffsetsMap.end() ) + { + // StartOffset flag is provided to reject the elements of non-required namespace + // Endoffset flag is provided to maintain state of first available element + // Depth flag is provided to support for workflow like <title><title>...</title></title> + if ( iterator->second.startOffset != -1 && iterator->second.endOffset == -1 && thiz->depth == reqDepth - 1 ) + { + iterator->second.endOffset = XML_GetCurrentByteIndex( thiz->parser ); + thiz->mPrevRequiredElement = localName; + } + } + else if ( localName != "svg" ) + return; + + ( void ) thiz->parseStack.pop_back(); + +} // EndElementHandler + +// ================================================================================================= + +static void CharacterDataHandler( void * userData, XMP_StringPtr cData, int len ) +{ + if ( !isRequireData ) + return; + isRequireData = false; + SVG_Adapter * thiz = ( SVG_Adapter* ) userData; + + if ( ( cData == 0 ) || ( len == 0 ) ) { cData = ""; len = 0; } + + XML_Node * parentNode = thiz->parseStack.back(); + XML_Node * cDataNode = new XML_Node( parentNode, "", kCDataNode ); + + cDataNode->value.assign( cData, len ); + parentNode->content.push_back( cDataNode ); + +} // CharacterDataHandler + +// ================================================================================================= + +static void ProcessingInstructionHandler( void * userData, XMP_StringPtr target, XMP_StringPtr data ) +{ + + if ( target == NULL || strncmp( target, "xpacket", 7 ) != 0 ) + return; // Ignore all PIs except the XMP packet wrapper. + SVG_Adapter * thiz = ( SVG_Adapter* ) userData; + XML_Node * parentNode = thiz->parseStack.back(); + if ( parentNode->name != "metadata" ) + return; + IteratorStringXMP_Int64 iterator = thiz->mPIWithOffsetMap.find( target ); + if ( iterator != thiz->mPIWithOffsetMap.end() ) + { + if ( iterator->second == -1 ) + iterator->second = XML_GetCurrentByteIndex( thiz->parser ); + else + thiz->mPIWithOffsetMap.insert( std::pair<std::string, XMP_Int64>( target, XML_GetCurrentByteIndex( thiz->parser ) ) ); + } + + if ( data == 0 ) data = ""; + XML_Node * piNode = new XML_Node( parentNode, target, kPINode ); + + piNode->value.assign( data ); + parentNode->content.push_back( piNode ); + +} // ProcessingInstructionHandler + +// ================================================================================================= + +static void DeclarationHandler( void *userData, const XML_Char *version, const XML_Char *encoding, int standalone ) +{ + if ( encoding == NULL || strlen( encoding ) != 5 || + ( tolower( encoding[ 0 ] ) == 'u' + && tolower( encoding[ 1 ] ) == 't' + && tolower( encoding[ 2 ] ) == 'f' + && encoding[ 3 ] == '-' + && encoding[ 4 ] == '8' ) ) + return; + else + { + SVG_Adapter * thiz = ( SVG_Adapter* ) userData; + ( void ) ( XML_StopParser( thiz->parser, false ) ); + } +} // DeclarationHandler + +// ================================================================================================= + +#if BanAllEntityUsage +static void StartDoctypeDeclHandler( void * userData, XMP_StringPtr doctypeName, + XMP_StringPtr sysid, XMP_StringPtr pubid, int has_internal_subset ) +{ + IgnoreParam( userData ); + + SVG_Adapter * thiz = ( SVG_Adapter* ) userData; + + thiz->isAborted = true; // ! Can't throw an exception across the plain C Expat frames. + ( void ) XML_StopParser( thiz->parser, XML_FALSE /* not resumable */ ); + +} // StartDoctypeDeclHandler +#endif + +// ================================================================================================= diff --git a/XMPFiles/source/FormatSupport/SVG_Adapter.hpp b/XMPFiles/source/FormatSupport/SVG_Adapter.hpp new file mode 100644 index 0000000..0c325a5 --- /dev/null +++ b/XMPFiles/source/FormatSupport/SVG_Adapter.hpp @@ -0,0 +1,79 @@ +#ifndef __SVG_Adapter_hpp__ +#define __SVG_Adapter_hpp__ + +// ================================================================================================= +// Copyright 2015 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. +// +// This file includes implementation of SVG metadata, according to Scalable Vector Graphics (SVG) 1.1 Specification. +// "https://www.w3.org/TR/2003/REC-SVG11-20030114/" +// Copyright © 1994-2002 World Wide Web Consortium, (Massachusetts Institute of Technology, +// Institut National de Recherche en Informatique et en Automatique, Keio University). +// All Rights Reserved . http://www.w3.org/Consortium/Legal +// +// ================================================================================================= + +#include "public/include/XMP_Environment.h" // ! Must be the first #include! +#include "source/XMLParserAdapter.hpp" + +// ================================================================================================= +// Derived SVG parser adapter for Expat. +// ================================================================================================= + +#ifndef BanAllEntityUsage +#define BanAllEntityUsage 0 +#endif + +struct XML_ParserStruct; // ! Hack to avoid exposing expat.h to all clients. +typedef struct XML_ParserStruct *XML_Parser; +typedef std::map<std::string, XMP_Int64>::iterator IteratorStringXMP_Int64; + +struct OffsetStruct +{ + XMP_Int64 startOffset; + XMP_Int64 nextOffset; + XMP_Int64 endOffset; + std::string parent; + OffsetStruct() : startOffset( -1 ), nextOffset( -1 ), endOffset( -1 ) {} + OffsetStruct( std::string reqParent ) : startOffset( -1 ), nextOffset( -1 ), endOffset( -1 ), parent( reqParent ) {} +}; + +typedef std::map<std::string, OffsetStruct>::iterator IteratorStringOffsetStruct; + +class SVG_Adapter : public XMLParserAdapter { +public: + + XML_Parser parser; + XMP_NamespaceTable * registeredNamespaces; + +#if BanAllEntityUsage + bool isAborted; +#endif + + SVG_Adapter( ); + virtual ~SVG_Adapter(); + + virtual void ParseBuffer( const void * buffer, size_t length, bool last = true ); + virtual XMP_Bool ParseBufferNoThrow( const void * buffer, size_t length, bool last = true ); + + virtual OffsetStruct GetElementOffsets( std::string elementName ); + virtual void RegisterElement( std::string elementName, std::string reqParent ); + + virtual XMP_Int64 GetPIOffset( std::string PIName, XMP_Uns32 requiredIndex = 1 ); + virtual void RegisterPI( std::string PIName ); + virtual XMP_Bool IsParsingRequire(); + + std::multimap<std::string, XMP_Int64> mPIWithOffsetMap; + std::map<std::string, OffsetStruct> mOffsetsMap; + XMP_Int64 firstSVGElementOffset; + + std::string mPrevRequiredElement; + XMP_Uns32 depth; +}; + +// ================================================================================================= + +#endif // __SVG_Adapter_hpp__ diff --git a/XMPFiles/source/FormatSupport/TIFF_FileWriter.cpp b/XMPFiles/source/FormatSupport/TIFF_FileWriter.cpp index da4d260..ec92c8e 100644 --- a/XMPFiles/source/FormatSupport/TIFF_FileWriter.cpp +++ b/XMPFiles/source/FormatSupport/TIFF_FileWriter.cpp @@ -571,7 +571,7 @@ 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 */, bool isAlreadyLittle /*= false */ ) { this->DeleteExistingInfo(); this->memParsed = true; @@ -733,7 +733,18 @@ XMP_Uns32 TIFF_FileWriter::ProcessMemoryIFD ( XMP_Uns32 ifdOffset, XMP_Uns8 ifd ifdPtr += (2 + tagCount*12); ifdInfo.origNextIFD = this->GetUns32 ( ifdPtr ); - +// The following code modifies a file in case it is invalid, we should keep this fix so that we can track this issue if we receive client bugs for this +#if 0 + if (ifdInfo.origNextIFD != 0) { + if ( (ifdInfo.origNextIFD < 8) || (ifdInfo.origNextIFD > (this->tiffLength - kEmptyIFDLength)) ) { + // Next IFD offset is invalid. Ignore it. + ifdInfo.origNextIFD = 0; + // Should we try to patch it? + ifdInfo.changed = true; + this->changed = true; + } + } +#endif return ifdInfo.origNextIFD; } // TIFF_FileWriter::ProcessMemoryIFD @@ -780,13 +791,13 @@ void TIFF_FileWriter::ParseFileStream ( XMP_IO* fileRef ) } const InternalTagInfo* exifIFDTag = this->FindTagInIFD ( kTIFF_PrimaryIFD, kTIFF_ExifIFDPointer ); - if ( (exifIFDTag != 0) && (exifIFDTag->type == kTIFF_LongType) && (exifIFDTag->count == 1) ) { + if ( ( exifIFDTag != 0 ) && ( ( exifIFDTag->type == kTIFF_LongType || exifIFDTag->type == kTIFF_IFDType ) && ( exifIFDTag->count == 1 ) ) ) { XMP_Uns32 exifOffset = this->GetUns32 ( exifIFDTag->dataPtr ); (void) this->ProcessFileIFD ( kTIFF_ExifIFD, exifOffset, fileRef ); } const InternalTagInfo* gpsIFDTag = this->FindTagInIFD ( kTIFF_PrimaryIFD, kTIFF_GPSInfoIFDPointer ); - if ( (gpsIFDTag != 0) && (gpsIFDTag->type == kTIFF_LongType) && (gpsIFDTag->count == 1) ) { + if ( ( gpsIFDTag != 0 ) && ( ( gpsIFDTag->type == kTIFF_LongType || gpsIFDTag->type == kTIFF_IFDType ) && ( gpsIFDTag->count == 1 ) ) ) { XMP_Uns32 gpsOffset = this->GetUns32 ( gpsIFDTag->dataPtr ); if ( IsOffsetValid (gpsOffset, 8, ifdLimit ) ) { // Remove a bad GPS IFD offset. (void) this->ProcessFileIFD ( kTIFF_GPSInfoIFD, gpsOffset, fileRef ); @@ -798,7 +809,7 @@ void TIFF_FileWriter::ParseFileStream ( XMP_IO* fileRef ) } const InternalTagInfo* interopIFDTag = this->FindTagInIFD ( kTIFF_ExifIFD, kTIFF_InteroperabilityIFDPointer ); - if ( (interopIFDTag != 0) && (interopIFDTag->type == kTIFF_LongType) && (interopIFDTag->dataLen == 4) ) { + if ( ( interopIFDTag != 0 ) && ( ( interopIFDTag->type == kTIFF_LongType || interopIFDTag->type == kTIFF_IFDType ) && ( interopIFDTag->dataLen == 4 ) ) ) { XMP_Uns32 interopOffset = this->GetUns32 ( interopIFDTag->dataPtr ); if ( IsOffsetValid (interopOffset, 8, ifdLimit ) ) { // Remove a bad Interoperability IFD offset. (void) this->ProcessFileIFD ( kTIFF_InteropIFD, interopOffset, fileRef ); diff --git a/XMPFiles/source/FormatSupport/TIFF_MemoryReader.cpp b/XMPFiles/source/FormatSupport/TIFF_MemoryReader.cpp index ea1b686..c1a0d44 100644 --- a/XMPFiles/source/FormatSupport/TIFF_MemoryReader.cpp +++ b/XMPFiles/source/FormatSupport/TIFF_MemoryReader.cpp @@ -528,8 +528,8 @@ bool TIFF_MemoryReader::GetTag_EncodedString ( XMP_Uns8 ifd, XMP_Uns16 id, std:: // ==================================== // *** Need to tell TIFF/Exif from TIFF/EP, DNG files are the latter. - -void TIFF_MemoryReader::ParseMemoryStream ( const void* data, XMP_Uns32 length, bool copyData /* = true */ ) +// isAlredyLittle is provided for case when data contain no information about Endianess, So need not to check for header +void TIFF_MemoryReader::ParseMemoryStream ( const void* data, XMP_Uns32 length, bool copyData /* = true */, bool isAlredyLittle /* = false */ ) { // Get rid of any current TIFF. @@ -560,13 +560,31 @@ void TIFF_MemoryReader::ParseMemoryStream ( const void* data, XMP_Uns32 length, this->tiffLength = length; XMP_Uns32 ifdLimit = this->tiffLength - 6; // An IFD must start before this offset. - - // Find and process the primary, Exif, GPS, and Interoperability IFDs. - - XMP_Uns32 primaryIFDOffset = this->CheckTIFFHeader ( this->tiffStream, length ); + XMP_Uns32 primaryIFDOffset = 0; XMP_Uns32 tnailIFDOffset = 0; + // Find and process the primary, Exif, GPS, and Interoperability IFDs. + + // If already is in little Endian then no need to check for Check TIFF header + if ( isAlredyLittle ) + { + this->nativeEndian = ! kBigEndianHost; + this->GetUns16 = GetUns16LE; + this->GetUns32 = GetUns32LE; + this->GetFloat = GetFloatLE; + this->GetDouble = GetDoubleLE; + + this->PutUns16 = PutUns16LE; + this->PutUns32 = PutUns32LE; + this->PutFloat = PutFloatLE; + this->PutDouble = PutDoubleLE; + tnailIFDOffset = this->ProcessOneIFD ( primaryIFDOffset, kTIFF_PrimaryIFD, true ); + } + else + { + primaryIFDOffset = this->CheckTIFFHeader ( this->tiffStream, length ); - if ( primaryIFDOffset != 0 ) tnailIFDOffset = this->ProcessOneIFD ( primaryIFDOffset, kTIFF_PrimaryIFD ); + 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 ) { @@ -611,14 +629,25 @@ void TIFF_MemoryReader::ParseMemoryStream ( const void* data, XMP_Uns32 length, // ================================================================================================= // TIFF_MemoryReader::ProcessOneIFD // ================================ +// ModifiedInitialCheck is provided for case when data contain no header -XMP_Uns32 TIFF_MemoryReader::ProcessOneIFD ( XMP_Uns32 ifdOffset, XMP_Uns8 ifd ) +XMP_Uns32 TIFF_MemoryReader::ProcessOneIFD ( XMP_Uns32 ifdOffset, XMP_Uns8 ifd, bool ModifiedInitialCheck /* = false */ ) { TweakedIFDInfo& ifdInfo = this->containedIFDs[ifd]; - if ( (ifdOffset < 8) || (ifdOffset > (this->tiffLength - kEmptyIFDLength)) ) { - XMP_Error error(kXMPErr_BadTIFF, "Bad IFD offset" ); - this->NotifyClient ( kXMPErrSev_FileFatal, error ); + if( ModifiedInitialCheck ) + { + if ((ifdOffset > (this->tiffLength)) ) { + XMP_Error error(kXMPErr_BadTIFF, "Bad IFD offset" ); + this->NotifyClient ( kXMPErrSev_FileFatal, error ); + } + } + else + { + if ( (ifdOffset < 8) || (ifdOffset > (this->tiffLength - kEmptyIFDLength)) ) { + XMP_Error error(kXMPErr_BadTIFF, "Bad IFD offset" ); + this->NotifyClient ( kXMPErrSev_FileFatal, error ); + } } XMP_Uns8* ifdPtr = this->tiffStream + ifdOffset; diff --git a/XMPFiles/source/FormatSupport/TIFF_Support.hpp b/XMPFiles/source/FormatSupport/TIFF_Support.hpp index d4e2f4d..5ed5965 100644 --- a/XMPFiles/source/FormatSupport/TIFF_Support.hpp +++ b/XMPFiles/source/FormatSupport/TIFF_Support.hpp @@ -91,10 +91,11 @@ enum { // Constants for the type field of a tag, as defined by TIFF. kTIFF_SRationalType = 10, kTIFF_FloatType = 11, kTIFF_DoubleType = 12, - kTIFF_LastType = 12 + kTIFF_IFDType = 13, + kTIFF_LastType = kTIFF_IFDType }; -static const size_t kTIFF_TypeSizes[] = { 0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8 }; +static const size_t kTIFF_TypeSizes[] = { 0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8, 4 }; static const bool kTIFF_IsIntegerType[] = { 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0 }; static const bool kTIFF_IsRationalType[] = { 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0 }; @@ -639,8 +640,9 @@ 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; + + // isAlredyLittle is provided for case when data contain no information about Endianess, So need not to check for header + virtual void ParseMemoryStream ( const void* data, XMP_Uns32 length, bool copyData = true, bool isAlreadyLittle = false ) = 0; virtual void ParseFileStream ( XMP_IO* fileRef ) = 0; virtual void IntegrateFromPShop6 ( const void * buriedPtr, size_t buriedLen ) = 0; @@ -736,7 +738,8 @@ public: bool IsChanged() { return false; }; bool IsLegacyChanged() { return false; }; - void ParseMemoryStream ( const void* data, XMP_Uns32 length, bool copyData = true ); + // isAlredyLittle is provided for case when data contain no information about Endianess, So need not to check for header + void ParseMemoryStream ( const void* data, XMP_Uns32 length, bool copyData = true, bool isAlreadyLittle = false ); void ParseFileStream ( XMP_IO* fileRef ) { NotAppropriate(); }; void IntegrateFromPShop6 ( const void * buriedPtr, size_t buriedLen ) { NotAppropriate(); }; @@ -778,7 +781,8 @@ private: static void SortIFD ( TweakedIFDInfo* thisIFD ); - XMP_Uns32 ProcessOneIFD ( XMP_Uns32 ifdOffset, XMP_Uns8 ifd ); + // ModifiedInitialCheck is provided for case when data contain no header + XMP_Uns32 ProcessOneIFD ( XMP_Uns32 ifdOffset, XMP_Uns8 ifd, bool ModifiedInitialCheck = false ); const TweakedIFDEntry* FindTagInIFD ( XMP_Uns8 ifd, XMP_Uns16 id ) const; @@ -846,7 +850,8 @@ public: enum { kDoNotCopyData = false }; - void ParseMemoryStream ( const void* data, XMP_Uns32 length, bool copyData = true ); + // isAlredyLittle is provided for case when data contain no information about Endianess, So need not to check for header + void ParseMemoryStream ( const void* data, XMP_Uns32 length, bool copyData = true, bool isAlreadyLittle = false ); void ParseFileStream ( XMP_IO* fileRef ); void IntegrateFromPShop6 ( const void * buriedPtr, size_t buriedLen ); diff --git a/XMPFiles/source/FormatSupport/TimeConversionUtils.hpp b/XMPFiles/source/FormatSupport/TimeConversionUtils.hpp index bf7ed04..73ba9ff 100644 --- a/XMPFiles/source/FormatSupport/TimeConversionUtils.hpp +++ b/XMPFiles/source/FormatSupport/TimeConversionUtils.hpp @@ -12,7 +12,10 @@ #include "public/include/XMP_Environment.h" // ! XMP_Environment.h must be the first included header. #include "public/include/XMP_Const.h" - +#include <string> +#include <sstream> +#include <iomanip> +#include <cmath> #include "XMPFiles/source/XMPFiles_Impl.hpp" namespace TimeConversionUtils { diff --git a/XMPFiles/source/FormatSupport/WAVE/CartMetadata.h b/XMPFiles/source/FormatSupport/WAVE/CartMetadata.h index 29266a7..b007afc 100644 --- a/XMPFiles/source/FormatSupport/WAVE/CartMetadata.h +++ b/XMPFiles/source/FormatSupport/WAVE/CartMetadata.h @@ -58,6 +58,14 @@ public: struct StoredCartTimer { XMP_Uns32 usage; XMP_Uns32 value; + + bool operator == (const StoredCartTimer & other ) const { + return usage == other.usage && value == other.value; + } + + bool operator != ( const StoredCartTimer & other ) const { + return usage != other.usage || value != other.value; + } }; enum { kPostTimerLength = 8 }; diff --git a/XMPFiles/source/FormatSupport/WAVE/INFOMetadata.cpp b/XMPFiles/source/FormatSupport/WAVE/INFOMetadata.cpp index 335acd7..9bdeb94 100644 --- a/XMPFiles/source/FormatSupport/WAVE/INFOMetadata.cpp +++ b/XMPFiles/source/FormatSupport/WAVE/INFOMetadata.cpp @@ -152,7 +152,7 @@ XMP_Uns64 INFOMetadata::serialize( XMP_Uns8** outBuffer ) { TValueObject<std::string>* strObj = dynamic_cast<TValueObject<std::string>*>(iter->second); - XMP_Uns32 chunkSize = kChunkHeaderSize + strObj->getValue().length(); + XMP_Uns32 chunkSize = kChunkHeaderSize + strObj->getValue().length() + 1; // 1 byte is added for NULL termination string if( chunkSize & 1 ) { @@ -194,7 +194,7 @@ XMP_Uns64 INFOMetadata::serialize( XMP_Uns8** outBuffer ) TValueObject<std::string>* strObj = dynamic_cast<TValueObject<std::string>*>(iter->second); std::string value = strObj->getValue(); XMP_Uns32 id = iter->first; - XMP_Uns32 size = value.length(); + XMP_Uns32 size = value.length() + 1; // Null terminated string if( size & 1 && strObj->hasChanged() ) { @@ -221,7 +221,7 @@ XMP_Uns64 INFOMetadata::serialize( XMP_Uns8** outBuffer ) memcpy( buffer+offset+kSizeChunkID, &size, kSizeChunkSize ); //size has been changed in little endian format. Change it back to bigendina size = LE.getUns32( &size ); - memcpy( buffer+offset+kChunkHeaderSize, value.c_str(), size ); + memcpy( buffer+offset+kChunkHeaderSize, value.c_str(), value.length() ); // // update pointer diff --git a/XMPFiles/source/FormatSupport/WAVE/WAVEReconcile.cpp b/XMPFiles/source/FormatSupport/WAVE/WAVEReconcile.cpp index 8c3067b..db15dab 100644 --- a/XMPFiles/source/FormatSupport/WAVE/WAVEReconcile.cpp +++ b/XMPFiles/source/FormatSupport/WAVE/WAVEReconcile.cpp @@ -10,6 +10,8 @@ #include "public/include/XMP_Environment.h" // ! XMP_Environment.h must be the first included header. #include "public/include/XMP_Const.h" +#include <string.h> + #include "XMPFiles/source/FormatSupport/WAVE/WAVEReconcile.h" #include "XMPFiles/source/FormatSupport/WAVE/DISPMetadata.h" #include "XMPFiles/source/FormatSupport/WAVE/INFOMetadata.h" @@ -26,6 +28,7 @@ #include "XMPFiles/source/FormatSupport/Reconcile_Impl.hpp" + using namespace IFF_RIFF; // ************** legacy Mappings ***************** // @@ -57,7 +60,7 @@ static const MetadataPropertyInfo kBextProperties[] = { NULL } }; -static const char * kDM_takeNumber = "takeNumber"; +static const char * kDM_shotNumber = "shotNumber"; static const char * kDM_audioSampleType = "audioSampleType"; static const char * kDM_scene = "scene"; static const char * kDM_tapeName = "tapeName"; @@ -68,12 +71,17 @@ static const char * kDM_startTimecode = "startTimecode"; static const char * kDM_timeFormat = "timeFormat"; static const char * kDM_timeValue = "timeValue"; static const char * kDM_good = "good"; +static const char * kIXML_trackList = "trackList"; +static const char * kIXML_channelIndex = "channelIndex"; +static const char * kIXML_interleaveIndex = "interleaveIndex"; +static const char * kIXML_Name = "name"; +static const char * kIXML_Function = "function"; static const MetadataPropertyInfo kiXMLProperties[] = { // XMP NS XMP Property Name Native Metadata Identifier Native Datatype XMP Datatype Delete Priority ExportPolicy { kXMP_NS_DM, kDM_tapeName, iXMLMetadata::kTape, kNativeType_StrUTF8, kXMPType_Simple, false, false, kExport_Always }, //xmpDM:tapeName <-> iXML:TAPE - { kXMP_NS_DM, kDM_takeNumber, iXMLMetadata::kTake, kNativeType_Uns64, kXMPType_Simple, false, false, kExport_Always }, //xmpDM:tapeName <-> iXML:TAPE + { kXMP_NS_DM, kDM_shotNumber, iXMLMetadata::kTake, kNativeType_StrUTF8, kXMPType_Simple, false, false, kExport_Always }, //xmpDM:shotNumber <-> iXML:TAKE { kXMP_NS_DM, kDM_scene, iXMLMetadata::kScene, kNativeType_StrUTF8, kXMPType_Simple, false, false, kExport_Always }, //xmpDM:scene <-> iXML:SCENE { kXMP_NS_DM, kDM_logComment, iXMLMetadata::kNote, kNativeType_StrUTF8, kXMPType_Simple, false, false, kExport_Always }, //xmpDM:logComment <-> iXML:NOTE { kXMP_NS_DM, kDM_projectName, iXMLMetadata::kProject, kNativeType_StrUTF8, kXMPType_Simple, false, false, kExport_Always }, //xmpDM:project <-> iXML:PROJECT @@ -93,6 +101,7 @@ static const MetadataPropertyInfo kiXMLProperties[] = // special case for timeReference // bext:timeReference <-> iXML:TIMESTAMP_SAMPLES_SINCE_MIDNIGHT_HO and iXML:TIMESTAMP_SAMPLES_SINCE_MIDNIGHT_HI // special case for startTimeCode // xmpDM:startTimecode <-> iXML:TIMECODE_RATE, iXML:TIMECODE_FLAG and bext:timeReference. { kXMP_NS_BWF, kBWF_timeStampSampleRate, iXMLMetadata::kTimeStampSampleRate, kNativeType_Uns64, kXMPType_Simple, false, false, kExport_NoDelete }, // bext::timeStampSampleRate <-> iXML + // special case for TRACK_LIST // ixml:Track_List <-> ixml:trackList { NULL } }; @@ -985,6 +994,37 @@ void IFF_RIFF::WAVEReconcile::exportSpecialXMPToiXML( SXMPMeta & inXMP, IMetadat outNativeMeta.deleteValue( iXMLMetadata::kBWFTimeReferenceHigh ); outNativeMeta.deleteValue( iXMLMetadata::kBWFTimeReferenceLow ); } + + // track list + try { + if ( inXMP.DoesPropertyExist( kXMP_NS_iXML, kIXML_trackList ) ) { + XMP_OptionBits options( 0 ); + if ( inXMP.GetProperty( kXMP_NS_iXML, kIXML_trackList, NULL, &options ) && + XMP_OptionIsSet( options, kXMP_PropArrayIsOrdered ) ) + { + XMP_Index count = inXMP.CountArrayItems( kXMP_NS_iXML, kIXML_trackList ); + std::vector< iXMLMetadata::TrackListInfo > trackListInfo( count ); + for ( XMP_Index i = 0; i < count; i++ ) { + iXMLMetadata::TrackListInfo & trackRef = trackListInfo[i]; + std::string trackPath; + SXMPUtils::ComposeArrayItemPath( kXMP_NS_iXML, kIXML_trackList, i + 1, &trackPath ); + std::string fieldPath; + SXMPUtils::ComposeStructFieldPath( kXMP_NS_iXML, trackPath.c_str(), kXMP_NS_iXML, kIXML_channelIndex, &fieldPath ); + XMP_Int64 int64Value; + inXMP.GetProperty_Int64( kXMP_NS_iXML, fieldPath.c_str(), &int64Value, &options ); + trackRef.mChannelIndex = int64Value; + inXMP.GetStructField( kXMP_NS_iXML, trackPath.c_str(), kXMP_NS_iXML, kIXML_Name, &trackRef.mName, &options ); + inXMP.GetStructField( kXMP_NS_iXML, trackPath.c_str(), kXMP_NS_iXML, kIXML_Function, &trackRef.mFunction, &options ); + } + outNativeMeta.setArray< iXMLMetadata::TrackListInfo >( iXMLMetadata::kTrackList, trackListInfo.data(), count ); + inXMP.DeleteProperty( kXMP_NS_iXML, kIXML_trackList ); + } + } else { + outNativeMeta.deleteValue( iXMLMetadata::kTrackList ); + } + } catch( ... ) { + // do nothing + } } bool IFF_RIFF::WAVEReconcile::exportSpecialiXMLToXMP( IMetadata & inNativeMeta, SXMPMeta & outXMP ) @@ -1063,5 +1103,30 @@ bool IFF_RIFF::WAVEReconcile::exportSpecialiXMLToXMP( IMetadata & inNativeMeta, } } + // special case for iXML:trackList + if ( inNativeMeta.valueExists( iXMLMetadata::kTrackList ) ) + { + XMP_Uns32 countOfTracks( 0 ); + const iXMLMetadata::TrackListInfo * trackInfoArray = + inNativeMeta.getArray< iXMLMetadata::TrackListInfo >( iXMLMetadata::kTrackList, countOfTracks ); + if ( countOfTracks > 0 && trackInfoArray != NULL ) { + outXMP.DeleteProperty( kXMP_NS_iXML, kIXML_trackList ); + outXMP.SetProperty( kXMP_NS_iXML, kIXML_trackList, 0, kXMP_PropArrayIsOrdered ); + for ( XMP_Uns32 i = 0; i < countOfTracks; i++ ) { + std::string trackPath; + SXMPUtils::ComposeArrayItemPath( kXMP_NS_iXML, kIXML_trackList, i + 1, &trackPath ); + const iXMLMetadata::TrackListInfo & ref = trackInfoArray[i]; + std::string value; + SXMPUtils::ConvertFromInt64( ref.mChannelIndex, "%llu", &value ); + outXMP.SetStructField( kXMP_NS_iXML, trackPath.c_str(), kXMP_NS_iXML, kIXML_channelIndex, value ); + if ( ref.mName.size() > 0 ) + outXMP.SetStructField( kXMP_NS_iXML, trackPath.c_str(), kXMP_NS_iXML, kIXML_Name, ref.mName ); + if ( ref.mFunction.size() > 0 ) + outXMP.SetStructField( kXMP_NS_iXML, trackPath.c_str(), kXMP_NS_iXML, kIXML_Function, ref.mFunction ); + } + changed = true; + } + } + return changed; } diff --git a/XMPFiles/source/FormatSupport/WAVE/iXMLMetadata.cpp b/XMPFiles/source/FormatSupport/WAVE/iXMLMetadata.cpp index d6ea068..a153bc7 100644 --- a/XMPFiles/source/FormatSupport/WAVE/iXMLMetadata.cpp +++ b/XMPFiles/source/FormatSupport/WAVE/iXMLMetadata.cpp @@ -21,7 +21,7 @@ namespace IFF_RIFF { static const char * tagNames[ iXMLMetadata::kLastEntry ] = { "TAPE", //kTape, // std::string - "TAKE", //kTake, // XMP_Uns64 + "TAKE", //kTake, // std::string "SCENE", //kScene, // std::string "NOTE", //kNote, // std::string "PROJECT", //kProject, // std::string @@ -44,11 +44,18 @@ namespace IFF_RIFF { "TIMESTAMP_SAMPLE_RATE", //kTimeStampSampleRate, // XMP_Uns64 "TIMESTAMP_SAMPLES_SINCE_MIDNIGHT_LO", //kTimeStampSampleSinceMidnightLow, // XMP_Uns32 "TIMESTAMP_SAMPLES_SINCE_MIDNIGHT_HI", //kTimeStampSampleSinceMidnightHi, // XMP_Uns32 + "TRACK_LIST", //kTrackList // std::vector< TrackListInfo > }; static const char * rootTagName = "BWFXML"; static const char * speedTagName = "SPEED"; static const char * bextTagName = "BEXT"; + static const char * trackCountTagName = "TRACK_COUNT"; + static const char * trackTagName = "TRACK"; + static const char * trackChannelIndexTagName = "CHANNEL_INDEX"; + static const char * trackInterleaveIndexTagName = "INTERLEAVE_INDEX"; + static const char * trackNameTagName = "NAME"; + static const char * trackFunctionTagName = "FUNCTION"; iXMLMetadata::iXMLMetadata() : mExpatAdapter( NULL ) @@ -194,6 +201,7 @@ namespace IFF_RIFF { switch( id ) { case kTape: + case kTake: case kScene: case kNote: case kProject: @@ -213,7 +221,6 @@ namespace IFF_RIFF { } break; - case kTake: case kFileSampleRate: case kAudioBitDepth: case kBWFTimeReferenceLow: @@ -230,6 +237,19 @@ namespace IFF_RIFF { ret = false; break; + case kTrackList: + ret = true; + { + TArrayObject< TrackListInfo > * trackListInfoArrayObj = dynamic_cast< TArrayObject< TrackListInfo > *>( &valueObj ); + if ( trackListInfoArrayObj ) { + XMP_Uns32 nElements ( 0 ); + const TrackListInfo * trackListInfoPtr = trackListInfoArrayObj->getArray( nElements ); + if ( nElements > 0 && trackListInfoPtr != NULL ) + ret = false; + } + } + break; + default: ret = true; } @@ -252,6 +272,15 @@ namespace IFF_RIFF { XMPFileHandler::NotifyClient( mErrorCallback, severity, error ); } + static std::string & Trim( std::string & str ) { + static const char * whiteSpaceChars = " \t\n\r\v"; + size_t pos = str.find_last_not_of( whiteSpaceChars ); + if ( pos != std::string::npos ) { + str.erase( pos + 1 ); + } + return str; + } + static XMP_Uns64 ConvertStringToUns64( const std::string & strValue ) { int count; char nextCh; @@ -269,7 +298,7 @@ namespace IFF_RIFF { if ( strValue.size() > 0 ) { XMP_Uns64 uValue; try { - uValue = ConvertStringToUns64( strValue ); + uValue = ConvertStringToUns64( Trim( strValue ) ); } catch( ... ) { // some nodes like tape can be non integer also. Treat it as warning XMP_Error error( kXMPErr_BadFileFormat, "iXML Metadata reconciliation failure: node is supposed to have integer value" ); @@ -298,7 +327,7 @@ namespace IFF_RIFF { // top level properties ParseAndSetStringProperty( mRootNode, kTape ); - ParseAndSetIntegerProperty( mRootNode, kTake ); + ParseAndSetStringProperty( mRootNode, kTake ); ParseAndSetStringProperty( mRootNode, kScene ); ParseAndSetStringProperty( mRootNode, kNote ); ParseAndSetStringProperty( mRootNode, kProject ); @@ -331,6 +360,12 @@ namespace IFF_RIFF { ParseAndSetStringProperty( bextNode, kBWFHistory ); ParseAndSetStringProperty( bextNode, kBWFUMID ); } + + // TRACK_LIST + XML_Node * trackListNode = mRootNode->GetNamedElement( "", tagNames[ kTrackList ] ); + if ( trackListNode ) { + ParseAndSetTrackListInfo( trackListNode ); + } } void iXMLMetadata::UpdateStringProperty( XML_Node * parentNode, XMP_Uns32 id ) { @@ -431,10 +466,67 @@ namespace IFF_RIFF { } } + void iXMLMetadata::UpdateTrackListInfo( XML_Node * parentNode ) { + if ( valueExists( kTrackList ) ) { + XMP_Uns32 size; + const TrackListInfo * arrayObj( NULL ); + try { + arrayObj = this->getArray< TrackListInfo >( kTrackList, size ); + } catch( ... ) { + XMP_Error error( kXMPErr_BadValue, "iXML Metadata reconciliation failure: expected the array of TrackListInfo type" ); + NotifyClient( kXMPErrSev_Recoverable, error ); + return; + } + + if ( size > 0 ) { + XML_Node * node = parentNode->GetNamedElement( "", tagNames[ kTrackList ] ); + + if ( node == NULL ) { + node = new XML_Node( parentNode, tagNames[ kTrackList ], kElemNode ); + if ( node == NULL ) { + XMP_Error error( kXMPErr_NoMemory, "Unable to create new objects" ); + NotifyClient( kXMPErrSev_OperationFatal, error ); + return; + } + parentNode->content.push_back( node ); + } + + // add count + std::string count = ConvertUns64ToString( size ); + UpdateXMLNode( node, trackCountTagName, count ); + + for ( XMP_Uns32 i = 0; i < size; i++ ) { + XML_Node * track = node->GetNamedElement( "", trackTagName, i ); + if ( track == NULL ) { + track = new XML_Node( parentNode, trackTagName, kElemNode ); + if ( track == NULL ) { + XMP_Error error( kXMPErr_NoMemory, "Unable to create new objects" ); + NotifyClient( kXMPErrSev_OperationFatal, error ); + return; + } + node->content.push_back( track ); + } + const TrackListInfo & ref = arrayObj[i]; + count = ConvertUns64ToString( ref.mChannelIndex ); + UpdateXMLNode( track, trackChannelIndexTagName, count ); + if ( ref.mChannelIndex != i + 1 ) + count = ConvertUns64ToString( i + 1 ); + UpdateXMLNode( track, trackInterleaveIndexTagName, count ); + UpdateXMLNode( track, trackNameTagName, ref.mName ); + UpdateXMLNode( track, trackFunctionTagName, ref.mFunction ); + } + } else { + RemoveXMLNode( parentNode, tagNames[ kTrackList ] ); + } + } else { + RemoveXMLNode( parentNode, tagNames[ kTrackList ] ); + } + } + void iXMLMetadata::UpdateProperties() { // top level properties UpdateStringProperty( mRootNode, kTape ); - UpdateIntegerProperty( mRootNode, kTake ); + UpdateStringProperty( mRootNode, kTake ); UpdateStringProperty( mRootNode, kScene ); UpdateStringProperty( mRootNode, kNote ); UpdateStringProperty( mRootNode, kProject ); @@ -468,6 +560,7 @@ namespace IFF_RIFF { UpdateStringProperty( bextNode, kBWFUMID ); } + UpdateTrackListInfo( mRootNode ); } bool iXMLMetadata::valueValid( XMP_Uns32 id, ValueObject * valueObj ) { @@ -477,7 +570,7 @@ namespace IFF_RIFF { break; case kTake: - return validateInt( valueObj ); + return validateStringSize( valueObj ); break; case kScene: @@ -752,8 +845,12 @@ namespace IFF_RIFF { } std::string iXMLMetadata::ParseStringValue( XML_Node * parentNode, XMP_Uns32 id ) { + return ParseStringValue( parentNode, tagNames[ id ] ); + } + + std::string iXMLMetadata::ParseStringValue( XML_Node * parentNode, const char * tagName, bool recoverable ) { std::string nodeValue; - XML_Node * node = parentNode->GetNamedElement( "", tagNames[ id ] ); + XML_Node * node = parentNode->GetNamedElement( "", tagName ); if ( node ) { if ( node->IsLeafContentNode() && node->content.size() != 0 ) { size_t lengthOfValue = node->content[0]->value.size(); @@ -762,12 +859,84 @@ namespace IFF_RIFF { } } else { XMP_Error error( kXMPErr_BadBlockFormat, "iXML Metadata reconciliation failure: node was supposed to be a leaf node" ); - NotifyClient( kXMPErrSev_Recoverable, error ); + NotifyClient( recoverable ? kXMPErrSev_Recoverable : kXMPErrSev_OperationFatal, error ); } + } else { + XMP_Error error ( recoverable ? kXMPErrSev_Recoverable : kXMPErrSev_OperationFatal, "iXML Metadata reconciliation failure: node not present" ); } return nodeValue; } + XMP_Uns64 iXMLMetadata::ParseUns64Value( XML_Node * parentNode, const char * tagName ) { + std::string strValue = ParseStringValue( parentNode, tagName, false ); + + if ( strValue.size() > 0 ) { + XMP_Uns64 uValue; + try { + uValue = ConvertStringToUns64( Trim( strValue ) ); + } catch( ... ) { + // some nodes like tape can be non integer also. Treat it as warning + XMP_Error error( kXMPErr_BadFileFormat, "iXML Metadata reconciliation failure: node is supposed to have integer value" ); + NotifyClient( kXMPErrSev_OperationFatal, error ); + } + return uValue; + } + return Max_XMP_Uns64; + } + + void iXMLMetadata::ParseAndSetTrackListInfo( XML_Node * parentNode ) { + XMP_Uns64 trackCount( 0 ); + try { + trackCount = ParseUns64Value( parentNode, trackCountTagName ); + } catch( ... ) { + XMP_Error error( kXMPErr_BadBlockFormat, "iXML Metadata reconciliation failure: failed parsing track list" ); + NotifyClient( kXMPErrSev_Recoverable, error ); + return; + } + std::vector< TrackListInfo > trackInfoListVector( trackCount ); + + for ( size_t i = 0; i < trackCount; i++ ) { + XML_Node * trackElement = parentNode->GetNamedElement( "", trackTagName, i ); + if ( trackElement ) { + XMP_Uns64 channelIndex( 0 ), interleaveIndex( 0 ); + std::string name, function; + + try { + channelIndex = ParseUns64Value( trackElement, trackChannelIndexTagName ); + interleaveIndex = ParseUns64Value( trackElement, trackInterleaveIndexTagName ); + name = ParseStringValue( trackElement, trackNameTagName ); + function = ParseStringValue( trackElement, trackFunctionTagName ); + } catch( ... ) { + XMP_Error error( kXMPErr_BadBlockFormat, "iXML Metadata reconciliation failure: failed parsing track list" ); + NotifyClient( kXMPErrSev_Recoverable, error ); + return; + } + + // check value of interleave index it should be between 1 and trackCount. + if ( interleaveIndex > 0 && interleaveIndex <= trackCount + && trackInfoListVector[ interleaveIndex - 1 ].mChannelIndex == 0 ) + { + TrackListInfo & currentElement = trackInfoListVector[ interleaveIndex - 1 ]; + currentElement.mChannelIndex = channelIndex; + currentElement.mName = name; + currentElement.mFunction = function; + } else { + XMP_Error error( kXMPErr_BadBlockFormat, "iXML Metadata reconciliation failure: interleave index is not correct" ); + NotifyClient( kXMPErrSev_Recoverable, error ); + return; + } + } else { + XMP_Error error( kXMPErr_BadBlockFormat, "iXML Metadata reconciliation failure: number of track elements is less than expected" ); + NotifyClient( kXMPErrSev_Recoverable, error ); + return; + } + } + + if ( trackCount > 0 ) { + this->setArray( kTrackList, trackInfoListVector.data(), ( XMP_Uns32 ) trackInfoListVector.size() ); + } + } + bool iXMLMetadata::validateTimeCodeFlag( ValueObject * value ) { bool returnValue = validateStringSize( value, 2, 3 ); if ( returnValue ) { @@ -812,4 +981,8 @@ namespace IFF_RIFF { return false; } + bool iXMLMetadata::validateTrackListInfo( ValueObject * value ) { + return true; + } + } diff --git a/XMPFiles/source/FormatSupport/WAVE/iXMLMetadata.h b/XMPFiles/source/FormatSupport/WAVE/iXMLMetadata.h index 815fe85..5c18268 100644 --- a/XMPFiles/source/FormatSupport/WAVE/iXMLMetadata.h +++ b/XMPFiles/source/FormatSupport/WAVE/iXMLMetadata.h @@ -28,10 +28,35 @@ namespace IFF_RIFF { class iXMLMetadata : public IMetadata { public: + + class TrackListInfo { + public: + TrackListInfo() : mChannelIndex( 0 ) {} + + TrackListInfo( XMP_Uns64 channelIndex, const std::string & name, const std::string & function ) + : mChannelIndex( channelIndex ) + , mName( name ) + , mFunction( function ) {} + + bool operator == ( const TrackListInfo & other ) const { + return mChannelIndex == other.mChannelIndex && + mName.compare( other.mName ) == 0 && + mFunction.compare( other.mFunction ) == 0; + } + + bool operator != ( const TrackListInfo & other ) const { + return !( this->operator==( other ) ); + } + + XMP_Uns64 mChannelIndex; + std::string mName; + std::string mFunction; + }; + enum { kTape, // std::string - kTake, // XMP_Uns64 + kTake, // std::string kScene, // std::string kNote, // std::string kProject, // std::string @@ -54,6 +79,7 @@ namespace IFF_RIFF { kTimeStampSampleRate, // XMP_Uns64 kTimeStampSampleSinceMidnightLow, // XMP_Uns32 kTimeStampSampleSinceMidnightHigh, // XMP_Uns32 + kTrackList, // std::vector< TrackListInfo > kLastEntry }; @@ -118,6 +144,10 @@ namespace IFF_RIFF { void UpdateProperties(); std::string ParseStringValue( XML_Node * parentNode, XMP_Uns32 id ); + std::string ParseStringValue( XML_Node * parentNode, const char * tagName, bool recoverableError = true ); + XMP_Uns64 ParseUns64Value( XML_Node * parentNode, const char * tagName ); + + void ParseAndSetTrackListInfo( XML_Node * parentNode ); void ParseAndSetStringProperty( XML_Node * parentNode, XMP_Uns32 id ); void ParseAndSetIntegerProperty( XML_Node * parentNode, XMP_Uns32 id ); void ParseAndSetBoolProperty( XML_Node * parentNode, XMP_Uns32 id ); @@ -125,6 +155,7 @@ namespace IFF_RIFF { void UpdateStringProperty( XML_Node * parentNode, XMP_Uns32 id ); void UpdateIntegerProperty( XML_Node * parentNode, XMP_Uns32 id ); void UpdateBoolProperty( XML_Node * parentNode, XMP_Uns32 id ); + void UpdateTrackListInfo( XML_Node * parentNode ); void UpdateXMLNode( XML_Node * parentNode, const char * localName, const std::string & value ); void RemoveXMLNode( XML_Node * parentNode, const char * localName ); @@ -140,7 +171,7 @@ namespace IFF_RIFF { bool validateUMID( ValueObject * value ); bool validateTimeCodeFlag( ValueObject * value ); bool validateRational( ValueObject * value ); - + bool validateTrackListInfo( ValueObject * value ); private: // Operators hidden on purpose iXMLMetadata( const iXMLMetadata& ) {}; diff --git a/XMPFiles/source/FormatSupport/XMPScanner.cpp b/XMPFiles/source/FormatSupport/XMPScanner.cpp index 0dd34ae..22726d0 100644 --- a/XMPFiles/source/FormatSupport/XMPScanner.cpp +++ b/XMPFiles/source/FormatSupport/XMPScanner.cpp @@ -20,11 +20,7 @@ #include "public/include/XMP_Const.h" -#if TestRunnerBuild - #define EnablePacketScanning 1 -#else - #include "XMPFiles/source/XMPFiles_Impl.hpp" -#endif + #include "XMPFiles/source/FormatSupport/XMPScanner.hpp" @@ -38,6 +34,12 @@ #include <fstream> #endif +#if TestRunnerBuild +#define EnablePacketScanning 1 +#else +#include "XMPFiles/source/XMPFiles_Impl.hpp" +#endif + #ifndef UseStringPushBack // VC++ 6.x does not provide push_back for strings! #define UseStringPushBack 0 #endif diff --git a/XMPFiles/source/HandlerRegistry.cpp b/XMPFiles/source/HandlerRegistry.cpp index 69aed6e..60547fc 100644 --- a/XMPFiles/source/HandlerRegistry.cpp +++ b/XMPFiles/source/HandlerRegistry.cpp @@ -22,6 +22,7 @@ #include "XMPFiles/source/FileHandlers/JPEG_Handler.hpp" #include "XMPFiles/source/FileHandlers/PSD_Handler.hpp" #include "XMPFiles/source/FileHandlers/TIFF_Handler.hpp" + #include "XMPFiles/source/FileHandlers/GIF_Handler.hpp" #endif #if EnableDynamicMediaHandlers @@ -36,8 +37,9 @@ #include "XMPFiles/source/FileHandlers/RIFF_Handler.hpp" #include "XMPFiles/source/FileHandlers/SonyHDV_Handler.hpp" #include "XMPFiles/source/FileHandlers/SWF_Handler.hpp" - #include "XMPFiles/source/FileHandlers/XDCAM_Handler.hpp" #include "XMPFiles/source/FileHandlers/XDCAMEX_Handler.hpp" + #include "XMPFiles/source/FileHandlers/XDCAMFAM_Handler.hpp" + #include "XMPFiles/source/FileHandlers/XDCAMSAM_Handler.hpp" #include "XMPFiles/source/FileHandlers/WEBP_Handler.hpp" #endif @@ -46,7 +48,7 @@ #include "XMPFiles/source/FileHandlers/PNG_Handler.hpp" #include "XMPFiles/source/FileHandlers/PostScript_Handler.hpp" #include "XMPFiles/source/FileHandlers/UCF_Handler.hpp" - #include "XMPFiles/source/FileHandlers/GIF_Handler.hpp" + #include "XMPFiles/source/FileHandlers/SVG_Handler.hpp" #endif //#if EnablePacketScanning @@ -123,8 +125,8 @@ void HandlerRegistry::initialize() #if EnableDynamicMediaHandlers allOK &= this->registerFolderHandler ( kXMP_P2File, kP2_HandlerFlags, P2_CheckFormat, P2_MetaHandlerCTor ); allOK &= this->registerFolderHandler ( kXMP_SonyHDVFile, kSonyHDV_HandlerFlags, SonyHDV_CheckFormat, SonyHDV_MetaHandlerCTor ); - allOK &= this->registerFolderHandler ( kXMP_XDCAM_FAMFile, kXDCAM_HandlerFlags, XDCAM_CheckFormat, XDCAM_MetaHandlerCTor ); - allOK &= this->registerFolderHandler ( kXMP_XDCAM_SAMFile, kXDCAM_HandlerFlags, XDCAM_CheckFormat, XDCAM_MetaHandlerCTor ); + allOK &= this->registerFolderHandler ( kXMP_XDCAM_FAMFile, kXDCAMFAM_HandlerFlags, XDCAMFAM_CheckFormat, XDCAMFAM_MetaHandlerCTor ); + allOK &= this->registerFolderHandler ( kXMP_XDCAM_SAMFile, kXDCAMSAM_HandlerFlags, XDCAMSAM_CheckFormat, XDCAMSAM_MetaHandlerCTor ); allOK &= this->registerFolderHandler ( kXMP_XDCAM_EXFile, kXDCAMEX_HandlerFlags, XDCAMEX_CheckFormat, XDCAMEX_MetaHandlerCTor ); #endif @@ -135,6 +137,7 @@ void HandlerRegistry::initialize() allOK &= this->registerNormalHandler ( kXMP_JPEGFile, kJPEG_HandlerFlags, JPEG_CheckFormat, JPEG_MetaHandlerCTor ); allOK &= this->registerNormalHandler ( kXMP_PhotoshopFile, kPSD_HandlerFlags, PSD_CheckFormat, PSD_MetaHandlerCTor ); allOK &= this->registerNormalHandler ( kXMP_TIFFFile, kTIFF_HandlerFlags, TIFF_CheckFormat, TIFF_MetaHandlerCTor ); + allOK &= this->registerNormalHandler( kXMP_GIFFile, kGIF_HandlerFlags, GIF_CheckFormat, GIF_MetaHandlerCTor ); #endif #if EnableDynamicMediaHandlers @@ -158,6 +161,7 @@ void HandlerRegistry::initialize() // ! EPS and PostScript have the same handler, EPS is a proper subset of PostScript. allOK &= this->registerNormalHandler ( kXMP_EPSFile, kPostScript_HandlerFlags, PostScript_CheckFormat, PostScript_MetaHandlerCTor ); allOK &= this->registerNormalHandler ( kXMP_PostScriptFile, kPostScript_HandlerFlags, PostScript_CheckFormat, PostScript_MetaHandlerCTor ); + allOK &= this->registerNormalHandler ( kXMP_SVGFile, kSVG_HandlerFlags, SVG_CheckFormat, SVG_MetaHandlerCTor ); #endif // ------------------------------------------------------------------------------------ diff --git a/XMPFiles/source/NativeMetadataSupport/IMetadata.h b/XMPFiles/source/NativeMetadataSupport/IMetadata.h index b85239b..608d4a2 100644 --- a/XMPFiles/source/NativeMetadataSupport/IMetadata.h +++ b/XMPFiles/source/NativeMetadataSupport/IMetadata.h @@ -237,7 +237,7 @@ template<class T> void IMetadata::setValue( XMP_Uns32 id, const T& value ) // // check if the value is "empty" // - if( this->isEmptyValue( id, *valueObj ) ) + if( valueObj == NULL || this->isEmptyValue( id, *valueObj ) ) { // // value is "empty", delete it diff --git a/XMPFiles/source/NativeMetadataSupport/IReconcile.h b/XMPFiles/source/NativeMetadataSupport/IReconcile.h index 16a0109..d28d5c4 100644 --- a/XMPFiles/source/NativeMetadataSupport/IReconcile.h +++ b/XMPFiles/source/NativeMetadataSupport/IReconcile.h @@ -11,6 +11,7 @@ #define _IReconcile_h_ #include <string> +#include <vector> #ifndef TXMP_STRING_TYPE #define TXMP_STRING_TYPE std::string diff --git a/XMPFiles/source/NativeMetadataSupport/ValueObject.h b/XMPFiles/source/NativeMetadataSupport/ValueObject.h index cfd0c49..4ef2f50 100644 --- a/XMPFiles/source/NativeMetadataSupport/ValueObject.h +++ b/XMPFiles/source/NativeMetadataSupport/ValueObject.h @@ -110,7 +110,13 @@ template<class T> inline void TArrayObject<T>::setArray( const T* buffer, XMP_Un if( mArray != NULL && mSize == numElements ) { - doSet = ( memcmp( mArray, buffer, numElements*sizeof(T) ) != 0 ); + doSet = false; + for( size_t i = 0; i < mSize; i++ ) { + if ( mArray[i] != buffer[i] ) { + doSet = true; + break; + } + } } if( doSet ) @@ -123,7 +129,9 @@ template<class T> inline void TArrayObject<T>::setArray( const T* buffer, XMP_Un mArray = new T[numElements]; mSize = numElements; - memcpy( mArray, buffer, numElements*sizeof(T) ); + for ( size_t i = 0; i < mSize; i++ ) { + mArray[i] = buffer[i]; + } mDirty = true; } diff --git a/XMPFiles/source/PluginHandler/FileHandlerInstance.cpp b/XMPFiles/source/PluginHandler/FileHandlerInstance.cpp index 3d2ffd8..c70a794 100644 --- a/XMPFiles/source/PluginHandler/FileHandlerInstance.cpp +++ b/XMPFiles/source/PluginHandler/FileHandlerInstance.cpp @@ -49,70 +49,116 @@ void FileHandlerInstance::CacheFileData() if( error.mErrorID != kXMPErr_NoError ) { if ( xmpStr != 0 ) free( (void*) xmpStr ); - throw XMP_Error( kXMPErr_InternalFailure, error.mErrorMsg ); + if ( error.mErrorID == kXMPErr_FilePermission ) + throw XMP_Error( kXMPErr_FilePermission, error.mErrorMsg ); + else + throw XMP_Error( kXMPErr_InternalFailure, error.mErrorMsg ); } if( xmpStr != NULL ) { this->xmpPacket.assign( xmpStr ); free( (void*) xmpStr ); // It should be freed as documentation of mCacheFileDataProc says so. + this->containsXMP = true; } - this->containsXMP = true; + else + this->containsXMP = false; } void FileHandlerInstance::ProcessXMP() { - if( !this->containsXMP || this->processedXMP ) return; + if( this->processedXMP ) return; this->processedXMP = true; SXMPUtils::RemoveProperties ( &this->xmpObj, 0, 0, kXMPUtil_DoAllProperties ); - this->xmpObj.ParseFromBuffer ( this->xmpPacket.c_str(), (XMP_StringLen)this->xmpPacket.size() ); + if ( this->xmpPacket.size() != 0 ) + this->xmpObj.ParseFromBuffer ( this->xmpPacket.c_str(), (XMP_StringLen)this->xmpPacket.size() ); WXMP_Error error; - if( mHandler->getModule()->getPluginAPIs()->mImportToXMPStringProc ) + if ( mHandler->getModule()->getPluginAPIs()->mVersion >= 4 && mHandler->getModule()->getPluginAPIs()->mImportToXMPStringWithPacketProc ) + { + XMP_StringPtr xmpStr = this->xmpPacket.c_str(); + XMP_StringPtr oldPacketPtr = NULL; + XMP_PacketInfo packetInfo; + mHandler->getModule()->getPluginAPIs()->mImportToXMPStringWithPacketProc( this->mObject, &xmpStr, &error, &oldPacketPtr, &packetInfo ); + + if( xmpStr != NULL && xmpStr != this->xmpPacket.c_str() ) + { + XMP_StringLen newLen = static_cast<XMP_StringLen>(strlen( xmpStr )); + this->xmpObj.Erase(); + this->xmpObj.ParseFromBuffer( xmpStr, newLen, 0 ); + + // Note: Freeing memory would not create any problem as plugin would have allocated memory using Host library function + free( ( void * ) xmpStr ); + this->containsXMP = true; + } + + if( oldPacketPtr != NULL ) + { + this->xmpPacket.resize( strlen( oldPacketPtr ) ); + this->xmpPacket.assign( oldPacketPtr ); + this->packetInfo = packetInfo; + + // Note: Freeing memory would not create any problem as plugin would have allocated memory using Host library function + free( ( void * ) oldPacketPtr ); + this->containsXMP = true; + } + } + else if( mHandler->getModule()->getPluginAPIs()->mVersion >= 2 && mHandler->getModule()->getPluginAPIs()->mImportToXMPStringProc ) { - std::string xmp; - this->xmpObj.SerializeToBuffer(&xmp, kXMP_NoOptions, 0); - XMP_StringPtr xmpStr=xmp.c_str(); + XMP_StringPtr xmpStr = this->xmpPacket.c_str(); mHandler->getModule()->getPluginAPIs()->mImportToXMPStringProc( this->mObject, &xmpStr, &error ); - if( xmpStr!= NULL && xmpStr != xmp.c_str() ) + + if( xmpStr != NULL && xmpStr != this->xmpPacket.c_str() ) { - xmp.resize(0); - xmp.assign(xmpStr); - SXMPMeta newMeta(xmp.c_str(),xmp.length()); - this->xmpObj=newMeta; - free( (void*) xmpStr ); // It should be freed as documentation of mImportToXMPStringProc says so. + XMP_StringLen newLen = static_cast<XMP_StringLen>(strlen( xmpStr )); + this->xmpObj.Erase(); + this->xmpObj.ParseFromBuffer( xmpStr, newLen, 0 ); + + // Note: Freeing memory would not create any problem as plugin would have allocated memory using Host library function + free( ( void * ) xmpStr ); + this->containsXMP = true; } } else { if( mHandler->getModule()->getPluginAPIs()->mImportToXMPProc ) mHandler->getModule()->getPluginAPIs()->mImportToXMPProc( this->mObject, this->xmpObj.GetInternalRef(), &error ); + this->containsXMP = true; } CheckError( error ); } void FileHandlerInstance::UpdateFile ( bool doSafeUpdate ) { - if ( !this->needsUpdate || this->xmpPacket.size() == 0 ) return; - + bool optimizeFileLayout = XMP_OptionIsSet ( this->parent->openFlags, kXMPFiles_OptimizeFileLayout ); + this->needsUpdate |= optimizeFileLayout; + if( !this->needsUpdate ) return; WXMP_Error error; - if( mHandler->getModule()->getPluginAPIs()->mExportFromXMPStringProc ) - { - std::string xmp; - this->xmpObj.SerializeToBuffer(&xmp, kXMP_NoOptions, 0); - XMP_StringPtr xmpStr=xmp.c_str(); - mHandler->getModule()->getPluginAPIs()->mExportFromXMPStringProc( this->mObject, xmpStr, &error ); - } - else + + if ( xmpPacket.size() != 0 ) { - if( mHandler->getModule()->getPluginAPIs()->mExportFromXMPProc ) - mHandler->getModule()->getPluginAPIs()->mExportFromXMPProc( this->mObject, this->xmpObj.GetInternalRef(), &error ); - } - CheckError( error ); + if( mHandler->getModule()->getPluginAPIs()->mExportFromXMPStringProc ) + { + std::string xmp; + this->xmpObj.SerializeToBuffer( &xmp, kXMP_NoOptions, 0 ); + XMP_StringPtr xmpStr = xmp.c_str(); + mHandler->getModule()->getPluginAPIs()->mExportFromXMPStringProc( this->mObject, xmpStr, &error ); + if ( xmpStr != xmp.c_str() ) + this->xmpObj.SerializeToBuffer ( &this->xmpPacket, mHandler->getSerializeOption() ); + } + else + { + if( mHandler->getModule()->getPluginAPIs()->mExportFromXMPProc ) + { + mHandler->getModule()->getPluginAPIs()->mExportFromXMPProc( this->mObject, this->xmpObj.GetInternalRef(), &error ); + this->xmpObj.SerializeToBuffer ( &this->xmpPacket, mHandler->getSerializeOption() ); + } + } + CheckError( error ); - this->xmpObj.SerializeToBuffer ( &this->xmpPacket, mHandler->getSerializeOption() ); - + + } mHandler->getModule()->getPluginAPIs()->mUpdateFileProc( this->mObject, this->parent->ioRef, doSafeUpdate, this->xmpPacket.c_str(), &error ); CheckError( error ); this->needsUpdate = false; @@ -141,7 +187,6 @@ static void SetStringVector ( StringVectorRef clientPtr, XMP_StringPtr * arrayPt } } - void FileHandlerInstance::FillMetadataFiles( std::vector<std::string> * metadataFiles ) { WXMP_Error error; @@ -180,4 +225,28 @@ bool FileHandlerInstance::IsMetadataWritable( ) return ConvertXMP_BoolToBool( result ); } +void FileHandlerInstance::SetErrorCallback ( ErrorCallbackBox errorCallbackBox ) +{ + WXMP_Error error; + SetErrorCallbackproc wSetErrorCallbackproc = mHandler->getModule()->getPluginAPIs()->mSetErrorCallbackproc ; + if( wSetErrorCallbackproc ) { + wSetErrorCallbackproc( this->mObject, errorCallbackBox, &error ); + CheckError( error ); + } else { + XMP_Throw ( "This version of plugin does not support IsMetadataWritable API", kXMPErr_Unimplemented ); + } +} + +void FileHandlerInstance::SetProgressCallback ( XMP_ProgressTracker::CallbackInfo * progCBInfoPtr ) +{ + WXMP_Error error; + SetProgressCallbackproc wSetProgressCallbackproc = mHandler->getModule()->getPluginAPIs()->mSetProgressCallbackproc ; + if( wSetProgressCallbackproc ) { + wSetProgressCallbackproc( this->mObject, progCBInfoPtr, &error ); + CheckError( error ); + } else { + XMP_Throw ( "This version of plugin does not support IsMetadataWritable API", kXMPErr_Unimplemented ); + } +} + } //namespace XMP_PLUGIN diff --git a/XMPFiles/source/PluginHandler/FileHandlerInstance.h b/XMPFiles/source/PluginHandler/FileHandlerInstance.h index 9810831..b1c2a7b 100644 --- a/XMPFiles/source/PluginHandler/FileHandlerInstance.h +++ b/XMPFiles/source/PluginHandler/FileHandlerInstance.h @@ -37,6 +37,8 @@ public: virtual void FillMetadataFiles ( std::vector<std::string> * metadataFiles ); virtual void FillAssociatedResources ( std::vector<std::string> * resourceList ); virtual bool IsMetadataWritable ( ); + virtual void SetErrorCallback ( ErrorCallbackBox errorCallbackBox ); + virtual void SetProgressCallback ( XMP_ProgressTracker::CallbackInfo * progCBInfoPtr ); inline SessionRef GetSession() const { return mObject; } inline FileHandlerSharedPtr GetHandlerInfo() const { return mHandler; } diff --git a/XMPFiles/source/PluginHandler/HostAPIImpl.cpp b/XMPFiles/source/PluginHandler/HostAPIImpl.cpp index cf6a389..dd7031e 100644 --- a/XMPFiles/source/PluginHandler/HostAPIImpl.cpp +++ b/XMPFiles/source/PluginHandler/HostAPIImpl.cpp @@ -376,7 +376,7 @@ static void GetAbortAPI( Abort_API* abortAPI ) // StandardHandler_API // -static XMPErrorID CheckFormatStandardHandler( SessionRef session, XMP_FileFormat format, StringPtr path, XMP_Bool & checkOK, WXMP_Error* wError ) +static XMPErrorID CheckFormatStandardHandlerInternal( SessionRef session, XMP_FileFormat format, StringPtr path, XMP_Bool & checkOK, WXMP_Error* wError, XMPFiles * standardClient = NULL ) { if( wError == NULL ) return kXMPErr_BadParam; @@ -400,14 +400,23 @@ static XMPErrorID CheckFormatStandardHandler( SessionRef session, XMP_FileFormat { if( hdlInfo->checkProc != NULL ) { + bool xmpFilesCreated = false; + if ( standardClient == NULL ) + { + standardClient = new XMPFiles(); + xmpFilesCreated = true; + } + try { // // setup temporary XMPFiles instance // - XMPFiles standardClient; - standardClient.format = format; - standardClient.SetFilePath( path ); + if ( xmpFilesCreated ) + { + standardClient->format = format; + standardClient->SetFilePath( path ); + } if( hdlInfo->flags & kXMPFiles_FolderBasedFormat ) { @@ -447,7 +456,7 @@ static XMPErrorID CheckFormatStandardHandler( SessionRef session, XMP_FileFormat // The case that a logical path to a clip has been passed, which does not point to a real file if( Host_IO::GetFileMode( path ) == Host_IO::kFMode_DoesNotExist ) { - checkOK = CheckProc( hdlInfo->format, rootPath, gpName, parentName, leafName, &standardClient ); + checkOK = CheckProc( hdlInfo->format, rootPath, gpName, parentName, leafName, standardClient ); } else { @@ -463,7 +472,7 @@ static XMPErrorID CheckFormatStandardHandler( SessionRef session, XMP_FileFormat gpName = origGPName; // ! XDCAM-FAM has just 1 level of inner folder, preserve the "MyMovie" case. } - checkOK = CheckProc( hdlInfo->format, rootPath, gpName, parentName, leafName, &standardClient ); + checkOK = CheckProc( hdlInfo->format, rootPath, gpName, parentName, leafName, standardClient ); } } else @@ -483,14 +492,24 @@ static XMPErrorID CheckFormatStandardHandler( SessionRef session, XMP_FileFormat // CheckFileFormatProc CheckProc = (CheckFileFormatProc) (hdlInfo->checkProc); XMPFiles_IO* io = XMPFiles_IO::New_XMPFiles_IO ( path, true ); - checkOK = CheckProc( hdlInfo->format, path, io, &standardClient ); + checkOK = CheckProc( hdlInfo->format, path, io, standardClient ); delete io; } wError->mErrorID = kXMPErr_NoError; + if ( xmpFilesCreated ) + { + delete standardClient; + standardClient = NULL; + } } catch( ... ) { + if ( xmpFilesCreated ) + { + delete standardClient; + standardClient = NULL; + } HandleException( wError ); } } @@ -508,7 +527,12 @@ static XMPErrorID CheckFormatStandardHandler( SessionRef session, XMP_FileFormat return wError->mErrorID; } -static XMPErrorID GetXMPStandardHandler( SessionRef session, XMP_FileFormat format, StringPtr path, XMPMetaRef xmpRef, XMP_Bool * xmpExists, WXMP_Error* wError ) +static XMPErrorID CheckFormatStandardHandler( SessionRef session, XMP_FileFormat format, StringPtr path, XMP_Bool & checkOK, WXMP_Error* wError ) +{ + return CheckFormatStandardHandlerInternal( session, format, path, checkOK, wError ); +} + +static XMPErrorID GetXMPStandardHandlerInternal( SessionRef session, XMP_FileFormat format, StringPtr path, XMPMetaRef xmpRef, XMP_Bool * xmpExists, WXMP_Error* wError, XMP_OptionBits flags = 0, XMP_StringPtr *packet = NULL, XMP_PacketInfo *packetInfo = NULL, ErrorCallbackBox * errorCallback = NULL, XMP_ProgressTracker::CallbackInfo * progCBInfoPtr = NULL ) { if( wError == NULL ) return kXMPErr_BadParam; @@ -533,34 +557,58 @@ static XMPErrorID GetXMPStandardHandler( SessionRef session, XMP_FileFormat form // check format first // XMP_Bool suc = kXMP_Bool_False; + if( flags == 0 ) + flags = kXMPFiles_OpenForRead; + // + // setup temporary XMPFiles instance + // + XMPFiles standardClient; + standardClient.format = format; + standardClient.SetFilePath( path ); + standardClient.openFlags = flags; + + if ( errorCallback != NULL ) + { + standardClient.SetErrorCallback( errorCallback->wrapperProc, errorCallback->clientProc, errorCallback->context, errorCallback->limit ); + } + if ( progCBInfoPtr != NULL && progCBInfoPtr->wrapperProc != NULL ) + { + standardClient.SetProgressCallback( *progCBInfoPtr ); + } - XMPErrorID errorID = CheckFormatStandardHandler( session, format, path, suc, wError ); + XMPErrorID errorID = kXMPErr_NoError; + if ( flags & kXMPFiles_ForceGivenHandler ) + { + suc = ConvertBoolToXMP_Bool( true ); + wError->mErrorID = kXMPErr_NoError; + } + else + errorID = CheckFormatStandardHandlerInternal( session, format, path, suc, wError, &standardClient ); if( errorID == kXMPErr_NoError && ConvertXMP_BoolToBool( suc ) ) { - // - // setup temporary XMPFiles instance - // - XMPFiles standardClient; - standardClient.format = format; - standardClient.SetFilePath( path ); - SXMPMeta meta( xmpRef ); - + try { - // - // open with passed handler info - // - suc = standardClient.OpenFile( *hdlInfo, path, kXMPFiles_OpenForRead ); + suc = standardClient.OpenFile( *hdlInfo, path, flags ); if( suc ) { - // - // read meta data - // - suc = standardClient.GetXMP( &meta ); - + XMP_StringPtr oldStr; + XMP_StringLen length = 0; + suc = standardClient.GetXMP( &meta, &oldStr, &length, packetInfo ); + if( length != 0 && packet != NULL ) + { + StringPtr buffer = NULL; + CreateBuffer( &buffer, length + 1, wError); + if( wError->mErrorID != kXMPErr_NoError ) + return wError->mErrorID; + + memcpy( buffer, oldStr, length ); + buffer[length] = '\0'; + *packet = buffer; + } if( xmpExists != NULL ) *xmpExists = suc; } } @@ -569,9 +617,6 @@ static XMPErrorID GetXMPStandardHandler( SessionRef session, XMP_FileFormat form HandleException( wError ); } - // - // close and cleanup - // try { standardClient.CloseFile(); @@ -601,6 +646,11 @@ static XMPErrorID GetXMPStandardHandler( SessionRef session, XMP_FileFormat form return wError->mErrorID; } +static XMPErrorID GetXMPStandardHandler( SessionRef session, XMP_FileFormat format, StringPtr path, XMPMetaRef xmpRef, XMP_Bool * xmpExists, WXMP_Error* wError ) +{ + return GetXMPStandardHandlerInternal( session, format, path, xmpRef, xmpExists, wError ); +} + static XMPErrorID GetXMPStandardHandler_V2( SessionRef session, XMP_FileFormat format, StringPtr path, XMP_StringPtr* xmpStr, XMP_Bool * xmpExists, WXMP_Error* wError ) { SXMPMeta meta; @@ -621,6 +671,334 @@ static XMPErrorID GetXMPStandardHandler_V2( SessionRef session, XMP_FileFormat f return wError->mErrorID ; } +static XMPErrorID GetXMPStandardHandler_V3( SessionRef session, XMP_FileFormat format, StringPtr path, XMP_StringPtr* xmpStr, XMP_Bool * xmpExists, WXMP_Error* wError, XMP_OptionBits flags, XMP_StringPtr* packet, XMP_PacketInfo* packetInfo, ErrorCallbackBox * errorCallback, XMP_ProgressTracker::CallbackInfo * progCBInfoPtr ) +{ + SXMPMeta meta; + std::string xmp; + GetXMPStandardHandlerInternal( session, format, path, meta.GetInternalRef(), xmpExists, wError, flags, packet, packetInfo, errorCallback, progCBInfoPtr ) ; + if( wError->mErrorID != kXMPErr_NoError ) + return wError->mErrorID; + + meta.SerializeToBuffer( &xmp, kXMP_NoOptions, 0); + XMP_Uns32 length = (XMP_Uns32)xmp.size() + 1 ; + StringPtr buffer = NULL; + CreateBuffer( &buffer, length , wError); + if( wError->mErrorID != kXMPErr_NoError ) + return wError->mErrorID; + + memcpy( buffer, xmp.c_str(), length ); + *xmpStr = buffer; // callee function should free the memory. + return wError->mErrorID ; +} + +static XMPErrorID PutXMPStandardHandler( SessionRef session, XMP_FileFormat format, StringPtr path, const XMP_StringPtr xmpStr, WXMP_Error* wError , XMP_OptionBits flags, ErrorCallbackBox * errorCallback, XMP_ProgressTracker::CallbackInfo * progCBInfoPtr ) +{ + if( wError == NULL ) return kXMPErr_BadParam; + + wError->mErrorID = kXMPErr_InternalFailure; + wError->mErrorMsg = NULL; + + // + // find FileHandlerInstance associated to session reference + // + FileHandlerInstancePtr instance = PluginManager::getHandlerInstance( session ); + + if( instance != NULL && PluginManager::getHandlerPriority( instance ) == PluginManager::kReplacementHandler ) + { + // + // find previous file handler for file format identifier + // + XMPFileHandlerInfo* hdlInfo = HandlerRegistry::getInstance().getStandardHandlerInfo( format ); + + if( hdlInfo != NULL && HandlerRegistry::getInstance().isReplaced( format ) ) + { + // + // check format first + // + XMP_Bool suc = kXMP_Bool_False; + if( flags == 0 ) + flags = kXMPFiles_OpenForUpdate; + XMPFiles standardClient; + standardClient.format = format; + standardClient.SetFilePath( path ); + standardClient.openFlags = flags ; + + if ( errorCallback != NULL ) + { + standardClient.SetErrorCallback( errorCallback->wrapperProc, errorCallback->clientProc, errorCallback->context, errorCallback->limit ); + } + if ( progCBInfoPtr != NULL && progCBInfoPtr->wrapperProc != NULL ) + { + standardClient.SetProgressCallback( *progCBInfoPtr ); + } + + XMPErrorID errorID = kXMPErr_NoError; + if ( flags & kXMPFiles_ForceGivenHandler ) + { + suc = ConvertBoolToXMP_Bool( true ); + wError->mErrorID = kXMPErr_NoError; + } + else + errorID = CheckFormatStandardHandlerInternal( session, format, path, suc, wError, &standardClient ); + + if( errorID == kXMPErr_NoError && ConvertXMP_BoolToBool( suc ) ) + { + try + { + // open with passed handler info + suc = standardClient.OpenFile( *hdlInfo, path, flags ); + size_t length = strnlen( xmpStr, Max_XMP_Uns32 ); + + if( suc && length != 0 && length <= Max_XMP_Uns32 ) + { + // insert xmp into file + SXMPMeta meta( xmpStr, static_cast<XMP_Uns32>( length )); + standardClient.PutXMP( meta ); + } + // close and cleanup + standardClient.CloseFile(); + } + catch( ... ) + { + HandleException( wError ); + } + + } + else if( errorID == kXMPErr_NoError ) + { + wError->mErrorID = kXMPErr_BadFileFormat; + wError->mErrorMsg = "Standard handler can't process file format"; + } + } + else + { + wError->mErrorID = kXMPErr_NoFileHandler; + wError->mErrorMsg = "No standard handler available"; + } + } + else + { + wError->mErrorMsg = "Standard file handler can't call prior handler"; + } + + return wError->mErrorID; +} + +static XMPErrorID GetFileModDateStandardHandler( SessionRef session, XMP_FileFormat format, StringPtr path, XMP_DateTime * modDate, XMP_Bool * isSuccess, WXMP_Error* wError, XMP_OptionBits flags ) +{ + *isSuccess = false; + if( wError == NULL ) return kXMPErr_BadParam; + + wError->mErrorID = kXMPErr_InternalFailure; + wError->mErrorMsg = NULL; + + // + // find FileHandlerInstance associated to session reference + // + FileHandlerInstancePtr instance = PluginManager::getHandlerInstance( session ); + + if( instance != NULL && PluginManager::getHandlerPriority( instance ) == PluginManager::kReplacementHandler ) + { + // + // find previous file handler for file format identifier + // + XMPFileHandlerInfo* hdlInfo = HandlerRegistry::getInstance().getStandardHandlerInfo( format ); + + if( hdlInfo != NULL && HandlerRegistry::getInstance().isReplaced( format ) ) + { + // + // check format first + // + XMP_Bool suc = kXMP_Bool_False; + + XMPErrorID errorID = kXMPErr_NoError; + if ( flags & kXMPFiles_ForceGivenHandler ) + { + suc = ConvertBoolToXMP_Bool( true ); + wError->mErrorID = kXMPErr_NoError; + } + else + errorID = CheckFormatStandardHandlerInternal( session, format, path, suc, wError ); + + if( errorID == kXMPErr_NoError && ConvertXMP_BoolToBool( suc ) ) + { + + try + { + *isSuccess = XMPFiles::GetFileModDate( *hdlInfo, path, modDate, flags ); + } + catch( ... ) + { + HandleException( wError ); + } + } + else if( errorID == kXMPErr_NoError ) + { + wError->mErrorID = kXMPErr_BadFileFormat; + wError->mErrorMsg = "Standard handler can't process file format"; + } + } + else + { + wError->mErrorID = kXMPErr_NoFileHandler; + wError->mErrorMsg = "No standard handler available"; + } + } + else + { + wError->mErrorMsg = "Standard file handler can't call prior handler"; + } + + return wError->mErrorID; +} + +static XMPErrorID IsMetadataWritableStandardHandler( SessionRef session, XMP_FileFormat format, StringPtr path, XMP_Bool * isWritable, WXMP_Error* wError, XMP_OptionBits flags ) +{ + if( wError == NULL ) return kXMPErr_BadParam; + + wError->mErrorID = kXMPErr_InternalFailure; + wError->mErrorMsg = NULL; + + // + // find FileHandlerInstance associated to session reference + // + FileHandlerInstancePtr instance = PluginManager::getHandlerInstance( session ); + + if( instance != NULL && PluginManager::getHandlerPriority( instance ) == PluginManager::kReplacementHandler ) + { + // + // find previous file handler for file format identifier + // + XMPFileHandlerInfo* hdlInfo = HandlerRegistry::getInstance().getStandardHandlerInfo( format ); + + if( hdlInfo != NULL && HandlerRegistry::getInstance().isReplaced( format ) ) + { + // + // check format first + // + XMP_Bool suc = kXMP_Bool_False; + + XMPErrorID errorID = kXMPErr_NoError; + if ( flags & kXMPFiles_ForceGivenHandler ) + { + suc = ConvertBoolToXMP_Bool( true ); + wError->mErrorID = kXMPErr_NoError; + } + else + errorID = CheckFormatStandardHandlerInternal( session, format, path, suc, wError ); + + if( errorID == kXMPErr_NoError && ConvertXMP_BoolToBool( suc ) ) + { + try + { + (void)XMPFiles::IsMetadataWritable( *hdlInfo, path, isWritable, flags ); + } + catch( ... ) + { + HandleException( wError ); + } + } + else if( errorID == kXMPErr_NoError ) + { + wError->mErrorID = kXMPErr_BadFileFormat; + wError->mErrorMsg = "Standard handler can't process file format"; + } + } + else + { + wError->mErrorID = kXMPErr_NoFileHandler; + wError->mErrorMsg = "No standard handler available"; + } + } + else + { + wError->mErrorMsg = "Standard file handler can't call prior handler"; + } + + return wError->mErrorID; +} + +static XMPErrorID GetAssociatedResourcesStandardHandler( SessionRef session, XMP_FileFormat format, StringPtr path, void * resourceList, SetClientStringVectorProc SetClientStringVector, WXMP_Error* wError, XMP_OptionBits flags ) +{ + if( wError == NULL ) return kXMPErr_BadParam; + + wError->mErrorID = kXMPErr_InternalFailure; + wError->mErrorMsg = NULL; + + // + // find FileHandlerInstance associated to session reference + // + FileHandlerInstancePtr instance = PluginManager::getHandlerInstance( session ); + + if( instance != NULL && PluginManager::getHandlerPriority( instance ) == PluginManager::kReplacementHandler ) + { + // + // find previous file handler for file format identifier + // + XMPFileHandlerInfo* hdlInfo = HandlerRegistry::getInstance().getStandardHandlerInfo( format ); + + if( hdlInfo != NULL && HandlerRegistry::getInstance().isReplaced( format ) ) + { + // + // check format first + // + XMP_Bool suc = kXMP_Bool_False; + + XMPErrorID errorID = kXMPErr_NoError; + if ( flags & kXMPFiles_ForceGivenHandler ) + { + suc = ConvertBoolToXMP_Bool( true ); + wError->mErrorID = kXMPErr_NoError; + } + else + errorID = CheckFormatStandardHandlerInternal( session, format, path, suc, wError ); + + if( errorID == kXMPErr_NoError && ConvertXMP_BoolToBool( suc ) ) + { + try + { + bool isSuccess; + + // Host XMPFile library is providing a new vector of string and will use that for getting list of associated resources using standard handler + std::vector<std::string> resList; + ( *SetClientStringVector )( resourceList, 0, 0 ); + isSuccess = XMPFiles::GetAssociatedResources( *hdlInfo, path, &resList, flags ); + if ( isSuccess && (! resList.empty()) ) + { + const size_t fileCount = resList.size(); + std::vector<XMP_StringPtr> ptrArray; + ptrArray.reserve ( fileCount ); + for ( size_t index = 0; index < fileCount; ++index ) + ptrArray.push_back( resList[ index ].c_str() ); + + // Filling list of resources into plugin provided vector of strings using plugin provided method of creating vector of strings + ( *SetClientStringVector ) ( resourceList, ptrArray.data(), static_cast<XMP_Uns32>( fileCount ) ); + } + } + catch( ... ) + { + HandleException( wError ); + } + } + else if( errorID == kXMPErr_NoError ) + { + wError->mErrorID = kXMPErr_BadFileFormat; + wError->mErrorMsg = "Standard handler can't process file format"; + } + } + else + { + wError->mErrorID = kXMPErr_NoFileHandler; + wError->mErrorMsg = "No standard handler available"; + } + } + else + { + wError->mErrorMsg = "Standard file handler can't call prior handler"; + } + + return wError->mErrorID; +} static void GetStandardHandlerAPI( StandardHandler_API* standardHandlerAPI ) { @@ -632,6 +1010,10 @@ static void GetStandardHandlerAPI( StandardHandler_API* standardHandlerAPI ) } +// +// +// +/////////////////////////////////////////////////////////////////////////////// static XMPErrorID RequestAPISuite( const char* apiName, XMP_Uns32 apiVersion, void** apiSuite, WXMP_Error* wError ) { @@ -655,14 +1037,39 @@ static XMPErrorID RequestAPISuite( const char* apiName, XMP_Uns32 apiVersion, vo { *apiSuite = (void*) &RequestAPISuite; } - else if ( ! strcmp( apiName, "StandardHandler" ) && apiVersion == 2 ) + else if ( ! strcmp( apiName, "StandardHandler" ) ) { - static const StandardHandler_API_V2 standardHandlerAPI = - { + static const StandardHandler_API_V3 standardHandlerAPI( &CheckFormatStandardHandler, - &GetXMPStandardHandler_V2 - }; - *apiSuite=(void*)&standardHandlerAPI; + &GetXMPStandardHandler_V2, + &GetXMPStandardHandler_V3, + &PutXMPStandardHandler, + &GetFileModDateStandardHandler, + &GetAssociatedResourcesStandardHandler, + &IsMetadataWritableStandardHandler + ); + + switch ( apiVersion ) + { + case 2: + { + const StandardHandler_API_V2 * ptr = &standardHandlerAPI; + *apiSuite = (void *) ptr; + } + break; + + case 3: + { + *apiSuite = (void *) &standardHandlerAPI; + } + break; + + default: + *apiSuite = NULL; + wError->mErrorID = kXMPErr_Unimplemented; + break; + + } } else { diff --git a/XMPFiles/source/PluginHandler/ModuleUtils.h b/XMPFiles/source/PluginHandler/ModuleUtils.h index e261271..b4c2d71 100644 --- a/XMPFiles/source/PluginHandler/ModuleUtils.h +++ b/XMPFiles/source/PluginHandler/ModuleUtils.h @@ -16,6 +16,7 @@ typedef HMODULE OS_ModuleRef; #elif XMP_MacBuild #include <CoreFoundation/CFBundle.h> +#include <memory> typedef CFBundleRef OS_ModuleRef; #elif XMP_UNIXBuild #include <tr1/memory> diff --git a/XMPFiles/source/PluginHandler/OS_Utils_Mac.cpp b/XMPFiles/source/PluginHandler/OS_Utils_Mac.cpp index e9c2b46..e29084e 100644 --- a/XMPFiles/source/PluginHandler/OS_Utils_Mac.cpp +++ b/XMPFiles/source/PluginHandler/OS_Utils_Mac.cpp @@ -267,27 +267,27 @@ bool GetResourceDataFromModule( if ( !url.IsNull() ) { - typedef AutoCFRef<CFDataRef> AutoCFData; typedef AutoCFRef<CFNumberRef> AutoCFNumber; - AutoCFData resourceData; - SInt32 errorCode = 0; + typedef AutoCFRef<CFErrorRef> AutoCFError; + typedef AutoCFRef<CFReadStreamRef> AutoCFReadStream; + AutoCFError cfError; AutoCFNumber length; - *length = reinterpret_cast<CFNumberRef> ( ::CFURLCreatePropertyFromResource( kCFAllocatorDefault, *url, kCFURLFileLength, &errorCode ) ); - if ( !errorCode ) + if ( ::CFURLCopyResourcePropertyForKey(*url, kCFURLFileSizeKey, &length, &(*cfError)) && !length.IsNull()) { SInt64 sizeOfFile = 0; success = ::CFNumberGetValue( *length, kCFNumberSInt64Type, &sizeOfFile ); + // presumingly we don't want to load more than 2GByte at once (!) - if ( success && sizeOfFile < std::numeric_limits<XMP_Int32>::max() ) + if ( success && sizeOfFile != 0 && sizeOfFile < std::numeric_limits<XMP_Int32>::max() ) { - success = ::CFURLCreateDataAndPropertiesFromResource( kCFAllocatorDefault, *url, &(*resourceData), NULL, NULL, &errorCode ); - if ( success && errorCode == 0 ) + AutoCFReadStream readStream(::CFReadStreamCreateWithFile(kCFAllocatorDefault, *url)); + if( *readStream != NULL && CFReadStreamOpen(*readStream) ) { - outBuffer.resize( sizeOfFile ); - CFRange range = CFRangeMake (0, sizeOfFile ); - ::CFDataGetBytes( *resourceData, range, reinterpret_cast< UInt8 * > (&outBuffer[0]) ); - return true; + outBuffer.assign(sizeOfFile, NULL); + success = ( ::CFReadStreamRead(*readStream,reinterpret_cast< UInt8 * > (&outBuffer[0]), sizeOfFile) != -1 ); + ::CFReadStreamClose(*readStream); + return success; } } } diff --git a/XMPFiles/source/PluginHandler/PluginManager.cpp b/XMPFiles/source/PluginHandler/PluginManager.cpp index 0f47300..6bcea90 100644 --- a/XMPFiles/source/PluginHandler/PluginManager.cpp +++ b/XMPFiles/source/PluginHandler/PluginManager.cpp @@ -56,8 +56,11 @@ static XMPFileHandler* Plugin_MetaHandlerCTor ( FileHandlerSharedPtr handler, XM { XMP_Throw ( "Plugin not loaded", kXMPErr_InternalFailure ); } - - handler->getModule()->getPluginAPIs()->mInitializeSessionProc ( handler->getUID().c_str(), parent->GetFilePath().c_str(), (XMP_Uns32)parent->format, (XMP_Uns32)handler->getHandlerFlags(), (XMP_Uns32)parent->openFlags, &object, &error ); + + if( handler->getModule()->getPluginAPIs()->mInitializeSessionV2Proc ) + handler->getModule()->getPluginAPIs()->mInitializeSessionV2Proc ( handler->getUID().c_str(), parent->GetFilePath().c_str(), (XMP_Uns32)parent->format, (XMP_Uns32)handler->getHandlerFlags(), (XMP_Uns32)parent->openFlags, &object, &error, ErrorCallbackBox( parent->errorCallback.wrapperProc, parent->errorCallback.clientProc, parent->errorCallback.context, parent->errorCallback.limit ), parent->progressTracker->GetCallbackInfo() ); + else + handler->getModule()->getPluginAPIs()->mInitializeSessionProc ( handler->getUID().c_str(), parent->GetFilePath().c_str(), (XMP_Uns32)parent->format, (XMP_Uns32)handler->getHandlerFlags(), (XMP_Uns32)parent->openFlags, &object, &error ); CheckError ( error ); FileHandlerInstance* instance = new FileHandlerInstance ( object, handler, parent ); diff --git a/XMPFiles/source/PluginHandler/PluginManager.h b/XMPFiles/source/PluginHandler/PluginManager.h index 85ec785..a7ea17d 100644 --- a/XMPFiles/source/PluginHandler/PluginManager.h +++ b/XMPFiles/source/PluginHandler/PluginManager.h @@ -12,26 +12,39 @@ #include "PluginHandler.h" #include "ModuleUtils.h" -#if XMP_WinBuild && _MSC_VER >= 1700 - // Visual Studio 2012 or newer supports C++11 (mostly) +#include "XMPCommon/XMPCommonDefines.h" + + +#if SUPPORT_SHARED_POINTERS_IN_STD #include <memory> #include <functional> - #define XMP_SHARED_PTR std::shared_ptr -#elif XMP_MacBuild - #include <memory> - #define XMP_SHARED_PTR std::shared_ptr +#elif SUPPORT_SHARED_POINTERS_IN_TR1 + #if XMP_WinBuild + #include <memory> + #include <functional> + #else + #include <tr1/memory> + #include <tr1/functional> + #endif #else - #define XMP_SHARED_PTR std::tr1::shared_ptr + #error "location of shared pointer stuff is unknown" #endif + namespace XMP_PLUGIN { +#if SUPPORT_SHARED_POINTERS_IN_STD + using std::shared_ptr; +#elif SUPPORT_SHARED_POINTERS_IN_TR1 + using std::tr1::shared_ptr; +#endif + typedef XMP_Uns32 XMPAtom; typedef XMPAtom FileHandlerType; -typedef XMP_SHARED_PTR<class Module> ModuleSharedPtr; -typedef XMP_SHARED_PTR<class FileHandler> FileHandlerSharedPtr; +typedef shared_ptr<class Module> ModuleSharedPtr; +typedef shared_ptr<class FileHandler> FileHandlerSharedPtr; class FileHandlerInstance; typedef FileHandlerInstance* FileHandlerInstancePtr; diff --git a/XMPFiles/source/WXMPFiles.cpp b/XMPFiles/source/WXMPFiles.cpp index 26fdcac..8ffd214 100644 --- a/XMPFiles/source/WXMPFiles.cpp +++ b/XMPFiles/source/WXMPFiles.cpp @@ -201,7 +201,7 @@ void WXMPFiles_GetAssociatedResources_1 ( XMP_StringPtr filePath, std::vector<XMP_StringPtr> ptrArray; ptrArray.reserve ( fileCount ); for ( size_t i = 0; i < fileCount; ++i ) ptrArray.push_back ( resList[i].c_str() ); - (*SetClientStringVector) ( resourceList, ptrArray.data(), fileCount ); + (*SetClientStringVector) ( resourceList, ptrArray.data(), static_cast<XMP_Uns32>(fileCount )); } XMP_EXIT } @@ -219,7 +219,6 @@ void WXMPFiles_IsMetadataWritable_1 ( XMP_StringPtr filePath, XMP_EXIT } - // ================================================================================================= void WXMPFiles_OpenFile_1 ( XMPFilesRef xmpObjRef, diff --git a/XMPFiles/source/XMPFiles.cpp b/XMPFiles/source/XMPFiles.cpp index ae8f701..db1fc8b 100644 --- a/XMPFiles/source/XMPFiles.cpp +++ b/XMPFiles/source/XMPFiles.cpp @@ -36,6 +36,14 @@ #include "XMPFiles/source/FileHandlers/Generic_Handler.hpp" #endif +#include "XMPCore/XMPCoreDefines.h" +#if ENABLE_CPP_DOM_MODEL + #include "XMPCore/Interfaces/IMetadata.h" + #include "XMPCore/Interfaces/INodeIterator.h" + #include "XMPCommon/Interfaces/IUTF8String.h" + #include "XMPCore/Interfaces/ICoreObjectFactory.h" +#endif + // ================================================================================================= /// \file XMPFiles.cpp /// \brief High level support to access metadata in files of interest to Adobe applications. @@ -81,12 +89,12 @@ const char * kXMPFiles_EmbeddedVersion = kXMPFiles_VersionMessage; const char * kXMPFiles_EmbeddedCopyright = kXMPFilesName " " kXMP_CopyrightStr; #define EMPTY_FILE_PATH "" -#define XMP_FILES_STATIC_START try { int a; -#define XMP_FILES_STATIC_END1(severity) a = 1; } catch ( XMP_Error & error ) { sDefaultErrorCallback.NotifyClient ( (severity), error, EMPTY_FILE_PATH ); } -#define XMP_FILES_STATIC_END2(filePath, severity) a = 1; } catch ( XMP_Error & error ) { sDefaultErrorCallback.NotifyClient ( (severity), error, (filePath) ); } -#define XMP_FILES_START try { int b; -#define XMP_FILES_END1(severity) b = 1; } catch ( XMP_Error & error ) { errorCallback.NotifyClient ( (severity), error, this->filePath.c_str() ); } -#define XMP_FILES_END2(filePath, severity) b = 1; } catch ( XMP_Error & error ) { errorCallback.NotifyClient ( (severity), error, (filePath) ); } +#define XMP_FILES_STATIC_START try { /*int a;*/ +#define XMP_FILES_STATIC_END1(severity) /*a = 1;*/ } catch ( XMP_Error & error ) { sDefaultErrorCallback.NotifyClient ( (severity), error, EMPTY_FILE_PATH ); } +#define XMP_FILES_STATIC_END2(filePath, severity) /*a = 1;*/ } catch ( XMP_Error & error ) { sDefaultErrorCallback.NotifyClient ( (severity), error, (filePath) ); } +#define XMP_FILES_START try { /*int b;*/ +#define XMP_FILES_END1(severity) /*b = 1;*/ } catch ( XMP_Error & error ) { errorCallback.NotifyClient ( (severity), error, this->filePath.c_str() ); } +#define XMP_FILES_END2(filePath, severity) /*b = 1;*/ } catch ( XMP_Error & error ) { errorCallback.NotifyClient ( (severity), error, (filePath) ); } #define XMP_FILES_STATIC_NOTIFY_ERROR(errorCallbackPtr, filePath, severity, error) \ if ( (errorCallbackPtr) != NULL ) (errorCallbackPtr)->NotifyClient ( (severity), (error), (filePath) ); @@ -574,6 +582,7 @@ XMPFiles::GetFileModDate ( XMP_StringPtr clientPath, dummyParent.format = handlerInfo->format; if ( format ) *format = handlerInfo->format; + dummyParent.openFlags = handlerInfo->flags; dummyParent.handler = handlerInfo->handlerCTor ( &dummyParent ); @@ -618,6 +627,64 @@ XMPFiles::GetFileModDate ( XMP_StringPtr clientPath, /* class static */ bool +XMPFiles::GetFileModDate ( const Common::XMPFileHandlerInfo& hdlInfo, + XMP_StringPtr clientPath, + XMP_DateTime * modDate, + XMP_OptionBits options /* = 0 */ ) +{ + XMP_FILES_STATIC_START + Host_IO::FileMode clientMode; + std::string fileExt; // Used to check for excluded files. + bool excluded = FileIsExcluded ( clientPath, &fileExt, &clientMode, &sDefaultErrorCallback ); // ! Fills in fileExt and clientMode. + if ( excluded ) return false; + + XMPFiles dummyParent; // GetFileModDate is static, but the handler needs a parent. + dummyParent.SetFilePath ( clientPath ); + dummyParent.format = hdlInfo.format; + dummyParent.openFlags = hdlInfo.flags; + dummyParent.handler = hdlInfo.handlerCTor ( &dummyParent ); + + bool ok = false; + + std::vector <std::string> resourceList; + try{ + XMP_DateTime lastModDate; + XMP_DateTime junkDate; + if ( modDate == 0 ) modDate = &junkDate; + dummyParent.handler->FillAssociatedResources ( &resourceList ); + size_t countRes = resourceList.size(); + for ( size_t index = 0; index < countRes ; ++index ){ + XMP_StringPtr curFilePath = resourceList[index].c_str(); + if( Host_IO::GetFileMode ( curFilePath ) != Host_IO::kFMode_IsFile ) continue;// only interested in files + if (!Host_IO::GetModifyDate ( curFilePath, &lastModDate ) ) continue; + if ( ! ok || ( SXMPUtils::CompareDateTime ( *modDate , lastModDate ) < 0 ) ) + { + *modDate = lastModDate; + ok = true; + } + } + } + catch(...){ + // Fallback to the old way + // eventually this method would go away as + // soon as the implementation for + // GetAssociatedResources is added to all + // the file/Plugin Handlers + ok = dummyParent.handler->GetFileModDate ( modDate ); + } + delete dummyParent.handler; + dummyParent.handler = 0; + + return ok; + XMP_FILES_STATIC_END2 ( clientPath, kXMPErrSev_OperationFatal ) + return false; + +} // XMPFiles::GetFileModDate + +// ================================================================================================= + +/* class static */ +bool XMPFiles::GetAssociatedResources ( XMP_StringPtr filePath, std::vector<std::string> * resourceList, @@ -661,6 +728,7 @@ XMPFiles::GetAssociatedResources ( // Fill in the format output. Call the handler to get the Associated Resources. dummyParent.format = handlerInfo->format; + dummyParent.openFlags = handlerInfo->flags; dummyParent.handler = handlerInfo->handlerCTor ( &dummyParent ); @@ -686,6 +754,53 @@ XMPFiles::GetAssociatedResources ( } // XMPFiles::GetAssociatedResources +// ================================================================================================= + +/* class static */ +bool +XMPFiles::GetAssociatedResources ( + const Common::XMPFileHandlerInfo& hdlInfo, + XMP_StringPtr filePath, + std::vector<std::string> * resourceList, + XMP_OptionBits options /* = 0 */ ) +{ + XMP_FILES_STATIC_START + Host_IO::FileMode clientMode; + std::string fileExt; // Used to check for excluded files. + bool excluded = FileIsExcluded ( filePath, &fileExt, &clientMode, &sDefaultErrorCallback ); // ! Fills in fileExt and clientMode. + if ( excluded ) return false; + + XMPFiles dummyParent; // GetFileModDate is static, but the handler needs a parent. + dummyParent.SetFilePath ( filePath ); + dummyParent.format = hdlInfo.format; + dummyParent.openFlags = hdlInfo.flags; + dummyParent.handler = hdlInfo.handlerCTor ( &dummyParent ); + + try { + dummyParent.handler->FillAssociatedResources ( resourceList ); + } catch ( XMP_Error& error ) { + if ( error.GetID() == kXMPErr_Unimplemented ) { + XMP_FILES_STATIC_NOTIFY_ERROR ( &sDefaultErrorCallback, filePath, kXMPErrSev_Recoverable, error ); + return false; + } else { + throw; + } + } + XMP_Assert ( ! resourceList->empty() ); + + delete dummyParent.handler; + dummyParent.handler = 0; + + return true; + + XMP_FILES_STATIC_END2 ( filePath, kXMPErrSev_OperationFatal ) + return false; + +} // XMPFiles::GetAssociatedResources + +// ================================================================================================= + +/* class static */ bool XMPFiles::IsMetadataWritable ( XMP_StringPtr filePath, @@ -732,6 +847,7 @@ XMPFiles::IsMetadataWritable ( dummyParent.format = handlerInfo->format; + dummyParent.openFlags = handlerInfo->flags; dummyParent.handler = handlerInfo->handlerCTor ( &dummyParent ); // We don't require any of the files to be opened at this point. @@ -759,6 +875,58 @@ XMPFiles::IsMetadataWritable ( return true; } // XMPFiles::IsMetadataWritable +// ================================================================================================= + +/* class static */ +bool +XMPFiles::IsMetadataWritable ( + const Common::XMPFileHandlerInfo& hdlInfo, + XMP_StringPtr filePath, + XMP_Bool * writable, + XMP_OptionBits options /* = 0 */ ) +{ + XMP_FILES_STATIC_START + Host_IO::FileMode clientMode; + std::string fileExt; // Used to check for excluded files. + bool excluded = FileIsExcluded ( filePath, &fileExt, &clientMode, &sDefaultErrorCallback ); // ! Fills in fileExt and clientMode. + if ( excluded ) return false; + + if ( writable == 0 ) { + XMP_Throw("Boolean parameter is required for IsMetadataWritable() API.", kXMPErr_BadParam); + } else { + *writable = kXMP_Bool_False; + } + + XMPFiles dummyParent; // GetFileModDate is static, but the handler needs a parent. + dummyParent.SetFilePath ( filePath ); + dummyParent.format = hdlInfo.format; + dummyParent.openFlags = hdlInfo.flags; + dummyParent.handler = hdlInfo.handlerCTor ( &dummyParent ); + + // We don't require any of the files to be opened at this point. + // Also, if We don't close them then this will be a problem for embedded handlers because we will be checking + // write permission on the same file which could be open (in some mode) already. + CloseLocalFile(&dummyParent); + + try { + *writable = ConvertBoolToXMP_Bool( dummyParent.handler->IsMetadataWritable() ); + } catch ( XMP_Error& error ) { + delete dummyParent.handler; + dummyParent.handler = 0; + if ( error.GetID() == kXMPErr_Unimplemented ) { + XMP_FILES_STATIC_NOTIFY_ERROR ( &sDefaultErrorCallback, filePath, kXMPErrSev_Recoverable, error ); + return false; + } else { + throw; + } + } + if ( dummyParent.handler ) { + delete dummyParent.handler; + dummyParent.handler = 0; + } + XMP_FILES_STATIC_END2 ( filePath, kXMPErrSev_OperationFatal ) + return true; +} // XMPFiles::IsMetadataWritable // ================================================================================================= @@ -1086,7 +1254,7 @@ XMPFiles::CloseFile ( XMP_OptionBits closeFlags /* = 0 */ ) XMP_Throw ( "XMPFiles::CloseFile - Safe update not supported", kXMPErr_Unavailable ); } - if ( (this->progressTracker != 0) && this->UsesLocalIO() ) { + if ( (this->progressTracker != 0) && this->UsesLocalIO() && this->ioRef != NULL ) { XMPFiles_IO * localFile = (XMPFiles_IO*)this->ioRef; localFile->SetProgressTracker ( this->progressTracker ); } @@ -1306,7 +1474,7 @@ XMPFiles::GetXMP ( SXMPMeta * xmpObj /* = 0 */, if ( xmpObj != 0 ) { // ! 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 ); + 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(); @@ -1322,9 +1490,8 @@ XMPFiles::GetXMP ( SXMPMeta * xmpObj /* = 0 */, if ( xmpObj != 0 ) *xmpObj = this->handler->xmpObj.Clone(); #else if ( xmpObj != 0 ) { - // ! 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 ); + SXMPUtils::ApplyTemplate(xmpObj, this->handler->xmpObj, applyTemplateFlags); } #endif @@ -1482,6 +1649,7 @@ XMPFiles::CanPutXMP ( XMP_StringPtr xmpPacket, } // XMPFiles::CanPutXMP + // ================================================================================================= /* class-static */ @@ -1513,6 +1681,11 @@ XMPFiles::SetProgressCallback ( const XMP_ProgressTracker::CallbackInfo & cbInfo if ( cbInfo.clientProc != 0 ) { this->progressTracker = new XMP_ProgressTracker ( cbInfo ); + if ( this->handler != 0 ) { // Provided to support ProgressTracker in Plugins + XMP_ProgressTracker::CallbackInfo * callbackInfo = new XMP_ProgressTracker::CallbackInfo(cbInfo); + this->handler->SetProgressCallback( callbackInfo ) ; + delete callbackInfo; + } } XMP_FILES_END1 ( kXMPErrSev_OperationFatal ) @@ -1559,6 +1732,8 @@ void XMPFiles::SetErrorCallback ( XMPFiles_ErrorCallbackWrapper wrapperProc, this->errorCallback.clientProc = clientProc; this->errorCallback.context = context; this->errorCallback.limit = limit; + if ( this->handler != 0 ) // Provided to support ErrroCallback in Plugins + this->handler->SetErrorCallback( ErrorCallbackBox( errorCallback.wrapperProc, errorCallback.clientProc, errorCallback.context, errorCallback.limit ) ); XMP_FILES_END1 ( kXMPErrSev_OperationFatal ) } // SetErrorCallback @@ -1571,6 +1746,8 @@ void XMPFiles::ResetErrorCallbackLimit ( XMP_Uns32 limit ) { this->errorCallback.limit = limit; this->errorCallback.notifications = 0; this->errorCallback.topSeverity = kXMPErrSev_Recoverable; + if ( this->handler != 0 ) // Provided to support ErrroCallback in Plugins + this->handler->SetErrorCallback( ErrorCallbackBox( errorCallback.wrapperProc, errorCallback.clientProc, errorCallback.context, errorCallback.limit ) ); XMP_FILES_END1 ( kXMPErrSev_OperationFatal ) } // ResetErrorCallbackLimit diff --git a/XMPFiles/source/XMPFiles.hpp b/XMPFiles/source/XMPFiles.hpp index e3ecb8b..2cb15aa 100644 --- a/XMPFiles/source/XMPFiles.hpp +++ b/XMPFiles/source/XMPFiles.hpp @@ -173,6 +173,12 @@ public: XMP_FileFormat * format = 0, XMP_OptionBits options = 0); + static bool GetFileModDate( + const Common::XMPFileHandlerInfo& hdlInfo, + XMP_StringPtr clientPath, + XMP_DateTime * modDate, + XMP_OptionBits options = 0 ); + static XMP_FileFormat CheckFileFormat(XMP_StringPtr filePath); static XMP_FileFormat CheckPackageFormat(XMP_StringPtr folderPath); @@ -182,12 +188,24 @@ public: XMP_FileFormat format = kXMP_UnknownFile , XMP_OptionBits options = 0 ); + static bool GetAssociatedResources ( + const Common::XMPFileHandlerInfo& hdlInfo, + XMP_StringPtr filePath, + std::vector<std::string> * resourceList, + XMP_OptionBits options = 0 ); + static bool IsMetadataWritable ( XMP_StringPtr filePath, XMP_Bool * writable, XMP_FileFormat format = kXMP_UnknownFile , XMP_OptionBits options = 0 ); + static bool IsMetadataWritable ( + const Common::XMPFileHandlerInfo& hdlInfo, + XMP_StringPtr filePath, + XMP_Bool * writable, + XMP_OptionBits options = 0 ); + static void SetDefaultProgressCallback(const XMP_ProgressTracker::CallbackInfo & cbInfo); static void SetDefaultErrorCallback(XMPFiles_ErrorCallbackWrapper wrapperProc, XMPFiles_ErrorCallbackProc clientProc, @@ -225,7 +243,6 @@ public: bool CanPutXMP(const SXMPMeta & xmpObj); bool CanPutXMP(XMP_StringPtr xmpPacket, XMP_StringLen xmpPacketLen = kXMP_UseNullTermination); - void SetAbortProc(XMP_AbortProc abortProc, void * abortArg); void SetProgressCallback(const XMP_ProgressTracker::CallbackInfo & cbInfo); diff --git a/XMPFiles/source/XMPFiles_Impl.cpp b/XMPFiles/source/XMPFiles_Impl.cpp index cf4c768..febb790 100644 --- a/XMPFiles/source/XMPFiles_Impl.cpp +++ b/XMPFiles/source/XMPFiles_Impl.cpp @@ -98,6 +98,8 @@ const FileExtMapping kFileExtMap[] = { "m4v", kXMP_MPEG4File }, { "m4a", kXMP_MPEG4File }, { "f4v", kXMP_MPEG4File }, + { "3gp", kXMP_MPEG4File }, + { "3g2", kXMP_MPEG4File }, { "ses", kXMP_SESFile }, { "cel", kXMP_CELFile }, { "wma", kXMP_WMAVFile }, @@ -124,6 +126,7 @@ const FileExtMapping kFileExtMap[] = { "xml", kXMP_XMLFile }, { "txt", kXMP_TextFile }, { "text", kXMP_TextFile }, + { "svg", kXMP_SVGFile }, { "psd", kXMP_PhotoshopFile }, { "ai", kXMP_IllustratorFile }, @@ -147,10 +150,8 @@ const FileExtMapping kFileExtMap[] = // Files known to contain XMP but have no smart handling, here or elsewhere. const char * kKnownScannedFiles[] = - { - "ai", // Illustrator, actually a PDF file. + { "ai", // Illustrator, actually a PDF file. "ait", // Illustrator template, actually a PDF file. - "svg", // SVG, an XML file. "aet", // After Effects template project file. "ffx", // After Effects filter preset file. "aep", // After Effects project file in proprietary format diff --git a/XMPFiles/source/XMPFiles_Impl.hpp b/XMPFiles/source/XMPFiles_Impl.hpp index c67d391..f8bc5ac 100644 --- a/XMPFiles/source/XMPFiles_Impl.hpp +++ b/XMPFiles/source/XMPFiles_Impl.hpp @@ -279,7 +279,7 @@ public: #define DefaultCTorPresets \ handlerFlags(0), stdCharForm(kXMP_CharUnknown), \ - containsXMP(false), processedXMP(false), needsUpdate(false) + containsXMP(false), processedXMP(false), needsUpdate(false), needsArtUpdate (false) XMPFileHandler() : parent(0), DefaultCTorPresets {}; XMPFileHandler (XMPFiles * _parent) : parent(_parent), DefaultCTorPresets @@ -287,7 +287,7 @@ public: xmpObj.SetErrorCallback(ErrorCallbackForXMPMeta, &parent->errorCallback); }; - virtual ~XMPFileHandler() {}; // ! The specific handler is responsible for tnailInfo.tnailImage. + virtual ~XMPFileHandler() {}; // ! The specific handler is responsible for tnailInfo.tnailImage or AlbumArt. virtual bool GetFileModDate ( XMP_DateTime * modDate ); // The default implementation is for embedding handlers. virtual void FillMetadataFiles ( std::vector<std::string> * metadataFiles ); @@ -296,12 +296,17 @@ public: virtual void CacheFileData() = 0; virtual void ProcessXMP(); // The default implementation just parses the XMP. - virtual XMP_OptionBits GetSerializeOptions(); // The default is compact. virtual void UpdateFile ( bool doSafeUpdate ) = 0; virtual void WriteTempFile ( XMP_IO* tempRef ) = 0; + // Currently, FileHandleInstance needs to implement Error and progress callback because of plugins, + // Rest handlers need not to implement them because handlers can access them parent + virtual void SetErrorCallback ( ErrorCallbackBox errorCallbackBox ) {} + virtual void SetProgressCallback ( XMP_ProgressTracker::CallbackInfo * progCBInfoPtr ) {} + + static void NotifyClient(GenericErrorCallback * errCBptr, XMP_ErrorSeverity severity, XMP_Error & error); // ! Leave the data members public so common code can see them. @@ -313,11 +318,12 @@ public: 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. + + bool needsArtUpdate; // True if Album arts need to be updated. - XMP_PacketInfo packetInfo; // ! This is always info about the packet in the file, if any! - std::string xmpPacket; // ! This is the current XMP, updated by XMPFiles::PutXMP. - SXMPMeta xmpObj; - + XMP_PacketInfo packetInfo; // ! This is always info about the packet in the file, if any! + std::string xmpPacket; // ! This is the current XMP, updated by XMPFiles::PutXMP. + SXMPMeta xmpObj; }; // XMPFileHandler typedef XMPFileHandler * (* XMPFileHandlerCTor) ( XMPFiles * parent ); |