summaryrefslogtreecommitdiff
path: root/XMPFiles
diff options
context:
space:
mode:
authorHubert Figuière <hub@figuiere.net>2013-06-29 22:31:09 -0400
committerHubert Figuière <hub@figuiere.net>2013-06-29 22:31:09 -0400
commita36182b4304c2f0d4c27091fcf26c36ea648d9f2 (patch)
tree98a365bd6a7bd95270c093021b815c1fc09b0f9a /XMPFiles
parent71d488b0d4a91ef83e63b1a01e199f29c0412821 (diff)
parent4652015fe779e12fb06ff8fa56bf70e373cd3894 (diff)
Update to XMP SDK CC-2013.06.
Merge branch 'adobe-sdk' into cc-2013.06-integration Conflicts: XMPCore/source/XMPMeta.cpp XMPCore/source/XMPMeta.hpp XMPFiles/source/FormatSupport/TIFF_FileWriter.cpp XMPFiles/source/PluginHandler/XMPAtoms.h public/include/XMP_Const.h samples/source/DumpMainXMP.cpp samples/source/DumpScannedXMP.cpp samples/source/XMPCoreCoverage.cpp samples/source/XMPFilesCoverage.cpp samples/source/common/LargeFileAccess.cpp samples/source/common/globals.h source/EndianUtils.hpp
Diffstat (limited to 'XMPFiles')
-rw-r--r--XMPFiles/build/CMakeLists.txt382
-rw-r--r--XMPFiles/resource/linux/XMPFiles.exp34
-rw-r--r--XMPFiles/resource/mac/XMPFiles.exp25
-rw-r--r--XMPFiles/resource/mac/XMPFiles.plist28
-rw-r--r--XMPFiles/resource/mac/XMPFilesPList.h17
-rw-r--r--XMPFiles/resource/win/XMPFiles.def39
-rw-r--r--XMPFiles/resource/win/XMPFiles.rc62
-rw-r--r--XMPFiles/source/FileHandlers/AIFF_Handler.cpp20
-rw-r--r--XMPFiles/source/FileHandlers/AIFF_Handler.hpp7
-rw-r--r--XMPFiles/source/FileHandlers/ASF_Handler.cpp38
-rw-r--r--XMPFiles/source/FileHandlers/ASF_Handler.hpp3
-rw-r--r--XMPFiles/source/FileHandlers/AVCHD_Handler.cpp170
-rw-r--r--XMPFiles/source/FileHandlers/AVCHD_Handler.hpp3
-rw-r--r--XMPFiles/source/FileHandlers/FLV_Handler.cpp31
-rw-r--r--XMPFiles/source/FileHandlers/FLV_Handler.hpp3
-rw-r--r--XMPFiles/source/FileHandlers/InDesign_Handler.cpp89
-rw-r--r--XMPFiles/source/FileHandlers/JPEG_Handler.cpp548
-rw-r--r--XMPFiles/source/FileHandlers/JPEG_Handler.hpp1
-rw-r--r--XMPFiles/source/FileHandlers/MP3_Handler.cpp94
-rw-r--r--XMPFiles/source/FileHandlers/MP3_Handler.hpp2
-rw-r--r--XMPFiles/source/FileHandlers/MPEG2_Handler.cpp35
-rw-r--r--XMPFiles/source/FileHandlers/MPEG2_Handler.hpp2
-rw-r--r--XMPFiles/source/FileHandlers/MPEG4_Handler.cpp268
-rw-r--r--XMPFiles/source/FileHandlers/MPEG4_Handler.hpp3
-rw-r--r--XMPFiles/source/FileHandlers/P2_Handler.cpp187
-rw-r--r--XMPFiles/source/FileHandlers/P2_Handler.hpp6
-rw-r--r--XMPFiles/source/FileHandlers/PNG_Handler.cpp8
-rw-r--r--XMPFiles/source/FileHandlers/PSD_Handler.cpp78
-rw-r--r--XMPFiles/source/FileHandlers/PSD_Handler.hpp3
-rw-r--r--XMPFiles/source/FileHandlers/PostScript_Handler.cpp1700
-rw-r--r--XMPFiles/source/FileHandlers/PostScript_Handler.hpp111
-rw-r--r--XMPFiles/source/FileHandlers/SWF_Handler.cpp13
-rw-r--r--XMPFiles/source/FileHandlers/SonyHDV_Handler.cpp140
-rw-r--r--XMPFiles/source/FileHandlers/SonyHDV_Handler.hpp3
-rw-r--r--XMPFiles/source/FileHandlers/TIFF_Handler.cpp48
-rw-r--r--XMPFiles/source/FileHandlers/TIFF_Handler.hpp3
-rw-r--r--XMPFiles/source/FileHandlers/UCF_Handler.cpp4
-rw-r--r--XMPFiles/source/FileHandlers/WAVE_Handler.cpp18
-rw-r--r--XMPFiles/source/FileHandlers/WAVE_Handler.hpp3
-rw-r--r--XMPFiles/source/FileHandlers/XDCAMEX_Handler.cpp186
-rw-r--r--XMPFiles/source/FileHandlers/XDCAMEX_Handler.hpp7
-rw-r--r--XMPFiles/source/FileHandlers/XDCAM_Handler.cpp852
-rw-r--r--XMPFiles/source/FileHandlers/XDCAM_Handler.hpp17
-rw-r--r--XMPFiles/source/FormatSupport/ASF_Support.cpp14
-rw-r--r--XMPFiles/source/FormatSupport/ASF_Support.hpp4
-rw-r--r--XMPFiles/source/FormatSupport/ID3_Support.cpp433
-rw-r--r--XMPFiles/source/FormatSupport/ID3_Support.hpp146
-rw-r--r--XMPFiles/source/FormatSupport/IFF/Chunk.cpp46
-rw-r--r--XMPFiles/source/FormatSupport/IFF/Chunk.h6
-rw-r--r--XMPFiles/source/FormatSupport/IFF/ChunkController.cpp18
-rw-r--r--XMPFiles/source/FormatSupport/IFF/ChunkController.h4
-rw-r--r--XMPFiles/source/FormatSupport/ISOBaseMedia_Support.hpp4
-rw-r--r--XMPFiles/source/FormatSupport/MOOV_Support.cpp1
-rw-r--r--XMPFiles/source/FormatSupport/MOOV_Support.hpp8
-rw-r--r--XMPFiles/source/FormatSupport/PSIR_FileWriter.cpp117
-rw-r--r--XMPFiles/source/FormatSupport/PSIR_Support.hpp13
-rw-r--r--XMPFiles/source/FormatSupport/PackageFormat_Support.cpp124
-rw-r--r--XMPFiles/source/FormatSupport/PackageFormat_Support.hpp39
-rw-r--r--XMPFiles/source/FormatSupport/PostScript_Support.cpp1094
-rw-r--r--XMPFiles/source/FormatSupport/PostScript_Support.hpp266
-rw-r--r--XMPFiles/source/FormatSupport/QuickTime_Support.cpp1
-rw-r--r--XMPFiles/source/FormatSupport/RIFF.hpp8
-rw-r--r--XMPFiles/source/FormatSupport/ReconcileTIFF.cpp68
-rw-r--r--XMPFiles/source/FormatSupport/SWF_Support.cpp2
-rw-r--r--XMPFiles/source/FormatSupport/TIFF_FileWriter.cpp269
-rw-r--r--XMPFiles/source/FormatSupport/TIFF_MemoryReader.cpp136
-rw-r--r--XMPFiles/source/FormatSupport/TIFF_Support.cpp19
-rw-r--r--XMPFiles/source/FormatSupport/TIFF_Support.hpp33
-rw-r--r--XMPFiles/source/FormatSupport/WAVE/BEXTMetadata.cpp8
-rw-r--r--XMPFiles/source/FormatSupport/WAVE/CartMetadata.cpp8
-rw-r--r--XMPFiles/source/FormatSupport/WAVE/Cr8rMetadata.cpp8
-rw-r--r--XMPFiles/source/FormatSupport/WAVE/INFOMetadata.cpp2
-rw-r--r--XMPFiles/source/FormatSupport/WAVE/PrmLMetadata.cpp8
-rw-r--r--XMPFiles/source/FormatSupport/WAVE/WAVEBehavior.h8
-rw-r--r--XMPFiles/source/FormatSupport/XDCAM_Support.cpp30
-rw-r--r--XMPFiles/source/HandlerRegistry.cpp25
-rw-r--r--XMPFiles/source/PluginHandler/FileHandler.h6
-rw-r--r--XMPFiles/source/PluginHandler/FileHandlerInstance.cpp89
-rw-r--r--XMPFiles/source/PluginHandler/FileHandlerInstance.h3
-rw-r--r--XMPFiles/source/PluginHandler/HostAPIImpl.cpp108
-rw-r--r--XMPFiles/source/PluginHandler/Module.cpp277
-rw-r--r--XMPFiles/source/PluginHandler/Module.h12
-rw-r--r--XMPFiles/source/PluginHandler/OS_Utils_Linux.cpp10
-rw-r--r--XMPFiles/source/PluginHandler/OS_Utils_Mac.cpp127
-rw-r--r--XMPFiles/source/PluginHandler/PluginManager.cpp136
-rw-r--r--XMPFiles/source/PluginHandler/PluginManager.h46
-rw-r--r--XMPFiles/source/PluginHandler/XMPAtoms.cpp55
-rw-r--r--XMPFiles/source/PluginHandler/XMPAtoms.h5
-rw-r--r--XMPFiles/source/WXMPFiles.cpp130
-rw-r--r--XMPFiles/source/XMPFiles.cpp580
-rw-r--r--XMPFiles/source/XMPFiles.hpp174
-rw-r--r--XMPFiles/source/XMPFiles_Impl.cpp94
-rw-r--r--XMPFiles/source/XMPFiles_Impl.hpp26
93 files changed, 8355 insertions, 1784 deletions
diff --git a/XMPFiles/build/CMakeLists.txt b/XMPFiles/build/CMakeLists.txt
new file mode 100644
index 0000000..28dc36a
--- /dev/null
+++ b/XMPFiles/build/CMakeLists.txt
@@ -0,0 +1,382 @@
+# =================================================================================================
+# ADOBE SYSTEMS INCORPORATED
+# Copyright 2013 Adobe Systems Incorporated
+# All Rights Reserved
+#
+# NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
+# of the Adobe license agreement accompanying it.
+# =================================================================================================
+
+# ==============================================================================
+# define minimum cmake version
+cmake_minimum_required(VERSION 2.8.6)
+
+# Enable folder grouping of projects in IDEs
+set_property(GLOBAL PROPERTY USE_FOLDERS ON)
+
+# ==============================================================================
+# This project
+set(TARGET_NAME XMPFiles)
+if(CMAKE_CL_64)
+ project(${TARGET_NAME}64)
+else(CMAKE_CL_64)
+ project(${TARGET_NAME})
+endif(CMAKE_CL_64)
+
+set(STATIC_STR "Static")
+if (XMP_BUILD_STATIC)
+ set(TARGET_NAME "${TARGET_NAME}${STATIC_STR}")
+ set(PROJECT_LABEL_STR "${TARGET_NAME}")
+else(XMP_BUILD_STATIC)
+ set(PROJECT_LABEL_STR "${TARGET_NAME}")
+endif(XMP_BUILD_STATIC)
+
+# ==============================================================================
+# Shared config
+# ==============================================================================
+# setup some values before calling shared config
+set(XMP_THIS_PROJECT_RELATIVEPATH "../..")
+
+include(${CMAKE_CURRENT_SOURCE_DIR}/${XMP_THIS_PROJECT_RELATIVEPATH}/build/XMP_Config.cmake)
+
+# ==============================================================================
+# platform specific config
+# ==============================================================================
+if(UNIX)
+ if(APPLE)
+ # OSX -------------------------------------------
+ if(XMP_BUILD_STATIC)
+ set(XMPFILES_LIB "XMPFilesStatic")
+ else()
+ set(XMPFILES_LIB "XMPFiles")
+ endif()
+ else(APPLE)
+ # Linux -------------------------------------------
+ if(XMP_BUILD_STATIC)
+ set(XMPFILES_LIB "staticXMPFiles")
+ else()
+ set(XMPFILES_LIB "XMPFiles")
+ endif()
+ endif(APPLE)
+else(UNIX)
+ if(WIN32)
+ # Windows -------------------------------------------
+ if(XMP_BUILD_STATIC)
+ set(XMPFILES_LIB "XMPFilesStatic")
+ else(XMP_BUILD_STATIC)
+ set(XMPFILES_LIB "XMPFiles")
+ endif()
+ endif(WIN32)
+endif(UNIX)
+
+# ==============================================================================
+# For convenience we define the sources as a variable. You can add
+# header files and cpp/c files and CMake will sort them out
+# ==============================================================================
+
+#file (GLOB INTERNAL_HEADER_FILES ${PRODUCT_ROOT}/source/*.hpp ${PRODUCT_ROOT}/source/*.incl_cpp ${PRODUCT_ROOT}/build/*.h)
+file (GLOB INTERNAL_HEADER_COMMONCODE_NMDS ${PRODUCT_ROOT}/XMPFiles/source/NativeMetadataSupport/*.h)
+source_group("Header Files\\Internal Headers\\Common Code\\NativeMetadataSupport" FILES ${INTERNAL_HEADER_COMMONCODE_NMDS})
+
+list (APPEND INTERNAL_HEADER_COMMONCODE
+ ${XMPROOT_DIR}/source/Endian.h
+ ${XMPROOT_DIR}/build/XMP_BuildInfo.h
+ ${SOURCE_ROOT}/XMPFiles.hpp
+ ${SOURCE_ROOT}/XMPFiles_Impl.hpp
+ )
+source_group("Header Files\\Internal Headers\\Common Code" FILES ${INTERNAL_HEADER_COMMONCODE})
+
+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})
+
+file (GLOB INTERNAL_HEADER_FORMATSUPPORT_AIFF ${SOURCE_ROOT}/FormatSupport/AIFF/*.h)
+source_group("Header Files\\Internal Headers\\Format Support\\AIFF" FILES ${INTERNAL_HEADER_FORMATSUPPORT_AIFF})
+
+file (GLOB INTERNAL_HEADER_FORMATSUPPORT_IFF ${SOURCE_ROOT}/FormatSupport/IFF/*.h)
+source_group("Header Files\\Internal Headers\\Format Support\\IFF" FILES ${INTERNAL_HEADER_FORMATSUPPORT_IFF})
+
+file (GLOB INTERNAL_HEADER_FORMATSUPPORT_WAVE ${SOURCE_ROOT}/FormatSupport/WAVE/*.h)
+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
+ )
+source_group("Header Files\\Internal Headers\\Format Support" FILES ${INTERNAL_HEADER_FORMATSUPPORT})
+
+file (GLOB INTERNAL_HEADER_PLUGINHANDLER ${SOURCE_ROOT}/PluginHandler/*.h)
+source_group("Header Files\\Internal Headers\\PluginHandler" FILES ${INTERNAL_HEADER_PLUGINHANDLER})
+
+list (APPEND PUBLIC_HEADER_CLIENTGLUE
+ ${XMPROOT_DIR}/public/include/client-glue/TXMPFiles.incl_cpp
+ ${XMPROOT_DIR}/public/include/client-glue/WXMP_Common.hpp
+ ${XMPROOT_DIR}/public/include/client-glue/WXMPFiles.hpp
+ )
+source_group("Header Files\\Public Headers\\Client Glue" FILES ${PUBLIC_HEADER_CLIENTGLUE})
+list (APPEND PUBLIC_HEADER
+ ${XMPROOT_DIR}/public/include/TXMPFiles.hpp
+ ${XMPROOT_DIR}/public/include/TXMPIterator.hpp
+ ${XMPROOT_DIR}/public/include/TXMPMeta.hpp
+ ${XMPROOT_DIR}/public/include/TXMPUtils.hpp
+ ${XMPROOT_DIR}/public/include/XMP.hpp
+ ${XMPROOT_DIR}/public/include/XMP.incl_cpp
+ ${XMPROOT_DIR}/public/include/XMP_Const.h
+ ${XMPROOT_DIR}/public/include/XMP_Environment.h
+ ${XMPROOT_DIR}/public/include/XMP_IO.hpp
+ ${XMPROOT_DIR}/public/include/XMP_Version.h
+ )
+source_group("Header Files\\Public Headers" FILES ${PUBLIC_HEADER})
+
+file (GLOB HEADERFILES_THIRDPARTY_ZLIB ${XMPROOT_DIR}/third-party/zlib/*.h)
+list (REMOVE_ITEM HEADERFILES_THIRDPARTY_ZLIB
+ ${CMAKE_CURRENT_SOURCE_DIR}/${XMPROOT_DIR}/third-party/zlib/gzguts.h
+ )
+source_group("Header Files\\ThirdParty\\zlib" FILES ${HEADERFILES_THIRDPARTY_ZLIB})
+
+list (APPEND HEADERFILES
+ ${XMPROOT_DIR}/source/Host_IO.hpp
+ ${XMPROOT_DIR}/source/XIO.hpp
+ ${XMPROOT_DIR}/source/IOUtils.hpp
+ ${XMPROOT_DIR}/source/XMPFiles_IO.hpp
+ )
+source_group("Header Files" FILES ${HEADERFILES})
+
+#resource files
+file (GLOB RESOURCE_FILES ${RESOURCE_ROOT}/${XMP_PLATFORM_SHORT}/${TARGET_NAME}.*)
+if(WIN32 AND ${XMP_BUILD_STATIC})
+ list (REMOVE_ITEM RESOURCE_FILES ${RESOURCE_ROOT}/${XMP_PLATFORM_SHORT}/${TARGET_NAME}.rc)
+endif()
+source_group("Resource Files" FILES ${RESOURCE_FILES})
+
+#source files
+file (GLOB SOURCEFILES_COMMONCODE_NMDS ${SOURCE_ROOT}/NativeMetadataSupport/*.cpp)
+source_group("Source Files\\Common Code\\NativeMetadataSupport" FILES ${SOURCEFILES_COMMONCODE_NMDS})
+
+list (APPEND SOURCEFILES_COMMONCODE
+ ${XMPROOT_DIR}/third-party/zuid/interfaces/MD5.cpp
+ ${XMPROOT_DIR}/source/UnicodeConversions.cpp
+ ${SOURCE_ROOT}/HandlerRegistry.cpp
+ ${XMPROOT_DIR}/source/PerfUtils.cpp
+ ${SOURCE_ROOT}/WXMPFiles.cpp
+ ${XMPROOT_DIR}/source/XIO.cpp
+ ${XMPROOT_DIR}/source/IOUtils.cpp
+ ${XMPROOT_DIR}/source/XML_Node.cpp
+ ${XMPROOT_DIR}/source/XMP_LibUtils.cpp
+ ${XMPROOT_DIR}/source/XMP_ProgressTracker.cpp
+ ${SOURCE_ROOT}/XMPFiles.cpp
+ ${SOURCE_ROOT}/XMPFiles_Impl.cpp
+ ${XMPROOT_DIR}/source/XMPFiles_IO.cpp
+ )
+if(UNIX)
+ list(APPEND SOURCEFILES_COMMONCODE ${XMPROOT_DIR}/source/Host_IO-POSIX.cpp)
+else()
+ list(APPEND SOURCEFILES_COMMONCODE ${XMPROOT_DIR}/source/Host_IO-Win.cpp)
+endif()
+source_group("Source Files\\Common Code" FILES ${SOURCEFILES_COMMONCODE})
+
+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)
+source_group("Source Files\\Format Support\\AIFF" FILES ${SOURCEFILES_FORMATSUPPORT_AIFF})
+
+file (GLOB SOURCEFILES_FORMATSUPPORT_IFF ${SOURCE_ROOT}/FormatSupport/IFF/*.cpp)
+source_group("Source Files\\Format Support\\IFF" FILES ${SOURCEFILES_FORMATSUPPORT_IFF})
+
+file (GLOB SOURCEFILES_FORMATSUPPORT_WAVE ${SOURCE_ROOT}/FormatSupport/WAVE/*.cpp)
+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})
+
+file (GLOB SOURCEFILES_PLUGINHANDLER ${SOURCE_ROOT}/PluginHandler/*.cpp)
+list (REMOVE_ITEM SOURCEFILES_PLUGINHANDLER
+ ${SOURCE_ROOT}/PluginHandler/OS_Utils_Linux.cpp
+ ${SOURCE_ROOT}/PluginHandler/OS_Utils_WIN.cpp
+ ${SOURCE_ROOT}/PluginHandler/OS_Utils_Mac.cpp
+ )
+if (UNIX)
+ if (APPLE)
+ list (APPEND SOURCEFILES_PLUGINHANDLER ${SOURCE_ROOT}/PluginHandler/OS_Utils_Mac.cpp)
+ else()
+ list (APPEND SOURCEFILES_PLUGINHANDLER ${SOURCE_ROOT}/PluginHandler/OS_Utils_Linux.cpp)
+ endif()
+else()
+ list (APPEND SOURCEFILES_PLUGINHANDLER ${SOURCE_ROOT}/PluginHandler/OS_Utils_WIN.cpp)
+endif()
+
+source_group("Source Files\\PluginHandler" FILES ${SOURCEFILES_PLUGINHANDLER})
+
+list (APPEND HEADERFILES_THIRDPARTY_ZLIB
+ ${XMPROOT_DIR}/third-party/zlib/adler32.c
+ ${XMPROOT_DIR}/third-party/zlib/compress.c
+ ${XMPROOT_DIR}/third-party/zlib/crc32.c
+ ${XMPROOT_DIR}/third-party/zlib/deflate.c
+ ${XMPROOT_DIR}/third-party/zlib/infback.c
+ ${XMPROOT_DIR}/third-party/zlib/inffast.c
+ ${XMPROOT_DIR}/third-party/zlib/inflate.c
+ ${XMPROOT_DIR}/third-party/zlib/inftrees.c
+ ${XMPROOT_DIR}/third-party/zlib/trees.c
+ ${XMPROOT_DIR}/third-party/zlib/uncompr.c
+ ${XMPROOT_DIR}/third-party/zlib/zutil.c
+ )
+source_group("Source Files\\ThirdParty\\zlib" FILES ${HEADERFILES_THIRDPARTY_ZLIB})
+
+list(APPEND SOURCE_FILES
+ ${INTERNAL_HEADER_COMMONCODE_NMDS}
+ ${INTERNAL_HEADER_COMMONCODE}
+ ${INTERNAL_HEADER_FILEHANDLERS}
+ ${INTERNAL_HEADER_FORMATSUPPORT_AIFF}
+ ${INTERNAL_HEADER_FORMATSUPPORT_IFF}
+ ${INTERNAL_HEADER_FORMATSUPPORT_WAVE}
+ ${INTERNAL_HEADER_FORMATSUPPORT}
+ ${INTERNAL_HEADER_PLUGINHANDLER}
+ ${PUBLIC_HEADER_CLIENTGLUE}
+ ${PUBLIC_HEADER}
+ ${HEADERFILES_THIRDPARTY_ZLIB}
+ ${HEADERFILES}
+ ${RESOURCE_FILES}
+ ${SOURCEFILES_COMMONCODE_NMDS}
+ ${SOURCEFILES_COMMONCODE}
+ ${SOURCEFILES_FILEHANDLERS}
+ ${SOURCEFILES_FORMATSUPPORT_AIFF}
+ ${SOURCEFILES_FORMATSUPPORT_IFF}
+ ${SOURCEFILES_FORMATSUPPORT_WAVE}
+ ${SOURCEFILES_FORMATSUPPORT}
+ ${SOURCEFILES_PLUGINHANDLER}
+ ${HEADERFILES_THIRDPARTY_ZLIB}
+ )
+
+# include directories
+include_directories(${XMPROOT_DIR})
+include_directories(${XMPROOT_DIR}/public/include)
+include_directories(${XMPROOT_DIR}/third-party/expat/zlib)
+include_directories(${XMPROOT_DIR}/XMPFilesPlugins/api/source)
+include_directories(${RESOURCE_ROOT}/${XMP_PLATFORM_SHORT})
+
+#additional link directory
+set(LIB_ADOBEXMP XMPCore)
+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"
+ )
+ set(DEPENDENCY_LIST "ALL:${TARGET_NAME}InfoPlist" "DLL:XMPCore")
+else ()
+ set(DEPENDENCY_LIST "DLL:XMPCore")
+endif()
+
+AddLibraryAndDependencies(${TARGET_NAME} ${XMP_BUILD_STATIC} YES "SHARED" SOURCE_FILES DEPENDENCY_LIST)
+
+# ==============================================================================
+# Link dependencies
+
+if(WIN32)
+ target_link_libraries(
+ ${TARGET_NAME}
+ ${LIB_ADOBEXMP}
+ ${XMP_PLATFORM_LINK}
+ )
+else(WIN32)
+ if(UNIX AND NOT APPLE)
+ target_link_libraries(
+ ${TARGET_NAME}
+ ${LIB_ADOBEXMP}
+ )
+ endif()
+endif()
+
+set(FRAMEWORK_LIST "Mac:CoreFoundation" "Mac:CoreServices" "Mac:${LIB_ADOBEXMP}" "Mac:${XMP_PLATFORM_LINK}")
+AddMacFramework(${TARGET_NAME} FRAMEWORK_LIST)
+
+if(UNIX)
+ if (NOT APPLE)
+ SetWinLinkFlags(${TARGET_NAME} "-Xlinker --version-script -Xlinker \"${RESOURCE_ROOT}/${XMP_PLATFORM_SHORT}/${TARGET_NAME}.exp\"" "")
+ else()
+ set_target_properties(${TARGET_NAME} PROPERTIES BUILD_WITH_INSTALL_RPATH ON INSTALL_NAME_DIR "@executable_path/../Frameworks")
+ SetWinLinkFlags(${TARGET_NAME} "-exported_symbols_list \"${RESOURCE_ROOT}/${XMP_PLATFORM_SHORT}/${TARGET_NAME}.exp\"" "${XMPFILES_LIB}")
+ endif()
+else()
+ SetWinLinkFlags(${TARGET_NAME} "" "${XMPFILES_LIB}")
+endif()
+
+set_target_properties(${TARGET_NAME} PROPERTIES OUTPUT_NAME ${XMPFILES_LIB})
+
+# ==============================================================================
+# Define output for this project
+SetOutputPath(${OUTPUT_DIR} 0)
+
+# ==============================================================================
+# Post build
+# ==============================================================================
+
+if(UNIX)
+ #hack for unix to rename the output static library. cmake add lib and extenstion as .a, rename it
+ if (NOT APPLE)
+ if (${XMP_BUILD_STATIC})
+ add_custom_command (TARGET ${TARGET_NAME}
+ POST_BUILD
+ COMMAND mv ${OUTPUT_DIR}/lib${XMPFILES_LIB}.a ${OUTPUT_DIR}/${XMPFILES_LIB}.ar
+ )
+ else()
+ if((${CMAKE_BUILD_TYPE} MATCHES "Debug") OR (${CMAKE_BUILD_TYPE} MATCHES "debug") )
+ add_custom_command (TARGET ${TARGET_NAME}
+ POST_BUILD
+ COMMAND ls -l ${OUTPUT_DIR}/lib${XMPFILES_LIB}.so
+ )
+ else()
+ add_custom_command (TARGET ${TARGET_NAME}
+ POST_BUILD
+ COMMAND strip ${OUTPUT_DIR}/lib${XMPFILES_LIB}.so
+ COMMAND ls -l ${OUTPUT_DIR}/lib${XMPFILES_LIB}.so
+ )
+ endif()
+ endif()
+ endif()
+
+else()
+ set_target_properties(${TARGET_NAME} PROPERTIES PROJECT_LABEL ${PROJECT_LABEL_STR})
+
+endif()
+
+message (STATUS "===========================================================================")
+message (STATUS " ${PROJECT_NAME} ")
+message (STATUS "===========================================================================")
+message (STATUS " OUTPUT_DIR = ${OUTPUT_DIR}")
diff --git a/XMPFiles/resource/linux/XMPFiles.exp b/XMPFiles/resource/linux/XMPFiles.exp
new file mode 100644
index 0000000..dea14a7
--- /dev/null
+++ b/XMPFiles/resource/linux/XMPFiles.exp
@@ -0,0 +1,34 @@
+VERSION {
+global:
+
+ WXMPFiles_Initialize_1;
+ WXMPFiles_Initialize_2;
+ WXMPFiles_Terminate_1;
+ WXMPFiles_CTor_1;
+ WXMPFiles_IncrementRefCount_1;
+ WXMPFiles_DecrementRefCount_1;
+ WXMPFiles_GetVersionInfo_1;
+ WXMPFiles_GetFormatInfo_1;
+ WXMPFiles_CheckFileFormat_1;
+ WXMPFiles_CheckPackageFormat_1;
+ WXMPFiles_GetFileModDate_1;
+ WXMPFiles_OpenFile_1;
+ WXMPFiles_CloseFile_1;
+ WXMPFiles_GetFileInfo_1;
+ WXMPFiles_SetAbortProc_1;
+ WXMPFiles_GetXMP_1;
+ WXMPFiles_PutXMP_1;
+ WXMPFiles_CanPutXMP_1;
+ WXMPFiles_SetDefaultProgressCallback_1;
+ WXMPFiles_SetProgressCallback_1;
+ WXMPFiles_SetDefaultErrorCallback_1;
+ WXMPFiles_SetErrorCallback_1;
+ WXMPFiles_ResetErrorCallbackLimit_1;
+ WXMPFiles_GetAssociatedResources_1;
+ WXMPFiles_IsMetadataWritable_1;
+
+local:
+
+ *;
+
+};
diff --git a/XMPFiles/resource/mac/XMPFiles.exp b/XMPFiles/resource/mac/XMPFiles.exp
new file mode 100644
index 0000000..7d5b9d2
--- /dev/null
+++ b/XMPFiles/resource/mac/XMPFiles.exp
@@ -0,0 +1,25 @@
+_WXMPFiles_Initialize_1
+_WXMPFiles_Initialize_2
+_WXMPFiles_Terminate_1
+_WXMPFiles_CTor_1
+_WXMPFiles_IncrementRefCount_1
+_WXMPFiles_DecrementRefCount_1
+_WXMPFiles_GetVersionInfo_1
+_WXMPFiles_GetFormatInfo_1
+_WXMPFiles_CheckFileFormat_1
+_WXMPFiles_CheckPackageFormat_1
+_WXMPFiles_GetFileModDate_1
+_WXMPFiles_OpenFile_1
+_WXMPFiles_CloseFile_1
+_WXMPFiles_GetFileInfo_1
+_WXMPFiles_SetAbortProc_1
+_WXMPFiles_GetXMP_1
+_WXMPFiles_PutXMP_1
+_WXMPFiles_CanPutXMP_1
+_WXMPFiles_SetDefaultProgressCallback_1
+_WXMPFiles_SetProgressCallback_1
+_WXMPFiles_SetDefaultErrorCallback_1
+_WXMPFiles_SetErrorCallback_1
+_WXMPFiles_ResetErrorCallbackLimit_1
+_WXMPFiles_GetAssociatedResources_1
+_WXMPFiles_IsMetadataWritable_1
diff --git a/XMPFiles/resource/mac/XMPFiles.plist b/XMPFiles/resource/mac/XMPFiles.plist
new file mode 100644
index 0000000..1974f55
--- /dev/null
+++ b/XMPFiles/resource/mac/XMPFiles.plist
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleExecutable</key>
+ <string>XMPFiles</string>
+ <key>CFBundleIdentifier</key>
+ <string>com.xmp.XMPFiles</string>
+ <key>CFBundleName</key>
+ <string>XMPFiles</string>
+ <key>CFBundleGetInfoString</key>
+ <string>XMP Files kBasicVersion, kXMP_Copyright</string>
+ <key>CFBundleShortVersionString</key>
+ <string>XMP Files kBasicVersion</string>
+ <key>CFBundlePackageType</key>
+ <string>FMWK</string>
+ <key>CFBundleSignature</key>
+ <string>XMP </string>
+ <key>Configuration</key>
+ <string>kConfig</string>
+ <key>FileVersion</key>
+ <string>kBasicVersion</string>
+ <key>ProductName</key>
+ <string>XMP Files</string>
+ <key>ProductVersion</key>
+ <string>XMPFILES_API_VERSION</string>
+</dict>
+</plist>
diff --git a/XMPFiles/resource/mac/XMPFilesPList.h b/XMPFiles/resource/mac/XMPFilesPList.h
new file mode 100644
index 0000000..ef53f7f
--- /dev/null
+++ b/XMPFiles/resource/mac/XMPFilesPList.h
@@ -0,0 +1,17 @@
+#include "../../../public/include/XMP_Version.h"
+#include "../../../build/XMP_BuildInfo.h"
+
+#if NDEBUG // Can't use XMP_Environment.h, it seems to mess up the PList compiler.
+ #define kConfig Release
+ #define kDebugSuffix
+ #define kBasicVersion XMPFILES_API_VERSION
+#else
+ #define kConfig Debug
+ #if IncludeNewHandlers
+ #define kDebugSuffix (debug/NewHandlers)
+ #else
+ #define kDebugSuffix (debug)
+ #endif
+ #define kBasicVersion XMPFILES_API_VERSION kDebugSuffix
+#endif
+
diff --git a/XMPFiles/resource/win/XMPFiles.def b/XMPFiles/resource/win/XMPFiles.def
new file mode 100644
index 0000000..c6d6b9a
--- /dev/null
+++ b/XMPFiles/resource/win/XMPFiles.def
@@ -0,0 +1,39 @@
+; Declares the entry points for the DLL.
+; Highest index: 25, WXMPFiles_IsMetadataWritable_1
+
+LIBRARY XMPFiles
+
+EXPORTS
+
+ WXMPFiles_Initialize_1 @1
+ WXMPFiles_Initialize_2 @17
+ WXMPFiles_Terminate_1 @2
+
+ WXMPFiles_CTor_1 @3
+ WXMPFiles_IncrementRefCount_1 @4
+ WXMPFiles_DecrementRefCount_1 @5
+
+ WXMPFiles_GetVersionInfo_1 @6
+ WXMPFiles_GetFormatInfo_1 @7
+ WXMPFiles_GetFileModDate_1 @18
+
+ WXMPFiles_OpenFile_1 @8
+ WXMPFiles_CloseFile_1 @9
+ WXMPFiles_GetFileInfo_1 @10
+ WXMPFiles_SetAbortProc_1 @11
+ WXMPFiles_GetXMP_1 @12
+ WXMPFiles_PutXMP_1 @13
+ WXMPFiles_CanPutXMP_1 @14
+
+ WXMPFiles_CheckFileFormat_1 @15
+ WXMPFiles_CheckPackageFormat_1 @16
+
+ WXMPFiles_SetDefaultProgressCallback_1 @19
+ WXMPFiles_SetProgressCallback_1 @20
+ WXMPFiles_SetDefaultErrorCallback_1 @21
+ WXMPFiles_SetErrorCallback_1 @22
+ WXMPFiles_ResetErrorCallbackLimit_1 @23
+
+ WXMPFiles_GetAssociatedResources_1 @24
+ WXMPFiles_IsMetadataWritable_1 @25
+ \ No newline at end of file
diff --git a/XMPFiles/resource/win/XMPFiles.rc b/XMPFiles/resource/win/XMPFiles.rc
new file mode 100644
index 0000000..f2ac79b
--- /dev/null
+++ b/XMPFiles/resource/win/XMPFiles.rc
@@ -0,0 +1,62 @@
+// =================================================================================================
+// Copyright 2004-2008 Adobe Systems Incorporated
+// All Rights Reserved.
+//
+// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
+// of the Adobe license agreement accompanying it.
+// =================================================================================================
+
+#include "winres.h" // ! Needed to create the Version tab in the Properties panel.
+
+#include "../../../build/XMP_BuildInfo.h"
+#include "../../../public/include/XMP_Version.h"
+
+// Defines must be set in project settings: Resources/Preprocessor Definitions
+#if NDEBUG
+ #define kConfig "Release"
+ #define kDebugSuffix ""
+#else
+ #define kConfig "Debug"
+ #if IncludeNewHandlers
+ #define kDebugSuffix "debug/NewHandlers "
+ #else
+ #define kDebugSuffix "debug "
+ #endif
+#endif
+
+#if _WIN64
+ #define BINTYPE "64"
+#else
+ #define BINTYPE "32"
+#endif
+
+#define kEngVersion XMPFILES_API_VERSION_STRING " ( " BINTYPE " bit " kDebugSuffix ")"
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION XMPFILES_API_VERSION_MAJOR,XMPFILES_API_VERSION_MINOR
+ PRODUCTVERSION XMPFILES_API_VERSION_MAJOR,XMPFILES_API_VERSION_MINOR
+ FILEFLAGSMASK 0x3FL
+ FILEOS 0x4L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "080004b0"
+ BEGIN
+ VALUE "Configuration", kConfig "\0"
+ VALUE "FileDescription", "XMP Files " kEngVersion "\0"
+ VALUE "FileVersion", kEngVersion "\0"
+ VALUE "InternalName", "XMPFiles\0"
+ VALUE "LegalCopyright", kXMP_CopyrightStr "\0"
+ VALUE "OriginalFilename", "XMPFiles.dll\0"
+ VALUE "ProductName", "XMP Files\0"
+ VALUE "ProductVersion", XMPFILES_API_VERSION_STRING "\0"
+ VALUE "BinType", BINTYPE "\0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x800, 1200
+ END
+END
diff --git a/XMPFiles/source/FileHandlers/AIFF_Handler.cpp b/XMPFiles/source/FileHandlers/AIFF_Handler.cpp
index bd59ea7..8c9d604 100644
--- a/XMPFiles/source/FileHandlers/AIFF_Handler.cpp
+++ b/XMPFiles/source/FileHandlers/AIFF_Handler.cpp
@@ -135,7 +135,7 @@ AIFF_MetaHandler::AIFF_MetaHandler ( XMPFiles * _parent )
: mChunkBehavior(NULL), mChunkController(NULL),
mAiffMeta(), mXMPChunk(NULL),
mNameChunk(NULL), mAuthChunk(NULL),
- mCprChunk(NULL), mAnnoChunk(NULL), mFileType(0)
+ mCprChunk(NULL), mAnnoChunk(NULL)
{
this->parent = _parent;
this->handlerFlags = kAIFF_HandlerFlags;
@@ -368,9 +368,23 @@ void AIFF_MetaHandler::UpdateFile ( bool doSafeUpdate )
updateLegacyChunk( &mCprChunk, kChunk_CPR, AIFFMetadata::kCopyright );
updateLegacyChunk( &mAnnoChunk, kChunk_ANNO, AIFFMetadata::kAnnotation );
}
-
+
+ XMP_ProgressTracker* progressTracker=this->parent->progressTracker;
+ // local progess tracking required because for Handlers incapable of
+ // kXMPFiles_CanRewrite XMPFiles call this Update method after making
+ // a copy of the orignal file
+ bool localProgressTracking=false;
+ if ( progressTracker != 0 )
+ {
+ if ( ! progressTracker->WorkInProgress() )
+ {
+ localProgressTracking = true;
+ progressTracker->BeginWork ();
+ }
+ }
//write tree back to file
- mChunkController->writeFile( this->parent->ioRef );
+ mChunkController->writeFile( this->parent->ioRef ,progressTracker);
+ if ( localProgressTracking && progressTracker != 0 ) progressTracker->WorkComplete();
this->needsUpdate = false; // Make sure this is only called once.
} // AIFF_MetaHandler::UpdateFile
diff --git a/XMPFiles/source/FileHandlers/AIFF_Handler.hpp b/XMPFiles/source/FileHandlers/AIFF_Handler.hpp
index 13bfa02..1ccb6a9 100644
--- a/XMPFiles/source/FileHandlers/AIFF_Handler.hpp
+++ b/XMPFiles/source/FileHandlers/AIFF_Handler.hpp
@@ -49,7 +49,8 @@ static const XMP_OptionBits kAIFF_HandlerFlags = (kXMPFiles_CanInjectXMP |
kXMPFiles_CanExpand |
kXMPFiles_PrefersInPlace |
kXMPFiles_CanReconcile |
- kXMPFiles_AllowsSafeUpdate
+ kXMPFiles_AllowsSafeUpdate |
+ kXMPFiles_CanNotifyProgress
);
/**
@@ -98,7 +99,7 @@ private:
AIFF_MetaHandler (): mChunkController(NULL), mChunkBehavior(NULL),
mAiffMeta(), mXMPChunk(NULL),
mNameChunk(NULL), mAuthChunk(NULL),
- mCprChunk(NULL), mAnnoChunk(NULL), mFileType(0) {};
+ mCprChunk(NULL), mAnnoChunk(NULL) {};
// ----- MEMBERS ----- //
@@ -119,7 +120,7 @@ private:
IChunkData *mAnnoChunk;
/** Type of the file, either AIFF or AIFC */
- XMP_Uns32 mFileType;
+ //XMP_Uns32 mFileType;
// ----- CONSTANTS ----- //
diff --git a/XMPFiles/source/FileHandlers/ASF_Handler.cpp b/XMPFiles/source/FileHandlers/ASF_Handler.cpp
index f7026bb..14bf225 100644
--- a/XMPFiles/source/FileHandlers/ASF_Handler.cpp
+++ b/XMPFiles/source/FileHandlers/ASF_Handler.cpp
@@ -49,14 +49,11 @@ bool ASF_CheckFormat ( XMP_FileFormat format,
IgnoreParam(format); IgnoreParam(fileRef); IgnoreParam(parent);
XMP_Assert ( format == kXMP_WMAVFile );
- IOBuffer ioBuf;
-
- fileRef->Rewind();
- if ( ! CheckFileSpace ( fileRef, &ioBuf, guidLen ) ) return false;
-
+ if ( fileRef->Length() < guidLen ) return false;
GUID guid;
- memcpy ( &guid, ioBuf.ptr, guidLen );
+ fileRef->Rewind();
+ fileRef->Read ( &guid, guidLen );
if ( ! IsEqualGUID ( ASF_Header_Object, guid ) ) return false;
return true;
@@ -96,7 +93,7 @@ void ASF_MetaHandler::CacheFileData()
XMP_IO* fileRef ( this->parent->ioRef );
if ( fileRef == 0 ) return;
- ASF_Support support ( &this->legacyManager );
+ ASF_Support support ( &this->legacyManager,0 );
ASF_Support::ObjectState objectState;
long numTags = support.OpenASF ( fileRef, objectState );
if ( numTags == 0 ) return;
@@ -174,7 +171,7 @@ void ASF_MetaHandler::UpdateFile ( bool doSafeUpdate )
XMP_IO* fileRef ( this->parent->ioRef );
if ( fileRef == 0 ) return;
- ASF_Support support;
+ ASF_Support support(0,this->parent->progressTracker);
ASF_Support::ObjectState objectState;
long numTags = support.OpenASF ( fileRef, objectState );
if ( numTags == 0 ) return;
@@ -233,7 +230,10 @@ void ASF_MetaHandler::UpdateFile ( bool doSafeUpdate )
updated = SafeWriteFile();
} else {
-
+
+ XMP_ProgressTracker* progressTracker = this->parent->progressTracker;
+ if ( progressTracker != 0 ) progressTracker->BeginWork ( (float)packetLen );
+
// current XMP chunk size is sufficient -> write (in place update)
updated = ASF_Support::WriteBuffer(fileRef, objectState.xmpPos, packetLen, packetStr );
@@ -257,6 +257,7 @@ void ASF_MetaHandler::UpdateFile ( bool doSafeUpdate )
}
+ if ( progressTracker != 0 ) progressTracker->WorkComplete();
}
}
@@ -276,7 +277,7 @@ void ASF_MetaHandler::WriteTempFile ( XMP_IO* tempRef )
bool ok;
XMP_IO* originalRef = this->parent->ioRef;
- ASF_Support support;
+ ASF_Support support(0,this->parent->progressTracker);
ASF_Support::ObjectState objectState;
long numTags = support.OpenASF ( originalRef, objectState );
if ( numTags == 0 ) return;
@@ -285,7 +286,21 @@ void ASF_MetaHandler::WriteTempFile ( XMP_IO* tempRef )
ASF_Support::ObjectIterator curPos = objectState.objects.begin();
ASF_Support::ObjectIterator endPos = objectState.objects.end();
-
+ XMP_ProgressTracker* progressTracker = this->parent->progressTracker;
+ if ( progressTracker != 0 ) {
+ float nonheadersize = (float)(xmpPacket.size()+kASF_ObjectBaseLen+8);
+ bool legacyChange=this->legacyManager.hasLegacyChanged( );
+ for ( ; curPos != endPos; ++curPos ) {
+ if (curPos->xmp) continue;
+ //header objects are taken care of in ASF_Support::WriteHeaderObject
+ if ( ! ( IsEqualGUID ( ASF_Header_Object, curPos->guid) && legacyChange ) ) {
+ nonheadersize+=(curPos->len);
+ }
+ }
+ curPos = objectState.objects.begin();
+ endPos = objectState.objects.end();
+ progressTracker->BeginWork ( nonheadersize );
+ }
for ( ; curPos != endPos; ++curPos ) {
ASF_Support::ObjectData object = *curPos;
@@ -316,6 +331,7 @@ void ASF_MetaHandler::WriteTempFile ( XMP_IO* tempRef )
ok = support.UpdateFileSize ( tempRef );
if ( ! ok ) XMP_Throw ( "Failure updating ASF file size", kXMPErr_InternalFailure );
+ if ( progressTracker != 0 ) progressTracker->WorkComplete();
} // ASF_MetaHandler::WriteTempFile
diff --git a/XMPFiles/source/FileHandlers/ASF_Handler.hpp b/XMPFiles/source/FileHandlers/ASF_Handler.hpp
index 2a4f63e..f8b883e 100644
--- a/XMPFiles/source/FileHandlers/ASF_Handler.hpp
+++ b/XMPFiles/source/FileHandlers/ASF_Handler.hpp
@@ -36,7 +36,8 @@ static const XMP_OptionBits kASF_HandlerFlags = ( kXMPFiles_CanInjectXMP |
kXMPFiles_CanReconcile |
kXMPFiles_AllowsOnlyXMP |
kXMPFiles_ReturnsRawPacket |
- kXMPFiles_NeedsReadOnlyPacket );
+ kXMPFiles_NeedsReadOnlyPacket |
+ kXMPFiles_CanNotifyProgress );
class ASF_MetaHandler : public XMPFileHandler
{
diff --git a/XMPFiles/source/FileHandlers/AVCHD_Handler.cpp b/XMPFiles/source/FileHandlers/AVCHD_Handler.cpp
index 5b22a13..a58594f 100644
--- a/XMPFiles/source/FileHandlers/AVCHD_Handler.cpp
+++ b/XMPFiles/source/FileHandlers/AVCHD_Handler.cpp
@@ -15,8 +15,10 @@
#include "XMPFiles/source/XMPFiles_Impl.hpp"
#include "source/XMPFiles_IO.hpp"
#include "source/XIO.hpp"
+#include "source/IOUtils.hpp"
#include "XMPFiles/source/FileHandlers/AVCHD_Handler.hpp"
+#include "XMPFiles/source/FormatSupport/PackageFormat_Support.hpp"
#include "source/UnicodeConversions.hpp"
#include "third-party/zuid/interfaces/MD5.h"
@@ -405,6 +407,16 @@ static bool MakeLeafPath ( std::string * path, XMP_StringPtr root, XMP_StringPtr
*path += ".MPL";
if ( Host_IO::GetFileMode ( path->c_str() ) == Host_IO::kFMode_IsFile ) return true;
+ } else if ( XMP_LitMatch ( suffix, ".m2ts" ) ) { // Special case of ".mts" for the stream file.
+
+ path->erase ( partialLen );
+ *path += ".mts";
+ if ( Host_IO::GetFileMode ( path->c_str() ) == Host_IO::kFMode_IsFile ) return true;
+
+ path->erase ( partialLen );
+ *path += ".MTS";
+ if ( Host_IO::GetFileMode ( path->c_str() ) == Host_IO::kFMode_IsFile ) return true;
+
}
// Still not found, revert to the original suffix.
@@ -1221,10 +1233,15 @@ static bool ReadAVCHDPlaylistExtensionData ( XMPFiles_IO & mplFile,
if ( extensionData.mMakersPrivateDataStartAddress > 0 ) {
- if ( ! avchdLegacyData.mClipExtensionData.mMakersPrivateData.mPanasonicPrivateData.mPresent ) return false;
+ // return true here because all the data is already read successfully except the maker's private data and more
+ // specifically of panasonic. So if the relevant panasonic data is not present we just skip it.
+ // Assumption here is that if its not present in ClipExtension then it will not be in Playlist extension
+ if ( ! avchdLegacyData.mClipExtensionData.mMakersPrivateData.mPanasonicPrivateData.mPresent ) return true;
mplFile.Seek ( dataBlockStart + extensionData.mMakersPrivateDataStartAddress, kXMP_SeekFromStart );
-
+
+ // Here private data was found.If the data was panasonic private data and we were unable to read it ,
+ // Return false
if ( ! ReadAVCHDMakersPrivateData ( mplFile, playlistMarkID, extensionData.mMakersPrivateData ) ) return false;
}
@@ -1450,10 +1467,10 @@ static bool ReadAVCHDLegacyPlaylistFile ( const std::string& mplPath,
static bool FindAVCHDLegacyPlaylistFile ( const std::string& strRootPath,
const std::string& strClipName,
- AVCHD_LegacyMetadata& avchdLegacyData )
+ AVCHD_LegacyMetadata& avchdLegacyData,
+ std::string &mplPath )
{
bool success = false;
- std::string mplPath;
// Find the corresponding .MPL file -- because of clip spanning the .MPL name may not match the
// .CPI name for a given clip -- we need to open .MPL files and look for one that contains a
@@ -1510,12 +1527,13 @@ static bool FindAVCHDLegacyPlaylistFile ( const std::string& strRootPath,
static bool ReadAVCHDLegacyMetadata ( const std::string& strPath,
const std::string& strRootPath,
const std::string& strClipName,
- AVCHD_LegacyMetadata& avchdLegacyData )
+ AVCHD_LegacyMetadata& avchdLegacyData,
+ std::string& mplFile)
{
bool success = ReadAVCHDLegacyClipFile ( strPath, avchdLegacyData );
if ( success && avchdLegacyData.mClipExtensionData.mPresent ) {
- success = FindAVCHDLegacyPlaylistFile ( strRootPath, strClipName, avchdLegacyData );
+ success = FindAVCHDLegacyPlaylistFile ( strRootPath, strClipName, avchdLegacyData, mplFile );
}
return success;
@@ -1820,7 +1838,7 @@ AVCHD_MetaHandler::AVCHD_MetaHandler ( XMPFiles * _parent )
if ( this->parent->tempPtr == 0 ) {
// The CheckFormat call might have been skipped.
- this->parent->tempPtr = CreatePseudoClipPath ( this->parent->filePath );
+ this->parent->tempPtr = CreatePseudoClipPath ( this->parent->GetFilePath() );
}
this->rootPath.assign ( (char*) this->parent->tempPtr );
@@ -1984,6 +2002,130 @@ bool AVCHD_MetaHandler::GetFileModDate ( XMP_DateTime * modDate )
} // AVCHD_MetaHandler::GetFileModDate
// =================================================================================================
+// AVCHD_MetaHandler::FillMetadataFiles
+// ================================
+void AVCHD_MetaHandler::FillMetadataFiles ( std::vector<std::string> * metadataFiles)
+{
+ std::string noExtPath, filePath, altPath;
+
+ noExtPath = rootPath + kDirChar + "BDMV" + kDirChar + "STREAM" + kDirChar + clipName;
+ filePath = noExtPath + ".xmp";
+ if ( ! Host_IO::Exists ( filePath.c_str() ) ) {
+ altPath = noExtPath + ".XMP";
+ if ( Host_IO::Exists ( altPath.c_str() ) ) filePath = altPath;
+ }
+ metadataFiles->push_back ( filePath );
+
+ noExtPath = rootPath + kDirChar + "BDMV" + kDirChar + "CLIPINF" + kDirChar + clipName;
+ filePath = noExtPath + ".clpi";
+ if ( ! Host_IO::Exists ( filePath.c_str() ) ) {
+ altPath = noExtPath + ".CLPI";
+ if ( ! Host_IO::Exists ( altPath.c_str() ) ) altPath = noExtPath + ".cpi";
+ if ( ! Host_IO::Exists ( altPath.c_str() ) ) altPath = noExtPath + ".CPI";
+ if ( Host_IO::Exists ( altPath.c_str() ) ) filePath = altPath;
+ }
+ metadataFiles->push_back ( filePath );
+
+} // FillMetadataFiles_AVCHD
+
+// =================================================================================================
+// AVCHD_MetaHandler::IsMetadataWritable
+// =======================================
+
+bool AVCHD_MetaHandler::IsMetadataWritable ( )
+{
+ std::vector<std::string> metadataFiles;
+ FillMetadataFiles(&metadataFiles);
+ std::vector<std::string>::iterator itr = metadataFiles.begin();
+ // Check whether sidecar is writable, if not then check if it can be created.
+ return Host_IO::Writable( itr->c_str(), true );
+}// AVCHD_MetaHandler::IsMetadataWritable
+
+// =================================================================================================
+// AVCHD_MetaHandler::FillAssociatedResources
+// ======================================
+void AVCHD_MetaHandler::FillAssociatedResources ( std::vector<std::string> * resourceList )
+{
+ /// The possible associated resources:
+ /// BDMV/
+ /// index.bdmv
+ /// MovieObject.bdmv
+ /// PLAYLIST/
+ /// xxxxx.mpls
+ /// STREAM/
+ /// zzzzz.m2ts
+ /// zzzzz.xmp
+ /// CLIPINF/
+ /// zzzzz.clpi
+ /// BACKUP/
+ // xxxxx is a five digit playlist name
+ // zzzzz is a five digit clip name
+ //
+ std::string bdmvPath = rootPath + kDirChar + "BDMV" + kDirChar;
+ std::string filePath, clipInfoPath;
+ //Add RootPath
+ filePath = rootPath + kDirChar;
+ PackageFormat_Support::AddResourceIfExists( resourceList, filePath );
+ // Add existing files under the folder "BDMV"
+ filePath = bdmvPath + "index.bdmv";
+ if ( ! PackageFormat_Support::AddResourceIfExists ( resourceList, filePath ) ) {
+ filePath = bdmvPath + "INDEX.BDMV";
+ if ( ! PackageFormat_Support::AddResourceIfExists ( resourceList, filePath ) ) {
+ filePath = bdmvPath + "index.bdm";
+ if ( ! PackageFormat_Support::AddResourceIfExists ( resourceList, filePath ) ) {
+ filePath = bdmvPath + "INDEX.BDM";
+ PackageFormat_Support::AddResourceIfExists ( resourceList, filePath );
+ }
+ }
+ }
+ filePath = bdmvPath + "MovieObject.bdmv";
+ if ( ! PackageFormat_Support::AddResourceIfExists ( resourceList, filePath ) ) {
+ filePath = bdmvPath + "MOVIEOBJECT.BDMV";
+ if ( ! PackageFormat_Support::AddResourceIfExists ( resourceList, filePath ) ) {
+ filePath = bdmvPath + "MovieObj.bdm";
+ if ( ! PackageFormat_Support::AddResourceIfExists ( resourceList, filePath ) ) {
+ filePath = bdmvPath + "MOVIEOBJ.BDM";
+ PackageFormat_Support::AddResourceIfExists ( resourceList, filePath );
+ }
+ }
+ }
+
+
+ if ( MakeClipInfoPath ( &filePath, ".clpi", true /* checkFile */ ) ) {
+ PackageFormat_Support::AddResourceIfExists ( resourceList, filePath );
+ clipInfoPath = filePath;
+ }
+ else {
+ filePath = bdmvPath + "CLIPINF" + kDirChar ;
+ PackageFormat_Support::AddResourceIfExists ( resourceList, filePath );
+ }
+
+ bool addedStreamDir=false;
+ if ( MakeClipStreamPath ( &filePath, ".xmp", true /* checkFile */ ) ) {
+ PackageFormat_Support::AddResourceIfExists ( resourceList, filePath );
+ addedStreamDir = true;
+ }
+
+
+ if ( MakeClipStreamPath ( &filePath, ".m2ts", true /* checkFile */ ) ) {
+ PackageFormat_Support::AddResourceIfExists ( resourceList, filePath );
+ }
+ else if ( ! addedStreamDir ) {
+ filePath = bdmvPath + "STREAM" + kDirChar ;
+ PackageFormat_Support::AddResourceIfExists ( resourceList, filePath );
+ }
+
+ AVCHD_LegacyMetadata avchdLegacyData;
+ if ( ReadAVCHDLegacyMetadata ( clipInfoPath, this->rootPath, this->clipName, avchdLegacyData, filePath ) ) {
+ PackageFormat_Support::AddResourceIfExists ( resourceList, filePath );
+ }
+ else {
+ filePath = bdmvPath + "PLAYLIST" + kDirChar ;
+ PackageFormat_Support::AddResourceIfExists ( resourceList, filePath );
+ }
+}
+
+// =================================================================================================
// AVCHD_MetaHandler::CacheFileData
// ================================
@@ -1992,22 +2134,24 @@ void AVCHD_MetaHandler::CacheFileData()
XMP_Assert ( ! this->containsXMP );
if ( this->parent->UsesClientIO() ) {
- XMP_Throw ( "XDCAM cannot be used with client-managed I/O", kXMPErr_InternalFailure );
+ XMP_Throw ( "AVCHD cannot be used with client-managed I/O", kXMPErr_InternalFailure );
}
// See if the clip's .XMP file exists.
std::string xmpPath;
bool found = this->MakeClipStreamPath ( &xmpPath, ".xmp", true /* checkFile */ );
- if ( ! found ) return;
+ if ( ! found ) return; // No XMP.
+ XMP_Assert ( Host_IO::Exists ( xmpPath.c_str() ) ); // MakeClipStreamPath should ensure this.
- // Read the entire .XMP file.
+ // Read the entire .XMP file. We know the XMP exists, New_XMPFiles_IO is supposed to return 0
+ // only if the file does not exist.
bool readOnly = XMP_OptionIsClear ( this->parent->openFlags, kXMPFiles_OpenForUpdate );
XMP_Assert ( this->parent->ioRef == 0 );
XMPFiles_IO* xmpFile = XMPFiles_IO::New_XMPFiles_IO ( xmpPath.c_str(), readOnly );
- if ( xmpFile == 0 ) return; // The open failed.
+ if ( xmpFile == 0 ) XMP_Throw ( "AVCHD XMP file open failure", kXMPErr_InternalFailure );
this->parent->ioRef = xmpFile;
XMP_Int64 xmpLen = xmpFile->Length();
@@ -2043,10 +2187,10 @@ void AVCHD_MetaHandler::ProcessXMP()
// read clip info
AVCHD_LegacyMetadata avchdLegacyData;
- std::string strPath;
+ std::string strPath,mplfile;
bool ok = this->MakeClipInfoPath ( &strPath, ".clpi", true /* checkFile */ );
- if ( ok ) ReadAVCHDLegacyMetadata ( strPath, this->rootPath, this->clipName, avchdLegacyData );
+ if ( ok ) ReadAVCHDLegacyMetadata ( strPath, this->rootPath, this->clipName, avchdLegacyData , mplfile);
if ( ! ok ) return;
const AVCHD_blkPlayListMarkExt& markExt = avchdLegacyData.mPlaylistExtensionData.mPlaylistMarkExt;
diff --git a/XMPFiles/source/FileHandlers/AVCHD_Handler.hpp b/XMPFiles/source/FileHandlers/AVCHD_Handler.hpp
index 46c417e..0860d5b 100644
--- a/XMPFiles/source/FileHandlers/AVCHD_Handler.hpp
+++ b/XMPFiles/source/FileHandlers/AVCHD_Handler.hpp
@@ -49,6 +49,9 @@ class AVCHD_MetaHandler : public XMPFileHandler
public:
bool GetFileModDate ( XMP_DateTime * modDate );
+ void FillMetadataFiles ( std::vector<std::string> * metadataFiles );
+ void FillAssociatedResources ( std::vector<std::string> * resourceList );
+ bool IsMetadataWritable ( );
void CacheFileData();
void ProcessXMP();
diff --git a/XMPFiles/source/FileHandlers/FLV_Handler.cpp b/XMPFiles/source/FileHandlers/FLV_Handler.cpp
index 8c1c745..db6ae4b 100644
--- a/XMPFiles/source/FileHandlers/FLV_Handler.cpp
+++ b/XMPFiles/source/FileHandlers/FLV_Handler.cpp
@@ -551,9 +551,12 @@ void FLV_MetaHandler::UpdateFile ( bool doSafeUpdate )
// Rewrite the packet in-place if it fits. Otherwise rewrite the whole file.
if ( this->xmpPacket.size() == (size_t)this->packetInfo.length ) {
-
+ XMP_ProgressTracker* progressTracker = this->parent->progressTracker;
+ if ( progressTracker != 0 ) progressTracker->BeginWork ( (float)this->xmpPacket.size() );
fileRef->Seek ( this->packetInfo.offset, kXMP_SeekFromStart );
fileRef->Write ( this->xmpPacket.data(), (XMP_Int32)this->xmpPacket.size() );
+ if ( progressTracker != 0 ) progressTracker->WorkComplete();
+
} else {
@@ -675,7 +678,29 @@ void FLV_MetaHandler::WriteTempFile ( XMP_IO* tempRef )
originalRef->Rewind();
tempRef->Rewind();
tempRef->Truncate ( 0 );
-
+ XMP_ProgressTracker* progressTracker = this->parent->progressTracker;
+ if ( progressTracker != 0 ) {
+ float fileSize=(float)(this->xmpPacket.size()+48);
+ if ( this->omdTagPos == 0 ) {
+ sourcePos=(this->flvHeaderLen+4);
+ fileSize+=sourcePos;
+ } else {
+ if ( this->xmpTagPos < this->omdTagPos ) {
+ fileSize+=this->xmpTagPos;
+ }
+ fileSize+=(this->omdTagPos + this->omdTagLen-
+ ((this->xmpTagPos!=0 && this->xmpTagPos < this->omdTagPos)?
+ (this->xmpTagPos + this->xmpTagLen):0));
+ sourcePos =this->omdTagPos + this->omdTagLen;
+ }
+ if ( (this->xmpTagPos != 0) && (this->xmpTagPos >= sourcePos) ) {
+ fileSize+=(this->xmpTagPos - sourcePos);
+ sourcePos=this->xmpTagPos + this->xmpTagLen;
+ }
+ fileSize+=(sourceLen - sourcePos);
+ sourcePos=0;
+ progressTracker->BeginWork ( fileSize );
+ }
// First do whatever is needed to put the new XMP after any existing onMetaData tag, or as the
// first time 0 tag.
@@ -729,6 +754,8 @@ void FLV_MetaHandler::WriteTempFile ( XMP_IO* tempRef )
XIO::Copy ( originalRef, tempRef, (sourceLen - sourcePos), abortProc, abortArg );
this->needsUpdate = false;
+
+ if ( progressTracker != 0 ) progressTracker->WorkComplete();
} // FLV_MetaHandler::WriteTempFile
diff --git a/XMPFiles/source/FileHandlers/FLV_Handler.hpp b/XMPFiles/source/FileHandlers/FLV_Handler.hpp
index 7a63b5c..fe129de 100644
--- a/XMPFiles/source/FileHandlers/FLV_Handler.hpp
+++ b/XMPFiles/source/FileHandlers/FLV_Handler.hpp
@@ -40,7 +40,8 @@ static const XMP_OptionBits kFLV_HandlerFlags = ( kXMPFiles_CanInjectXMP |
kXMPFiles_CanReconcile |
kXMPFiles_AllowsOnlyXMP |
kXMPFiles_ReturnsRawPacket |
- kXMPFiles_AllowsSafeUpdate
+ kXMPFiles_AllowsSafeUpdate |
+ kXMPFiles_CanNotifyProgress
);
class FLV_MetaHandler : public XMPFileHandler
diff --git a/XMPFiles/source/FileHandlers/InDesign_Handler.cpp b/XMPFiles/source/FileHandlers/InDesign_Handler.cpp
index 5031cef..d627580 100644
--- a/XMPFiles/source/FileHandlers/InDesign_Handler.cpp
+++ b/XMPFiles/source/FileHandlers/InDesign_Handler.cpp
@@ -176,7 +176,10 @@ void InDesign_MetaHandler::CacheFileData()
XMP_IO* fileRef = this->parent->ioRef;
XMP_PacketInfo & packetInfo = this->packetInfo;
- IOBuffer ioBuf;
+ XMP_Assert ( kINDD_PageSize == sizeof(InDesignMasterPage) );
+ static const size_t kBufferSize = (2 * kINDD_PageSize);
+ XMP_Uns8 buffer [kBufferSize];
+
size_t dbPages;
XMP_Uns8 cobjEndian;
@@ -184,19 +187,16 @@ void InDesign_MetaHandler::CacheFileData()
void * abortArg = this->parent->abortArg;
const bool checkAbort = (abortProc != 0);
- XMP_Assert ( kINDD_PageSize == sizeof(InDesignMasterPage) );
- XMP_Assert ( kIOBufferSize >= (2 * kINDD_PageSize) );
-
this->containsXMP = false;
// ---------------------------------------------------------------------------------
// Figure out which master page is active and seek to the contiguous object portion.
{
- FillBuffer ( fileRef, 0, &ioBuf );
- if ( ioBuf.len < (2 * kINDD_PageSize) ) XMP_Throw ( "GetMainPacket/ScanInDesignFile: Read failure", kXMPErr_ExternalFailure );
+ fileRef->Rewind();
+ fileRef->ReadAll ( buffer, (2 * kINDD_PageSize) );
- InDesignMasterPage * masters = (InDesignMasterPage *) ioBuf.ptr;
+ InDesignMasterPage * masters = (InDesignMasterPage *) &buffer[0];
XMP_Uns64 seq0 = GetUns64LE ( (XMP_Uns8 *) &masters[0].fSequenceNumber );
XMP_Uns64 seq1 = GetUns64LE ( (XMP_Uns8 *) &masters[1].fSequenceNumber );
@@ -212,9 +212,11 @@ void InDesign_MetaHandler::CacheFileData()
if ( cobjEndian == kINDD_BigEndian ) this->streamBigEndian = true;
// ---------------------------------------------------------------------------------------------
- // Look for the XMP contiguous object stream. Most of the time there will be just one stream and
- // it will be the XMP. So we might as well fill the whole buffer and not worry about reading too
- // much and seeking back to the start of the following stream.
+ // Look for the XMP contiguous object. Each contiguous object has a header and trailer, both of
+ // the InDesignContigObjMarker structure. The stream size in the header/trailer is the number of
+ // data bytes between the header and trailer. The XMP stream begins with a 4 byte size of the
+ // XMP packet. Yes, this is the contiguous object data size minus 4 - silly but true. The XMP
+ // must have a packet wrapper, the leading xpacket PI is used as the marker of XMP.
XMP_Int64 cobjPos = (XMP_Int64)dbPages * kINDD_PageSize; // ! Use a 64 bit multiply!
cobjPos -= (2 * sizeof(InDesignContigObjMarker)); // ! For the first pass in the loop.
@@ -230,72 +232,73 @@ void InDesign_MetaHandler::CacheFileData()
// ! The writeable bit of fObjectClassID is ignored, we use the packet trailer flag.
cobjPos += streamLength + (2 * sizeof(InDesignContigObjMarker));
- FillBuffer ( fileRef, cobjPos, &ioBuf ); // Make sure buffer starts at cobjPos for length check.
- if ( ioBuf.len < (2 * sizeof(InDesignContigObjMarker)) ) break; // Too small, must be end of file.
+ fileRef->Seek ( cobjPos, kXMP_SeekFromStart );
+ fileRef->ReadAll ( buffer, sizeof(InDesignContigObjMarker) );
- const InDesignContigObjMarker * cobjHeader = (const InDesignContigObjMarker *) ioBuf.ptr;
+ const InDesignContigObjMarker * cobjHeader = (const InDesignContigObjMarker *) &buffer[0];
if ( ! CheckBytes ( Uns8Ptr(&cobjHeader->fGUID), kINDDContigObjHeaderGUID, kInDesignGUIDSize ) ) break; // Not a contiguous object header.
this->xmpObjID = cobjHeader->fObjectUID; // Save these now while the buffer is good.
this->xmpClassID = cobjHeader->fObjectClassID;
streamLength = GetUns32LE ( (XMP_Uns8 *) &cobjHeader->fStreamLength );
- ioBuf.ptr += sizeof ( InDesignContigObjMarker );
- // See if this is the XMP stream. Only check for UTF-8, others get caught in fallback scanning.
+ // See if this is the XMP stream.
- if ( ! CheckFileSpace ( fileRef, &ioBuf, 4 ) ) continue; // Too small, can't possibly be XMP.
+ if ( streamLength < (4 + kUTF8_PacketHeaderLen + kUTF8_PacketTrailerLen) ) continue; // Too small, can't possibly be XMP.
- XMP_Uns32 innerLength = GetUns32LE ( ioBuf.ptr );
- if ( this->streamBigEndian ) innerLength = GetUns32BE ( ioBuf.ptr );
+ fileRef->ReadAll ( buffer, (4 + kUTF8_PacketHeaderLen) );
+ XMP_Uns32 innerLength = GetUns32LE ( &buffer[0] );
+ if ( this->streamBigEndian ) innerLength = GetUns32BE ( &buffer[0] );
if ( innerLength != (streamLength - 4) ) {
// Be tolerant of a mistake with the endian flag.
innerLength = Flip4 ( innerLength );
if ( innerLength != (streamLength - 4) ) continue; // Not legit XMP.
}
- ioBuf.ptr += 4;
-
- if ( ! CheckFileSpace ( fileRef, &ioBuf, kUTF8_PacketHeaderLen ) ) continue; // Too small, can't possibly be XMP.
- if ( ! CheckBytes ( ioBuf.ptr, kUTF8_PacketStart, strlen((char*)kUTF8_PacketStart) ) ) continue;
- ioBuf.ptr += strlen((char*)kUTF8_PacketStart);
+ XMP_Uns8 * chPtr = &buffer[4];
+ size_t startLen = strlen((char*)kUTF8_PacketStart);
+ size_t idLen = strlen((char*)kUTF8_PacketID);
+
+ if ( ! CheckBytes ( chPtr, kUTF8_PacketStart, startLen ) ) continue;
+ chPtr += startLen;
- XMP_Uns8 quote = *ioBuf.ptr;
+ XMP_Uns8 quote = *chPtr;
if ( (quote != '\'') && (quote != '"') ) continue;
- ioBuf.ptr += 1;
- if ( *ioBuf.ptr != quote ) {
- if ( ! CheckBytes ( ioBuf.ptr, Uns8Ptr("\xEF\xBB\xBF"), 3 ) ) continue;
- ioBuf.ptr += 3;
+ chPtr += 1;
+ if ( *chPtr != quote ) {
+ if ( ! CheckBytes ( chPtr, Uns8Ptr("\xEF\xBB\xBF"), 3 ) ) continue;
+ chPtr += 3;
}
- if ( *ioBuf.ptr != quote ) continue;
- ioBuf.ptr += 1;
+ if ( *chPtr != quote ) continue;
+ chPtr += 1;
- if ( ! CheckBytes ( ioBuf.ptr, Uns8Ptr(" id="), 4 ) ) continue;
- ioBuf.ptr += 4;
- quote = *ioBuf.ptr;
+ if ( ! CheckBytes ( chPtr, Uns8Ptr(" id="), 4 ) ) continue;
+ chPtr += 4;
+ quote = *chPtr;
if ( (quote != '\'') && (quote != '"') ) continue;
- ioBuf.ptr += 1;
- if ( ! CheckBytes ( ioBuf.ptr, kUTF8_PacketID, strlen((char*)kUTF8_PacketID) ) ) continue;
- ioBuf.ptr += strlen((char*)kUTF8_PacketID);
- if ( *ioBuf.ptr != quote ) continue;
- ioBuf.ptr += 1;
+ chPtr += 1;
+ if ( ! CheckBytes ( chPtr, kUTF8_PacketID, idLen ) ) continue;
+ chPtr += idLen;
+ if ( *chPtr != quote ) continue;
+ chPtr += 1;
// We've seen enough, it is the XMP. To fit the Basic_Handler model we need to compute the
- // total size of remaining contiguous objects, the trailingContentSize.
+ // total size of remaining contiguous objects, the trailingContentSize. We don't use the
+ // size to EOF, that would wrongly include the final zero padding for 4KB alignment.
this->xmpPrefixSize = sizeof(InDesignContigObjMarker) + 4;
this->xmpSuffixSize = sizeof(InDesignContigObjMarker);
packetInfo.offset = cobjPos + this->xmpPrefixSize;
packetInfo.length = innerLength;
-
XMP_Int64 tcStart = cobjPos + streamLength + (2 * sizeof(InDesignContigObjMarker));
while ( true ) {
if ( checkAbort && abortProc(abortArg) ) {
XMP_Throw ( "InDesign_MetaHandler::LocateXMP - User abort", kXMPErr_UserAbort );
}
cobjPos += streamLength + (2 * sizeof(InDesignContigObjMarker));
- FillBuffer ( fileRef, cobjPos, &ioBuf ); // Make sure buffer starts at cobjPos for length check.
- if ( ioBuf.len < sizeof(InDesignContigObjMarker) ) break; // Too small, must be end of file.
- cobjHeader = (const InDesignContigObjMarker *) ioBuf.ptr;
+ XMP_Uns32 len = fileRef->Read ( buffer, sizeof(InDesignContigObjMarker) );
+ if ( len < sizeof(InDesignContigObjMarker) ) break; // Too small, must be end of file.
+ cobjHeader = (const InDesignContigObjMarker *) &buffer[0];
if ( ! CheckBytes ( Uns8Ptr(&cobjHeader->fGUID), kINDDContigObjHeaderGUID, kInDesignGUIDSize ) ) break; // Not a contiguous object header.
streamLength = GetUns32LE ( (XMP_Uns8 *) &cobjHeader->fStreamLength );
}
diff --git a/XMPFiles/source/FileHandlers/JPEG_Handler.cpp b/XMPFiles/source/FileHandlers/JPEG_Handler.cpp
index 2cc4c31..ea025bd 100644
--- a/XMPFiles/source/FileHandlers/JPEG_Handler.cpp
+++ b/XMPFiles/source/FileHandlers/JPEG_Handler.cpp
@@ -34,8 +34,8 @@ using namespace std;
///
// =================================================================================================
-static const char * kExifSignatureString = "Exif\0\x00";
-static const char * kExifSignatureAltStr = "Exif\0\xFF";
+static const char * kExifSignatureString = "Exif\0\x00"; // There are supposed to be two zero bytes,
+static const char * kExifSignatureAltStr = "Exif\0\xFF"; // but files have been seen with just one.
static const size_t kExifSignatureLength = 6;
static const size_t kExifMaxDataLength = 0xFFFF - 2 - kExifSignatureLength;
@@ -89,26 +89,28 @@ XMPFileHandler * JPEG_MetaHandlerCTor ( XMPFiles * parent )
bool JPEG_CheckFormat ( XMP_FileFormat format,
XMP_StringPtr filePath,
- XMP_IO* fileRef,
+ XMP_IO * fileRef,
XMPFiles * parent )
{
IgnoreParam(format); IgnoreParam(filePath); IgnoreParam(parent);
XMP_Assert ( format == kXMP_JPEGFile );
- IOBuffer ioBuf;
-
- fileRef->Rewind ( );
- if ( ! CheckFileSpace ( fileRef, &ioBuf, 4 ) ) return false; // We need at least 4, the buffer is filled anyway.
+ XMP_Uns8 buffer [100];
+ XMP_Uns16 marker;
- // First look for the SOI standalone marker. Then skip all 0xFF bytes, padding plus the high
- // order byte of the next marker. Finally see if the next marker is legit.
+ fileRef->Rewind();
+ if ( fileRef->Length() < 2 ) return false; // Need at least the SOI marker.
+ size_t bufferLen = fileRef->Read ( buffer, sizeof(buffer) );
- if ( ! CheckBytes ( ioBuf.ptr, "\xFF\xD8", 2 ) ) return false;
- ioBuf.ptr += 2; // Move past the SOI.
- while ( (ioBuf.ptr < ioBuf.limit) && (*ioBuf.ptr == 0xFF) ) ++ioBuf.ptr;
- if ( ioBuf.ptr == ioBuf.limit ) return false;
+ marker = GetUns16BE ( &buffer[0] );
+ if ( marker != 0xFFD8 ) return false; // Offset 0 must have the SOI marker.
+
+ // Skip 0xFF padding and high order 0xFF of next marker.
+ size_t bufferPos = 2;
+ while ( (bufferPos < bufferLen) && (buffer[bufferPos] == 0xFF) ) bufferPos += 1;
+ if ( bufferPos == bufferLen ) return true; // Nothing but 0xFF bytes, close enough.
- XMP_Uns8 id = *ioBuf.ptr;
+ XMP_Uns8 id = buffer[bufferPos]; // Check the ID of the second marker.
if ( id >= 0xDD ) return true; // The most probable cases.
if ( (id < 0xC0) || ((id & 0xF8) == 0xD0) || (id == 0xD8) || (id == 0xDA) || (id == 0xDC) ) return false;
return true;
@@ -142,6 +144,87 @@ JPEG_MetaHandler::~JPEG_MetaHandler()
} // JPEG_MetaHandler::~JPEG_MetaHandler
// =================================================================================================
+// CacheExtendedXMP
+// ================
+
+static void CacheExtendedXMP ( ExtendedXMPInfo * extXMP, XMP_Uns8 * buffer, size_t bufferLen )
+{
+
+ // Have a portion of the extended XMP, cache the contents. This is complicated by the need to
+ // tolerate files where the extension portions are not in order. The local ExtendedXMPInfo map
+ // uses the GUID as the key and maps that to a struct that has the full length and a map of the
+ // known portions. This known portion map uses the offset of the portion as the key and maps
+ // that to a string. Only fully seen extended XMP streams are kept, the right one gets picked in
+ // ProcessXMP.
+
+ // The extended XMP JPEG marker segment content holds:
+ // - a signature string, "http://ns.adobe.com/xmp/extension/\0", already verified
+ // - a 128 bit GUID stored as a 32 byte ASCII hex string
+ // - a UInt32 full length of the entire extended XMP
+ // - a UInt32 offset for this portion of the extended XMP
+ // - the UTF-8 text for this portion of the extended XMP
+
+ if ( bufferLen < kExtXMPPrefixLength ) return; // Ignore bad input.
+ XMP_Assert ( CheckBytes ( &buffer[0], kExtXMPSignatureString, kExtXMPSignatureLength ) );
+
+ XMP_Uns8 * bufferPtr = buffer + kExtXMPSignatureLength; // Start at the GUID.
+
+ JPEG_MetaHandler::GUID_32 guid;
+ XMP_Assert ( sizeof(guid.data) == 32 );
+ memcpy ( &guid.data[0], bufferPtr, sizeof(guid.data) ); // AUDIT: Use of sizeof(guid.data) is safe.
+
+ bufferPtr += sizeof(guid.data); // Move to the length and offset.
+ XMP_Uns32 fullLen = GetUns32BE ( bufferPtr );
+ XMP_Uns32 offset = GetUns32BE ( bufferPtr+4 );
+
+ bufferPtr += 8; // Move to the XMP stream portion.
+ size_t xmpLen = bufferLen - kExtXMPPrefixLength;
+
+ #if Trace_UnlimitedJPEG
+ printf ( "New extended XMP portion: fullLen %d, offset %d, GUID %.32s\n", fullLen, offset, guid.data );
+ #endif
+
+ // Find the ExtXMPContent for this GUID, and the string for this portion's offset.
+
+ ExtendedXMPInfo::iterator guidPos = extXMP->find ( guid );
+ if ( guidPos == extXMP->end() ) {
+ ExtXMPContent newExtContent ( fullLen );
+ guidPos = extXMP->insert ( extXMP->begin(), ExtendedXMPInfo::value_type ( guid, newExtContent ) );
+ }
+
+ ExtXMPPortions::iterator offsetPos;
+ ExtXMPContent & extContent = guidPos->second;
+
+ if ( extContent.portions.empty() ) {
+ // When new create a full size offset 0 string, to which all in-order portions will get appended.
+ offsetPos = extContent.portions.insert ( extContent.portions.begin(),
+ ExtXMPPortions::value_type ( 0, std::string() ) );
+ offsetPos->second.reserve ( extContent.length );
+ }
+
+ // Try to append this portion to a logically contiguous preceeding one.
+
+ if ( offset == 0 ) {
+ offsetPos = extContent.portions.begin();
+ XMP_Assert ( (offsetPos->first == 0) && (offsetPos->second.size() == 0) );
+ } else {
+ offsetPos = extContent.portions.lower_bound ( offset );
+ --offsetPos; // Back up to the portion whose offset is less than the new offset.
+ if ( (offsetPos->first + offsetPos->second.size()) != offset ) {
+ // Can't append, create a new portion.
+ offsetPos = extContent.portions.insert ( extContent.portions.begin(),
+ ExtXMPPortions::value_type ( offset, std::string() ) );
+ }
+ }
+
+ // Cache this portion of the extended XMP.
+
+ std::string & extPortion = offsetPos->second;
+ extPortion.append ( (XMP_StringPtr)bufferPtr, xmpLen );
+
+} // CacheExtendedXMP
+
+// =================================================================================================
// JPEG_MetaHandler::CacheFileData
// ===============================
//
@@ -187,12 +270,11 @@ JPEG_MetaHandler::~JPEG_MetaHandler()
void JPEG_MetaHandler::CacheFileData()
{
- XMP_IO* fileRef = this->parent->ioRef;
+ XMP_IO* fileRef = this->parent->ioRef;
XMP_PacketInfo & packetInfo = this->packetInfo;
- size_t segLen;
- bool ok;
- IOBuffer ioBuf;
+ static const size_t kBufferSize = 64*1024; // Enough for maximum segment contents.
+ XMP_Uns8 buffer [kBufferSize];
XMP_AbortProc abortProc = this->parent->abortProc;
void * abortArg = this->parent->abortArg;
@@ -211,9 +293,7 @@ void JPEG_MetaHandler::CacheFileData()
// Look for any of the Exif, PSIR, main XMP, or extended XMP marker segments. Quit when we hit
// an SOFn, EOI, or invalid/unexpected marker.
- fileRef->Seek ( 2, kXMP_SeekFromStart ); // Skip the SOI. The JPEG header has already been verified.
- ioBuf.filePos = 2;
- RefillBuffer ( fileRef, &ioBuf );
+ fileRef->Seek ( 2, kXMP_SeekFromStart ); // Skip the SOI, CheckFormat made sure it is present.
while ( true ) {
@@ -221,238 +301,95 @@ void JPEG_MetaHandler::CacheFileData()
XMP_Throw ( "JPEG_MetaHandler::CacheFileData - User abort", kXMPErr_UserAbort );
}
- if ( ! CheckFileSpace ( fileRef, &ioBuf, 2 ) ) return;
-
- if ( *ioBuf.ptr != 0xFF ) return; // All valid markers have a high byte of 0xFF.
- while ( *ioBuf.ptr == 0xFF ) { // Skip padding 0xFF bytes and the marker's high byte.
- ++ioBuf.ptr;
- if ( ! CheckFileSpace ( fileRef, &ioBuf, 1 ) ) return;
+ if ( ! XIO::CheckFileSpace ( fileRef, 2 ) ) return; // Quit, don't throw, if the file ends unexpectedly.
+
+ XMP_Uns16 marker = XIO::ReadUns16_BE ( fileRef ); // Read the next marker.
+ if ( marker == 0xFFFF ) {
+ // Have a pad byte, skip it. These are almost unheard of, so efficiency isn't critical.
+ fileRef->Seek ( -1, kXMP_SeekFromCurrent ); // Skip the first 0xFF, read the second again.
+ continue;
}
- XMP_Uns16 marker = 0xFF00 + *ioBuf.ptr;
-
if ( (marker == 0xFFDA) || (marker == 0xFFD9) ) break; // Quit reading at the first SOS marker or at EOI.
if ( (marker == 0xFF01) || // Ill-formed file if we encounter a TEM or RSTn marker.
((0xFFD0 <= marker) && (marker <= 0xFFD7)) ) return;
- if ( marker == 0xFFED ) {
-
- // This is an APP13 marker, is it the Photoshop image resources?
-
- ++ioBuf.ptr; // Move ioBuf.ptr to the marker segment length field.
- if ( ! CheckFileSpace ( fileRef, &ioBuf, 2 ) ) return;
-
- segLen = GetUns16BE ( ioBuf.ptr );
- if ( segLen < 2 ) return; // Invalid JPEG.
-
- ioBuf.ptr += 2; // Move ioBuf.ptr to the marker segment content.
- segLen -= 2; // Adjust segLen to count just the content portion.
+ XMP_Uns16 contentLen = XIO::ReadUns16_BE ( fileRef ); // Read this segment's length.
+ if ( contentLen < 2 ) XMP_Throw ( "Invalid JPEG segment length", kXMPErr_BadJPEG );
+ contentLen -= 2; // Reduce to just the content length.
+
+ XMP_Int64 contentOrigin = fileRef->Offset();
+ size_t signatureLen;
- ok = CheckFileSpace ( fileRef, &ioBuf, kPSIRSignatureLength );
- if ( ok && (segLen >= kPSIRSignatureLength) &&
- CheckBytes ( ioBuf.ptr, kPSIRSignatureString, kPSIRSignatureLength ) ) {
+ if ( (marker == 0xFFED) && (contentLen >= kPSIRSignatureLength) ) {
- // This is the Photoshop image resources, cache the contents.
-
- ioBuf.ptr += kPSIRSignatureLength; // Move ioBuf.ptr to the image resources.
- segLen -= kPSIRSignatureLength; // Adjust segLen to count just the image resources.
- ok = CheckFileSpace ( fileRef, &ioBuf, segLen ); // Buffer the full content portion.
- if ( ! ok ) return; // Must be a truncated file.
-
- this->psirContents.assign ( (XMP_StringPtr)ioBuf.ptr, segLen );
- ioBuf.ptr += segLen;
-
- } else {
+ // This is an APP13 marker, is it the Photoshop image resources?
- // This is the not Photoshop image resources, skip the marker segment's content.
+ signatureLen = fileRef->Read ( buffer, kPSIRSignatureLength );
+ if ( (signatureLen == kPSIRSignatureLength) &&
+ CheckBytes ( &buffer[0], kPSIRSignatureString, kPSIRSignatureLength ) ) {
- if ( segLen <= size_t(ioBuf.limit - ioBuf.ptr) ) {
- ioBuf.ptr += segLen; // The next marker is in this buffer.
- } else {
- // The next marker is beyond this buffer, move to the start of it and fill the buffer.
- size_t skipCount = segLen - (ioBuf.limit - ioBuf.ptr); // The amount to move beyond this buffer.
- XMP_Int64 bufferEnd = ioBuf.filePos + (XMP_Int64)ioBuf.len; // File offset at the end of this buffer.
- XMP_Int64 nextPos = bufferEnd + (XMP_Int64)skipCount;
- MoveToOffset ( fileRef, nextPos, &ioBuf );
- }
+ size_t psirLen = contentLen - kPSIRSignatureLength;
+ fileRef->Seek ( (contentOrigin + kPSIRSignatureLength), kXMP_SeekFromStart );
+ fileRef->ReadAll ( buffer, psirLen );
+ this->psirContents.assign ( (char*)buffer, psirLen );
+ continue; // Move on to the next marker.
}
- continue; // Move on to the next marker.
-
- } else if ( marker == 0xFFE1 ) {
+ } else if ( (marker == 0xFFE1) && (contentLen >= kExifSignatureLength) ) { // Check for the shortest signature.
// This is an APP1 marker, is it the Exif, main XMP, or extended XMP?
- // ! Check in that order, which happens to be increasing signature string length.
-
- ++ioBuf.ptr; // Move ioBuf.ptr to the marker segment length field.
- if ( ! CheckFileSpace ( fileRef, &ioBuf, 2 ) ) return;
-
- segLen = GetUns16BE ( ioBuf.ptr );
- if ( segLen < 2 ) return; // Invalid JPEG.
-
- ioBuf.ptr += 2; // Move ioBuf.ptr to the marker segment content.
- segLen -= 2; // Adjust segLen to count just the content portion.
-
- // Check for the Exif APP1 marker segment.
-
- ok = CheckFileSpace ( fileRef, &ioBuf, kExifSignatureLength );
- if ( ok && (segLen >= kExifSignatureLength) &&
- (CheckBytes ( ioBuf.ptr, kExifSignatureString, kExifSignatureLength ) ||
- CheckBytes ( ioBuf.ptr, kExifSignatureAltStr, kExifSignatureLength )) ) {
-
- // This is the Exif metadata, cache the contents.
-
- ioBuf.ptr += kExifSignatureLength; // Move ioBuf.ptr to the TIFF stream.
- segLen -= kExifSignatureLength; // Adjust segLen to count just the TIFF stream.
- ok = CheckFileSpace ( fileRef, &ioBuf, segLen ); // Buffer the full content portion.
- if ( ! ok ) return; // Must be a truncated file.
-
- this->exifContents.assign ( (XMP_StringPtr)ioBuf.ptr, segLen );
- ioBuf.ptr += segLen;
-
+ // ! Check in that order, which is in increasing signature string length.
+
+ XMP_Assert ( (kExifSignatureLength < kMainXMPSignatureLength) &&
+ (kMainXMPSignatureLength < kExtXMPSignatureLength) );
+ signatureLen = fileRef->Read ( buffer, kExtXMPSignatureLength ); // Read for the longest signature.
+
+ if ( (signatureLen >= kExifSignatureLength) &&
+ (CheckBytes ( &buffer[0], kExifSignatureString, kExifSignatureLength ) ||
+ CheckBytes ( &buffer[0], kExifSignatureAltStr, kExifSignatureLength )) ) {
+
+ size_t exifLen = contentLen - kExifSignatureLength;
+ fileRef->Seek ( (contentOrigin + kExifSignatureLength), kXMP_SeekFromStart );
+ fileRef->ReadAll ( buffer, exifLen );
+ this->exifContents.assign ( (char*)buffer, exifLen );
continue; // Move on to the next marker.
}
+
+ if ( (signatureLen >= kMainXMPSignatureLength) &&
+ CheckBytes ( &buffer[0], kMainXMPSignatureString, kMainXMPSignatureLength ) ) {
- // Check for the main XMP APP1 marker segment.
-
- ok = CheckFileSpace ( fileRef, &ioBuf, kMainXMPSignatureLength );
- if ( ok && (segLen >= kMainXMPSignatureLength) &&
- CheckBytes ( ioBuf.ptr, kMainXMPSignatureString, kMainXMPSignatureLength ) ) {
-
- // This is the main XMP, cache the contents.
-
- ioBuf.ptr += kMainXMPSignatureLength; // Move ioBuf.ptr to the XMP Packet.
- segLen -= kMainXMPSignatureLength; // Adjust segLen to count just the XMP Packet.
- ok = CheckFileSpace ( fileRef, &ioBuf, segLen ); // Buffer the full content portion.
- if ( ! ok ) return; // Must be a truncated file.
-
- this->packetInfo.offset = ioBuf.filePos + (ioBuf.ptr - &ioBuf.data[0]);
- this->packetInfo.length = (XMP_Int32)segLen;
- this->packetInfo.padSize = 0; // Assume for now, set these properly in ProcessXMP.
+ this->containsXMP = true; // Found the standard XMP packet.
+ size_t xmpLen = contentLen - kMainXMPSignatureLength;
+ fileRef->Seek ( (contentOrigin + kMainXMPSignatureLength), kXMP_SeekFromStart );
+ fileRef->ReadAll ( buffer, xmpLen );
+ this->xmpPacket.assign ( (char*)buffer, xmpLen );
+ this->packetInfo.offset = contentOrigin + kMainXMPSignatureLength;
+ this->packetInfo.length = (XMP_Int32)xmpLen;
+ this->packetInfo.padSize = 0; // Assume the rest for now, set later in ProcessXMP.
this->packetInfo.charForm = kXMP_CharUnknown;
this->packetInfo.writeable = true;
-
- this->xmpPacket.assign ( (XMP_StringPtr)ioBuf.ptr, segLen );
- ioBuf.ptr += segLen; // ! Set this->packetInfo.offset first!
-
- this->containsXMP = true; // Found the standard XMP packet.
continue; // Move on to the next marker.
}
+
+ if ( (signatureLen >= kExtXMPSignatureLength) &&
+ CheckBytes ( &buffer[0], kExtXMPSignatureString, kExtXMPSignatureLength ) ) {
- // Check for an extension XMP APP1 marker segment.
-
- ok = CheckFileSpace ( fileRef, &ioBuf, kExtXMPPrefixLength ); // ! The signature, GUID, length, and offset.
- if ( ok && (segLen >= kExtXMPPrefixLength) &&
- CheckBytes ( ioBuf.ptr, kExtXMPSignatureString, kExtXMPSignatureLength ) ) {
-
- // This is a portion of the extended XMP, cache the contents. This is complicated by
- // the need to tolerate files where the extension portions are not in order. The
- // local ExtendedXMPInfo map uses the GUID as the key and maps that to a struct that
- // has the full length and a map of the known portions. This known portion map uses
- // the offset of the portion as the key and maps that to a string. Only fully seen
- // extended XMP streams are kept, the right one gets picked in ProcessXMP.
-
- segLen -= kExtXMPPrefixLength; // Adjust segLen to count just the XMP stream portion.
-
- ioBuf.ptr += kExtXMPSignatureLength; // Move ioBuf.ptr to the GUID.
- GUID_32 guid;
- XMP_Assert ( sizeof(guid.data) == 32 );
- memcpy ( &guid.data[0], ioBuf.ptr, sizeof(guid.data) ); // AUDIT: Use of sizeof(guid.data) is safe.
-
- ioBuf.ptr += 32; // Move ioBuf.ptr to the length and offset.
- XMP_Uns32 fullLen = GetUns32BE ( ioBuf.ptr );
- XMP_Uns32 offset = GetUns32BE ( ioBuf.ptr+4 );
-
- ioBuf.ptr += 8; // Move ioBuf.ptr to the XMP stream portion.
-
- #if Trace_UnlimitedJPEG
- printf ( "New extended XMP portion: fullLen %d, offset %d, GUID %.32s\n", fullLen, offset, guid.data );
- #endif
-
- // Find the ExtXMPContent for this GUID, and the string for this portion's offset.
-
- ExtendedXMPInfo::iterator guidPos = extXMP.find ( guid );
- if ( guidPos == extXMP.end() ) {
- ExtXMPContent newExtContent ( fullLen );
- guidPos = extXMP.insert ( extXMP.begin(), ExtendedXMPInfo::value_type ( guid, newExtContent ) );
- }
-
- ExtXMPPortions::iterator offsetPos;
- ExtXMPContent & extContent = guidPos->second;
-
- if ( extContent.portions.empty() ) {
- // When new create a full size offset 0 string, to which all in-order portions will get appended.
- offsetPos = extContent.portions.insert ( extContent.portions.begin(),
- ExtXMPPortions::value_type ( 0, std::string() ) );
- offsetPos->second.reserve ( extContent.length );
- }
-
- // Try to append this portion to a logically contiguous preceeding one.
-
- if ( offset == 0 ) {
- offsetPos = extContent.portions.begin();
- XMP_Assert ( (offsetPos->first == 0) && (offsetPos->second.size() == 0) );
- } else {
- offsetPos = extContent.portions.lower_bound ( offset );
- --offsetPos; // Back up to the portion whose offset is less than the new offset.
- if ( (offsetPos->first + offsetPos->second.size()) != offset ) {
- // Can't append, create a new portion.
- offsetPos = extContent.portions.insert ( extContent.portions.begin(),
- ExtXMPPortions::value_type ( offset, std::string() ) );
- }
- }
-
- // Cache this portion of the extended XMP.
-
- std::string & extPortion = offsetPos->second;
- ok = CheckFileSpace ( fileRef, &ioBuf, segLen ); // Buffer the full content portion.
- if ( ! ok ) return; // Must be a truncated file.
- extPortion.append ( (XMP_StringPtr)ioBuf.ptr, segLen );
- ioBuf.ptr += segLen;
-
+ fileRef->Seek ( contentOrigin, kXMP_SeekFromStart );
+ fileRef->ReadAll ( buffer, contentLen );
+ CacheExtendedXMP ( &extXMP, buffer, contentLen );
continue; // Move on to the next marker.
}
- // If we get here this is some other uninteresting APP1 marker segment, skip it.
-
- if ( segLen <= size_t(ioBuf.limit - ioBuf.ptr) ) {
- ioBuf.ptr += segLen; // The next marker is in this buffer.
- } else {
- // The next marker is beyond this buffer, move to the start of it and fill the buffer.
- size_t skipCount = segLen - (ioBuf.limit - ioBuf.ptr); // The amount to move beyond this buffer.
- XMP_Int64 bufferEnd = ioBuf.filePos + (XMP_Int64)ioBuf.len; // File offset at the end of this buffer.
- XMP_Int64 nextPos = bufferEnd + (XMP_Int64)skipCount;
- MoveToOffset ( fileRef, nextPos, &ioBuf );
- }
-
- } else {
-
- // This is a non-terminating but uninteresting marker segment. Skip it.
-
- ++ioBuf.ptr; // Move ioBuf.ptr to the marker segment length field.
- if ( ! CheckFileSpace ( fileRef, &ioBuf, 2 ) ) return;
-
- segLen = GetUns16BE ( ioBuf.ptr ); // Remember that the length includes itself.
- if ( segLen < 2 ) return; // Invalid JPEG.
-
- if ( segLen <= size_t(ioBuf.limit - ioBuf.ptr) ) {
- ioBuf.ptr += segLen; // The next marker is in this buffer.
- } else {
- // The next marker is beyond this buffer, move to the start of it and fill the buffer.
- size_t skipCount = segLen - (ioBuf.limit - ioBuf.ptr); // The amount to move beyond this buffer.
- XMP_Int64 bufferEnd = ioBuf.filePos + (XMP_Int64)ioBuf.len; // File offset at the end of this buffer.
- XMP_Int64 nextPos = bufferEnd + (XMP_Int64)skipCount;
- MoveToOffset ( fileRef, nextPos, &ioBuf );
- }
-
- continue; // Move on to the next marker.
-
}
+
+ // None of the above, seek to the next marker.
+ fileRef->Seek ( (contentOrigin + contentLen) , kXMP_SeekFromStart );
}
@@ -610,6 +547,8 @@ void JPEG_MetaHandler::ProcessXMP()
this->psirMgr = new PSIR_FileWriter();
this->iptcMgr = new IPTC_Writer(); // ! Parse it later.
}
+ if ( this->parent )
+ exifMgr->SetErrorCallback( &this->parent->errorCallback );
// Set up everything for the legacy import, but don't do it yet. This lets us do a forced legacy
// import if the XMP packet gets parsing errors.
@@ -666,14 +605,8 @@ void JPEG_MetaHandler::ProcessXMP()
XMP_StringLen packetLen = (XMP_StringLen)this->xmpPacket.size();
try {
this->xmpObj.ParseFromBuffer ( packetStr, packetLen );
- haveXMP = true;
- } catch ( ... ) {
- XMP_ClearOption ( options, k2XMP_FileHadXMP );
- if ( haveIPTC ) iptc.ParseMemoryDataSets ( iptcInfo.dataPtr, iptcInfo.dataLen );
- if ( iptcDigestState == kDigestMatches ) iptcDigestState = kDigestMissing;
- ImportPhotoData ( exif, iptc, psir, iptcDigestState, &this->xmpObj, options );
- throw; // ! Rethrow the exception, don't absorb it.
- }
+ } catch ( ... ) { /* Ignore parsing failures, someday we hope to get partial XMP back. */ }
+ haveXMP = true;
}
// Process the extended XMP if it has a matching GUID.
@@ -822,8 +755,6 @@ void JPEG_MetaHandler::UpdateFile ( bool doSafeUpdate )
// rest of the file is copied, skipping the old Exif, XMP, and PSIR. The checking for old metadata
// stops at the first SOFn marker.
-// *** What about Mac resources?
-
void JPEG_MetaHandler::WriteTempFile ( XMP_IO* tempRef )
{
XMP_IO* origRef = this->parent->ioRef;
@@ -832,16 +763,16 @@ void JPEG_MetaHandler::WriteTempFile ( XMP_IO* tempRef )
void * abortArg = this->parent->abortArg;
const bool checkAbort = (abortProc != 0);
- XMP_Uns16 marker;
- size_t segLen; // ! Must be a size to hold at least 64k+2.
- IOBuffer ioBuf;
- XMP_Uns32 first4;
-
- XMP_Assert ( kIOBufferSize >= (2 + 64*1024) ); // Enough for a marker plus maximum contents.
+ XMP_Uns16 marker, contentLen;
- if ( origRef->Length() == 0 ) return; // Tolerate empty files.
- origRef->Rewind();
- tempRef->Truncate ( 0 );
+ static const size_t kBufferSize = 64*1024; // Enough for a segment with maximum contents.
+ XMP_Uns8 buffer [kBufferSize];
+
+ XMP_Int64 origLength = origRef->Length();
+ if ( origLength == 0 ) return; // Tolerate empty files.
+ if ( origLength < 4 ) {
+ XMP_Throw ( "JPEG must have at least SOI and EOI markers", kXMPErr_BadJPEG );
+ }
if ( ! skipReconcile ) {
// Update the IPTC-IIM and native TIFF/Exif metadata, and reserialize the now final XMP packet.
@@ -849,46 +780,47 @@ void JPEG_MetaHandler::WriteTempFile ( XMP_IO* tempRef )
this->xmpObj.SerializeToBuffer ( &this->xmpPacket, kXMP_UseCompactFormat );
}
- RefillBuffer ( origRef, &ioBuf );
- if ( ! CheckFileSpace ( origRef, &ioBuf, 4 ) ) {
- XMP_Throw ( "JPEG must have at least SOI and EOI markers", kXMPErr_BadJPEG );
- }
+ origRef->Rewind();
+ tempRef->Truncate ( 0 );
- marker = GetUns16BE ( ioBuf.ptr );
+ marker = XIO::ReadUns16_BE ( origRef ); // Just read the SOI marker.
if ( marker != 0xFFD8 ) XMP_Throw ( "Missing SOI marker", kXMPErr_BadJPEG );
- tempRef->Write ( ioBuf.ptr, 2 );
- ioBuf.ptr += 2;
+ XIO::WriteUns16_BE ( tempRef, marker );
- // Copy the leading APP0 marker segments.
+ // Copy any leading APP0 marker segments.
while ( true ) {
if ( checkAbort && abortProc(abortArg) ) {
XMP_Throw ( "JPEG_MetaHandler::WriteFile - User abort", kXMPErr_UserAbort );
}
-
- if ( ! CheckFileSpace ( origRef, &ioBuf, 2 ) ) XMP_Throw ( "Unexpected end to JPEG", kXMPErr_BadJPEG );
- marker = GetUns16BE ( ioBuf.ptr );
+
+ if ( ! XIO::CheckFileSpace ( origRef, 2 ) ) break; // Tolerate a file that ends abruptly.
+
+ marker = XIO::ReadUns16_BE ( origRef ); // Read the next marker.
if ( marker == 0xFFFF ) {
- tempRef->Write ( ioBuf.ptr, 1 ); // Copy the 0xFF pad byte.
- ++ioBuf.ptr;
+ // Have a pad byte, skip it. These are almost unheard of, so efficiency isn't critical.
+ origRef->Seek ( -1, kXMP_SeekFromCurrent ); // Skip the first 0xFF, read the second again.
continue;
}
- if ( marker != 0xFFE0 ) break;
-
- if ( ! CheckFileSpace ( origRef, &ioBuf, 4 ) ) XMP_Throw ( "Unexpected end to JPEG", kXMPErr_BadJPEG );
- segLen = GetUns16BE ( ioBuf.ptr+2 );
- segLen += 2; // ! Don't do above in case machine does 16 bit "+".
+ if ( marker != 0xFFE0 ) break; // Have a non-APP0 marker.
+ XIO::WriteUns16_BE ( tempRef, marker ); // Write the APP0 marker.
+
+ contentLen = XIO::ReadUns16_BE ( origRef ); // Copy the APP0 segment's length.
+ XIO::WriteUns16_BE ( tempRef, contentLen );
- if ( ! CheckFileSpace ( origRef, &ioBuf, segLen ) ) XMP_Throw ( "Unexpected end to JPEG", kXMPErr_BadJPEG );
- tempRef->Write ( ioBuf.ptr, (XMP_Int32)segLen );
- ioBuf.ptr += segLen;
+ if ( contentLen < 2 ) XMP_Throw ( "Invalid JPEG segment length", kXMPErr_BadJPEG );
+ contentLen -= 2; // Reduce to just the content length.
+ origRef->ReadAll ( buffer, contentLen ); // Copy the APP0 segment's content.
+ tempRef->Write ( buffer, contentLen );
}
// Write the new Exif APP1 marker segment.
+ XMP_Uns32 first4;
+
if ( this->exifMgr != 0 ) {
void* exifPtr;
@@ -965,73 +897,91 @@ void JPEG_MetaHandler::WriteTempFile ( XMP_IO* tempRef )
// Copy remaining marker segments, skipping old metadata, to the first SOS marker or to EOI.
+ origRef->Seek ( -2, kXMP_SeekFromCurrent ); // Back up to the marker from the end of the APP0 copy loop.
+
while ( true ) {
if ( checkAbort && abortProc(abortArg) ) {
XMP_Throw ( "JPEG_MetaHandler::WriteFile - User abort", kXMPErr_UserAbort );
}
- if ( ! CheckFileSpace ( origRef, &ioBuf, 2 ) ) XMP_Throw ( "Unexpected end to JPEG", kXMPErr_BadJPEG );
- marker = GetUns16BE ( ioBuf.ptr );
+ if ( ! XIO::CheckFileSpace ( origRef, 2 ) ) break; // Tolerate a file that ends abruptly.
+
+ marker = XIO::ReadUns16_BE ( origRef ); // Read the next marker.
if ( marker == 0xFFFF ) {
- tempRef->Write ( ioBuf.ptr, 1 ); // Copy the 0xFF pad byte.
- ++ioBuf.ptr;
+ // Have a pad byte, skip it. These are almost unheard of, so efficiency isn't critical.
+ origRef->Seek ( -1, kXMP_SeekFromCurrent ); // Skip the first 0xFF, read the second again.
continue;
}
- if ( (marker == 0xFFDA) || (marker == 0xFFD9) ) break; // Quit at the first SOS marker or at EOI.
+ if ( (marker == 0xFFDA) || (marker == 0xFFD9) ) { // Quit at the first SOS marker or at EOI.
+ origRef->Seek ( -2, kXMP_SeekFromCurrent ); // The tail copy must include this marker.
+ break;
+ }
if ( (marker == 0xFF01) || // Ill-formed file if we encounter a TEM or RSTn marker.
((0xFFD0 <= marker) && (marker <= 0xFFD7)) ) {
XMP_Throw ( "Unexpected TEM or RSTn marker", kXMPErr_BadJPEG );
}
- if ( ! CheckFileSpace ( origRef, &ioBuf, 4 ) ) XMP_Throw ( "Unexpected end to JPEG", kXMPErr_BadJPEG );
- segLen = GetUns16BE ( ioBuf.ptr+2 );
-
- if ( ! CheckFileSpace ( origRef, &ioBuf, 2+segLen ) ) XMP_Throw ( "Unexpected end to JPEG", kXMPErr_BadJPEG );
-
+ contentLen = XIO::ReadUns16_BE ( origRef ); // Read this segment's length.
+ if ( contentLen < 2 ) XMP_Throw ( "Invalid JPEG segment length", kXMPErr_BadJPEG );
+ contentLen -= 2; // Reduce to just the content length.
+
+ XMP_Int64 contentOrigin = origRef->Offset();
bool copySegment = true;
- XMP_Uns8* signaturePtr = ioBuf.ptr + 4;
+ size_t signatureLen;
+
+ if ( (marker == 0xFFED) && (contentLen >= kPSIRSignatureLength) ) {
- if ( marker == 0xFFED ) {
- if ( (segLen >= kPSIRSignatureLength) &&
- CheckBytes ( signaturePtr, kPSIRSignatureString, kPSIRSignatureLength ) ) {
+ // This is an APP13 segment, skip if it is the old PSIR.
+ signatureLen = origRef->Read ( buffer, kPSIRSignatureLength );
+ if ( (signatureLen == kPSIRSignatureLength) &&
+ CheckBytes ( &buffer[0], kPSIRSignatureString, kPSIRSignatureLength ) ) {
copySegment = false;
}
- } else if ( marker == 0xFFE1 ) {
- if ( (segLen >= kExifSignatureLength) &&
- (CheckBytes ( signaturePtr, kExifSignatureString, kExifSignatureLength ) ||
- CheckBytes ( signaturePtr, kExifSignatureAltStr, kExifSignatureLength )) ) {
+
+ } else if ( (marker == 0xFFE1) && (contentLen >= kExifSignatureLength) ) { // Check for the shortest signature.
+
+ // This is an APP1 segment, skip if it is the old Exif or XMP.
+
+ XMP_Assert ( (kExifSignatureLength < kMainXMPSignatureLength) &&
+ (kMainXMPSignatureLength < kExtXMPSignatureLength) );
+ signatureLen = origRef->Read ( buffer, kExtXMPSignatureLength ); // Read for the longest signature.
+
+ if ( (signatureLen >= kExifSignatureLength) &&
+ (CheckBytes ( &buffer[0], kExifSignatureString, kExifSignatureLength ) ||
+ CheckBytes ( &buffer[0], kExifSignatureAltStr, kExifSignatureLength )) ) {
copySegment = false;
- } else if ( (segLen >= kMainXMPSignatureLength) &&
- CheckBytes ( signaturePtr, kMainXMPSignatureString, kMainXMPSignatureLength ) ) {
+ }
+
+ if ( copySegment && (signatureLen >= kMainXMPSignatureLength) &&
+ CheckBytes ( &buffer[0], kMainXMPSignatureString, kMainXMPSignatureLength ) ) {
copySegment = false;
- } else if ( (segLen >= kExtXMPPrefixLength) &&
- CheckBytes ( signaturePtr, kExtXMPSignatureString, kExtXMPSignatureLength ) ) {
+ }
+
+ if ( copySegment && (signatureLen == kExtXMPSignatureLength) &&
+ CheckBytes ( &buffer[0], kExtXMPSignatureString, kExtXMPPrefixLength ) ) {
copySegment = false;
}
+
+ }
+
+ if ( ! copySegment ) {
+ origRef->Seek ( (contentOrigin + contentLen), kXMP_SeekFromStart );
+ } else {
+ XIO::WriteUns16_BE ( tempRef, marker );
+ XIO::WriteUns16_BE ( tempRef, (contentLen + 2) );
+ origRef->Seek ( contentOrigin, kXMP_SeekFromStart );
+ origRef->ReadAll ( buffer, contentLen );
+ tempRef->Write ( buffer, contentLen );
}
-
- if ( copySegment ) tempRef->Write ( ioBuf.ptr, (XMP_Int32)(2+segLen) );
-
- ioBuf.ptr += 2+segLen;
}
// Copy the remainder of the source file.
- size_t bufTail = ioBuf.len - (ioBuf.ptr - &ioBuf.data[0]);
- tempRef->Write ( ioBuf.ptr, (XMP_Int32)bufTail );
- ioBuf.ptr += bufTail;
-
- while ( true ) {
- RefillBuffer ( origRef, &ioBuf );
- if ( ioBuf.len == 0 ) break;
- tempRef->Write ( ioBuf.ptr, (XMP_Int32)ioBuf.len );
- ioBuf.ptr += ioBuf.len;
- }
-
+ XIO::Copy ( origRef, tempRef, (origLength - origRef->Offset()) );
this->needsUpdate = false;
} // JPEG_MetaHandler::WriteTempFile
diff --git a/XMPFiles/source/FileHandlers/JPEG_Handler.hpp b/XMPFiles/source/FileHandlers/JPEG_Handler.hpp
index 568ee1b..9e859c1 100644
--- a/XMPFiles/source/FileHandlers/JPEG_Handler.hpp
+++ b/XMPFiles/source/FileHandlers/JPEG_Handler.hpp
@@ -90,6 +90,7 @@ private:
typedef std::map < GUID_32, std::string > ExtendedXMPMap;
ExtendedXMPMap extendedXMP; // ! Only contains those with complete data.
+// void CacheExtendedXMP ( ExtendedXMPInfo * extXMP, XMP_Uns8 * buffer, size_t bufferLen );
}; // JPEG_MetaHandler
diff --git a/XMPFiles/source/FileHandlers/MP3_Handler.cpp b/XMPFiles/source/FileHandlers/MP3_Handler.cpp
index 47f3aae..a568c1a 100644
--- a/XMPFiles/source/FileHandlers/MP3_Handler.cpp
+++ b/XMPFiles/source/FileHandlers/MP3_Handler.cpp
@@ -13,6 +13,7 @@
#include "public/include/XMP_IO.hpp"
#include "XMPFiles/source/XMPFiles_Impl.hpp"
+#include "XMPFiles/source/FormatSupport/Reconcile_Impl.hpp"
#include "source/XMPFiles_IO.hpp"
#include "source/XIO.hpp"
@@ -232,7 +233,7 @@ void MP3_MetaHandler::CacheFileData()
// No space for another frame? => assume into ID3v2.4 padding.
XMP_Int64 newPos = file->Offset();
XMP_Int64 spaceLeft = this->oldTagSize - newPos; // Depends on first check below!
- if ( (newPos > this->oldTagSize) || (spaceLeft < ID3Header::kID3_TagHeaderSize) ) break;
+ if ( (newPos > this->oldTagSize) || (spaceLeft < (XMP_Int64)ID3Header::kID3_TagHeaderSize) ) break;
}
@@ -309,8 +310,8 @@ void MP3_MetaHandler::ProcessXMP()
// go deal with it!
// get the property
- std::string utf8string;
- bool result = curFrame->getFrameValue ( this->majorVersion, logicalID, &utf8string );
+ std::string id3Text, xmpText;
+ bool result = curFrame->getFrameValue ( this->majorVersion, logicalID, &id3Text );
if ( ! result ) continue; //ignore but preserve this frame (i.e. not applicable COMM frame)
//////////////////////////////////////////////////////////////////////////////////
@@ -334,41 +335,19 @@ void MP3_MetaHandler::ProcessXMP()
this->xmpObj.SetProperty ( kXMP_NS_DM, "partOfCompilation", "true" );
- } else if ( ! utf8string.empty() ) {
+ } else if ( ! id3Text.empty() ) {
switch ( logicalID ) {
case 0x54495432: // TIT2 -> title["x-default"]
case 0x54434F50: // TCOP -> rights["x-default"]
- this->xmpObj.SetLocalizedText ( reconProps[r].ns, reconProps[r].prop,"", "x-default", utf8string );
+ this->xmpObj.SetLocalizedText ( reconProps[r].ns, reconProps[r].prop,"", "x-default", id3Text );
break;
- case 0x54434F4E: // TCON -> genre ( might be numeric string. prior to 2.3 a one-byte numeric value? )
- {
- XMP_Int32 pos = 0; // going through input string
- if ( utf8string[pos] == '(' ) { // number in brackets?
- pos++;
- XMP_Uns8 iGenre = (XMP_Uns8) atoi( &utf8string.c_str()[1] );
- if ( iGenre < 127 ) {
- utf8string.assign( Genres[iGenre] );
- } else {
- utf8string.assign( Genres[12] ); // "Other"
- }
- } else {
- // Text, let's "try" to find it anyway (for best upper/lower casing)
- int i;
- const char* genreCString = utf8string.c_str();
- for ( i = 0; i < 127; ++i ) {
- if ( (strlen ( genreCString ) == strlen(Genres[i])) && //fixing buggy stricmp behaviour on PPC
- (stricmp ( genreCString, Genres[i] ) == 0 )) {
- utf8string.assign ( Genres[i] ); // found, let's use the one in the list
- break;
- }
- }
- // otherwise (if for-loop runs through): leave as is
- }
- // write out property (derived or direct, but certainly non-numeric)
- this->xmpObj.SetProperty ( reconProps[r].ns, reconProps[r].prop, utf8string );
+ case 0x54434F4E: // TCON -> genre
+ ID3_Support::GenreUtils::ConvertGenreToXMP ( id3Text.c_str(), &xmpText );
+ if ( ! xmpText.empty() ) {
+ this->xmpObj.SetProperty ( reconProps[r].ns, reconProps[r].prop, xmpText );
}
break;
@@ -376,7 +355,7 @@ void MP3_MetaHandler::ProcessXMP()
{
try { // Don't let wrong dates in id3 stop import.
if ( ! hasTDRC ) {
- newDateTime.year = SXMPUtils::ConvertToInt ( utf8string );
+ newDateTime.year = SXMPUtils::ConvertToInt ( id3Text );
newDateTime.hasDate = true;
}
} catch ( ... ) {
@@ -390,9 +369,9 @@ void MP3_MetaHandler::ProcessXMP()
try { // Don't let wrong dates in id3 stop import.
// only if no TDRC has been found before
//&& must have the format DDMM
- if ( (! hasTDRC) && (utf8string.length() == 4) ) {
- newDateTime.day = SXMPUtils::ConvertToInt (utf8string.substr(0,2));
- newDateTime.month = SXMPUtils::ConvertToInt ( utf8string.substr(2,2));
+ if ( (! hasTDRC) && (id3Text.length() == 4) ) {
+ newDateTime.day = SXMPUtils::ConvertToInt (id3Text.substr(0,2));
+ newDateTime.month = SXMPUtils::ConvertToInt ( id3Text.substr(2,2));
newDateTime.hasDate = true;
}
} catch ( ... ) {
@@ -406,9 +385,9 @@ void MP3_MetaHandler::ProcessXMP()
try { // Don't let wrong dates in id3 stop import.
// only if no TDRC has been found before
// && must have the format HHMM
- if ( (! hasTDRC) && (utf8string.length() == 4) ) {
- newDateTime.hour = SXMPUtils::ConvertToInt (utf8string.substr(0,2));
- newDateTime.minute = SXMPUtils::ConvertToInt ( utf8string.substr(2,2));
+ if ( (! hasTDRC) && (id3Text.length() == 4) ) {
+ newDateTime.hour = SXMPUtils::ConvertToInt (id3Text.substr(0,2));
+ newDateTime.minute = SXMPUtils::ConvertToInt ( id3Text.substr(2,2));
newDateTime.hasTime = true;
}
} catch ( ... ) {
@@ -422,7 +401,7 @@ void MP3_MetaHandler::ProcessXMP()
try { // Don't let wrong dates in id3 stop import.
hasTDRC = true;
// This always wins over TYER, TDAT and TIME
- SXMPUtils::ConvertToDate ( utf8string, &newDateTime );
+ SXMPUtils::ConvertToDate ( id3Text, &newDateTime );
} catch ( ... ) {
// Do nothing, let other imports proceed.
}
@@ -432,7 +411,7 @@ void MP3_MetaHandler::ProcessXMP()
default:
// NB: COMM/USLT need no special fork regarding language alternatives/multiple occurence.
// relevant code forks are in ID3_Support::getFrameValue()
- this->xmpObj.SetProperty ( reconProps[r].ns, reconProps[r].prop, utf8string );
+ this->xmpObj.SetProperty ( reconProps[r].ns, reconProps[r].prop, id3Text );
break;
}//switch
@@ -497,7 +476,6 @@ void MP3_MetaHandler::UpdateFile ( bool doSafeUpdate )
std::string value;
bool needDescriptor = false;
- bool need16LE = true;
bool needEncodingByte = true;
XMP_Uns32 logicalID = GetUns32BE ( reconProps[r].mainID );
@@ -512,7 +490,6 @@ void MP3_MetaHandler::UpdateFile ( bool doSafeUpdate )
switch ( logicalID ) {
case 0x54434D50: // TCMP if exists: part of compilation
- need16LE = false;
if ( xmpObj.GetProperty( kXMP_NS_DM, "partOfCompilation", &value, 0 ) && ( 0 == stricmp( value.c_str(), "true" ) )) {
value = "1"; // set a TCMP frame of value 1
} else {
@@ -527,27 +504,14 @@ void MP3_MetaHandler::UpdateFile ( bool doSafeUpdate )
case 0x54434F4E: // TCON -> genre
{
- if (! xmpObj.GetProperty( reconProps[r].ns, reconProps[r].prop, &value, 0 )) { // nothing found? -> Erase string. (Leads to Unset below)
- value.erase();
- break;
+ bool found = xmpObj.GetProperty ( reconProps[r].ns, reconProps[r].prop, &value, 0 );
+ if ( found ) {
+ std::string xmpValue = value;
+ ID3_Support::GenreUtils::ConvertGenreToID3 ( xmpValue.c_str(), &value );
}
- // genre: we need to get the number back, if possible
- XMP_Int16 iFound = -1; // flag as "not found"
- for ( int i=0; i < 127; ++i ) {
- if ( (value.size() == strlen(Genres[i])) && (stricmp( value.c_str(), Genres[i] ) == 0) ) {
- iFound = i; // Found
- break;
- }
- }
- if ( iFound == -1 ) break; // stick with the literal value (also for v2.3, since this is common practice!)
-
- need16LE = false; // no unicode need for (##)
- char strGenre[64];
- snprintf ( strGenre, sizeof(strGenre), "(%d)", iFound ); // AUDIT: Using sizeof(strGenre) is safe.
- value.assign(strGenre);
}
break;
-
+
case 0x434F4D4D: // COMM
case 0x55534C54: // USLT, both need descriptor.
needDescriptor = true;
@@ -617,13 +581,11 @@ void MP3_MetaHandler::UpdateFile ( bool doSafeUpdate )
case 0x57434F50: //WCOP
needEncodingByte = false;
- need16LE = false;
if (! xmpObj.GetProperty( reconProps[r].ns, reconProps[r].prop, &value, 0 )) value.erase(); // if not, erase string
break;
case 0x5452434B: // TRCK
case 0x54504F53: // TPOS
- need16LE = false;
// no break, go on:
default:
@@ -646,11 +608,13 @@ void MP3_MetaHandler::UpdateFile ( bool doSafeUpdate )
}
// 3/4) no old value, create new value
+ bool needUTF16 = false;
+ if ( needEncodingByte ) needUTF16 = (! ReconcileUtils::IsASCII ( value.c_str(), value.size() ) );
if ( frame != 0 ) {
- frame->setFrameValue( value, needDescriptor, need16LE, false, needEncodingByte );
+ frame->setFrameValue( value, needDescriptor, needUTF16, false, needEncodingByte );
} else {
ID3v2Frame* newFrame=new ID3v2Frame( storedID );
- newFrame->setFrameValue( value, needDescriptor, need16LE, false, needEncodingByte ); //always write as utf16-le incl. BOM
+ newFrame->setFrameValue( value, needDescriptor, needUTF16, false, needEncodingByte ); //always write as utf16-le incl. BOM
framesVector.push_back( newFrame );
framesMap[ storedID ] = newFrame;
continue;
@@ -685,7 +649,7 @@ void MP3_MetaHandler::UpdateFile ( bool doSafeUpdate )
if ( framesVector[i]->active ) newFramesSize += (frameHeaderSize + framesVector[i]->contentSize);
}
- mustShift = (newFramesSize > (oldTagSize - ID3Header::kID3_TagHeaderSize)) ||
+ mustShift = (newFramesSize > (XMP_Int64)(oldTagSize - ID3Header::kID3_TagHeaderSize)) ||
//optimization: If more than 8K can be saved by rewriting the MP3, go do it:
((newFramesSize + 8*1024) < oldTagSize );
diff --git a/XMPFiles/source/FileHandlers/MP3_Handler.hpp b/XMPFiles/source/FileHandlers/MP3_Handler.hpp
index 261ca3e..05345c6 100644
--- a/XMPFiles/source/FileHandlers/MP3_Handler.hpp
+++ b/XMPFiles/source/FileHandlers/MP3_Handler.hpp
@@ -63,7 +63,7 @@ private:
XMP_Uns8 majorVersion, minorVersion; // Version Number post ID3v2, i.e. 3 0 ==> ID3v2.3.0
bool hasID3Tag; //incoming file has an ID3 tag?
bool hasFooter;
- bool legacyChanged; // tag rewrite certainly needed?
+ //bool legacyChanged; // tag rewrite certainly needed?
ID3Header id3Header;
diff --git a/XMPFiles/source/FileHandlers/MPEG2_Handler.cpp b/XMPFiles/source/FileHandlers/MPEG2_Handler.cpp
index cd8512e..abeb781 100644
--- a/XMPFiles/source/FileHandlers/MPEG2_Handler.cpp
+++ b/XMPFiles/source/FileHandlers/MPEG2_Handler.cpp
@@ -19,9 +19,10 @@
#include "XMPFiles/source/XMPFiles_Impl.hpp"
#include "source/XMPFiles_IO.hpp"
#include "source/XIO.hpp"
+#include "source/IOUtils.hpp"
#include "XMPFiles/source/FileHandlers/MPEG2_Handler.hpp"
-
+#include "../FormatSupport/PackageFormat_Support.hpp"
using namespace std;
// =================================================================================================
@@ -96,6 +97,10 @@ MPEG2_MetaHandler::MPEG2_MetaHandler ( XMPFiles * _parent )
this->handlerFlags = kMPEG2_HandlerFlags;
this->stdCharForm = kXMP_Char8Bit;
+ XMP_StringPtr filePath = this->parent->GetFilePath().c_str();
+ XMP_StringPtr extPtr = FindFileExtension ( filePath );
+ this->sidecarPath.assign ( filePath, (extPtr - filePath) );
+ this->sidecarPath += ".xmp";
} // MPEG2_MetaHandler::MPEG2_MetaHandler
// =================================================================================================
@@ -114,18 +119,29 @@ MPEG2_MetaHandler::~MPEG2_MetaHandler()
bool MPEG2_MetaHandler::GetFileModDate ( XMP_DateTime * modDate )
{
-
- XMP_StringPtr filePath = this->parent->filePath.c_str();
- XMP_StringPtr extPtr = FindFileExtension ( filePath );
- this->sidecarPath.assign ( filePath, (extPtr - filePath) );
- this->sidecarPath += ".xmp";
-
if ( ! Host_IO::Exists ( this->sidecarPath.c_str() ) ) return false;
return Host_IO::GetModifyDate ( this->sidecarPath.c_str(), modDate );
} // MPEG2_MetaHandler::GetFileModDate
// =================================================================================================
+// MPEG2_MetaHandler::FillAssociatedResources
+// =================================
+void MPEG2_MetaHandler::FillAssociatedResources ( std::vector<std::string> * resourceList )
+{
+ resourceList->push_back(this->parent->GetFilePath());
+ PackageFormat_Support::AddResourceIfExists(resourceList, this->sidecarPath);
+} // MPEG2_MetaHandler::FillAssociatedResources
+
+// =================================================================================================
+// MPEG2_MetaHandler::IsMetadataWritable
+// =================================
+bool MPEG2_MetaHandler::IsMetadataWritable ( )
+{
+ return Host_IO::Writable( this->sidecarPath.c_str(), true );
+} // MPEG2_MetaHandler::IsMetadataWritable
+
+// =================================================================================================
// MPEG2_MetaHandler::CacheFileData
// ================================
@@ -143,11 +159,6 @@ void MPEG2_MetaHandler::CacheFileData()
// Try to open the sidecar XMP file. Tolerate an open failure, there might not be any XMP.
// Note that MPEG2_CheckFormat can't save the sidecar path because the handler doesn't exist then.
- XMP_StringPtr filePath = this->parent->filePath.c_str();
- XMP_StringPtr extPtr = FindFileExtension ( filePath );
- this->sidecarPath.assign ( filePath, (extPtr - filePath) );
- this->sidecarPath += ".xmp";
-
if ( ! Host_IO::Exists ( this->sidecarPath.c_str() ) ) return; // OK to not have XMP.
XMPFiles_IO * localFile = XMPFiles_IO::New_XMPFiles_IO ( this->sidecarPath.c_str(), readOnly );
diff --git a/XMPFiles/source/FileHandlers/MPEG2_Handler.hpp b/XMPFiles/source/FileHandlers/MPEG2_Handler.hpp
index be6d38b..549781f 100644
--- a/XMPFiles/source/FileHandlers/MPEG2_Handler.hpp
+++ b/XMPFiles/source/FileHandlers/MPEG2_Handler.hpp
@@ -51,6 +51,8 @@ public:
~MPEG2_MetaHandler();
bool GetFileModDate ( XMP_DateTime * modDate );
+ void FillAssociatedResources ( std::vector<std::string> * resourceList );
+ bool IsMetadataWritable ( ) ;
void CacheFileData();
diff --git a/XMPFiles/source/FileHandlers/MPEG4_Handler.cpp b/XMPFiles/source/FileHandlers/MPEG4_Handler.cpp
index fad238f..2dfe088 100644
--- a/XMPFiles/source/FileHandlers/MPEG4_Handler.cpp
+++ b/XMPFiles/source/FileHandlers/MPEG4_Handler.cpp
@@ -21,6 +21,7 @@
#include "XMPFiles/source/FormatSupport/ISOBaseMedia_Support.hpp"
#include "XMPFiles/source/FormatSupport/MOOV_Support.hpp"
+#include "source/XMP_ProgressTracker.hpp"
#include "source/UnicodeConversions.hpp"
#include "third-party/zuid/interfaces/MD5.h"
@@ -40,7 +41,11 @@ using namespace std;
// The basic content of a timecode sample description table entry. Does not include trailing boxes.
+#if SUNOS_SPARC || SUNOS_X86
+#pragma pack ( 1 )
+#else
#pragma pack ( push, 1 )
+#endif //#if SUNOS_SPARC || SUNOS_X86
struct stsdBasicEntry {
XMP_Uns32 entrySize;
@@ -55,7 +60,11 @@ struct stsdBasicEntry {
XMP_Uns8 reserved_3;
};
+#if SUNOS_SPARC || SUNOS_X86
+#pragma pack ( )
+#else
#pragma pack ( pop )
+#endif //#if SUNOS_SPARC || SUNOS_X86
// =================================================================================================
@@ -226,7 +235,8 @@ 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) ) {
+ } else if ( (brand == ISOMedia::k_mp41) || (brand == ISOMedia::k_mp42) ||
+ (brand == ISOMedia::k_f4v) || ( brand == ISOMedia::k_avc1 ) ) {
haveCompatibleBrand = true; // Need to keep looking in case 'qt ' follows.
}
@@ -239,7 +249,6 @@ bool MPEG4_CheckFormat ( XMP_FileFormat format,
return true;
} else {
-
// No 'ftyp', look for classic QuickTime: 'moov', 'mdat', 'pnot', 'free', 'skip', and 'wide'.
// As an expedient, quit when a 'moov' box is found. Tolerate other boxes, they are in the
// wild for ill-formed files, e.g. seen when 'moov'/'udta' children get left at top level.
@@ -643,7 +652,7 @@ static void ExportISOCopyrights ( const SXMPMeta & xmp, MOOV_Manager * moovMgr )
MOOV_Manager::BoxInfo cprtInfo;
MOOV_Manager::BoxRef cprtRef = moovMgr->GetNthChild ( udtaRef, ordinal-1, &cprtInfo );
- if ( (cprtRef == 0) ) break; // Sanity check, should not happen.
+ if ( cprtRef == 0 ) break; // Sanity check, should not happen.
if ( (cprtInfo.boxType != ISOMedia::k_cprt) || (cprtInfo.contentSize < 6) ) continue;
if ( *cprtInfo.content != 0 ) continue; // Only accept version 0, ignore the flags.
@@ -729,7 +738,7 @@ static void ExportISOCopyrights ( const SXMPMeta & xmp, MOOV_Manager * moovMgr )
MOOV_Manager::BoxInfo cprtInfo;
MOOV_Manager::BoxRef cprtRef = moovMgr->GetNthChild ( udtaRef, isoIndex, &cprtInfo );
- if ( (cprtRef == 0) ) break; // Sanity check, should not happen.
+ if ( cprtRef == 0 ) break; // Sanity check, should not happen.
if ( (cprtInfo.boxType != ISOMedia::k_cprt) || (cprtInfo.contentSize < 6) ) continue;
if ( *cprtInfo.content != 0 ) continue; // Only accept version 0, ignore the flags.
if ( packedLang != GetUns16BE ( cprtInfo.content + 4 ) ) continue; // Look for matching language.
@@ -1056,6 +1065,36 @@ static MOOV_Manager::BoxRef FindTimecode_trak ( const MOOV_Manager & moovMgr )
} // FindTimecode_trak
// =================================================================================================
+// FindTimecode_dref
+// =================
+//
+// Look for the mdia/minf/dinf/dref box within a well-formed timecode track, return the dref box ref.
+
+static MOOV_Manager::BoxRef FindTimecode_dref ( const MOOV_Manager & moovMgr )
+{
+
+ MOOV_Manager::BoxRef trakRef = FindTimecode_trak ( moovMgr );
+ if ( trakRef == 0 ) return 0;
+
+ MOOV_Manager::BoxInfo tempInfo;
+ MOOV_Manager::BoxRef tempRef, drefRef;
+
+ tempRef = moovMgr.GetTypeChild ( trakRef, ISOMedia::k_mdia, &tempInfo );
+ if ( tempRef == 0 ) return 0;
+
+ tempRef = moovMgr.GetTypeChild ( tempRef, ISOMedia::k_minf, &tempInfo );
+ if ( tempRef == 0 ) return 0;
+
+ tempRef = moovMgr.GetTypeChild ( tempRef, ISOMedia::k_dinf, &tempInfo );
+ if ( tempRef == 0 ) return 0;
+
+ drefRef = moovMgr.GetTypeChild ( tempRef, ISOMedia::k_dref, &tempInfo );
+
+ return drefRef;
+
+} // FindTimecode_dref
+
+// =================================================================================================
// FindTimecode_stbl
// =================
//
@@ -1343,7 +1382,11 @@ static void ExportTimecodeItems ( const SXMPMeta & xmp, MPEG4_MetaHandler::Timec
// ImportCr8rItems
// ===============
+#if SUNOS_SPARC || SUNOS_X86
+#pragma pack ( 1 )
+#else
#pragma pack ( push, 1 )
+#endif //#if SUNOS_SPARC || SUNOS_X86
struct PrmLBoxContent {
XMP_Uns32 magic;
@@ -1370,7 +1413,11 @@ struct Cr8rBoxContent {
char appName[32];
};
+#if SUNOS_SPARC || SUNOS_X86
+#pragma pack ( )
+#else
#pragma pack ( pop )
+#endif //#if SUNOS_SPARC || SUNOS_X86
// -------------------------------------------------------------------------------------------------
@@ -2142,6 +2189,46 @@ void MPEG4_MetaHandler::ProcessXMP()
bool MPEG4_MetaHandler::ParseTimecodeTrack()
{
+ MOOV_Manager::BoxInfo drefInfo;
+ MOOV_Manager::BoxRef drefRef = FindTimecode_dref ( this->moovMgr );
+ bool qtTimecodeIsExternal=false;
+ if( drefRef != 0 )
+ {
+ this->moovMgr.GetBoxInfo( drefRef , &drefInfo );
+ // After dref atom in a QT file we should only
+ // proceed further to check the Data refernces
+ // if the total size of the content is greater
+ // than 8 bytes which suggests that there is atleast
+ // one data reference to check for external references.
+ if ( drefInfo.contentSize>8)
+ {
+ XMP_Uns32 noOfDrefs=GetUns32BE(drefInfo.content+4);
+ if(noOfDrefs>0)
+ {
+ const XMP_Uns8* dataReference = drefInfo.content + 8;
+ const XMP_Uns8* nextDataref = 0;
+ const XMP_Uns8* boxlimit = drefInfo.content + drefInfo.contentSize;
+ ISOMedia::BoxInfo dataRefernceInfo;
+ while(noOfDrefs--)
+ {
+ nextDataref= ISOMedia::GetBoxInfo( dataReference , boxlimit,
+ &dataRefernceInfo);
+ //The content atleast contains the flag and some data
+ if ( dataRefernceInfo.contentSize > 4 )
+ {
+ if (dataRefernceInfo.boxType==ISOMedia::k_alis &&
+ *((XMP_Uns8*)(dataReference + dataRefernceInfo.headerSize + 4)) !=1 )
+ {
+ qtTimecodeIsExternal=true;
+ break;
+ }
+ }
+ dataReference=nextDataref;
+ }
+ }
+ }
+ }
+
MOOV_Manager::BoxRef stblRef = FindTimecode_stbl ( this->moovMgr );
if ( stblRef == 0 ) return false;
@@ -2217,91 +2304,98 @@ bool MPEG4_MetaHandler::ParseTimecodeTrack()
}
// Find the timecode sample.
+ // Read the timecode only if we are sure that it is not External
+ // This way we never find stsdBox and ExportTimecodeItems and
+ // ImportTimecodeItems doesn't do anything with timeCodeSample
+ // Also because sampleOffset is/remains zero UpdateFile doesn't
+ // update the timeCodeSample value
+ if(!qtTimecodeIsExternal)
+ {
+ XMP_Uns64 sampleOffset = 0;
+ MOOV_Manager::BoxInfo tempInfo;
+ MOOV_Manager::BoxRef tempRef;
+
+ tempRef = this->moovMgr.GetTypeChild ( stblRef, ISOMedia::k_stsc, &tempInfo );
+ if ( tempRef == 0 ) return false;
+ if ( tempInfo.contentSize < (8 + sizeof ( MOOV_Manager::Content_stsc_entry )) ) return false;
+ if ( GetUns32BE ( tempInfo.content + 4 ) == 0 ) return false; // Make sure the entry count is non-zero.
+
+ XMP_Uns32 firstChunkNumber = GetUns32BE ( tempInfo.content + 8 ); // Want first field of first entry.
+
+ tempRef = this->moovMgr.GetTypeChild ( stblRef, ISOMedia::k_stco, &tempInfo );
+
+ if ( tempRef != 0 ) {
+
+ if ( tempInfo.contentSize < (8 + 4) ) return false;
+ XMP_Uns32 stcoCount = GetUns32BE ( tempInfo.content + 4 );
+ if ( stcoCount < firstChunkNumber ) return false;
+ XMP_Uns32 * stcoPtr = (XMP_Uns32*) (tempInfo.content + 8);
+ sampleOffset = GetUns32BE ( &stcoPtr[firstChunkNumber-1] ); // ! Chunk number is 1-based.
- XMP_Uns64 sampleOffset = 0;
- MOOV_Manager::BoxInfo tempInfo;
- MOOV_Manager::BoxRef tempRef;
-
- tempRef = this->moovMgr.GetTypeChild ( stblRef, ISOMedia::k_stsc, &tempInfo );
- if ( tempRef == 0 ) return false;
- if ( tempInfo.contentSize < (8 + sizeof ( MOOV_Manager::Content_stsc_entry )) ) return false;
- if ( GetUns32BE ( tempInfo.content + 4 ) == 0 ) return false; // Make sure the entry count is non-zero.
-
- XMP_Uns32 firstChunkNumber = GetUns32BE ( tempInfo.content + 8 ); // Want first field of first entry.
-
- tempRef = this->moovMgr.GetTypeChild ( stblRef, ISOMedia::k_stco, &tempInfo );
-
- if ( tempRef != 0 ) {
+ } else {
- if ( tempInfo.contentSize < (8 + 4) ) return false;
- XMP_Uns32 stcoCount = GetUns32BE ( tempInfo.content + 4 );
- if ( stcoCount < firstChunkNumber ) return false;
- XMP_Uns32 * stcoPtr = (XMP_Uns32*) (tempInfo.content + 8);
- sampleOffset = GetUns32BE ( &stcoPtr[firstChunkNumber-1] ); // ! Chunk number is 1-based.
+ tempRef = this->moovMgr.GetTypeChild ( stblRef, ISOMedia::k_co64, &tempInfo );
+ if ( (tempRef == 0) || (tempInfo.contentSize < (8 + 8)) ) return false;
+ XMP_Uns32 co64Count = GetUns32BE ( tempInfo.content + 4 );
+ if ( co64Count < firstChunkNumber ) return false;
+ XMP_Uns64 * co64Ptr = (XMP_Uns64*) (tempInfo.content + 8);
+ sampleOffset = GetUns64BE ( &co64Ptr[firstChunkNumber-1] ); // ! Chunk number is 1-based.
- } else {
+ }
- tempRef = this->moovMgr.GetTypeChild ( stblRef, ISOMedia::k_co64, &tempInfo );
- if ( (tempRef == 0) || (tempInfo.contentSize < (8 + 8)) ) return false;
- XMP_Uns32 co64Count = GetUns32BE ( tempInfo.content + 4 );
- if ( co64Count < firstChunkNumber ) return false;
- XMP_Uns64 * co64Ptr = (XMP_Uns64*) (tempInfo.content + 8);
- sampleOffset = GetUns64BE ( &co64Ptr[firstChunkNumber-1] ); // ! Chunk number is 1-based.
+ if ( sampleOffset != 0 ) { // Read the timecode sample.
- }
+ XMPFiles_IO* localFile = 0;
- if ( sampleOffset != 0 ) { // Read the timecode sample.
-
- XMPFiles_IO* localFile = 0;
+ if ( this->parent->ioRef == 0 ) { // Local read-only files get closed in CacheFileData.
+ XMP_Assert ( this->parent->UsesLocalIO() );
+ localFile = XMPFiles_IO::New_XMPFiles_IO ( this->parent->GetFilePath().c_str(), Host_IO::openReadOnly, &this->parent->errorCallback);
+ XMP_Enforce ( localFile != 0 );
+ this->parent->ioRef = localFile;
+ }
- if ( this->parent->ioRef == 0 ) { // Local read-only files get closed in CacheFileData.
- XMP_Assert ( this->parent->UsesLocalIO() );
- localFile = XMPFiles_IO::New_XMPFiles_IO ( this->parent->filePath.c_str(), Host_IO::openReadOnly );
- XMP_Enforce ( localFile != 0 );
- this->parent->ioRef = localFile;
- }
+ this->parent->ioRef->Seek ( sampleOffset, kXMP_SeekFromStart );
+ this->parent->ioRef->ReadAll ( &this->tmcdInfo.timecodeSample, 4 );
+ this->tmcdInfo.timecodeSample = MakeUns32BE ( this->tmcdInfo.timecodeSample );
+ if ( localFile != 0 ) {
+ localFile->Close();
+ delete localFile;
+ this->parent->ioRef = 0;
+ }
- this->parent->ioRef->Seek ( sampleOffset, kXMP_SeekFromStart );
- this->parent->ioRef->ReadAll ( &this->tmcdInfo.timecodeSample, 4 );
- this->tmcdInfo.timecodeSample = MakeUns32BE ( this->tmcdInfo.timecodeSample );
- if ( localFile != 0 ) {
- localFile->Close();
- delete localFile;
- this->parent->ioRef = 0;
}
-
- }
- // If this is a QT file, look for an edit list offset to add to the timecode sample. Look in the
- // timecode track for an edts/elst box. The content is a UInt8 version, UInt8[3] flags, a UInt32
- // entry count, and a sequence of UInt32 triples (trackDuration, mediaTime, mediaRate). Take
- // mediaTime from the first entry, divide it by tmcdInfo.frameDuration, add that to
- // tmcdInfo.timecodeSample.
+ // If this is a QT file, look for an edit list offset to add to the timecode sample. Look in the
+ // timecode track for an edts/elst box. The content is a UInt8 version, UInt8[3] flags, a UInt32
+ // entry count, and a sequence of UInt32 triples (trackDuration, mediaTime, mediaRate). Take
+ // mediaTime from the first entry, divide it by tmcdInfo.frameDuration, add that to
+ // tmcdInfo.timecodeSample.
- bool isQT = (this->fileMode == MOOV_Manager::kFileIsModernQT) ||
- (this->fileMode == MOOV_Manager::kFileIsTraditionalQT);
+ bool isQT = (this->fileMode == MOOV_Manager::kFileIsModernQT) ||
+ (this->fileMode == MOOV_Manager::kFileIsTraditionalQT);
- MOOV_Manager::BoxRef elstRef = 0;
- if ( isQT ) elstRef = FindTimecode_elst ( this->moovMgr );
- if ( elstRef != 0 ) {
+ MOOV_Manager::BoxRef elstRef = 0;
+ if ( isQT ) elstRef = FindTimecode_elst ( this->moovMgr );
+ if ( elstRef != 0 ) {
- MOOV_Manager::BoxInfo elstInfo;
- this->moovMgr.GetBoxInfo ( elstRef, &elstInfo );
+ MOOV_Manager::BoxInfo elstInfo;
+ this->moovMgr.GetBoxInfo ( elstRef, &elstInfo );
- if ( elstInfo.contentSize >= (4+4+12) ) {
- XMP_Uns32 elstCount = GetUns32BE ( elstInfo.content + 4 );
- if ( elstCount >= 1 ) {
- XMP_Uns32 mediaTime = GetUns32BE ( elstInfo.content + (4+4+4) );
- this->tmcdInfo.timecodeSample += (mediaTime / this->tmcdInfo.frameDuration);
+ if ( elstInfo.contentSize >= (4+4+12) ) {
+ XMP_Uns32 elstCount = GetUns32BE ( elstInfo.content + 4 );
+ if ( elstCount >= 1 ) {
+ XMP_Uns32 mediaTime = GetUns32BE ( elstInfo.content + (4+4+4) );
+ this->tmcdInfo.timecodeSample += (mediaTime / this->tmcdInfo.frameDuration);
+ }
}
- }
- }
+ }
- // Finally update this->tmcdInfo to remember (for update) that there is an OK timecode track.
+ // Finally update this->tmcdInfo to remember (for update) that there is an OK timecode track.
- this->tmcdInfo.stsdBoxFound = true;
- this->tmcdInfo.sampleOffset = sampleOffset;
+ this->tmcdInfo.stsdBoxFound = true;
+ this->tmcdInfo.sampleOffset = sampleOffset;
+ }
return true;
} // MPEG4_MetaHandler::ParseTimecodeTrack
@@ -2484,6 +2578,21 @@ void MPEG4_MetaHandler::UpdateFile ( bool doSafeUpdate )
if ( ! haveISOFile ) ExportCr8rItems ( this->xmpObj, &this->moovMgr );
+ // Set up progress tracking if necessary. At this point just include the XMP size, we don't
+ // know the 'moov' box size until later.
+
+ bool localProgressTracking = false;
+ XMP_ProgressTracker* progressTracker = this->parent->progressTracker;
+ if ( progressTracker != 0 ) {
+ float xmpSize = (float)this->xmpPacket.size();
+ if ( progressTracker->WorkInProgress() ) {
+ progressTracker->AddTotalWork ( xmpSize );
+ } else {
+ localProgressTracking = true;
+ progressTracker->BeginWork ( xmpSize );
+ }
+ }
+
// Try to update the XMP in-place if that is all that changed, or if it is in a preferred 'uuid' box.
// The XMP has already been serialized by common code to the appropriate length. Otherwise, update
// the 'moov'/'udta'/'XMP_' box in the MOOV_Manager, or the 'uuid' XMP box in the file.
@@ -2506,6 +2615,7 @@ void MPEG4_MetaHandler::UpdateFile ( bool doSafeUpdate )
if ( udtaRef != 0 ) this->moovMgr.DeleteTypeChild ( udtaRef, ISOMedia::k_XMP_ );
} else {
+
// Don't leave an old uuid XMP around (if we know about it).
if ( (! havePreferredXMP) && (this->xmpBoxSize != 0) ) {
WipeBoxFree ( fileRef, this->xmpBoxPos, this->xmpBoxSize );
@@ -2520,7 +2630,11 @@ void MPEG4_MetaHandler::UpdateFile ( bool doSafeUpdate )
if ( this->moovMgr.IsChanged() ) {
this->moovMgr.UpdateMemoryTree();
- this->UpdateTopLevelBox ( moovBoxPos, moovBoxSize, &this->moovMgr.fullSubtree[0], (XMP_Uns32)this->moovMgr.fullSubtree.size() );
+ if ( progressTracker != 0 ) {
+ progressTracker->AddTotalWork ( (float)this->moovMgr.fullSubtree.size() );
+ }
+ this->UpdateTopLevelBox ( moovBoxPos, moovBoxSize, &this->moovMgr.fullSubtree[0],
+ (XMP_Uns32)this->moovMgr.fullSubtree.size() );
}
if ( this->tmcdInfo.sampleOffset != 0 ) {
@@ -2546,6 +2660,8 @@ void MPEG4_MetaHandler::UpdateFile ( bool doSafeUpdate )
}
+ if ( localProgressTracking ) progressTracker->WorkComplete();
+
} // MPEG4_MetaHandler::UpdateFile
// =================================================================================================
@@ -2561,11 +2677,13 @@ void MPEG4_MetaHandler::WriteTempFile ( XMP_IO* tempRef )
XMP_Assert ( this->needsUpdate );
XMP_IO* originalRef = this->parent->ioRef;
+ XMP_ProgressTracker* progressTracker = this->parent->progressTracker;
- originalRef->Rewind();
tempRef->Rewind();
+ originalRef->Rewind();
+ if ( progressTracker != 0 ) progressTracker->BeginWork ( (float) originalRef->Length() );
XIO::Copy ( originalRef, tempRef, originalRef->Length(),
- this->parent->abortProc, this->parent->abortArg );
+ this->parent->abortProc, this->parent->abortArg );
try {
this->parent->ioRef = tempRef; // ! Fool UpdateFile into using the temp file.
@@ -2576,6 +2694,8 @@ void MPEG4_MetaHandler::WriteTempFile ( XMP_IO* tempRef )
throw;
}
+ if ( progressTracker != 0 ) progressTracker->WorkComplete();
+
} // MPEG4_MetaHandler::WriteTempFile
// =================================================================================================
diff --git a/XMPFiles/source/FileHandlers/MPEG4_Handler.hpp b/XMPFiles/source/FileHandlers/MPEG4_Handler.hpp
index 6474f0d..49c749f 100644
--- a/XMPFiles/source/FileHandlers/MPEG4_Handler.hpp
+++ b/XMPFiles/source/FileHandlers/MPEG4_Handler.hpp
@@ -37,7 +37,8 @@ static const XMP_OptionBits kMPEG4_HandlerFlags = ( kXMPFiles_CanInjectXMP |
kXMPFiles_CanReconcile |
kXMPFiles_AllowsOnlyXMP |
kXMPFiles_ReturnsRawPacket |
- kXMPFiles_AllowsSafeUpdate
+ kXMPFiles_AllowsSafeUpdate |
+ kXMPFiles_CanNotifyProgress
);
class MPEG4_MetaHandler : public XMPFileHandler
diff --git a/XMPFiles/source/FileHandlers/P2_Handler.cpp b/XMPFiles/source/FileHandlers/P2_Handler.cpp
index d3c9d1f..ee10945 100644
--- a/XMPFiles/source/FileHandlers/P2_Handler.cpp
+++ b/XMPFiles/source/FileHandlers/P2_Handler.cpp
@@ -15,9 +15,10 @@
#include "XMPFiles/source/XMPFiles_Impl.hpp"
#include "source/XMPFiles_IO.hpp"
#include "source/XIO.hpp"
+#include "source/IOUtils.hpp"
#include "XMPFiles/source/FileHandlers/P2_Handler.hpp"
-
+#include "XMPFiles/source/FormatSupport/PackageFormat_Support.hpp"
#include "third-party/zuid/interfaces/MD5.h"
#include <cmath>
@@ -273,7 +274,7 @@ P2_MetaHandler::P2_MetaHandler ( XMPFiles * _parent ) : expat(0), clipMetadata(0
if ( this->parent->tempPtr == 0 ) {
// The CheckFormat call might have been skipped.
- this->parent->tempPtr = CreatePseudoClipPath ( this->parent->filePath );
+ this->parent->tempPtr = CreatePseudoClipPath ( this->parent->GetFilePath() );
}
this->rootPath.assign ( (char*) this->parent->tempPtr );
@@ -909,29 +910,47 @@ void P2_MetaHandler::SetAltitudeFromLegacyXML ( XML_NodePtr legacyLocationConte
// P2_MetaHandler::ForceChildElement
// =================================
-XML_Node * P2_MetaHandler::ForceChildElement ( XML_Node * parent, XMP_StringPtr localName, int indent /* = 0 */ )
+XML_Node * P2_MetaHandler::ForceChildElement ( XML_Node * parent, XMP_StringPtr localName, XMP_Int32 indent , XMP_Bool insertAtFront )
{
- XML_Node * wsNode;
+ XML_Node * wsNodeBefore, * wsNodeAfter;
XML_Node * childNode = parent->GetNamedElement ( this->p2NS.c_str(), localName );
+ //
if ( childNode == 0 ) {
// The indenting is a hack, assuming existing 2 spaces per level.
- wsNode = new XML_Node ( parent, "", kCDataNode );
- wsNode->value = " "; // Add 2 spaces to the existing WS before the parent's close tag.
- parent->content.push_back ( wsNode );
+ wsNodeBefore = new XML_Node ( parent, "", kCDataNode );
+ wsNodeBefore->value = " "; // Add 2 spaces to the existing WS before the parent's close tag.
childNode = new XML_Node ( parent, localName, kElemNode );
childNode->ns = parent->ns;
childNode->nsPrefixLen = parent->nsPrefixLen;
childNode->name.insert ( 0, parent->name, 0, parent->nsPrefixLen );
- parent->content.push_back ( childNode );
- wsNode = new XML_Node ( parent, "", kCDataNode );
- wsNode->value = '\n';
- for ( ; indent > 1; --indent ) wsNode->value += " "; // Indent less 1, to "outdent" the parent's close.
- parent->content.push_back ( wsNode );
+ wsNodeAfter = new XML_Node ( parent, "", kCDataNode );
+ wsNodeAfter->value = '\n';
+ for ( ; indent > 1; --indent ) wsNodeAfter->value += " "; // Indent less 1, to "outdent" the parent's close.
+
+ if(insertAtFront){
+ // we are asked to insert this child as the first child of it's parent.So if P is the parent and B,C are children
+ // already present. Then if we add a new child A as it's first child then we need to first add new line character right
+ // after "<P>" and then proper indentation to bring them on the level of other children.
+ //<P>
+ // <B>
+ // <C>
+ //</P>
+ std::vector<XML_Node *> indentedNode;
+ indentedNode.push_back(wsNodeAfter);
+ indentedNode.push_back(wsNodeBefore);
+ indentedNode.push_back(childNode);
+ parent->content.insert(parent->content.begin(), indentedNode.begin(), indentedNode.end());
+ }
+ else{
+ parent->content.push_back(wsNodeBefore);
+ parent->content.push_back(childNode);
+ parent->content.push_back(wsNodeAfter);
+ }
}
@@ -1086,6 +1105,103 @@ bool P2_MetaHandler::GetFileModDate ( XMP_DateTime * modDate )
} // P2_MetaHandler::GetFileModDate
// =================================================================================================
+// P2_MetaHandler::FillMetadataFiles
+// =================================
+void P2_MetaHandler::FillMetadataFiles ( std::vector<std::string>* metadataFiles )
+{
+ std::string noExtPath, filePath;
+
+ noExtPath = rootPath + kDirChar + "CONTENTS" + kDirChar + "CLIP" + kDirChar + clipName;
+
+ filePath = noExtPath + ".XMP";
+ metadataFiles->push_back ( filePath );
+ filePath = noExtPath + ".XML";
+ metadataFiles->push_back ( filePath );
+
+} // FillMetadataFiles_P2
+
+// =================================================================================================
+// P2_MetaHandler::IsMetadataWritable
+// =======================================
+
+bool P2_MetaHandler::IsMetadataWritable ( )
+{
+ std::vector<std::string> metadataFiles;
+ FillMetadataFiles(&metadataFiles);
+ std::vector<std::string>::iterator itr = metadataFiles.begin();
+ // Check whether sidecar is writable, if not then check if it can be created.
+ bool xmpWritable = Host_IO::Writable( itr->c_str(), true );
+ // Check if legacy XML is writable.
+ bool xmlWritable = Host_IO::Writable( (++itr)->c_str(), false );
+ return (xmlWritable && xmpWritable);
+}// P2_MetaHandler::IsMetadataWritable
+
+
+// =================================================================================================
+// P2_MetaHandler::FillAssociatedResources
+// ======================================
+void P2_MetaHandler::FillAssociatedResources ( std::vector<std::string> * resourceList )
+{
+ // The possible associated resources:
+ // CONTENTS/
+ // CLIP/
+ // XXXXXX.XML XXXXXX is clip name
+ // XXXXXX.XMP
+ // VIDEO/
+ // XXXXXX.MXF
+ // AUDIO/
+ // XXXXXXNN.MXF NN is a counter which can go from 00 to 15.
+ // ICON/
+ // XXXXXX.BMP
+ // VOICE/
+ // XXXXXXNN.WAV NN is a counter which can go from 00 to 99.
+ // PROXY/
+ // XXXXXX.MP4
+ // XXXXXX.BIN
+
+ XMP_VarString contentsPath = this->rootPath + kDirChar + "CONTENTS" + kDirChar;
+ XMP_VarString path;
+
+ //Add RootPath
+ path = this->rootPath + kDirChar;
+ PackageFormat_Support::AddResourceIfExists(resourceList, path);
+
+ std::string clipPathNoExt = contentsPath + "CLIP" + kDirChar + this->clipName;
+ // Get the files present inside CLIP folder.
+ path = clipPathNoExt + ".XML";
+ PackageFormat_Support::AddResourceIfExists(resourceList, path);
+ path = clipPathNoExt + ".XMP";
+ PackageFormat_Support::AddResourceIfExists(resourceList, path);
+
+ // Get the files present inside VIDEO folder.
+ path = contentsPath + "VIDEO" + kDirChar + this->clipName + ".MXF";
+ PackageFormat_Support::AddResourceIfExists(resourceList, path);
+
+ // Get the files present inside AUDIO folder.
+ path = contentsPath + "AUDIO" + kDirChar;
+ XMP_VarString regExp;
+ regExp = "^" + this->clipName + "\\d\\d.MXF$";
+ IOUtils::GetMatchingChildren ( *resourceList, path, regExp, false, true, true );
+
+ // Get the files present inside ICON folder.
+ path = contentsPath + "ICON" + kDirChar + this->clipName + ".BMP";
+ PackageFormat_Support::AddResourceIfExists(resourceList, path);
+
+ // Get the files present inside VOICE folder.
+ path = contentsPath + "VOICE" + kDirChar;
+ regExp = "^" + clipName + "\\d\\d.WAV$";
+ IOUtils::GetMatchingChildren ( *resourceList, path, regExp, false, true, true );
+
+ // Get the files present inside PROXY folder.
+ std::string proxyPathNoExt = contentsPath + "PROXY" + kDirChar + this->clipName;
+
+ path = proxyPathNoExt + ".MP4";
+ PackageFormat_Support::AddResourceIfExists(resourceList, path);
+ path = proxyPathNoExt + ".BIN";
+ PackageFormat_Support::AddResourceIfExists(resourceList, path);
+} // P2_MetaHandler::FillAssociatedResources
+
+// =================================================================================================
// P2_MetaHandler::CacheFileData
// =============================
@@ -1094,22 +1210,23 @@ void P2_MetaHandler::CacheFileData()
XMP_Assert ( ! this->containsXMP );
if ( this->parent->UsesClientIO() ) {
- XMP_Throw ( "XDCAM cannot be used with client-managed I/O", kXMPErr_InternalFailure );
+ XMP_Throw ( "P2 cannot be used with client-managed I/O", kXMPErr_InternalFailure );
}
// Make sure the clip's .XMP file exists.
std::string xmpPath;
this->MakeClipFilePath ( &xmpPath, ".XMP" );
- if ( Host_IO::GetFileMode ( xmpPath.c_str() ) != Host_IO::kFMode_IsFile ) return; // No XMP.
+ if ( ! Host_IO::Exists ( xmpPath.c_str() ) ) return; // No XMP.
- // Read the entire .XMP file.
+ // Read the entire .XMP file. We know the XMP exists, New_XMPFiles_IO is supposed to return 0
+ // only if the file does not exist.
bool readOnly = XMP_OptionIsClear ( this->parent->openFlags, kXMPFiles_OpenForUpdate );
XMP_Assert ( this->parent->ioRef == 0 );
XMPFiles_IO* xmpFile = XMPFiles_IO::New_XMPFiles_IO ( xmpPath.c_str(), readOnly );
- if ( xmpFile == 0 ) return; // The open failed.
+ if ( xmpFile == 0 ) XMP_Throw ( "P2 XMP file open failure", kXMPErr_InternalFailure );
this->parent->ioRef = xmpFile;
XMP_Int64 xmpLen = xmpFile->Length();
@@ -1289,6 +1406,23 @@ void P2_MetaHandler::ProcessXMP()
} // P2_MetaHandler::ProcessXMP
+// This function adds a dummy attribute to the clipContent/clipMetadata (whichever is non-null)
+// with empty value and namespace as xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance
+static XML_Node* AddXSINamespace(XML_Node *clipContent, XML_Node *clipMetadata){
+
+ XML_Node *parent = clipContent ? clipContent : clipMetadata;
+ if(parent){
+ XML_Node *attrWithXSINamespace = new XML_Node ( parent, "xsi:", kCDataNode );
+ attrWithXSINamespace->value="";
+ attrWithXSINamespace->ns="http://www.w3.org/2001/XMLSchema-instance";
+ parent->attrs.push_back(attrWithXSINamespace);
+ return parent;
+ }
+
+ return NULL;
+
+}
+
// =================================================================================================
// P2_MetaHandler::UpdateFile
// ==========================
@@ -1318,7 +1452,7 @@ void P2_MetaHandler::UpdateFile ( bool doSafeUpdate )
if ( xmpFound ) {
- xmlNode = this->ForceChildElement ( this->clipContent, "ClipName", 3 );
+ xmlNode = this->ForceChildElement ( this->clipContent, "ClipName", 3, false );
if ( xmpValue != xmlNode->GetLeafContentValue() ) {
xmlNode->SetLeafContentValue ( xmpValue.c_str() );
@@ -1330,8 +1464,10 @@ void P2_MetaHandler::UpdateFile ( bool doSafeUpdate )
xmpFound = this->xmpObj.GetArrayItem ( kXMP_NS_DC, "creator", 1, &xmpValue, 0 );
if ( xmpFound ) {
- xmlNode = this->ForceChildElement ( this->clipMetadata, "Access", 3 );
- xmlNode = this->ForceChildElement ( xmlNode, "Creator", 4 );
+ xmlNode = this->ForceChildElement ( this->clipMetadata, "Access", 3, false );
+
+ // "Creator" must be first child of "Access" node else Panasonic P2 Viewer gives an error.
+ xmlNode = this->ForceChildElement ( xmlNode, "Creator", 4 , true);
if ( xmpValue != xmlNode->GetLeafContentValue() ) {
xmlNode->SetLeafContentValue ( xmpValue.c_str() );
updateLegacyXML = true;
@@ -1370,7 +1506,20 @@ void P2_MetaHandler::UpdateFile ( bool doSafeUpdate )
if ( updateLegacyXML ) {
std::string legacyXML, xmlPath;
+
+ /*bug # 3217688: xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance namespace must be defined at the
+ root node "P2Main" in legacy XML else Panasonic P2 Viewer gives an error. So we are adding a
+ dummy attribute with this namespace to clipContent/clipMetadata (whichever is non-null) before
+ serializing the XML tree. We are also undoing it below after serialization.*/
+
+ XML_Node *parentNode = AddXSINamespace(this->clipContent, this->clipMetadata);
this->expat->tree.Serialize ( &legacyXML );
+ if(parentNode){
+ // Remove the dummy attribute added to clipContent/clipMetadata.
+ delete parentNode->attrs[parentNode->attrs.size()-1];
+ parentNode->attrs.pop_back();
+ }
+
this->MakeClipFilePath ( &xmlPath, ".XML" );
bool haveXML = Host_IO::Exists ( xmlPath.c_str() );
diff --git a/XMPFiles/source/FileHandlers/P2_Handler.hpp b/XMPFiles/source/FileHandlers/P2_Handler.hpp
index 93b2672..513e9ea 100644
--- a/XMPFiles/source/FileHandlers/P2_Handler.hpp
+++ b/XMPFiles/source/FileHandlers/P2_Handler.hpp
@@ -46,7 +46,6 @@ static const XMP_OptionBits kP2_HandlerFlags = (kXMPFiles_CanInjectXMP |
kXMPFiles_ReturnsRawPacket |
kXMPFiles_HandlerOwnsFile |
kXMPFiles_AllowsSafeUpdate |
- kXMPFiles_UsesSidecarXMP |
kXMPFiles_FolderBasedFormat);
class P2_MetaHandler : public XMPFileHandler
@@ -54,6 +53,9 @@ class P2_MetaHandler : public XMPFileHandler
public:
bool GetFileModDate ( XMP_DateTime * modDate );
+ void FillMetadataFiles ( std::vector<std::string> * metadataFiles );
+ void FillAssociatedResources ( std::vector<std::string> * resourceList );
+ bool IsMetadataWritable ( );
void CacheFileData();
void ProcessXMP();
@@ -95,7 +97,7 @@ private:
void SetGPSPropertyFromLegacyXML ( XML_NodePtr legacyLocationContext, bool digestFound, XMP_StringPtr propName, XMP_StringPtr legacyPropName );
void SetAltitudeFromLegacyXML ( XML_NodePtr legacyLocationContext, bool digestFound );
- XML_Node * ForceChildElement ( XML_Node * parent, XMP_StringPtr localName, int indent = 0 );
+ XML_Node * ForceChildElement ( XML_Node * parent, XMP_StringPtr localName, XMP_Int32 indent, XMP_Bool insertAtFront );
std::string rootPath, clipName, p2NS;
diff --git a/XMPFiles/source/FileHandlers/PNG_Handler.cpp b/XMPFiles/source/FileHandlers/PNG_Handler.cpp
index 52b65ff..8c2fe8c 100644
--- a/XMPFiles/source/FileHandlers/PNG_Handler.cpp
+++ b/XMPFiles/source/FileHandlers/PNG_Handler.cpp
@@ -47,12 +47,12 @@ bool PNG_CheckFormat ( XMP_FileFormat format,
IgnoreParam(format); IgnoreParam(fileRef); IgnoreParam(parent);
XMP_Assert ( format == kXMP_PNGFile );
- IOBuffer ioBuf;
+ if ( fileRef->Length() < PNG_SIGNATURE_LEN ) return false;
+ XMP_Uns8 buffer [PNG_SIGNATURE_LEN];
fileRef->Rewind();
- if ( ! CheckFileSpace ( fileRef, &ioBuf, PNG_SIGNATURE_LEN ) ) return false; // We need at least 8, the buffer is filled anyway.
-
- if ( ! CheckBytes ( ioBuf.ptr, PNG_SIGNATURE_DATA, PNG_SIGNATURE_LEN ) ) return false;
+ fileRef->Read ( buffer, PNG_SIGNATURE_LEN );
+ if ( ! CheckBytes ( buffer, PNG_SIGNATURE_DATA, PNG_SIGNATURE_LEN ) ) return false;
return true;
diff --git a/XMPFiles/source/FileHandlers/PSD_Handler.cpp b/XMPFiles/source/FileHandlers/PSD_Handler.cpp
index d57f91c..89e1cc6 100644
--- a/XMPFiles/source/FileHandlers/PSD_Handler.cpp
+++ b/XMPFiles/source/FileHandlers/PSD_Handler.cpp
@@ -48,20 +48,19 @@ using namespace std;
bool PSD_CheckFormat ( XMP_FileFormat format,
XMP_StringPtr filePath,
- XMP_IO* fileRef,
+ XMP_IO* fileRef,
XMPFiles * parent )
{
IgnoreParam(format); IgnoreParam(filePath); IgnoreParam(parent);
XMP_Assert ( format == kXMP_PhotoshopFile );
- IOBuffer ioBuf;
-
fileRef->Rewind ( );
- if ( ! CheckFileSpace ( fileRef, &ioBuf, 34 ) ) return false; // 34 = header plus 2 lengths
+ if ( fileRef->Length() < 34 ) return false; // 34 = header plus 2 lengths
- if ( ! CheckBytes ( ioBuf.ptr, "8BPS", 4 ) ) return false;
- ioBuf.ptr += 4; // Move to the version.
- XMP_Uns16 version = GetUns16BE ( ioBuf.ptr );
+ XMP_Uns8 buffer [4];
+ fileRef->ReadAll ( buffer, 4 );
+ if ( ! CheckBytes ( buffer, "8BPS", 4 ) ) return false;
+ XMP_Uns16 version = XIO::ReadUns16_BE ( fileRef );
if ( (version != 1) && (version != 2) ) return false;
return true;
@@ -128,7 +127,7 @@ void PSD_MetaHandler::CacheFileData()
}
XMP_Uns8 psdHeader[30];
- XMP_Uns32 ioLen, cmLen, psirLen;
+ XMP_Uns32 ioLen, cmLen;
XMP_Int64 filePos = 0;
fileRef->Rewind ( );
@@ -146,10 +145,8 @@ void PSD_MetaHandler::CacheFileData()
filePos = fileRef->Seek ( psirOrigin, kXMP_SeekFromStart );
if ( filePos != psirOrigin ) return; // Throw?
- ioLen = fileRef->Read ( psdHeader, 4 );
- if ( ioLen != 4 ) return; // Throw?
-
- psirLen = GetUns32BE ( &psdHeader[0] );
+ if ( ! XIO::CheckFileSpace ( fileRef, 4 ) ) return; // Throw?
+ XMP_Uns32 psirLen = XIO::ReadUns32_BE ( fileRef );
this->psirMgr.ParseFileResources ( fileRef, psirLen );
@@ -197,6 +194,8 @@ void PSD_MetaHandler::ProcessXMP()
this->iptcMgr = new IPTC_Writer(); // ! Parse it later.
this->exifMgr = new TIFF_FileWriter();
}
+ if ( this->parent )
+ exifMgr->SetErrorCallback( &this->parent->errorCallback );
PSIR_Manager & psir = this->psirMgr; // Give the compiler help in recognizing non-aliases.
IPTC_Manager & iptc = *this->iptcMgr;
@@ -242,14 +241,8 @@ void PSD_MetaHandler::ProcessXMP()
XMP_StringLen packetLen = (XMP_StringLen)this->xmpPacket.size();
try {
this->xmpObj.ParseFromBuffer ( packetStr, packetLen );
- haveXMP = true;
- } catch ( ... ) {
- XMP_ClearOption ( options, k2XMP_FileHadXMP );
- if ( haveIPTC ) iptc.ParseMemoryDataSets ( iptcInfo.dataPtr, iptcInfo.dataLen );
- if ( iptcDigestState == kDigestMatches ) iptcDigestState = kDigestMissing;
- ImportPhotoData ( exif, iptc, psir, iptcDigestState, &this->xmpObj, options );
- throw; // ! Rethrow the exception, don't absorb it.
- }
+ } catch ( ... ) { /* Ignore parsing failures, someday we hope to get partial XMP back. */ }
+ haveXMP = true;
}
// Process the legacy metadata.
@@ -298,6 +291,7 @@ void PSD_MetaHandler::UpdateFile ( bool doSafeUpdate )
bool doInPlace = (fileHadXMP && (this->xmpPacket.size() <= (size_t)oldPacketLength));
if ( this->psirMgr.IsLegacyChanged() ) doInPlace = false;
+ XMP_ProgressTracker* progressTracker = this->parent->progressTracker;
if ( doInPlace ) {
@@ -315,10 +309,10 @@ void PSD_MetaHandler::UpdateFile ( bool doSafeUpdate )
XMP_Assert ( this->xmpPacket.size() == (size_t)oldPacketLength ); // ! Done by common PutXMP logic.
- // printf ( "PSD_MetaHandler::UpdateFile - XMP in-place packet offset %lld (0x%llX), size %d\n",
- // oldPacketOffset, oldPacketOffset, this->xmpPacket.size() );
+ if ( progressTracker != 0 ) progressTracker->BeginWork ( this->xmpPacket.size() );
liveFile->Seek ( oldPacketOffset, kXMP_SeekFromStart );
liveFile->Write ( this->xmpPacket.c_str(), (XMP_StringLen)this->xmpPacket.size() );
+ if ( progressTracker != 0 ) progressTracker->WorkComplete();
} else {
@@ -363,6 +357,7 @@ void PSD_MetaHandler::WriteTempFile ( XMP_IO* tempRef )
XMP_AbortProc abortProc = this->parent->abortProc;
void * abortArg = this->parent->abortArg;
const bool checkAbort = (abortProc != 0);
+ XMP_ProgressTracker* progressTracker = this->parent->progressTracker;
XMP_Uns64 sourceLen = origRef->Length();
if ( sourceLen == 0 ) return; // Tolerate empty files.
@@ -382,35 +377,50 @@ void PSD_MetaHandler::WriteTempFile ( XMP_IO* tempRef )
FillPacketInfo ( this->xmpPacket, &this->packetInfo );
this->psirMgr.SetImgRsrc ( kPSIR_XMP, this->xmpPacket.c_str(), (XMP_StringLen)this->xmpPacket.size() );
+
+ // Calculate the total writes I/O to be done by this method. This includes header section, color
+ // mode section and tail length after the image resources section. The write I/O for image
+ // resources section is added to total work in PSIR_FileWriter::UpdateFileResources.
+
+ origRef->Seek ( 26, kXMP_SeekFromStart ); //move to the point after Header 26 is the header length
+
+ XMP_Uns32 cmLen,cmLen1;
+ origRef->Read ( &cmLen, 4 ); // get the length of color mode section
+ cmLen1 = GetUns32BE ( &cmLen );
+ origRef->Seek ( cmLen1, kXMP_SeekFromCurrent ); //move to the end of color mode section
+
+ XMP_Uns32 irLen;
+ origRef->Read ( &irLen, 4 ); // Get the source image resource section length.
+ irLen = GetUns32BE ( &irLen );
+
+ XMP_Uns64 tailOffset = 26 + 4 + cmLen1 + 4 + irLen;
+ XMP_Uns64 tailLength = sourceLen - tailOffset;
+
+ // Add work for 26 bytes header, 4 bytes color mode section length, color mode section length
+ // and tail length after the image resources section length.
+
+ if ( progressTracker != 0 ) progressTracker->BeginWork ( (float)(26.0f + 4.0f + cmLen1 + tailLength) );
// Copy the file header and color mode section, then write the updated image resource section,
// and copy the tail of the source file (layer and mask section to EOF).
origRef->Rewind ( );
tempRef->Truncate ( 0 );
-
XIO::Copy ( origRef, tempRef, 26 ); // Copy the file header.
- XMP_Uns32 cmLen;
- origRef->Read ( &cmLen, 4 );
+ origRef->Seek ( 4, kXMP_SeekFromCurrent );
tempRef->Write ( &cmLen, 4 ); // Copy the color mode section length.
- cmLen = GetUns32BE ( &cmLen );
- XIO::Copy ( origRef, tempRef, cmLen ); // Copy the color mode section contents.
- XMP_Uns32 irLen;
- origRef->Read ( &irLen, 4 ); // Get the source image resource section length.
- irLen = GetUns32BE ( &irLen );
-
- this->psirMgr.UpdateFileResources ( origRef, tempRef, 0, abortProc, abortArg );
+ XIO::Copy ( origRef, tempRef, cmLen1 ); // Copy the color mode section contents.
- XMP_Uns64 tailOffset = 26 + 4 + cmLen + 4 + irLen;
- XMP_Uns64 tailLength = sourceLen - tailOffset;
+ this->psirMgr.UpdateFileResources ( origRef, tempRef, abortProc, abortArg ,progressTracker );
origRef->Seek ( tailOffset, kXMP_SeekFromStart );
tempRef->Seek ( 0, kXMP_SeekFromEnd );
XIO::Copy ( origRef, tempRef, tailLength ); // Copy the tail of the file.
this->needsUpdate = false;
+ if ( progressTracker != 0 ) progressTracker->WorkComplete();
} // PSD_MetaHandler::WriteTempFile
diff --git a/XMPFiles/source/FileHandlers/PSD_Handler.hpp b/XMPFiles/source/FileHandlers/PSD_Handler.hpp
index 51b8c79..4caec1b 100644
--- a/XMPFiles/source/FileHandlers/PSD_Handler.hpp
+++ b/XMPFiles/source/FileHandlers/PSD_Handler.hpp
@@ -38,7 +38,8 @@ static const XMP_OptionBits kPSD_HandlerFlags = (kXMPFiles_CanInjectXMP |
kXMPFiles_CanReconcile |
kXMPFiles_AllowsOnlyXMP |
kXMPFiles_ReturnsRawPacket |
- kXMPFiles_AllowsSafeUpdate);
+ kXMPFiles_AllowsSafeUpdate |
+ kXMPFiles_CanNotifyProgress);
class PSD_MetaHandler : public XMPFileHandler
{
diff --git a/XMPFiles/source/FileHandlers/PostScript_Handler.cpp b/XMPFiles/source/FileHandlers/PostScript_Handler.cpp
index bd5d4f7..d21ab8d 100644
--- a/XMPFiles/source/FileHandlers/PostScript_Handler.cpp
+++ b/XMPFiles/source/FileHandlers/PostScript_Handler.cpp
@@ -21,19 +21,15 @@
#include "XMPFiles/source/FormatSupport/XMPScanner.hpp"
#include "XMPFiles/source/FileHandlers/Scanner_Handler.hpp"
-using namespace std;
+#include <algorithm>
+using namespace std;
// =================================================================================================
/// \file PostScript_Handler.cpp
/// \brief File format handler for PostScript and EPS files.
///
-/// This header ...
-///
// =================================================================================================
-static const char * kPSFileTag = "%!PS-Adobe-";
-static const size_t kPSFileTagLen = strlen ( kPSFileTag );
-
// =================================================================================================
// PostScript_MetaHandlerCTor
// ==========================
@@ -58,107 +54,7 @@ bool PostScript_CheckFormat ( XMP_FileFormat format,
IgnoreParam(filePath); IgnoreParam(parent);
XMP_Assert ( (format == kXMP_EPSFile) || (format == kXMP_PostScriptFile) );
- IOBuffer ioBuf;
-
- XMP_Int64 psOffset;
- size_t psLength;
- XMP_Uns32 temp1, temp2;
-
- // Check for the binary EPSF preview header.
-
- fileRef->Rewind();
- if ( ! CheckFileSpace ( fileRef, &ioBuf, 4 ) ) return false;
- temp1 = GetUns32BE ( ioBuf.ptr );
-
- if ( temp1 == 0xC5D0D3C6 ) {
-
- if ( ! CheckFileSpace ( fileRef, &ioBuf, 30 ) ) return false;
-
- psOffset = GetUns32LE ( ioBuf.ptr+4 ); // PostScript offset.
- psLength = GetUns32LE ( ioBuf.ptr+8 ); // PostScript length.
-
- FillBuffer ( fileRef, psOffset, &ioBuf ); // Make sure buffer starts at psOffset for length check.
- if ( (ioBuf.len < kIOBufferSize) && (ioBuf.len < psLength) ) return false; // Not enough PostScript.
-
- }
-
- // Check the start of the PostScript DSC header comment.
-
- if ( ! CheckFileSpace ( fileRef, &ioBuf, (kPSFileTagLen + 3 + 1) ) ) return false;
- if ( ! CheckBytes ( ioBuf.ptr, Uns8Ptr(kPSFileTag), kPSFileTagLen ) ) return false;
- ioBuf.ptr += kPSFileTagLen;
-
- // Check the PostScript DSC major version number.
-
- temp1 = 0;
- while ( (ioBuf.ptr < ioBuf.limit) && ('0' <= *ioBuf.ptr) && (*ioBuf.ptr <= '9') ) {
- temp1 = (temp1 * 10) + (*ioBuf.ptr - '0');
- if ( temp1 > 1000 ) return false; // Overflow.
- ioBuf.ptr += 1;
- }
- if ( temp1 < 3 ) return false; // The version must be at least 3.0.
-
- if ( ! CheckFileSpace ( fileRef, &ioBuf, 3 ) ) return false;
- if ( *ioBuf.ptr != '.' ) return false; // No minor number.
- ioBuf.ptr += 1;
-
- // Check the PostScript DSC minor version number.
-
- temp2 = 0;
- while ( (ioBuf.ptr < ioBuf.limit) && ('0' <= *ioBuf.ptr) && (*ioBuf.ptr <= '9') ) {
- temp2 = (temp2 * 10) + (*ioBuf.ptr - '0');
- if ( temp2 > 1000 ) return false; // Overflow.
- ioBuf.ptr += 1;
- }
- // We don't care about the actual minor version number.
-
- if ( format == kXMP_PostScriptFile ) {
-
- // Almost done for plain PostScript, check for whitespace.
-
- if ( ! CheckFileSpace ( fileRef, &ioBuf, 1 ) ) return false;
- if ( (*ioBuf.ptr != ' ') && (*ioBuf.ptr != kLF) && (*ioBuf.ptr != kCR) ) return false;
- ioBuf.ptr += 1;
-
- } else {
-
- // Check for the EPSF keyword on the header comment.
-
- if ( ! CheckFileSpace ( fileRef, &ioBuf, 6+3+1 ) ) return false;
- if ( ! CheckBytes ( ioBuf.ptr, Uns8Ptr(" EPSF-"), 6 ) ) return false;
- ioBuf.ptr += 6;
-
- // Check the EPS major version number.
-
- temp1 = 0;
- while ( (ioBuf.ptr < ioBuf.limit) && ('0' <= *ioBuf.ptr) && (*ioBuf.ptr <= '9') ) {
- temp1 = (temp1 * 10) + (*ioBuf.ptr - '0');
- if ( temp1 > 1000 ) return false; // Overflow.
- ioBuf.ptr += 1;
- }
- if ( temp1 < 3 ) return false; // The version must be at least 3.0.
-
- if ( ! CheckFileSpace ( fileRef, &ioBuf, 3 ) ) return false;
- if ( *ioBuf.ptr != '.' ) return false; // No minor number.
- ioBuf.ptr += 1;
-
- // Check the EPS minor version number.
-
- temp2 = 0;
- while ( (ioBuf.ptr < ioBuf.limit) && ('0' <= *ioBuf.ptr) && (*ioBuf.ptr <= '9') ) {
- temp2 = (temp2 * 10) + (*ioBuf.ptr - '0');
- if ( temp2 > 1000 ) return false; // Overflow.
- ioBuf.ptr += 1;
- }
- // We don't care about the actual minor version number.
-
- if ( ! CheckFileSpace ( fileRef, &ioBuf, 1 ) ) return false;
- if ( (*ioBuf.ptr != kLF) && (*ioBuf.ptr != kCR) ) return false;
- ioBuf.ptr += 1;
-
- }
-
- return true;
+ return PostScript_Support::IsValidPSFile(fileRef,format) ;
} // PostScript_CheckFormat
@@ -166,7 +62,8 @@ bool PostScript_CheckFormat ( XMP_FileFormat format,
// PostScript_MetaHandler::PostScript_MetaHandler
// ==============================================
-PostScript_MetaHandler::PostScript_MetaHandler ( XMPFiles * _parent )
+PostScript_MetaHandler::PostScript_MetaHandler ( XMPFiles * _parent ):dscFlags(0),docInfoFlags(0)
+ ,containsXMPHint(false),fileformat(kXMP_UnknownFile)
{
this->parent = _parent;
this->handlerFlags = kPostScript_HandlerFlags;
@@ -192,12 +89,6 @@ PostScript_MetaHandler::~PostScript_MetaHandler()
// Search for "%ADO_ContainsXMP:" at the beginning of a line, it must be before "%%EndComments". If
// the XMP marker is found, look for the MainFirst/MainLast/NoMain options.
-static const char * kPSContainsXMPString = "%ADO_ContainsXMP:";
-static const size_t kPSContainsXMPLength = strlen ( kPSContainsXMPString );
-
-static const char * kPSEndCommentString = "%%EndComments"; // ! Assumed shorter than kPSContainsXMPString.
-static const size_t kPSEndCommentLength = strlen ( kPSEndCommentString );
-
int PostScript_MetaHandler::FindPostScriptHint()
{
bool found = false;
@@ -214,9 +105,9 @@ int PostScript_MetaHandler::FindPostScriptHint()
fileRef->Rewind();
if ( ! CheckFileSpace ( fileRef, &ioBuf, 4 ) ) return false;
- XMP_Uns32 temp1 = GetUns32BE ( ioBuf.ptr );
+ XMP_Uns32 fileheader = GetUns32BE ( ioBuf.ptr );
- if ( temp1 == 0xC5D0D3C6 ) {
+ if ( fileheader == 0xC5D0D3C6 ) {
if ( ! CheckFileSpace ( fileRef, &ioBuf, 30 ) ) return false;
@@ -235,14 +126,14 @@ int PostScript_MetaHandler::FindPostScriptHint()
XMP_Throw ( "PostScript_MetaHandler::FindPostScriptHint - User abort", kXMPErr_UserAbort );
}
- if ( ! CheckFileSpace ( fileRef, &ioBuf, kPSContainsXMPLength ) ) return kPSHint_NoMarker;
+ if ( ! CheckFileSpace ( fileRef, &ioBuf, kPSContainsXMPString.length() ) ) return kPSHint_NoMarker;
- if ( CheckBytes ( ioBuf.ptr, Uns8Ptr(kPSEndCommentString), kPSEndCommentLength ) ) {
+ if ( CheckBytes ( ioBuf.ptr, Uns8Ptr(kPSEndCommentString.c_str()), kPSEndCommentString.length() ) ) {
// Found "%%EndComments", don't look any further.
return kPSHint_NoMarker;
- } else if ( ! CheckBytes ( ioBuf.ptr, Uns8Ptr(kPSContainsXMPString), kPSContainsXMPLength ) ) {
+ } else if ( ! CheckBytes ( ioBuf.ptr, Uns8Ptr(kPSContainsXMPString.c_str()), kPSContainsXMPString.length() ) ) {
// Not "%%EndComments" or "%ADO_ContainsXMP:", skip past the end of this line.
do {
@@ -255,7 +146,7 @@ int PostScript_MetaHandler::FindPostScriptHint()
// Found "%ADO_ContainsXMP:", look for the main packet location option.
- ioBuf.ptr += kPSContainsXMPLength;
+ ioBuf.ptr += kPSContainsXMPString.length();
int xmpHint = kPSHint_NoMain; // ! From here on, a failure means "no main", not "no marker".
if ( ! CheckFileSpace ( fileRef, &ioBuf, 1 ) ) return kPSHint_NoMain;
if ( ! IsSpaceOrTab ( *ioBuf.ptr ) ) return kPSHint_NoMain;
@@ -357,35 +248,51 @@ bool PostScript_MetaHandler::FindFirstPacket()
bufLen = 0;
fileRef->Rewind(); // Seek back to the beginning of the file.
+ bool firstfound=false;
- while ( true ) {
+ while ( true )
+ {
- if ( checkAbort && abortProc(abortArg) ) {
+ if ( checkAbort && abortProc(abortArg) )
+ {
XMP_Throw ( "PostScript_MetaHandler::FindFirstPacket - User abort", kXMPErr_UserAbort );
}
bufPos += bufLen;
bufLen = fileRef->Read ( buffer, kBufferSize );
- if ( bufLen == 0 ) return false; // Must be at EoF, no packets found.
+ if ( bufLen == 0 ) return firstfound; // Must be at EoF, no packets found.
scanner.Scan ( buffer, bufPos, bufLen );
snipCount = scanner.GetSnipCount();
scanner.Report ( snips );
-
- for ( int i = 0; i < snipCount; ++i ) {
- if ( snips[i].fState == XMPScanner::eValidPacketSnip ) {
- if ( snips[i].fLength > 0x7FFFFFFF ) XMP_Throw ( "PostScript_MetaHandler::FindFirstPacket: Oversize packet", kXMPErr_BadXMP );
- packetInfo.offset = snips[i].fOffset;
- packetInfo.length = (XMP_Int32)snips[i].fLength;
- packetInfo.charForm = snips[i].fCharForm;
- packetInfo.writeable = (snips[i].fAccess == 'w');
- return true;
+ for ( int i = 0; i < snipCount; ++i )
+ {
+ if ( snips[i].fState == XMPScanner::eValidPacketSnip )
+ {
+ if (!firstfound)
+ {
+ if ( snips[i].fLength > 0x7FFFFFFF ) XMP_Throw ( "PostScript_MetaHandler::FindFirstPacket: Oversize packet", kXMPErr_BadXMP );
+ packetInfo.offset = snips[i].fOffset;
+ packetInfo.length = (XMP_Int32)snips[i].fLength;
+ packetInfo.charForm = snips[i].fCharForm;
+ packetInfo.writeable = (snips[i].fAccess == 'w');
+ firstPacketInfo=packetInfo;
+ lastPacketInfo=packetInfo;
+ firstfound=true;
+ }
+ else
+ {
+ lastPacketInfo.offset = snips[i].fOffset;
+ lastPacketInfo.length = (XMP_Int32)snips[i].fLength;
+ lastPacketInfo.charForm = snips[i].fCharForm;
+ lastPacketInfo.writeable = (snips[i].fAccess == 'w');
+ }
}
}
}
-
- return false;
+
+ return firstfound;
} // FindFirstPacket
@@ -398,14 +305,8 @@ bool PostScript_MetaHandler::FindFirstPacket()
// found a packet start, resume forward scanning to see if it is a valid packet. For simplicity, all
// of the snips are checked on each pass, for much the same reasons as in FindFirstPacket.
-#if 1
-
-// *** Doing this right (as described above) requires out of order scanning support which isn't
-// *** implemented yet. For now we scan the whole file and pick the last valid packet.
-
bool PostScript_MetaHandler::FindLastPacket()
{
- int pkt;
size_t bufPos, bufLen;
XMP_IO* fileRef = this->parent->ioRef;
@@ -426,8 +327,10 @@ bool PostScript_MetaHandler::FindLastPacket()
fileRef->Rewind(); // Seek back to the beginning of the file.
- for ( bufPos = 0; bufPos < (size_t)fileLen; bufPos += bufLen ) {
- if ( checkAbort && abortProc(abortArg) ) {
+ for ( bufPos = 0; bufPos < (size_t)fileLen; bufPos += bufLen )
+ {
+ if ( checkAbort && abortProc(abortArg) )
+ {
XMP_Throw ( "PostScript_MetaHandler::FindLastPacket - User abort", kXMPErr_UserAbort );
}
bufLen = fileRef->Read ( buffer, kBufferSize );
@@ -443,132 +346,1495 @@ bool PostScript_MetaHandler::FindLastPacket()
XMPScanner::SnipInfoVector snips ( snipCount );
scanner.Report ( snips );
- for ( pkt = snipCount-1; pkt >= 0; --pkt ) {
- if ( snips[pkt].fState == XMPScanner::eValidPacketSnip ) break;
+ bool lastfound=false;
+ for ( int i = 0; i < snipCount; ++i )
+ {
+ if ( snips[i].fState == XMPScanner::eValidPacketSnip )
+ {
+ if (!lastfound)
+ {
+ if ( snips[i].fLength > 0x7FFFFFFF ) XMP_Throw ( "PostScript_MetaHandler::FindLastPacket: Oversize packet", kXMPErr_BadXMP );
+ packetInfo.offset = snips[i].fOffset;
+ packetInfo.length = (XMP_Int32)snips[i].fLength;
+ packetInfo.charForm = snips[i].fCharForm;
+ packetInfo.writeable = (snips[i].fAccess == 'w');
+ firstPacketInfo=packetInfo;
+ lastPacketInfo=packetInfo;
+ lastfound=true;
+ }
+ else
+ {
+ lastPacketInfo.offset = snips[i].fOffset;
+ lastPacketInfo.length = (XMP_Int32)snips[i].fLength;
+ lastPacketInfo.charForm = snips[i].fCharForm;
+ lastPacketInfo.writeable = (snips[i].fAccess == 'w');
+ packetInfo=lastPacketInfo;
+ }
+ }
}
+ return lastfound;
- if ( pkt >= 0 ) {
- if ( snips[pkt].fLength > 0x7FFFFFFF ) XMP_Throw ( "PostScript_MetaHandler::FindLastPacket: Oversize packet", kXMPErr_BadXMP );
- packetInfo.offset = snips[pkt].fOffset;
- packetInfo.length = (XMP_Int32)snips[pkt].fLength;
- packetInfo.charForm = snips[pkt].fCharForm;
- packetInfo.writeable = (snips[pkt].fAccess == 'w');
- return true;
+} // PostScript_MetaHandler::FindLastPacket
+
+// =================================================================================================
+// PostScript_MetaHandler::setTokenInfo
+// ====================================
+//
+// Function Sets the docInfo flag for tokens(PostScript DSC comments/ Docinfo Dictionary values)
+// when parsing the file stream.Also takes note of the token offset from the start of the file
+// and the length of the token
+void PostScript_MetaHandler::setTokenInfo(TokenFlag tFlag,XMP_Int64 offset,XMP_Int64 length)
+{
+ if (!(docInfoFlags&tFlag)&&tFlag>=kPS_ADOContainsXMP && tFlag<=kPS_EndPostScript)
+ {
+ size_t index=0;
+ XMP_Uns64 flag=tFlag;
+ while(flag>>=1) index++;
+ fileTokenInfo[index].offsetStart=offset;
+ fileTokenInfo[index].tokenlen=length;
+ docInfoFlags|=tFlag;
}
+}
- return false;
+// =================================================================================================
+// PostScript_MetaHandler::getTokenInfo
+// ====================================
+//
+// Function returns the token info for the flag, which was collected in parsing the input file
+//
+PostScript_MetaHandler::TokenLocation& PostScript_MetaHandler::getTokenInfo(TokenFlag tFlag)
+{
+ if ((docInfoFlags&tFlag)&&tFlag>=kPS_ADOContainsXMP && tFlag<=kPS_EndPostScript)
+ {
+ size_t index=0;
+ XMP_Uns64 flag=tFlag;
+ while(flag>>=1) index++;
+ return fileTokenInfo[index];
+ }
+ return fileTokenInfo[kPS_NoData];
+}
-} // PostScript_MetaHandler::FindLastPacket
+// =================================================================================================
+// PostScript_MetaHandler::ExtractDSCCommentValue
+// ==============================================
+//
+// Function extracts the DSC comment value when parsing the file.This may be later used to reconcile
+//
+bool PostScript_MetaHandler::ExtractDSCCommentValue(IOBuffer &ioBuf,NativeMetadataIndex index)
+{
+
+ XMP_IO* fileRef = this->parent->ioRef;
+ if ( ! PostScript_Support::SkipTabsAndSpaces(fileRef,ioBuf) ) return false;
+ if ( !IsNewline ( *ioBuf.ptr ) )
+ {
+ do
+ {
+ if ( ! CheckFileSpace ( fileRef, &ioBuf, 1 ) ) return false;
+ nativeMeta[index] += *ioBuf.ptr;
+ ++ioBuf.ptr;
+ } while ( ! IsNewline ( *ioBuf.ptr) );
+ if (!PostScript_Support::HasCodesGT127(nativeMeta[index]))
+ {
+ dscFlags|=nativeIndextoFlag[index];
+ }
+ else
+ nativeMeta[index].clear();
+ }
+ return true;
+}
-#else
-bool PostScript_MetaHandler::FindLastPacket()
+// =================================================================================================
+// PostScript_MetaHandler::ExtractContainsXMPHint
+// ==============================================
+//
+// Function extracts the the value of "ADOContainsXMP:" DSC comment's value
+//
+bool PostScript_MetaHandler::ExtractContainsXMPHint(IOBuffer &ioBuf,XMP_Int64 containsXMPStartpos)
{
- int err, snipCount;
- bool found = false;
- XMP_Int64 backPos, backLen;
- size_t ioCount;
+ XMP_IO* fileRef = this->parent->ioRef;
+ int xmpHint = kPSHint_NoMain; // ! From here on, a failure means "no main", not "no marker".
+ //checkfor atleast one whitespace
+ if ( ! CheckFileSpace ( fileRef, &ioBuf, 1 ) ) return false;
+ if ( ! IsSpaceOrTab ( *ioBuf.ptr ) ) return false;
+ //skip extra ones
+ if ( ! PostScript_Support::SkipTabsAndSpaces(fileRef,ioBuf) ) return false;
+ if ( IsNewline ( *ioBuf.ptr ) ) return false; // Reached the end of the ContainsXMP comment.
+
+ if ( ! CheckFileSpace ( fileRef, &ioBuf, 6 ) ) return false ;
+
+ if ( CheckBytes ( ioBuf.ptr, Uns8Ptr("NoMain"), 6 ) )
+ {
+ ioBuf.ptr += 6;
+ if ( ! PostScript_Support::SkipTabsAndSpaces(fileRef,ioBuf) ) return false;
+ if ( ! IsNewline( *ioBuf.ptr) ) return false;
+ this->psHint = kPSHint_NoMain;
+ setTokenInfo(kPS_ADOContainsXMP,containsXMPStartpos,ioBuf.filePos+ioBuf.ptr-ioBuf.data-containsXMPStartpos);
+
+ }
+ else if ( CheckBytes ( ioBuf.ptr, Uns8Ptr("MainFi"), 6 ) )
+ {
+ ioBuf.ptr += 6;
+ if ( ! CheckFileSpace ( fileRef, &ioBuf, 3 ) ) return false;
+ if ( CheckBytes ( ioBuf.ptr, Uns8Ptr("rst"), 3 ) )
+ {
+ ioBuf.ptr += 3;
+ if ( ! PostScript_Support::SkipTabsAndSpaces(fileRef,ioBuf) ) return false;
+ if ( ! IsNewline( *ioBuf.ptr) ) return false;
+ this->psHint = kPSHint_MainFirst;
+ setTokenInfo(kPS_ADOContainsXMP,containsXMPStartpos,ioBuf.filePos+ioBuf.ptr-ioBuf.data-containsXMPStartpos);
+ containsXMPHint=true;
+ }
+ }
+ else if ( CheckBytes ( ioBuf.ptr, Uns8Ptr("MainLa"), 6 ) )
+ {
+ ioBuf.ptr += 6;
+ if ( ! CheckFileSpace ( fileRef, &ioBuf, 2 ) ) return false;
+ if ( CheckBytes ( ioBuf.ptr, Uns8Ptr("st"), 2 ) ) {
+ ioBuf.ptr += 2;
+ if ( ! PostScript_Support::SkipTabsAndSpaces(fileRef,ioBuf) ) return false;
+ if ( ! IsNewline( *ioBuf.ptr) ) return false;
+ this->psHint = kPSHint_MainLast;
+ setTokenInfo(kPS_ADOContainsXMP,containsXMPStartpos,ioBuf.filePos+ioBuf.ptr-ioBuf.data-containsXMPStartpos);
+ containsXMPHint=true;
+ }
+ }
+ else
+ {
+ if ( ! PostScript_Support::SkipUntilNewline(fileRef,ioBuf) ) return false;
+ }
+ return true;
+}
- XMP_IO* fileRef = this->parent->fileRef;
- XMP_Int64 fileLen = fileRef->Length();
- XMP_PacketInfo & packetInfo = this->packetInfo;
- XMPScanner scanner ( fileLen );
- XMPScanner::SnipInfoVector snips;
+// =================================================================================================
+// PostScript_MetaHandler::ExtractDocInfoDict
+// ==============================================
+//
+// Function extracts the the value of DocInfo Dictionary.The keys that are looked in the dictionary
+// are Creator, CreationDate, ModDate, Author, Title, Subject and Keywords.Other keys for the
+// Dictionary are ignored
+bool PostScript_MetaHandler::ExtractDocInfoDict(IOBuffer &ioBuf)
+{
+ XMP_Uns8 ch;
+ XMP_IO* fileRef = this->parent->ioRef;
+ XMP_Int64 endofDocInfo=(ioBuf.ptr-ioBuf.data)+ioBuf.filePos;
+ if ( ! CheckFileSpace ( fileRef, &ioBuf, 1 ) ) return false;
+ if ( IsWhitespace (*ioBuf.ptr))
+ {
+ //skip the whitespaces
+ if ( ! ( PostScript_Support::SkipTabsAndSpaces(fileRef, ioBuf))) return false;
+ //check the pdfmark
+ if ( ! CheckFileSpace ( fileRef, &ioBuf, kPSContainsPdfmarkString.length() ) ) return false;
+ if ( ! CheckBytes ( ioBuf.ptr, Uns8Ptr(kPSContainsPdfmarkString.c_str()), kPSContainsPdfmarkString.length() ) ) return false;
+ //reverse the direction to collect data
+ while(true)
+ {
+ if ( ! PostScript_Support::RevCheckFileSpace ( fileRef, &ioBuf, 1 ) ) return false;
+ ch=*ioBuf.ptr;
+ --ioBuf.ptr;
+ if (ch=='/') break;//slash of /DOCINFO
+ }
+ //skip white spaces
+ while(true)
+ {
+ if ( ! PostScript_Support::RevCheckFileSpace ( fileRef, &ioBuf, 1 ) ) return false;
+ if (!IsWhitespace(*ioBuf.ptr)) break;
+ --ioBuf.ptr;
+ }
+
+ bool findingkey=false;
+ std::string key, value;
+ while(true)
+ {
+ XMP_Uns32 noOfMarks=0;
+ if ( ! PostScript_Support::RevCheckFileSpace ( fileRef, &ioBuf, 1 ) ) return false;
+ if (*ioBuf.ptr==')')
+ {
+ --ioBuf.ptr;
+ while(true)
+ {
+ //get the string till '('
+ if (*ioBuf.ptr=='(')
+ {
+ if(findingkey)
+ {
+ reverse(key.begin(), key.end());
+ reverse(value.begin(), value.end());
+ RegisterKeyValue(key,value);
+ }
+ if ( ! PostScript_Support::RevCheckFileSpace ( fileRef, &ioBuf, 1 ) ) return false;
+ --ioBuf.ptr;
+ findingkey=!findingkey;
+ break;
+ }
+ else
+ {
+ if ( ! PostScript_Support::RevCheckFileSpace ( fileRef, &ioBuf, 1 ) ) return false;
+ if (findingkey)
+ key+=*ioBuf.ptr;
+ else
+ value+=*ioBuf.ptr;
+ --ioBuf.ptr;
+ }
+ }
+ }
+ else if(*ioBuf.ptr=='[')
+ {
+ //end of Doc Info parsing
+ //return;
+ break;
+ }
+ else
+ {
+ while(true)
+ {
+ if ( ! PostScript_Support::RevCheckFileSpace ( fileRef, &ioBuf, 1 ) ) return false;
+ if (findingkey)
+ key+=*ioBuf.ptr;
+ else
+ value+=*ioBuf.ptr;
+ --ioBuf.ptr;
+ if (*ioBuf.ptr=='/')
+ {
+ if(findingkey)
+ {
+ reverse(key.begin(), key.end());
+ reverse(value.begin(), value.end());
+ RegisterKeyValue(key,value);
+ }
+ if ( ! PostScript_Support::RevCheckFileSpace ( fileRef, &ioBuf, 1 ) ) return false;
+ --ioBuf.ptr;
+ findingkey=!findingkey;
+ break;
+ }
+ else if(IsWhitespace(*ioBuf.ptr))
+ {
+ //something not expected in Doc Info
+ break;
+ }
+ }
+ }
+ while(true)
+ {
+ if ( ! PostScript_Support::RevCheckFileSpace ( fileRef, &ioBuf, 1 ) ) return false;
+ if (!IsWhitespace(*ioBuf.ptr)) break;
+ --ioBuf.ptr;
+ }
+
+ }
+
+ fileRef->Rewind();
+ FillBuffer (fileRef, endofDocInfo, &ioBuf );
+ return true;
+ }//white space not after DOCINFO
+ return false;
+}
- enum { kBufferSize = 64*1024 };
- XMP_Uns8 buffer [kBufferSize];
+// =================================================================================================
+// PostScript_MetaHandler::ParsePSFile
+// ===================================
+//
+// Main parser for the Post script file.This is where all the DSC comments , Docinfo key value pairs
+// and other insertion related Data is looked for and stored
+void PostScript_MetaHandler::ParsePSFile()
+{
+ bool found = false;
+ IOBuffer ioBuf;
+
+ XMP_IO* fileRef = this->parent->ioRef;
XMP_AbortProc abortProc = this->parent->abortProc;
- void * abortArg = this->parent->abortArg;
- const bool checkAbort = (abortProc != 0);
+ void * abortArg = this->parent->abortArg;
+ const bool checkAbort = (abortProc != 0);
- backPos = fileLen;
- backLen = 0;
+ //Determine the file type PS or EPS
+ if ( ! PostScript_Support::IsValidPSFile(fileRef,this->fileformat) ) return ;
+ // Check for the binary EPSF preview header.
- while ( true ) {
+ fileRef->Rewind();
+ if ( ! CheckFileSpace ( fileRef, &ioBuf, 4 ) ) return ;
+ XMP_Uns32 fileheader = GetUns32BE ( ioBuf.ptr );
+ if ( fileheader == 0xC5D0D3C6 )
+ {
+
+ if ( ! CheckFileSpace ( fileRef, &ioBuf, 30 ) ) return ;
+
+ XMP_Uns32 psOffset = GetUns32LE ( ioBuf.ptr+4 ); // PostScript offset.
+ XMP_Uns32 psLength = GetUns32LE ( ioBuf.ptr+8 ); // PostScript length.
+
+ setTokenInfo(kPS_EndPostScript,psOffset+psLength,0);
+ MoveToOffset ( fileRef, psOffset, &ioBuf );
+
+ }
+
+ while ( true )
+ {
if ( checkAbort && abortProc(abortArg) ) {
- XMP_Throw ( "PostScript_MetaHandler::FindLastPacket - User abort", kXMPErr_UserAbort );
+ XMP_Throw ( "PostScript_MetaHandler::FindPostScriptHint - User abort", kXMPErr_UserAbort );
}
- backLen = kBufferSize;
- if ( backPos < kBufferSize ) backLen = backPos;
- if ( backLen == 0 ) return false; // Must be at BoF, no packets found.
+ if ( ! CheckFileSpace ( fileRef, &ioBuf, kPSContainsForString.length() ) ) return ;
- backPos -= backLen;
- fileRef->Seek ( backPos, kXMP_SeekFromStart ); // Seek back to the start of the next buffer.
+ if ( (CheckFileSpace ( fileRef, &ioBuf, kPSEndCommentString.length() )&&
+ CheckBytes ( ioBuf.ptr, Uns8Ptr(kPSEndCommentString.c_str()), kPSEndCommentString.length() )
+ )|| *ioBuf.ptr!='%' || !(*(ioBuf.ptr+1)>32 && *(ioBuf.ptr+1)<=126 )) // implicit endcomment check
+ {
+ if (CheckBytes ( ioBuf.ptr, Uns8Ptr(kPSEndCommentString.c_str()), kPSEndCommentString.length() ))
+ {
+ setTokenInfo(kPS_EndComments,ioBuf.filePos+ioBuf.ptr-ioBuf.data,kPSEndCommentString.length());
+ ioBuf.ptr+=kPSEndCommentString.length();
+ }
+ else
+ {
+ setTokenInfo(kPS_EndComments,ioBuf.filePos+ioBuf.ptr-ioBuf.data,0);
+ }
+ // Found "%%EndComments", look for docInfo Dictionary
+ // skip past the end of this line.
+ while(true)
+ {
+ if ( ! CheckFileSpace ( fileRef, &ioBuf, 1 ) ) return ;
+ if (! IsWhitespace (*ioBuf.ptr)) break;
+ ++ioBuf.ptr;
+ }
+ // search for /DOCINFO
+ while(true)
+ {
+ if ( ! CheckFileSpace ( fileRef, &ioBuf, 5 ) ) return ;
+ if (CheckBytes ( ioBuf.ptr, Uns8Ptr("/DOCI"), 5 )
+ && CheckFileSpace ( fileRef, &ioBuf, kPSContainsDocInfoString.length() )
+ &&CheckBytes ( ioBuf.ptr, Uns8Ptr(kPSContainsDocInfoString.c_str()), kPSContainsDocInfoString.length() ))
+
+ {
+
+ ioBuf.ptr+=kPSContainsDocInfoString.length();
+ ExtractDocInfoDict(ioBuf);
+ }// DOCINFO Not found in document
+ else if(CheckBytes ( ioBuf.ptr, Uns8Ptr("%%Beg"), 5 ))
+ {//possibly one of %%BeginProlog %%BeginSetup %%BeginBinary %%BeginData
+ // %%BeginDocument %%BeginPageSetup
+ XMP_Int64 begStartpos=ioBuf.filePos+ioBuf.ptr-ioBuf.data;
+ ioBuf.ptr+=5;
+ if (!CheckFileSpace ( fileRef, &ioBuf, 6 )) return;
+ if(CheckBytes ( ioBuf.ptr, Uns8Ptr("inProl"), 6 ))
+ {//%%BeginProlog
+ ioBuf.ptr+=6;
+ if (!CheckFileSpace ( fileRef, &ioBuf, 2 ))return;
+ if(CheckBytes ( ioBuf.ptr, Uns8Ptr("og"), 2 ))
+ {
+ ioBuf.ptr+=2;
+ setTokenInfo(kPS_BeginProlog,begStartpos,13);
+ }
+ }
+ else if (CheckBytes ( ioBuf.ptr, Uns8Ptr("inSetu"), 6 ))
+ {//%%BeginSetup
+ ioBuf.ptr+=6;
+ if (!CheckFileSpace ( fileRef, &ioBuf, 1 ))return;
+ if(CheckBytes ( ioBuf.ptr, Uns8Ptr("p"), 1 ))
+ {
+ ioBuf.ptr+=1;
+ setTokenInfo(kPS_BeginSetup,begStartpos,12);
+ }
+ }
+ else if (CheckBytes ( ioBuf.ptr, Uns8Ptr("inBina"), 6 ))
+ {//%%BeginBinary
+ ioBuf.ptr+=6;
+ if (!CheckFileSpace ( fileRef, &ioBuf, 3 ))return;
+ if(CheckBytes ( ioBuf.ptr, Uns8Ptr("ry"), 3 ))
+ {
+ ioBuf.ptr+=3;
+ //ignore till %%EndBinary
+ while(true)
+ {
+ if (!CheckFileSpace ( fileRef, &ioBuf, 12 ))return;
+ if (CheckBytes ( ioBuf.ptr, Uns8Ptr("%%EndBinary"), 11 ))
+ {
+ ioBuf.ptr+=11;
+ if (IsWhitespace(*ioBuf.ptr))
+ {
+ ioBuf.ptr++;
+ break;
+ }
+ }
+ ++ioBuf.ptr;
+ }
+ }
+ }
+ else if (CheckBytes ( ioBuf.ptr, Uns8Ptr("inData"), 6 ))
+ {//%%BeginData
+ ioBuf.ptr+=6;
+ if (!CheckFileSpace ( fileRef, &ioBuf, 1 ))return;
+ if(CheckBytes ( ioBuf.ptr, Uns8Ptr(":"), 1 ))
+ {
+ //ignore till %%EndData
+ while(true)
+ {
+ if (!CheckFileSpace ( fileRef, &ioBuf, 10 ))return;
+ if (CheckBytes ( ioBuf.ptr, Uns8Ptr("%%EndData"), 9 ))
+ {
+ ioBuf.ptr+=9;
+ if (IsWhitespace(*ioBuf.ptr))
+ {
+ ioBuf.ptr++;
+ break;
+ }
+ }
+ ++ioBuf.ptr;
+ }
+ }
+ }
+ else if (CheckBytes ( ioBuf.ptr, Uns8Ptr("inDocu"), 6 ))
+ {// %%BeginDocument
+ ioBuf.ptr+=6;
+ if (!CheckFileSpace ( fileRef, &ioBuf, 5 ))return;
+ if(CheckBytes ( ioBuf.ptr, Uns8Ptr("ment:"), 5 ))
+ {
+ ioBuf.ptr+=5;
+ //ignore till %%EndDocument
+ while(true)
+ {
+ if (!CheckFileSpace ( fileRef, &ioBuf, 14 ))return;
+ if (CheckBytes ( ioBuf.ptr, Uns8Ptr("%%EndDocument"), 13 ))
+ {
+ ioBuf.ptr+=13;
+ if (IsWhitespace(*ioBuf.ptr))
+ {
+ ioBuf.ptr++;
+ break;
+ }
+ }
+ ++ioBuf.ptr;
+ }
+ }
+ }
+ else if (CheckBytes ( ioBuf.ptr, Uns8Ptr("inPage"), 6 ))
+ {// %%BeginPageSetup
+ ioBuf.ptr+=6;
+ if (!CheckFileSpace ( fileRef, &ioBuf, 5 ))return;
+ if(CheckBytes ( ioBuf.ptr, Uns8Ptr("Setup"), 5 ))
+ {
+ ioBuf.ptr+=5;
+ setTokenInfo(kPS_BeginPageSetup,begStartpos,16);
+ }
+ }
+ }
+ else if(CheckBytes ( ioBuf.ptr, Uns8Ptr("%%End"), 5 ))
+ {//possibly %%EndProlog %%EndSetup %%EndPageSetup %%EndPageComments
+ XMP_Int64 begStartpos=ioBuf.filePos+ioBuf.ptr-ioBuf.data;
+ ioBuf.ptr+=5;
+ if ( ! CheckFileSpace ( fileRef, &ioBuf, 5 ) ) return ;
+ if (CheckBytes ( ioBuf.ptr, Uns8Ptr("Prolo"), 5 ))
+ {// %%EndProlog
+ ioBuf.ptr+=5;
+ if ( ! CheckFileSpace ( fileRef, &ioBuf, 1 ) ) return ;
+ if (CheckBytes ( ioBuf.ptr, Uns8Ptr("g"), 1 ))
+ {
+ ioBuf.ptr+=1;
+ setTokenInfo(kPS_EndProlog,begStartpos,11);
+ }
+ }
+ else if (CheckBytes ( ioBuf.ptr, Uns8Ptr("Setup"), 5 ))
+ {//%%EndSetup
+ ioBuf.ptr+=5;
+ setTokenInfo(kPS_EndSetup,begStartpos,10);
+ }
+ else if (CheckBytes ( ioBuf.ptr, Uns8Ptr("PageS"), 5 ))
+ {//%%EndPageSetup
+ ioBuf.ptr+=5;
+ if ( ! CheckFileSpace ( fileRef, &ioBuf, 4 ) ) return ;
+ if (CheckBytes ( ioBuf.ptr, Uns8Ptr("etup"), 4 ))
+ {
+ ioBuf.ptr+=4;
+ setTokenInfo(kPS_EndPageSetup,begStartpos,14);
+ }
+ }
+ else if (CheckBytes ( ioBuf.ptr, Uns8Ptr("PageC"), 5 ))
+ {//%%EndPageComments
+ ioBuf.ptr+=5;
+ if ( ! CheckFileSpace ( fileRef, &ioBuf, 7 ) ) return ;
+ if (CheckBytes ( ioBuf.ptr, Uns8Ptr("omments"), 7 ))
+ {
+ ioBuf.ptr+=7;
+ setTokenInfo(kPS_EndPageComments,begStartpos,17);
+ }
+ }
+ }
+ else if(CheckBytes ( ioBuf.ptr, Uns8Ptr("%%Pag"), 5 ))
+ {
+ XMP_Int64 begStartpos=ioBuf.filePos+ioBuf.ptr-ioBuf.data;
+ ioBuf.ptr+=5;
+ if ( ! CheckFileSpace ( fileRef, &ioBuf, 2 ) ) return ;
+ if (CheckBytes ( ioBuf.ptr, Uns8Ptr(":"), 2 ))
+ {
+ ioBuf.ptr+=2;
+ while(!IsNewline(*ioBuf.ptr))
+ {
+ if ( ! CheckFileSpace ( fileRef, &ioBuf, 1 ) ) return ;
+ ++ioBuf.ptr;
+ }
+ setTokenInfo(kPS_Page,begStartpos,ioBuf.filePos+ioBuf.ptr-ioBuf.data-begStartpos);
+ }
- #error "ioCount is 32 bits, backLen is 64"
- ioCount = fileRef->Read ( buffer, backLen );
- if ( ioCount != backLen ) XMP_Throw ( "PostScript_MetaHandler::FindLastPacket: Read failure", kXMPErr_ExternalFailure );
+ }
+ else if(CheckBytes ( ioBuf.ptr, Uns8Ptr("%%Tra"), 5 ))
+ {
+ XMP_Int64 begStartpos=ioBuf.filePos+ioBuf.ptr-ioBuf.data;
+ ioBuf.ptr+=5;
+ if ( ! CheckFileSpace ( fileRef, &ioBuf, 4 ) ) return ;
+ if (CheckBytes ( ioBuf.ptr, Uns8Ptr("iler"), 4 ))
+ {
+ ioBuf.ptr+=4;
+ while(!IsNewline(*ioBuf.ptr)) ++ioBuf.ptr;
+ setTokenInfo(kPS_Trailer,begStartpos,ioBuf.filePos+ioBuf.ptr-ioBuf.data-begStartpos);
+ }
+ }
+ else if(CheckBytes ( ioBuf.ptr, Uns8Ptr("%%EOF"), 5 ))
+ {
+ ioBuf.ptr+=5;
+ setTokenInfo(kPS_EOF,ioBuf.filePos+ioBuf.ptr-ioBuf.data,5);
+ }
+ if ( ! CheckFileSpace ( fileRef, &ioBuf, 1 ) ) return ;
+ ++ioBuf.ptr;
+ }
+ //dont have to search after this DOCINFO last thing
+ return;
+
+ }else if (!(kPS_Creator & dscFlags) &&
+ CheckFileSpace ( fileRef, &ioBuf, kPSContainsForString.length() )&&
+ CheckBytes ( ioBuf.ptr, Uns8Ptr(kPSContainsForString.c_str()), kPSContainsForString.length() ))
+ {
+ ioBuf.ptr+=kPSContainsForString.length();
+ if ( ! ExtractDSCCommentValue(ioBuf,kPS_dscFor) ) return ;
+ }
+ else if (!(kPS_CreatorTool & dscFlags) &&
+ CheckFileSpace ( fileRef, &ioBuf, kPSContainsCreatorString.length() )&&
+ CheckBytes ( ioBuf.ptr, Uns8Ptr(kPSContainsCreatorString.c_str()), kPSContainsCreatorString.length() ))
+ {
+ ioBuf.ptr+=kPSContainsCreatorString.length();
+ if ( ! ExtractDSCCommentValue(ioBuf,kPS_dscCreator) ) return ;
+ }
+ else if (!(kPS_CreateDate & dscFlags) &&
+ CheckFileSpace ( fileRef, &ioBuf, kPSContainsCreateDateString.length() )&&
+ CheckBytes ( ioBuf.ptr, Uns8Ptr(kPSContainsCreateDateString.c_str()), kPSContainsCreateDateString.length() ))
+ {
+
+ ioBuf.ptr+=kPSContainsCreateDateString.length();
+ if ( ! ExtractDSCCommentValue(ioBuf,kPS_dscCreateDate) ) return ;
+ }
+ else if (!(kPS_Title & dscFlags) &&
+ CheckFileSpace ( fileRef, &ioBuf, kPSContainsTitleString.length() )&&
+ CheckBytes ( ioBuf.ptr, Uns8Ptr(kPSContainsTitleString.c_str()), kPSContainsTitleString.length() ))
+ {
+
+ ioBuf.ptr+=kPSContainsTitleString.length();
+ if ( ! ExtractDSCCommentValue(ioBuf,kPS_dscTitle) ) return ;
+ }
+ else if( CheckFileSpace ( fileRef, &ioBuf, kPSContainsXMPString.length() )&&
+ ( CheckBytes ( ioBuf.ptr, Uns8Ptr(kPSContainsXMPString.c_str()), kPSContainsXMPString.length() ) )) {
- scanner.Scan ( buffer, backPos, backLen );
- snipCount = scanner.GetSnipCount();
- scanner.Report ( snips );
+ // Found "%ADO_ContainsXMP:", look for the main packet location option.
+
+ XMP_Int64 containsXMPStartpos=ioBuf.filePos+ioBuf.ptr-ioBuf.data;
+ ioBuf.ptr += kPSContainsXMPString.length();
+ ExtractContainsXMPHint(ioBuf,containsXMPStartpos);
- for ( int i = snipCount-1; i >= 0; --i ) {
+ } // Found "%ADO_ContainsXMP:".
+ //Some other DSC comments skip past the end of this line.
+ if ( ! PostScript_Support::SkipUntilNewline(fileRef,ioBuf) ) return ;
- if ( snips[i].fState == XMPScanner::eValidPacketSnip ) {
+ } // Outer marker loop.
- return VerifyMainPacket ( fileRef, snips[i].fOffset, snips[i].fLength, format, beLenient, mainInfo );
+ return ; // Should never reach here.
- } else if ( snips[i].fState == XMPScanner::ePartialPacketSnip ) {
+}
- // This part is a tad tricky. We have a partial packet, so we need to scan
- // forward from its ending to see if it is a valid packet. Snips won't recombine,
- // the partial snip will change state. Be careful with the I/O to not clobber the
- // backward scan positions, so that it can be resumed if necessary.
+// =================================================================================================
+// PostScript_MetaHandler::ReadXMPPacket
+// =====================================
+//
+// Helper method read the raw xmp into a string from a file
+void PostScript_MetaHandler::ReadXMPPacket (std::string & xmpPacket )
+{
+ if ( packetInfo.length == 0 ) XMP_Throw ( "ReadXMPPacket - No XMP packet", kXMPErr_BadXMP );
- size_t fwdPos = snips[i].fOffset + snips[i].fLength;
- fileRef->Seek ( fwdPos, kXMP_SeekFromStart ); // Seek to the end of the partial snip.
+ xmpPacket.erase();
+ xmpPacket.reserve ( packetInfo.length );
+ xmpPacket.append ( packetInfo.length, ' ' );
- while ( (fwdPos < fileLen) && (snips[i].fState == XMPScanner::ePartialPacketSnip) ) {
- ioCount = fileRef->Read ( buffer, kBufferSize );
- if ( ioCount == 0 ) XMP_Throw ( "PostScript_MetaHandler::FindLastPacket: Read failure", kXMPErr_ExternalFailure );
- scanner.Scan ( buffer, fwdPos, ioCount );
- scanner.Report ( snips );
- fwdPos += ioCount;
- }
+ XMP_StringPtr packetStr = XMP_StringPtr ( xmpPacket.c_str() ); // Don't set until after reserving the space!
- if ( snips[i].fState == XMPScanner::eValidPacketSnip ) {
- if ( snips[i].fLength > 0x7FFFFFFF ) XMP_Throw ( "PostScript_MetaHandler::FindLastPacket: Oversize packet", kXMPErr_BadXMP );
- packetInfo.offset = snips[i].fOffset;
- packetInfo.length = (XMP_Int32)snips[i].fLength;
- packetInfo.charForm = snips[i].fCharForm;
- packetInfo.writeable = (snips[i].fAccess == 'w');
- return true;
- }
+ this->parent->ioRef->Seek ( packetInfo.offset, kXMP_SeekFromStart );
+ this->parent->ioRef->ReadAll ( (char*)packetStr, packetInfo.length );
+
+} // ReadXMPPacket
+
+// =================================================================================================
+// PostScript_MetaHandler::RegisterKeyValue
+// =========================================
+//
+// Helper method registers the Key value pairs for the DocInfo dictionary and sets the Appriopriate
+// DocInfo flags
+void PostScript_MetaHandler::RegisterKeyValue(std::string& key, std::string& value)
+{
+ size_t vallen=value.length();
+ if (key.length()==0||vallen==0)
+ {
+ key.clear();
+ value.clear();
+ return;
+ }
+ for (size_t index=0;index<vallen;index++)
+ {
+ if ((unsigned char)value[index]>127)
+ {
+ key.clear();
+ value.clear();
+ return;
+ }
+ }
+ switch (key[0])
+ {
+ case 'A': // probably Author
+ {
+ if (!key.compare("Author"))
+ {
+ nativeMeta[kPS_docInfoAuthor]=value;
+ docInfoFlags|=kPS_Creator;
+ }
+ break;
+ }
+ case 'C': //probably Creator or CreationDate
+ {
+ if (!key.compare("Creator"))
+ {
+ nativeMeta[kPS_docInfoCreator]=value;
+ docInfoFlags|=kPS_CreatorTool;
+ }
+ else if (!key.compare("CreationDate"))
+ {
+ nativeMeta[kPS_docInfoCreateDate]=value;
+ docInfoFlags|=kPS_CreateDate;
+ }
+ break;
+ }
+ case 'T': // probably Title
+ {
+ if (!key.compare("Title"))
+ {
+ nativeMeta[kPS_docInfoTitle]=value;
+ docInfoFlags|=kPS_Title;
+ }
+ break;
+ }
+ case 'S':// probably Subject
+ {
+ if (!key.compare("Subject"))
+ {
+ nativeMeta[kPS_docInfoSubject]=value;
+ docInfoFlags|=kPS_Description;
+ }
+ break;
+ }
+ case 'K':// probably Keywords
+ {
+ if (!key.compare("Keywords"))
+ {
+ nativeMeta[kPS_docInfoKeywords]=value;
+ docInfoFlags|=kPS_Subject;
+ }
+ break;
+ }
+ case 'M': // probably ModDate
+ {
+ if (!key.compare("ModDate"))
+ {
+ nativeMeta[kPS_docInfoModDate]=value;
+ docInfoFlags|=kPS_ModifyDate;
+ }
+ break;
+ }
+ default: //ignore everything else
+ {
+ ;
}
+ }
+ key.clear();
+ value.clear();
+}
- } // Backwards snip loop.
- } // Backwards read loop.
+// =================================================================================================
+// PostScript_MetaHandler::ReconcileXMP
+// =========================================
+//
+// Helper method that facilitates the read time reconcilliation of native metadata
- return false; // Should never get here.
+void PostScript_MetaHandler::ReconcileXMP( const std::string &xmpStr, std::string *outStr )
+{
+ SXMPMeta xmp;
+ xmp.ParseFromBuffer( xmpStr.c_str(), xmpStr.length() );
+ // Adding creator Toll if any
+ if (!xmp.DoesPropertyExist ( kXMP_NS_XMP,"CreatorTool" ))
+ {
+ if(docInfoFlags&kPS_CreatorTool)
+ {
+ xmp.SetProperty( kXMP_NS_XMP, "CreatorTool", nativeMeta[kPS_docInfoCreator] );
+ }
+ else if (dscFlags&kPS_CreatorTool)
+ {
+ xmp.SetProperty( kXMP_NS_XMP, "CreatorTool", nativeMeta[kPS_dscCreator] );
+ }
+ }
+ if (!xmp.DoesPropertyExist ( kXMP_NS_XMP,"CreateDate" ))
+ {
+ if(docInfoFlags&kPS_CreateDate && nativeMeta[kPS_docInfoCreateDate].length()>0)
+ {
+ std::string xmpdate=PostScript_Support::ConvertToDate(nativeMeta[kPS_docInfoCreateDate].c_str());
+ if (xmpdate.length()>0)
+ {
+ xmp.SetProperty( kXMP_NS_XMP, "CreateDate", xmpdate );
+ }
+ }
+ else if (dscFlags&kPS_CreateDate&& nativeMeta[kPS_dscCreateDate].length()>0)
+ {
+ std::string xmpdate=PostScript_Support::ConvertToDate(nativeMeta[kPS_dscCreateDate].c_str());
+ xmp.SetProperty( kXMP_NS_XMP, "CreateDate", xmpdate );
+ }
+ }
+ if (!xmp.DoesPropertyExist ( kXMP_NS_XMP,"ModifyDate" ))
+ {
+ if(docInfoFlags&kPS_ModifyDate && nativeMeta[kPS_docInfoModDate].length()>0)
+ {
+ std::string xmpdate=PostScript_Support::ConvertToDate(nativeMeta[kPS_docInfoModDate].c_str());
+ if (xmpdate.length()>0)
+ {
+ xmp.SetProperty( kXMP_NS_XMP, "ModifyDate", xmpdate );
+ }
+ }
+ }
+ if (!xmp.DoesPropertyExist ( kXMP_NS_DC,"creator" ))
+ {
+ if(docInfoFlags&kPS_Creator)
+ {
+ xmp.AppendArrayItem ( kXMP_NS_DC, "creator", kXMP_PropArrayIsOrdered,
+ nativeMeta[kPS_docInfoAuthor] );
+ }
+ else if ( dscFlags&kPS_Creator)
+ {
+ xmp.AppendArrayItem ( kXMP_NS_DC, "creator", kXMP_PropArrayIsOrdered,
+ nativeMeta[kPS_dscFor] );
+ }
+ }
+ if (!xmp.DoesPropertyExist ( kXMP_NS_DC,"title" ))
+ {
+ if(docInfoFlags&kPS_Title)
+ {
+ xmp.SetLocalizedText( kXMP_NS_DC, "title",NULL,"x-default", nativeMeta[kPS_docInfoTitle] );
+ }
+ else if ( dscFlags&kPS_Title)
+ {
+ xmp.SetLocalizedText( kXMP_NS_DC, "title",NULL,"x-default", nativeMeta[kPS_dscTitle] );
+ }
+ }
+ if (!xmp.DoesPropertyExist ( kXMP_NS_DC,"description" ))
+ {
+ if(docInfoFlags&kPS_Description)
+ {
+ xmp.SetLocalizedText( kXMP_NS_DC, "description",NULL,"x-default", nativeMeta[kPS_docInfoSubject] );
+ }
+ }
+ if (!xmp.DoesPropertyExist ( kXMP_NS_DC,"subject" ))
+ {
+ if(docInfoFlags&kPS_Subject)
+ {
+ xmp.AppendArrayItem( kXMP_NS_DC, "subject", kXMP_PropArrayIsUnordered,
+ nativeMeta[kPS_docInfoKeywords], kXMP_NoOptions );
+ }
+ }
-} // PostScript_MetaHandler::FindLastPacket
+ if (packetInfo.length>0)
+ {
+ try
+ {
+ xmp.SerializeToBuffer( outStr, kXMP_ExactPacketLength|kXMP_UseCompactFormat,packetInfo.length);
+ }
+ catch(...)
+ {
+ xmp.SerializeToBuffer( outStr, kXMP_UseCompactFormat,0);
+ }
+ }
+ else
+ {
+ xmp.SerializeToBuffer( outStr, kXMP_UseCompactFormat,0);
+ }
+}
-#endif
// =================================================================================================
// PostScript_MetaHandler::CacheFileData
// =====================================
-
void PostScript_MetaHandler::CacheFileData()
{
this->containsXMP = false;
- this->psHint = FindPostScriptHint();
+ this->psHint = kPSHint_NoMarker;
+ ParsePSFile();
if ( this->psHint == kPSHint_MainFirst ) {
this->containsXMP = FindFirstPacket();
} else if ( this->psHint == kPSHint_MainLast ) {
this->containsXMP = FindLastPacket();
+ }else
+ {
+ //find first packet in case of NoMain or absence of hint
+ //When inserting new packet should be inserted in front
+ //any other existing packet
+ FindFirstPacket();
}
+ if ( this->containsXMP )
+ {
+ ReadXMPPacket ( xmpPacket );
+ }
+} // PostScript_MetaHandler::CacheFileData
+
+// =================================================================================================
+// PostScript_MetaHandler::ProcessXMP
+// ==================================
+void PostScript_MetaHandler::ProcessXMP()
+{
+
+ XMP_Assert ( ! this->processedXMP );
+ this->processedXMP = true; // Make sure we only come through here once.
- if ( this->containsXMP ) ReadXMPPacket ( this );
+ std::string xmptempStr=xmpPacket;
+
+ //Read time reconciliation with native metadata
+ try
+ {
+ ReconcileXMP(xmptempStr, &xmpPacket);
+ }
+ catch(...)
+ {
+ }
+ if ( ! this->xmpPacket.empty() )
+ {
+ XMP_StringPtr packetStr = this->xmpPacket.c_str();
+ XMP_StringLen packetLen = (XMP_StringLen)this->xmpPacket.size();
+ this->xmpObj.ParseFromBuffer ( packetStr, packetLen );
+ }
+ if (xmpPacket.length()>0)
+ {
+ this->containsXMP = true; // Assume we had something for the XMP.
+ }
+}
-} // PostScript_MetaHandler::CacheFileData
// =================================================================================================
+// PostScript_MetaHandler::modifyHeader
+// =====================================
+//
+// Method modifies the header (if one is present) for the postscript offset, tiff offset etc.
+// when an XMP update resulted in increase in the file size(non-inplace updates)
+void PostScript_MetaHandler::modifyHeader(XMP_IO* fileRef,XMP_Int64 extrabytes,XMP_Int64 offset )
+{
+ //change the header
+ IOBuffer temp;
+ fileRef->Rewind();
+ if ( ! CheckFileSpace ( fileRef, &temp, 4 ) ) return ;
+ XMP_Uns32 fileheader = GetUns32BE ( temp.ptr );
+
+ if ( fileheader == 0xC5D0D3C6 )
+ {
+ XMP_Uns8 buffLE[4];
+ if ( ! CheckFileSpace ( fileRef, &temp, 32 ) ) return ;
+ XMP_Uns32 psLength = GetUns32LE ( temp.ptr+8 ); // PostScript length.
+ if (psLength>0)
+ {
+ psLength+=extrabytes;
+ PutUns32LE ( psLength, buffLE);
+ fileRef->Seek ( 8, kXMP_SeekFromStart );
+ fileRef->Write(buffLE,4);
+ }
+ XMP_Uns32 wmfOffset = GetUns32LE ( temp.ptr+12 ); // WMF offset.
+ if (wmfOffset>0 && wmfOffset>offset)
+ {
+ wmfOffset+=extrabytes;
+ PutUns32LE ( wmfOffset, buffLE);
+ fileRef->Seek ( 12, kXMP_SeekFromStart );
+ fileRef->Write(buffLE,4);
+ }
+
+ XMP_Uns32 tiffOffset = GetUns32LE ( temp.ptr+20 ); // Tiff offset.
+ if (tiffOffset>0 && tiffOffset>offset)
+ {
+ tiffOffset+=extrabytes;
+ PutUns32LE ( tiffOffset, buffLE);
+ fileRef->Seek ( 20, kXMP_SeekFromStart );
+ fileRef->Write(buffLE,4);
+ }
+ //set the checksum to 0xFFFFFFFF
+ XMP_Uns16 checksum=0xFFFF;
+ PutUns16LE ( checksum, buffLE);
+ fileRef->Seek ( 28, kXMP_SeekFromStart );
+ fileRef->Write(buffLE,2);
+ }
+}
+
+// =================================================================================================
+// PostScript_MetaHandler::DetermineUpdateMethod
+// =============================================
+//
+// The policy followed to update a Postscript file is
+// a) if the update can fit into the existing xpacket size, go for inplace update.
+// b) If sub file decode filter is used to embed the metadata expand the metadata
+// and the move the rest contents(after the xpacket) by some calc offset.
+// c) If some other method is used to embed the xpacket readstring or readline
+// insert a new metadata xpacket before the existing xpacket.
+// The preference to use these methods is in the same order a , b and then c
+// DetermineUpdateMethod helps to decide which method be used for the given
+// input file
+//
+UpdateMethod PostScript_MetaHandler::DetermineUpdateMethod(std::string & outStr)
+{
+ SXMPMeta xmp;
+ std::string & xmpPacket = this->xmpPacket;
+ XMP_PacketInfo & packetInfo = this->packetInfo;
+ xmp.ParseFromBuffer( xmpPacket.c_str(), xmpPacket.length() );
+ if (packetInfo.length>0)
+ {
+ try
+ {
+ //First try to fit the modified XMP data into existing XMP packet length
+ //prefers Inplace
+ xmp.SerializeToBuffer( &outStr, kXMP_ExactPacketLength|kXMP_UseCompactFormat,packetInfo.length);
+ }
+ catch(...)
+ {
+ // if it doesnt fit :(
+ xmp.SerializeToBuffer( &outStr, kXMP_UseCompactFormat,0);
+ }
+ }
+ else
+ {
+ // this will be the case for Injecting new metadata
+ xmp.SerializeToBuffer( &outStr, kXMP_UseCompactFormat,0);
+ }
+ if ( this->containsXMPHint && (outStr.size() == (size_t)packetInfo.length) )
+ {
+ return kPS_Inplace;
+ }
+ else if (this->containsXMPHint && PostScript_Support::IsSFDFilterUsed(this->parent->ioRef,packetInfo.offset))
+ {
+ return kPS_ExpandSFDFilter;
+ }
+ else
+ {
+ return kPS_InjectNew;
+ }
+
+}
+
+// =================================================================================================
+// PostScript_MetaHandler::InplaceUpdate
+// =====================================
+//
+// Method does the inplace update of the metadata
+void PostScript_MetaHandler::InplaceUpdate (std::string &outStr,XMP_IO* &tempRef ,bool doSafeUpdate)
+{
+
+ XMP_IO* fileRef = this->parent->ioRef;
+ XMP_Int64 pos = 0;
+ XMP_ProgressTracker* progressTracker = this->parent->progressTracker;
+ //Inplace update of metadata
+ if (!doSafeUpdate)
+ {
+ if ( progressTracker != 0 ) progressTracker->AddTotalWork ((float) outStr.length() );
+ fileRef->Seek(packetInfo.offset,kXMP_SeekFromStart);
+ fileRef->Write((void *)outStr.c_str(), static_cast<XMP_Uns32>(outStr.length()));
+ }
+ else
+ {
+ if ( ! tempRef ) tempRef=fileRef->DeriveTemp();
+ pos=fileRef->Length();
+ if ( progressTracker != 0 ) progressTracker->AddTotalWork ((float) pos );
+ //copy data till xmp Packet
+ fileRef->Seek(0,kXMP_SeekFromStart);
+ XIO::Copy ( fileRef, tempRef, packetInfo.offset, this->parent->abortProc, this->parent->abortArg );
+
+ //insert the new XMP packet
+ fileRef->Seek(packetInfo.offset+packetInfo.length,kXMP_SeekFromStart);
+ tempRef->Write((void *)outStr.c_str(), static_cast<XMP_Uns32>(outStr.length()));
+
+ //copy the rest of data
+ XIO::Copy ( fileRef, tempRef,pos-packetInfo.offset-packetInfo.length, this->parent->abortProc, this->parent->abortArg );
+ }
+}
+
+
+// =================================================================================================
+// PostScript_MetaHandler::ExpandingSFDFilterUpdate
+// ================================================
+//
+// Method updates the metadata by expanding it
+void PostScript_MetaHandler::ExpandingSFDFilterUpdate (std::string &outStr,XMP_IO* &tempRef ,bool doSafeUpdate)
+{
+ //If SubFileDecode Filter is present expanding the
+ //existing metadata is easy
+
+ XMP_IO* fileRef = this->parent->ioRef;
+ XMP_Int64 pos = 0;
+ XMP_Int32 extrapacketlength=outStr.length()-packetInfo.length;
+ XMP_ProgressTracker* progressTracker = this->parent->progressTracker;
+ if ( progressTracker != 0 ) progressTracker->AddTotalWork ((float) (extrapacketlength + fileRef->Length() -packetInfo.offset+14) );
+ if (!doSafeUpdate)
+ {
+ size_t bufSize=extrapacketlength/(kIOBufferSize) +1*(extrapacketlength!=(kIOBufferSize));
+ std::vector<IOBuffer> tempfilebuffer1(bufSize);
+ IOBuffer temp;
+ XMP_Int64 readpoint=packetInfo.offset+packetInfo.length,writepoint=packetInfo.offset;
+ fileRef->Seek ( readpoint, kXMP_SeekFromStart );
+
+ for(size_t x=0;x<bufSize;x++)
+ {
+ tempfilebuffer1[x].len=fileRef->Read(tempfilebuffer1[x].data,kIOBufferSize,false);
+ readpoint+=tempfilebuffer1[x].len;
+ }
+
+ fileRef->Seek ( writepoint, kXMP_SeekFromStart );
+ fileRef->Write( (void *)outStr.c_str(), static_cast<XMP_Uns32>(outStr.length()));
+ writepoint+=static_cast<XMP_Uns32>(outStr.length());
+ size_t y=0;
+ bool continueread=(tempfilebuffer1[bufSize-1].len==kIOBufferSize);
+ size_t loop=bufSize;
+ while(loop)
+ {
+ if(continueread)
+ {
+ fileRef->Seek ( readpoint, kXMP_SeekFromStart );
+ temp.len=fileRef->Read(temp.data,kIOBufferSize,false);
+ readpoint+=temp.len;
+ }
+ fileRef->Seek ( writepoint, kXMP_SeekFromStart );
+ fileRef->Write(tempfilebuffer1[y].data,tempfilebuffer1[y].len);
+ writepoint+=tempfilebuffer1[y].len;
+ if (continueread)
+ tempfilebuffer1[y]=temp;
+ else
+ --loop;
+ if (temp.len<kIOBufferSize)continueread=false;
+ y=(y+1)%bufSize;
+ }
+
+ modifyHeader(fileRef,extrapacketlength,packetInfo.offset );
+ }
+ else
+ {
+ if ( progressTracker != 0 ) progressTracker->AddTotalWork ((float) (packetInfo.offset) );
+ if ( ! tempRef ) tempRef=fileRef->DeriveTemp();
+ //copy data till xmp Packet
+ fileRef->Seek(0,kXMP_SeekFromStart);
+ XIO::Copy ( fileRef, tempRef, packetInfo.offset, this->parent->abortProc, this->parent->abortArg );
+
+ //insert the new XMP packet
+ fileRef->Seek(packetInfo.offset+packetInfo.length,kXMP_SeekFromStart);
+ tempRef->Write((void *)outStr.c_str(), static_cast<XMP_Uns32>(outStr.length()));
+
+ //copy the rest of data
+ pos=fileRef->Length();
+ XIO::Copy ( fileRef, tempRef,pos-packetInfo.offset-packetInfo.length, this->parent->abortProc, this->parent->abortArg );
+ modifyHeader(tempRef,extrapacketlength,packetInfo.offset );
+ }
+}
+
+// =================================================================================================
+// PostScript_MetaHandler::DetermineInsertionOffsets
+// =============================================
+//
+// Method determines the offsets at which the new xpacket and other postscript code be inserted
+void PostScript_MetaHandler::DetermineInsertionOffsets(XMP_Int64& ADOhintOffset,XMP_Int64& InjectData1Offset,
+ XMP_Int64& InjectData3Offset)
+{
+ //find the position to place ADOContainsXMP hint
+ if(psHint!=kPSHint_MainFirst && (fileformat==kXMP_EPSFile||kXMPFiles_UnknownLength==packetInfo.offset))
+ {
+ TokenLocation& tokenLoc= getTokenInfo(kPS_ADOContainsXMP);
+ if(tokenLoc.offsetStart==-1)
+ {
+ TokenLocation& tokenLoc1= getTokenInfo(kPS_EndComments);
+ if(tokenLoc1.offsetStart==-1)
+ {
+ //should never reach here
+ throw XMP_Error(kXMPErr_BadFileFormat,"%%EndComment Missing");
+ }
+ ADOhintOffset=tokenLoc1.offsetStart;
+ }
+ else
+ {
+ ADOhintOffset= tokenLoc.offsetStart;
+ }
+ }
+ else if (psHint!=kPSHint_MainLast &&fileformat==kXMP_PostScriptFile)
+ {
+ TokenLocation& tokenLoc= getTokenInfo(kPS_ADOContainsXMP);
+ if(tokenLoc.offsetStart==-1)
+ {
+ TokenLocation& tokenLoc1= getTokenInfo(kPS_EndComments);
+ if(tokenLoc1.offsetStart==-1)
+ {
+ //should never reach here
+ throw XMP_Error(kXMPErr_BadFileFormat,"%%EndComment Missing");
+ }
+ ADOhintOffset=tokenLoc1.offsetStart;
+ }
+ else
+ {
+ ADOhintOffset= tokenLoc.offsetStart;
+ }
+ }
+ //Find the location to insert kEPS_Injectdata1
+ XMP_Uns64 xpacketLoc;
+ if ( (fileformat == kXMP_PostScriptFile) && (kXMPFiles_UnknownLength != packetInfo.offset) )
+ {
+ xpacketLoc = (XMP_Uns64)lastPacketInfo.offset;
+ TokenLocation& endPagsetuploc = getTokenInfo(kPS_EndPageSetup);
+ if ( (endPagsetuploc.offsetStart > -1) && (xpacketLoc < (XMP_Uns64)endPagsetuploc.offsetStart) )
+ {
+ InjectData1Offset=endPagsetuploc.offsetStart;
+ }
+ else
+ {
+ TokenLocation& trailerloc= getTokenInfo(kPS_Trailer);
+ if ( (trailerloc.offsetStart > -1) && (xpacketLoc < (XMP_Uns64)trailerloc.offsetStart) )
+ {
+ InjectData1Offset=trailerloc.offsetStart;
+ }
+ else
+ {
+ TokenLocation& eofloc= getTokenInfo(kPS_EOF);
+ if ( (eofloc.offsetStart > -1) && (xpacketLoc < (XMP_Uns64)eofloc.offsetStart) )
+ {
+ InjectData1Offset=eofloc.offsetStart;
+ }
+ else
+ {
+ TokenLocation& endPostScriptloc= getTokenInfo(kPS_EndPostScript);
+ if ( (endPostScriptloc.offsetStart > -1) && (xpacketLoc < (XMP_Uns64)endPostScriptloc.offsetStart) )
+ {
+ InjectData1Offset=endPostScriptloc.offsetStart;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ xpacketLoc = (XMP_Uns64)firstPacketInfo.offset;
+ TokenLocation& endPagsetuploc = getTokenInfo(kPS_EndPageSetup);
+ if ( (endPagsetuploc.offsetStart > -1) && (xpacketLoc > (XMP_Uns64)endPagsetuploc.offsetStart) )
+ {
+ InjectData1Offset=endPagsetuploc.offsetStart;
+ }
+ else
+ {
+ TokenLocation& beginPagsetuploc= getTokenInfo(kPS_BeginPageSetup);
+ if ( (beginPagsetuploc.offsetStart > -1) &&
+ (xpacketLoc > (XMP_Uns64)(beginPagsetuploc.offsetStart + beginPagsetuploc.tokenlen)) )
+ {
+ InjectData1Offset=beginPagsetuploc.offsetStart+beginPagsetuploc.tokenlen;
+ }
+ else
+ {
+ TokenLocation& endPageCommentsloc= getTokenInfo(kPS_EndPageComments);
+ if ( (endPageCommentsloc.offsetStart > -1) &&
+ (xpacketLoc > (XMP_Uns64)(endPageCommentsloc.offsetStart + endPageCommentsloc.tokenlen)) )
+ {
+ InjectData1Offset=endPageCommentsloc.offsetStart+endPageCommentsloc.tokenlen;
+ }
+ else
+ {
+ TokenLocation& pageLoc= getTokenInfo(kPS_Page);
+ if ( (pageLoc.offsetStart > -1) &&
+ (xpacketLoc > (XMP_Uns64)(pageLoc.offsetStart + pageLoc.tokenlen)) )
+ {
+ InjectData1Offset=pageLoc.offsetStart+pageLoc.tokenlen;
+ }
+ else
+ {
+ TokenLocation& endSetupLoc= getTokenInfo(kPS_EndSetup);
+ if ( (endSetupLoc.offsetStart > -1) && (xpacketLoc > (XMP_Uns64)endSetupLoc.offsetStart) )
+ {
+ InjectData1Offset=endSetupLoc.offsetStart;
+ }
+ else
+ {
+ TokenLocation& beginSetupLoc= getTokenInfo(kPS_BeginSetup);
+ if ( (beginSetupLoc.offsetStart > -1) &&
+ (xpacketLoc > (XMP_Uns64)(beginSetupLoc.offsetStart + beginSetupLoc.tokenlen)) )
+ {
+ InjectData1Offset=beginSetupLoc.offsetStart+beginSetupLoc.tokenlen;
+ }
+ else
+ {
+ TokenLocation& endPrologLoc= getTokenInfo(kPS_EndProlog);
+ if ( (endPrologLoc.offsetStart > -1) &&
+ (xpacketLoc > (XMP_Uns64)(endPrologLoc.offsetStart + endPrologLoc.tokenlen)) )
+ {
+ InjectData1Offset=endPrologLoc.offsetStart+endPrologLoc.tokenlen;
+ }
+ else
+ {
+ TokenLocation& endCommentsLoc= getTokenInfo(kPS_EndComments);
+ if ( (endCommentsLoc.offsetStart > -1) &&
+ (xpacketLoc > (XMP_Uns64)(endCommentsLoc.offsetStart + endCommentsLoc.tokenlen)) )
+ {
+ InjectData1Offset=endCommentsLoc.offsetStart+endCommentsLoc.tokenlen;
+ }
+ else
+ {
+ //should never reach here
+ throw XMP_Error(kXMPErr_BadFileFormat,"%%EndComment Missing");
+ }
+ }
+
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+
+ //Find the location to insert kEPS_Injectdata3
+ TokenLocation& trailerloc= getTokenInfo(kPS_Trailer);
+ if(trailerloc.offsetStart>-1 )
+ {
+ InjectData3Offset=trailerloc.offsetStart+trailerloc.tokenlen;
+ }
+ else
+ {
+ TokenLocation& eofloc= getTokenInfo(kPS_EOF);
+ if(eofloc.offsetStart>-1 )
+ {
+ InjectData3Offset=eofloc.offsetStart;
+ }
+ else
+ {
+ TokenLocation& endPostScriptloc= getTokenInfo(kPS_EndPostScript);
+ if(endPostScriptloc.offsetStart>-1 )
+ {
+ InjectData3Offset=endPostScriptloc.offsetStart;
+ }
+ }
+ }
+}
+
+// =================================================================================================
+// PostScript_MetaHandler::InsertNewUpdate
+// =======================================
+//
+// Method inserts a new Xpacket in the postscript file.This will be called in two cases
+// a) If there is no xpacket in the PS file
+// b) If the existing xpacket is embedded using readstring or readline method
+void PostScript_MetaHandler::InsertNewUpdate (std::string &outStr,XMP_IO* &tempRef,bool doSafeUpdate )
+{
+ // In this case it is better to have safe update
+ // as non-safe update implementation is going to be complex
+ // and more time consuming
+ // ignoring doSafeUpdate for this update method
+
+ //No SubFileDecode Filter
+ // Need to insert new Metadata before existing metadata
+ // with SubFileDecode Filter
+
+ XMP_IO* fileRef = this->parent->ioRef;
+ if ( ! tempRef ) tempRef=fileRef->DeriveTemp();
+ //inject metadata at the right place
+ XMP_Int64 ADOhintOffset=-1,InjectData1Offset=-1,InjectData3Offset=-1;
+ DetermineInsertionOffsets(ADOhintOffset,InjectData1Offset,InjectData3Offset);
+ XMP_Int64 tempInjectData1Offset=InjectData1Offset;
+ fileRef->Rewind();
+ XMP_ProgressTracker* progressTracker = this->parent->progressTracker;
+ if ( progressTracker != 0 )
+ {
+ progressTracker->AddTotalWork ((float) ( fileRef->Length() +outStr.length() + 14) );
+ if (fileformat==kXMP_EPSFile)
+ {
+ progressTracker->AddTotalWork ((float) ( kEPS_Injectdata1.length() + kEPS_Injectdata2.length() + kEPS_Injectdata3.length()) );
+ }
+ else
+ {
+ progressTracker->AddTotalWork ((float) ( kPS_Injectdata1.length() + kPS_Injectdata2.length()) );
+ }
+ }
+ XMP_Int64 totalReadLength=0;
+ //copy contents from orignal file to Temp File
+ if(ADOhintOffset!=-1)
+ {
+ XIO::Copy(fileRef,tempRef,ADOhintOffset,this->parent->abortProc,this->parent->abortArg);
+ totalReadLength+=ADOhintOffset;
+ if (fileformat==kXMP_EPSFile || kXMPFiles_UnknownLength==packetInfo.offset)
+ {
+ if ( progressTracker != 0 ) progressTracker->AddTotalWork ((float) ( kPS_XMPHintMainFirst.length()) );
+ tempRef->Write(kPS_XMPHintMainFirst.c_str(),kPS_XMPHintMainFirst.length());
+ }
+ else
+ {
+ if ( progressTracker != 0 ) progressTracker->AddTotalWork ((float) ( kPS_XMPHintMainLast.length()) );
+ tempRef->Write(kPS_XMPHintMainLast.c_str(),kPS_XMPHintMainLast.length());
+ }
+ }
+ InjectData1Offset-=totalReadLength;
+ XIO::Copy(fileRef,tempRef,InjectData1Offset,this->parent->abortProc,this->parent->abortArg);
+ totalReadLength+=InjectData1Offset;
+ if (fileformat==kXMP_EPSFile)
+ {
+ tempRef->Write(kEPS_Injectdata1.c_str(),kEPS_Injectdata1.length());
+ tempRef->Write((void *)outStr.c_str(), static_cast<XMP_Uns32>(outStr.length()));
+ tempRef->Write(kEPS_Injectdata2.c_str(),kEPS_Injectdata2.length());
+ }
+ else
+ {
+ tempRef->Write(kPS_Injectdata1.c_str(),kPS_Injectdata1.length());
+ tempRef->Write((void *)outStr.c_str(), static_cast<XMP_Uns32>(outStr.length()));
+ tempRef->Write(kPS_Injectdata2.c_str(),kPS_Injectdata2.length());
+ }
+ if (InjectData3Offset!=-1)
+ {
+ InjectData3Offset-=totalReadLength;
+ XIO::Copy(fileRef,tempRef,InjectData3Offset,this->parent->abortProc,this->parent->abortArg);
+ totalReadLength+=InjectData3Offset;
+ if (fileformat==kXMP_EPSFile)
+ {
+ tempRef->Write(kEPS_Injectdata3.c_str(),kEPS_Injectdata3.length());
+ }
+ XMP_Int64 remlength=fileRef->Length()-totalReadLength;
+ XIO::Copy(fileRef,tempRef,remlength,this->parent->abortProc,this->parent->abortArg);
+ totalReadLength+=remlength;
+ }
+ else
+ {
+ XMP_Int64 remlength=fileRef->Length()-totalReadLength;
+ XIO::Copy(fileRef,tempRef,remlength,this->parent->abortProc,this->parent->abortArg);
+ totalReadLength+=remlength;
+ if (fileformat==kXMP_EPSFile)
+ {
+ tempRef->Write(kEPS_Injectdata3.c_str(),kEPS_Injectdata3.length());
+ }
+ }
+ XMP_Int64 extraBytes;
+ if (fileformat==kXMP_EPSFile )
+ {
+ extraBytes=((ADOhintOffset!=-1)?kPS_XMPHintMainFirst.length():0)+kEPS_Injectdata3.length()+kEPS_Injectdata2.length()+
+ kEPS_Injectdata1.length()+outStr.length();
+ }
+ else
+ {
+ extraBytes=((ADOhintOffset!=-1)?(kXMPFiles_UnknownLength!=packetInfo.offset?kPS_XMPHintMainLast.length():kPS_XMPHintMainFirst.length()):0)+kPS_Injectdata2.length()+kPS_Injectdata1.length()+outStr.length();
+ }
+ modifyHeader(tempRef,extraBytes,tempInjectData1Offset );
+}
+
+// =================================================================================================
+// PostScript_MetaHandler::UpdateFile
+// ==================================
+//
+// Virtual Method implementation to update XMP metadata in a PS file
+void PostScript_MetaHandler::UpdateFile ( bool doSafeUpdate )
+{
+ IgnoreParam ( doSafeUpdate );
+ if ( ! this->needsUpdate ) return;
+
+ XMP_IO * tempRef = 0;
+ XMP_IO* fileRef = this->parent->ioRef;
+ std::string & xmpPacket = this->xmpPacket;
+ std::string outStr;
+
+ if (!fileRef )
+ {
+ XMP_Throw ( "Invalid File Refernce Cannot update XMP", kXMPErr_BadOptions );
+ }
+ bool localProgressTracking = false;
+ XMP_ProgressTracker* progressTracker = this->parent->progressTracker;
+ if ( progressTracker != 0 )
+ {
+ if ( ! progressTracker->WorkInProgress() )
+ {
+ localProgressTracking = true;
+ progressTracker->BeginWork ();
+ }
+ }
+ try
+ { switch(DetermineUpdateMethod(outStr))
+ {
+ case kPS_Inplace:
+ {
+ InplaceUpdate ( outStr, tempRef, doSafeUpdate );
+ break;
+ }
+ case kPS_ExpandSFDFilter:
+ {
+ ExpandingSFDFilterUpdate ( outStr, tempRef, doSafeUpdate );
+ break;
+ }
+ case kPS_InjectNew:
+ {
+ InsertNewUpdate ( outStr, tempRef, doSafeUpdate );
+ break;
+ }
+ case kPS_None:
+ default:
+ {
+ XMP_Throw ( "XMP Write Failed ", kXMPErr_BadOptions );
+ }
+ }
+ }
+ catch(...)
+ {
+ if( tempRef ) fileRef->DeleteTemp();
+ throw;
+ }
+ // rename the modified temp file and then delete the temp file
+ if ( tempRef ) fileRef->AbsorbTemp();
+ if ( localProgressTracking ) progressTracker->WorkComplete();
+ this->needsUpdate = false;
+
+} // PostScript_MetaHandler::UpdateFile
+
+
+// =================================================================================================
+// PostScript_MetaHandler::UpdateFile
+// ==================================
+//
+// Method to write the file with updated XMP metadata to the passed temp file reference
+void PostScript_MetaHandler::WriteTempFile ( XMP_IO* tempRef )
+{
+ XMP_IO* origRef = this->parent->ioRef;
+
+ XMP_AbortProc abortProc = this->parent->abortProc;
+ void * abortArg = this->parent->abortArg;
+
+ XMP_Int64 fileLen = origRef->Length();
+
+ XMP_ProgressTracker* progressTracker = this->parent->progressTracker;
+ if ( progressTracker != 0 ) progressTracker->BeginWork ((float) fileLen );
+ origRef->Rewind ( );
+ tempRef->Truncate ( 0 );
+ XIO::Copy ( origRef, tempRef, fileLen, abortProc, abortArg );
+
+ try
+ {
+ this->parent->ioRef = tempRef; // ! Make UpdateFile update the temp.
+ this->UpdateFile ( false );
+ this->parent->ioRef = origRef;
+ }
+ catch ( ... )
+ {
+ this->parent->ioRef = origRef;
+ throw;
+ }
+
+ if ( progressTracker != 0 ) progressTracker->WorkComplete();
+}
+// =================================================================================================
diff --git a/XMPFiles/source/FileHandlers/PostScript_Handler.hpp b/XMPFiles/source/FileHandlers/PostScript_Handler.hpp
index 8813336..7bff0fc 100644
--- a/XMPFiles/source/FileHandlers/PostScript_Handler.hpp
+++ b/XMPFiles/source/FileHandlers/PostScript_Handler.hpp
@@ -15,7 +15,9 @@
#include "public/include/XMP_Const.h"
#include "public/include/XMP_IO.hpp"
-#include "XMPFiles/source/FileHandlers/Trivial_Handler.hpp"
+#include "XMPFiles/source/FormatSupport/PostScript_Support.hpp"
+
+
// =================================================================================================
/// \file PostScript_Handler.hpp
@@ -25,8 +27,6 @@
///
// =================================================================================================
-// *** This probably could be derived from Basic_Handler, buffer the file tail in a temp file.
-
extern XMPFileHandler * PostScript_MetaHandlerCTor ( XMPFiles * parent );
extern bool PostScript_CheckFormat ( XMP_FileFormat format,
@@ -34,33 +34,112 @@ extern bool PostScript_CheckFormat ( XMP_FileFormat format,
XMP_IO * fileRef,
XMPFiles * parent );
-static const XMP_OptionBits kPostScript_HandlerFlags = kTrivial_HandlerFlags;
-
-enum {
- kPSHint_NoMarker = 0,
- kPSHint_NoMain = 1,
- kPSHint_MainFirst = 2,
- kPSHint_MainLast = 3
-};
-
-class PostScript_MetaHandler : public Trivial_MetaHandler
+static const XMP_OptionBits kPostScript_HandlerFlags = (
+ kXMPFiles_CanInjectXMP
+ |kXMPFiles_CanExpand
+ |kXMPFiles_CanRewrite
+ |kXMPFiles_PrefersInPlace
+ |kXMPFiles_CanReconcile
+ |kXMPFiles_AllowsOnlyXMP
+ |kXMPFiles_ReturnsRawPacket
+ |kXMPFiles_AllowsSafeUpdate
+ |kXMPFiles_CanNotifyProgress );
+
+class PostScript_MetaHandler : public XMPFileHandler
{
public:
PostScript_MetaHandler ( XMPFiles * parent );
~PostScript_MetaHandler();
- void CacheFileData();
+ void CacheFileData();
+ void UpdateFile ( bool doSafeUpdate );
+ void ProcessXMP ( );
+ void WriteTempFile ( XMP_IO* tempRef );
int psHint;
-
+ /* Structure used to keep
+ Track of Tokens in
+ EPS files
+ */
+ struct TokenLocation{
+ //offset from the begining of the file
+ // at which the token string starts
+ XMP_Int64 offsetStart;
+ //Total length of the token string
+ XMP_Int64 tokenlen;
+ TokenLocation():offsetStart(-1),tokenlen(0)
+ {}
+ };
protected:
-
+ //Determines the postscript hint in the DSC comments
int FindPostScriptHint();
+ // Helper methods to get the First or the Last packet from the
+ // PS file based upon the PostScript hint that is present in the PS file
bool FindFirstPacket();
bool FindLastPacket();
+
+ //Facilitates read time reconciliation of PS native metadata
+ void ReconcileXMP( const std::string &xmpStr, std::string *outStr );
+
+ //Facilitates reading of XMP packet , if one exists
+ void ReadXMPPacket ( std::string & xmpPacket);
+
+ // Parses the PS file to record th epresence and location of
+ // XMP packet and native metadata in the file
+ void ParsePSFile();
+
+ // Helper function to record the native metadata key/avlue pairs
+ // when parsing the PS file
+ void RegisterKeyValue(std::string& key, std::string& value);
+
+ // Helper Function to record the location and length of the Tokens
+ // in the opened PS file
+ void setTokenInfo(TokenFlag tFlag,XMP_Int64 offset,XMP_Int64 length);
+
+ // Getter to get the location of a token ina PS file.
+ TokenLocation& getTokenInfo(TokenFlag tFlag);
+
+ //modifies the Binary Header of a PS file as per the modifications
+ void modifyHeader(XMP_IO* fileRef,XMP_Int64 extrabytes,XMP_Int64 offset );
+
+ //Extract the values for different DSC comments
+ bool ExtractDSCCommentValue(IOBuffer &ioBuf,NativeMetadataIndex index);
+
+ //Extract value for ADO_ContainsXMP Comment
+ bool ExtractContainsXMPHint(IOBuffer &ioBuf,XMP_Int64 containsXMPStartpos);
+
+ //Extract values from DocInfo Dict
+ bool ExtractDocInfoDict(IOBuffer &ioBuf);
+
+ //Determine the update method to be used
+ UpdateMethod DetermineUpdateMethod(std::string & outStr);
+ void DetermineInsertionOffsets(XMP_Int64& ADOhintOffset,XMP_Int64& InjectData1Offset,
+ XMP_Int64& InjectData3Offset);
+ //Different update methods
+ void InplaceUpdate (std::string &outStr,XMP_IO* &tempRef, bool doSafeUpdate);
+ void ExpandingSFDFilterUpdate (std::string &outStr,XMP_IO* &tempRef, bool doSafeUpdate );
+ void InsertNewUpdate ( std::string &outStr,XMP_IO* &tempRef, bool doSafeUpdate );
+private:
+ //Flag tracks DSC comments
+ XMP_Uns32 dscFlags;
+ //Flag tracks DOCINFO keys
+ XMP_Uns32 docInfoFlags;
+ //stores the native metadata values. Index values an enum var NativeMetadataIndex
+ std::string nativeMeta[kPS_MaxNativeIndexValue];
+ //all offsets are to the end of the comment after atleast one whitespace
+ TokenLocation fileTokenInfo[25];
+ //Indicates the presence of both XMP hint and XMP
+ bool containsXMPHint;
+ //Keeps track whether a PS or EPS
+ XMP_FileFormat fileformat;
+ //keep the first packet info
+ XMP_PacketInfo firstPacketInfo;
+ //keep the last packet info
+ XMP_PacketInfo lastPacketInfo;
+
}; // PostScript_MetaHandler
// =================================================================================================
diff --git a/XMPFiles/source/FileHandlers/SWF_Handler.cpp b/XMPFiles/source/FileHandlers/SWF_Handler.cpp
index a0554ce..3266e94 100644
--- a/XMPFiles/source/FileHandlers/SWF_Handler.cpp
+++ b/XMPFiles/source/FileHandlers/SWF_Handler.cpp
@@ -53,7 +53,7 @@ bool SWF_CheckFormat ( XMP_FileFormat format,
// Make sure the file is long enough for an empty SWF stream. Check the signature.
- if ( fileRef->Length() < SWF_IO::HeaderPrefixSize ) return false;
+ if ( fileRef->Length() < (XMP_Int64)SWF_IO::HeaderPrefixSize ) return false;
fileRef->Rewind();
XMP_Uns8 buffer [4];
@@ -306,12 +306,19 @@ void SWF_MetaHandler::UpdateFile ( bool doSafeUpdate )
this->hasMetadata = true;
- // Rewrite the file.
+ // Update the uncompressed file length and rewrite the file.
+ PutUns32LE ( this->expandedSWF.size(), &this->expandedSWF[4] );
+
XMP_IO * fileRef = this->parent->ioRef;
fileRef->Rewind();
fileRef->Truncate ( 0 );
- fileRef->Write ( &this->expandedSWF[0], this->expandedSWF.size() );
+
+ if ( this->isCompressed ) {
+ SWF_IO::CompressMemoryToFile ( this->expandedSWF, fileRef );
+ } else {
+ fileRef->Write ( &this->expandedSWF[0], this->expandedSWF.size() );
+ }
} // SWF_MetaHandler::UpdateFile
diff --git a/XMPFiles/source/FileHandlers/SonyHDV_Handler.cpp b/XMPFiles/source/FileHandlers/SonyHDV_Handler.cpp
index 863eab7..9d9a9d4 100644
--- a/XMPFiles/source/FileHandlers/SonyHDV_Handler.cpp
+++ b/XMPFiles/source/FileHandlers/SonyHDV_Handler.cpp
@@ -15,9 +15,10 @@
#include "XMPFiles/source/XMPFiles_Impl.hpp"
#include "source/XMPFiles_IO.hpp"
#include "source/XIO.hpp"
+#include "source/IOUtils.hpp"
#include "XMPFiles/source/FileHandlers/SonyHDV_Handler.hpp"
-
+#include "XMPFiles/source/FormatSupport/PackageFormat_Support.hpp"
#include "third-party/zuid/interfaces/MD5.h"
#if XMP_WinBuild
@@ -535,7 +536,7 @@ SonyHDV_MetaHandler::SonyHDV_MetaHandler ( XMPFiles * _parent )
if ( this->parent->tempPtr == 0 ) {
// The CheckFormat call might have been skipped.
- this->parent->tempPtr = CreatePseudoClipPath ( this->parent->filePath );
+ this->parent->tempPtr = CreatePseudoClipPath ( this->parent->GetFilePath() );
}
this->rootPath.assign ( (char*) this->parent->tempPtr );
@@ -581,6 +582,24 @@ bool SonyHDV_MetaHandler::MakeClipFilePath ( std::string * path, XMP_StringPtr s
} // SonyHDV_MetaHandler::MakeClipFilePath
+// This method removes the timestamp information from a clip name. It returns the clip name with a following "_".
+// For example: The clip name "00_0001_2007-08-06_165555" becomes "00_0001_".
+static void RemoveTimeStampFromClipName(std::string &clipName)
+{
+ int usCount = 0;
+ size_t i, limit = clipName.size();
+
+ for ( i = 0; i < limit; ++i ) {
+ if ( clipName[i] == '_' ) {
+ ++usCount;
+ if ( usCount == 2 ) break;
+ }
+ }
+
+ if ( i < limit ) clipName.erase ( i );
+ clipName += '_'; // Make sure a final '_' is there for the search comparisons.
+}
+
// =================================================================================================
// SonyHDV_MetaHandler::MakeIndexFilePath
// ======================================
@@ -608,18 +627,7 @@ bool SonyHDV_MetaHandler::MakeIndexFilePath ( std::string& idxPath, const std::s
// Can be isolated to a separate function.
std::string clipName = leafName;
- int usCount = 0;
- size_t i, limit = leafName.size();
-
- for ( i = 0; i < limit; ++i ) {
- if ( clipName[i] == '_' ) {
- ++usCount;
- if ( usCount == 2 ) break;
- }
- }
-
- if ( i < limit ) clipName.erase ( i );
- clipName += '_'; // Make sure a final '_' is there for the search comparisons.
+ RemoveTimeStampFromClipName(clipName);
Host_IO::AutoFolder aFolder;
std::string childName;
@@ -721,6 +729,101 @@ bool SonyHDV_MetaHandler::GetFileModDate ( XMP_DateTime * modDate )
} // SonyHDV_MetaHandler::GetFileModDate
// =================================================================================================
+// SonyHDV_MetaHandler::FillMetadataFiles
+// ================================
+void SonyHDV_MetaHandler::FillMetadataFiles ( std::vector<std::string>* metadataFiles )
+{
+ std::string noExtPath, filePath;
+
+ noExtPath = rootPath + kDirChar + "VIDEO" + kDirChar + "HVR" + kDirChar + clipName;
+
+ filePath = noExtPath + ".XMP";
+ metadataFiles->push_back ( filePath );
+ filePath = noExtPath + ".IDX";
+ metadataFiles->push_back ( filePath );
+
+} // FillMetadataFiles_SonyHDV
+
+// =================================================================================================
+// SonyHDV_MetaHandler::IsMetadataWritable
+// =======================================
+
+bool SonyHDV_MetaHandler::IsMetadataWritable ( )
+{
+ std::vector<std::string> metadataFiles;
+ FillMetadataFiles(&metadataFiles);
+ std::vector<std::string>::iterator itr = metadataFiles.begin();
+ // Check whether sidecar is writable, if not then check if it can be created.
+ return Host_IO::Writable( itr->c_str(), true );
+}// SonyHDV_MetaHandler::IsMetadataWritable
+
+
+// =================================================================================================
+// SonyHDV_MetaHandler::FillAssociatedResources
+// ======================================
+//
+// This method returns all clip associated "media files","index files" whose name
+// starts with XX_CCCC_ and side cars starting with XX_CCCC.
+void SonyHDV_MetaHandler::FillAssociatedResources ( std::vector<std::string> * resourceList )
+{
+ // The possible associated resources:
+ // VIDEO/
+ // HVR/
+ // XX_CCCC_YYYY-MM-DD_hhmmss.M2T // HDV media
+ // XX_CCCC_YYYY-MM-DD_hhmmss.IDX // Metadata Index file
+ // XX_CCCC_YYYY-MM-DD_hhmmss.XMP // sidecar
+ //
+ // XX_CCCC_YYYY-MM-DD_hhmmss.AVI // DV(AVI) medi
+ // XX_CCCC_YYYY-MM-DD_hhmmss.IDX // Metadata Index file
+ // XX_CCCC_YYYY-MM-DD_hhmmss.XMP // sidecar
+ //
+ // XX_CCCC_YYYY-MM-DD_hhmmss.DV // DV(RAW) media
+ // XX_CCCC_YYYY-MM-DD_hhmmss.IDX // Metadata Index file
+ // XX_CCCC_YYYY-MM-DD_hhmmss.XMP // sidecar
+ //
+ // tracks.dat // Clip database file
+
+ std:: string hvrPath = this->rootPath + kDirChar + "VIDEO" + kDirChar + "HVR";
+ std::string filePath;
+
+ //Add RootPath
+ filePath = this->rootPath + kDirChar;
+ PackageFormat_Support::AddResourceIfExists( resourceList, filePath );
+
+ // If XX_CCCC_YYYY-MM-DD_hhmmss is clip name then we remove YYYY-MM-DD_hhmmss from this and return
+ // all files starting with XX_CCCC_ and having required extension.
+ std::string clipNameWithoutTimeStamp = this->clipName;
+ RemoveTimeStampFromClipName(clipNameWithoutTimeStamp);
+
+ // Add media files.
+ // We don't know the extension of the media so we will check for all
+ // three possible extensions and add whichever is existing.
+
+ // "AddResourceIfExists" will add all spanned clips that match the clip prefix "clipNameWithoutTimeStamp"
+ // and specified extensions.
+ PackageFormat_Support::AddResourceIfExists(resourceList, hvrPath, clipNameWithoutTimeStamp.c_str(), ".M2T");
+
+ PackageFormat_Support::AddResourceIfExists(resourceList, hvrPath, clipNameWithoutTimeStamp.c_str(), ".AVI");
+
+ PackageFormat_Support::AddResourceIfExists(resourceList, hvrPath, clipNameWithoutTimeStamp.c_str(), ".DV");
+
+ // Add Index files.
+ PackageFormat_Support::AddResourceIfExists(resourceList, hvrPath, clipNameWithoutTimeStamp.c_str(), ".IDX");
+
+ // Add sidecars.
+ // For sidecars we will look for XX_CCCC*.XMP instead of XX_CCCC_*.XMP because we may generate such files
+ // in case of spanning (in future) or logical paths.
+ clipNameWithoutTimeStamp.erase(clipNameWithoutTimeStamp.end()-1);
+ PackageFormat_Support::AddResourceIfExists(resourceList, hvrPath, clipNameWithoutTimeStamp.c_str(), ".XMP");
+
+ //Add clip database file
+ filePath = hvrPath + kDirChar + "tracks.dat";
+ PackageFormat_Support::AddResourceIfExists(resourceList, filePath);
+
+} // SonyHDV_MetaHandler::FillAssociatedResources
+
+
+// =================================================================================================
// SonyHDV_MetaHandler::CacheFileData
// ==================================
@@ -729,22 +832,23 @@ void SonyHDV_MetaHandler::CacheFileData()
XMP_Assert ( ! this->containsXMP );
if ( this->parent->UsesClientIO() ) {
- XMP_Throw ( "XDCAM cannot be used with client-managed I/O", kXMPErr_InternalFailure );
+ XMP_Throw ( "SonyHDV cannot be used with client-managed I/O", kXMPErr_InternalFailure );
}
// See if the clip's .XMP file exists.
std::string xmpPath;
this->MakeClipFilePath ( &xmpPath, ".XMP" );
- if ( Host_IO::GetFileMode ( xmpPath.c_str() ) != Host_IO::kFMode_IsFile ) return; // No XMP.
+ if ( ! Host_IO::Exists ( xmpPath.c_str() ) ) return; // No XMP.
- // Read the entire .XMP file.
+ // Read the entire .XMP file. We know the XMP exists, New_XMPFiles_IO is supposed to return 0
+ // only if the file does not exist.
bool readOnly = XMP_OptionIsClear ( this->parent->openFlags, kXMPFiles_OpenForUpdate );
XMP_Assert ( this->parent->ioRef == 0 );
XMPFiles_IO* xmpFile = XMPFiles_IO::New_XMPFiles_IO ( xmpPath.c_str(), readOnly );
- if ( xmpFile == 0 ) return; // The open failed.
+ if ( xmpFile == 0 ) XMP_Throw ( "SonyHDV XMP file open failure", kXMPErr_InternalFailure );
this->parent->ioRef = xmpFile;
XMP_Int64 xmpLen = xmpFile->Length();
diff --git a/XMPFiles/source/FileHandlers/SonyHDV_Handler.hpp b/XMPFiles/source/FileHandlers/SonyHDV_Handler.hpp
index ac27c66..190930d 100644
--- a/XMPFiles/source/FileHandlers/SonyHDV_Handler.hpp
+++ b/XMPFiles/source/FileHandlers/SonyHDV_Handler.hpp
@@ -49,6 +49,9 @@ class SonyHDV_MetaHandler : public XMPFileHandler
public:
bool GetFileModDate ( XMP_DateTime * modDate );
+ void FillMetadataFiles(std::vector<std::string>* metadataFiles );
+ void FillAssociatedResources ( std::vector<std::string> * resourceList );
+ bool IsMetadataWritable ( );
void CacheFileData();
void ProcessXMP();
diff --git a/XMPFiles/source/FileHandlers/TIFF_Handler.cpp b/XMPFiles/source/FileHandlers/TIFF_Handler.cpp
index 109fe43..a09b879 100644
--- a/XMPFiles/source/FileHandlers/TIFF_Handler.cpp
+++ b/XMPFiles/source/FileHandlers/TIFF_Handler.cpp
@@ -53,13 +53,14 @@ bool TIFF_CheckFormat ( XMP_FileFormat format,
enum { kMinimalTIFFSize = 4+4+2+12+4 }; // Header plus IFD with 1 entry.
- IOBuffer ioBuf;
-
fileRef->Rewind ( );
- if ( ! CheckFileSpace ( fileRef, &ioBuf, kMinimalTIFFSize ) ) return false;
+ if ( ! XIO::CheckFileSpace ( fileRef, kMinimalTIFFSize ) ) return false;
- bool leTIFF = CheckBytes ( ioBuf.ptr, "\x49\x49\x2A\x00", 4 );
- bool beTIFF = CheckBytes ( ioBuf.ptr, "\x4D\x4D\x00\x2A", 4 );
+ XMP_Uns8 buffer [4];
+ fileRef->Read ( buffer, 4 );
+
+ bool leTIFF = CheckBytes ( buffer, "\x49\x49\x2A\x00", 4 );
+ bool beTIFF = CheckBytes ( buffer, "\x4D\x4D\x00\x2A", 4 );
return (leTIFF | beTIFF);
@@ -264,14 +265,8 @@ void TIFF_MetaHandler::ProcessXMP()
XMP_StringLen packetLen = (XMP_StringLen)this->xmpPacket.size();
try {
this->xmpObj.ParseFromBuffer ( packetStr, packetLen );
- haveXMP = true;
- } catch ( ... ) {
- XMP_ClearOption ( options, k2XMP_FileHadXMP );
- if ( haveIPTC ) iptc.ParseMemoryDataSets ( iptcInfo.dataPtr, iptcInfo.dataLen );
- if ( iptcDigestState == kDigestMatches ) iptcDigestState = kDigestMissing;
- ImportPhotoData ( tiff, iptc, psir, iptcDigestState, &this->xmpObj, options );
- throw; // ! Rethrow the exception, don't absorb it.
- }
+ } catch ( ... ) { /* Ignore parsing failures, someday we hope to get partial XMP back. */ }
+ haveXMP = true;
}
// Process the legacy metadata.
@@ -331,6 +326,9 @@ void TIFF_MetaHandler::UpdateFile ( bool doSafeUpdate )
bool doInPlace = (fileHadXMP && (this->xmpPacket.size() <= (size_t)oldPacketLength));
if ( this->tiffMgr.IsLegacyChanged() ) doInPlace = false;
+
+ bool localProgressTracking = false;
+ XMP_ProgressTracker* progressTracker = this->parent->progressTracker;
if ( ! doInPlace ) {
@@ -338,8 +336,13 @@ void TIFF_MetaHandler::UpdateFile ( bool doSafeUpdate )
sAPIPerf->back().extraInfo += ", TIFF append update";
#endif
+ if ( (progressTracker != 0) && (! progressTracker->WorkInProgress()) ) {
+ localProgressTracking = true;
+ progressTracker->BeginWork();
+ }
+
this->tiffMgr.SetTag ( kTIFF_PrimaryIFD, kTIFF_XMP, kTIFF_UndefinedType, (XMP_Uns32)this->xmpPacket.size(), this->xmpPacket.c_str() );
- this->tiffMgr.UpdateFileStream ( destRef );
+ this->tiffMgr.UpdateFileStream ( destRef, progressTracker );
} else {
@@ -357,11 +360,21 @@ void TIFF_MetaHandler::UpdateFile ( bool doSafeUpdate )
XMP_Assert ( this->xmpPacket.size() == (size_t)oldPacketLength ); // ! Done by common PutXMP logic.
+ if ( progressTracker != 0 ) {
+ if ( progressTracker->WorkInProgress() ) {
+ progressTracker->AddTotalWork ( this->xmpPacket.size() );
+ } else {
+ localProgressTracking = true;
+ progressTracker->BeginWork ( this->xmpPacket.size() );
+ }
+ }
+
liveFile->Seek ( oldPacketOffset, kXMP_SeekFromStart );
liveFile->Write ( this->xmpPacket.c_str(), (XMP_Int32)this->xmpPacket.size() );
}
-
+
+ if ( localProgressTracking ) progressTracker->WorkComplete();
this->needsUpdate = false;
} // TIFF_MetaHandler::UpdateFile
@@ -384,6 +397,9 @@ void TIFF_MetaHandler::WriteTempFile ( XMP_IO* tempRef )
if ( fileLen > 0xFFFFFFFFLL ) { // Check before making a copy of the file.
XMP_Throw ( "TIFF fles can't exceed 4GB", kXMPErr_BadTIFF );
}
+
+ XMP_ProgressTracker* progressTracker = this->parent->progressTracker;
+ if ( progressTracker != 0 ) progressTracker->BeginWork ( (float)fileLen );
origRef->Rewind ( );
tempRef->Truncate ( 0 );
@@ -397,6 +413,8 @@ void TIFF_MetaHandler::WriteTempFile ( XMP_IO* tempRef )
this->parent->ioRef = origRef;
throw;
}
+
+ if ( progressTracker != 0 ) progressTracker->WorkComplete();
} // TIFF_MetaHandler::WriteTempFile
diff --git a/XMPFiles/source/FileHandlers/TIFF_Handler.hpp b/XMPFiles/source/FileHandlers/TIFF_Handler.hpp
index 40dc50a..bc653b2 100644
--- a/XMPFiles/source/FileHandlers/TIFF_Handler.hpp
+++ b/XMPFiles/source/FileHandlers/TIFF_Handler.hpp
@@ -38,7 +38,8 @@ static const XMP_OptionBits kTIFF_HandlerFlags = (kXMPFiles_CanInjectXMP |
kXMPFiles_CanReconcile |
kXMPFiles_AllowsOnlyXMP |
kXMPFiles_ReturnsRawPacket |
- kXMPFiles_AllowsSafeUpdate);
+ kXMPFiles_AllowsSafeUpdate |
+ kXMPFiles_CanNotifyProgress);
class TIFF_MetaHandler : public XMPFileHandler
{
diff --git a/XMPFiles/source/FileHandlers/UCF_Handler.cpp b/XMPFiles/source/FileHandlers/UCF_Handler.cpp
index c9f63b9..8e1e1ff 100644
--- a/XMPFiles/source/FileHandlers/UCF_Handler.cpp
+++ b/XMPFiles/source/FileHandlers/UCF_Handler.cpp
@@ -128,7 +128,9 @@ bool UCF_CheckFormat ( XMP_FileFormat format,
XMP_LitMatch( mimetype, "application/vnd.adobe.indesign-idml-package" ) || //inCopy (inDesign) IDML Document
XMP_LitMatch( mimetype, "application/vnd.adobe.incopy-package" ) || // InDesign Document
XMP_LitMatch( mimetype, "application/vnd.adobe.indesign-package" ) || // InDesign Document
-
+ XMP_LitMatch( mimetype, "application/vnd.adobe.collage" ) || //Adobe Collage
+ XMP_LitMatch( mimetype, "application/vnd.adobe.ideas" ) || //Adobe Ideas
+ XMP_LitMatch( mimetype, "application/vnd.adobe.proto" ) || //Adobe Proto
false ) // "sentinel"
// *** ==> unknown are also treated as not acceptable
diff --git a/XMPFiles/source/FileHandlers/WAVE_Handler.cpp b/XMPFiles/source/FileHandlers/WAVE_Handler.cpp
index 5f1b330..876234e 100644
--- a/XMPFiles/source/FileHandlers/WAVE_Handler.cpp
+++ b/XMPFiles/source/FileHandlers/WAVE_Handler.cpp
@@ -434,9 +434,23 @@ void WAVE_MetaHandler::UpdateFile ( bool doSafeUpdate )
}
}
// XMP Packet is never completely removed from the file.
-
+
+ XMP_ProgressTracker* progressTracker=this->parent->progressTracker;
+ // local progess tracking required because for Handlers incapable of
+ // kXMPFiles_CanRewrite XMPFiles call this Update method after making
+ // a copy of the orignal file
+ bool localProgressTracking=false;
+ if ( progressTracker != 0 )
+ {
+ if ( ! progressTracker->WorkInProgress() )
+ {
+ localProgressTracking = true;
+ progressTracker->BeginWork ();
+ }
+ }
//write tree back to file
- mChunkController->writeFile( this->parent->ioRef );
+ mChunkController->writeFile( this->parent->ioRef ,progressTracker);
+ if ( localProgressTracking && progressTracker != 0 ) progressTracker->WorkComplete();
this->needsUpdate = false; // Make sure this is only called once.
} // WAVE_MetaHandler::UpdateFile
diff --git a/XMPFiles/source/FileHandlers/WAVE_Handler.hpp b/XMPFiles/source/FileHandlers/WAVE_Handler.hpp
index 49f873c..f8cc31b 100644
--- a/XMPFiles/source/FileHandlers/WAVE_Handler.hpp
+++ b/XMPFiles/source/FileHandlers/WAVE_Handler.hpp
@@ -52,7 +52,8 @@ static const XMP_OptionBits kWAVE_HandlerFlags = (kXMPFiles_CanInjectXMP |
kXMPFiles_PrefersInPlace |
kXMPFiles_CanReconcile |
kXMPFiles_ReturnsRawPacket |
- kXMPFiles_AllowsSafeUpdate
+ kXMPFiles_AllowsSafeUpdate |
+ kXMPFiles_CanNotifyProgress
);
/**
diff --git a/XMPFiles/source/FileHandlers/XDCAMEX_Handler.cpp b/XMPFiles/source/FileHandlers/XDCAMEX_Handler.cpp
index 350ab1f..9005fe8 100644
--- a/XMPFiles/source/FileHandlers/XDCAMEX_Handler.cpp
+++ b/XMPFiles/source/FileHandlers/XDCAMEX_Handler.cpp
@@ -15,10 +15,12 @@
#include "XMPFiles/source/XMPFiles_Impl.hpp"
#include "source/XMPFiles_IO.hpp"
#include "source/XIO.hpp"
+#include "source/IOUtils.hpp"
#include "XMPFiles/source/FileHandlers/XDCAMEX_Handler.hpp"
#include "XMPFiles/source/FormatSupport/XDCAM_Support.hpp"
#include "third-party/zuid/interfaces/MD5.h"
+#include "XMPFiles/source/FormatSupport/PackageFormat_Support.hpp"
using namespace std;
@@ -212,7 +214,7 @@ XMPFileHandler * XDCAMEX_MetaHandlerCTor ( XMPFiles * parent )
// XDCAMEX_MetaHandler::XDCAMEX_MetaHandler
// ========================================
-XDCAMEX_MetaHandler::XDCAMEX_MetaHandler ( XMPFiles * _parent ) : expat(0)
+XDCAMEX_MetaHandler::XDCAMEX_MetaHandler ( XMPFiles * _parent ) : expat(0),clipMetadata(0)
{
this->parent = _parent; // Inherited, can't set in the prefix.
this->handlerFlags = kXDCAMEX_HandlerFlags;
@@ -222,7 +224,7 @@ XDCAMEX_MetaHandler::XDCAMEX_MetaHandler ( XMPFiles * _parent ) : expat(0)
if ( this->parent->tempPtr == 0 ) {
// The CheckFormat call might have been skipped.
- this->parent->tempPtr = CreatePseudoClipPath ( this->parent->filePath );
+ this->parent->tempPtr = CreatePseudoClipPath ( this->parent->GetFilePath() );
}
this->rootPath.assign ( (char*) this->parent->tempPtr );
@@ -351,7 +353,8 @@ void XDCAMEX_MetaHandler::MakeLegacyDigest ( std::string * digestStr )
void XDCAMEX_MetaHandler::CleanupLegacyXML()
{
- if ( this->expat != 0 ) { delete ( this->expat ); this->expat = 0; }
+ delete this->expat;
+ this->expat = 0;
clipMetadata = 0; // ! Was a pointer into the expat tree.
@@ -407,6 +410,152 @@ bool XDCAMEX_MetaHandler::GetFileModDate ( XMP_DateTime * modDate )
} // XDCAMEX_MetaHandler::GetFileModDate
+// Adds all the associated resources for the specified clip only (not related spanned ones)
+static void FillClipAssociatedResources( std::vector<std::string> * resourceList, std::string &clipPath, std::string &clipName )
+{
+ std::string filePath;
+ std::string spannedClipFolderPath = clipPath + clipName + kDirChar;
+
+ std::string clipPathNoExt = spannedClipFolderPath + clipName;
+ // Get the files present inside clip folder.
+ std::vector<std::string> regExpStringVec;
+ std::string regExpString;
+ regExpString = "^" + clipName + ".MP4$";
+ regExpStringVec.push_back(regExpString);
+ regExpString = "^" + clipName + "M\\d\\d.XMP$";
+ regExpStringVec.push_back(regExpString);
+ regExpString = "^" + clipName + "M\\d\\d.XML$";
+ regExpStringVec.push_back(regExpString);
+ regExpString = "^" + clipName + "I\\d\\d.PPN$";
+ regExpStringVec.push_back(regExpString);
+ regExpString = "^" + clipName + "R\\d\\d.BIM$";
+ regExpStringVec.push_back(regExpString);
+ regExpString = "^" + clipName + ".SMI$";
+ regExpStringVec.push_back(regExpString);
+
+ IOUtils::GetMatchingChildren (*resourceList, spannedClipFolderPath, regExpStringVec, false, true, true );
+
+}
+
+
+// =================================================================================================
+// XDCAMEX_MetaHandler::FillAssociatedResources
+// ======================================
+void XDCAMEX_MetaHandler::FillAssociatedResources ( std::vector<std::string> * resourceList )
+{
+ // The possible associated resources:
+ // BPAV/
+ // MEDIAPRO.XML
+ // CUEUP.XML
+ // CLPR/
+ // MIXXXX_YY: MI is MachineID, XXXX is TakeSerial,
+ // YY is ClipSuffix(as single take can be divided across multiple clips.)
+ // In case of spanning, all the clip folders starting from "MIXXXX_" are looked for.
+ // MIXXXX_YY.MP4
+ // MIXXXX_YYMNN.XML 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.
+ // MIXXXX_YYMNN.XMP
+ // MIXXXX_YYINN.PPN
+ // MIXXXX_YYRNN.BIM
+ // MXXXX_YY.SMI
+ // TAKR/
+ // MIXXXX:
+ // MIXXXXMNN.XML 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.
+ // MIXXXX.SMI
+ // MIXXXXUNN.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, MIXXXX.SMI shall be picked up.
+ XMP_VarString bpavPath = this->rootPath + kDirChar + "BPAV" + kDirChar;
+ XMP_VarString filePath;
+ //Add RootPath
+ filePath = this->rootPath + kDirChar;
+ PackageFormat_Support::AddResourceIfExists ( resourceList, filePath );
+
+ // Get the files present directly inside BPAV folder.
+ filePath = bpavPath + "MEDIAPRO.XML";
+ PackageFormat_Support::AddResourceIfExists ( resourceList, filePath );
+ filePath = bpavPath + "MEDIAPRO.BUP";
+ PackageFormat_Support::AddResourceIfExists ( resourceList, filePath );
+ filePath = bpavPath + "CUEUP.XML";
+ PackageFormat_Support::AddResourceIfExists ( resourceList, filePath );
+ filePath = bpavPath + "CUEUP.BUP";
+ PackageFormat_Support::AddResourceIfExists ( resourceList, filePath );
+
+ XMP_VarString clipPath = bpavPath + "CLPR" + kDirChar;
+ size_t clipSuffixIndex = this->clipName.find_last_of('_');
+ XMP_VarString takeName = this->clipName.substr(0, clipSuffixIndex);
+
+ // Add spanned clip files.
+ // Here, we iterate over all the folders present inside "/BPAV/CLPR/" and whose name starts from
+ // "MIXXXX_". All valid files present inside such folders are added to the list.
+ XMP_VarString regExpString;
+ regExpString = "^" + takeName + "_\\d\\d$";
+ XMP_StringVector list;
+
+ IOUtils::GetMatchingChildren ( list, clipPath, regExpString, true, false, false );
+ size_t spaningClipsCount = list.size();
+ for ( size_t index = 0; index < spaningClipsCount; index++ ) {
+ FillClipAssociatedResources ( resourceList, clipPath, list[index] );
+ }
+ list.clear();
+
+ size_t sizeWithoutTakeFiles = resourceList->size();
+ XMP_VarString takeFolderPath = bpavPath + "TAKR" + kDirChar + takeName + kDirChar;
+ XMP_StringVector regExpStringVec;
+
+ // Get the files present inside take folder.
+ regExpString = "^" + takeName + "M\\d\\d.XML$";
+ regExpStringVec.push_back ( regExpString );
+ regExpString = "^" + takeName + "U\\d\\d.SMI$";
+ regExpStringVec.push_back ( regExpString );
+ regExpString = "^" + takeName + ".SMI$";
+ regExpStringVec.push_back ( regExpString );
+ IOUtils::GetMatchingChildren ( *resourceList, takeFolderPath, regExpStringVec, false, true, true );
+
+ if ( sizeWithoutTakeFiles == resourceList->size() )
+ {
+ // no Take files added to resource list. But "TAKR" folder is necessary to recognize this format
+ // so let's add it to the list.
+ filePath = bpavPath + "TAKR" + kDirChar;
+ PackageFormat_Support::AddResourceIfExists(resourceList, filePath);
+ }
+} // XDCAMEX_MetaHandler::FillAssociatedResources
+
+// =================================================================================================
+// XDCAMEX_MetaHandler::FillMetadataFiles
+// ======================================
+void XDCAMEX_MetaHandler::FillMetadataFiles ( std::vector<std::string> * metadataFiles )
+{
+ std::string noExtPath, filePath;
+
+ noExtPath = rootPath + kDirChar + "BPAV" + kDirChar + "CLPR" +
+ kDirChar + clipName + kDirChar + clipName;
+
+ filePath = noExtPath + "M01.XMP";
+ metadataFiles->push_back ( filePath );
+ filePath = noExtPath + "M01.XML";
+ metadataFiles->push_back ( filePath );
+ filePath = rootPath + kDirChar + "BPAV" + kDirChar + "MEDIAPRO.XML";
+ metadataFiles->push_back ( filePath );
+
+} // FillMetadataFiles_XDCAM_EX
+
+// =================================================================================================
+// XDCAMEX_MetaHandler::IsMetadataWritable
+// =======================================
+
+bool XDCAMEX_MetaHandler::IsMetadataWritable ( )
+{
+ std::vector<std::string> metadataFiles;
+ FillMetadataFiles(&metadataFiles);
+ std::vector<std::string>::iterator itr = metadataFiles.begin();
+ // Check whether sidecar is writable, if not then check if it can be created.
+ XMP_Bool xmpWritable = Host_IO::Writable( itr->c_str(), true );
+ // Check for legacy metadata file.
+ XMP_Bool xmlWritable = Host_IO::Writable( (++itr)->c_str(), false );
+ return ( xmlWritable && xmpWritable );
+}// XDCAMEX_MetaHandler::IsMetadataWritable
+
// =================================================================================================
// XDCAMEX_MetaHandler::CacheFileData
// ==================================
@@ -423,15 +572,16 @@ void XDCAMEX_MetaHandler::CacheFileData()
std::string xmpPath;
this->MakeClipFilePath ( &xmpPath, "M01.XMP" );
- if ( Host_IO::GetFileMode ( xmpPath.c_str() ) != Host_IO::kFMode_IsFile ) return; // No XMP.
+ if ( ! Host_IO::Exists ( xmpPath.c_str() ) ) return; // No XMP.
- // Read the entire .XMP file.
+ // Read the entire .XMP file. We know the XMP exists, New_XMPFiles_IO is supposed to return 0
+ // only if the file does not exist.
bool readOnly = XMP_OptionIsClear ( this->parent->openFlags, kXMPFiles_OpenForUpdate );
XMP_Assert ( this->parent->ioRef == 0 );
XMPFiles_IO* xmpFile = XMPFiles_IO::New_XMPFiles_IO ( xmpPath.c_str(), readOnly );
- if ( xmpFile == 0 ) return; // The open failed.
+ if ( xmpFile == 0 ) XMP_Throw ( "XDCAMEX XMP file open failure", kXMPErr_InternalFailure );
this->parent->ioRef = xmpFile;
XMP_Int64 xmpLen = xmpFile->Length();
@@ -463,7 +613,7 @@ void XDCAMEX_MetaHandler::GetTakeDuration ( const std::string & takeURI, std::st
// *** Better yet, avoid this cruft with self-cleaning objects.
#define CleanupAndExit \
{ \
- if ( expat != 0 ) delete expat; \
+ delete expatMediaPro; \
takeXMLFile.Close(); \
return; \
}
@@ -500,22 +650,22 @@ void XDCAMEX_MetaHandler::GetTakeDuration ( const std::string & takeURI, std::st
if ( hostRef == Host_IO::noFileRef ) return; // The open failed.
XMPFiles_IO takeXMLFile ( hostRef, takePath.c_str(), Host_IO::openReadOnly );
- ExpatAdapter * expat = XMP_NewExpatAdapter ( ExpatAdapter::kUseLocalNamespaces );
- if ( this->expat == 0 ) return;
+ ExpatAdapter * expatMediaPro = XMP_NewExpatAdapter ( ExpatAdapter::kUseLocalNamespaces );
+ if ( expatMediaPro == 0 ) return;
XMP_Uns8 buffer [64*1024];
while ( true ) {
XMP_Int32 ioCount = takeXMLFile.Read ( buffer, sizeof(buffer) );
if ( ioCount == 0 ) break;
- expat->ParseBuffer ( buffer, ioCount, false /* not the end */ );
+ expatMediaPro->ParseBuffer ( buffer, ioCount, false /* not the end */ );
}
- expat->ParseBuffer ( 0, 0, true ); // End the parse.
+ expatMediaPro->ParseBuffer ( 0, 0, true ); // End the parse.
takeXMLFile.Close();
// Get the root node of the XML tree.
- XML_Node & mediaproXMLTree = expat->tree;
+ XML_Node & mediaproXMLTree = expatMediaPro->tree;
for ( size_t i = 0, limit = mediaproXMLTree.content.size(); i < limit; ++i ) {
if ( mediaproXMLTree.content[i]->kind == kElemNode ) {
takeRootElem = mediaproXMLTree.content[i];
@@ -568,7 +718,7 @@ void XDCAMEX_MetaHandler::GetTakeUMID ( const std::string& clipUMID,
// *** Better yet, avoid this cruft with self-cleaning objects.
#define CleanupAndExit \
{ \
- if (expat != 0) delete expat; \
+ delete expatMediaPro; \
mediaproXMLFile.Close(); \
return; \
}
@@ -593,22 +743,22 @@ void XDCAMEX_MetaHandler::GetTakeUMID ( const std::string& clipUMID,
if ( hostRef == Host_IO::noFileRef ) return; // The open failed.
XMPFiles_IO mediaproXMLFile ( hostRef, mediapropath.c_str(), Host_IO::openReadOnly );
- ExpatAdapter * expat = XMP_NewExpatAdapter ( ExpatAdapter::kUseLocalNamespaces );
- if ( this->expat == 0 ) return;
+ ExpatAdapter * expatMediaPro = XMP_NewExpatAdapter ( ExpatAdapter::kUseLocalNamespaces );
+ if ( expatMediaPro == 0 ) return;
XMP_Uns8 buffer [64*1024];
while ( true ) {
XMP_Int32 ioCount = mediaproXMLFile.Read ( buffer, sizeof(buffer) );
if ( ioCount == 0 ) break;
- expat->ParseBuffer ( buffer, ioCount, false /* not the end */ );
+ expatMediaPro->ParseBuffer ( buffer, ioCount, false /* not the end */ );
}
- expat->ParseBuffer ( 0, 0, true ); // End the parse.
+ expatMediaPro->ParseBuffer ( 0, 0, true ); // End the parse.
mediaproXMLFile.Close();
// Get the root node of the XML tree.
- XML_Node & mediaproXMLTree = expat->tree;
+ XML_Node & mediaproXMLTree = expatMediaPro->tree;
for ( size_t i = 0, limit = mediaproXMLTree.content.size(); i < limit; ++i ) {
if ( mediaproXMLTree.content[i]->kind == kElemNode ) {
mediaproRootElem = mediaproXMLTree.content[i];
diff --git a/XMPFiles/source/FileHandlers/XDCAMEX_Handler.hpp b/XMPFiles/source/FileHandlers/XDCAMEX_Handler.hpp
index a8ab89f..3673b1f 100644
--- a/XMPFiles/source/FileHandlers/XDCAMEX_Handler.hpp
+++ b/XMPFiles/source/FileHandlers/XDCAMEX_Handler.hpp
@@ -46,6 +46,10 @@ class XDCAMEX_MetaHandler : public XMPFileHandler
public:
bool GetFileModDate ( XMP_DateTime * modDate );
+
+ void FillMetadataFiles ( std::vector<std::string> * metadataFiles );
+ void FillAssociatedResources ( std::vector<std::string> * resourceList );
+ bool IsMetadataWritable ( );
void CacheFileData();
void ProcessXMP();
@@ -61,7 +65,7 @@ public:
private:
- XDCAMEX_MetaHandler() : expat(0) {}; // Hidden on purpose.
+ XDCAMEX_MetaHandler() : expat(0), clipMetadata(0) {}; // Hidden on purpose.
bool MakeClipFilePath ( std::string * path, XMP_StringPtr suffix, bool checkFile = false );
bool MakeMediaproPath ( std::string * path, bool checkFile = false );
@@ -75,6 +79,7 @@ private:
std::string rootPath, clipName, xdcNS, legacyNS, clipUMID;
+ // Used to Parse the Non-XMP /non real time metadata file associated with the clip
ExpatAdapter * expat;
XML_Node * clipMetadata;
diff --git a/XMPFiles/source/FileHandlers/XDCAM_Handler.cpp b/XMPFiles/source/FileHandlers/XDCAM_Handler.cpp
index fab0925..f455a8c 100644
--- a/XMPFiles/source/FileHandlers/XDCAM_Handler.cpp
+++ b/XMPFiles/source/FileHandlers/XDCAM_Handler.cpp
@@ -15,9 +15,11 @@
#include "XMPFiles/source/XMPFiles_Impl.hpp"
#include "source/XMPFiles_IO.hpp"
#include "source/XIO.hpp"
+#include "source/IOUtils.hpp"
#include "XMPFiles/source/FileHandlers/XDCAM_Handler.hpp"
#include "XMPFiles/source/FormatSupport/XDCAM_Support.hpp"
+#include "XMPFiles/source/FormatSupport/PackageFormat_Support.hpp"
#include "third-party/zuid/interfaces/MD5.h"
using namespace std;
@@ -30,7 +32,7 @@ using namespace std;
/// well-defined layout and naming rules. There are 2 different layouts for XDCAM, called FAM and SAM.
/// The FAM layout is used by "normal" XDCAM devices. The SAM layout is used by XDCAM-EX devices.
///
-/// A typical FAM layout looks like (note mixed case for General, Clip, Edit, and Sub folders):
+/// A typical FAM layout looks like (note mixed case for the nested folders):
///
/// .../MyMovie/
/// INDEX.XML
@@ -54,6 +56,51 @@ using namespace std;
/// E0002E01.SMI
/// E0002M01.XML
///
+/// A typical FAM XMPilot layout looks like (note mixed case for the nested folders):
+///
+/// .../MyMovie/
+/// DISCMETA.XML
+/// MEDIAPRO.XML
+/// General/
+/// Clip/
+/// Office_0001.MXF
+/// Office_0001M01.XML
+/// Office_0001M01.XMP
+/// Office_0002.MXF
+/// Office_0002M01.XML
+/// Office_0002M01.XMP
+/// Sub/
+/// Office_0001S01.MXF
+/// Office_0002S01.MXF
+/// Edit/
+/// UserData/
+/// unknown files
+///
+/// A typical FAM XDCAM Memory SxS layout looks like (note mixed case for the nested folders):
+///
+/// .../MyMovie/
+/// DISCMETA.XML
+/// MEDIAPRO.XML
+/// CUEUP.XML
+/// General/
+/// Clip/
+/// C0001.MXF
+/// C0001M01.XML
+/// C0001M01.XMP
+/// C0001R01.BIM
+/// C0002.MXF
+/// C0002M01.XML
+/// C0002M01.XMP
+/// C0001R01.BIM
+/// Sub/
+/// C0001S01.MXF
+/// C0002S01.MXF
+/// Edit/
+/// Take/
+/// T0001.SMI
+/// T0001M01.XML
+/// UserData/
+///
/// A typical SAM layout looks like:
///
/// .../MyMovie/
@@ -191,7 +238,10 @@ bool XDCAM_CheckFormat ( XMP_FileFormat format,
rootPath += gpName;
gpName.erase();
- if ( Host_IO::GetChildMode ( rootPath.c_str(), "ALIAS.XML" ) != Host_IO::kFMode_IsFile ) {
+ // 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.
}
@@ -232,7 +282,13 @@ bool XDCAM_CheckFormat ( XMP_FileFormat format,
tempPath = rootPath;
- if ( Host_IO::GetChildMode ( tempPath.c_str(), "INDEX.XML" ) != Host_IO::kFMode_IsFile ) return false;
+ // 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;
@@ -391,6 +447,79 @@ XMPFileHandler * XDCAM_MetaHandlerCTor ( XMPFiles * 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 )
+ )
+ {
+ // If cached MXF file name is present then use it otherwise
+ // side car generated on case insensitive OS may not be read on case sensitive OS.
+ // For example, if file name is X.MXF then windows says X.mxf is same as X.MXF so
+ // we may land up generating side car name as X.mxf.xmp which will not be read on
+ // Mac which will search specifically for X.MXF.xmp
+ XMP_VarString filePath = this->parent->GetFilePath();
+ XMP_VarString ext;
+ XIO::SplitFileExtension(&filePath, &ext);
+ if(ext == "MXF" || ext == "mxf")
+ {
+ this->sidecarPath = this->parent->GetFilePath() + ".xmp";
+ }
+ else
+ {
+ 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
// ====================================
@@ -406,7 +535,7 @@ XDCAM_MetaHandler::XDCAM_MetaHandler ( XMPFiles * _parent ) : isFAM(false), expa
if ( this->parent->tempPtr == 0 ) {
// The CheckFormat call might have been skipped.
- this->parent->tempPtr = CreatePseudoClipPath ( this->parent->filePath );
+ this->parent->tempPtr = CreatePseudoClipPath ( this->parent->GetFilePath() );
}
this->rootPath.assign ( (char*) this->parent->tempPtr );
@@ -419,6 +548,9 @@ XDCAM_MetaHandler::XDCAM_MetaHandler ( XMPFiles * _parent ) : isFAM(false), expa
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
@@ -551,6 +683,26 @@ void XDCAM_MetaHandler::CleanupLegacyXML()
} // XDCAM_MetaHandler::CleanupLegacyXML
+void XDCAM_MetaHandler::readXMLFile( XMP_StringPtr filePath, ExpatAdapter* &expat )
+{
+ Host_IO::FileRef hostRef = Host_IO::Open ( filePath, Host_IO::openReadOnly );
+ if ( hostRef == Host_IO::noFileRef ) return; // The open failed.
+ XMPFiles_IO xmlFile ( hostRef, filePath, Host_IO::openReadOnly );
+
+ expat = XMP_NewExpatAdapter ( ExpatAdapter::kUseLocalNamespaces );
+ if ( expat == 0 ) XMP_Throw ( "XDCAM_MetaHandler: Can't create Expat adapter", kXMPErr_NoMemory );
+
+ XMP_Uns8 buffer [64*1024];
+ while ( true ) {
+ XMP_Int32 ioCount = xmlFile.Read ( buffer, sizeof(buffer) );
+ if ( ioCount == 0 ) break;
+ expat->ParseBuffer ( buffer, ioCount, false /* not the end */ );
+ }
+ expat->ParseBuffer ( 0, 0, true ); // End the parse.
+
+ xmlFile.Close();
+}
+
// =================================================================================================
// XDCAM_MetaHandler::GetFileModDate
// =================================
@@ -607,6 +759,660 @@ 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 ( 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
+// ==================================
+bool XDCAM_MetaHandler::RefersClipUmid ( std::string clipUmid , XMP_StringPtr editInfoPath )
+{
+ ExpatAdapter* editInfoExpat = 0 ;
+ XMP_StringPtr nameSpace = 0 ;
+ try {
+ readXMLFile( editInfoPath, editInfoExpat );
+ if ( editInfoExpat != 0 )
+ {
+ XML_Node & xmlTree = editInfoExpat->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" ) )
+ {
+ nameSpace = rootElem->ns.c_str() ;
+ size_t noOfBodyElements = rootElem->CountNamedElements ( nameSpace, "body" ) ;
+ while( noOfBodyElements-- )
+ {
+ XML_NodePtr bodyNode = rootElem->GetNamedElement( nameSpace, "body" );
+ size_t noOfParElements = bodyNode->CountNamedElements ( nameSpace, "par" ) ;
+ while( noOfParElements-- )
+ {
+ XML_NodePtr parNode = bodyNode->GetNamedElement( nameSpace, "par" );
+ size_t noOfRefElements = parNode->CountNamedElements ( nameSpace, "ref" ) ;
+ size_t whichElem = 0;
+ while( noOfRefElements-- )
+ {
+ XML_NodePtr refNode = parNode->GetNamedElement( nameSpace, "ref" ,whichElem++ );
+ XMP_StringPtr umidValue = refNode->GetAttrValue ( "src" );
+ if ( umidValue != 0 &&
+ ( XMP_LitMatch( umidValue , clipUmid.c_str() ) ||
+ ( strlen(umidValue) > 15 && XMP_LitMatch( &umidValue[15] , clipUmid.c_str() ) )
+ )
+ )
+ {
+ delete ( editInfoExpat ) ;
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ } catch ( ... ) {
+ }
+ delete ( editInfoExpat ) ;
+ 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;
+ FillMetadataFiles(&metadataFiles);
+ std::vector<std::string>::iterator itr = metadataFiles.begin();
+ // Check whether sidecar is writable, if not then check if it can be created.
+ bool xmpWritable = Host_IO::Writable( itr->c_str(), true );
+ // Check for legacy metadata file.
+ bool xmlWritable = Host_IO::Writable( (++itr)->c_str(), false );
+ return ( xmlWritable && xmpWritable );
+}// 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 );
+
+} // XDCAM_MetaHandler::FillMetadataFiles
+
// =================================================================================================
// XDCAM_MetaHandler::CacheFileData
// ================================
@@ -621,17 +1427,16 @@ void XDCAM_MetaHandler::CacheFileData()
// See if the clip's .XMP file exists.
- std::string xmpPath;
- this->MakeClipFilePath ( &xmpPath, "M01.XMP" );
- if ( Host_IO::GetFileMode ( xmpPath.c_str() ) != Host_IO::kFMode_IsFile ) return; // No XMP.
+ if ( ! Host_IO::Exists ( this->sidecarPath.c_str() ) ) return; // No XMP.
- // Read the entire .XMP file.
+ // Read the entire .XMP file. We know the XMP exists, New_XMPFiles_IO is supposed to return 0
+ // only if the file does not exist.
bool readOnly = XMP_OptionIsClear ( this->parent->openFlags, kXMPFiles_OpenForUpdate );
XMP_Assert ( this->parent->ioRef == 0 );
- XMPFiles_IO* xmpFile = XMPFiles_IO::New_XMPFiles_IO ( xmpPath.c_str(), readOnly );
- if ( xmpFile == 0 ) return; // The open failed.
+ 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;
XMP_Int64 xmpLen = xmpFile->Length();
@@ -697,22 +1502,8 @@ void XDCAM_MetaHandler::ProcessXMP()
std::string xmlPath, umid;
this->MakeClipFilePath ( &xmlPath, "M01.XML" );
- Host_IO::FileRef hostRef = Host_IO::Open ( xmlPath.c_str(), Host_IO::openReadOnly );
- if ( hostRef == Host_IO::noFileRef ) return; // The open failed.
- XMPFiles_IO xmlFile ( hostRef, xmlPath.c_str(), Host_IO::openReadOnly );
-
- this->expat = XMP_NewExpatAdapter ( ExpatAdapter::kUseLocalNamespaces );
- if ( this->expat == 0 ) XMP_Throw ( "XDCAM_MetaHandler: Can't create Expat adapter", kXMPErr_NoMemory );
-
- XMP_Uns8 buffer [64*1024];
- while ( true ) {
- XMP_Int32 ioCount = xmlFile.Read ( buffer, sizeof(buffer) );
- if ( ioCount == 0 ) break;
- this->expat->ParseBuffer ( buffer, ioCount, false /* not the end */ );
- }
- this->expat->ParseBuffer ( 0, 0, true ); // End the parse.
-
- xmlFile.Close();
+ 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.
@@ -785,14 +1576,13 @@ void XDCAM_MetaHandler::UpdateFile ( bool doSafeUpdate )
// -----------------------------------------------------------------------
// Update the XMP file first, don't let legacy XML failures block the XMP.
- std::string xmpPath;
- this->MakeClipFilePath ( &xmpPath, "M01.XMP" );
+
- bool haveXMP = Host_IO::Exists ( xmpPath.c_str() );
+ bool haveXMP = Host_IO::Exists ( this->sidecarPath.c_str() );
if ( ! haveXMP ) {
XMP_Assert ( this->parent->ioRef == 0 );
- Host_IO::Create ( xmpPath.c_str() );
- this->parent->ioRef = XMPFiles_IO::New_XMPFiles_IO ( xmpPath.c_str(), Host_IO::openReadWrite );
+ Host_IO::Create ( this->sidecarPath.c_str() );
+ this->parent->ioRef = XMPFiles_IO::New_XMPFiles_IO ( this->sidecarPath.c_str(), Host_IO::openReadWrite );
if ( this->parent->ioRef == 0 ) XMP_Throw ( "Failure opening XDCAM XMP file", kXMPErr_ExternalFailure );
}
diff --git a/XMPFiles/source/FileHandlers/XDCAM_Handler.hpp b/XMPFiles/source/FileHandlers/XDCAM_Handler.hpp
index fbcc8bb..f03ada8 100644
--- a/XMPFiles/source/FileHandlers/XDCAM_Handler.hpp
+++ b/XMPFiles/source/FileHandlers/XDCAM_Handler.hpp
@@ -50,6 +50,10 @@ public:
bool GetFileModDate ( XMP_DateTime * modDate );
+ void FillMetadataFiles ( std::vector<std::string> * metadataFiles );
+ void FillAssociatedResources ( std::vector<std::string> * resourceList );
+ bool IsMetadataWritable ( ) ;
+
void CacheFileData();
void ProcessXMP();
@@ -70,10 +74,21 @@ private:
bool MakeMediaproPath ( std::string * path, bool checkFile = false );
void MakeLegacyDigest ( std::string * digestStr );
void CleanupLegacyXML();
+ void SetSidecarPath();
+
+ 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 );
bool GetMediaProMetadata ( SXMPMeta * xmpObjPtr, const std::string& clipUMID, bool digestFound );
- std::string rootPath, clipName, xdcNS, legacyNS;
+ std::string rootPath, clipName, xdcNS, legacyNS, sidecarPath;
bool isFAM;
diff --git a/XMPFiles/source/FormatSupport/ASF_Support.cpp b/XMPFiles/source/FormatSupport/ASF_Support.cpp
index 35adce6..709aea5 100644
--- a/XMPFiles/source/FormatSupport/ASF_Support.cpp
+++ b/XMPFiles/source/FormatSupport/ASF_Support.cpp
@@ -30,11 +30,11 @@ int IsEqualGUID ( const GUID& guid1, const GUID& guid2 )
}
#endif
-ASF_Support::ASF_Support() : legacyManager(0), posFileSizeInfo(0) {}
+ASF_Support::ASF_Support() : legacyManager(0),progressTracker(0), posFileSizeInfo(0) {}
-ASF_Support::ASF_Support ( ASF_LegacyManager* _legacyManager ) : posFileSizeInfo(0)
+ASF_Support::ASF_Support ( ASF_LegacyManager* _legacyManager,XMP_ProgressTracker* _progressTracker )
+ :legacyManager(_legacyManager),progressTracker(_progressTracker), posFileSizeInfo(0)
{
- legacyManager = _legacyManager;
}
ASF_Support::~ASF_Support()
@@ -645,7 +645,11 @@ bool ASF_Support::WriteHeaderObject ( XMP_IO* sourceRef, XMP_IO* destRef, const
// if we are operating on the same file (in-place update), place pointer before writing
if ( sourceRef == destRef ) destRef->Seek ( object.pos, kXMP_SeekFromStart );
-
+ if ( this->progressTracker != 0 )
+ {
+ XMP_Assert ( this->progressTracker->WorkInProgress() );
+ this->progressTracker->AddTotalWork ( (float)header.size() );
+ }
// write header
destRef->Write ( header.c_str(), header.size() );
@@ -1139,7 +1143,7 @@ void ASF_LegacyManager::ImportLegacy ( SXMPMeta* xmp )
FromUTF16 ( (UTF16Unit*)fields[fieldDescription].c_str(), (fields[fieldDescription].size() / 2), &utf8, false );
if ( ! utf8.empty() ) xmp->SetLocalizedText ( kXMP_NS_DC, "description", "", "x-default", utf8.c_str(), kXMP_DeleteExisting );
- if ( ! utf8.empty() ) xmp->SetProperty ( kXMP_NS_XMP_Rights, "WebStatement", fields[fieldCopyrightURL].c_str(), kXMP_DeleteExisting );
+ if ( ! fields[fieldCopyrightURL].empty() ) xmp->SetProperty ( kXMP_NS_XMP_Rights, "WebStatement", fields[fieldCopyrightURL].c_str(), kXMP_DeleteExisting );
#if ! Exclude_LicenseURL_Recon
if ( ! fields[fieldLicenseURL].empty() ) xmp->SetProperty ( kXMP_NS_XMP_Rights, "Certificate", fields[fieldLicenseURL].c_str(), kXMP_DeleteExisting );
diff --git a/XMPFiles/source/FormatSupport/ASF_Support.hpp b/XMPFiles/source/FormatSupport/ASF_Support.hpp
index 9d9060b..3e170e3 100644
--- a/XMPFiles/source/FormatSupport/ASF_Support.hpp
+++ b/XMPFiles/source/FormatSupport/ASF_Support.hpp
@@ -16,6 +16,7 @@
#include "XMPFiles/source/XMPFiles_Impl.hpp"
#include "XMPFiles/source/FormatSupport/Reconcile_Impl.hpp"
#include "source/XIO.hpp"
+#include "source/XMP_ProgressTracker.hpp"
// currently exclude LicenseURL from reconciliation
#define Exclude_LicenseURL_Recon 1
@@ -187,7 +188,7 @@ public:
};
ASF_Support();
- ASF_Support ( ASF_LegacyManager* legacyManager );
+ ASF_Support ( ASF_LegacyManager* legacyManager, XMP_ProgressTracker* _progressTracker);
virtual ~ASF_Support();
long OpenASF ( XMP_IO* fileRef, ObjectState & inOutObjectState );
@@ -215,6 +216,7 @@ public:
private:
ASF_LegacyManager* legacyManager;
+ XMP_ProgressTracker* progressTracker;//not owned by ASF_Support
XMP_Uns64 posFileSizeInfo;
static std::string ReplaceString ( std::string& operand, std::string& str, int offset, int count );
diff --git a/XMPFiles/source/FormatSupport/ID3_Support.cpp b/XMPFiles/source/FormatSupport/ID3_Support.cpp
index 2bc9a1f..e33e39e 100644
--- a/XMPFiles/source/FormatSupport/ID3_Support.cpp
+++ b/XMPFiles/source/FormatSupport/ID3_Support.cpp
@@ -18,32 +18,370 @@
#include <vector>
-// =================================================================================================
-
#define MIN(a,b) ((a) < (b) ? (a) : (b))
+namespace ID3_Support {
+
+// =================================================================================================
+
ID3GenreMap* kMapID3GenreCodeToName = 0; // Map from a code like "21" or "RX" to the full name.
ID3GenreMap* kMapID3GenreNameToCode = 0; // Map from the full name to a code like "21" or "RX".
+static size_t numberedGenreCount = 0; // Set in InitializeGlobals, used in ID3v1Tag::read and write.
+
+struct GenreInfo { const char * code; const char * name; };
+
+static const GenreInfo kAbbreviatedGenres[] = { // ID3 v3 or v4 genre abbreviations.
+ { "RX", "Remix" },
+ { "CR", "Cover" },
+ { 0, 0 }
+};
+
+static const GenreInfo kNumberedGenres[] = { // Numeric genre codes from ID3 v1, complete range of 0..125.
+ { "0", "Blues" },
+ { "1", "Classic Rock" },
+ { "2", "Country" },
+ { "3", "Dance" },
+ { "4", "Disco" },
+ { "5", "Funk" },
+ { "6", "Grunge" },
+ { "7", "Hip-Hop" },
+ { "8", "Jazz" },
+ { "9", "Metal" },
+ { "10", "New Age" },
+ { "11", "Oldies" },
+ { "12", "Other" },
+ { "13", "Pop" },
+ { "14", "R&B" },
+ { "15", "Rap" },
+ { "16", "Reggae" },
+ { "17", "Rock" },
+ { "18", "Techno" },
+ { "19", "Industrial" },
+ { "20", "Alternative" },
+ { "21", "Ska" },
+ { "22", "Death Metal" },
+ { "23", "Pranks" },
+ { "24", "Soundtrack" },
+ { "25", "Euro-Techno" },
+ { "26", "Ambient" },
+ { "27", "Trip-Hop" },
+ { "28", "Vocal" },
+ { "29", "Jazz+Funk" },
+ { "30", "Fusion" },
+ { "31", "Trance" },
+ { "32", "Classical" },
+ { "33", "Instrumental" },
+ { "34", "Acid" },
+ { "35", "House" },
+ { "36", "Game" },
+ { "37", "Sound Clip" },
+ { "38", "Gospel" },
+ { "39", "Noise" },
+ { "40", "AlternRock" },
+ { "41", "Bass" },
+ { "42", "Soul" },
+ { "43", "Punk" },
+ { "44", "Space" },
+ { "45", "Meditative" },
+ { "46", "Instrumental Pop" },
+ { "47", "Instrumental Rock" },
+ { "48", "Ethnic" },
+ { "49", "Gothic" },
+ { "50", "Darkwave" },
+ { "51", "Techno-Industrial" },
+ { "52", "Electronic" },
+ { "53", "Pop-Folk" },
+ { "54", "Eurodance" },
+ { "55", "Dream" },
+ { "56", "Southern Rock" },
+ { "57", "Comedy" },
+ { "58", "Cult" },
+ { "59", "Gangsta" },
+ { "60", "Top 40" },
+ { "61", "Christian Rap" },
+ { "62", "Pop/Funk" },
+ { "63", "Jungle" },
+ { "64", "Native American" },
+ { "65", "Cabaret" },
+ { "66", "New Wave" },
+ { "67", "Psychadelic" },
+ { "68", "Rave" },
+ { "69", "Showtunes" },
+ { "70", "Trailer" },
+ { "71", "Lo-Fi" },
+ { "72", "Tribal" },
+ { "73", "Acid Punk" },
+ { "74", "Acid Jazz" },
+ { "75", "Polka" },
+ { "76", "Retro" },
+ { "77", "Musical" },
+ { "78", "Rock & Roll" },
+ { "79", "Hard Rock" },
+ { "80", "Folk" },
+ { "81", "Folk-Rock" },
+ { "82", "National Folk" },
+ { "83", "Swing" },
+ { "84", "Fast Fusion" },
+ { "85", "Bebob" },
+ { "86", "Latin" },
+ { "87", "Revival" },
+ { "88", "Celtic" },
+ { "89", "Bluegrass" },
+ { "90", "Avantgarde" },
+ { "91", "Gothic Rock" },
+ { "92", "Progressive Rock" },
+ { "93", "Psychedelic Rock" },
+ { "94", "Symphonic Rock" },
+ { "95", "Slow Rock" },
+ { "96", "Big Band" },
+ { "97", "Chorus" },
+ { "98", "Easy Listening" },
+ { "99", "Acoustic" },
+ { "100", "Humour" },
+ { "101", "Speech" },
+ { "102", "Chanson" },
+ { "103", "Opera" },
+ { "104", "Chamber Music" },
+ { "105", "Sonata" },
+ { "106", "Symphony" },
+ { "107", "Booty Bass" },
+ { "108", "Primus" },
+ { "109", "Porn Groove" },
+ { "110", "Satire" },
+ { "111", "Slow Jam" },
+ { "112", "Club" },
+ { "113", "Tango" },
+ { "114", "Samba" },
+ { "115", "Folklore" },
+ { "116", "Ballad" },
+ { "117", "Power Ballad" },
+ { "118", "Rhythmic Soul" },
+ { "119", "Freestyle" },
+ { "120", "Duet" },
+ { "121", "Punk Rock" },
+ { "122", "Drum Solo" },
+ { "123", "A capella" }, // ! Should be Acapella, keep space for compatibility with old code.
+ { "124", "Euro-House" },
+ { "125", "Dance Hall" },
+ { 0, 0 }
+};
+
// =================================================================================================
-bool ID3_Support::InitializeGlobals()
+bool InitializeGlobals()
{
+
+ kMapID3GenreCodeToName = new ID3GenreMap;
+ if ( kMapID3GenreCodeToName == 0 ) return false;
+ kMapID3GenreNameToCode = new ID3GenreMap;
+ if ( kMapID3GenreNameToCode == 0 ) return false;
+
+ ID3GenreMap::value_type newValue;
+
+ size_t i;
+
+ for ( i = 0; kNumberedGenres[i].code != 0; ++i ) {
+ XMP_Assert ( (long)i == strtol ( kNumberedGenres[i].code, 0, 10 ) );
+ ID3GenreMap::value_type code2Name ( kNumberedGenres[i].code, kNumberedGenres[i].name );
+ kMapID3GenreCodeToName->insert ( kMapID3GenreCodeToName->end(), code2Name );
+ ID3GenreMap::value_type name2Code ( kNumberedGenres[i].name, kNumberedGenres[i].code );
+ kMapID3GenreNameToCode->insert ( kMapID3GenreNameToCode->end(), name2Code );
+ }
+
+ numberedGenreCount = i; // Used in ID3v1Tag::read and write.
+
+ for ( i = 0; kAbbreviatedGenres[i].code != 0; ++i ) {
+ ID3GenreMap::value_type code2Name ( kAbbreviatedGenres[i].code, kAbbreviatedGenres[i].name );
+ kMapID3GenreCodeToName->insert ( kMapID3GenreCodeToName->end(), code2Name );
+ ID3GenreMap::value_type name2Code ( kAbbreviatedGenres[i].name, kAbbreviatedGenres[i].code );
+ kMapID3GenreNameToCode->insert ( kMapID3GenreNameToCode->end(), name2Code );
+ }
+
return true;
+
+} // InitializeGlobals
+
+// =================================================================================================
+
+void TerminateGlobals()
+{
+ delete kMapID3GenreCodeToName;
+ delete kMapID3GenreNameToCode;
+ kMapID3GenreCodeToName = kMapID3GenreNameToCode = 0;
+}
+
+// =================================================================================================
+// GenreUtils
+// =================================================================================================
+
+const char * GenreUtils::FindGenreName ( const std::string & code )
+{
+ // Lookup a genre code and return its name if known, otherwise 0.
+
+ const char * name = 0;
+ ID3GenreMap::iterator mapPos = kMapID3GenreCodeToName->find ( code.c_str() );
+ if ( mapPos != kMapID3GenreCodeToName->end() ) name = mapPos->second;
+ return name;
+
+}
+
+// =================================================================================================
+
+const char * GenreUtils::FindGenreCode ( const std::string & name )
+{
+ // Lookup a genre name and return its code if known, otherwise 0.
+
+ const char * code = 0;
+ ID3GenreMap::iterator mapPos = kMapID3GenreNameToCode->find ( name.c_str() );
+ if ( mapPos != kMapID3GenreNameToCode->end() ) code = mapPos->second;
+ return code;
+
+}
+
+// =================================================================================================
+
+static void StripOutsideSpaces ( std::string * value )
+{
+ size_t length = value->size();
+ size_t first, last;
+
+ for ( first = 0; ((first < length) && ((*value)[first] == ' ')); ++first ) {}
+ if ( first == length ) { value->erase(); return; }
+ XMP_Assert ( (first < length) && ((*value)[first] != ' ') );
+
+ for ( last = length-1; ((last > first) && ((*value)[last] == ' ')); --last ) {}
+ if ( (first == 0) && (last == length-1) ) return;
+
+ size_t newLen = last - first + 1;
+ if ( newLen < length ) *value = value->substr ( first, newLen );
+
+}
+
+// =================================================================================================
+
+void GenreUtils::ConvertGenreToXMP ( const char * id3Genre, std::string * xmpGenre )
+{
+ // If the first character of TCON is not '(' then the entire TCON value is taken as the genre
+ // name and the suffix is empty.
+ //
+ // If the first character of TCON is '(' then the string up to ')' (or the end) is taken as the
+ // coded genre name. The rest of the TCON value after ')' is taken as the suffix.
+ //
+ // If the coded name is known then the corresponsing full name is used as the genre name, with
+ // no parens.
+ //
+ // If the coded name is not known then the coded name with parens is used as the genre name.
+ //
+ // The value of xmpDM:genre begins with the genre name. If the suffix is not empty we append
+ // "; " and the suffix. The known coded genre names currently do not use semicolon.
+ //
+ // Keeping the parens when importing unknown coded names might seem odd. But it preserves the
+ // ID3 syntax when exporting. Otherwise we would import "(XX)" and export "XX". We don't add
+ // parens all the time on export, that would import "Blues/R&B" and export "(Blues/R&B)".
+
+ xmpGenre->erase();
+ size_t id3Length = strlen ( id3Genre );
+ if ( id3Length == 0 ) return;
+
+ if ( id3Genre[0] != '(' ) {
+ // No left paren, take the whole TCON value as the XMP value.
+ xmpGenre->assign ( id3Genre, id3Length );
+ StripOutsideSpaces ( xmpGenre );
+ return;
+ }
+
+ // The first character of TCON is '(', process the coded part and the suffix.
+
+ size_t codeEnd;
+ std::string genreCode, suffix;
+
+ for ( codeEnd = 1; ((codeEnd < id3Length) && (id3Genre[codeEnd] != ')')); ++codeEnd ) {}
+ genreCode.assign ( &id3Genre[1], codeEnd-1 );
+ if ( codeEnd < id3Length ) suffix.assign ( &id3Genre[codeEnd+1], id3Length-codeEnd-1 );
+
+ StripOutsideSpaces ( &genreCode );
+ StripOutsideSpaces ( &suffix );
+
+ if ( genreCode.empty() ) {
+
+ (*xmpGenre) = suffix; // Degenerate case of "()suffix", treat as if "suffix".
+
+ } else {
+
+ const char * fullName = FindGenreName ( genreCode );
+
+ if ( fullName != 0 ) {
+ (*xmpGenre) = fullName;
+ } else {
+ (*xmpGenre) = '(';
+ (*xmpGenre) += genreCode;
+ (*xmpGenre) += ')';
+ }
+
+ if ( ! suffix.empty() ) {
+ (*xmpGenre) += "; ";
+ (*xmpGenre) += suffix;
+ }
+
+ }
+
}
// =================================================================================================
-void ID3_Support::TerminateGlobals()
+void GenreUtils::ConvertGenreToID3 ( const char * xmpGenre, std::string * id3Genre )
{
- // nothing yet
+ // The genre name is the xmpDM:genre value up to ';', with spaces at the front or back removed.
+ // The suffix is everything after ';', also with spaces at the front or back removed.
+ //
+ // If the genre name is known, it is replaced by the coded name in parens.
+ //
+ // The TCON value is the genre name plus the suffix. If the genre name does not end in ')' then
+ // a space is inserted.
+
+ id3Genre->erase();
+ size_t xmpLength = strlen ( xmpGenre );
+ if ( xmpLength == 0 ) return;
+
+ size_t nameEnd;
+ std::string genreName, suffix;
+
+ for ( nameEnd = 0; ((nameEnd < xmpLength) && (xmpGenre[nameEnd] != ';')); ++nameEnd ) {}
+ genreName.assign ( xmpGenre, nameEnd );
+ if ( nameEnd < xmpLength ) suffix.assign ( &xmpGenre[nameEnd+1], xmpLength-nameEnd-1 );
+
+ StripOutsideSpaces ( &genreName );
+ StripOutsideSpaces ( &suffix );
+
+ if ( genreName.empty() ) {
+
+ (*id3Genre) = suffix; // Degenerate case of "; suffix", treat as if "suffix".
+
+ } else {
+
+ const char * codedName = FindGenreCode ( genreName );
+ if ( codedName != 0 ) {
+ genreName = '(';
+ genreName += codedName;
+ genreName += ')';
+ }
+
+ (*id3Genre) = genreName;
+ if ( ! suffix.empty() ) {
+ if ( genreName[genreName.size()-1] != ')' ) (*id3Genre) += ' ';
+ (*id3Genre) += suffix;
+ }
+
+ }
+
}
// =================================================================================================
// ID3Header
// =================================================================================================
-bool ID3_Support::ID3Header::read ( XMP_IO* file )
+bool ID3Header::read ( XMP_IO* file )
{
XMP_Assert ( sizeof(fields) == kID3_TagHeaderSize );
@@ -66,10 +404,10 @@ bool ID3_Support::ID3Header::read ( XMP_IO* file )
// =================================================================================================
-void ID3_Support::ID3Header::write ( XMP_IO* file, XMP_Int64 tagSize )
+void ID3Header::write ( XMP_IO* file, XMP_Int64 tagSize )
{
- XMP_Assert ( (kID3_TagHeaderSize <= tagSize) && (tagSize < 256*1024*1024) ); // 256 MB limit due to synching.
+ XMP_Assert ( ((XMP_Int64)kID3_TagHeaderSize <= tagSize) && (tagSize < 256*1024*1024) ); // 256 MB limit due to synching.
XMP_Uns32 synchSize = int32ToSynch ( (XMP_Uns32)tagSize - kID3_TagHeaderSize );
PutUns32BE ( synchSize, &this->fields[ID3Header::o_size] );
@@ -83,7 +421,7 @@ void ID3_Support::ID3Header::write ( XMP_IO* file, XMP_Int64 tagSize )
#define frameDefaults id(0), flags(0), content(0), contentSize(0), active(true), changed(false)
-ID3_Support::ID3v2Frame::ID3v2Frame() : frameDefaults
+ID3v2Frame::ID3v2Frame() : frameDefaults
{
XMP_Assert ( sizeof(fields) == kV23_FrameHeaderSize ); // Only need to do this in one place.
memset ( this->fields, 0, kV23_FrameHeaderSize );
@@ -91,7 +429,7 @@ ID3_Support::ID3v2Frame::ID3v2Frame() : frameDefaults
// =================================================================================================
-ID3_Support::ID3v2Frame::ID3v2Frame ( XMP_Uns32 id ) : frameDefaults
+ID3v2Frame::ID3v2Frame ( XMP_Uns32 id ) : frameDefaults
{
memset ( this->fields, 0, kV23_FrameHeaderSize );
this->id = id;
@@ -100,7 +438,7 @@ ID3_Support::ID3v2Frame::ID3v2Frame ( XMP_Uns32 id ) : frameDefaults
// =================================================================================================
-void ID3_Support::ID3v2Frame::release()
+void ID3v2Frame::release()
{
if ( this->content != 0 ) delete this->content;
this->content = 0;
@@ -109,7 +447,7 @@ void ID3_Support::ID3v2Frame::release()
// =================================================================================================
-void ID3_Support::ID3v2Frame::setFrameValue ( const std::string& rawvalue, bool needDescriptor,
+void ID3v2Frame::setFrameValue ( const std::string& rawvalue, bool needDescriptor,
bool utf16, bool isXMPPRIVFrame, bool needEncodingByte )
{
@@ -170,7 +508,7 @@ void ID3_Support::ID3v2Frame::setFrameValue ( const std::string& rawvalue, bool
// =================================================================================================
-XMP_Int64 ID3_Support::ID3v2Frame::read ( XMP_IO* file, XMP_Uns8 majorVersion )
+XMP_Int64 ID3v2Frame::read ( XMP_IO* file, XMP_Uns8 majorVersion )
{
XMP_Assert ( (2 <= majorVersion) && (majorVersion <= 4) );
@@ -213,7 +551,7 @@ XMP_Int64 ID3_Support::ID3v2Frame::read ( XMP_IO* file, XMP_Uns8 majorVersion )
// =================================================================================================
-void ID3_Support::ID3v2Frame::write ( XMP_IO* file, XMP_Uns8 majorVersion )
+void ID3v2Frame::write ( XMP_IO* file, XMP_Uns8 majorVersion )
{
XMP_Assert ( (2 <= majorVersion) && (majorVersion <= 4) );
@@ -236,7 +574,7 @@ void ID3_Support::ID3v2Frame::write ( XMP_IO* file, XMP_Uns8 majorVersion )
// =================================================================================================
-bool ID3_Support::ID3v2Frame::advancePastCOMMDescriptor ( XMP_Int32& pos )
+bool ID3v2Frame::advancePastCOMMDescriptor ( XMP_Int32& pos )
{
if ( (this->contentSize - pos) <= 3 ) return false; // silent error, no room left behing language tag
@@ -267,7 +605,7 @@ bool ID3_Support::ID3v2Frame::advancePastCOMMDescriptor ( XMP_Int32& pos )
// =================================================================================================
-bool ID3_Support::ID3v2Frame::getFrameValue ( XMP_Uns8 majorVersion, XMP_Uns32 logicalID, std::string* utf8string )
+bool ID3v2Frame::getFrameValue ( XMP_Uns8 majorVersion, XMP_Uns32 logicalID, std::string* utf8string )
{
XMP_Assert ( (this->content != 0) && (this->contentSize >= 0) && (this->contentSize < 20*1024*1024) );
@@ -349,9 +687,9 @@ bool ID3_Support::ID3v2Frame::getFrameValue ( XMP_Uns8 majorVersion, XMP_Uns32 l
// ID3v1Tag
// =================================================================================================
-bool ID3_Support::ID3v1Tag::read ( XMP_IO* file, SXMPMeta* meta )
+bool ID3v1Tag::read ( XMP_IO* file, SXMPMeta* meta )
{
- // returns returns true, if ID3v1 (or v1.1) exists, otherwise false, sets XMP properties en route
+ // Returns true if ID3v1 (or v1.1) exists, otherwise false, sets XMP properties en route.
if ( file->Length() <= 128 ) return false; // ensure sufficient room
file->Seek ( -128, kXMP_SeekFromEnd );
@@ -410,8 +748,13 @@ bool ID3_Support::ID3v1Tag::read ( XMP_IO* file, SXMPMeta* meta )
}
XMP_Uns8 genreNo = XIO::ReadUns8 ( file );
- if ( genreNo < 127 ) {
- meta->SetProperty ( kXMP_NS_DM, "genre", Genres[genreNo] );
+ if ( genreNo < numberedGenreCount ) {
+ meta->SetProperty ( kXMP_NS_DM, "genre", kNumberedGenres[genreNo].name );
+ } else {
+ char buffer[4]; // AUDIT: Big enough for UInt8.
+ snprintf ( buffer, 4, "%d", genreNo );
+ XMP_Assert ( strlen(buffer) == 3 ); // Should be in the range 126..255.
+ meta->SetProperty ( kXMP_NS_DM, "genre", buffer );
}
return true; // ID3Tag found
@@ -420,7 +763,25 @@ bool ID3_Support::ID3v1Tag::read ( XMP_IO* file, SXMPMeta* meta )
// =================================================================================================
-void ID3_Support::ID3v1Tag::write ( XMP_IO* file, SXMPMeta* meta )
+static inline bool GetDecimalUns32 ( const char * str, XMP_Uns32 * bin )
+{
+ XMP_Assert ( bin != 0 );
+ if ( (str == 0) || (str[0] == 0) ) return false;
+
+ *bin = 0;
+ for ( size_t i = 0; str[i] != 0; ++i ) {
+ char ch = str[i];
+ if ( (ch < '0') || (ch > '9') ) return false;
+ *bin = (*bin * 10) + (ch - '0');
+ }
+
+ return true;
+
+}
+
+// =================================================================================================
+
+void ID3v1Tag::write ( XMP_IO* file, SXMPMeta* meta )
{
std::string zeros ( 128, '\0' );
@@ -470,21 +831,23 @@ void ID3_Support::ID3v1Tag::write ( XMP_IO* file, SXMPMeta* meta )
if ( meta->GetProperty ( kXMP_NS_DM, "genre", &utf8, 0 ) ) {
- XMP_Uns8 genreNo = 0;
-
- int i;
- const char* genreCString = utf8.c_str();
- for ( i = 0; i < 127; ++i ) {
- if ( (strlen(genreCString) == strlen(Genres[i])) && //fixing buggy stricmp behaviour on PPC
- (stricmp ( genreCString, Genres[i] ) == 0 ) ) {
- genreNo = i; // found
- break;
+ // Write the first genre code as a UInt8.
+ size_t nameEnd;
+ std::string name;
+
+ for ( nameEnd = 0; ((nameEnd < utf8.size()) && (utf8[nameEnd] != ';')); ++nameEnd ) {}
+ name.assign ( utf8.c_str(), nameEnd );
+ const char * code = GenreUtils::FindGenreCode ( name );
+
+ if ( code != 0 ) {
+ XMP_Uns32 value;
+ bool ok = GetDecimalUns32 ( code, &value );
+ if ( ok && (value <= 255) ) {
+ file->Seek ( (-128 + 127), kXMP_SeekFromEnd );
+ XIO::WriteUns8 ( file, (XMP_Uns8)value );
}
}
- file->Seek ( (-128 + 127), kXMP_SeekFromEnd );
- XIO::WriteUns8 ( file, genreNo );
-
}
if ( meta->GetProperty ( kXMP_NS_DM, "trackNumber", &utf8, kXMP_NoOptions ) ) {
@@ -502,3 +865,7 @@ void ID3_Support::ID3v1Tag::write ( XMP_IO* file, SXMPMeta* meta )
}
} // ID3v1Tag::write
+
+// =================================================================================================
+
+}; // namespace ID3_Support
diff --git a/XMPFiles/source/FormatSupport/ID3_Support.hpp b/XMPFiles/source/FormatSupport/ID3_Support.hpp
index 43b917d..5228fd7 100644
--- a/XMPFiles/source/FormatSupport/ID3_Support.hpp
+++ b/XMPFiles/source/FormatSupport/ID3_Support.hpp
@@ -42,137 +42,6 @@
namespace ID3_Support {
- // Genres
- static char Genres[128][32] = {
- "Blues", // 0
- "Classic Rock", // 1
- "Country", // 2
- "Dance",
- "Disco",
- "Funk",
- "Grunge",
- "Hip-Hop",
- "Jazz", // 8
- "Metal",
- "New Age", // 10
- "Oldies",
- "Other", // 12
- "Pop",
- "R&B",
- "Rap",
- "Reggae", // 16
- "Rock", // 17
- "Techno",
- "Industrial",
- "Alternative",
- "Ska",
- "Death Metal",
- "Pranks",
- "Soundtrack", // 24
- "Euro-Techno",
- "Ambient",
- "Trip-Hop",
- "Vocal",
- "Jazz+Funk",
- "Fusion",
- "Trance",
- "Classical", // 32
- "Instrumental",
- "Acid",
- "House",
- "Game",
- "Sound Clip",
- "Gospel",
- "Noise",
- "AlternRock",
- "Bass",
- "Soul", //42
- "Punk",
- "Space",
- "Meditative",
- "Instrumental Pop",
- "Instrumental Rock",
- "Ethnic",
- "Gothic",
- "Darkwave",
- "Techno-Industrial",
- "Electronic",
- "Pop-Folk",
- "Eurodance",
- "Dream",
- "Southern Rock",
- "Comedy",
- "Cult",
- "Gangsta",
- "Top 40",
- "Christian Rap",
- "Pop/Funk",
- "Jungle",
- "Native American",
- "Cabaret",
- "New Wave", // 66
- "Psychadelic",
- "Rave",
- "Showtunes",
- "Trailer",
- "Lo-Fi",
- "Tribal",
- "Acid Punk",
- "Acid Jazz",
- "Polka",
- "Retro",
- "Musical",
- "Rock & Roll",
- "Hard Rock",
- "Folk", // 80
- "Folk-Rock",
- "National Folk",
- "Swing",
- "Fast Fusion",
- "Bebob",
- "Latin",
- "Revival",
- "Celtic",
- "Bluegrass", // 89
- "Avantgarde",
- "Gothic Rock",
- "Progressive Rock",
- "Psychedelic Rock",
- "Symphonic Rock",
- "Slow Rock",
- "Big Band",
- "Chorus",
- "Easy Listening",
- "Acoustic",
- "Humour", // 100
- "Speech",
- "Chanson",
- "Opera",
- "Chamber Music",
- "Sonata",
- "Symphony",
- "Booty Bass",
- "Primus",
- "Porn Groove",
- "Satire",
- "Slow Jam",
- "Club",
- "Tango",
- "Samba",
- "Folklore",
- "Ballad",
- "Power Ballad",
- "Rhythmic Soul",
- "Freestyle",
- "Duet",
- "Punk Rock",
- "Drum Solo",
- "A capella",
- "Euro-House",
- "Dance Hall",
- "Unknown" // 126
- };
-
// =============================================================================================
inline XMP_Int32 synchToInt32 ( XMP_Uns32 rawDataBE ) {
@@ -191,8 +60,21 @@ namespace ID3_Support {
// =============================================================================================
- bool InitializeGlobals();
+ bool InitializeGlobals(); // Initialize and terminate the known genre maps.
void TerminateGlobals();
+
+ // =============================================================================================
+
+ namespace GenreUtils {
+
+ void ConvertGenreToXMP ( const char * id3Genre, std::string * xmpGenre );
+ void ConvertGenreToID3 ( const char * xmpGenre, std::string * id3Genre );
+
+ // Internal utilities, exposed for unit testing:
+ const char * FindGenreName ( const std::string & code );
+ const char * FindGenreCode ( const std::string & name );
+
+ };
// =============================================================================================
diff --git a/XMPFiles/source/FormatSupport/IFF/Chunk.cpp b/XMPFiles/source/FormatSupport/IFF/Chunk.cpp
index a2de741..9e1407f 100644
--- a/XMPFiles/source/FormatSupport/IFF/Chunk.cpp
+++ b/XMPFiles/source/FormatSupport/IFF/Chunk.cpp
@@ -538,6 +538,52 @@ XMP_Uns64 Chunk::calculateSize( bool setOriginal /*= false*/ )
//-----------------------------------------------------------------------------
//
+// Chunk::calculateWriteSize(...)
+//
+// Purpose: Calculate the size of the chunks that are dirty including the size
+// of its children
+//
+//-----------------------------------------------------------------------------
+
+XMP_Int64 Chunk::calculateWriteSize( ) const
+{
+ XMP_Int64 size=0;
+ if (hasChanged())
+ {
+ size+=(sizeof(XMP_Uns32)*2);
+ if (mChunkMode == CHUNK_LEAF)
+ {
+ if ( mSize % 2 == 1 )
+ {
+ // for odd file sizes, a pad byte is written
+ size+=(mSize+1);
+ }
+ else
+ {
+ size+=mSize;
+ }
+ }
+ else // mChunkMode == CHUNK_NODE
+ {
+ // writes type if defined
+ if (mChunkId.type != kType_NONE)
+ {
+ size+=sizeof(XMP_Uns32);
+ }
+
+ // calls calculateWriteSize recursively on it's children
+ for( ConstChunkIterator iter = mChildren.begin(); iter != mChildren.end(); iter++ )
+ {
+ size+=(*iter)->calculateWriteSize( );
+ }
+ }
+ }
+
+ return size;
+}
+
+//-----------------------------------------------------------------------------
+//
// Chunk::setOffset(...)
//
// Purpose: Adjust the offset that this chunk has within the file
diff --git a/XMPFiles/source/FormatSupport/IFF/Chunk.h b/XMPFiles/source/FormatSupport/IFF/Chunk.h
index 2d170ed..ef2ba47 100644
--- a/XMPFiles/source/FormatSupport/IFF/Chunk.h
+++ b/XMPFiles/source/FormatSupport/IFF/Chunk.h
@@ -195,6 +195,12 @@ class Chunk : public IChunkData,
*/
inline void setSize( XMP_Uns64 newSize, bool setOriginal = false ) { mDirty = mSize != newSize; mSize = newSize; mOriginalSize = setOriginal ? newSize : mOriginalSize; }
+ /**
+ * Calculate the size of the chunks that are dirty including the size
+ * of its children
+ */
+ XMP_Int64 calculateWriteSize( ) const;
+
/**
* Calculate the size of this chunks based on its children sizes.
* If this chunk has no children then no new size will be calculated.
diff --git a/XMPFiles/source/FormatSupport/IFF/ChunkController.cpp b/XMPFiles/source/FormatSupport/IFF/ChunkController.cpp
index 786cbd1..2a5a322 100644
--- a/XMPFiles/source/FormatSupport/IFF/ChunkController.cpp
+++ b/XMPFiles/source/FormatSupport/IFF/ChunkController.cpp
@@ -321,7 +321,7 @@ void ChunkController::parseFile( XMP_IO* stream, XMP_OptionBits* options /* = NU
// Purpose: Called by the handler to write back the changes to the file.
//
//-----------------------------------------------------------------------------
-void ChunkController::writeFile( XMP_IO* stream )
+void ChunkController::writeFile( XMP_IO* stream ,XMP_ProgressTracker * progressTracker )
{
//
@@ -344,11 +344,27 @@ void ChunkController::writeFile( XMP_IO* stream )
// NOTE: the padding bytes can be ignored, as the top-level chunk is always a node, not a leaf.
Chunk* lastChild = mRoot->getChildAt(mRoot->numChildren() - 1);
XMP_Uns64 newFileSize = lastChild->getOffset() + lastChild->getSize(true);
+ if ( progressTracker != 0 )
+ {
+ float fileWriteSize=0.0f;
+ for( XMP_Uns32 i = 0; i < mRoot->numChildren(); i++ )
+ {
+ Chunk* child = mRoot->getChildAt(i);
+ fileWriteSize+=child->calculateWriteSize( );
+ }
+ XMP_Assert ( progressTracker->WorkInProgress() );
+ progressTracker->AddTotalWork ( fileWriteSize );
+ }
// Move garbage tail after last top-level chunk,
// BEFORE the chunks are written -- in case the file shrinks
if (mTrailingGarbageSize > 0 && newFileSize != mTrailingGarbageOffset)
{
+ if ( progressTracker != 0 )
+ {
+ XMP_Assert ( progressTracker->WorkInProgress() );
+ progressTracker->AddTotalWork ( (float)mTrailingGarbageSize );
+ }
XIO::Move( stream, mTrailingGarbageOffset, stream, newFileSize, mTrailingGarbageSize );
newFileSize += mTrailingGarbageSize;
}
diff --git a/XMPFiles/source/FormatSupport/IFF/ChunkController.h b/XMPFiles/source/FormatSupport/IFF/ChunkController.h
index 933da36..52dea42 100644
--- a/XMPFiles/source/FormatSupport/IFF/ChunkController.h
+++ b/XMPFiles/source/FormatSupport/IFF/ChunkController.h
@@ -16,6 +16,7 @@
#include "public/include/XMP_IO.hpp"
#include "source/XMP_LibUtils.hpp"
+#include "source/XMP_ProgressTracker.hpp"
#include "XMPFiles/source/FormatSupport/IFF/ChunkPath.h"
#include "XMPFiles/source/FormatSupport/IFF/IChunkBehavior.h"
@@ -105,8 +106,9 @@ class ChunkController
* 2. write the changed chunks to the file
*
* @param stream the open [file] stream for writing, the file pointer must be at the beginning
+ * @param progressTracker Progress tracker to track the file write progress and reporting it to client
*/
- void writeFile( XMP_IO* stream );
+ void writeFile( XMP_IO* stream,XMP_ProgressTracker * progressTracker );
/**
* Returns the first (or last) Chunk that matches the passed path.
diff --git a/XMPFiles/source/FormatSupport/ISOBaseMedia_Support.hpp b/XMPFiles/source/FormatSupport/ISOBaseMedia_Support.hpp
index 115a0e8..dd2fbea 100644
--- a/XMPFiles/source/FormatSupport/ISOBaseMedia_Support.hpp
+++ b/XMPFiles/source/FormatSupport/ISOBaseMedia_Support.hpp
@@ -33,6 +33,7 @@ namespace ISOMedia {
k_mp41 = 0x6D703431UL, // Compatible brand codes
k_mp42 = 0x6D703432UL,
k_f4v = 0x66347620UL,
+ k_avc1 = 0x61766331UL,
k_qt = 0x71742020UL,
k_moov = 0x6D6F6F76UL, // Container Box, no version/flags.
@@ -58,6 +59,9 @@ namespace ISOMedia {
k_stsc = 0x73747363UL,
k_stco = 0x7374636FUL,
k_co64 = 0x636F3634UL,
+ k_dinf = 0x64696E66UL,
+ k_dref = 0x64726566UL,
+ k_alis = 0x616C6973UL,
k_meta = 0x6D657461UL, // Types for the iTunes metadata boxes.
k_ilst = 0x696C7374UL,
diff --git a/XMPFiles/source/FormatSupport/MOOV_Support.cpp b/XMPFiles/source/FormatSupport/MOOV_Support.cpp
index b9b4996..959df44 100644
--- a/XMPFiles/source/FormatSupport/MOOV_Support.cpp
+++ b/XMPFiles/source/FormatSupport/MOOV_Support.cpp
@@ -272,6 +272,7 @@ void MOOV_Manager::ParseNestedBoxes ( BoxNode * parentNode, const std::string &
case ISOMedia::k_edts : pathSuffix = "/edts"; break;
case ISOMedia::k_mdia : pathSuffix = "/mdia"; break;
case ISOMedia::k_minf : pathSuffix = "/minf"; break;
+ case ISOMedia::k_dinf : pathSuffix = "/dinf"; break;
case ISOMedia::k_stbl : pathSuffix = "/stbl"; break;
}
if ( pathSuffix != 0 ) {
diff --git a/XMPFiles/source/FormatSupport/MOOV_Support.hpp b/XMPFiles/source/FormatSupport/MOOV_Support.hpp
index 814a8bb..1dace2a 100644
--- a/XMPFiles/source/FormatSupport/MOOV_Support.hpp
+++ b/XMPFiles/source/FormatSupport/MOOV_Support.hpp
@@ -104,7 +104,7 @@ public:
// ---------------------------------------------------------------------------------------------
- #pragma pack (1) // ! These must match the file layout!
+ #pragma pack (push, 1) // ! These must match the file layout!
struct Content_mvhd_0 {
XMP_Uns32 vFlags; // 0
@@ -164,6 +164,12 @@ public:
XMP_Uns32 sampleDescrID; // 8
}; // 12
+ #pragma pack( pop )
+
+#if SUNOS_SPARC
+ #pragma pack( )
+#endif //#if SUNOS_SPARC
+
// ---------------------------------------------------------------------------------------------
MOOV_Manager() : fileMode(0)
diff --git a/XMPFiles/source/FormatSupport/PSIR_FileWriter.cpp b/XMPFiles/source/FormatSupport/PSIR_FileWriter.cpp
index aecfec1..5d06220 100644
--- a/XMPFiles/source/FormatSupport/PSIR_FileWriter.cpp
+++ b/XMPFiles/source/FormatSupport/PSIR_FileWriter.cpp
@@ -273,12 +273,6 @@ void PSIR_FileWriter::ParseMemoryResources ( const void* data, XMP_Uns32 length,
void PSIR_FileWriter::ParseFileResources ( XMP_IO* fileRef, XMP_Uns32 length )
{
- bool ok;
-
- this->DeleteExistingInfo();
- this->fileParsed = true;
- if ( length == 0 ) return;
-
// Parse the image resource block. We're using a map keyed by ID, so only one resource of each
// ID is recognized. Redundant resources are not legit, but have been seen in the field. In
// particular, one case has been seen of a duplicate IIM block with one empty. In general we
@@ -286,45 +280,54 @@ void PSIR_FileWriter::ParseFileResources ( XMP_IO* fileRef, XMP_Uns32 length )
// taken though if the current one is empty.
// ! Don't use map[id] to lookup, that creates a default entry if none exists!
+
+ // PSIR layout:
+ // - Uns32 type, usually '8BIM'
+ // - Uns16 ID
+ // - PString name
+ // - Uns8 optional pad for even alignment
+ // - Uns32 data size
+ // - data
+ // - Uns8 optional pad for even alignment
+
+ static const size_t kMinPSIRSize = 12; // 4+2+1+1+4
+
+ this->DeleteExistingInfo();
+ this->fileParsed = true;
+ if ( length == 0 ) return;
- IOBuffer ioBuf;
- ioBuf.filePos = fileRef->Offset();
-
- XMP_Int64 psirOrigin = ioBuf.filePos; // Need this to determine the resource data offsets.
- XMP_Int64 fileEnd = ioBuf.filePos + length;
+ XMP_Int64 psirOrigin = fileRef->Offset(); // Need this to determine the resource data offsets.
+ XMP_Int64 fileEnd = psirOrigin + length;
- std::string rsrcPName;
+ char nameBuffer [260]; // The name is a PString, at 1+255+1 including length and pad.
- while ( (ioBuf.filePos + (ioBuf.ptr - ioBuf.data)) < fileEnd ) {
+ while ( fileRef->Offset() < fileEnd ) {
- ok = CheckFileSpace ( fileRef, &ioBuf, 12 ); // The minimal image resource takes 12 bytes.
- if ( ! ok ) break; // Bad image resource. Throw instead?
+ if ( ! XIO::CheckFileSpace ( fileRef, kMinPSIRSize ) ) break; // Bad image resource.
- XMP_Int64 thisRsrcPos = ioBuf.filePos + (ioBuf.ptr - ioBuf.data);
+ XMP_Int64 thisRsrcPos = fileRef->Offset();
- XMP_Uns32 type = GetUns32BE(ioBuf.ptr);
- XMP_Uns16 id = GetUns16BE(ioBuf.ptr+4);
- ioBuf.ptr += 6; // Advance to the resource name.
+ XMP_Uns32 type = XIO::ReadUns32_BE ( fileRef );
+ XMP_Uns16 id = XIO::ReadUns16_BE ( fileRef );
- XMP_Uns16 nameLen = ioBuf.ptr[0]; // ! The length for the Pascal string.
+ XMP_Uns8 nameLen = XIO::ReadUns8 ( fileRef ); // ! The length for the Pascal string.
XMP_Uns16 paddedLen = (nameLen + 2) & 0xFFFE; // ! Round up to an even total. Yes, +2!
- ok = CheckFileSpace ( fileRef, &ioBuf, paddedLen+4 ); // Get the name text and the data length.
- if ( ! ok ) break; // Bad image resource. Throw instead?
+ if ( ! XIO::CheckFileSpace ( fileRef, paddedLen+4 ) ) break; // Bad image resource.
- if ( nameLen > 0 ) rsrcPName.assign ( (char*)(ioBuf.ptr), paddedLen ); // ! Include the length byte and pad.
+ nameBuffer[0] = nameLen;
+ fileRef->ReadAll ( &nameBuffer[1], paddedLen-1 ); // Include the pad byte, present for zero nameLen.
- ioBuf.ptr += paddedLen; // Move to the data length.
- XMP_Uns32 dataLen = GetUns32BE(ioBuf.ptr);
+ XMP_Uns32 dataLen = XIO::ReadUns32_BE ( fileRef );
XMP_Uns32 dataTotal = ((dataLen + 1) & 0xFFFFFFFEUL); // Round up to an even total.
- ioBuf.ptr += 4; // Advance to the resource data.
+ if ( ! XIO::CheckFileSpace ( fileRef, dataTotal ) ) break; // Bad image resource.
- XMP_Int64 thisDataPos = ioBuf.filePos + (ioBuf.ptr - ioBuf.data);
+ XMP_Int64 thisDataPos = fileRef->Offset();
XMP_Int64 nextRsrcPos = thisDataPos + dataTotal;
if ( type != k8BIM ) {
XMP_Uns32 fullRsrcLen = (XMP_Uns32) (nextRsrcPos - thisRsrcPos);
this->otherRsrcs.push_back ( OtherRsrcInfo ( (XMP_Uns32)thisRsrcPos, fullRsrcLen ) );
- MoveToOffset ( fileRef, nextRsrcPos, &ioBuf );
+ fileRef->Seek ( nextRsrcPos, kXMP_SeekFromStart );
continue;
}
@@ -335,7 +338,7 @@ void PSIR_FileWriter::ParseFileResources ( XMP_IO* fileRef, XMP_Uns32 length )
} else if ( (rsrcPos->second.dataLen == 0) && (newInfo.dataLen != 0) ) {
rsrcPos->second = newInfo;
} else {
- MoveToOffset ( fileRef, nextRsrcPos, &ioBuf );
+ fileRef->Seek ( nextRsrcPos, kXMP_SeekFromStart );
continue;
}
InternalRsrcInfo* rsrcPtr = &rsrcPos->second;
@@ -345,29 +348,17 @@ void PSIR_FileWriter::ParseFileResources ( XMP_IO* fileRef, XMP_Uns32 length )
if ( nameLen > 0 ) {
rsrcPtr->rsrcName = (XMP_Uns8*) malloc ( paddedLen );
if ( rsrcPtr->rsrcName == 0 ) XMP_Throw ( "Out of memory", kXMPErr_NoMemory );
- memcpy ( (void*)rsrcPtr->rsrcName, rsrcPName.c_str(), paddedLen ); // AUDIT: Safe, allocated enough bytes above.
+ memcpy ( (void*)rsrcPtr->rsrcName, nameBuffer, paddedLen ); // AUDIT: Safe, allocated enough bytes above.
}
if ( ! IsMetadataImgRsrc ( id ) ) {
- MoveToOffset ( fileRef, nextRsrcPos, &ioBuf );
+ fileRef->Seek ( nextRsrcPos, kXMP_SeekFromStart );
continue;
}
- rsrcPtr->dataPtr = malloc ( dataLen ); // ! Allocate after the IsMetadataImgRsrc check.
+ rsrcPtr->dataPtr = malloc ( dataTotal ); // ! Allocate after the IsMetadataImgRsrc check.
if ( rsrcPtr->dataPtr == 0 ) XMP_Throw ( "Out of memory", kXMPErr_NoMemory );
-
- if ( dataTotal <= kIOBufferSize ) {
- // The image resource data fits within the I/O buffer.
- ok = CheckFileSpace ( fileRef, &ioBuf, dataTotal );
- if ( ! ok ) break; // Bad image resource. Throw instead?
- memcpy ( (void*)rsrcPtr->dataPtr, ioBuf.ptr, dataLen ); // AUDIT: Safe, malloc'ed dataLen bytes above.
- ioBuf.ptr += dataTotal; // ! Add the rounded length.
- } else {
- // The image resource data is bigger than the I/O buffer.
- fileRef->Seek ( (ioBuf.filePos + (ioBuf.ptr - ioBuf.data)), kXMP_SeekFromStart );
- fileRef->ReadAll ( (void*)rsrcPtr->dataPtr, dataLen );
- FillBuffer ( fileRef, nextRsrcPos, &ioBuf );
- }
+ fileRef->ReadAll ( (void*)rsrcPtr->dataPtr, dataTotal );
}
@@ -492,11 +483,10 @@ XMP_Uns32 PSIR_FileWriter::UpdateMemoryResources ( void** dataPtr )
// ====================================
XMP_Uns32 PSIR_FileWriter::UpdateFileResources ( XMP_IO* sourceRef, XMP_IO* destRef,
- void * _ioBuf, XMP_AbortProc abortProc, void * abortArg )
+ XMP_AbortProc abortProc, void * abortArg,
+ XMP_ProgressTracker* progressTracker )
{
- IgnoreParam(_ioBuf);
const XMP_Uns32 zero32 = 0;
-
const bool checkAbort = (abortProc != 0);
struct RsrcHeader {
@@ -507,9 +497,29 @@ XMP_Uns32 PSIR_FileWriter::UpdateFileResources ( XMP_IO* sourceRef, XMP_IO* dest
if ( this->memParsed ) XMP_Throw ( "Not file based", kXMPErr_EnforceFailure );
- XMP_Int64 destLenOffset = destRef->Offset();
- XMP_Uns32 destLength = 0;
+ InternalRsrcMap::const_iterator rsrcPos;
+ InternalRsrcMap::const_iterator rsrcEnd = this->imgRsrcs.end();
+
+ if ( progressTracker != 0 ) {
+
+ float totalLength = 8;
+ for ( rsrcPos = this->imgRsrcs.begin(); rsrcPos != rsrcEnd; ++rsrcPos ) {
+ const InternalRsrcInfo& currRsrc = rsrcPos->second;
+ totalLength += (currRsrc.dataLen + 12);
+ }
+
+ size_t sizeOtherRsrc = this->otherRsrcs.size();
+ for ( size_t i = 0; i < sizeOtherRsrc; ++i ) {
+ totalLength += this->otherRsrcs[i].rsrcLength;
+ }
+
+ XMP_Assert ( progressTracker->WorkInProgress() );
+ progressTracker->AddTotalWork ( totalLength );
+
+ }
+ XMP_Uns32 destLength = 0;
+ XMP_Int64 destLenOffset = destRef->Offset();
destRef->Write ( &destLength, 4 ); // Write a placeholder for the new PSIR section length.
#if 0
@@ -532,13 +542,10 @@ XMP_Uns32 PSIR_FileWriter::UpdateFileResources ( XMP_IO* sourceRef, XMP_IO* dest
RsrcHeader outHeader;
outHeader.type = MakeUns32BE ( k8BIM );
- InternalRsrcMap::iterator rsrcPos = this->imgRsrcs.begin();
- InternalRsrcMap::iterator rsrcEnd = this->imgRsrcs.end();
-
// printf ( "\nPSIR_FileWriter::UpdateFileResources - 8BIM resources\n" );
- for ( ; rsrcPos != rsrcEnd; ++rsrcPos ) {
+ for ( rsrcPos = this->imgRsrcs.begin(); rsrcPos != rsrcEnd; ++rsrcPos ) {
- InternalRsrcInfo& currRsrc = rsrcPos->second;
+ const InternalRsrcInfo& currRsrc = rsrcPos->second;
outHeader.id = MakeUns16BE ( currRsrc.id );
destRef->Write ( &outHeader, 6 );
diff --git a/XMPFiles/source/FormatSupport/PSIR_Support.hpp b/XMPFiles/source/FormatSupport/PSIR_Support.hpp
index b0ec13a..bd231b3 100644
--- a/XMPFiles/source/FormatSupport/PSIR_Support.hpp
+++ b/XMPFiles/source/FormatSupport/PSIR_Support.hpp
@@ -19,6 +19,7 @@
#include "source/XMPFiles_IO.hpp"
#include "source/EndianUtils.hpp"
+#include "source/XMP_ProgressTracker.hpp"
#include <map>
@@ -140,8 +141,8 @@ public:
virtual XMP_Uns32 UpdateMemoryResources ( void** dataPtr ) = 0;
virtual XMP_Uns32 UpdateFileResources ( XMP_IO* sourceRef, XMP_IO* destRef,
- void * _ioBuf, XMP_AbortProc abortProc, void * abortArg ) = 0;
- // *** Temporary hack above, _ioBuf is IOBuffer* but don't want to include XIO.hpp.
+ XMP_AbortProc abortProc, void * abortArg,
+ XMP_ProgressTracker* progressTracker ) = 0;
// ---------------------------------------------------------------------------------------------
@@ -179,8 +180,8 @@ public:
XMP_Uns32 UpdateMemoryResources ( void** dataPtr ) { if ( dataPtr != 0 ) *dataPtr = psirContent; return psirLength; };
XMP_Uns32 UpdateFileResources ( XMP_IO* sourceRef, XMP_IO* destRef,
- void * _ioBuf, XMP_AbortProc abortProc, void * abortArg ) { NotAppropriate(); return 0; };
- // *** Temporary hack above, _ioBuf is IOBuffer* but don't want to include XIO.hpp.
+ XMP_AbortProc abortProc, void * abortArg,
+ XMP_ProgressTracker* progressTracker ) { NotAppropriate(); return 0; };
PSIR_MemoryReader() : ownedContent(false), psirLength(0), psirContent(0) {};
@@ -232,8 +233,8 @@ public:
XMP_Uns32 UpdateMemoryResources ( void** dataPtr );
XMP_Uns32 UpdateFileResources ( XMP_IO* sourceRef, XMP_IO* destRef,
- void * _ioBuf, XMP_AbortProc abortProc, void * abortArg );
- // *** Temporary hack above, _ioBuf is IOBuffer* but don't want to include XIO.hpp.
+ XMP_AbortProc abortProc, void * abortArg,
+ XMP_ProgressTracker* progressTracker );
PSIR_FileWriter() : changed(false), legacyDeleted(false), memParsed(false), fileParsed(false),
ownedContent(false), memLength(0), memContent(0) {};
diff --git a/XMPFiles/source/FormatSupport/PackageFormat_Support.cpp b/XMPFiles/source/FormatSupport/PackageFormat_Support.cpp
new file mode 100644
index 0000000..9405293
--- /dev/null
+++ b/XMPFiles/source/FormatSupport/PackageFormat_Support.cpp
@@ -0,0 +1,124 @@
+// =================================================================================================
+// ADOBE SYSTEMS INCORPORATED
+// Copyright 2013 Adobe Systems Incorporated
+// All Rights Reserved
+//
+// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
+// of the Adobe license agreement accompanying it.
+// =================================================================================================
+
+#include "public/include/XMP_Environment.h" // ! XMP_Environment.h must be the first included header.
+
+#include "XMPFiles/source/XMPFiles_Impl.hpp"
+#include "XMPFiles/source/FormatSupport/PackageFormat_Support.hpp"
+#include <algorithm>
+
+// =================================================================================================
+/// \file PackageFormat_Support.cpp
+///
+// =================================================================================================
+
+
+// =================================================================================================
+// PackageFormat_Support::GetPostfixRange
+// ================================
+
+#if 0
+XMP_Bool PackageFormat_Support::GetPostfixRange ( XMP_FileFormat format , XMP_StringPtr extension, XMP_Uns32 * range )
+{
+ switch ( format ) {
+ case kXMP_XDCAM_EXFile:
+ range[0] = 1; range[1] = 99;
+ break;
+ case kXMP_CanonXFFile:
+ range[0] = 1; range[1] = 99;
+ break;
+ case kXMP_XDCAM_SAMFile:
+ case kXMP_XDCAM_FAMFile:
+ range[0] = 1; range[1] = 99;
+ break;
+ case kXMP_P2File:
+ range[0] = 0; range[1] = 99;// for voice memo files
+ if(strcmp(extension, ".MXF") == 0)
+ range[1] = 15;// for audio essence files
+ break;
+ default:
+ return false;
+ }
+ return true;
+} // PackageFormat_Support::GetPostfixRange
+#endif
+
+// =================================================================================================
+// PackageFormat_Support::AddResourceIfExists
+// ================================
+
+bool PackageFormat_Support::AddResourceIfExists ( XMP_StringVector * resourceList, const XMP_VarString & file )
+{
+ if ( Host_IO::Exists ( file.c_str() ) ) {
+ resourceList->push_back ( file );
+ return true;
+ }
+ return false;
+} // PackageFormat_Support::AddResourceIfExists
+
+#if 0
+// =================================================================================================
+// PackageFormat_Support::AddResourceIfExists
+// ================================
+
+bool PackageFormat_Support::AddResourceIfExists ( XMP_StringVector * resourceList, const XMP_VarString & noExtPath,
+ XMP_StringPtr extension, XMP_FileFormat format )
+{
+ XMP_Uns32 range[2];
+ XMP_Bool atLeastOneFileAdded = false, fileAdded = false;
+ XMP_VarString iStr, filePath;
+ if ( GetPostfixRange ( format, extension, range ) ) {
+ for( XMP_Uns32 index = range[0]; index <= range[1] ; ++index )
+ {
+ SXMPUtils::ConvertFromInt ( index, NULL, &iStr ) ;
+ if ( index < LEAST_TWO_DIGIT_INT )
+ iStr = '0' + iStr;
+ filePath = noExtPath + iStr + extension;
+ fileAdded = AddResourceIfExists ( resourceList, filePath );
+ atLeastOneFileAdded |= fileAdded ;
+ }
+ }
+ return atLeastOneFileAdded;
+} // PackageFormat_Support::AddResourceIfExists
+
+#endif
+// =================================================================================================
+// PackageFormat_Support::AddResourceIfExists
+// ================================
+bool PackageFormat_Support::AddResourceIfExists ( XMP_StringVector * resourceList, const XMP_VarString & folderPath,
+ XMP_StringPtr prefix, XMP_StringPtr postfix )
+{
+ Host_IO::FolderRef folderHandle = Host_IO::OpenFolder ( folderPath.c_str() );
+ if ( folderHandle == Host_IO::noFolderRef || !prefix || !postfix )
+ return false;// can't open folder.
+ XMP_VarString fileName, filePath;
+ size_t fileNameLength;
+ size_t prefixLength = strlen ( prefix );
+ size_t postfixLength = strlen ( postfix );
+ bool atleastOneFileAdded = false;
+ while ( Host_IO::GetNextChild ( folderHandle, &fileName ) )
+ {
+ fileNameLength = fileName.length();
+ // Check if the file name starts with prefix and ends with postfix
+ if ( fileNameLength >= ( prefixLength + postfixLength ) &&
+ fileName.compare ( fileNameLength-postfixLength, postfixLength, postfix ) == 0 &&
+ fileName.compare ( 0, prefixLength, prefix ) == 0)
+ {
+ filePath = folderPath + kDirChar + fileName;
+ PackageFormat_Support::AddResourceIfExists ( resourceList, filePath );
+ atleastOneFileAdded = true;
+ }
+ }
+ // close folder
+ Host_IO::CloseFolder ( folderHandle );
+ return atleastOneFileAdded;
+} // PackageFormat_Support::AddResourceIfExists
+
+
+// =================================================================================================
diff --git a/XMPFiles/source/FormatSupport/PackageFormat_Support.hpp b/XMPFiles/source/FormatSupport/PackageFormat_Support.hpp
new file mode 100644
index 0000000..8883c9e
--- /dev/null
+++ b/XMPFiles/source/FormatSupport/PackageFormat_Support.hpp
@@ -0,0 +1,39 @@
+#ifndef __PackageFormat_Support_hpp__
+#define __PackageFormat_Support_hpp__ 1
+
+// =================================================================================================
+// ADOBE SYSTEMS INCORPORATED
+// Copyright 2013 Adobe Systems Incorporated
+// All Rights Reserved
+//
+// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
+// of the Adobe license agreement accompanying it.
+// =================================================================================================
+
+#include "public/include/XMP_Environment.h" // ! This must be the first include.
+
+#include "source/XMP_LibUtils.hpp"
+
+// =================================================================================================
+/// \file PackageFormat_Support.hpp
+/// \brief XMPFiles support for folder based formats.
+///
+// =================================================================================================
+
+namespace PackageFormat_Support
+{
+
+ // Checks if the file at path "file" exists.
+ // If it exists then it adds to "resourceList" and returns true.
+ bool AddResourceIfExists ( XMP_StringVector * resourceList, const XMP_VarString & file );
+
+ // This function adds all the existing files in the specified folder whose name starts with prefix and ends with postfix.
+ bool AddResourceIfExists ( XMP_StringVector * resourceList, const XMP_VarString & folderPath,
+ XMP_StringPtr prefix, XMP_StringPtr postfix);
+
+
+} // namespace PackageFormat_Support
+
+// =================================================================================================
+
+#endif // __PackageFormat_Support_hpp__
diff --git a/XMPFiles/source/FormatSupport/PostScript_Support.cpp b/XMPFiles/source/FormatSupport/PostScript_Support.cpp
new file mode 100644
index 0000000..a0c13d2
--- /dev/null
+++ b/XMPFiles/source/FormatSupport/PostScript_Support.cpp
@@ -0,0 +1,1094 @@
+// =================================================================================================
+// ADOBE SYSTEMS INCORPORATED
+// Copyright 2012 Adobe Systems Incorporated
+// All Rights Reserved
+//
+// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
+// of the Adobe license agreement accompanying it.
+// =================================================================================================
+
+#include "XMPFiles/source/FormatSupport/PostScript_Support.hpp"
+#include "XMP.hpp"
+#include <algorithm>
+#include <climits>
+
+// =================================================================================================
+// PostScript_Support::HasCodesGT127
+// =================================
+//
+// function to detect character codes greater than 127 in a string
+bool PostScript_Support::HasCodesGT127(const std::string & value)
+{
+ size_t vallen=value.length();
+ for (size_t index=0;index<vallen;index++)
+ {
+ if ((unsigned char)value[index]>127)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+// =================================================================================================
+// PostScript_Support::SkipTabsAndSpaces
+// =====================================
+//
+// function moves the file pointer ahead such that it skips all tabs and spaces
+bool PostScript_Support::SkipTabsAndSpaces(XMP_IO* file,IOBuffer& ioBuf)
+{
+ while ( true )
+ {
+ if ( ! CheckFileSpace ( file, &ioBuf, 1 ) ) return false;
+ if ( ! IsSpaceOrTab ( *ioBuf.ptr ) ) break;
+ ++ioBuf.ptr;
+ }
+ return true;
+}
+
+// =================================================================================================
+// PostScript_Support::SkipUntilNewline
+// ====================================
+//
+// function moves the file pointer ahead such that it skips all characters until a newline
+bool PostScript_Support::SkipUntilNewline(XMP_IO* file,IOBuffer& ioBuf)
+{
+ char ch;
+ do
+ {
+ if ( ! CheckFileSpace ( file, &ioBuf, 1 ) ) return false;
+ ch = *ioBuf.ptr;
+ ++ioBuf.ptr;
+ } while ( ! IsNewline ( ch ) );
+ if (ch==kCR &&*ioBuf.ptr==kLF)
+ {
+ if ( ! CheckFileSpace ( file, &ioBuf, 1 ) ) return false;
+ ++ioBuf.ptr;
+ }
+ return true;
+}
+
+
+// =================================================================================================
+// RevRefillBuffer and RevCheckFileSpace
+// ======================================
+//
+// These helpers are similar to RefillBuffer and CheckFileSpace with the difference that the it traverses
+// the file stream in reverse order
+void PostScript_Support::RevRefillBuffer ( XMP_IO* fileRef, IOBuffer* ioBuf )
+{
+ // Refill including part of the current data, seek back to the new buffer origin and read.
+ size_t reverseSeek = ioBuf->limit - ioBuf->ptr;
+ if (ioBuf->filePos>kIOBufferSize)
+ {
+ ioBuf->filePos = fileRef->Seek ( -((XMP_Int64)(kIOBufferSize+reverseSeek)), kXMP_SeekFromCurrent );
+ ioBuf->len = fileRef->Read ( &ioBuf->data[0], kIOBufferSize );
+ ioBuf->ptr = &ioBuf->data[0]+ioBuf->len;
+ ioBuf->limit = ioBuf->ptr ;
+ }
+ else
+ {
+ XMP_Int64 rev = (ioBuf->ptr-&ioBuf->data[0]) + ioBuf->filePos;
+ ioBuf->filePos = fileRef->Seek ( 0, kXMP_SeekFromStart );
+ ioBuf->len = fileRef->Read ( &ioBuf->data[0], kIOBufferSize );
+ if ( rev > (XMP_Int64)ioBuf->len )throw XMP_Error ( kXMPErr_ExternalFailure, "Seek failure in FillBuffer" );
+ ioBuf->ptr = &ioBuf->data[0]+rev;
+ ioBuf->limit = &ioBuf->data[0]+ioBuf->len;
+ }
+
+
+}
+bool PostScript_Support::RevCheckFileSpace ( XMP_IO* fileRef, IOBuffer* ioBuf, size_t neededLen )
+{
+ if ( size_t(ioBuf->ptr - &ioBuf->data[0]) < size_t(neededLen) )
+ { // ! Avoid VS.Net compare warnings.
+ PostScript_Support::RevRefillBuffer ( fileRef, ioBuf );
+ }
+ return (size_t(ioBuf->ptr - &ioBuf->data[0]) >= size_t(neededLen));
+}
+
+// =================================================================================================
+// SearchBBoxInTrailer
+// ===================
+//
+// Function searches the Bounding Box in the comments after the %Trailer
+// this function gets called when the DSC comment BoundingBox: value is
+// (atend)
+// returns true if atleast one BoundingBox: is found after %Trailer
+inline static bool SearchBBoxInTrailer(XMP_IO* fileRef,IOBuffer& ioBuf)
+{
+ bool bboxfoundintrailer=false;
+ if ( ! PostScript_Support::SkipTabsAndSpaces( fileRef, ioBuf ) ) return false;
+ if ( ! IsNewline ( *ioBuf.ptr ) ) return false;
+ ++ioBuf.ptr;
+ // Scan for all the %%Trailer outside %%BeginDocument: & %%EndDocument comments
+ while(true)
+ {
+ if ( ! CheckFileSpace ( fileRef, &ioBuf, kPSContainsBeginDocString.length() ) ) return false;
+ if ( CheckBytes ( ioBuf.ptr, Uns8Ptr(kPSContainsTrailerString.c_str()), kPSContainsTrailerString.length() ))
+ {
+ //found %%Trailer now search for proper %%BoundingBox
+ ioBuf.ptr+=kPSContainsTrailerString.length();
+ //skip chars after %%Trailer till newline
+ if ( ! PostScript_Support::SkipUntilNewline( fileRef, ioBuf ) ) return false;
+ while(true)
+ {
+ if ( ! CheckFileSpace ( fileRef, &ioBuf, kPSContainsBBoxString.length() ) ) return false;
+ //check for "%%BoundingBox:"
+ if (CheckBytes ( ioBuf.ptr, Uns8Ptr(kPSContainsBBoxString.c_str()), kPSContainsBBoxString.length() ) )
+ {
+ //found "%%BoundingBox:"
+ ioBuf.ptr+=kPSContainsBBoxString.length();
+ // Skip leading spaces and tabs.
+ if ( ! PostScript_Support::SkipTabsAndSpaces( fileRef, ioBuf ) ) return false;
+ if ( IsNewline ( *ioBuf.ptr ) ) return false; // Reached the end of the %%BoundingBox comment.
+ bboxfoundintrailer=true;
+ break;
+ }
+ //skip chars till newline
+ if ( ! PostScript_Support::SkipUntilNewline( fileRef, ioBuf ) ) return false;
+ }
+ if (!bboxfoundintrailer)
+ return false;
+ else
+ break;
+ }
+ else if ( CheckBytes ( ioBuf.ptr, Uns8Ptr(kPSContainsBeginDocString.c_str()), kPSContainsBeginDocString.length() ) )
+ {
+ //"%%BeginDocument:" Found search for "%%EndDocument"
+ ioBuf.ptr+=kPSContainsBeginDocString.length();
+ //skip chars after "%%BeginDocument:" till newline
+ if ( ! PostScript_Support::SkipUntilNewline( fileRef, ioBuf ) ) return false;
+ while(true)
+ {
+ if ( ! CheckFileSpace ( fileRef, &ioBuf, kPSContainsEndDocString.length() ) ) return false;
+ //check for "%%EndDocument"
+ if (CheckBytes ( ioBuf.ptr, Uns8Ptr(kPSContainsEndDocString.c_str()), kPSContainsEndDocString.length() ) )
+ {
+ //found "%%EndDocument"
+ ioBuf.ptr+=kPSContainsEndDocString.length();
+ break;
+ }
+ //skip chars till newline
+ if ( ! PostScript_Support::SkipUntilNewline( fileRef, ioBuf ) ) return false;
+ }// while to search %%EndDocument
+
+ } //else if to consume a pair of %%BeginDocument: and %%EndDocument
+ //skip chars till newline
+ if ( ! PostScript_Support::SkipUntilNewline( fileRef, ioBuf ) ) return false;
+ }// while for scanning %%BoundingBox after %%Trailer
+ if (!bboxfoundintrailer) return false;
+ return true;
+}
+
+// =================================================================================================
+// PostScript_Support::IsValidPSFile
+// =================================
+//
+// Determines if the file is a valid PostScript or EPS file
+// Checks done
+// Looks for a valid Poscript header
+// For EPS file checks for a valid Bounding Box comment
+bool PostScript_Support::IsValidPSFile(XMP_IO* fileRef,XMP_FileFormat &format)
+{
+ IOBuffer ioBuf;
+ XMP_Int64 psOffset;
+ size_t psLength;
+ XMP_Uns32 fileheader,psMajorVer, psMinorVer,epsMajorVer,epsMinorVer;
+ char ch;
+ // Check for the binary EPSF preview header.
+
+ fileRef->Rewind();
+ if ( ! CheckFileSpace ( fileRef, &ioBuf, 4 ) ) return false;
+ fileheader = GetUns32BE ( ioBuf.ptr );
+
+ if ( fileheader == 0xC5D0D3C6 )
+ {
+
+ if ( ! CheckFileSpace ( fileRef, &ioBuf, 30 ) ) return false;
+
+ psOffset = GetUns32LE ( ioBuf.ptr+4 ); // PostScript offset.
+ psLength = GetUns32LE ( ioBuf.ptr+8 ); // PostScript length.
+
+ FillBuffer ( fileRef, psOffset, &ioBuf ); // Make sure buffer starts at psOffset for length check.
+ if ( (ioBuf.len < kIOBufferSize) && (ioBuf.len < psLength) ) return false; // Not enough PostScript.
+
+ }
+
+ // Check the start of the PostScript DSC header comment.
+
+ if ( ! CheckFileSpace ( fileRef, &ioBuf, (kPSFileTag.length() + 3 + 1) ) ) return false;
+ if ( ! CheckBytes ( ioBuf.ptr, Uns8Ptr(kPSFileTag.c_str()), kPSFileTag.length() ) ) return false;
+ ioBuf.ptr += kPSFileTag.length();
+
+ // Check the PostScript DSC major version number.
+
+ psMajorVer = 0;
+ while ( (ioBuf.ptr < ioBuf.limit) && IsNumeric( *ioBuf.ptr ) )
+ {
+ psMajorVer = (psMajorVer * 10) + (*ioBuf.ptr - '0');
+ if ( psMajorVer > 1000 ) return false; // Overflow.
+ ioBuf.ptr += 1;
+ }
+ if ( psMajorVer < 3 ) return false; // The version must be at least 3.0.
+
+ if ( ! CheckFileSpace ( fileRef, &ioBuf, 3 ) ) return false;
+ if ( *ioBuf.ptr != '.' ) return false; // No minor number.
+ ioBuf.ptr += 1;
+
+ // Check the PostScript DSC minor version number.
+
+ psMinorVer = 0;
+ while ( (ioBuf.ptr < ioBuf.limit) && IsNumeric( *ioBuf.ptr ) )
+ {
+ psMinorVer = (psMinorVer * 10) + (*ioBuf.ptr - '0');
+ if ( psMinorVer > 1000 ) return false; // Overflow.
+ ioBuf.ptr += 1;
+ }
+
+ switch( format )
+ {
+ case kXMP_PostScriptFile:
+ {
+ // Almost done for plain PostScript, check for whitespace.
+
+ if ( ! CheckFileSpace ( fileRef, &ioBuf, 1 ) ) return false;
+ if ( ! IsWhitespace(*ioBuf.ptr) ) return false;
+ ioBuf.ptr += 1;
+
+ break;
+ }
+ case kXMP_UnknownFile:
+ {
+ if ( ! SkipTabsAndSpaces ( fileRef, ioBuf ) ) return false;
+
+ if ( ! CheckFileSpace ( fileRef, &ioBuf, 5 ) ) return false;
+ // checked PS header to this point Atkleast a PostScript File
+ format=kXMP_PostScriptFile;
+ //return true if no "EPSF-" is found as it is a valid PS atleast
+ if ( ! CheckBytes ( ioBuf.ptr, Uns8Ptr("EPSF-"), 5 ) ) return true;
+ }
+ case kXMP_EPSFile:
+ {
+
+ if ( ! SkipTabsAndSpaces ( fileRef, ioBuf ) ) return false;
+ // Check for the EPSF keyword on the header comment.
+
+ if ( ! CheckFileSpace ( fileRef, &ioBuf, 5+3+1 ) ) return false;
+ if ( ! CheckBytes ( ioBuf.ptr, Uns8Ptr("EPSF-"), 5 ) ) return false;
+ ioBuf.ptr += 5;
+
+ // Check the EPS major version number.
+
+ epsMajorVer = 0;
+ while ( (ioBuf.ptr < ioBuf.limit) &&IsNumeric( *ioBuf.ptr ) ) {
+ epsMajorVer = (epsMajorVer * 10) + (*ioBuf.ptr - '0');
+ if ( epsMajorVer > 1000 ) return false; // Overflow.
+ ioBuf.ptr += 1;
+ }
+ if ( epsMajorVer < 3 ) return false; // The version must be at least 3.0.
+
+ if ( ! CheckFileSpace ( fileRef, &ioBuf, 3 ) ) return false;
+ if ( *ioBuf.ptr != '.' ) return false; // No minor number.
+ ioBuf.ptr += 1;
+
+ // Check the EPS minor version number.
+
+ epsMinorVer = 0;
+ while ( (ioBuf.ptr < ioBuf.limit) && IsNumeric( *ioBuf.ptr ) ) {
+ epsMinorVer = (epsMinorVer * 10) + (*ioBuf.ptr - '0');
+ if ( epsMinorVer > 1000 ) return false; // Overflow.
+ ioBuf.ptr += 1;
+ }
+
+ if ( ! SkipTabsAndSpaces ( fileRef, ioBuf ) ) return false;
+ if ( ! CheckFileSpace ( fileRef, &ioBuf, 1 ) ) return false;
+ if ( ! IsNewline( *ioBuf.ptr ) ) return false;
+ ch=*ioBuf.ptr;
+ ioBuf.ptr += 1;
+ if (ch==kCR &&*ioBuf.ptr==kLF)
+ {
+ if ( ! CheckFileSpace ( fileRef, &ioBuf, 1 ) ) return false;
+ ++ioBuf.ptr;
+ }
+
+
+ while ( true )
+ {
+ if ( ! CheckFileSpace ( fileRef, &ioBuf, kPSContainsBBoxString.length() ) ) return false;
+
+ if ( CheckBytes ( ioBuf.ptr, Uns8Ptr(kPSEndCommentString.c_str()), kPSEndCommentString.length() ) //explicit endcommentcheck
+ || *ioBuf.ptr!='%' || !(*(ioBuf.ptr+1)>32 && *(ioBuf.ptr+1)<=126 )) // implicit endcomment check
+ {
+ // Found "%%EndComments", don't look any further.
+ return false;
+ }
+ else if ( ! CheckBytes ( ioBuf.ptr, Uns8Ptr(kPSContainsBBoxString.c_str()), kPSContainsBBoxString.length() ) )
+ {
+ // Not "%%EndComments" or "%%BoundingBox:", skip past the end of this line.
+ if ( ! SkipUntilNewline ( fileRef, ioBuf ) ) return true;
+ }
+ else
+ {
+
+ // Found "%%BoundingBox:", look for llx lly urx ury.
+ ioBuf.ptr += kPSContainsBBoxString.length();
+ //Check for atleast a mandatory space b/w "%%BoundingBox:" and llx lly urx ury
+ if ( ! CheckFileSpace ( fileRef, &ioBuf, 1 ) ) return false;
+ if ( ! IsSpaceOrTab ( *ioBuf.ptr ) ) return false;
+ ioBuf.ptr++;
+
+ while ( true )
+ {
+ // Skip leading spaces and tabs.
+ if ( ! SkipTabsAndSpaces( fileRef, ioBuf ) ) return false;
+ if ( IsNewline ( *ioBuf.ptr ) ) return false; // Reached the end of the %%BoundingBox comment.
+
+ //if the comment is %%BoundingBox: (atend) go past the %%Trailer to check BBox
+ bool bboxfoundintrailer=false;
+ if (*ioBuf.ptr=='(')
+ {
+ if ( ! CheckFileSpace ( fileRef, &ioBuf, kPSContainsAtendString.length() ) ) return false;
+
+ if ( CheckBytes ( ioBuf.ptr, Uns8Ptr(kPSContainsAtendString.c_str()), kPSContainsAtendString.length() ) )
+ {
+ // search for Bounding Box Past Trailer
+ ioBuf.ptr += kPSContainsAtendString.length();
+ bboxfoundintrailer=SearchBBoxInTrailer( fileRef, ioBuf );
+ }
+
+ if (!bboxfoundintrailer)
+ return false;
+
+ }//if (*ioBuf.ptr=='(')
+
+ int noOfIntegers=0;
+ // verifies for llx lly urx ury.
+ while(true)
+ {
+ if ( ! CheckFileSpace ( fileRef, &ioBuf, 1 ) ) return false;
+ if(IsPlusMinusSign(*ioBuf.ptr ))
+ ++ioBuf.ptr;
+ bool atleastOneNumeric=false;
+ while ( true)
+ {
+ if ( ! CheckFileSpace ( fileRef, &ioBuf, 1 ) ) return false;
+ if (!IsNumeric ( *ioBuf.ptr ) ) break;
+ ++ioBuf.ptr;
+ atleastOneNumeric=true;
+ }
+ if (!atleastOneNumeric) return false;
+
+ if ( ! SkipTabsAndSpaces( fileRef, ioBuf ) ) return false;
+ noOfIntegers++;
+ if ( IsNewline ( *ioBuf.ptr ) ) break;
+ }
+ if (noOfIntegers!=4)
+ return false;
+ format=kXMP_EPSFile;
+ return true;
+ }
+
+ } //Found "%%BoundingBox:"
+
+ } // Outer marker loop.
+ }
+ default:
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+
+// =================================================================================================
+// PostScript_Support::IsSFDFilterUsed
+// =================================
+//
+// Determines Whether the metadata is embedded using the Sub-FileDecode Approach or no
+// In case of Sub-FileDecode filter approach the metaData can be easily extended without
+// the need to inject a new XMP packet before the existing Packet.
+//
+bool PostScript_Support::IsSFDFilterUsed(XMP_IO* &fileRef, XMP_Int64 xpacketOffset)
+{
+ IOBuffer ioBuf;
+ fileRef->Rewind();
+ fileRef->Seek((xpacketOffset/kIOBufferSize)*kIOBufferSize,kXMP_SeekFromStart);
+ if ( ! CheckFileSpace ( fileRef, &ioBuf,xpacketOffset%kIOBufferSize ) ) return false;
+ ioBuf.ptr+=(xpacketOffset%kIOBufferSize);
+ //skip white spaces
+ while(true)
+ {
+ if ( ! PostScript_Support::RevCheckFileSpace ( fileRef, &ioBuf, 1 ) ) return false;
+ if (!IsWhitespace(*ioBuf.ptr)) break;
+ --ioBuf.ptr;
+ }
+ std::string temp;
+ bool filterFound=false;
+ while(true)
+ {
+ if ( ! PostScript_Support::RevCheckFileSpace ( fileRef, &ioBuf, 1 ) ) return false;
+ if (*ioBuf.ptr==')')
+ {
+ --ioBuf.ptr;
+ while(true)
+ {
+ //get the string till '('
+ if ( ! PostScript_Support::RevCheckFileSpace ( fileRef, &ioBuf, 1 ) ) return false ;
+ temp+=*ioBuf.ptr;
+ --ioBuf.ptr;
+ if (*ioBuf.ptr=='(')
+ {
+ if(filterFound)
+ {
+ reverse(temp.begin(), temp.end());
+ if(!temp.compare("SubFileDecode"))
+ return true;
+ }
+ if ( ! PostScript_Support::RevCheckFileSpace ( fileRef, &ioBuf, 1 ) ) return false ;
+ --ioBuf.ptr;
+ temp.clear();
+ break;
+ }
+ }
+
+ filterFound=false;
+ }
+ else if(*ioBuf.ptr=='[')
+ {
+ //end of SubFileDecode Filter parsing
+ return false;
+ }
+ else if(*ioBuf.ptr=='k' )
+ {
+ if ( ! PostScript_Support::RevCheckFileSpace ( fileRef, &ioBuf, 4 ) ) return false;
+ if(IsWhitespace(*(ioBuf.ptr-4))&& *(ioBuf.ptr-3)=='m'
+ && *(ioBuf.ptr-2)=='a' && *(ioBuf.ptr-1)=='r' )
+ //end of SubFileDecode Filter parsing
+ return false;
+ while(true)//ignore till any special mark
+ {
+ if ( ! PostScript_Support::RevCheckFileSpace ( fileRef, &ioBuf, 4 ) ) return false;
+ if (IsWhitespace(*(ioBuf.ptr))||*(ioBuf.ptr)=='['||
+ *(ioBuf.ptr)=='<' ||*(ioBuf.ptr)=='>') break;
+ --ioBuf.ptr;
+ }
+ filterFound=false;
+ }
+ else if(*ioBuf.ptr=='<')
+ {
+ --ioBuf.ptr;
+ if ( ! PostScript_Support::RevCheckFileSpace ( fileRef, &ioBuf, 1 ) ) return false;
+ if (*(ioBuf.ptr)=='<')
+ {
+ //end of SubFileDecode Filter parsing
+ return false;
+ }
+ while(true)//ignore till any special mark
+ {
+ if ( ! PostScript_Support::RevCheckFileSpace ( fileRef, &ioBuf, 4 ) ) return false;
+ if (IsWhitespace(*(ioBuf.ptr))||*(ioBuf.ptr)=='['||
+ *(ioBuf.ptr)=='<' ||*(ioBuf.ptr)=='>') break;
+ --ioBuf.ptr;
+ }
+ filterFound=false;
+ }
+ else if(*ioBuf.ptr=='>')
+ {
+ --ioBuf.ptr;
+ if ( ! PostScript_Support::RevCheckFileSpace ( fileRef, &ioBuf, 1 ) ) return false;
+ if (*(ioBuf.ptr)=='>')//ignore the dictionary
+ {
+ --ioBuf.ptr;
+ XMP_Int16 count=1;
+ while(true)
+ {
+ if ( ! PostScript_Support::RevCheckFileSpace ( fileRef, &ioBuf, 2 ) ) return false;
+ if(*(ioBuf.ptr)=='<' && *(ioBuf.ptr-1)=='<')
+ {
+ count--;
+ ioBuf.ptr-=2;
+ }
+ else if(*(ioBuf.ptr)=='>' && *(ioBuf.ptr-1)=='>')
+ {
+ count++;
+ ioBuf.ptr-=2;
+ }
+ else
+ {
+ ioBuf.ptr-=1;
+ }
+ if(count==0)
+ break;
+ }
+ }
+ filterFound=false;
+ }
+ else
+ {
+ while(true)
+ {
+ if ( ! PostScript_Support::RevCheckFileSpace ( fileRef, &ioBuf, 1 ) ) return false;
+ temp+=(*ioBuf.ptr);
+ --ioBuf.ptr;
+ if (*ioBuf.ptr=='/')
+ {
+ if(filterFound)
+ {
+ reverse(temp.begin(), temp.end());
+ if(!temp.compare("SubFileDecode"))
+ return true;
+ }
+ temp.clear();
+ filterFound=false;
+ break;
+ }
+ else if(IsWhitespace(*ioBuf.ptr))
+ {
+ reverse(temp.begin(), temp.end());
+ if(!temp.compare("filter")&&!filterFound)
+ filterFound=true;
+ else
+ filterFound=false;
+ temp.clear();
+ break;
+ }
+ }
+ if ( ! PostScript_Support::RevCheckFileSpace ( fileRef, &ioBuf, 1 ) ) return false;
+ --ioBuf.ptr;
+ }
+ while(true)
+ {
+ if ( ! PostScript_Support::RevCheckFileSpace ( fileRef, &ioBuf, 1 ) ) return false;
+ if (!IsWhitespace(*ioBuf.ptr)) break;
+ --ioBuf.ptr;
+ }
+
+ }
+ return false;
+
+}
+
+
+// =================================================================================================
+// constructDateTime
+// =================================
+//
+// If input string date is of the format D:YYYYMMDDHHmmSSOHH'mm' and valid
+// output format YYYY-MM-DDThh:mm:ssTZD is returned
+//
+static void constructDateTime(const std::string &input,std::string& outDate)
+{
+ std::string date;
+ XMP_DateTime datetime;
+ std::string verdate;
+ size_t start =0;
+ if(input[0]=='D' && input[1]==':')
+ start=2;
+ if (input.length() >=14+start)
+ {
+ for(int x=0;x<4;x++)
+ {
+ date+=input[start+x];//YYYY
+ }
+ date+='-';
+ start+=4;
+ for(int x=0;x<2;x++)
+ {
+ date+=input[start+x];//MM
+ }
+ date+='-';
+ start+=2;
+ for(int x=0;x<2;x++)
+ {
+ date+=input[start+x];//DD
+ }
+ date+='T';
+ start+=2;
+ for(int x=0;x<2;x++)
+ {
+ date+=input[start+x];//HH
+ }
+
+ date+=':';
+ start+=2;
+ for(int x=0;x<2;x++)
+ {
+ date+=input[start+x];//MM
+ }
+
+ date+=':';
+ start+=2;
+ for(int x=0;x<2;x++)
+ {
+ date+=input[start+x];//SS
+ }
+ start+=2;
+ if ((input[start]=='+' || input[start]=='-' )&&input.length() ==19+start)
+ {
+ date+=input[start];
+ start++;
+ for(int x=0;x<2;x++)
+ {
+ date+=input[start+x];//hh
+ }
+ date+=':';
+ start+=2;
+ for(int x=0;x<2;x++)
+ {
+ date+=input[start+x];//mm
+ }
+ }
+ else
+ {
+ date+='Z';
+ }
+
+ try
+ {
+ SXMPUtils::ConvertToDate ( date.c_str(),&datetime);
+ SXMPUtils::ConvertFromDate (datetime,&verdate);
+ }
+ catch(...)
+ {
+ return;
+ }
+ outDate=verdate;
+ }
+
+}
+
+// =================================================================================================
+// GetNumber
+// =========
+//
+// Extracts number from a char string
+//
+static short GetNumber(const char **inString,short noOfChars=SHRT_MAX)
+{
+ const char* inStr=*inString;
+ short number=0;
+ while(IsNumeric ( *inStr )&& noOfChars--)
+ {
+ number=number*10 +inStr[0]-'0';
+ inStr++;
+ }
+ *inString=inStr;
+ return number;
+}
+
+// =================================================================================================
+// tokeniseDateString
+// ==================
+//
+// Parses Date string and tokenizes it for extracting different parts of a date
+//
+static bool tokeniseDateString(const char* inString,std::vector<PostScript_Support::DateTimeTokens> &tokens )
+{
+ const char* inStr= inString;
+ PostScript_Support::DateTimeTokens dttoken;
+ //skip leading whitespace
+ while ( inStr[0]!='\0' )
+ {
+ while( IsSpaceOrTab ( *inStr ) || *inStr =='('
+ ||*inStr ==')' ||*inStr==',')
+ {
+ ++inStr;
+ }
+ if (*inStr=='\0') return true;
+ dttoken=PostScript_Support::DateTimeTokens();
+ if (IsNumeric(*inStr))
+ {
+ while(IsNumeric(*inStr)||(IsDelimiter(*inStr)&&dttoken.noOfDelimiter==0)||
+ (dttoken.delimiter==*inStr && dttoken.noOfDelimiter!=0))
+ {
+ if (IsDelimiter(*inStr))
+ {
+ dttoken.delimiter=inStr[0];
+ dttoken.noOfDelimiter++;
+ }
+ dttoken.token+=*(inStr++);
+ }
+ tokens.push_back(dttoken);
+ }
+ else if (IsAlpha(*inStr))
+ {
+ if(*inStr=='D'&& *(inStr+1)==':')
+ {
+ inStr+=2;
+ while( IsNumeric(*inStr))
+ {
+ dttoken.token+=*(inStr++);
+ }
+ }
+ else
+ {
+ while( IsAlpha(*inStr))
+ {
+ dttoken.token+=*(inStr++);
+ }
+
+ }
+ tokens.push_back(dttoken);
+ }
+ else if (*inStr=='-' ||*inStr=='+')
+ {
+ dttoken.token+=*(inStr++);
+ while( IsNumeric(*inStr)||*inStr==':')
+ {
+ if (*inStr==':')
+ {
+ dttoken.delimiter=inStr[0];
+ dttoken.noOfDelimiter++;
+ }
+ dttoken.token+=*(inStr++);
+ }
+ tokens.push_back(dttoken);
+ }
+ else
+ {
+ ++inStr;
+ }
+ }
+ return true;
+}
+
+// =================================================================================================
+// SwapMonthDateIfNeeded
+// =====================
+//
+// Swaps month and date value if it creates a possible valid date
+//
+static void SwapMonthDateIfNeeded(short &day, short&month)
+{
+ if(month>12&& day<13)
+ {
+ short temp=month;
+ month=day;
+ day=temp;
+ }
+}
+
+// =================================================================================================
+// AdjustYearIfNeeded
+// =====================
+//
+// Guess the year for a two digit year in a date
+//
+static void AdjustYearIfNeeded(short &year)
+{
+ if (year<100)
+ {
+ if (year >40)
+ {
+ year=1900+year;
+ }
+ else
+ {
+ year=2000+year;
+ }
+ }
+}
+
+static bool IsLeapYear ( XMP_Int32 year )
+{
+ if ( year < 0 ) year = -year + 1; // Fold the negative years, assuming there is a year 0.
+ if ( (year % 4) != 0 ) return false; // Not a multiple of 4.
+ if ( (year % 100) != 0 ) return true; // A multiple of 4 but not a multiple of 100.
+ if ( (year % 400) == 0 ) return true; // A multiple of 400.
+ return false; // A multiple of 100 but not a multiple of 400.
+}
+// =================================================================================================
+// PostScript_Support::ConvertToDate
+// =================================
+//
+// Converts date string from native of Postscript to YYYY-MM-DDThh:mm:ssTZD if a valid date is identified
+//
+std::string PostScript_Support::ConvertToDate(const char* inString)
+{
+ PostScript_Support::Date date(0,0,0,0,0,0);
+ std::string dateTimeString;
+ short number=0;
+ std::vector<PostScript_Support::DateTimeTokens> tokenzs;
+ tokeniseDateString(inString,tokenzs );
+ std::vector<PostScript_Support::DateTimeTokens>:: const_iterator itr=tokenzs.begin();
+ for(;itr!=tokenzs.end();itr++)
+ {
+ if(itr->token[0]=='+' ||itr->token[0]=='-')
+ {
+ const char *str=itr->token.c_str();
+ date.offsetSign=*(str++);
+ date.offsetHour=GetNumber(&str,2);
+ if (*str==':')str++;
+ date.offsetMin=GetNumber(&str,2);
+ if (!(date.offsetHour<=12 && date.offsetHour>=0
+ &&date.offsetMin>=0 && date.offsetMin<=59))
+ {
+ date.offsetSign='+';
+ date.offsetHour=0;
+ date.offsetMin=0;
+ }
+ else
+ {
+ date.containsOffset= true;
+ }
+ }
+ else if(itr->noOfDelimiter!=0)
+ {//either a date or time token
+ if(itr->noOfDelimiter==2 && itr->delimiter=='/')
+ {
+ if(date.day==0&& date.month==0 && date.year==0)
+ {
+ const char *str=itr->token.c_str();
+ number=GetNumber(&str);
+ str++;
+ if (number<32)
+ {
+ date.month=number;
+ date.day=GetNumber(&str);
+ SwapMonthDateIfNeeded(date.day, date.month);
+ str++;
+ date.year=GetNumber(&str);
+ AdjustYearIfNeeded(date.year);
+ }
+ else
+ {
+ date.year=number;
+ AdjustYearIfNeeded(date.year);
+ date.month=GetNumber(&str);
+ str++;
+ date.day=GetNumber(&str);
+ SwapMonthDateIfNeeded(date.day, date.month);
+ }
+ }
+ }
+ else if(itr->noOfDelimiter==2 && itr->delimiter==':')
+ {
+ const char *str=itr->token.c_str();
+ date.hours=GetNumber(&str);
+ str++;
+ date.minutes=GetNumber(&str);
+ str++;
+ date.seconds=GetNumber(&str);
+ if (date.hours>23|| date.minutes>59|| date.seconds>59)
+ {
+ date.hours=0;
+ date.minutes=0;
+ date.seconds=0;
+ }
+ }
+ else if(itr->noOfDelimiter==1 && itr->delimiter==':')
+ {
+ const char *str=itr->token.c_str();
+ date.hours=GetNumber(&str);
+ str++;
+ date.minutes=GetNumber(&str);
+ if (date.hours>23|| date.minutes>59)
+ {
+ date.hours=0;
+ date.minutes=0;
+ }
+ }
+ else if(itr->noOfDelimiter==2 && itr->delimiter=='-')
+ {
+ const char *str=itr->token.c_str();
+ number=GetNumber(&str);
+ str++;
+ if (number>31)
+ {
+ date.year=number;
+ AdjustYearIfNeeded(date.year);
+ date.month=GetNumber(&str);
+ str++;
+ date.day=GetNumber(&str);
+ SwapMonthDateIfNeeded(date.day, date.month);
+ }
+ else
+ {
+ date.month=number;
+ date.day=GetNumber(&str);
+ str++;
+ SwapMonthDateIfNeeded(date.day, date.month);
+ date.year=GetNumber(&str);
+ AdjustYearIfNeeded(date.year);
+ }
+ }
+ else if(itr->noOfDelimiter==2 && itr->delimiter=='.')
+ {
+ const char *str=itr->token.c_str();
+ date.year=GetNumber(&str);
+ str++;
+ AdjustYearIfNeeded(date.year);
+ date.month=GetNumber(&str);
+ str++;
+ date.day=GetNumber(&str);
+ SwapMonthDateIfNeeded(date.day, date.month);
+
+ }
+ }
+ else if (IsAlpha(itr->token[0]))
+ { //this can be a name of the Month
+ // name of the day is ignored
+ short month=0;
+ std::string lowerToken=itr->token;
+ std::transform(lowerToken.begin(), lowerToken.end(), lowerToken.begin(), ::tolower);
+ if(!lowerToken.compare("jan")||!lowerToken.compare("january"))
+ {
+ month=1;
+ }
+ else if (!lowerToken.compare("feb")||!lowerToken.compare("february"))
+ {
+ month=2;
+ }
+ else if (!lowerToken.compare("mar")||!lowerToken.compare("march"))
+ {
+ month=3;
+ }
+ else if (!lowerToken.compare("apr")||!lowerToken.compare("april"))
+ {
+ month=4;
+ }
+ else if (!lowerToken.compare("may"))
+ {
+ month=5;
+ }
+ else if (!lowerToken.compare("jun")||!lowerToken.compare("june"))
+ {
+ month=6;
+ }
+ else if (!lowerToken.compare("jul")||!lowerToken.compare("july"))
+ {
+ month=7;
+ }
+ else if (!lowerToken.compare("aug")||!lowerToken.compare("august"))
+ {
+ month=8;
+ }
+ else if (!lowerToken.compare("sep")||!lowerToken.compare("september"))
+ {
+ month=9;
+ }
+ else if (!lowerToken.compare("oct")||!lowerToken.compare("october"))
+ {
+ month=10;
+ }
+ else if (!lowerToken.compare("nov")||!lowerToken.compare("november"))
+ {
+ month=11;
+ }
+ else if (!lowerToken.compare("dec")||!lowerToken.compare("december"))
+ {
+ month=12;
+ }
+ else if (!lowerToken.compare("pm"))
+ {
+ if (date.hours<13)
+ {
+ date.hours+=12;
+ }
+ }
+ else if (itr->token.length()>14)
+ {
+ constructDateTime(itr->token,dateTimeString);
+ }
+ if(month!=0 && date.month==0)
+ {
+ date.month=month;
+ if(date.day==0)
+ {
+ if(itr!=tokenzs.begin())
+ {
+ --itr;
+ if (itr->noOfDelimiter==0 && IsNumeric(itr->token[0]) )
+ {
+ const char * str=itr->token.c_str();
+ short day= GetNumber(&str);
+ if (day<=31 &&day >=1)
+ {
+ date.day=day;
+ }
+ }
+ ++itr;
+ }
+ if(itr!=tokenzs.end())
+ {
+ ++itr;
+ if (itr!=tokenzs.end()&&itr->noOfDelimiter==0 && IsNumeric(itr->token[0]) )
+ {
+ const char * str=itr->token.c_str();
+ short day= GetNumber(&str);
+ if (day<=31 &&day >=1)
+ {
+ date.day=day;
+ }
+ }
+ }
+ }
+ }
+ }
+ else if (IsNumeric(itr->token[0]) && date.year==0&&itr->token.length()==4)
+ {//this is the year
+ const char * str=itr->token.c_str();
+ date.year=GetNumber(&str);
+ }
+ else if (itr->token.length()>=14)
+ {
+ constructDateTime(itr->token,dateTimeString);
+ }
+ }
+ if (dateTimeString.length()==0)
+ {
+ char dtstr[100];
+ XMP_DateTime datetime;
+ if ( date.year < 10000 && date.month < 13 && date.month > 0 && date.day > 0 )
+ {
+ bool isValidDate=true;
+ if ( date.month == 2 )
+ {
+ if ( IsLeapYear ( date.year ) )
+ {
+ if ( date.day > 29 ) isValidDate=false;
+ }
+ else
+ {
+ if ( date.day > 28 ) isValidDate=false;
+ }
+ }
+ else if ( date.month == 4 || date.month == 6 || date.month == 9
+ || date.month == 11 )
+ {
+ if ( date.day > 30 ) isValidDate=false;
+ }
+ else
+ {
+ if ( date.day > 31 ) isValidDate=false;
+ }
+ if( isValidDate && ! ( date == PostScript_Support::Date ( 0, 0, 0, 0, 0, 0 ) ) )
+ {
+ if ( date.containsOffset )
+ {
+ sprintf(dtstr,"%04d-%02d-%02dT%02d:%02d:%02d%c%02d:%02d",date.year,date.month,date.day,
+ date.hours,date.minutes,date.seconds,date.offsetSign,date.offsetHour,date.offsetMin);
+ }
+ else
+ {
+ sprintf(dtstr,"%04d-%02d-%02dT%02d:%02d:%02dZ",date.year,date.month,date.day,
+ date.hours,date.minutes,date.seconds);
+ }
+ try
+ {
+
+ SXMPUtils::ConvertToDate ( dtstr, &datetime ) ;
+ SXMPUtils::ConvertFromDate ( datetime, &dateTimeString ) ;
+ }
+ catch(...)
+ {
+ }
+ }
+ }
+ }
+
+ return dateTimeString;
+}
+// =================================================================================================
diff --git a/XMPFiles/source/FormatSupport/PostScript_Support.hpp b/XMPFiles/source/FormatSupport/PostScript_Support.hpp
new file mode 100644
index 0000000..688fa6d
--- /dev/null
+++ b/XMPFiles/source/FormatSupport/PostScript_Support.hpp
@@ -0,0 +1,266 @@
+#ifndef __PostScript_Support_hpp__
+#define __PostScript_Support_hpp__ 1
+
+// =================================================================================================
+// ADOBE SYSTEMS INCORPORATED
+// Copyright 2012 Adobe Systems Incorporated
+// All Rights Reserved
+//
+// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
+// of the Adobe license agreement accompanying it.
+// =================================================================================================
+
+#include "public/include/XMP_Environment.h"
+
+#include "public/include/XMP_Const.h"
+#include "public/include/XMP_IO.hpp"
+
+#include "XMPFiles/source/XMPFiles_Impl.hpp"
+#include "source/XMPFiles_IO.hpp"
+#include "source/XIO.hpp"
+
+
+#define MAX_NO_MARK 100
+#define IsNumeric( ch ) ( ch >='0' && ch<='9' )
+#define Uns8Ptr(p) ((XMP_Uns8 *) (p))
+#define IsPlusMinusSign(ch) ( ch =='+' || ch=='-' )
+#define IsDateDelimiter( ch ) ( ((ch) == '/') || ((ch) == '-') || ((ch) == '.') )
+#define IsTimeDelimiter( ch ) ( ((ch) == ':') )
+#define IsDelimiter(ch) (IsDateDelimiter( ch ) || IsTimeDelimiter( ch ))
+#define IsAlpha(ch) ((ch>=97 &&ch <=122) || (ch>=65 && ch<=91))
+
+
+enum {
+ kPSHint_NoMarker = 0,
+ kPSHint_NoMain = 1,
+ kPSHint_MainFirst = 2,
+ kPSHint_MainLast = 3
+};
+
+enum UpdateMethod{
+ kPS_None = 0,
+ kPS_Inplace = 1,
+ kPS_ExpandSFDFilter = 2,
+ kPS_InjectNew = 3
+};
+
+enum TokenFlag{
+ // --------------------
+ // Flags for native Metadata and DSC commnents in EPS format
+
+ /// No Native MetaData
+ kPS_NoData = 0x00000001,
+ /// Document Creator tool
+ kPS_CreatorTool = 0x00000002,
+ /// Document Creation Date
+ kPS_CreateDate = 0x00000004,
+ /// Document Modify Date
+ kPS_ModifyDate = 0x00000008,
+ /// Document Creator/Author
+ kPS_Creator = 0x00000010,
+ /// Document Title
+ kPS_Title = 0x00000020,
+ /// Document Desciption
+ kPS_Description = 0x00000040,
+ /// Document Subject/Keywords
+ kPS_Subject = 0x00000080,
+ /// ADO_ContainsXMP hint
+ kPS_ADOContainsXMP = 0x00000100,
+ /// End Comments
+ kPS_EndComments = 0x00000200,
+ /// Begin Prolog
+ kPS_BeginProlog = 0x00000400,
+ /// End Prolog
+ kPS_EndProlog = 0x00000800,
+ /// Begin Setup
+ kPS_BeginSetup = 0x00001000,
+ /// End Setup
+ kPS_EndSetup = 0x00002000,
+ /// Page
+ kPS_Page = 0x00004000,
+ /// End Page Comments
+ kPS_EndPageComments = 0x00008000,
+ /// Begin Page SetUp
+ kPS_BeginPageSetup = 0x00010000,
+ /// End Page SetUp
+ kPS_EndPageSetup = 0x00020000,
+ /// Trailer
+ kPS_Trailer = 0x00040000,
+ /// EOF
+ kPS_EOF = 0x00080000,
+ /// End PostScript
+ kPS_EndPostScript = 0x00100000,
+ /// Max Token
+ kPS_MaxToken = 0x00200000
+};
+
+enum NativeMetadataIndex{
+ // --------------------
+ // Index native Metadata ina PS file
+ kPS_dscCreator = 0,
+ kPS_dscCreateDate = 1,
+ kPS_dscFor = 2,
+ kPS_dscTitle = 3,
+ kPS_docInfoCreator = 4,
+ kPS_docInfoCreateDate = 5,
+ kPS_docInfoModDate = 6,
+ kPS_docInfoAuthor = 7,
+ kPS_docInfoTitle = 8,
+ kPS_docInfoSubject = 9,
+ kPS_docInfoKeywords = 10,
+ kPS_MaxNativeIndexValue
+};
+
+static XMP_Uns64 nativeIndextoFlag[]={ kPS_CreatorTool,
+ kPS_CreateDate,
+ kPS_Creator,
+ kPS_Title,
+ kPS_CreatorTool,
+ kPS_CreateDate,
+ kPS_ModifyDate,
+ kPS_Creator,
+ kPS_Title,
+ kPS_Description,
+ kPS_Subject
+ };
+
+static const std::string kPSFileTag = "%!PS-Adobe-";
+static const std::string kPSContainsXMPString = "%ADO_ContainsXMP:";
+static const std::string kPSContainsBBoxString = "%%BoundingBox:";
+static const std::string kPSContainsBeginDocString = "%%BeginDocument:";
+static const std::string kPSContainsEndDocString = "%%EndDocument";
+static const std::string kPSContainsTrailerString = "%%Trailer";
+static const std::string kPSContainsCreatorString = "%%Creator:";
+static const std::string kPSContainsCreateDateString = "%%CreationDate:";
+static const std::string kPSContainsForString = "%%For:";
+static const std::string kPSContainsTitleString = "%%Title:";
+static const std::string kPSContainsAtendString = "(atend)";
+static const std::string kPSEndCommentString = "%%EndComments"; // ! Assumed shorter than kPSContainsXMPString.
+static const std::string kPSContainsDocInfoString = "/DOCINFO";
+static const std::string kPSContainsPdfmarkString = "pdfmark";
+static const std::string kPS_XMPHintMainFirst="%ADO_ContainsXMP: MainFirst\n";
+static const std::string kPS_XMPHintMainLast="%ADO_ContainsXMP: MainLast\n";
+
+// For new xpacket injection into the EPS file is done in Postscript using the pdfmark operator
+// There are different conventions described for EPS and PS files in XMP Spec part 3.
+// The tokens kEPS_Injectdata1, kEPS_Injectdata2 and kEPS_Injectdata3 are used to
+// embedd xpacket in EPS files.the xpacket is written inbetween kEPS_Injectdata1 and kEPS_Injectdata2.
+// The tokens kPS_Injectdata1 and kPS_Injectdata2 are used to embedd xpacket in DSC compliant PS files
+// The code inside the tokens is taken from examples in XMP Spec part 3
+// section 2.6.2 PS, EPS (PostScript® and Encapsulated PostScript)
+static const std::string kEPS_Injectdata1="\n/currentdistillerparams where\n"
+"{pop currentdistillerparams /CoreDistVersion get 5000 lt} {true} ifelse\n"
+"{userdict /EPSHandler1_pdfmark /cleartomark load put\n"
+"userdict /EPSHandler1_ReadMetadata_pdfmark {flushfile cleartomark} bind put}\n"
+"{ userdict /EPSHandler1_pdfmark /pdfmark load put\n"
+"userdict /EPSHandler1_ReadMetadata_pdfmark {/PUT pdfmark} bind put } ifelse\n"
+"[/NamespacePush EPSHandler1_pdfmark\n"
+"[/_objdef {eps_metadata_stream} /type /stream /OBJ EPSHandler1_pdfmark\n"
+"[{eps_metadata_stream} 2 dict begin\n"
+"/Type /Metadata def /Subtype /XML def currentdict end /PUT EPSHandler1_pdfmark\n"
+"[{eps_metadata_stream}\n"
+"currentfile 0 (% &&end EPS XMP packet marker&&)\n"
+"/SubFileDecode filter EPSHandler1_ReadMetadata_pdfmark\n";
+
+static const std::string kPS_Injectdata1="\n/currentdistillerparams where\n"
+"{pop currentdistillerparams /CoreDistVersion get 5000 lt} {true} ifelse\n"
+"{userdict /PSHandler1_pdfmark /cleartomark load put\n"
+"userdict /PSHandler1_ReadMetadata_pdfmark {flushfile cleartomark} bind put}\n"
+"{ userdict /PSHandler1_pdfmark /pdfmark load put\n"
+"userdict /PSHandler1_ReadMetadata_pdfmark {/PUT pdfmark} bind put } ifelse\n"
+"[/NamespacePush PSHandler1_pdfmark\n"
+"[/_objdef {ps_metadata_stream} /type /stream /OBJ PSHandler1_pdfmark\n"
+"[{ps_metadata_stream} 2 dict begin\n"
+"/Type /Metadata def /Subtype /XML def currentdict end /PUT PSHandler1_pdfmark\n"
+"[{ps_metadata_stream}\n"
+"currentfile 0 (% &&end PS XMP packet marker&&)\n"
+"/SubFileDecode filter PSHandler1_ReadMetadata_pdfmark\n";
+
+static const std::string kEPS_Injectdata2="\n% &&end EPS XMP packet marker&&\n"
+"[/Document\n"
+"1 dict begin /Metadata {eps_metadata_stream} def\n"
+"currentdict end /BDC EPSHandler1_pdfmark\n"
+"[/NamespacePop EPSHandler1_pdfmark\n";
+
+
+static const std::string kPS_Injectdata2="\n% &&end PS XMP packet marker&&\n"
+"[{Catalog} {ps_metadata_stream} /Metadata PSHandler1_pdfmark\n"
+"[/NamespacePop PSHandler1_pdfmark\n";
+
+static const std::string kEPS_Injectdata3="\n/currentdistillerparams where\n"
+ "{pop currentdistillerparams /CoreDistVersion get 5000 lt} {true} ifelse\n"
+ "{userdict /EPSHandler1_pdfmark /cleartomark load put}\n"
+ "{ userdict /EPSHandler1_pdfmark /pdfmark load put} ifelse\n"
+ "[/EMC EPSHandler1_pdfmark\n";
+
+
+namespace PostScript_Support
+{
+ struct Date
+ {
+ short day;
+ short month;
+ short year;
+ short hours;
+ short minutes;
+ short seconds;
+ bool containsOffset;
+ char offsetSign;
+ short offsetHour;
+ short offsetMin;
+ Date(short pday=1,short pmonth=1,short pyear=1900,short phours=0,
+ short pminutes=0,short pseconds=0):day(pday),month(pmonth),
+ year(pyear),hours(phours),minutes(pminutes),seconds(pseconds),
+ containsOffset(false),offsetSign('+'),offsetHour(0),offsetMin(0)
+ {
+ }
+ bool operator==(const Date &a)
+ {
+ return this->day==a.day &&
+ this->month==a.month &&
+ this->year==a.year &&
+ this->hours==a.hours &&
+ this->minutes==a.minutes &&
+ this->seconds==a.seconds &&
+ this->containsOffset==a.containsOffset &&
+ this->offsetSign==a.offsetSign &&
+ this->offsetHour==a.offsetHour &&
+ this->offsetMin==a.offsetMin;
+ }
+ };
+ struct DateTimeTokens
+ {
+ std::string token;
+ short noOfDelimiter;
+ char delimiter;
+ DateTimeTokens(std::string ptoken="",short pnoOfDelimiter=0,char pdelimiter=0):
+ token(ptoken),noOfDelimiter(pnoOfDelimiter),delimiter(pdelimiter)
+ {
+ }
+ };
+
+ //function to parse strings and get date out of it
+ std::string ConvertToDate(const char* inString);
+ // These helpers are similar to RefillBuffer and CheckFileSpace with the difference that the it traverses
+ // the file stream in reverse order
+ void RevRefillBuffer ( XMP_IO* fileRef, IOBuffer* ioBuf );
+ bool RevCheckFileSpace ( XMP_IO* fileRef, IOBuffer* ioBuf, size_t neededLen );
+
+ // function to detect character codes greater than 127 in a string
+ bool HasCodesGT127(const std::string & value);
+
+ // function moves the file pointer ahead such that it skips all tabs and spaces
+ bool SkipTabsAndSpaces(XMP_IO* file,IOBuffer& ioBuf);
+
+ // function moves the file pointer ahead such that it skips all characters until a newline
+ bool SkipUntilNewline(XMP_IO* file,IOBuffer& ioBuf);
+
+ // function to detect character codes greater than 127 in a string
+ bool IsValidPSFile(XMP_IO* fileRef,XMP_FileFormat &format);
+
+ // Determines Whether the metadata is embedded using the Sub-FileDecode Approach or no
+ bool IsSFDFilterUsed(XMP_IO* &fileRef, XMP_Int64 xpacketOffset);
+
+} // namespace PostScript_Support
+
+#endif // __PostScript_Support_hpp__
diff --git a/XMPFiles/source/FormatSupport/QuickTime_Support.cpp b/XMPFiles/source/FormatSupport/QuickTime_Support.cpp
index 5a33ab4..5dde0c5 100644
--- a/XMPFiles/source/FormatSupport/QuickTime_Support.cpp
+++ b/XMPFiles/source/FormatSupport/QuickTime_Support.cpp
@@ -22,6 +22,7 @@
#include "source/UnicodeInlines.incl_cpp"
#include "XMPFiles/source/FormatSupport/Reconcile_Impl.hpp"
#include "source/XIO.hpp"
+#include "source/EndianUtils.hpp"
// =================================================================================================
diff --git a/XMPFiles/source/FormatSupport/RIFF.hpp b/XMPFiles/source/FormatSupport/RIFF.hpp
index 533d9a3..e2451e5 100644
--- a/XMPFiles/source/FormatSupport/RIFF.hpp
+++ b/XMPFiles/source/FormatSupport/RIFF.hpp
@@ -174,7 +174,11 @@ namespace RIFF {
// =================================================================================================
// ImportCr8rItems
// ===============
+#if SUNOS_SPARC || SUNOS_X86
+ #pragma pack ( 1 )
+#else
#pragma pack ( push, 1 )
+#endif //#if SUNOS_SPARC || SUNOS_X86
struct PrmLBoxContent {
XMP_Uns32 magic;
XMP_Uns32 size;
@@ -199,7 +203,11 @@ namespace RIFF {
char appOptions[16];
char appName[32];
};
+#if SUNOS_SPARC || SUNOS_X86
+ #pragma pack ( )
+#else
#pragma pack ( pop )
+#endif //#if SUNOS_SPARC || SUNOS_X86
// static getter, determines appropriate chunkType (peeking)and returns
// the respective constructor. It's the caller's responsibility to
diff --git a/XMPFiles/source/FormatSupport/ReconcileTIFF.cpp b/XMPFiles/source/FormatSupport/ReconcileTIFF.cpp
index a3195ab..cad1138 100644
--- a/XMPFiles/source/FormatSupport/ReconcileTIFF.cpp
+++ b/XMPFiles/source/FormatSupport/ReconcileTIFF.cpp
@@ -19,6 +19,8 @@
#define snprintf _snprintf
#endif
+#include "source/EndianUtils.hpp"
+
#if XMP_WinBuild
#pragma warning ( disable : 4146 ) // unary minus operator applied to unsigned type
#pragma warning ( disable : 4800 ) // forcing value to bool 'true' or 'false'
@@ -387,7 +389,7 @@ ImportSingleTIFF_Short ( const TIFF_Manager::TagInfo & tagInfo, const bool nativ
{
try { // Don't let errors with one stop the others.
- XMP_Uns16 binValue = *((XMP_Uns16*)tagInfo.dataPtr);
+ XMP_Uns16 binValue = GetUns16AsIs ( tagInfo.dataPtr );
if ( ! nativeEndian ) binValue = Flip2 ( binValue );
char strValue[20];
@@ -412,7 +414,7 @@ ImportSingleTIFF_Long ( const TIFF_Manager::TagInfo & tagInfo, const bool native
{
try { // Don't let errors with one stop the others.
- XMP_Uns32 binValue = *((XMP_Uns32*)tagInfo.dataPtr);
+ XMP_Uns32 binValue = GetUns32AsIs ( tagInfo.dataPtr );
if ( ! nativeEndian ) binValue = Flip4 ( binValue );
char strValue[20];
@@ -438,8 +440,8 @@ ImportSingleTIFF_Rational ( const TIFF_Manager::TagInfo & tagInfo, const bool na
try { // Don't let errors with one stop the others.
XMP_Uns32 * binPtr = (XMP_Uns32*)tagInfo.dataPtr;
- XMP_Uns32 binNum = binPtr[0];
- XMP_Uns32 binDenom = binPtr[1];
+ XMP_Uns32 binNum = GetUns32AsIs ( &binPtr[0] );
+ XMP_Uns32 binDenom = GetUns32AsIs ( &binPtr[1] );
if ( ! nativeEndian ) {
binNum = Flip4 ( binNum );
binDenom = Flip4 ( binDenom );
@@ -467,9 +469,14 @@ ImportSingleTIFF_SRational ( const TIFF_Manager::TagInfo & tagInfo, const bool n
{
try { // Don't let errors with one stop the others.
- XMP_Int32 * binPtr = (XMP_Int32*)tagInfo.dataPtr;
- XMP_Int32 binNum = binPtr[0];
- XMP_Int32 binDenom = binPtr[1];
+#if SUNOS_SPARC
+ XMP_Uns32 binPtr[2];
+ memcpy(&binPtr, tagInfo.dataPtr, sizeof(XMP_Uns32)*2);
+#else
+ XMP_Uns32 * binPtr = (XMP_Uns32*)tagInfo.dataPtr;
+#endif //#if SUNOS_SPARC
+ XMP_Int32 binNum = GetUns32AsIs ( &binPtr[0] );
+ XMP_Int32 binDenom = GetUns32AsIs ( &binPtr[1] );
if ( ! nativeEndian ) {
Flip4 ( &binNum );
Flip4 ( &binDenom );
@@ -537,7 +544,7 @@ ImportSingleTIFF_Byte ( const TIFF_Manager::TagInfo & tagInfo,
XMP_Uns8 binValue = *((XMP_Uns8*)tagInfo.dataPtr);
char strValue[20];
- snprintf ( strValue, sizeof(strValue), "%hu", binValue ); // AUDIT: Using sizeof(strValue) is safe.
+ snprintf ( strValue, sizeof(strValue), "%hu", (XMP_Uns16)binValue ); // AUDIT: Using sizeof(strValue) is safe.
xmp->SetProperty ( xmpNS, xmpProp, strValue );
@@ -561,7 +568,7 @@ ImportSingleTIFF_SByte ( const TIFF_Manager::TagInfo & tagInfo,
XMP_Int8 binValue = *((XMP_Int8*)tagInfo.dataPtr);
char strValue[20];
- snprintf ( strValue, sizeof(strValue), "%hd", binValue ); // AUDIT: Using sizeof(strValue) is safe.
+ snprintf ( strValue, sizeof(strValue), "%hd", (short)binValue ); // AUDIT: Using sizeof(strValue) is safe.
xmp->SetProperty ( xmpNS, xmpProp, strValue );
@@ -815,8 +822,8 @@ ImportArrayTIFF_Rational ( const TIFF_Manager::TagInfo & tagInfo, const bool nat
for ( size_t i = 0; i < tagInfo.count; ++i, binPtr += 2 ) {
- XMP_Uns32 binNum = binPtr[0];
- XMP_Uns32 binDenom = binPtr[1];
+ XMP_Uns32 binNum = GetUns32AsIs ( &binPtr[0] );
+ XMP_Uns32 binDenom = GetUns32AsIs ( &binPtr[1] );
if ( ! nativeEndian ) {
binNum = Flip4 ( binNum );
binDenom = Flip4 ( binDenom );
@@ -936,7 +943,7 @@ ImportArrayTIFF_Byte ( const TIFF_Manager::TagInfo & tagInfo,
XMP_Uns8 binValue = *binPtr;
char strValue[20];
- snprintf ( strValue, sizeof(strValue), "%hu", binValue ); // AUDIT: Using sizeof(strValue) is safe.
+ snprintf ( strValue, sizeof(strValue), "%hu", (XMP_Uns16)binValue ); // AUDIT: Using sizeof(strValue) is safe.
xmp->AppendArrayItem ( xmpNS, xmpProp, kXMP_PropArrayIsOrdered, strValue );
@@ -968,7 +975,7 @@ ImportArrayTIFF_SByte ( const TIFF_Manager::TagInfo & tagInfo,
XMP_Int8 binValue = *binPtr;
char strValue[20];
- snprintf ( strValue, sizeof(strValue), "%hd", binValue ); // AUDIT: Using sizeof(strValue) is safe.
+ snprintf ( strValue, sizeof(strValue), "%hd", (short)binValue ); // AUDIT: Using sizeof(strValue) is safe.
xmp->AppendArrayItem ( xmpNS, xmpProp, kXMP_PropArrayIsOrdered, strValue );
@@ -1420,7 +1427,7 @@ ImportTIFF_Flash ( const TIFF_Manager::TagInfo & tagInfo, bool nativeEndian,
{
try { // Don't let errors with one stop the others.
- XMP_Uns16 binValue = *((XMP_Uns16*)tagInfo.dataPtr);
+ XMP_Uns16 binValue = GetUns16AsIs ( tagInfo.dataPtr );
if ( ! nativeEndian ) binValue = Flip2 ( binValue );
bool fired = (bool)(binValue & 1); // Avoid implicit 0/1 conversion.
@@ -1559,7 +1566,7 @@ ImportTIFF_CFATable ( const TIFF_Manager::TagInfo & tagInfo, bool nativeEndian,
SXMPUtils::ComposeStructFieldPath ( xmpNS, xmpProp, kXMP_NS_EXIF, "Values", &arrayPath );
for ( size_t i = (columns * rows); i > 0; --i, ++bytePtr ) {
- snprintf ( buffer, sizeof(buffer), "%hu", *bytePtr ); // AUDIT: Use of sizeof(buffer) is safe.
+ snprintf ( buffer, sizeof(buffer), "%hu", (XMP_Uns16)(*bytePtr) ); // AUDIT: Use of sizeof(buffer) is safe.
xmp->AppendArrayItem ( xmpNS, arrayPath.c_str(), kXMP_PropArrayIsOrdered, buffer );
}
@@ -1679,14 +1686,15 @@ ImportTIFF_GPSCoordinate ( const TIFF_Manager & tiff, const TIFF_Manager::TagInf
secDenom = Flip4 ( secDenom );
}
- degNum = binPtr[0];
- degDenom = binPtr[1];
+ degNum = GetUns32AsIs ( &binPtr[0] );
+ degDenom = GetUns32AsIs ( &binPtr[1] );
+
if ( posInfo.count >= 2 ) {
- minNum = binPtr[2];
- minDenom = binPtr[3];
+ minNum = GetUns32AsIs ( &binPtr[2] );
+ minDenom = GetUns32AsIs ( &binPtr[3] );
if ( posInfo.count >= 3 ) {
- secNum = binPtr[4];
- secDenom = binPtr[5];
+ secNum = GetUns32AsIs ( &binPtr[4] );
+ secDenom = GetUns32AsIs ( &binPtr[5] );
}
}
@@ -1790,12 +1798,12 @@ ImportTIFF_GPSTimeStamp ( const TIFF_Manager & tiff, const TIFF_Manager::TagInfo
if ( (dateStr[10] != 0) && (dateStr[10] != ' ') ) return;
XMP_Uns32 * binPtr = (XMP_Uns32*)timeInfo.dataPtr;
- XMP_Uns32 hourNum = binPtr[0];
- XMP_Uns32 hourDenom = binPtr[1];
- XMP_Uns32 minNum = binPtr[2];
- XMP_Uns32 minDenom = binPtr[3];
- XMP_Uns32 secNum = binPtr[4];
- XMP_Uns32 secDenom = binPtr[5];
+ XMP_Uns32 hourNum = GetUns32AsIs ( &binPtr[0] );
+ XMP_Uns32 hourDenom = GetUns32AsIs ( &binPtr[1] );
+ XMP_Uns32 minNum = GetUns32AsIs ( &binPtr[2] );
+ XMP_Uns32 minDenom = GetUns32AsIs ( &binPtr[3] );
+ XMP_Uns32 secNum = GetUns32AsIs ( &binPtr[4] );
+ XMP_Uns32 secDenom = GetUns32AsIs ( &binPtr[5] );
if ( ! nativeEndian ) {
hourNum = Flip4 ( hourNum );
hourDenom = Flip4 ( hourDenom );
@@ -2116,7 +2124,8 @@ PhotoDataUtils::Import2WayExif ( const TIFF_Manager & exif, SXMPMeta * xmp, int
found = exif.GetTag ( kTIFF_ExifIFD, kTIFF_ExifVersion, &tagInfo );
if ( found && (tagInfo.type == kTIFF_UndefinedType) && (tagInfo.count == 4) ) {
char str[5];
- *((XMP_Uns32*)str) = *((XMP_Uns32*)tagInfo.dataPtr);
+
+ *((XMP_Uns32*)str) = GetUns32AsIs ( tagInfo.dataPtr );
str[4] = 0;
xmp->SetProperty ( kXMP_NS_EXIF, "ExifVersion", str );
}
@@ -2125,7 +2134,8 @@ PhotoDataUtils::Import2WayExif ( const TIFF_Manager & exif, SXMPMeta * xmp, int
found = exif.GetTag ( kTIFF_ExifIFD, kTIFF_FlashpixVersion, &tagInfo );
if ( found && (tagInfo.type == kTIFF_UndefinedType) && (tagInfo.count == 4) ) {
char str[5];
- *((XMP_Uns32*)str) = *((XMP_Uns32*)tagInfo.dataPtr);
+
+ *((XMP_Uns32*)str) = GetUns32AsIs ( tagInfo.dataPtr );
str[4] = 0;
xmp->SetProperty ( kXMP_NS_EXIF, "FlashpixVersion", str );
}
diff --git a/XMPFiles/source/FormatSupport/SWF_Support.cpp b/XMPFiles/source/FormatSupport/SWF_Support.cpp
index ec7b9a6..5ff80f1 100644
--- a/XMPFiles/source/FormatSupport/SWF_Support.cpp
+++ b/XMPFiles/source/FormatSupport/SWF_Support.cpp
@@ -127,7 +127,7 @@ XMP_Int64 SWF_IO::DecompressFileToMemory ( XMP_IO * fileIn, RawDataBlock * dataO
XMP_Int32 ioCount;
XMP_Int64 offsetIn;
const XMP_Int64 lengthIn = fileIn->Length();
- XMP_Enforce ( (SWF_IO::HeaderPrefixSize <= lengthIn) && (lengthIn <= SWF_IO::MaxExpandedSize) );
+ XMP_Enforce ( ((XMP_Int64)SWF_IO::HeaderPrefixSize <= lengthIn) && (lengthIn <= SWF_IO::MaxExpandedSize) );
// Set the uncompressed part of the header. Save the expanded size from the file.
diff --git a/XMPFiles/source/FormatSupport/TIFF_FileWriter.cpp b/XMPFiles/source/FormatSupport/TIFF_FileWriter.cpp
index 5f94a9d..da4d260 100644
--- a/XMPFiles/source/FormatSupport/TIFF_FileWriter.cpp
+++ b/XMPFiles/source/FormatSupport/TIFF_FileWriter.cpp
@@ -11,8 +11,11 @@
#include "public/include/XMP_Const.h"
#include "XMPFiles/source/FormatSupport/TIFF_Support.hpp"
+
#include "source/XIO.hpp"
+#include "source/EndianUtils.hpp"
+
// =================================================================================================
/// \file TIFF_FileWriter.cpp
/// \brief TIFF_FileWriter is used for memory-based read-write access and all file-based access.
@@ -587,15 +590,26 @@ void TIFF_FileWriter::ParseMemoryStream ( const void* data, XMP_Uns32 length, bo
memcpy ( this->memStream, data, length ); // AUDIT: Safe, malloc'ed length bytes above.
this->ownedStream = true;
}
-
+
this->tiffLength = length;
XMP_Uns32 ifdLimit = this->tiffLength - 6; // An IFD must start before this offset.
- // Find and process the primary, Exif, GPS, and Interoperability IFDs.
+ // Find and process the primary, thumbnail, Exif, GPS, and Interoperability IFDs.
XMP_Uns32 primaryIFDOffset = this->CheckTIFFHeader ( this->memStream, length );
- if ( primaryIFDOffset != 0 ) (void) this->ProcessMemoryIFD ( primaryIFDOffset, kTIFF_PrimaryIFD );
+ if ( primaryIFDOffset != 0 ) {
+ XMP_Uns32 tnailOffset = this->ProcessMemoryIFD ( primaryIFDOffset, kTIFF_PrimaryIFD );
+ if ( tnailOffset != 0 ) {
+ if ( IsOffsetValid ( tnailOffset, 8, ifdLimit ) ) { // Remove a bad Thumbnail IFD Offset
+ ( void ) this->ProcessMemoryIFD ( tnailOffset, kTIFF_TNailIFD );
+ } else {
+ XMP_Error error ( kXMPErr_BadTIFF, "Bad IFD offset" );
+ this->NotifyClient (kXMPErrSev_Recoverable, error );
+ this->DeleteTag ( kTIFF_PrimaryIFD, kTIFF_TNailIFD );
+ }
+ }
+ }
const InternalTagInfo* exifIFDTag = this->FindTagInIFD ( kTIFF_PrimaryIFD, kTIFF_ExifIFDPointer );
if ( (exifIFDTag != 0) && (exifIFDTag->type == kTIFF_LongType) && (exifIFDTag->dataLen == 4) ) {
@@ -606,9 +620,11 @@ void TIFF_FileWriter::ParseMemoryStream ( const void* data, XMP_Uns32 length, bo
const InternalTagInfo* gpsIFDTag = this->FindTagInIFD ( kTIFF_PrimaryIFD, kTIFF_GPSInfoIFDPointer );
if ( (gpsIFDTag != 0) && (gpsIFDTag->type == kTIFF_LongType) && (gpsIFDTag->dataLen == 4) ) {
XMP_Uns32 gpsOffset = this->GetUns32 ( gpsIFDTag->dataPtr );
- if ( (8 <= gpsOffset) && (gpsOffset < ifdLimit) ) { // Remove a bad GPS IFD offset.
+ if ( IsOffsetValid ( gpsOffset, 8, ifdLimit ) ) { // Remove a bad GPS IFD offset.
(void) this->ProcessMemoryIFD ( gpsOffset, kTIFF_GPSInfoIFD );
} else {
+ XMP_Error error ( kXMPErr_BadTIFF, "Bad IFD offset" );
+ this->NotifyClient (kXMPErrSev_Recoverable, error );
this->DeleteTag ( kTIFF_PrimaryIFD, kTIFF_GPSInfoIFDPointer );
}
}
@@ -616,9 +632,11 @@ void TIFF_FileWriter::ParseMemoryStream ( const void* data, XMP_Uns32 length, bo
const InternalTagInfo* interopIFDTag = this->FindTagInIFD ( kTIFF_ExifIFD, kTIFF_InteroperabilityIFDPointer );
if ( (interopIFDTag != 0) && (interopIFDTag->type == kTIFF_LongType) && (interopIFDTag->dataLen == 4) ) {
XMP_Uns32 interopOffset = this->GetUns32 ( interopIFDTag->dataPtr );
- if ( (8 <= interopOffset) && (interopOffset < ifdLimit) ) { // Remove a bad Interoperability IFD offset.
+ if ( IsOffsetValid ( interopOffset, 8, ifdLimit ) ) { // Remove a bad Interoperability IFD offset.
(void) this->ProcessMemoryIFD ( interopOffset, kTIFF_InteropIFD );
} else {
+ XMP_Error error ( kXMPErr_BadTIFF, "Bad IFD offset" );
+ this->NotifyClient (kXMPErrSev_Recoverable, error );
this->DeleteTag ( kTIFF_ExifIFD, kTIFF_InteroperabilityIFDPointer );
}
}
@@ -654,16 +672,22 @@ XMP_Uns32 TIFF_FileWriter::ProcessMemoryIFD ( XMP_Uns32 ifdOffset, XMP_Uns8 ifd
InternalIFDInfo& ifdInfo ( this->containedIFDs[ifd] );
if ( (ifdOffset < 8) || (ifdOffset > (this->tiffLength - kEmptyIFDLength)) ) {
- XMP_Throw ( "Bad IFD offset", kXMPErr_BadTIFF );
+ XMP_Error error ( kXMPErr_BadTIFF, "Bad IFD offset" );
+ this->NotifyClient ( kXMPErrSev_FileFatal, error );
}
XMP_Uns8* ifdPtr = this->memStream + ifdOffset;
XMP_Uns16 tagCount = this->GetUns16 ( ifdPtr );
RawIFDEntry* ifdEntries = (RawIFDEntry*)(ifdPtr+2);
- if ( tagCount >= 0x8000 ) XMP_Throw ( "Outrageous IFD count", kXMPErr_BadTIFF );
+ if ( tagCount >= 0x8000 ) {
+ XMP_Error error ( kXMPErr_BadTIFF, "Outrageous IFD count" );
+ this->NotifyClient ( kXMPErrSev_FileFatal, error );
+ }
+
if ( (XMP_Uns32)(2 + tagCount*12 + 4) > (this->tiffLength - ifdOffset) ) {
- XMP_Throw ( "Out of bounds IFD", kXMPErr_BadTIFF );
+ XMP_Error error ( kXMPErr_BadTIFF, "Out of bounds IFD" );
+ this->NotifyClient ( kXMPErrSev_FileFatal, error );
}
ifdInfo.origIFDOffset = ifdOffset;
@@ -683,7 +707,11 @@ XMP_Uns32 TIFF_FileWriter::ProcessMemoryIFD ( XMP_Uns32 ifdOffset, XMP_Uns8 ifd
InternalTagInfo& mapTag = newPos->second;
mapTag.dataLen = mapTag.origDataLen = mapTag.count * (XMP_Uns32)kTIFF_TypeSizes[mapTag.type];
- mapTag.smallValue = rawTag->dataOrOffset; // Keep the value or offset in stream byte ordering.
+#if SUNOS_SPARC
+ mapTag.smallValue = IE.getUns32(&rawTag->dataOrOffset);
+#else
+ mapTag.smallValue = GetUns32AsIs ( &rawTag->dataOrOffset ); // Keep the value or offset in stream byte ordering.
+#endif //#if SUNOS_SPARC
if ( mapTag.dataLen <= 4 ) {
mapTag.origDataOffset = ifdOffset + 2 + (12 * (XMP_Uns32)i) + 8; // Compute the data offset.
@@ -721,39 +749,50 @@ XMP_Uns32 TIFF_FileWriter::ProcessMemoryIFD ( XMP_Uns32 ifdOffset, XMP_Uns8 ifd
void TIFF_FileWriter::ParseFileStream ( XMP_IO* fileRef )
{
- bool ok;
- IOBuffer ioBuf;
this->DeleteExistingInfo();
this->fileParsed = true;
this->tiffLength = (XMP_Uns32) fileRef->Length();
- if ( this->tiffLength == 0 ) return;
+ if ( this->tiffLength < 8 ) return; // Ignore empty or impossibly short.
+ fileRef->Rewind ( );
XMP_Uns32 ifdLimit = this->tiffLength - 6; // An IFD must start before this offset.
// Find and process the primary, Exif, GPS, and Interoperability IFDs.
- ioBuf.filePos = 0;
- fileRef->Rewind ( );
- ok = CheckFileSpace ( fileRef, &ioBuf, 8 );
- if ( ! ok ) XMP_Throw ( "TIFF too small", kXMPErr_BadTIFF );
+ XMP_Uns8 tiffHeader [8];
+ fileRef->ReadAll ( tiffHeader, sizeof(tiffHeader) );
+ XMP_Uns32 primaryIFDOffset = this->CheckTIFFHeader ( tiffHeader, this->tiffLength );
- XMP_Uns32 primaryIFDOffset = this->CheckTIFFHeader ( ioBuf.ptr, this->tiffLength );
-
- if ( primaryIFDOffset != 0 ) (void) this->ProcessFileIFD ( kTIFF_PrimaryIFD, primaryIFDOffset, fileRef, &ioBuf );
+ if ( primaryIFDOffset == 0 ) {
+ return;
+ } else {
+ XMP_Uns32 tnailOffset = this->ProcessFileIFD ( kTIFF_PrimaryIFD, primaryIFDOffset, fileRef );
+ if ( tnailOffset != 0 ) {
+ if ( IsOffsetValid ( tnailOffset, 8, ifdLimit ) ) {
+ ( void ) this->ProcessFileIFD ( kTIFF_TNailIFD, tnailOffset, fileRef );
+ } else {
+ XMP_Error error ( kXMPErr_BadTIFF, "Bad IFD offset" );
+ this->NotifyClient ( kXMPErrSev_Recoverable, error );
+ this->DeleteTag( kTIFF_PrimaryIFD, kTIFF_TNailIFD );
+ }
+ }
+ }
const InternalTagInfo* exifIFDTag = this->FindTagInIFD ( kTIFF_PrimaryIFD, kTIFF_ExifIFDPointer );
if ( (exifIFDTag != 0) && (exifIFDTag->type == kTIFF_LongType) && (exifIFDTag->count == 1) ) {
XMP_Uns32 exifOffset = this->GetUns32 ( exifIFDTag->dataPtr );
- (void) this->ProcessFileIFD ( kTIFF_ExifIFD, exifOffset, fileRef, &ioBuf );
+ (void) this->ProcessFileIFD ( kTIFF_ExifIFD, exifOffset, fileRef );
}
const InternalTagInfo* gpsIFDTag = this->FindTagInIFD ( kTIFF_PrimaryIFD, kTIFF_GPSInfoIFDPointer );
if ( (gpsIFDTag != 0) && (gpsIFDTag->type == kTIFF_LongType) && (gpsIFDTag->count == 1) ) {
XMP_Uns32 gpsOffset = this->GetUns32 ( gpsIFDTag->dataPtr );
- if ( (8 <= gpsOffset) && (gpsOffset < ifdLimit) ) { // Remove a bad GPS IFD offset.
- (void) this->ProcessFileIFD ( kTIFF_GPSInfoIFD, gpsOffset, fileRef, &ioBuf );
+ if ( IsOffsetValid (gpsOffset, 8, ifdLimit ) ) { // Remove a bad GPS IFD offset.
+ (void) this->ProcessFileIFD ( kTIFF_GPSInfoIFD, gpsOffset, fileRef );
} else {
+ XMP_Error error ( kXMPErr_BadTIFF, "Bad IFD offset" );
+ this->NotifyClient ( kXMPErrSev_Recoverable, error );
this->DeleteTag ( kTIFF_PrimaryIFD, kTIFF_GPSInfoIFDPointer );
}
}
@@ -761,9 +800,11 @@ void TIFF_FileWriter::ParseFileStream ( XMP_IO* fileRef )
const InternalTagInfo* interopIFDTag = this->FindTagInIFD ( kTIFF_ExifIFD, kTIFF_InteroperabilityIFDPointer );
if ( (interopIFDTag != 0) && (interopIFDTag->type == kTIFF_LongType) && (interopIFDTag->dataLen == 4) ) {
XMP_Uns32 interopOffset = this->GetUns32 ( interopIFDTag->dataPtr );
- if ( (8 <= interopOffset) && (interopOffset < ifdLimit) ) { // Remove a bad Interoperability IFD offset.
- (void) this->ProcessFileIFD ( kTIFF_InteropIFD, interopOffset, fileRef, &ioBuf );
+ if ( IsOffsetValid (interopOffset, 8, ifdLimit ) ) { // Remove a bad Interoperability IFD offset.
+ (void) this->ProcessFileIFD ( kTIFF_InteropIFD, interopOffset, fileRef );
} else {
+ XMP_Error error ( kXMPErr_BadTIFF, "Bad IFD offset" );
+ this->NotifyClient ( kXMPErrSev_Recoverable, error );
this->DeleteTag ( kTIFF_ExifIFD, kTIFF_InteroperabilityIFDPointer );
}
}
@@ -793,25 +834,36 @@ void TIFF_FileWriter::ParseFileStream ( XMP_IO* fileRef )
// =================================================================================================
// TIFF_FileWriter::ProcessFileIFD
// ===============================
+//
+// Each IFD has a UInt16 count of IFD entries, a sequence of 12 byte IFD entries, then a UInt32
+// offset to the next IFD. The integer byte order is determined by the II or MM at the TIFF start.
-XMP_Uns32 TIFF_FileWriter::ProcessFileIFD ( XMP_Uns8 ifd, XMP_Uns32 ifdOffset, XMP_IO* fileRef, void* _ioBuf )
+XMP_Uns32 TIFF_FileWriter::ProcessFileIFD ( XMP_Uns8 ifd, XMP_Uns32 ifdOffset, XMP_IO* fileRef )
{
- IOBuffer* ioBuf = (IOBuffer*)_ioBuf; // *** Temporary hack, _ioBuf is IOBuffer* but don't want to include XIO.hpp.
+ static const size_t ifdBufferSize = 12*65536; // Enough for the largest possible IFD.
+ std::vector<XMP_Uns8> ifdBuffer(ifdBufferSize);
+ XMP_Uns8 intBuffer [4]; // For the IFD count and offset to next IFD.
+
InternalIFDInfo& ifdInfo ( this->containedIFDs[ifd] );
if ( (ifdOffset < 8) || (ifdOffset > (this->tiffLength - kEmptyIFDLength)) ) {
XMP_Throw ( "Bad IFD offset", kXMPErr_BadTIFF );
}
- MoveToOffset ( fileRef, ifdOffset, ioBuf ); // Move to the start of the IFD.
+ fileRef->Seek ( ifdOffset, kXMP_SeekFromStart );
+ if ( ! XIO::CheckFileSpace ( fileRef, 2 ) ) return 0; // Bail for a truncated file.
+ fileRef->ReadAll ( intBuffer, 2 );
- bool ok = CheckFileSpace ( fileRef, ioBuf, 2 );
- if ( ! ok ) XMP_Throw ( "IFD count missing", kXMPErr_BadTIFF );
- XMP_Uns16 tagCount = this->GetUns16 ( ioBuf->ptr );
+ XMP_Uns16 tagCount = this->GetUns16 ( intBuffer );
+ if ( tagCount >= 0x8000 ) return 0; // Maybe wrong byte order.
+ if ( ! XIO::CheckFileSpace ( fileRef, 12*tagCount ) ) return 0; // Bail for a truncated file.
+ fileRef->ReadAll ( &ifdBuffer[0], 12*tagCount );
- if ( tagCount >= 0x8000 ) XMP_Throw ( "Outrageous IFD count", kXMPErr_BadTIFF );
- if ( (XMP_Uns32)(2 + tagCount*12 + 4) > (this->tiffLength - ifdOffset) ) {
- XMP_Throw ( "Out of bounds IFD", kXMPErr_BadTIFF );
+ if ( ! XIO::CheckFileSpace ( fileRef, 4 ) ) {
+ ifdInfo.origNextIFD = 0; // Tolerate a trncated file, do the remaining processing.
+ } else {
+ fileRef->ReadAll ( intBuffer, 4 );
+ ifdInfo.origNextIFD = this->GetUns32 ( intBuffer );
}
ifdInfo.origIFDOffset = ifdOffset;
@@ -823,13 +875,11 @@ XMP_Uns32 TIFF_FileWriter::ProcessFileIFD ( XMP_Uns8 ifd, XMP_Uns32 ifdOffset, X
// sorted output. Plus the "map[key] = value" assignment conveniently keeps the last encountered
// value, following Photoshop's behavior.
- ioBuf->ptr += 2; // Move to the first IFD entry.
+ XMP_Uns8* ifdPtr = &ifdBuffer[0]; // Move to the first IFD entry.
- for ( XMP_Uns16 i = 0; i < tagCount; ++i, ioBuf->ptr += 12 ) {
+ for ( XMP_Uns16 i = 0; i < tagCount; ++i, ifdPtr += 12 ) {
- if ( ! CheckFileSpace ( fileRef, ioBuf, 12 ) ) XMP_Throw ( "EOF within IFD", kXMPErr_BadTIFF );
-
- RawIFDEntry* rawTag = (RawIFDEntry*)ioBuf->ptr;
+ RawIFDEntry* rawTag = (RawIFDEntry*)ifdPtr;
XMP_Uns16 tagType = this->GetUns16 ( &rawTag->type );
if ( (tagType < kTIFF_ByteType) || (tagType > kTIFF_LastType) ) continue; // Bad type, skip this tag.
@@ -841,7 +891,7 @@ XMP_Uns32 TIFF_FileWriter::ProcessFileIFD ( XMP_Uns8 ifd, XMP_Uns32 ifdOffset, X
InternalTagInfo& mapTag = newPos->second;
mapTag.dataLen = mapTag.origDataLen = mapTag.count * (XMP_Uns32)kTIFF_TypeSizes[mapTag.type];
- mapTag.smallValue = rawTag->dataOrOffset; // Keep the value or offset in stream byte ordering.
+ mapTag.smallValue = GetUns32AsIs ( &rawTag->dataOrOffset ); // Keep the value or offset in stream byte ordering.
if ( mapTag.dataLen <= 4 ) {
mapTag.dataPtr = (XMP_Uns8*) &mapTag.smallValue;
@@ -862,25 +912,14 @@ XMP_Uns32 TIFF_FileWriter::ProcessFileIFD ( XMP_Uns8 ifd, XMP_Uns32 ifdOffset, X
}
- if ( ! CheckFileSpace ( fileRef, ioBuf, 4 ) ) XMP_Throw ( "EOF at next IFD offset", kXMPErr_BadTIFF );
- ifdInfo.origNextIFD = this->GetUns32 ( ioBuf->ptr );
-
- // ---------------------------------------------------------------------------------------------
- // Go back over the tag map and extract the data for large recognized tags. This is done in 2
- // passes, in order to lessen the typical amount of I/O. On the first pass make sure we have at
- // least 32K of data following the IFD in the buffer, and extract all of the values in that
- // portion. This should cover an original file, or the appended values with an appended IFD.
-
- if ( (ioBuf->limit - ioBuf->ptr) < 32*1024 ) RefillBuffer ( fileRef, ioBuf );
+ // ------------------------------------------------------------------------
+ // Go back over the tag map and extract the data for large recognized tags.
InternalTagMap::iterator tagPos = ifdInfo.tagMap.begin();
InternalTagMap::iterator tagEnd = ifdInfo.tagMap.end();
const XMP_Uns16* knownTagPtr = sKnownTags[ifd]; // Points into the ordered recognized tag list.
- XMP_Uns32 bufBegin = (XMP_Uns32)ioBuf->filePos; // TIFF stream bounds for the current buffer.
- XMP_Uns32 bufEnd = bufBegin + (XMP_Uns32)ioBuf->len;
-
for ( ; tagPos != tagEnd; ++tagPos ) {
InternalTagInfo* currTag = &tagPos->second;
@@ -890,48 +929,10 @@ XMP_Uns32 TIFF_FileWriter::ProcessFileIFD ( XMP_Uns8 ifd, XMP_Uns32 ifdOffset, X
while ( *knownTagPtr < currTag->id ) ++knownTagPtr;
if ( *knownTagPtr != currTag->id ) continue; // Skip unrecognized tags.
- if ( (bufBegin <= currTag->origDataOffset) && ((currTag->origDataOffset + currTag->dataLen) <= bufEnd) ) {
- // This value is already fully within the current I/O buffer, copy it.
- MoveToOffset ( fileRef, currTag->origDataOffset, ioBuf );
- currTag->dataPtr = (XMP_Uns8*) malloc ( currTag->dataLen );
- if ( currTag->dataPtr == 0 ) XMP_Throw ( "No data block", kXMPErr_NoMemory );
- memcpy ( currTag->dataPtr, ioBuf->ptr, currTag->dataLen ); // AUDIT: Safe, malloc'ed currTag->dataLen bytes above.
- currTag->changed = true; // Memory leaks otherwise
- }
-
- }
-
- // ---------------------------------------------------------------------------------------------
- // Now the second large value pass. This will reposition the I/O buffer as necessary. Hopefully
- // just once, to pick up the span of data not covered in the first pass.
-
- tagPos = ifdInfo.tagMap.begin(); // Reset both map/array positions.
- knownTagPtr = sKnownTags[ifd];
-
- for ( ; tagPos != tagEnd; ++tagPos ) {
-
- InternalTagInfo* currTag = &tagPos->second;
-
- if ( (currTag->dataLen <= 4) || (currTag->dataPtr != 0) ) continue; // Done this tag?
- while ( *knownTagPtr < currTag->id ) ++knownTagPtr;
- if ( *knownTagPtr != currTag->id ) continue; // Skip unrecognized tags.
-
+ fileRef->Seek ( currTag->origDataOffset, kXMP_SeekFromStart );
currTag->dataPtr = (XMP_Uns8*) malloc ( currTag->dataLen );
if ( currTag->dataPtr == 0 ) XMP_Throw ( "No data block", kXMPErr_NoMemory );
-//HUB-merge currTag->changed = true; // Memory leaks otherwise
-
- if ( currTag->dataLen > kIOBufferSize ) {
- // This value is bigger than the I/O buffer, read it directly and restore the file position.
- fileRef->Seek ( currTag->origDataOffset, kXMP_SeekFromStart );
- fileRef->ReadAll ( currTag->dataPtr, currTag->dataLen );
- fileRef->Seek ( (ioBuf->filePos + ioBuf->len), kXMP_SeekFromStart );
- } else {
- // This value can fit in the I/O buffer, so use that.
- MoveToOffset ( fileRef, currTag->origDataOffset, ioBuf );
- ok = CheckFileSpace ( fileRef, ioBuf, currTag->dataLen );
- if ( ! ok ) XMP_Throw ( "EOF in data block", kXMPErr_BadTIFF );
- memcpy ( currTag->dataPtr, ioBuf->ptr, currTag->dataLen ); // AUDIT: Safe, malloc'ed currTag->dataLen bytes above.
- }
+ fileRef->ReadAll ( currTag->dataPtr, currTag->dataLen );
}
@@ -1265,6 +1266,9 @@ XMP_Uns32 TIFF_FileWriter::DetermineVisibleLength()
#define Trace_DetermineAppendInfo 0
#endif
+// An IFD grows if it has more tags than before.
+#define DoesIFDGrow(ifd) (this->containedIFDs[ifd].origCount < this->containedIFDs[ifd].tagMap.size())
+
XMP_Uns32 TIFF_FileWriter::DetermineAppendInfo ( XMP_Uns32 appendedOrigin,
bool appendedIFDs[kTIFF_KnownIFDCount],
XMP_Uns32 newIFDOffsets[kTIFF_KnownIFDCount],
@@ -1309,40 +1313,38 @@ XMP_Uns32 TIFF_FileWriter::DetermineAppendInfo ( XMP_Uns32 appendedOrigin,
for ( int i = 0; i < kTIFF_KnownIFDCount ;++i ) appendedIFDs[i] = (this->containedIFDs[i].tagMap.size() > 0);
}
- appendedIFDs[kTIFF_InteropIFD] |= (this->containedIFDs[kTIFF_InteropIFD].origCount <
- this->containedIFDs[kTIFF_InteropIFD].tagMap.size());
+ appendedIFDs[kTIFF_InteropIFD] |= DoesIFDGrow ( kTIFF_InteropIFD );
if ( appendedIFDs[kTIFF_InteropIFD] ) {
this->SetTag_Long ( kTIFF_ExifIFD, kTIFF_InteroperabilityIFDPointer, 0xABADABAD );
}
- appendedIFDs[kTIFF_GPSInfoIFD] |= (this->containedIFDs[kTIFF_GPSInfoIFD].origCount <
- this->containedIFDs[kTIFF_GPSInfoIFD].tagMap.size());
+ appendedIFDs[kTIFF_GPSInfoIFD] |= DoesIFDGrow ( kTIFF_GPSInfoIFD );
if ( appendedIFDs[kTIFF_GPSInfoIFD] ) {
this->SetTag_Long ( kTIFF_PrimaryIFD, kTIFF_GPSInfoIFDPointer, 0xABADABAD );
}
- appendedIFDs[kTIFF_ExifIFD] |= (this->containedIFDs[kTIFF_ExifIFD].origCount <
- this->containedIFDs[kTIFF_ExifIFD].tagMap.size());
+ appendedIFDs[kTIFF_ExifIFD] |= DoesIFDGrow ( kTIFF_ExifIFD );
if ( appendedIFDs[kTIFF_ExifIFD] ) {
this->SetTag_Long ( kTIFF_PrimaryIFD, kTIFF_ExifIFDPointer, 0xABADABAD );
}
- appendedIFDs[kTIFF_PrimaryIFD] |= (this->containedIFDs[kTIFF_PrimaryIFD].origCount <
- this->containedIFDs[kTIFF_PrimaryIFD].tagMap.size());
+ appendedIFDs[kTIFF_PrimaryIFD] |= DoesIFDGrow ( kTIFF_PrimaryIFD );
// The appended data (if any) will be a sequence of an IFD followed by its large values.
// Determine the new offsets for the appended IFDs and tag values, and the total amount of
- // appended stuff.
+ // appended stuff. The final IFD offset is set in newIFDOffsets for all IFDs, changed or not.
+ // This makes it easier to set the offsets to the primary and thumbnail IFDs when writing.
for ( int ifd = 0; ifd < kTIFF_KnownIFDCount ;++ifd ) {
InternalIFDInfo& ifdInfo ( this->containedIFDs[ifd] );
size_t tagCount = ifdInfo.tagMap.size();
+ newIFDOffsets[ifd] = ifdInfo.origIFDOffset; // Make the new offset valid for unchanged IFDs.
+
if ( ! (appendAll | ifdInfo.changed) ) continue;
if ( tagCount == 0 ) continue;
- newIFDOffsets[ifd] = ifdInfo.origIFDOffset;
if ( appendedIFDs[ifd] ) {
newIFDOffsets[ifd] = appendedOrigin + appendedLength;
appendedLength += (XMP_Uns32)( 6 + (12 * tagCount) );
@@ -1491,7 +1493,7 @@ void TIFF_FileWriter::UpdateMemByAppend ( XMP_Uns8** newStream_out, XMP_Uns32* n
this->PutUns32 ( currTag.count, ifdPtr );
ifdPtr += 4;
- *((XMP_Uns32*)ifdPtr) = currTag.smallValue;
+ PutUns32AsIs ( currTag.smallValue, ifdPtr );
if ( (appendAll | currTag.changed) && (currTag.dataLen > 4) ) {
@@ -1528,6 +1530,14 @@ void TIFF_FileWriter::UpdateMemByAppend ( XMP_Uns8** newStream_out, XMP_Uns32* n
this->PutUns32 ( newIFDOffsets[kTIFF_PrimaryIFD], (newStream + 4) );
}
+ if ( appendedIFDs[kTIFF_TNailIFD] ) {
+ size_t primaryTagCount = this->containedIFDs[kTIFF_PrimaryIFD].tagMap.size();
+ if ( primaryTagCount > 0 ) {
+ XMP_Uns32 tnailLinkOffset = newIFDOffsets[kTIFF_PrimaryIFD] + 2 + (12 * primaryTagCount);
+ this->PutUns32 ( newIFDOffsets[kTIFF_TNailIFD], (newStream + tnailLinkOffset) );
+ }
+ }
+
} catch ( ... ) {
free ( newStream );
@@ -1557,7 +1567,11 @@ void TIFF_FileWriter::UpdateMemByAppend ( XMP_Uns8** newStream_out, XMP_Uns32* n
// We don't do most of the actual rewrite here. We set things up so that UpdateMemByAppend can be
// called to append onto a bare TIFF header. Additional hidden offsets are then handled here.
//
-// These tags are recognized as being hidden offsets when composing a condensed stream:
+// The hidden offsets for the Exif, GPS, and Interoperability IFDs (tags 34665, 34853, and 40965)
+// are handled by the code in DetermineAppendInfo, which is called from UpdateMemByAppend, which is
+// called from here.
+//
+// These other tags are recognized as being hidden offsets when composing a condensed stream:
// 273 - StripOffsets, lengths in tag 279
// 288 - FreeOffsets, lengths in tag 289
// 324 - TileOffsets, lengths in tag 325
@@ -1566,11 +1580,11 @@ void TIFF_FileWriter::UpdateMemByAppend ( XMP_Uns8** newStream_out, XMP_Uns32* n
// 519 - JPEGQTables, each table is 64 bytes
// 520 - JPEGDCTables, lengths ???
// 521 - JPEGACTables, lengths ???
-// Some of these will handled and kept, some will be thrown out, some will cause the rewrite to fail.
//
-// The hidden offsets for the Exif, GPS, and Interoperability IFDs (tags 34665, 34853, and 40965)
-// are handled by the code in DetermineAppendInfo, which is called from UpdateMemByAppend, which is
-// called from here.
+// Some of these will handled and kept, some will be thrown out, some will cause the rewrite to fail.
+// At this time only the JPEG thumbnail tags, 513 and 514, contain hidden data that is kept. The
+// final stream layout is whatever UpdateMemByAppend does for the visible content, followed by the
+// hidden offset data. The Exif, GPS, and Interoperability IFDs are visible to UpdateMemByAppend.
// ! So far, a memory-based TIFF rewrite would only be done for the Exif portion of a JPEG file.
// ! In which case we're probably OK to handle JPEGInterchangeFormat (used for compressed thumbnails)
@@ -1656,7 +1670,7 @@ void TIFF_FileWriter::UpdateMemByRewrite ( XMP_Uns8** newStream_out, XMP_Uns32*
}
// Determine the offsets and additional size for the hidden offset content. Set the offset tags
- // to the new offset.
+ // to the new offset so that UpdateMemByAppend writes the new offsets.
XMP_Uns32 hiddenContentLength = 0;
XMP_Uns32 hiddenContentOrigin = this->DetermineVisibleLength();
@@ -1681,6 +1695,7 @@ void TIFF_FileWriter::UpdateMemByRewrite ( XMP_Uns8** newStream_out, XMP_Uns32*
// Save any old memory stream for the content behind hidden offsets. Setup a bare TIFF header.
XMP_Uns8* oldStream = this->memStream;
+ bool ownedOldStream = this->ownedStream;
XMP_Uns8 bareTIFF [8];
if ( this->bigEndian ) {
@@ -1712,6 +1727,10 @@ void TIFF_FileWriter::UpdateMemByRewrite ( XMP_Uns8** newStream_out, XMP_Uns32*
memcpy ( destPtr, srcPtr, hiddenLocations[i].length ); // AUDIT: Safe copy, not user data, computed length.
}
+
+ // Delete the old stream if appropriate.
+
+ if ( ownedOldStream ) delete ( oldStream );
} // TIFF_FileWriter::UpdateMemByRewrite
@@ -1733,6 +1752,7 @@ XMP_Uns32 TIFF_FileWriter::UpdateMemoryStream ( void** dataPtr, bool condenseStr
{
if ( this->fileParsed ) XMP_Throw ( "Not memory based", kXMPErr_EnforceFailure );
+ this->changed |= condenseStream; // Make sure a compaction request takes effect.
if ( ! this->changed ) {
if ( dataPtr != 0 ) *dataPtr = this->memStream;
return this->tiffLength;
@@ -1761,8 +1781,6 @@ XMP_Uns32 TIFF_FileWriter::UpdateMemoryStream ( void** dataPtr, bool condenseStr
condenseStream = true; // Makes "conjured" TIFF take the full rewrite path.
}
- if ( condenseStream ) this->changed = true; // A prior regular call would have cleared this->changed.
-
if ( condenseStream ) {
this->UpdateMemByRewrite ( &newStream, &newLength );
} else {
@@ -1808,7 +1826,7 @@ XMP_Uns32 TIFF_FileWriter::UpdateMemoryStream ( void** dataPtr, bool condenseStr
#define Trace_UpdateFileStream 0
#endif
-void TIFF_FileWriter::UpdateFileStream ( XMP_IO* fileRef )
+void TIFF_FileWriter::UpdateFileStream ( XMP_IO* fileRef, XMP_ProgressTracker* progressTracker )
{
if ( this->memParsed ) XMP_Throw ( "Not file based", kXMPErr_EnforceFailure );
if ( ! this->changed ) return;
@@ -1835,6 +1853,33 @@ void TIFF_FileWriter::UpdateFileStream ( XMP_IO* fileRef )
XMP_Uns32 appendedLength = DetermineAppendInfo ( appendedOrigin, appendedIFDs, newIFDOffsets );
if ( appendedLength > (0xFFFFFFFFUL - appendedOrigin) ) XMP_Throw ( "TIFF files can't exceed 4GB", kXMPErr_BadTIFF );
+ if ( progressTracker != 0 ) {
+
+ float filesize=0;
+
+ for ( int ifd = 0; ifd < kTIFF_KnownIFDCount; ++ifd ) {
+
+ InternalIFDInfo & thisIFD = this->containedIFDs[ifd];
+ if ( ! thisIFD.changed ) continue;
+
+ filesize += (thisIFD.tagMap.size() * sizeof(RawIFDEntry) + 6);
+
+ InternalTagMap::iterator tagPos;
+ InternalTagMap::iterator tagEnd = thisIFD.tagMap.end();
+
+ for ( tagPos = thisIFD.tagMap.begin(); tagPos != tagEnd; ++tagPos ) {
+ InternalTagInfo & thisTag = tagPos->second;
+ if ( (! thisTag.changed) || (thisTag.dataLen <= 4) ) continue;
+ filesize += (thisTag.dataLen) ;
+ }
+ }
+
+ if ( appendedIFDs[kTIFF_PrimaryIFD] ) filesize += 4;
+ XMP_Assert ( progressTracker->WorkInProgress() );
+ progressTracker->AddTotalWork ( filesize );
+
+ }
+
// Do the in-place update for the IFDs and tag values that fit. This part does separate seeks
// and writes for the IFDs and values. Things to be updated can be anywhere in the file.
diff --git a/XMPFiles/source/FormatSupport/TIFF_MemoryReader.cpp b/XMPFiles/source/FormatSupport/TIFF_MemoryReader.cpp
index 3e96620..05cd87f 100644
--- a/XMPFiles/source/FormatSupport/TIFF_MemoryReader.cpp
+++ b/XMPFiles/source/FormatSupport/TIFF_MemoryReader.cpp
@@ -12,6 +12,7 @@
#include "XMPFiles/source/FormatSupport/TIFF_Support.hpp"
#include "source/XIO.hpp"
+#include "source/EndianUtils.hpp"
// =================================================================================================
/// \file TIFF_MemoryReader.cpp
@@ -55,11 +56,11 @@ void TIFF_MemoryReader::SortIFD ( TweakedIFDInfo* thisIFD )
XMP_Uns16 tagCount = thisIFD->count;
TweakedIFDEntry* ifdEntries = thisIFD->entries;
- XMP_Uns16 prevTag = ifdEntries[0].id;
+ XMP_Uns16 prevTag = GetUns16AsIs ( &ifdEntries[0].id );
for ( size_t i = 1; i < tagCount; ++i ) {
- XMP_Uns16 thisTag = ifdEntries[i].id;
+ XMP_Uns16 thisTag = GetUns16AsIs ( &ifdEntries[i].id );
if ( thisTag > prevTag ) {
@@ -78,7 +79,7 @@ void TIFF_MemoryReader::SortIFD ( TweakedIFDInfo* thisIFD )
// Out of order, move this tag up, prevTag is unchanged. Might still be a duplicate!
XMP_Int32 j; // ! Need a signed value.
for ( j = (XMP_Int32)i-1; j >= 0; --j ) {
- if ( ifdEntries[j].id <= thisTag ) break;
+ if ( GetUns16AsIs(&ifdEntries[j].id) <= thisTag ) break;
}
if ( (j >= 0) && (ifdEntries[j].id == thisTag) ) {
@@ -92,10 +93,20 @@ void TIFF_MemoryReader::SortIFD ( TweakedIFDInfo* thisIFD )
} else {
// Move the out of order entry to position j+1, move the middle of the array down.
- TweakedIFDEntry temp = ifdEntries[i];
- ++j; // ! So the insertion index becomes j.
- memcpy ( &ifdEntries[j+1], &ifdEntries[j], 12*(i-j) ); // AUDIT: Safe, moving less than i entries to a location before i.
- ifdEntries[j] = temp;
+ #if ! SUNOS_SPARC
+ TweakedIFDEntry temp = ifdEntries[i];
+ ++j; // ! So the insertion index becomes j.
+ memcpy ( &ifdEntries[j+1], &ifdEntries[j], 12*(i-j) ); // AUDIT: Safe, moving less than i entries to a location before i.
+ ifdEntries[j] = temp;
+ #else
+ void * tempifdEntries = &ifdEntries[i];
+ TweakedIFDEntry temp;
+ memcpy ( &temp, tempifdEntries, sizeof(TweakedIFDEntry) );
+ ++j; // ! So the insertion index becomes j.
+ memcpy ( &ifdEntries[j+1], &ifdEntries[j], 12*(i-j) ); // AUDIT: Safe, moving less than i entries to a location before i.
+ tempifdEntries = &ifdEntries[j];
+ memcpy ( tempifdEntries, &temp, sizeof(TweakedIFDEntry) );
+ #endif
}
@@ -126,7 +137,7 @@ bool TIFF_MemoryReader::GetIFD ( XMP_Uns8 ifd, TagInfoMap* ifdMap ) const
TweakedIFDEntry* thisTag = &(thisIFD->entries[i]);
if ( (thisTag->type < kTIFF_ByteType) || (thisTag->type > kTIFF_LastType) ) continue; // Bad type, skip this tag.
- TagInfo info ( thisTag->id, thisTag->type, 0, 0, thisTag->bytes );
+ TagInfo info ( thisTag->id, thisTag->type, 0, 0, GetUns32AsIs(&thisTag->bytes) );
info.count = info.dataLen / (XMP_Uns32)kTIFF_TypeSizes[info.type];
info.dataPtr = this->GetDataPtr ( thisTag );
@@ -166,10 +177,11 @@ const TIFF_MemoryReader::TweakedIFDEntry* TIFF_MemoryReader::FindTagInIFD ( XMP_
// There are halfLength entries below spanMiddle, then the spanMiddle entry, then
// spanLength-halfLength-1 entries above spanMiddle (which can be none).
- if ( spanMiddle->id == id ) {
+ XMP_Uns16 middleID = GetUns16AsIs ( &spanMiddle->id );
+ if ( middleID == id ) {
spanBegin = spanMiddle;
break;
- } else if ( spanMiddle->id > id ) {
+ } else if ( middleID > id ) {
spanLength = halfLength; // Discard the middle.
} else {
spanBegin = spanMiddle; // Keep a valid spanBegin for the return check, don't use spanMiddle+1.
@@ -178,7 +190,7 @@ const TIFF_MemoryReader::TweakedIFDEntry* TIFF_MemoryReader::FindTagInIFD ( XMP_
}
- if ( spanBegin->id != id ) spanBegin = 0;
+ if ( GetUns16AsIs(&spanBegin->id) != id ) spanBegin = 0;
return spanBegin;
} // TIFF_MemoryReader::FindTagInIFD
@@ -206,14 +218,18 @@ bool TIFF_MemoryReader::GetTag ( XMP_Uns8 ifd, XMP_Uns16 id, TagInfo* info ) con
{
const TweakedIFDEntry* thisTag = this->FindTagInIFD ( ifd, id );
if ( thisTag == 0 ) return false;
- if ( (thisTag->type < kTIFF_ByteType) || (thisTag->type > kTIFF_LastType) ) return false; // Bad type, skip this tag.
+
+ XMP_Uns16 thisType = GetUns16AsIs ( &thisTag->type );
+ XMP_Uns32 thisBytes = GetUns32AsIs ( &thisTag->bytes );
+
+ if ( (thisType < kTIFF_ByteType) || (thisType > kTIFF_LastType) ) return false; // Bad type, skip this tag.
if ( info != 0 ) {
- info->id = thisTag->id;
- info->type = thisTag->type;
- info->count = thisTag->bytes / (XMP_Uns32)kTIFF_TypeSizes[thisTag->type];
- info->dataLen = thisTag->bytes;
+ info->id = GetUns16AsIs ( &thisTag->id );
+ info->type = thisType;
+ info->count = thisBytes / (XMP_Uns32)kTIFF_TypeSizes[thisType];
+ info->dataLen = thisBytes;
info->dataPtr = this->GetDataPtr ( thisTag );
@@ -242,7 +258,7 @@ bool TIFF_MemoryReader::GetTag_Integer ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns32*
if ( thisTag == 0 ) return false;
if ( thisTag->type > kTIFF_LastType ) return false; // Unknown type.
- if ( thisTag->bytes != kTIFF_TypeSizes[thisTag->type] ) return false; // Wrong count.
+ if ( GetUns32AsIs(&thisTag->bytes) != kTIFF_TypeSizes[thisTag->type] ) return false; // Wrong count.
XMP_Uns32 uns32;
XMP_Int32 int32;
@@ -553,27 +569,40 @@ void TIFF_MemoryReader::ParseMemoryStream ( const void* data, XMP_Uns32 length,
if ( primaryIFDOffset != 0 ) tnailIFDOffset = this->ProcessOneIFD ( primaryIFDOffset, kTIFF_PrimaryIFD );
// ! Need the thumbnail IFD for checking full Exif APP1 in some JPEG files!
- if ( tnailIFDOffset != 0 ) (void) this->ProcessOneIFD ( tnailIFDOffset, kTIFF_TNailIFD );
+ if ( tnailIFDOffset != 0 ) {
+ if ( IsOffsetValid(tnailIFDOffset, 8, ifdLimit ) ) { // Ignore a bad Thumbnail IFD offset.
+ (void) this->ProcessOneIFD ( tnailIFDOffset, kTIFF_TNailIFD );
+ } else {
+ XMP_Error error ( kXMPErr_BadTIFF, "Bad IFD offset" );
+ this->NotifyClient (kXMPErrSev_Recoverable, error );
+ }
+ }
const TweakedIFDEntry* exifIFDTag = this->FindTagInIFD ( kTIFF_PrimaryIFD, kTIFF_ExifIFDPointer );
- if ( (exifIFDTag != 0) && (exifIFDTag->type == kTIFF_LongType) && (exifIFDTag->bytes == 4) ) {
+ if ( (exifIFDTag != 0) && (exifIFDTag->type == kTIFF_LongType) && (GetUns32AsIs(&exifIFDTag->bytes) == 4) ) {
XMP_Uns32 exifOffset = this->GetUns32 ( &exifIFDTag->dataOrPos );
(void) this->ProcessOneIFD ( exifOffset, kTIFF_ExifIFD );
}
const TweakedIFDEntry* gpsIFDTag = this->FindTagInIFD ( kTIFF_PrimaryIFD, kTIFF_GPSInfoIFDPointer );
- if ( (gpsIFDTag != 0) && (gpsIFDTag->type == kTIFF_LongType) && (gpsIFDTag->bytes == 4) ) {
+ if ( (gpsIFDTag != 0) && (gpsIFDTag->type == kTIFF_LongType) && (GetUns32AsIs(&gpsIFDTag->bytes) == 4) ) {
XMP_Uns32 gpsOffset = this->GetUns32 ( &gpsIFDTag->dataOrPos );
- if ( (8 <= gpsOffset) && (gpsOffset < ifdLimit) ) { // Ignore a bad GPS IFD offset.
+ if ( IsOffsetValid ( gpsOffset, 8, ifdLimit ) ) { // Ignore a bad GPS IFD offset.
(void) this->ProcessOneIFD ( gpsOffset, kTIFF_GPSInfoIFD );
+ } else {
+ XMP_Error error ( kXMPErr_BadTIFF, "Bad IFD offset" );
+ this->NotifyClient (kXMPErrSev_Recoverable, error );
}
}
const TweakedIFDEntry* interopIFDTag = this->FindTagInIFD ( kTIFF_ExifIFD, kTIFF_InteroperabilityIFDPointer );
- if ( (interopIFDTag != 0) && (interopIFDTag->type == kTIFF_LongType) && (interopIFDTag->bytes == 4) ) {
+ if ( (interopIFDTag != 0) && (interopIFDTag->type == kTIFF_LongType) && (GetUns32AsIs(&interopIFDTag->bytes) == 4) ) {
XMP_Uns32 interopOffset = this->GetUns32 ( &interopIFDTag->dataOrPos );
- if ( (8 <= interopOffset) && (interopOffset < ifdLimit) ) { // Ignore a bad Interoperability IFD offset.
+ if ( IsOffsetValid ( interopOffset, 8, ifdLimit ) ) { // Ignore a bad Interoperability IFD offset.
(void) this->ProcessOneIFD ( interopOffset, kTIFF_InteropIFD );
+ } else {
+ XMP_Error error ( kXMPErr_BadTIFF, "Bad IFD offset" );
+ this->NotifyClient (kXMPErrSev_Recoverable, error );
}
}
@@ -588,16 +617,22 @@ XMP_Uns32 TIFF_MemoryReader::ProcessOneIFD ( XMP_Uns32 ifdOffset, XMP_Uns8 ifd )
TweakedIFDInfo& ifdInfo = this->containedIFDs[ifd];
if ( (ifdOffset < 8) || (ifdOffset > (this->tiffLength - kEmptyIFDLength)) ) {
- XMP_Throw ( "Bad IFD offset", kXMPErr_BadTIFF );
+ XMP_Error error(kXMPErr_BadTIFF, "Bad IFD offset" );
+ this->NotifyClient ( kXMPErrSev_FileFatal, error );
}
XMP_Uns8* ifdPtr = this->tiffStream + ifdOffset;
XMP_Uns16 ifdCount = this->GetUns16 ( ifdPtr );
TweakedIFDEntry* ifdEntries = (TweakedIFDEntry*)(ifdPtr+2);
- if ( ifdCount >= 0x8000 ) XMP_Throw ( "Outrageous IFD count", kXMPErr_BadTIFF );
+ if ( ifdCount >= 0x8000 ) {
+ XMP_Error error(kXMPErr_BadTIFF, "Outrageous IFD count" );
+ this->NotifyClient ( kXMPErrSev_FileFatal, error );
+ }
+
if ( (XMP_Uns32)(2 + ifdCount*12 + 4) > (this->tiffLength - ifdOffset) ) {
- XMP_Throw ( "Out of bounds IFD", kXMPErr_BadTIFF );
+ XMP_Error error(kXMPErr_BadTIFF, "Out of bounds IFD" );
+ this->NotifyClient ( kXMPErrSev_FileFatal, error );
}
ifdInfo.count = ifdCount;
@@ -615,21 +650,48 @@ XMP_Uns32 TIFF_MemoryReader::ProcessOneIFD ( XMP_Uns32 ifdOffset, XMP_Uns8 ifd )
Flip4 ( &thisEntry->bytes );
}
- if ( thisEntry->id <= prevTag ) needsSorting = true;
- prevTag = thisEntry->id;
+ if ( GetUns16AsIs(&thisEntry->id) <= prevTag ) needsSorting = true;
+ prevTag = GetUns16AsIs ( &thisEntry->id );
- if ( (thisEntry->type < kTIFF_ByteType) || (thisEntry->type > kTIFF_LastType) ) continue; // Bad type, skip this tag.
+ if ( (GetUns16AsIs(&thisEntry->type) < kTIFF_ByteType) || (GetUns16AsIs(&thisEntry->type) > kTIFF_LastType) ) continue; // Bad type, skip this tag.
- thisEntry->bytes *= (XMP_Uns32)kTIFF_TypeSizes[thisEntry->type];
- if ( thisEntry->bytes > 4 ) {
- if ( ! this->nativeEndian ) Flip4 ( &thisEntry->dataOrPos );
- if ( (thisEntry->dataOrPos < 8) || (thisEntry->dataOrPos >= this->tiffLength) ) {
- thisEntry->bytes = thisEntry->dataOrPos = 0; // Make this bad tag look empty.
+ #if ! SUNOS_SPARC
+
+ thisEntry->bytes *= (XMP_Uns32)kTIFF_TypeSizes[thisEntry->type];
+ if ( thisEntry->bytes > 4 ) {
+ if ( ! this->nativeEndian ) Flip4 ( &thisEntry->dataOrPos );
+ if ( (thisEntry->dataOrPos < 8) || (thisEntry->dataOrPos >= this->tiffLength) ) {
+ thisEntry->bytes = thisEntry->dataOrPos = 0; // Make this bad tag look empty.
+ }
+ if ( thisEntry->bytes > (this->tiffLength - thisEntry->dataOrPos) ) {
+ thisEntry->bytes = thisEntry->dataOrPos = 0; // Make this bad tag look empty.
+ }
}
- if ( thisEntry->bytes > (this->tiffLength - thisEntry->dataOrPos) ) {
- thisEntry->bytes = thisEntry->dataOrPos = 0; // Make this bad tag look empty.
+
+ #else
+
+ void *tempEntryByte = &thisEntry->bytes;
+ XMP_Uns32 temp = GetUns32AsIs(&thisEntry->bytes);
+ temp = temp * (XMP_Uns32)kTIFF_TypeSizes[GetUns16AsIs(&thisEntry->type)];
+ memcpy ( tempEntryByte, &temp, sizeof(thisEntry->bytes) );
+
+ // thisEntry->bytes *= (XMP_Uns32)kTIFF_TypeSizes[thisEntry->type];
+ if ( GetUns32AsIs(&thisEntry->bytes) > 4 ) {
+ void *tempEntryDataOrPos = &thisEntry->dataOrPos;
+ if ( ! this->nativeEndian ) Flip4 ( &thisEntry->dataOrPos );
+ if ( (GetUns32AsIs(&thisEntry->dataOrPos) < 8) || (GetUns32AsIs(&thisEntry->dataOrPos) >= this->tiffLength) ) {
+ // thisEntry->bytes = thisEntry->dataOrPos = 0; // Make this bad tag look empty.
+ memset ( tempEntryByte, 0, sizeof(XMP_Uns32) );
+ memset ( tempEntryDataOrPos, 0, sizeof(XMP_Uns32) );
+ }
+ if ( GetUns32AsIs(&thisEntry->bytes) > (this->tiffLength - GetUns32AsIs(&thisEntry->dataOrPos)) ) {
+ // thisEntry->bytes = thisEntry->dataOrPos = 0; // Make this bad tag look empty.
+ memset ( tempEntryByte, 0, sizeof(XMP_Uns32) );
+ memset ( tempEntryDataOrPos, 0, sizeof(XMP_Uns32) );
+ }
}
- }
+
+ #endif
}
diff --git a/XMPFiles/source/FormatSupport/TIFF_Support.cpp b/XMPFiles/source/FormatSupport/TIFF_Support.cpp
index 9da9b52..da2f9b6 100644
--- a/XMPFiles/source/FormatSupport/TIFF_Support.cpp
+++ b/XMPFiles/source/FormatSupport/TIFF_Support.cpp
@@ -29,7 +29,7 @@
static bool sFirstCTor = true;
TIFF_Manager::TIFF_Manager()
- : bigEndian(false), nativeEndian(false),
+ : bigEndian(false), nativeEndian(false), errorCallbackPtr( NULL ),
GetUns16(0), GetUns32(0), GetFloat(0), GetDouble(0),
PutUns16(0), PutUns32(0), PutFloat(0), PutDouble(0)
{
@@ -379,6 +379,13 @@ static void UTF8_to_UTF16 ( const UTF8Unit * utf8Ptr, size_t utf8Len, bool bigEn
} // UTF8_to_UTF16
+XMP_Bool IsOffsetValid( XMP_Uns32 offset, XMP_Uns32 lowerBound, XMP_Uns32 upperBound )
+{
+ if ( (lowerBound <= offset) && (offset < upperBound) )
+ return true;
+ return false;
+}
+
// =================================================================================================
// TIFF_Manager::EncodeString
// ==========================
@@ -436,4 +443,14 @@ bool TIFF_Manager::EncodeString ( const std::string& utf8Str, XMP_Uns8 encoding,
} // TIFF_Manager::EncodeString
+void TIFF_Manager::NotifyClient( XMP_ErrorSeverity severity, XMP_Error & error )
+{
+ if (this->errorCallbackPtr)
+ this->errorCallbackPtr->NotifyClient( severity, error );
+ else {
+ if ( severity != kXMPErrSev_Recoverable )
+ throw error;
+ }
+}
+
// =================================================================================================
diff --git a/XMPFiles/source/FormatSupport/TIFF_Support.hpp b/XMPFiles/source/FormatSupport/TIFF_Support.hpp
index 447ab79..002376b 100644
--- a/XMPFiles/source/FormatSupport/TIFF_Support.hpp
+++ b/XMPFiles/source/FormatSupport/TIFF_Support.hpp
@@ -23,6 +23,14 @@
#include "source/EndianUtils.hpp"
+#include "source/Endian.h"
+
+#if SUNOS_SPARC
+ static const IEndian &IE = BigEndian::getInstance();
+#else
+ static const IEndian &IE = LittleEndian::getInstance();
+#endif //#if SUNOS_SPARC
+
// =================================================================================================
/// \file TIFF_Support.hpp
/// \brief XMPFiles support for TIFF streams.
@@ -642,7 +650,7 @@ public:
virtual void IntegrateFromPShop6 ( const void * buriedPtr, size_t buriedLen ) = 0;
virtual XMP_Uns32 UpdateMemoryStream ( void** dataPtr, bool condenseStream = false ) = 0;
- virtual void UpdateFileStream ( XMP_IO* fileRef ) = 0;
+ virtual void UpdateFileStream ( XMP_IO* fileRef, XMP_ProgressTracker* progressTracker ) = 0;
// ---------------------------------------------------------------------------------------------
@@ -658,11 +666,17 @@ public:
virtual ~TIFF_Manager() {};
+ virtual void SetErrorCallback ( GenericErrorCallback * ec ) { this->errorCallbackPtr = ec; };
+
+ virtual void NotifyClient ( XMP_ErrorSeverity severity, XMP_Error & error );
+
protected:
bool bigEndian, nativeEndian;
XMP_Uns32 CheckTIFFHeader ( const XMP_Uns8* tiffPtr, XMP_Uns32 length );
+ // The pointer is to a buffer of the first 8 bytes. The length is the overall length, used
+ // to check the primary IFD offset.
TIFF_Manager(); // Force clients to use the reader or writer derived classes.
@@ -673,6 +687,8 @@ protected:
XMP_Uns32 dataOrOffset;
};
+ GenericErrorCallback *errorCallbackPtr;
+
}; // TIFF_Manager
@@ -730,7 +746,7 @@ public:
void IntegrateFromPShop6 ( const void * buriedPtr, size_t buriedLen ) { NotAppropriate(); };
XMP_Uns32 UpdateMemoryStream ( void** dataPtr, bool condenseStream = false ) { if ( dataPtr != 0 ) *dataPtr = tiffStream; return tiffLength; };
- void UpdateFileStream ( XMP_IO* fileRef ) { NotAppropriate(); };
+ void UpdateFileStream ( XMP_IO* fileRef, XMP_ProgressTracker* progressTracker ) { NotAppropriate(); };
TIFF_MemoryReader() : ownedStream(false), tiffStream(0), tiffLength(0) {};
@@ -771,7 +787,12 @@ private:
const TweakedIFDEntry* FindTagInIFD ( XMP_Uns8 ifd, XMP_Uns16 id ) const;
const inline void* GetDataPtr ( const TweakedIFDEntry* tifdEntry ) const
- { if ( tifdEntry->bytes <= 4 ) return &tifdEntry->dataOrPos; else return (this->tiffStream + tifdEntry->dataOrPos); };
+ { if ( GetUns32AsIs(&tifdEntry->bytes) <= 4 ) {
+ return &tifdEntry->dataOrPos;
+ } else {
+ return (this->tiffStream + GetUns32AsIs(&tifdEntry->dataOrPos));
+ }
+ }
static inline void NotAppropriate() { XMP_Throw ( "Not appropriate for TIFF_Reader", kXMPErr_InternalFailure ); };
@@ -835,7 +856,7 @@ public:
void IntegrateFromPShop6 ( const void * buriedPtr, size_t buriedLen );
XMP_Uns32 UpdateMemoryStream ( void** dataPtr, bool condenseStream = false );
- void UpdateFileStream ( XMP_IO* fileRef );
+ void UpdateFileStream ( XMP_IO* fileRef, XMP_ProgressTracker* progressTracker );
TIFF_FileWriter();
@@ -934,8 +955,7 @@ private:
void DeleteExistingInfo();
XMP_Uns32 ProcessMemoryIFD ( XMP_Uns32 ifdOffset, XMP_Uns8 ifd );
- XMP_Uns32 ProcessFileIFD ( XMP_Uns8 ifd, XMP_Uns32 ifdOffset, XMP_IO* fileRef, void* _ioBuf );
- // *** Temporary hack above, _ioBuf is IOBuffer* but don't want to include XIO.hpp.
+ XMP_Uns32 ProcessFileIFD ( XMP_Uns8 ifd, XMP_Uns32 ifdOffset, XMP_IO* fileRef );
void ProcessPShop6IFD ( const TIFF_MemoryReader& buriedExif, XMP_Uns8 ifd );
@@ -958,6 +978,7 @@ private:
}; // TIFF_FileWriter
+XMP_Bool IsOffsetValid( XMP_Uns32 offset, XMP_Uns32 lowerBound, XMP_Uns32 upperBound );
// =================================================================================================
diff --git a/XMPFiles/source/FormatSupport/WAVE/BEXTMetadata.cpp b/XMPFiles/source/FormatSupport/WAVE/BEXTMetadata.cpp
index a11e481..5b98ba0 100644
--- a/XMPFiles/source/FormatSupport/WAVE/BEXTMetadata.cpp
+++ b/XMPFiles/source/FormatSupport/WAVE/BEXTMetadata.cpp
@@ -26,7 +26,11 @@ static const XMP_Uns32 kSizeOriginationDate = 10;
static const XMP_Uns32 kSizeOriginationTime = 8;
// Needed to be able to memcpy directly to this struct.
+#if SUNOS_SPARC || SUNOS_X86
+#pragma pack ( 1 )
+#else
#pragma pack ( push, 1 )
+#endif //#if SUNOS_SPARC || SUNOS_X86
struct BEXT
{
char mDescription[256];
@@ -40,7 +44,11 @@ static const XMP_Uns32 kSizeOriginationTime = 8;
XMP_Uns8 mUMID[64];
XMP_Uns8 mReserved[190];
};
+#if SUNOS_SPARC || SUNOS_X86
+#pragma pack ( )
+#else
#pragma pack ( pop )
+#endif //#if SUNOS_SPARC || SUNOS_X86
//-----------------------------------------------------------------------------
//
diff --git a/XMPFiles/source/FormatSupport/WAVE/CartMetadata.cpp b/XMPFiles/source/FormatSupport/WAVE/CartMetadata.cpp
index 07c8969..73165e4 100644
--- a/XMPFiles/source/FormatSupport/WAVE/CartMetadata.cpp
+++ b/XMPFiles/source/FormatSupport/WAVE/CartMetadata.cpp
@@ -22,7 +22,11 @@ using namespace IFF_RIFF;
// Types and globals for the stored form of the cart chunk.
+#if SUNOS_SPARC || SUNOS_X86
+#pragma pack ( 1 )
+#else
#pragma pack ( push, 1 )
+#endif //#if SUNOS_SPARC || SUNOS_X86
struct StoredCartChunk {
char Version[4]; // All of the fixed size text fields are null-filled local text,
@@ -49,7 +53,11 @@ struct StoredCartChunk {
static const size_t kMinimumCartChunkSize = sizeof(StoredCartChunk);
+#if SUNOS_SPARC || SUNOS_X86
+#pragma pack ( )
+#else
#pragma pack ( pop )
+#endif //#if SUNOS_SPARC || SUNOS_X86
static const size_t kFixedTextCount = (CartMetadata::kLastFixedTextField - CartMetadata::kFirstFixedTextField + 1);
diff --git a/XMPFiles/source/FormatSupport/WAVE/Cr8rMetadata.cpp b/XMPFiles/source/FormatSupport/WAVE/Cr8rMetadata.cpp
index c9cbc23..277924f 100644
--- a/XMPFiles/source/FormatSupport/WAVE/Cr8rMetadata.cpp
+++ b/XMPFiles/source/FormatSupport/WAVE/Cr8rMetadata.cpp
@@ -24,7 +24,11 @@ static const XMP_Uns32 kSizeAppOtions = 16;
static const XMP_Uns32 kSizeAppName = 32;
// Needed to be able to memcpy directly to this struct.
+#if SUNOS_SPARC || SUNOS_X86
+#pragma pack ( 1 )
+#else
#pragma pack ( push, 1 )
+#endif //#if SUNOS_SPARC || SUNOS_X86
struct Cr8rBoxContent
{
XMP_Uns32 mMagic;
@@ -37,7 +41,11 @@ static const XMP_Uns32 kSizeAppName = 32;
char mAppOptions[kSizeAppOtions];
char mAppName[kSizeAppName];
};
+#if SUNOS_SPARC || SUNOS_X86
+#pragma pack ( )
+#else
#pragma pack ( pop )
+#endif //#if SUNOS_SPARC || SUNOS_X86
//-----------------------------------------------------------------------------
//
diff --git a/XMPFiles/source/FormatSupport/WAVE/INFOMetadata.cpp b/XMPFiles/source/FormatSupport/WAVE/INFOMetadata.cpp
index 76faaa3..6597f2a 100644
--- a/XMPFiles/source/FormatSupport/WAVE/INFOMetadata.cpp
+++ b/XMPFiles/source/FormatSupport/WAVE/INFOMetadata.cpp
@@ -217,6 +217,8 @@ XMP_Uns64 INFOMetadata::serialize( XMP_Uns8** outBuffer )
//
memcpy( buffer+offset, &id, kSizeChunkID );
memcpy( buffer+offset+kSizeChunkID, &size, kSizeChunkSize );
+ //size has been changed in little endian format. Change it back to bigendina
+ size = LE.getUns32( &size );
memcpy( buffer+offset+kChunkHeaderSize, value.c_str(), size );
//
diff --git a/XMPFiles/source/FormatSupport/WAVE/PrmLMetadata.cpp b/XMPFiles/source/FormatSupport/WAVE/PrmLMetadata.cpp
index c5dc42e..477c31e 100644
--- a/XMPFiles/source/FormatSupport/WAVE/PrmLMetadata.cpp
+++ b/XMPFiles/source/FormatSupport/WAVE/PrmLMetadata.cpp
@@ -22,7 +22,11 @@ static const XMP_Uns32 kPrmlSizeFix = 282; // always 282 bytes
static const XMP_Uns32 kSizeFilePath = 260;
// Needed to be able to memcpy directly to this struct.
+#if SUNOS_SPARC || SUNOS_X86
+#pragma pack ( 1 )
+#else
#pragma pack ( push, 1 )
+#endif //#if SUNOS_SPARC || SUNOS_X86
struct PrmlBoxContent
{
XMP_Uns32 mMagic;
@@ -34,7 +38,11 @@ static const XMP_Uns32 kSizeFilePath = 260;
XMP_Uns32 mMacParID;
char mFilePath[kSizeFilePath];
};
+#if SUNOS_SPARC || SUNOS_X86
+#pragma pack ( )
+#else
#pragma pack ( pop )
+#endif //#if SUNOS_SPARC || SUNOS_X86
//-----------------------------------------------------------------------------
//
diff --git a/XMPFiles/source/FormatSupport/WAVE/WAVEBehavior.h b/XMPFiles/source/FormatSupport/WAVE/WAVEBehavior.h
index 1395cd1..b5a2e3f 100644
--- a/XMPFiles/source/FormatSupport/WAVE/WAVEBehavior.h
+++ b/XMPFiles/source/FormatSupport/WAVE/WAVEBehavior.h
@@ -33,7 +33,11 @@ class WAVEBehavior : public IChunkBehavior
{
// Internal structure to hold RF64 related data
public:
+#if SUNOS_SPARC || SUNOS_X86
+#pragma pack ( 1 )
+#else
#pragma pack ( push, 1 )
+#endif //#if SUNOS_SPARC || SUNOS_X86
struct ChunkSize64
{
XMP_Uns64 size;
@@ -55,7 +59,11 @@ public:
// ctor
DS64(): riffSize(0), dataSize(0), sampleCount(0), tableLength(0), trailingBytes(0) {}
};
+#if SUNOS_SPARC || SUNOS_X86
+#pragma pack ( )
+#else
#pragma pack ( pop )
+#endif //#if SUNOS_SPARC || SUNOS_X86
/**
diff --git a/XMPFiles/source/FormatSupport/XDCAM_Support.cpp b/XMPFiles/source/FormatSupport/XDCAM_Support.cpp
index b9704b9..cb65bf0 100644
--- a/XMPFiles/source/FormatSupport/XDCAM_Support.cpp
+++ b/XMPFiles/source/FormatSupport/XDCAM_Support.cpp
@@ -194,7 +194,7 @@ bool XDCAM_Support::GetLegacyMetadata ( SXMPMeta * xmpObjPtr,
bool containsXMP = false;
XML_NodePtr legacyContext = 0, legacyProp = 0;
- XMP_StringPtr formatFPS;
+ XMP_StringPtr formatFPS = 0;
// UMID
if ( digestFound || (! xmpObjPtr->DoesPropertyExist ( kXMP_NS_DC, "identifier" )) ) {
@@ -307,18 +307,20 @@ bool XDCAM_Support::GetLegacyMetadata ( SXMPMeta * xmpObjPtr,
}
}
- // Frame rate
+ // Frame rate (always read, because its used later for the Duration
+ legacyProp = legacyContext->GetNamedElement ( legacyNS, "VideoFrame" );
+ if ( (legacyProp != 0) && legacyProp->IsEmptyLeafNode() ) {
+ formatFPS = legacyProp->GetAttrValue ( "formatFps" );
+ }
+
+ // only write back to XMP if framerate is not set in XMP yet
if ( digestFound || (! xmpObjPtr->DoesPropertyExist ( kXMP_NS_DM, "videoFrameRate" )) ) {
- legacyProp = legacyContext->GetNamedElement ( legacyNS, "VideoFrame" );
- if ( (legacyProp != 0) && legacyProp->IsEmptyLeafNode() ) {
- formatFPS = legacyProp->GetAttrValue ( "formatFps" );
- if ( formatFPS != 0 ) {
- xmpObjPtr->SetProperty ( kXMP_NS_DM, "videoFrameRate", formatFPS, kXMP_DeleteExisting );
- containsXMP = true;
- }
+ if ( formatFPS != 0 ) {
+ xmpObjPtr->SetProperty ( kXMP_NS_DM, "videoFrameRate", formatFPS, kXMP_DeleteExisting );
+ containsXMP = true;
}
}
-
+
// Video codec
if ( digestFound || (! xmpObjPtr->DoesPropertyExist ( kXMP_NS_DM, "videoCompressor" )) ) {
legacyProp = legacyContext->GetNamedElement ( legacyNS, "VideoFrame" );
@@ -361,8 +363,12 @@ bool XDCAM_Support::GetLegacyMetadata ( SXMPMeta * xmpObjPtr,
if ( durationValue != 0 ) durationFrames = durationValue;
}
- std::string timeScale = GetTimeScale ( formatFPS );
-
+ std::string timeScale;
+ if ( formatFPS ) {
+
+ timeScale = GetTimeScale ( formatFPS );
+ }
+
if ( (! timeScale.empty()) && (! durationFrames.empty()) ) {
xmpObjPtr->DeleteProperty ( kXMP_NS_DM, "duration" );
xmpObjPtr->SetStructField ( kXMP_NS_DM, "duration", kXMP_NS_DM, "value", durationFrames );
diff --git a/XMPFiles/source/HandlerRegistry.cpp b/XMPFiles/source/HandlerRegistry.cpp
index 144dbcf..abaa3a4 100644
--- a/XMPFiles/source/HandlerRegistry.cpp
+++ b/XMPFiles/source/HandlerRegistry.cpp
@@ -10,8 +10,13 @@
#include "public/include/XMP_Environment.h" // ! XMP_Environment.h must be the first included header.
#include "public/include/XMP_Const.h"
+#include "source/XIO.hpp"
+
#include "XMPFiles/source/HandlerRegistry.h"
-#include "XMPFiles/source/PluginHandler/XMPAtoms.h"
+
+#if EnablePluginManager
+ #include "XMPFiles/source/PluginHandler/XMPAtoms.h"
+#endif
#if EnablePhotoHandlers
#include "XMPFiles/source/FileHandlers/JPEG_Handler.hpp"
@@ -47,7 +52,10 @@
//#endif
using namespace Common;
-using namespace XMP_PLUGIN;
+
+#if EnablePluginManager
+ using namespace XMP_PLUGIN;
+#endif
// =================================================================================================
@@ -374,7 +382,11 @@ XMP_FileFormat HandlerRegistry::getFileFormat( const std::string & fileExt, bool
}
}
- return ResourceParser::getPluginFileFormat ( fileExt, addIfNotFound );
+ #if EnablePluginManager
+ return ResourceParser::getPluginFileFormat ( fileExt, addIfNotFound );
+ #else
+ return kXMP_UnknownFile;
+ #endif
}
// =================================================================================================
@@ -602,8 +614,7 @@ XMPFileHandlerInfo* HandlerRegistry::selectSmartHandler( XMPFiles* session, XMP_
{
if( ( session->ioRef == 0 ) && (! ( handlerInfo->flags & kXMPFiles_HandlerOwnsFile ) ) )
{
- session->ioRef = XMPFiles_IO::New_XMPFiles_IO( clientPath, readOnly );
-
+ session->ioRef = XMPFiles_IO::New_XMPFiles_IO( clientPath, readOnly, &session->errorCallback);
if ( session->ioRef == 0 ) return 0;
}
@@ -731,7 +742,7 @@ XMPFileHandlerInfo* HandlerRegistry::selectSmartHandler( XMPFiles* session, XMP_
{
if( (session->ioRef == 0) && (! (handlerInfo->flags & kXMPFiles_HandlerOwnsFile)) )
{
- session->ioRef = XMPFiles_IO::New_XMPFiles_IO ( clientPath, readOnly );
+ session->ioRef = XMPFiles_IO::New_XMPFiles_IO ( clientPath, readOnly, &session->errorCallback);
if ( session->ioRef == 0 ) return 0;
}
else if( (session->ioRef != 0) && (handlerInfo->flags & kXMPFiles_HandlerOwnsFile) )
@@ -753,7 +764,7 @@ XMPFileHandlerInfo* HandlerRegistry::selectSmartHandler( XMPFiles* session, XMP_
if( session->ioRef == 0 )
{
- session->ioRef = XMPFiles_IO::New_XMPFiles_IO ( clientPath, readOnly );
+ session->ioRef = XMPFiles_IO::New_XMPFiles_IO ( clientPath, readOnly, &session->errorCallback );
if ( session->ioRef == 0 ) return 0;
}
diff --git a/XMPFiles/source/PluginHandler/FileHandler.h b/XMPFiles/source/PluginHandler/FileHandler.h
index b8ac77d..8d68258 100644
--- a/XMPFiles/source/PluginHandler/FileHandler.h
+++ b/XMPFiles/source/PluginHandler/FileHandler.h
@@ -55,8 +55,8 @@ public:
virtual ~FileHandler(){}
- inline XMP_Int64 getVersion() const { return mVersion; }
- inline void setVersion( XMP_Uns32 version ) { mVersion = version; }
+ inline double getVersion() const { return mVersion; }
+ inline void setVersion( double version ) { mVersion = version; }
inline const std::string& getUID() const { return mUID; }
inline XMP_OptionBits getHandlerFlags() const { return mHandlerFlags; }
@@ -88,7 +88,7 @@ private:
typedef std::vector<CheckFormat> CheckFormatVec;
CheckFormatVec mCheckFormatVec;
- XMP_Uns32 mVersion;
+ double mVersion;
std::string mUID;
XMP_OptionBits mHandlerFlags;
XMP_OptionBits mSerializeOption;
diff --git a/XMPFiles/source/PluginHandler/FileHandlerInstance.cpp b/XMPFiles/source/PluginHandler/FileHandlerInstance.cpp
index 42de1a4..3d2ffd8 100644
--- a/XMPFiles/source/PluginHandler/FileHandlerInstance.cpp
+++ b/XMPFiles/source/PluginHandler/FileHandlerInstance.cpp
@@ -30,12 +30,12 @@ FileHandlerInstance::~FileHandlerInstance()
bool FileHandlerInstance::GetFileModDate ( XMP_DateTime * modDate )
{
- bool ok;
+ XMP_Bool ok;
WXMP_Error error;
GetSessionFileModDateProc wGetFileModDate = mHandler->getModule()->getPluginAPIs()->mGetFileModDateProc;
wGetFileModDate ( this->mObject, &ok, modDate, &error );
CheckError ( error );
- return ok;
+ return ConvertXMP_BoolToBool( ok );
}
void FileHandlerInstance::CacheFileData()
@@ -69,8 +69,26 @@ void FileHandlerInstance::ProcessXMP()
this->xmpObj.ParseFromBuffer ( this->xmpPacket.c_str(), (XMP_StringLen)this->xmpPacket.size() );
WXMP_Error error;
- if( mHandler->getModule()->getPluginAPIs()->mImportToXMPProc )
- mHandler->getModule()->getPluginAPIs()->mImportToXMPProc( this->mObject, this->xmpObj.GetInternalRef(), &error );
+ if( mHandler->getModule()->getPluginAPIs()->mImportToXMPStringProc )
+ {
+ std::string xmp;
+ this->xmpObj.SerializeToBuffer(&xmp, kXMP_NoOptions, 0);
+ XMP_StringPtr xmpStr=xmp.c_str();
+ mHandler->getModule()->getPluginAPIs()->mImportToXMPStringProc( this->mObject, &xmpStr, &error );
+ if( xmpStr!= NULL && xmpStr != xmp.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.
+ }
+ }
+ else
+ {
+ if( mHandler->getModule()->getPluginAPIs()->mImportToXMPProc )
+ mHandler->getModule()->getPluginAPIs()->mImportToXMPProc( this->mObject, this->xmpObj.GetInternalRef(), &error );
+ }
CheckError( error );
}
@@ -79,8 +97,18 @@ void FileHandlerInstance::UpdateFile ( bool doSafeUpdate )
if ( !this->needsUpdate || this->xmpPacket.size() == 0 ) return;
WXMP_Error error;
- if( mHandler->getModule()->getPluginAPIs()->mExportFromXMPProc )
- mHandler->getModule()->getPluginAPIs()->mExportFromXMPProc( this->mObject, this->xmpObj.GetInternalRef(), &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( mHandler->getModule()->getPluginAPIs()->mExportFromXMPProc )
+ mHandler->getModule()->getPluginAPIs()->mExportFromXMPProc( this->mObject, this->xmpObj.GetInternalRef(), &error );
+ }
CheckError( error );
this->xmpObj.SerializeToBuffer ( &this->xmpPacket, mHandler->getSerializeOption() );
@@ -103,4 +131,53 @@ void FileHandlerInstance::WriteTempFile( XMP_IO* tempRef )
CheckError( error );
}
+static void SetStringVector ( StringVectorRef clientPtr, XMP_StringPtr * arrayPtr, XMP_Uns32 stringCount )
+{
+ std::vector<std::string>* clientVec = (std::vector<std::string>*) clientPtr;
+ clientVec->clear();
+ for ( XMP_Uns32 i = 0; i < stringCount; ++i ) {
+ std::string nextValue ( arrayPtr[i] );
+ clientVec->push_back ( nextValue );
+ }
+}
+
+
+void FileHandlerInstance::FillMetadataFiles( std::vector<std::string> * metadataFiles )
+{
+ WXMP_Error error;
+ FillMetadataFilesProc wFillMetadataFilesProc = mHandler->getModule()->getPluginAPIs()->mFillMetadataFilesProc;
+ if ( wFillMetadataFilesProc ) {
+ wFillMetadataFilesProc( this->mObject, metadataFiles, SetStringVector, &error);
+ CheckError( error );
+ } else {
+ XMP_Throw ( "This version of plugin does not support FillMetadataFiles API", kXMPErr_Unimplemented );
+ }
+}
+
+void FileHandlerInstance::FillAssociatedResources( std::vector<std::string> * resourceList )
+{
+ WXMP_Error error;
+ FillAssociatedResourcesProc wFillAssociatedResourcesProc = mHandler->getModule()->getPluginAPIs()->mFillAssociatedResourcesProc;
+ if ( wFillAssociatedResourcesProc ) {
+ wFillAssociatedResourcesProc( this->mObject, resourceList, SetStringVector, &error);
+ CheckError( error );
+ } else {
+ XMP_Throw ( "This version of plugin does not support FillAssociatedResources API", kXMPErr_Unimplemented );
+ }
+}
+
+bool FileHandlerInstance::IsMetadataWritable( )
+{
+ WXMP_Error error;
+ XMP_Bool result = kXMP_Bool_False;
+ IsMetadataWritableProc wIsMetadataWritableProc = mHandler->getModule()->getPluginAPIs()->mIsMetadataWritableProc;
+ if ( wIsMetadataWritableProc ) {
+ wIsMetadataWritableProc( this->mObject, &result, &error);
+ CheckError( error );
+ } else {
+ XMP_Throw ( "This version of plugin does not support IsMetadataWritable API", kXMPErr_Unimplemented );
+ }
+ return ConvertXMP_BoolToBool( result );
+}
+
} //namespace XMP_PLUGIN
diff --git a/XMPFiles/source/PluginHandler/FileHandlerInstance.h b/XMPFiles/source/PluginHandler/FileHandlerInstance.h
index 5cf2420..9810831 100644
--- a/XMPFiles/source/PluginHandler/FileHandlerInstance.h
+++ b/XMPFiles/source/PluginHandler/FileHandlerInstance.h
@@ -34,6 +34,9 @@ public:
//virtual XMP_OptionBits GetSerializeOptions(); //It should not be needed as its required only inside updateFile.
virtual void UpdateFile ( bool doSafeUpdate );
virtual void WriteTempFile ( XMP_IO* tempRef );
+ virtual void FillMetadataFiles ( std::vector<std::string> * metadataFiles );
+ virtual void FillAssociatedResources ( std::vector<std::string> * resourceList );
+ virtual bool IsMetadataWritable ( );
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 04dcdd0..cf6a389 100644
--- a/XMPFiles/source/PluginHandler/HostAPIImpl.cpp
+++ b/XMPFiles/source/PluginHandler/HostAPIImpl.cpp
@@ -49,7 +49,7 @@ static void HandleException( WXMP_Error* wError )
// FileIO_API
//
-static XMPErrorID FileSysRead( XMP_IORef io, void* buffer, XMP_Uns32 count, bool readAll, XMP_Uns32& byteRead, WXMP_Error * wError )
+static XMPErrorID FileSysRead( XMP_IORef io, void* buffer, XMP_Uns32 count, XMP_Bool readAll, XMP_Uns32& byteRead, WXMP_Error * wError )
{
if( wError == NULL ) return kXMPErr_BadParam;
@@ -60,7 +60,7 @@ static XMPErrorID FileSysRead( XMP_IORef io, void* buffer, XMP_Uns32 count, bool
if( io != NULL )
{
::XMP_IO * thiz = (::XMP_IO*)io;
- byteRead = thiz->Read( buffer, count, readAll );
+ byteRead = thiz->Read( buffer, count, ConvertXMP_BoolToBool( readAll ) );
wError->mErrorID = kXMPErr_NoError;
}
}
@@ -318,7 +318,7 @@ static void GetStringAPI( String_API* strAPI )
// Abort_API
//
-static XMPErrorID CheckAbort( SessionRef session, bool* aborted, WXMP_Error* wError )
+static XMPErrorID CheckAbort( SessionRef session, XMP_Bool * aborted, WXMP_Error* wError )
{
if( wError == NULL ) return kXMPErr_BadParam;
@@ -326,7 +326,7 @@ static XMPErrorID CheckAbort( SessionRef session, bool* aborted, WXMP_Error* wEr
if( aborted )
{
- *aborted = false;
+ *aborted = kXMP_Bool_False;
//
// find FileHandlerInstance associated to session reference
@@ -346,7 +346,7 @@ static XMPErrorID CheckAbort( SessionRef session, bool* aborted, WXMP_Error* wEr
{
try
{
- *aborted = proc( arg );
+ *aborted = ConvertBoolToXMP_Bool( proc( arg ) );
}
catch( ... )
{
@@ -376,7 +376,7 @@ static void GetAbortAPI( Abort_API* abortAPI )
// StandardHandler_API
//
-static XMPErrorID CheckFormatStandardHandler( SessionRef session, XMP_FileFormat format, StringPtr path, bool& checkOK, WXMP_Error* wError )
+static XMPErrorID CheckFormatStandardHandler( SessionRef session, XMP_FileFormat format, StringPtr path, XMP_Bool & checkOK, WXMP_Error* wError )
{
if( wError == NULL ) return kXMPErr_BadParam;
@@ -407,7 +407,7 @@ static XMPErrorID CheckFormatStandardHandler( SessionRef session, XMP_FileFormat
//
XMPFiles standardClient;
standardClient.format = format;
- standardClient.filePath = std::string( path );
+ standardClient.SetFilePath( path );
if( hdlInfo->flags & kXMPFiles_FolderBasedFormat )
{
@@ -508,7 +508,7 @@ static XMPErrorID CheckFormatStandardHandler( SessionRef session, XMP_FileFormat
return wError->mErrorID;
}
-static XMPErrorID GetXMPStandardHandler( SessionRef session, XMP_FileFormat format, StringPtr path, XMPMetaRef xmpRef, bool* xmpExists, WXMP_Error* wError )
+static XMPErrorID GetXMPStandardHandler( SessionRef session, XMP_FileFormat format, StringPtr path, XMPMetaRef xmpRef, XMP_Bool * xmpExists, WXMP_Error* wError )
{
if( wError == NULL ) return kXMPErr_BadParam;
@@ -532,18 +532,18 @@ static XMPErrorID GetXMPStandardHandler( SessionRef session, XMP_FileFormat form
//
// check format first
//
- bool suc = false;
+ XMP_Bool suc = kXMP_Bool_False;
XMPErrorID errorID = CheckFormatStandardHandler( session, format, path, suc, wError );
- if( errorID == kXMPErr_NoError && suc )
+ if( errorID == kXMPErr_NoError && ConvertXMP_BoolToBool( suc ) )
{
//
// setup temporary XMPFiles instance
//
XMPFiles standardClient;
standardClient.format = format;
- standardClient.filePath = std::string( path );
+ standardClient.SetFilePath( path );
SXMPMeta meta( xmpRef );
@@ -601,6 +601,27 @@ static XMPErrorID GetXMPStandardHandler( SessionRef session, XMP_FileFormat form
return wError->mErrorID;
}
+static XMPErrorID GetXMPStandardHandler_V2( SessionRef session, XMP_FileFormat format, StringPtr path, XMP_StringPtr* xmpStr, XMP_Bool * xmpExists, WXMP_Error* wError )
+{
+ SXMPMeta meta;
+ std::string xmp;
+ GetXMPStandardHandler( session, format, path, meta.GetInternalRef(), xmpExists, wError ) ;
+ 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 void GetStandardHandlerAPI( StandardHandler_API* standardHandlerAPI )
{
if( standardHandlerAPI )
@@ -608,13 +629,60 @@ static void GetStandardHandlerAPI( StandardHandler_API* standardHandlerAPI )
standardHandlerAPI->mCheckFormatStandardHandler = CheckFormatStandardHandler;
standardHandlerAPI->mGetXMPStandardHandler = GetXMPStandardHandler;
}
+
+}
+
+
+static XMPErrorID RequestAPISuite( const char* apiName, XMP_Uns32 apiVersion, void** apiSuite, WXMP_Error* wError )
+{
+ if( wError == NULL )
+ {
+ return kXMPErr_BadParam;
+ }
+
+ wError->mErrorID = kXMPErr_NoError;
+
+ if( apiName == NULL
+ || apiVersion == 0
+ || apiSuite == NULL )
+ {
+ wError->mErrorID = kXMPErr_BadParam;
+ return kXMPErr_BadParam;
+ }
+
+ // dummy suite used by unit test
+ if( strcmp( apiName, "testDummy" ) == 0 && apiVersion == 1 )
+ {
+ *apiSuite = (void*) &RequestAPISuite;
+ }
+ else if ( ! strcmp( apiName, "StandardHandler" ) && apiVersion == 2 )
+ {
+ static const StandardHandler_API_V2 standardHandlerAPI =
+ {
+ &CheckFormatStandardHandler,
+ &GetXMPStandardHandler_V2
+ };
+ *apiSuite=(void*)&standardHandlerAPI;
+ }
+ else
+ {
+ wError->mErrorID = kXMPErr_Unimplemented;
+ }
+
+ return wError->mErrorID;
}
+
///////////////////////////////////////////////////////////////////////////////
//
// Init/Term Host APIs
//
+// Because of changes to the plugin versioning strategy,
+// the host API version is no longer tied to the plugin version
+// and the host API struct is supposed to be frozen.
+// New host APIs can be requested through the new function mRequestAPISuite.
+
void PluginManager::SetupHostAPI_V1( HostAPIRef hostAPI )
{
// Get XMP_IO APIs
@@ -632,6 +700,24 @@ void PluginManager::SetupHostAPI_V1( HostAPIRef hostAPI )
// Get standard handler APIs
hostAPI->mStandardHandlerAPI = new StandardHandler_API();
GetStandardHandlerAPI( hostAPI->mStandardHandlerAPI );
+
+ hostAPI->mRequestAPISuite = NULL;
+}
+
+void PluginManager::SetupHostAPI_V2( HostAPIRef hostAPI )
+{
+ SetupHostAPI_V1 ( hostAPI );
+}
+
+void PluginManager::SetupHostAPI_V3( HostAPIRef hostAPI )
+{
+ SetupHostAPI_V2 ( hostAPI );
+}
+
+void PluginManager::SetupHostAPI_V4( HostAPIRef hostAPI )
+{
+ SetupHostAPI_V3 ( hostAPI );
+ hostAPI->mRequestAPISuite = RequestAPISuite;
}
} //namespace XMP_PLUGIN
diff --git a/XMPFiles/source/PluginHandler/Module.cpp b/XMPFiles/source/PluginHandler/Module.cpp
index 91b8ea8..595c17d 100644
--- a/XMPFiles/source/PluginHandler/Module.cpp
+++ b/XMPFiles/source/PluginHandler/Module.cpp
@@ -8,28 +8,153 @@
// =================================================================================================
#include "Module.h"
+#include "HostAPI.h"
namespace XMP_PLUGIN
{
+static bool CheckAPICompatibility_V1 ( const PluginAPIRef pluginAPIs )
+{
+ // these plugin APIs are mandatory to run an XMP file handler
+ if ( pluginAPIs->mTerminatePluginProc
+ && pluginAPIs->mSetHostAPIProc
+ && pluginAPIs->mInitializeSessionProc
+ && pluginAPIs->mTerminateSessionProc
+ && pluginAPIs->mCheckFileFormatProc
+ && pluginAPIs->mCheckFolderFormatProc
+ && pluginAPIs->mGetFileModDateProc
+ && pluginAPIs->mCacheFileDataProc
+ && pluginAPIs->mUpdateFileProc
+ && pluginAPIs->mWriteTempFileProc )
+ {
+ return true;
+ }
+ return false;
+}
+
+static bool CheckAPICompatibility_V2 ( const PluginAPIRef pluginAPIs )
+{
+ if ( CheckAPICompatibility_V1 ( pluginAPIs ) )
+ {
+ if ( pluginAPIs->mFillMetadataFilesProc
+ && pluginAPIs->mFillAssociatedResourcesProc )
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+static bool CheckAPICompatibility_V3 ( const PluginAPIRef pluginAPIs )
+{
+ if ( CheckAPICompatibility_V2 ( pluginAPIs ) )
+ {
+ if ( pluginAPIs->mIsMetadataWritableProc )
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+static bool CheckAPICompatibility ( const PluginAPIRef pluginAPIs )
+{
+ // Note: This is the place where we can reject old plugins.
+ // For example if we consider all functionality of
+ // plugin API version 2 mandatory we can reject
+ // plugin version 1 by returning false in case 1.
+ switch ( pluginAPIs->mVersion )
+ {
+ case 1:
+ return CheckAPICompatibility_V1 ( pluginAPIs );
+ break;
+
+ case 2:
+ return CheckAPICompatibility_V2 ( pluginAPIs );
+ break;
+
+ case 3:
+ return CheckAPICompatibility_V3 ( pluginAPIs );
+ break;
+
+ default:
+ // The loaded plugin is newer than the host.
+ // Only basic functionality to run the plugin is required.
+ return CheckAPICompatibility_V1 ( pluginAPIs );
+ break;
+ }
+}
+
PluginAPIRef Module::getPluginAPIs()
{
//
// return ref. to Plugin API, load module if not yet loaded
//
- if( !mPluginAPIs && !load() )
+ if ( !mPluginAPIs || mLoaded != kModuleLoaded )
{
- XMP_Throw ( "Plugin API not available.", kXMPErr_Unavailable );
+ if ( !load() )
+ {
+ XMP_Throw ( "Plugin API not available.", kXMPErr_Unavailable );
+ }
}
-
return mPluginAPIs;
}
bool Module::load()
{
+ XMP_AutoLock lock ( &mLoadingLock, kXMP_WriteLock );
+ return loadInternal();
+}
+
+void Module::unload()
+{
+ XMP_AutoLock lock (&mLoadingLock, kXMP_WriteLock);
+ unloadInternal();
+}
+
+void Module::unloadInternal()
+{
+ WXMP_Error error;
+
+ //
+ // terminate plugin
+ //
+ if( mPluginAPIs != NULL )
+ {
+ if( mPluginAPIs->mTerminatePluginProc )
+ {
+ mPluginAPIs->mTerminatePluginProc( &error );
+ }
+ delete mPluginAPIs;
+ mPluginAPIs = NULL;
+ }
+
+ //
+ // unload plugin module
+ //
+ if( mLoaded != kModuleNotLoaded )
+ {
+ UnloadModule(mHandle, false);
+ mHandle = NULL;
+ if( mLoaded == kModuleLoaded )
+ {
+ //
+ // Reset mLoaded to kModuleNotLoaded, if the module was loaded successfully.
+ // Otherwise let it remain kModuleErrorOnLoad so that we won't try to load
+ // it again if some other handler ask to do so.
+ //
+ mLoaded = kModuleNotLoaded;
+ }
+ }
+
+ CheckError( error );
+}
+
+bool Module::loadInternal()
+{
if( mLoaded == kModuleNotLoaded )
{
- std::string errorMsg;
+ const char * errorMsg = NULL;
//
// load module
@@ -43,63 +168,88 @@ bool Module::load()
// get entry point function pointer
//
InitializePluginProc InitializePlugin = reinterpret_cast<InitializePluginProc>(
- GetFunctionPointerFromModuleImpl(mHandle, "InitializePlugin") );
-
- if( InitializePlugin != NULL )
+ GetFunctionPointerFromModuleImpl(mHandle, "InitializePlugin") ); // legacy entry point
+
+ InitializePlugin2Proc InitializePlugin2 = reinterpret_cast<InitializePlugin2Proc>(
+ GetFunctionPointerFromModuleImpl(mHandle, "InitializePlugin2") );
+
+ if( InitializePlugin2 != NULL || InitializePlugin != NULL )
{
- //
- // get module ID from plugin resource
- //
std::string moduleID;
GetResourceDataFromModule(mHandle, "MODULE_IDENTIFIER", "txt", moduleID);
mPluginAPIs = new PluginAPI();
-
+ memset( mPluginAPIs, 0, sizeof(PluginAPI) );
+ mPluginAPIs->mSize = sizeof(PluginAPI);
+ mPluginAPIs->mVersion = XMP_PLUGIN_VERSION; // informational: the latest version that the host knows about
+
+ WXMP_Error error;
+
//
// initialize plugin by calling entry point function
//
- WXMP_Error error;
- InitializePlugin( moduleID.c_str(), mPluginAPIs, &error );
-
- if( error.mErrorID == kXMPErr_NoError
- && mPluginAPIs->mTerminatePluginProc
- && mPluginAPIs->mSetHostAPIProc
- && mPluginAPIs->mInitializeSessionProc
- && mPluginAPIs->mTerminateSessionProc
- && mPluginAPIs->mCacheFileDataProc
- && mPluginAPIs->mUpdateFileProc
- && mPluginAPIs->mWriteTempFileProc
- )
+ if( InitializePlugin2 != NULL )
{
- //
- // set host API at plugin
- //
- HostAPIRef hostAPI = PluginManager::getHostAPI( mPluginAPIs->mVersion );
- mPluginAPIs->mSetHostAPIProc( hostAPI, &error );
-
- if( error.mErrorID == kXMPErr_NoError )
+ HostAPIRef hostAPI = PluginManager::getHostAPI( XMP_HOST_API_VERSION );
+ InitializePlugin2( moduleID.c_str(), hostAPI, mPluginAPIs, &error );
+
+ if ( error.mErrorID == kXMPErr_NoError )
{
- mLoaded = kModuleLoaded;
+ // check all function pointers are correct based on version numbers
+ if( CheckAPICompatibility( mPluginAPIs ) )
+ {
+ mLoaded = kModuleLoaded;
+ }
+ else
+ {
+ errorMsg = "Incompatible plugin API version.";
+ }
}
else
{
- errorMsg = "Incompatible plugin API version. (" + moduleID + ")";
+ errorMsg = "Plugin initialization failed.";
}
}
- else
+ else if( InitializePlugin != NULL )
{
- if( error.mErrorID != kXMPErr_NoError )
- {
- errorMsg = "Plugin initialization failed. (" + moduleID + ")";
+ // initialize through legacy plugin entry point
+ InitializePlugin( moduleID.c_str(), mPluginAPIs, &error );
+
+ if ( error.mErrorID == kXMPErr_NoError ) {
+ // check all function pointers are correct based on version numbers
+ bool compatibleAPIs = CheckAPICompatibility(mPluginAPIs);
+
+ if ( compatibleAPIs )
+ {
+ //
+ // set host API at plugin
+ //
+ HostAPIRef hostAPI = PluginManager::getHostAPI( mPluginAPIs->mVersion );
+
+ mPluginAPIs->mSetHostAPIProc( hostAPI, &error );
+
+ if( error.mErrorID == kXMPErr_NoError )
+ {
+ mLoaded = kModuleLoaded;
+ }
+ else
+ {
+ errorMsg = "Plugin API incomplete.";
+ }
+ }
+ else
+ {
+ errorMsg = "Incompatible plugin API version.";
+ }
}
else
{
- errorMsg = "Plugin API incomplete. (" + moduleID + ")";
+ errorMsg = "Plugin initialization failed.";
}
}
- }
- else
- {
- errorMsg = "Missing plugin entry point \"InitializePlugin\" in plugin " + mPath;
+ else
+ {
+ errorMsg = "Missing plugin entry point in plugin";
+ }
}
if( mLoaded != kModuleLoaded )
@@ -108,62 +258,25 @@ bool Module::load()
// plugin wasn't loaded and initialized successfully,
// so unload the module
//
- this->unload();
+ this->unloadInternal();
}
}
else
{
- errorMsg = "Can't load module " + mPath;
+ errorMsg = "Can't load module";
}
- if( mLoaded != kModuleLoaded )
+ if ( mLoaded != kModuleLoaded && errorMsg )
{
//
// error occurred
//
- throw XMP_Error( kXMPErr_InternalFailure, errorMsg.c_str() );
+ throw XMP_Error( kXMPErr_InternalFailure, errorMsg);
}
}
return ( mLoaded == kModuleLoaded );
-}
-void Module::unload()
-{
- WXMP_Error error;
-
- //
- // terminate plugin
- //
- if( mPluginAPIs != NULL )
- {
- if( mPluginAPIs->mTerminatePluginProc )
- {
- mPluginAPIs->mTerminatePluginProc( &error );
- }
- delete mPluginAPIs;
- mPluginAPIs = NULL;
- }
-
- //
- // unload plugin module
- //
- if( mLoaded != kModuleNotLoaded )
- {
- UnloadModule(mHandle, false);
- mHandle = NULL;
- if( mLoaded == kModuleLoaded )
- {
- //
- // Reset mLoaded to kModuleNotLoaded, if the module was loaded successfully.
- // Otherwise let it remain kModuleErrorOnLoad so that we won't try to load
- // it again if some other handler ask to do so.
- //
- mLoaded = kModuleNotLoaded;
- }
- }
-
- CheckError( error );
}
} //namespace XMP_PLUGIN
diff --git a/XMPFiles/source/PluginHandler/Module.h b/XMPFiles/source/PluginHandler/Module.h
index 0e308a0..1d7fdd9 100644
--- a/XMPFiles/source/PluginHandler/Module.h
+++ b/XMPFiles/source/PluginHandler/Module.h
@@ -48,6 +48,17 @@ public:
void unload();
private:
+
+ /************************************************************************/
+ /* Loads the module without acquiring the lock */
+ /************************************************************************/
+ bool loadInternal();
+
+ /************************************************************************/
+ /* Unloads the module without acquiring the lock */
+ /************************************************************************/
+ void unloadInternal();
+
typedef enum
{
kModuleNotLoaded = 0,
@@ -59,6 +70,7 @@ private:
OS_ModuleRef mHandle;
PluginAPIRef mPluginAPIs;
LoadStatus mLoaded;
+ XMP_ReadWriteLock mLoadingLock;
};
} //namespace XMP_PLUGIN
diff --git a/XMPFiles/source/PluginHandler/OS_Utils_Linux.cpp b/XMPFiles/source/PluginHandler/OS_Utils_Linux.cpp
index 1fe1f9f..7808a98 100644
--- a/XMPFiles/source/PluginHandler/OS_Utils_Linux.cpp
+++ b/XMPFiles/source/PluginHandler/OS_Utils_Linux.cpp
@@ -26,6 +26,7 @@ static ModuleRefToPathMap sMapModuleRefToPath;
//typedef std::map<void*, std::string> ResourceFileToPathMap;
typedef std::map<OS_ModuleRef, std::string> ResourceFileToPathMap;
static ResourceFileToPathMap sMapResourceFileToPath;
+static XMP_ReadWriteLock sMapModuleRWLock;
typedef std::tr1::shared_ptr<int> FilePtr;
@@ -104,6 +105,7 @@ OS_ModuleRef LoadModule( const std::string & inModulePath, bool inOnlyResourceAc
}
else
{ // success !
+ XMP_AutoLock writeLock ( &sMapModuleRWLock, kXMP_WriteLock );
ModuleRefToPathMap::const_iterator iter = sMapModuleRefToPath.find(result);
if( iter == sMapModuleRefToPath.end() )
{
@@ -136,6 +138,7 @@ void UnloadModule( OS_ModuleRef inModule, bool inOnlyResourceAccess )
}
else
{
+ XMP_AutoLock writeLock ( &sMapModuleRWLock, kXMP_WriteLock );
ModuleRefToPathMap::iterator iter = sMapModuleRefToPath.find(inModule);
if( iter != sMapModuleRefToPath.end() )
{
@@ -160,7 +163,12 @@ static std::string GetModulePath(
if( inOSModule != NULL )
{
- ModuleRefToPathMap::const_iterator iter = sMapModuleRefToPath.find(inOSModule);
+ ModuleRefToPathMap::const_iterator iter;
+ {
+ XMP_AutoLock readLock ( &sMapModuleRWLock, kXMP_ReadLock );
+ iter = sMapModuleRefToPath.find(inOSModule);
+ }
+
ResourceFileToPathMap::const_iterator iter2 = sMapResourceFileToPath.find(inOSModule);
if( (iter != sMapModuleRefToPath.end()) && (iter2 != sMapResourceFileToPath.end()) )
{
diff --git a/XMPFiles/source/PluginHandler/OS_Utils_Mac.cpp b/XMPFiles/source/PluginHandler/OS_Utils_Mac.cpp
index c042eb7..e9c2b46 100644
--- a/XMPFiles/source/PluginHandler/OS_Utils_Mac.cpp
+++ b/XMPFiles/source/PluginHandler/OS_Utils_Mac.cpp
@@ -166,9 +166,6 @@ private:
typedef AutoCFRef<CFURLRef> AutoCFURL;
typedef AutoCFRef<CFStringRef> AutoCFString;
-typedef std::tr1::shared_ptr<FSIORefNum> FilePtr;
-
-
/** ************************************************************************************************************************
** Convert string into CFString
*/
@@ -178,85 +175,6 @@ static inline CFStringRef MakeCFString(const std::string& inString, CFStringEnco
return str;
}
-/** ************************************************************************************************************************
-** Convert CFString into std::string
-*/
-static std::string MacToSTDString(CFStringRef inCFString)
-{
- std::string result;
-
- if (inCFString == NULL)
- {
- return result;
- }
-
- // The CFStringGetLength returns the length of the string in UTF-16 encoding units.
- ::CFIndex const stringUtf16Length = ::CFStringGetLength(inCFString);
- if (stringUtf16Length == 0)
- {
- return result;
- }
-
- // Check if the CFStringRef can allow fast-return.
- char const* ptr = ::CFStringGetCStringPtr(inCFString, kCFStringEncodingUTF8);
- if (ptr != NULL)
- {
- result = std::string( ptr ); // This kind of assign expects '\0' termination.
- return result;
- }
-
- // Since this is an encoding conversion, the converted string may not be the same length in bytes
- // as the CFStringRef in UTF-16 encoding units.
-
- ::UInt8 const noLossByte = 0;
- ::Boolean const internalRepresentation = FALSE; // TRUE is external representation, with a BOM. FALSE means no BOM.
- ::CFRange const range = ::CFRangeMake(0, stringUtf16Length); // [NOTE] length is in UTF-16 encoding units, not bytes.
- ::CFIndex const maxBufferLength = ::CFStringGetMaximumSizeForEncoding(stringUtf16Length, kCFStringEncodingUTF8); // Convert from UTF-16 encoding units to UTF-8 worst-case-scenario encoding units, in bytes.
- ::CFIndex usedBufferLength = 0; // In byte count.
- char buffer[1024];
- memset( buffer, 0, 1024 );
- ::CFIndex numberOfUtf16EncodingUnitsConverted = ::CFStringGetBytes(inCFString, range, ::kCFStringEncodingUTF8, noLossByte, internalRepresentation, (UInt8*)buffer, maxBufferLength, &usedBufferLength);
- result = std::string( buffer );
-
- return result;
-}
-
-
-static void CloseFile( FSIORefNum* inFilePtr )
-{
- FSCloseFork(*inFilePtr);
- delete inFilePtr;
-}
-
-static FilePtr OpenResourceFile( OS_ModuleRef inOSModule, const std::string& inResourceName, const std::string& inResourceType, bool inSearchInSubFolderWithNameOfResourceType)
-{
- FilePtr file;
- if (inOSModule != nil)
- {
- AutoCFString resourceName( MakeCFString( inResourceName ) );
- AutoCFString resourceType( MakeCFString( inResourceType ) );
- AutoCFString subfolderName(inSearchInSubFolderWithNameOfResourceType ? MakeCFString( inResourceType ) : nil);
-
- AutoCFURL url(
- ::CFBundleCopyResourceURL(inOSModule, *resourceName, *resourceType, *subfolderName));
-
- FSRef fileReference;
- if (!url.IsNull() && ::CFURLGetFSRef(*url, &fileReference))
- {
- HFSUniStr255 str;
- if (::FSGetDataForkName(&str) == noErr)
- {
- FSIORefNum forkRef;
- if (::FSOpenFork(&fileReference, str.length, str.unicode, fsRdPerm, &forkRef) == noErr)
- {
- file.reset(new FSIORefNum(forkRef), CloseFile);
- }
- }
- }
- }
- return file;
-}
-
bool IsValidLibrary( const std::string & inModulePath )
{
bool result = false;
@@ -303,12 +221,11 @@ OS_ModuleRef LoadModule( const std::string & inModulePath, bool inOnlyResourceAc
loaded = ::CFBundleLoadExecutableAndReturnError(result, &errorRef);
if(!loaded || errorRef != NULL)
{
- AutoCFString errorDescr(::CFErrorCopyDescription(errorRef));
- throw XMP_Error( kXMPErr_InternalFailure, MacToSTDString(*errorDescr).c_str() );
::CFRelease(errorRef);
// release bundle and return NULL
::CFRelease(result);
result = NULL;
+ throw XMP_Error( kXMPErr_InternalFailure, "Failed to load module" );
}
}
}
@@ -341,26 +258,42 @@ bool GetResourceDataFromModule(
std::string & outBuffer)
{
bool success = false;
-
- if (FilePtr file = OpenResourceFile(inOSModule, inResourceName, inResourceType, false))
+ bool inSearchInSubFolderWithNameOfResourceType = false;
+ AutoCFString resourceName( MakeCFString( inResourceName ) );
+ AutoCFString resourceType( MakeCFString( inResourceType ) );
+ AutoCFString subfolderName( inSearchInSubFolderWithNameOfResourceType ? MakeCFString( inResourceType ) : nil );
+
+ AutoCFURL url( ::CFBundleCopyResourceURL( inOSModule, *resourceName, *resourceType, *subfolderName ) );
+
+ if ( !url.IsNull() )
{
- ByteCount size = 0;
- SInt64 fork_size = 0;
- ::FSGetForkSize(*file.get(), &fork_size);
- // presumingly we don't want to load more than 2GByte at once (!)
- if( fork_size < std::numeric_limits<XMP_Int32>::max() )
+ typedef AutoCFRef<CFDataRef> AutoCFData;
+ typedef AutoCFRef<CFNumberRef> AutoCFNumber;
+ AutoCFData resourceData;
+ SInt32 errorCode = 0;
+ AutoCFNumber length;
+ *length = reinterpret_cast<CFNumberRef> ( ::CFURLCreatePropertyFromResource( kCFAllocatorDefault, *url, kCFURLFileLength, &errorCode ) );
+
+ if ( !errorCode )
{
- size = static_cast<ByteCount>(fork_size);
- if (size > 0)
+ 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() )
{
- outBuffer.resize(size);
- ::FSReadFork(*file.get(), fsFromStart, 0, size, (unsigned char*)&outBuffer[0], (ByteCount*)&size);
+ success = ::CFURLCreateDataAndPropertiesFromResource( kCFAllocatorDefault, *url, &(*resourceData), NULL, NULL, &errorCode );
+ if ( success && errorCode == 0 )
+ {
+ outBuffer.resize( sizeOfFile );
+ CFRange range = CFRangeMake (0, sizeOfFile );
+ ::CFDataGetBytes( *resourceData, range, reinterpret_cast< UInt8 * > (&outBuffer[0]) );
+ return true;
+ }
}
- success = true;
}
}
- return success;
+ return false;
}
} //namespace XMP_PLUGIN
diff --git a/XMPFiles/source/PluginHandler/PluginManager.cpp b/XMPFiles/source/PluginHandler/PluginManager.cpp
index af60388..3802c9b 100644
--- a/XMPFiles/source/PluginHandler/PluginManager.cpp
+++ b/XMPFiles/source/PluginHandler/PluginManager.cpp
@@ -57,7 +57,7 @@ static XMPFileHandler* Plugin_MetaHandlerCTor ( FileHandlerSharedPtr handler, XM
XMP_Throw ( "Plugin not loaded", kXMPErr_InternalFailure );
}
- handler->getModule()->getPluginAPIs()->mInitializeSessionProc ( handler->getUID().c_str(), parent->filePath.c_str(), (XMP_Uns32)parent->format, (XMP_Uns32)handler->getHandlerFlags(), (XMP_Uns32)parent->openFlags, &object, &error );
+ 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 );
@@ -87,12 +87,12 @@ static bool Plugin_CheckFileFormat ( FileHandlerSharedPtr handler, XMP_StringPtr
// call into plugin if owning handler or if manifest has no CheckFormat entry
if ( fileRef == 0 || handler->getCheckFormatSize() == 0) {
- bool ok;
+ XMP_Bool ok;
WXMP_Error error;
CheckSessionFileFormatProc checkProc = handler->getModule()->getPluginAPIs()->mCheckFileFormatProc;
checkProc ( handler->getUID().c_str(), filePath, fileRef, &ok, &error );
CheckError ( error );
- return ok;
+ return ConvertXMP_BoolToBool( ok );
} else {
@@ -123,10 +123,11 @@ static bool Plugin_CheckFileFormat ( FileHandlerSharedPtr handler, XMP_StringPtr
// Check if byteSeq is hexadecimal byte sequence, e.g 0x03045100
- bool isHex = ( (checkFormat.mLength > 2) &&
+ bool isHex = ( (checkFormat.mLength > 0 ) &&
+ (checkFormat.mByteSeq.size() == (2 + 2*checkFormat.mLength) &&
(checkFormat.mByteSeq[0] == '0') &&
- (checkFormat.mByteSeq[1] == 'x') &&
- (checkFormat.mByteSeq.size() == (2 + 2*checkFormat.mLength)) );
+ (checkFormat.mByteSeq[1] == 'x') )
+ );
if ( ! isHex ) {
@@ -184,7 +185,7 @@ static bool Plugin_CheckFolderFormat( FileHandlerSharedPtr handler,
const std::string & leafName,
XMPFiles * parent )
{
- bool result = false;
+ XMP_Bool result = false;
if ( handler != 0 )
{
@@ -194,7 +195,7 @@ static bool Plugin_CheckFolderFormat( FileHandlerSharedPtr handler,
CheckError( error );
}
- return result;
+ return ConvertXMP_BoolToBool( result );
}
@@ -226,7 +227,8 @@ static bool Plugin_CheckFolderFormat_Replacement( XMP_FileFormat format,
PluginManager* PluginManager::msPluginManager = 0;
-PluginManager::PluginManager( const std::string& pluginDir, const std::string& plugins ) : mPluginDir ( pluginDir )
+PluginManager::PluginManager( const std::string& pluginDir, const std::string& plugins )
+: mPluginDir ( pluginDir )
{
const std::size_t count = sizeof(kLibraryExtensions) / sizeof(kLibraryExtensions[0]);
@@ -303,10 +305,9 @@ PluginManager::~PluginManager()
mPluginDir.clear();
mExtensions.clear();
mPluginsNeeded.clear();
- mModules.clear();
mHandlers.clear();
mSessions.clear();
-
+
terminateHostAPI();
}
@@ -322,6 +323,18 @@ static bool registerHandler( XMP_FileFormat format, FileHandlerSharedPtr handler
CheckFolderFormatProc chkFolderFormat = NULL;
XMPFileHandlerCTor hdlCtor = NULL;
+ if ( handler->getHandlerFlags() & kXMPFiles_NeedsPreloading )
+ {
+ try
+ {
+ handler->load();
+ }
+ catch ( ... )
+ {
+ return false;
+ }
+ }
+
if( handler->getOverwriteHandler() )
{
//
@@ -374,6 +387,7 @@ void PluginManager::initialize( const std::string& pluginDir, const std::string&
{
HandlerRegistry & hdlrReg = HandlerRegistry::getInstance();
if( msPluginManager == 0 ) msPluginManager = new PluginManager( pluginDir, plugins );
+ msPluginManager->initializeHostAPI();
msPluginManager->doScan( 2 );
@@ -430,16 +444,31 @@ void PluginManager::addFileHandler( XMP_FileFormat format, FileHandlerSharedPtr
}
//
+ //
// if there is already a standard handler or a replacement handler for the file format
- // then just ignore it. The first one wins.
+ // then use the one with the highest version. If both versions are the same the first one wins.
//
- if( handler->getOverwriteHandler() )
+ FileHandlerSharedPtr& existingHandler =
+ handler->getOverwriteHandler() ? handlerMap[format].mReplacementHandler : handlerMap[format].mStandardHandler;
+
+ if( ! existingHandler )
{
- if( handlerMap[format].mReplacementHandler.get() == NULL ) handlerMap[format].mReplacementHandler = handler;
+ existingHandler = handler;
}
else
{
- if( handlerMap[format].mStandardHandler.get() == NULL ) handlerMap[format].mStandardHandler = handler;
+ if( existingHandler->getUID() == handler->getUID() )
+ {
+ if( existingHandler->getVersion() < handler->getVersion() )
+ {
+ existingHandler = handler; // replace older handler
+ }
+ }
+ else
+ {
+ // TODO: notify client that two plugin handlers try to handle the same file format
+ // -> need access to the global notification handler
+ }
}
}
}
@@ -469,13 +498,13 @@ FileHandlerSharedPtr PluginManager::getFileHandler( XMP_FileFormat format, Handl
}
// =================================================================================================
-
-static XMP_ReadWriteLock sPluginManagerRWLock;
+
+static XMP_ReadWriteLock sSessionMapPluginManagerRWLock;
void PluginManager::addHandlerInstance( SessionRef session, FileHandlerInstancePtr handler )
{
if ( msPluginManager != 0 ) {
- XMP_AutoLock(&sPluginManagerRWLock, kXMP_WriteLock);
+ XMP_AutoLock lock(&sSessionMapPluginManagerRWLock, kXMP_WriteLock);
SessionMap & sessionMap = msPluginManager->mSessions;
if ( sessionMap.find(session) == sessionMap.end() ) {
sessionMap[session] = handler;
@@ -488,7 +517,7 @@ void PluginManager::addHandlerInstance( SessionRef session, FileHandlerInstanceP
void PluginManager::removeHandlerInstance( SessionRef session )
{
if ( msPluginManager != 0 ) {
- XMP_AutoLock(&sPluginManagerRWLock, kXMP_WriteLock);
+ XMP_AutoLock lock(&sSessionMapPluginManagerRWLock, kXMP_WriteLock);
SessionMap & sessionMap = msPluginManager->mSessions;
sessionMap.erase ( session );
}
@@ -500,7 +529,7 @@ FileHandlerInstancePtr PluginManager::getHandlerInstance( SessionRef session )
{
FileHandlerInstancePtr ret = 0;
if ( msPluginManager != 0 ) {
- XMP_AutoLock(&sPluginManagerRWLock, kXMP_ReadLock);
+ XMP_AutoLock lock(&sSessionMapPluginManagerRWLock, kXMP_ReadLock);
ret = msPluginManager->mSessions[session];
}
return ret;
@@ -721,28 +750,6 @@ HostAPIRef PluginManager::getHostAPI( XMP_Uns32 version )
{
hostAPI = iter->second;
}
- else
- {
- hostAPI = new HostAPI();
- hostAPI->mSize = sizeof( HostAPI );
- hostAPI->mVersion = version;
-
- switch( version )
- {
- case 1: SetupHostAPI_V1( hostAPI ); break;
-
- default:
- {
- delete hostAPI;
- hostAPI = NULL;
- }
- }
-
- if( hostAPI != NULL )
- {
- msPluginManager->mHostAPIs[ version ] = hostAPI;
- }
- }
return hostAPI;
}
@@ -758,7 +765,10 @@ void PluginManager::terminateHostAPI()
switch( version )
{
- case 1:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
{
delete hostAPI->mFileIOAPI;
delete hostAPI->mStrAPI;
@@ -768,7 +778,7 @@ void PluginManager::terminateHostAPI()
}
break;
- default:
+ default:
{
delete hostAPI;
}
@@ -776,4 +786,42 @@ void PluginManager::terminateHostAPI()
}
}
+void PluginManager::initializeHostAPI()
+{
+ HostAPIRef hostAPI = NULL;
+
+
+ for ( int i = 0; i < XMP_HOST_API_VERSION_4; i++ )
+ {
+ hostAPI = new HostAPI();
+ hostAPI->mSize = sizeof( HostAPI );
+ hostAPI->mVersion = i + 1;
+
+ switch( hostAPI->mVersion )
+ {
+ case 1:
+ SetupHostAPI_V1( hostAPI );
+ break;
+
+ case 2:
+ SetupHostAPI_V2( hostAPI );
+ break;
+
+ case 3:
+ SetupHostAPI_V3( hostAPI );
+ break;
+
+ default:
+ case 4:
+ SetupHostAPI_V4( hostAPI );
+ break;
+ }
+
+ if( hostAPI != NULL )
+ {
+ msPluginManager->mHostAPIs[ hostAPI->mVersion ] = hostAPI;
+ }
+ }
+}
+
} // namespace XMP_PLUGIN
diff --git a/XMPFiles/source/PluginHandler/PluginManager.h b/XMPFiles/source/PluginHandler/PluginManager.h
index 9b2240e..65165b7 100644
--- a/XMPFiles/source/PluginHandler/PluginManager.h
+++ b/XMPFiles/source/PluginHandler/PluginManager.h
@@ -12,17 +12,23 @@
#include "PluginHandler.h"
#include "ModuleUtils.h"
+#if XMP_WinBuild && _MSC_VER >= 1700
+ // Visual Studio 2012 or newer supports C++11 (mostly)
+ #include <memory>
+ #include <functional>
+ #define XMP_SHARED_PTR std::shared_ptr
+#else
+ #define XMP_SHARED_PTR std::tr1::shared_ptr
+#endif
+
namespace XMP_PLUGIN
{
typedef XMP_Uns32 XMPAtom;
typedef XMPAtom FileHandlerType;
-class Module;
-typedef std::tr1::shared_ptr<Module> ModuleSharedPtr;
-
-class FileHandler;
-typedef std::tr1::shared_ptr<FileHandler> FileHandlerSharedPtr;
+typedef XMP_SHARED_PTR<class Module> ModuleSharedPtr;
+typedef XMP_SHARED_PTR<class FileHandler> FileHandlerSharedPtr;
class FileHandlerInstance;
typedef FileHandlerInstance* FileHandlerInstancePtr;
@@ -37,8 +43,8 @@ inline void CheckError( WXMP_Error & error )
{
if( error.mErrorID != kXMPErr_NoError )
{
- if( error.mErrorID >= 500 /* kXMPErr_PluginInternal */
- || error.mErrorID <= 512 /* kXMPErr_SetHostAPI */ )
+ if( error.mErrorID >= kXMPErr_PluginInternal &&
+ error.mErrorID <= kXMPErr_PluginLastError )
{
throw XMP_Error( kXMPErr_InternalFailure, error.mErrorMsg );
}
@@ -52,7 +58,7 @@ inline void CheckError( WXMP_Error & error )
/** @class PluginManager
* @brief Register all file handlers from all the plugins available in Plugin directory.
*
- * At the initialization time of XMPFiles, PluginManager loads all the avalbale plugins
+ * At the initialization time of XMPFiles, PluginManager loads all the available plugins
*/
class PluginManager
{
@@ -136,6 +142,7 @@ private:
/**
* Terminate host API
*/
+ void initializeHostAPI();
void terminateHostAPI();
/**
@@ -143,7 +150,7 @@ private:
* @param module
* @return Void.
*/
- void loadResourceFile( ModuleSharedPtr module );
+ void loadResourceFile( ModuleSharedPtr module );
/**
* Scan mPluginDir for the plugins. It also scans nested folder upto level inMaxNumOfNestedFolder.
@@ -164,21 +171,36 @@ private:
const std::string& inPath,
std::vector<std::string>& ioFoundLibs,
XMP_Int32 inLevel,
- XMP_Int32 inMaxNestingLevel );
+ XMP_Int32 inMaxNestingLevel );
/**
* Setup passed in HostAPI structure for the host API v1
*/
static void SetupHostAPI_V1( HostAPIRef hostAPI );
+ /**
+ * Setup passed in HostAPI structure for the host API v2
+ */
+ static void SetupHostAPI_V2( HostAPIRef hostAPI );
+
+ /**
+ * Setup passed in HostAPI structure for the host API v3
+ */
+ static void SetupHostAPI_V3( HostAPIRef hostAPI );
+
+ /**
+ * Setup passed in HostAPI structure for the host API v4
+ */
+ static void SetupHostAPI_V4( HostAPIRef hostAPI );
+
+
typedef std::map<XMP_FileFormat, FileHandlerPair> PluginHandlerMap;
typedef std::map<XMP_Uns32, HostAPIRef> HostAPIMap;
- typedef std::map<SessionRef, FileHandlerInstancePtr> SessionMap;
+ typedef std::map<SessionRef, FileHandlerInstancePtr> SessionMap;
std::string mPluginDir;
StringVec mExtensions;
StringVec mPluginsNeeded;
- ModuleVec mModules;
PluginHandlerMap mHandlers;
SessionMap mSessions;
HostAPIMap mHostAPIs;
diff --git a/XMPFiles/source/PluginHandler/XMPAtoms.cpp b/XMPFiles/source/PluginHandler/XMPAtoms.cpp
index ed01027..8379c8b 100644
--- a/XMPFiles/source/PluginHandler/XMPAtoms.cpp
+++ b/XMPFiles/source/PluginHandler/XMPAtoms.cpp
@@ -60,6 +60,7 @@ const XMPAtomMapping kXMPAtomVec[] =
{ "kXMPFiles_NeedsReadOnlyPacket", kXMPFiles_NeedsReadOnlyPacket_K },
{ "kXMPFiles_UsesSidecarXMP", kXMPFiles_UsesSidecarXMP_K },
{ "kXMPFiles_FolderBasedFormat", kXMPFiles_FolderBasedFormat_K },
+ { "kXMPFiles_NeedsPreloading", kXMPFiles_NeedsPreloading_K },
// Serialize option
{ "kXMP_OmitPacketWrapper", kXMP_OmitPacketWrapper_K },
@@ -88,7 +89,8 @@ void ResourceParser::clear()
mFormatIDs.clear();
mCheckFormat.clear();
mHandler = FileHandlerSharedPtr();
- mFlags = mSerializeOption = mType = mVersion = 0;
+ mFlags = mSerializeOption = mType = 0;
+ mVersion = 0.0;
}
void ResourceParser::addHandler()
@@ -103,7 +105,10 @@ void ResourceParser::addHandler()
mHandler->setHandlerType( mType );
mHandler->setSerializeOption( mSerializeOption );
mHandler->setOverwriteHandler( mOverwriteHandler );
- if( mVersion != 0) mHandler->setVersion( mVersion );
+ if( mVersion != 0.0)
+ {
+ mHandler->setVersion( mVersion );
+ }
// A plugin could define the XMP_FileFormat value in manifest file through keyword "FormatID" and
// file extensions for NormalHandler and OwningHandler through keyword "Extension".
@@ -119,8 +124,6 @@ void ResourceParser::addHandler()
bool ResourceParser::parseElementAttrs( const XML_Node * xmlNode, bool isTopLevel )
{
- XMP_OptionBits exclusiveAttrs = 0; // Used to detect attributes that are mutually exclusive.
-
XMPAtom nodeAtom = getXMPAtomFromString( xmlNode->name );
if( nodeAtom == Handler_K )
this->clear();
@@ -144,7 +147,7 @@ bool ResourceParser::parseElementAttrs( const XML_Node * xmlNode, bool isTopLeve
mHandler = FileHandlerSharedPtr( new FileHandler( this->mUID, 0, 0, mModule ) );
break;
case Version_K:
- this->mVersion = atoi( (*currAttr)->value.c_str() );
+ sscanf( (*currAttr)->value.c_str(), "%lf", &this->mVersion );
break;
case HandlerType_K:
this->mType = getXMPAtomFromString( (*currAttr)->value );
@@ -153,7 +156,9 @@ bool ResourceParser::parseElementAttrs( const XML_Node * xmlNode, bool isTopLeve
this->mOverwriteHandler = ( (*currAttr)->value == "true" );
break;
default:
- XMP_Throw( "Invalid Attr in Handler encountered in resource file", kXMPErr_Unavailable );
+ //fix for bug 3565147: Plugin Architecture: If any unknown node is present in the Plugin Manifest then the Plugin is not loaded
+ //XMP_Throw( "Invalid Attr in Handler encountered in resource file", kXMPErr_Unavailable );
+ break;
}
break;
case CheckFormat_K:
@@ -169,7 +174,9 @@ bool ResourceParser::parseElementAttrs( const XML_Node * xmlNode, bool isTopLeve
this->mCheckFormat.mByteSeq = (*currAttr)->value;
break;
default:
- XMP_Throw( "Invalid Attr in CheckFormat encountered in resource file", kXMPErr_Unavailable );
+ //fix for bug 3565147: Plugin Architecture: If any unknown node is present in the Plugin Manifest then the Plugin is not loaded
+ //XMP_Throw( "Invalid Attr in CheckFormat encountered in resource file", kXMPErr_Unavailable );
+ break;
}
break;
case Extension_K:
@@ -179,7 +186,9 @@ bool ResourceParser::parseElementAttrs( const XML_Node * xmlNode, bool isTopLeve
this->mFileExtensions.insert( HandlerRegistry::getInstance().getFileFormat( (*currAttr)->value, true) );
break;
default:
- XMP_Throw( "Invalid Attr in Extension encountered in resource file", kXMPErr_Unavailable );
+ //fix for bug 3565147: Plugin Architecture: If any unknown node is present in the Plugin Manifest then the Plugin is not loaded
+ //XMP_Throw( "Invalid Attr in Extension encountered in resource file", kXMPErr_Unavailable );
+ break;
}
break;
case FormatID_K:
@@ -196,7 +205,9 @@ bool ResourceParser::parseElementAttrs( const XML_Node * xmlNode, bool isTopLeve
this->mFormatIDs.insert( formatID );
break;
default:
- XMP_Throw( "Invalid Attr in FormatID encountered in resource file", kXMPErr_Unavailable );
+ //fix for bug 3565147: Plugin Architecture: If any unknown node is present in the Plugin Manifest then the Plugin is not loaded
+ //XMP_Throw( "Invalid Attr in FormatID encountered in resource file", kXMPErr_Unavailable );
+ break;
}
break;
case HandlerFlag_K:
@@ -204,12 +215,15 @@ bool ResourceParser::parseElementAttrs( const XML_Node * xmlNode, bool isTopLeve
{
case Name_K:
oneflag = getHandlerFlag( (*currAttr)->value );
- if( 0 == oneflag )
- XMP_Throw( "Invalid handler flag found in resource file...", kXMPErr_Unavailable );
+ //fix for bug 3565147: Plugin Architecture: If any unknown node is present in the Plugin Manifest then the Plugin is not loaded
+ //if( 0 == oneflag )
+ // XMP_Throw( "Invalid handler flag found in resource file...", kXMPErr_Unavailable );
this->mFlags |= oneflag;
break;
default:
- XMP_Throw( "Invalid handler flag found in resource file...", kXMPErr_Unavailable );
+ //fix for bug 3565147: Plugin Architecture: If any unknown node is present in the Plugin Manifest then the Plugin is not loaded
+ //XMP_Throw( "Invalid handler flag found in resource file...", kXMPErr_Unavailable );
+ break;
}
break;
case SerializeOption_K:
@@ -217,12 +231,15 @@ bool ResourceParser::parseElementAttrs( const XML_Node * xmlNode, bool isTopLeve
{
case Name_K:
oneflag = getSerializeOption( (*currAttr)->value );
- if( 0 == oneflag )
- XMP_Throw( "Invalid serialize option found in resource file...", kXMPErr_Unavailable );
+ //fix for bug 3565147: Plugin Architecture: If any unknown node is present in the Plugin Manifest then the Plugin is not loaded
+ //if( 0 == oneflag )
+ // XMP_Throw( "Invalid serialize option found in resource file...", kXMPErr_Unavailable );
this->mSerializeOption |= oneflag;
break;
default:
- XMP_Throw( "Invalid handler flag found in resource file...", kXMPErr_Unavailable );
+ //fix for bug 3565147: Plugin Architecture: If any unknown node is present in the Plugin Manifest then the Plugin is not loaded
+ //XMP_Throw( "Invalid handler flag found in resource file...", kXMPErr_Unavailable );
+ break;
}
break;
default:
@@ -330,8 +347,11 @@ XMP_OptionBits ResourceParser::getHandlerFlag( const std::string & stringAtom )
return kXMPFiles_UsesSidecarXMP;
case kXMPFiles_FolderBasedFormat_K:
return kXMPFiles_FolderBasedFormat;
+ case kXMPFiles_NeedsPreloading_K:
+ return kXMPFiles_NeedsPreloading;
default:
- XMP_Throw( "Invalid PluginhandlerFlag ...", kXMPErr_Unavailable );
+ //fix for bug 3565147: Plugin Architecture: If any unknown node is present in the Plugin Manifest then the Plugin is not loaded
+ //XMP_Throw( "Invalid PluginhandlerFlag ...", kXMPErr_Unavailable );
return 0;
}
}
@@ -375,7 +395,8 @@ XMP_OptionBits ResourceParser::getSerializeOption( const std::string & stringAto
case kXMP_EncodeUTF32Little_K:
return kXMP_EncodeUTF32Little;
default:
- XMP_Throw( "Invalid Serialize Option ...", kXMPErr_Unavailable );
+ //fix for bug 3565147: Plugin Architecture: If any unknown node is present in the Plugin Manifest then the Plugin is not loaded
+ //XMP_Throw( "Invalid Serialize Option ...", kXMPErr_Unavailable );
return 0;
}
}
diff --git a/XMPFiles/source/PluginHandler/XMPAtoms.h b/XMPFiles/source/PluginHandler/XMPAtoms.h
index dab0750..3daad38 100644
--- a/XMPFiles/source/PluginHandler/XMPAtoms.h
+++ b/XMPFiles/source/PluginHandler/XMPAtoms.h
@@ -59,6 +59,7 @@ enum
kXMPFiles_NeedsReadOnlyPacket_K,
kXMPFiles_UsesSidecarXMP_K,
kXMPFiles_FolderBasedFormat_K,
+ kXMPFiles_NeedsPreloading_K,
// Serialize option
kXMP_OmitPacketWrapper_K,
@@ -99,7 +100,7 @@ class ResourceParser
{
public:
ResourceParser(ModuleSharedPtr module)
- : mModule(module), mType(0), mFlags(0), mSerializeOption(0), mVersion(0), mOverwriteHandler(false) {}
+ : mModule(module), mType(0), mFlags(0), mSerializeOption(0), mVersion(0.0), mOverwriteHandler(false) {}
/**
* Initialize the XMPAtoms which will be used in parsing resource files.
@@ -186,7 +187,7 @@ private:
FileHandlerType mType;
XMP_OptionBits mFlags;
XMP_OptionBits mSerializeOption;
- XMP_Uns32 mVersion;
+ double mVersion;
bool mOverwriteHandler;
CheckFormat mCheckFormat;
std::set<XMP_FileFormat> mFileExtensions;
diff --git a/XMPFiles/source/WXMPFiles.cpp b/XMPFiles/source/WXMPFiles.cpp
index f0f7b52..26fdcac 100644
--- a/XMPFiles/source/WXMPFiles.cpp
+++ b/XMPFiles/source/WXMPFiles.cpp
@@ -12,6 +12,8 @@
#include "public/include/client-glue/WXMPFiles.hpp"
+#include "source/XMP_ProgressTracker.hpp"
+
#include "XMPFiles/source/XMPFiles_Impl.hpp"
#include "XMPFiles/source/XMPFiles.hpp"
@@ -125,7 +127,7 @@ void WXMPFiles_DecrementRefCount_1 ( XMPFilesRef xmpObjRef )
XMP_EXIT_NoThrow
}
-// =================================================================================================
+// -------------------------------------------------------------------------------------------------
void WXMPFiles_GetFormatInfo_1 ( XMP_FileFormat format,
XMP_OptionBits * flags,
@@ -138,7 +140,7 @@ void WXMPFiles_GetFormatInfo_1 ( XMP_FileFormat format,
XMP_EXIT
}
-// =================================================================================================
+// -------------------------------------------------------------------------------------------------
void WXMPFiles_CheckFileFormat_1 ( XMP_StringPtr filePath,
WXMP_Result * wResult )
@@ -150,7 +152,7 @@ void WXMPFiles_CheckFileFormat_1 ( XMP_StringPtr filePath,
XMP_EXIT
}
-// =================================================================================================
+// -------------------------------------------------------------------------------------------------
void WXMPFiles_CheckPackageFormat_1 ( XMP_StringPtr folderPath,
WXMP_Result * wResult )
@@ -162,7 +164,7 @@ void WXMPFiles_CheckPackageFormat_1 ( XMP_StringPtr folderPath,
XMP_EXIT
}
-// =================================================================================================
+// -------------------------------------------------------------------------------------------------
void WXMPFiles_GetFileModDate_1 ( XMP_StringPtr filePath,
XMP_DateTime * modDate,
@@ -177,6 +179,47 @@ void WXMPFiles_GetFileModDate_1 ( XMP_StringPtr filePath,
XMP_EXIT
}
+// -------------------------------------------------------------------------------------------------
+
+void WXMPFiles_GetAssociatedResources_1 ( XMP_StringPtr filePath,
+ void * resourceList,
+ XMP_FileFormat format,
+ XMP_OptionBits options,
+ SetClientStringVectorProc SetClientStringVector,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_Static ( "WXMPFiles_GetAssociatedResources_1" )
+
+ if ( resourceList == 0 ) XMP_Throw ( "An result resource list vector must be provided", kXMPErr_BadParam );
+
+ std::vector<std::string> resList; // Pass a local vector, not the client's.
+ (*SetClientStringVector) ( resourceList, 0, 0 ); // Clear the client's result vector.
+ wResult->int32Result = XMPFiles::GetAssociatedResources ( filePath, &resList, format, options );
+
+ if ( wResult->int32Result && (! resList.empty()) ) {
+ const size_t fileCount = resList.size();
+ 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 );
+ }
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void WXMPFiles_IsMetadataWritable_1 ( XMP_StringPtr filePath,
+ XMP_Bool * writable,
+ XMP_FileFormat format,
+ XMP_OptionBits options,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_Static ( "WXMPFiles_IsMetadataWritable_1" )
+ wResult->int32Result = XMPFiles::IsMetadataWritable ( filePath, writable, format, options );
+ XMP_EXIT
+}
+
+
// =================================================================================================
void WXMPFiles_OpenFile_1 ( XMPFilesRef xmpObjRef,
@@ -340,6 +383,85 @@ void WXMPFiles_CanPutXMP_1 ( XMPFilesRef xmpObjRef,
// =================================================================================================
+void WXMPFiles_SetDefaultProgressCallback_1 ( XMP_ProgressReportWrapper wrapperProc,
+ XMP_ProgressReportProc clientProc,
+ void * context,
+ float interval,
+ XMP_Bool sendStartStop,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_Static ( "WXMPFiles_SetDefaultProgressCallback_1" )
+
+ XMP_ProgressTracker::CallbackInfo cbInfo ( wrapperProc, clientProc, context, interval, ConvertXMP_BoolToBool( sendStartStop ) );
+ XMPFiles::SetDefaultProgressCallback ( cbInfo );
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void WXMPFiles_SetProgressCallback_1 ( XMPFilesRef xmpObjRef,
+ XMP_ProgressReportWrapper wrapperProc,
+ XMP_ProgressReportProc clientProc,
+ void * context,
+ float interval,
+ XMP_Bool sendStartStop,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_ObjWrite ( XMPFiles, "WXMPFiles_SetProgressCallback_1" )
+
+ XMP_ProgressTracker::CallbackInfo cbInfo ( wrapperProc, clientProc, context, interval, ConvertXMP_BoolToBool( sendStartStop) );
+ thiz->SetProgressCallback ( cbInfo );
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void WXMPFiles_SetDefaultErrorCallback_1 ( XMPFiles_ErrorCallbackWrapper wrapperProc,
+ XMPFiles_ErrorCallbackProc clientProc,
+ void * context,
+ XMP_Uns32 limit,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_Static ( "WXMPFiles_SetDefaultErrorCallback_1" )
+
+ XMPFiles::SetDefaultErrorCallback ( wrapperProc, clientProc, context, limit );
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void WXMPFiles_SetErrorCallback_1 ( XMPFilesRef xmpObjRef,
+ XMPFiles_ErrorCallbackWrapper wrapperProc,
+ XMPFiles_ErrorCallbackProc clientProc,
+ void * context,
+ XMP_Uns32 limit,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_ObjWrite ( XMPFiles, "WXMPFiles_SetErrorCallback_1" )
+
+ thiz->SetErrorCallback ( wrapperProc, clientProc, context, limit );
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void WXMPFiles_ResetErrorCallbackLimit_1 ( XMPFilesRef xmpObjRef,
+ XMP_Uns32 limit,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_ObjWrite ( XMPFiles, "WXMPFiles_ResetErrorCallbackLimit_1" )
+
+ thiz->ResetErrorCallbackLimit ( limit );
+
+ XMP_EXIT
+}
+
+// =================================================================================================
+
#if __cplusplus
}
#endif
diff --git a/XMPFiles/source/XMPFiles.cpp b/XMPFiles/source/XMPFiles.cpp
index b720725..32ea41f 100644
--- a/XMPFiles/source/XMPFiles.cpp
+++ b/XMPFiles/source/XMPFiles.cpp
@@ -20,7 +20,10 @@
#include "XMPFiles/source/XMPFiles_Impl.hpp"
#include "XMPFiles/source/HandlerRegistry.h"
-#include "XMPFiles/source/PluginHandler/PluginManager.h"
+
+#if EnablePluginManager
+ #include "XMPFiles/source/PluginHandler/PluginManager.h"
+#endif
#include "XMPFiles/source/FormatSupport/ID3_Support.hpp"
@@ -37,12 +40,19 @@
// =================================================================================================
using namespace Common;
-using namespace XMP_PLUGIN;
+
+#if EnablePluginManager
+ using namespace XMP_PLUGIN;
+#endif
// =================================================================================================
XMP_Int32 sXMPFilesInitCount = 0;
+static XMP_ProgressTracker::CallbackInfo sProgressDefault;
+static XMPFiles::ErrorCallbackInfo sDefaultErrorCallback;
+
+
#if GatherPerformanceData
APIPerfCollection* sAPIPerf = 0;
#endif
@@ -65,6 +75,17 @@ XMP_Int32 sXMPFilesInitCount = 0;
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_NOTIFY_ERROR(errorCallbackPtr, filePath, severity, error) \
+ if ( (errorCallbackPtr) != NULL ) (errorCallbackPtr)->NotifyClient ( (severity), (error), (filePath) );
+
+
// =================================================================================================
#if EnablePacketScanning
@@ -78,7 +99,7 @@ const char * kXMPFiles_EmbeddedCopyright = kXMPFilesName " " kXMP_CopyrightStr;
void
XMPFiles::GetVersionInfo ( XMP_VersionInfo * info )
{
-
+ XMP_FILES_STATIC_START
memset ( info, 0, sizeof(XMP_VersionInfo) );
info->major = XMPFILES_API_VERSION_MAJOR;
@@ -87,7 +108,7 @@ XMPFiles::GetVersionInfo ( XMP_VersionInfo * info )
info->isDebug = kXMPFiles_DebugFlag;
info->flags = 0; // ! None defined yet.
info->message = kXMPFiles_VersionMessage;
-
+ XMP_FILES_STATIC_END1 ( kXMPErrSev_OperationFatal )
} // XMPFiles::GetVersionInfo
// =================================================================================================
@@ -104,6 +125,7 @@ XMPFiles::GetVersionInfo ( XMP_VersionInfo * info )
bool
XMPFiles::Initialize( XMP_OptionBits options, const char* pluginFolder, const char* plugins /* = NULL */ )
{
+ XMP_FILES_STATIC_START
++sXMPFilesInitCount;
if ( sXMPFilesInitCount > 1 ) return true;
@@ -145,11 +167,13 @@ XMPFiles::Initialize( XMP_OptionBits options, const char* pluginFolder, const ch
if ( ! ignoreLocalText ) XMP_Throw ( "Generic UNIX clients must pass kXMPFiles_IgnoreLocalText", kXMPErr_EnforceFailure );
#endif
- if ( pluginFolder != 0 ) {
- std::string pluginList;
- if ( plugins != 0 ) pluginList.assign ( plugins );
- PluginManager::initialize ( pluginFolder, pluginList ); // Load file handler plugins.
- }
+ #if EnablePluginManager
+ if ( pluginFolder != 0 ) {
+ std::string pluginList;
+ if ( plugins != 0 ) pluginList.assign ( plugins );
+ PluginManager::initialize ( pluginFolder, pluginList ); // Load file handler plugins.
+ }
+ #endif
// Make sure the embedded info strings are referenced and kept.
if ( (kXMPFiles_EmbeddedVersion[0] == 0) || (kXMPFiles_EmbeddedCopyright[0] == 0) ) return false;
@@ -162,7 +186,8 @@ XMPFiles::Initialize( XMP_OptionBits options, const char* pluginFolder, const ch
XMP_Assert ( sizeof(XMP_Uns16) == 2 );
XMP_Assert ( sizeof(XMP_Uns32) == 4 );
XMP_Assert ( sizeof(XMP_Uns64) == 8 );
-
+ XMP_Assert ( sizeof(XMP_Bool) == 1 );
+ XMP_FILES_STATIC_END1 ( kXMPErrSev_ProcessFatal )
return true;
} // XMPFiles::Initialize
@@ -248,6 +273,7 @@ static void ReportPerformanceData()
void
XMPFiles::Terminate()
{
+ XMP_FILES_STATIC_START
--sXMPFilesInitCount;
if ( sXMPFilesInitCount != 0 ) return; // Not ready to terminate, or already terminated.
@@ -256,7 +282,10 @@ XMPFiles::Terminate()
EliminateGlobal ( sAPIPerf );
#endif
- PluginManager::terminate();
+ #if EnablePluginManager
+ PluginManager::terminate();
+ #endif
+
HandlerRegistry::terminate();
SXMPMeta::Terminate(); // Just in case the client does not.
@@ -273,23 +302,36 @@ XMPFiles::Terminate()
xmpFilesLog = stderr;
#endif
+ XMP_FILES_STATIC_END1 ( kXMPErrSev_ProcessFatal )
} // XMPFiles::Terminate
// =================================================================================================
-XMPFiles::XMPFiles() :
- clientRefs(0),
- format(kXMP_UnknownFile),
- ioRef(0),
- openFlags(0),
- handler(0),
- tempPtr(0),
- tempUI32(0),
- abortProc(0),
- abortArg(0)
+XMPFiles::XMPFiles()
+ : clientRefs(0)
+ , format(kXMP_UnknownFile)
+ , ioRef(0)
+ , openFlags(0)
+ , handler(0)
+ , tempPtr(0)
+ , tempUI32(0)
+ , abortProc(0)
+ , abortArg(0)
+ , progressTracker(0)
{
- // Nothing more to do, clientRefs is incremented in wrapper.
+ XMP_FILES_START
+ if ( sProgressDefault.clientProc != 0 ) {
+ this->progressTracker = new XMP_ProgressTracker ( sProgressDefault );
+ if (this->progressTracker == 0) XMP_Throw ( "XMPFiles: Unable to allocate memory for Progress Tracker", kXMPErr_NoMemory );
+ }
+ if ( sDefaultErrorCallback.clientProc != 0 ) {
+ this->errorCallback.wrapperProc = sDefaultErrorCallback.wrapperProc;
+ this->errorCallback.clientProc = sDefaultErrorCallback.clientProc;
+ this->errorCallback.context = sDefaultErrorCallback.context;
+ this->errorCallback.limit = sDefaultErrorCallback.limit;
+ }
+ XMP_FILES_END1 ( kXMPErrSev_OperationFatal );
} // XMPFiles::XMPFiles
// =================================================================================================
@@ -314,6 +356,7 @@ static inline void CloseLocalFile ( XMPFiles* thiz )
XMPFiles::~XMPFiles()
{
+ XMP_FILES_START
XMP_Assert ( this->clientRefs <= 0 );
if ( this->handler != 0 ) {
@@ -323,7 +366,9 @@ XMPFiles::~XMPFiles()
CloseLocalFile ( this );
+ if ( this->progressTracker != 0 ) delete this->progressTracker;
if ( this->tempPtr != 0 ) free ( this->tempPtr ); // ! Must have been malloc-ed!
+ XMP_FILES_END1 ( kXMPErrSev_OperationFatal )
} // XMPFiles::~XMPFiles
@@ -334,8 +379,10 @@ bool
XMPFiles::GetFormatInfo ( XMP_FileFormat format,
XMP_OptionBits * flags /* = 0 */ )
{
-
+ XMP_FILES_STATIC_START
return HandlerRegistry::getInstance().getFormatInfo ( format, flags );
+ XMP_FILES_STATIC_END1 ( kXMPErrSev_OperationFatal )
+ return false;
} // XMPFiles::GetFormatInfo
@@ -345,14 +392,23 @@ XMPFiles::GetFormatInfo ( XMP_FileFormat format,
XMP_FileFormat
XMPFiles::CheckFileFormat ( XMP_StringPtr clientPath )
{
+ XMP_FILES_STATIC_START
if ( (clientPath == 0) || (*clientPath == 0) ) return kXMP_UnknownFile;
XMPFiles bogus; // Needed to provide context to SelectSmartHandler.
- bogus.filePath = clientPath; // So that XMPFiles destructor cleans up the XMPFiles_IO object.
- XMPFileHandlerInfo * handlerInfo = HandlerRegistry::getInstance().selectSmartHandler( &bogus, clientPath, kXMP_UnknownFile, kXMPFiles_OpenForRead );
+ bogus.SetFilePath ( clientPath ); // So that XMPFiles destructor cleans up the XMPFiles_IO object.
+ XMPFileHandlerInfo * handlerInfo = HandlerRegistry::getInstance().selectSmartHandler(&bogus, clientPath, kXMP_UnknownFile, kXMPFiles_OpenForRead);
- if ( handlerInfo == 0 ) return kXMP_UnknownFile;
+ if ( handlerInfo == 0 ) {
+ if ( !Host_IO::Exists ( clientPath ) ) {
+ XMP_Error error ( kXMPErr_NoFile, "XMPFiles: file does not exist" );
+ XMP_FILES_STATIC_NOTIFY_ERROR ( &sDefaultErrorCallback, clientPath, kXMPErrSev_Recoverable, error );
+ }
+ return kXMP_UnknownFile;
+ }
return handlerInfo->format;
+ XMP_FILES_STATIC_END2 ( clientPath, kXMPErrSev_OperationFatal )
+ return kXMP_UnknownFile;
} // XMPFiles::CheckFileFormat
@@ -362,6 +418,7 @@ XMPFiles::CheckFileFormat ( XMP_StringPtr clientPath )
XMP_FileFormat
XMPFiles::CheckPackageFormat ( XMP_StringPtr folderPath )
{
+ XMP_FILES_STATIC_START
// This is called with a path to a folder, and checks to see if that folder is the top level of
// a "package" that should be recognized by one of the folder-oriented handlers. The checks here
// are not overly extensive, but hopefully enough to weed out false positives.
@@ -375,17 +432,29 @@ XMPFiles::CheckPackageFormat ( XMP_StringPtr folderPath )
if ( folderMode != Host_IO::kFMode_IsFolder ) return kXMP_UnknownFile;
return HandlerRegistry::checkTopFolderName ( std::string ( folderPath ) );
#endif
+ XMP_FILES_STATIC_END2 ( folderPath, kXMPErrSev_OperationFatal )
+ return kXMP_UnknownFile;
} // XMPFiles::CheckPackageFormat
// =================================================================================================
-static bool FileIsExcluded ( XMP_StringPtr clientPath, std::string * fileExt, Host_IO::FileMode * clientMode )
+static bool FileIsExcluded (
+ XMP_StringPtr clientPath,
+ std::string * fileExt,
+ Host_IO::FileMode * clientMode,
+ const XMPFiles::ErrorCallbackInfo * _errorCallbackInfoPtr = NULL )
{
// ! Return true for excluded files, false for OK files.
*clientMode = Host_IO::GetFileMode ( clientPath );
- if ( (*clientMode == Host_IO::kFMode_IsFolder) || (*clientMode == Host_IO::kFMode_IsOther) ) return true;
+
+ if ( (*clientMode == Host_IO::kFMode_IsFolder) || (*clientMode == Host_IO::kFMode_IsOther) ) {
+ XMP_Error error ( kXMPErr_FilePathNotAFile, "XMPFiles: path specified is not a file" );
+ XMP_FILES_STATIC_NOTIFY_ERROR ( _errorCallbackInfoPtr, clientPath, kXMPErrSev_Recoverable, error );
+ return true;
+ }
+
XMP_Assert ( (*clientMode == Host_IO::kFMode_IsFile) || (*clientMode == Host_IO::kFMode_DoesNotExist) );
if ( *clientMode == Host_IO::kFMode_IsFile ) {
@@ -401,7 +470,11 @@ static bool FileIsExcluded ( XMP_StringPtr clientPath, std::string * fileExt, Ho
// See if this file is one that XMPFiles should never process.
for ( size_t i = 0; kKnownRejectedFiles[i] != 0; ++i ) {
- if ( *fileExt == kKnownRejectedFiles[i] ) return true;
+ if ( *fileExt == kKnownRejectedFiles[i] ) {
+ XMP_Error error ( kXMPErr_RejectedFileExtension, "XMPFiles: rejected file extension specified" );
+ XMP_FILES_STATIC_NOTIFY_ERROR ( _errorCallbackInfoPtr, clientPath, kXMPErrSev_Recoverable, error );
+ return true;
+ }
}
}
@@ -410,6 +483,38 @@ static bool FileIsExcluded ( XMP_StringPtr clientPath, std::string * fileExt, Ho
} // FileIsExcluded
+static XMPFileHandlerInfo* CreateFileHandlerInfo (
+ XMPFiles* dummyParent,
+ XMP_FileFormat * format,
+ XMP_OptionBits options,
+ const XMPFiles::ErrorCallbackInfo * _errorCallbackInfoPtr = NULL )
+{
+ Host_IO::FileMode clientMode;
+ std::string fileExt; // Used to check for excluded files.
+ bool excluded = FileIsExcluded ( dummyParent->GetFilePath().c_str(), &fileExt, &clientMode, &sDefaultErrorCallback ); // ! Fills in fileExt and clientMode.
+ if ( excluded ) return 0;
+
+ XMPFileHandlerInfo * handlerInfo = 0;
+
+ XMP_FileFormat dummyFormat = kXMP_UnknownFile;
+ if ( format == 0 ) format = &dummyFormat;
+
+ options |= kXMPFiles_OpenForRead;
+ handlerInfo = HandlerRegistry::getInstance().selectSmartHandler ( dummyParent, dummyParent->GetFilePath().c_str(), *format, options );
+
+ if ( handlerInfo == 0 ) {
+ if ( clientMode == Host_IO::kFMode_DoesNotExist ) {
+ XMP_Error error ( kXMPErr_NoFile, "XMPFiles: file does not exist" );
+ XMP_FILES_STATIC_NOTIFY_ERROR ( _errorCallbackInfoPtr, dummyParent->GetFilePath().c_str(), kXMPErrSev_Recoverable, error );
+ }else{
+ XMP_Error error ( kXMPErr_NoFileHandler, "XMPFiles: No smart file handler available to handle file" );
+ XMP_FILES_STATIC_NOTIFY_ERROR ( _errorCallbackInfoPtr, dummyParent->GetFilePath().c_str(), kXMPErrSev_Recoverable, error );
+ }
+ return 0;
+ }
+ return handlerInfo;
+}
+
// =================================================================================================
/* class static */
@@ -419,45 +524,167 @@ XMPFiles::GetFileModDate ( XMP_StringPtr clientPath,
XMP_FileFormat * format, /* = 0 */
XMP_OptionBits options /* = 0 */ )
{
-
+ XMP_FILES_STATIC_START
// ---------------------------------------------------------------
// First try to select a smart handler. Return false if not found.
XMPFiles dummyParent; // GetFileModDate is static, but the handler needs a parent.
- dummyParent.filePath = clientPath;
+ dummyParent.SetFilePath ( clientPath );
+
+
+ XMPFileHandlerInfo * handlerInfo = 0;
+ handlerInfo = CreateFileHandlerInfo ( &dummyParent, format, options, &sDefaultErrorCallback );
+ if ( handlerInfo == 0 ) return false;
+
+ // -------------------------------------------------------------------------
+ // Fill in the format output. Call the handler to get the modification date.
- Host_IO::FileMode clientMode;
- std::string fileExt; // Used to check for excluded files.
- bool excluded = FileIsExcluded ( clientPath, &fileExt, &clientMode ); // ! Fills in fileExt and clientMode.
- if ( excluded ) return false;
+ dummyParent.format = handlerInfo->format;
+ if ( format ) *format = handlerInfo->format;
+
+ dummyParent.handler = handlerInfo->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
+ Host_IO::GetModifyDate ( curFilePath, &lastModDate );
+ 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,
+ XMP_FileFormat format /* = kXMP_UnknownFile */,
+ XMP_OptionBits options /* = 0 */ )
+{
+ XMP_FILES_STATIC_START
+ XMP_Assert ( (resourceList != 0) && resourceList->empty() ); // Ensure that the glue passes in an empty local.
+
+ // Try to select a handler.
+
+ if ( (filePath == 0) || (*filePath == 0) ) return false;
+
+ XMPFiles dummyParent; // GetAssociatedResources is static, but the handler needs a parent.
+ dummyParent.SetFilePath ( filePath );
XMPFileHandlerInfo * handlerInfo = 0;
- XMPFileHandlerCTor handlerCTor = 0;
- XMP_OptionBits handlerFlags = 0;
+ handlerInfo = CreateFileHandlerInfo ( &dummyParent, &format, options, &sDefaultErrorCallback );
+ if ( handlerInfo == 0 ) return false;
+
+ // -------------------------------------------------------------------------
+ // Fill in the format output. Call the handler to get the Associated Resources.
+
+ dummyParent.format = handlerInfo->format;
+
+ dummyParent.handler = handlerInfo->handlerCTor ( &dummyParent );
- XMP_FileFormat dummyFormat = kXMP_UnknownFile;
- if ( format == 0 ) format = &dummyFormat;
+ 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() );
- options |= kXMPFiles_OpenForRead;
- handlerInfo = HandlerRegistry::getInstance().selectSmartHandler ( &dummyParent, clientPath, *format, options );
- if ( handlerInfo == 0 ) return false;
+ delete dummyParent.handler;
+ dummyParent.handler = 0;
- // -------------------------------------------------------------------------
- // Fill in the format output. Call the handler to get the modification date.
+ return true;
+
+ XMP_FILES_STATIC_END2 ( filePath, kXMPErrSev_OperationFatal )
+ return false;
+
+} // XMPFiles::GetAssociatedResources
+
+bool
+XMPFiles::IsMetadataWritable (
+ XMP_StringPtr filePath,
+ XMP_Bool * writable,
+ XMP_FileFormat format /* = kXMP_UnknownFile */,
+ XMP_OptionBits options /* = 0 */ )
+{
+ XMP_FILES_STATIC_START
+ // Try to select a handler.
- dummyParent.format = handlerInfo->format;
- *format = handlerInfo->format;
+ if ( (filePath == 0) || (*filePath == 0) ) return false;
+ XMPFiles dummyParent; // IsMetadataWritable is static, but the handler needs a parent.
+ dummyParent.SetFilePath ( filePath );
+
+ XMPFileHandlerInfo * handlerInfo = 0;
+ handlerInfo = CreateFileHandlerInfo ( &dummyParent, &format, options, &sDefaultErrorCallback );
+ if ( handlerInfo == 0 ) return false;
+
+ if ( writable == 0 ) {
+ XMP_Throw("Boolean parameter is required for IsMetadataWritable() API.", kXMPErr_BadParam);
+ } else {
+ *writable = kXMP_Bool_False;
+ }
+
+
+ dummyParent.format = handlerInfo->format;
dummyParent.handler = handlerInfo->handlerCTor ( &dummyParent );
- bool ok = dummyParent.handler->GetFileModDate ( modDate );
+ // 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 ) {
+ if ( error.GetID() == kXMPErr_Unimplemented ) {
+ XMP_FILES_STATIC_NOTIFY_ERROR ( &sDefaultErrorCallback, filePath, kXMPErrSev_Recoverable, error );
+ return false;
+ } else {
+ throw;
+ }
+ }
delete dummyParent.handler;
dummyParent.handler = 0;
-
- return ok;
-
-} // XMPFiles::GetFileModDate
+ XMP_FILES_STATIC_END2 ( filePath, kXMPErrSev_OperationFatal )
+ return true;
+} // XMPFiles::IsMetadataWritable
+
// =================================================================================================
@@ -476,7 +703,7 @@ DoOpenFile ( XMPFiles * thiz,
CloseLocalFile ( thiz ); // Sanity checks if prior call failed.
thiz->ioRef = clientIO;
- thiz->filePath = clientPath;
+ thiz->SetFilePath ( clientPath );
thiz->format = kXMP_UnknownFile; // Make sure it is preset for later check.
thiz->openFlags = openFlags;
@@ -489,7 +716,7 @@ DoOpenFile ( XMPFiles * thiz,
if ( thiz->UsesClientIO() ) {
clientMode = Host_IO::kFMode_IsFile;
} else {
- bool excluded = FileIsExcluded ( clientPath, &fileExt, &clientMode ); // ! Fills in fileExt and clientMode.
+ bool excluded = FileIsExcluded ( clientPath, &fileExt, &clientMode, &thiz->errorCallback ); // ! Fills in fileExt and clientMode.
if ( excluded ) return false;
}
@@ -505,16 +732,32 @@ DoOpenFile ( XMPFiles * thiz,
#if ! EnablePacketScanning
- if ( handlerInfo == 0 ) return false;
+ if ( handlerInfo == 0 ) {
+ if ( clientMode == Host_IO::kFMode_DoesNotExist ) {
+ XMP_Error error ( kXMPErr_NoFile, "XMPFiles: file does not exist" );
+ XMP_FILES_STATIC_NOTIFY_ERROR ( &thiz->errorCallback, clientPath, kXMPErrSev_Recoverable, error );
+ }
+ return false;
+ }
#else
if ( handlerInfo == 0 ) {
// No smart handler, packet scan if appropriate.
+ if ( clientMode == Host_IO::kFMode_DoesNotExist ) {
+ XMP_Error error ( kXMPErr_NoFile, "XMPFiles: file does not exist" );
+ XMP_FILES_STATIC_NOTIFY_ERROR ( &thiz->errorCallback, clientPath, kXMPErrSev_Recoverable, error );
+ return false;
+ } else if ( clientMode != Host_IO::kFMode_IsFile ) {
+ return false;
+ }
- if ( clientMode != Host_IO::kFMode_IsFile ) return false;
- if ( openFlags & kXMPFiles_OpenUseSmartHandler ) return false;
+ if ( openFlags & kXMPFiles_OpenUseSmartHandler ) {
+ XMP_Error error ( kXMPErr_NoFileHandler, "XMPFiles: No smart file handler available to handle file" );
+ XMP_FILES_STATIC_NOTIFY_ERROR ( &thiz->errorCallback, clientPath, kXMPErrSev_Recoverable, error );
+ return false;
+ }
if ( openFlags & kXMPFiles_OpenLimitedScanning ) {
bool scanningOK = false;
@@ -588,7 +831,7 @@ static bool DoOpenFile( XMPFiles* thiz,
// setup members
//
thiz->ioRef = clientIO;
- thiz->filePath = std::string( clientPath );
+ thiz->SetFilePath ( clientPath );
thiz->format = hdlInfo.format;
thiz->openFlags = openFlags;
@@ -602,7 +845,12 @@ static bool DoOpenFile( XMPFiles* thiz,
XMP_Assert ( handlerFlags == handler->handlerFlags );
thiz->handler = handler;
+ bool readOnly = XMP_OptionIsClear ( openFlags, kXMPFiles_OpenForUpdate );
+ if ( thiz->ioRef == 0 ) { //Need to open the file if not done already
+ thiz->ioRef = XMPFiles_IO::New_XMPFiles_IO ( clientPath, readOnly );
+ if ( thiz->ioRef == 0 ) return false;
+ }
//
// try to read metadata
//
@@ -644,9 +892,10 @@ XMPFiles::OpenFile ( XMP_StringPtr clientPath,
XMP_FileFormat format /* = kXMP_UnknownFile */,
XMP_OptionBits openFlags /* = 0 */ )
{
-
+ XMP_FILES_START
return DoOpenFile ( this, 0, clientPath, format, openFlags );
-
+ XMP_FILES_END2 ( clientPath, kXMPErrSev_FileFatal )
+ return false;
}
// =================================================================================================
@@ -658,35 +907,38 @@ XMPFiles::OpenFile ( XMP_IO* clientIO,
XMP_FileFormat format /* = kXMP_UnknownFile */,
XMP_OptionBits openFlags /* = 0 */ )
{
-
+ XMP_FILES_START
+ this->progressTracker = 0; // Progress tracking is not supported for client-managed I/O.
return DoOpenFile ( this, clientIO, "", format, openFlags );
+ XMP_FILES_END1 ( kXMPErrSev_FileFatal )
+ return false;
} // XMPFiles::OpenFile
-#endif // XMP_StaticBuild
-
-// =================================================================================================
-
-#if XMP_StaticBuild // ! Client XMP_IO objects can only be used in static builds.
-
-bool XMPFiles::OpenFile( const Common::XMPFileHandlerInfo& hdlInfo,
+bool XMPFiles::OpenFile ( const Common::XMPFileHandlerInfo& hdlInfo,
XMP_IO* clientIO,
XMP_OptionBits openFlags /*= 0*/ )
{
+ XMP_FILES_START
+ this->progressTracker = 0; // Progress tracking is not supported for client-managed I/O.
+ return DoOpenFile ( this, hdlInfo, clientIO, NULL, openFlags );
+ XMP_FILES_END1 ( kXMPErrSev_FileFatal )
+ return false;
- return DoOpenFile( this, hdlInfo, clientIO, NULL, openFlags );
}
#endif // XMP_StaticBuild
// =================================================================================================
-bool XMPFiles::OpenFile( const Common::XMPFileHandlerInfo& hdlInfo,
+bool XMPFiles::OpenFile ( const Common::XMPFileHandlerInfo& hdlInfo,
XMP_StringPtr filePath,
XMP_OptionBits openFlags /*= 0*/ )
{
-
- return DoOpenFile( this, hdlInfo, NULL, filePath, openFlags );
+ XMP_FILES_START
+ return DoOpenFile ( this, hdlInfo, NULL, filePath, openFlags );
+ XMP_FILES_END2 (filePath, kXMPErrSev_FileFatal )
+ return false;
}
// =================================================================================================
@@ -694,6 +946,7 @@ bool XMPFiles::OpenFile( const Common::XMPFileHandlerInfo& hdlInfo,
void
XMPFiles::CloseFile ( XMP_OptionBits closeFlags /* = 0 */ )
{
+ XMP_FILES_START
if ( this->handler == 0 ) return; // Return if there is no open file (not an error).
bool needsUpdate = this->handler->needsUpdate;
@@ -716,6 +969,11 @@ XMPFiles::CloseFile ( XMP_OptionBits closeFlags /* = 0 */ )
XMP_Throw ( "XMPFiles::CloseFile - Safe update not supported", kXMPErr_Unavailable );
}
+ if ( (this->progressTracker != 0) && this->UsesLocalIO() ) {
+ XMPFiles_IO * localFile = (XMPFiles_IO*)this->ioRef;
+ localFile->SetProgressTracker ( this->progressTracker );
+ }
+
// Try really hard to make sure the file is closed and the handler is deleted.
try {
@@ -765,6 +1023,7 @@ XMPFiles::CloseFile ( XMP_OptionBits closeFlags /* = 0 */ )
XMP_IO* origFileRef = this->ioRef;
origFileRef->Rewind();
+ if ( this->progressTracker != 0 && (this->handler->handlerFlags & kXMPFiles_CanNotifyProgress) ) progressTracker->BeginWork ( (float) origFileRef->Length() );
XIO::Copy ( origFileRef, tempFileRef, origFileRef->Length(), abortProc, abortArg );
try {
@@ -781,6 +1040,8 @@ XMPFiles::CloseFile ( XMP_OptionBits closeFlags /* = 0 */ )
}
+ if ( progressTracker != 0 && (this->handler->handlerFlags & kXMPFiles_CanNotifyProgress)) progressTracker->WorkComplete();
+
}
this->ioRef->AbsorbTemp();
@@ -799,9 +1060,9 @@ XMPFiles::CloseFile ( XMP_OptionBits closeFlags /* = 0 */ )
if ( this->handler != 0 ) delete this->handler;
} catch ( ... ) { /* Do nothing, throw the outer exception later. */ }
- if( this->ioRef ) this->ioRef->DeleteTemp();
+ if ( this->ioRef ) this->ioRef->DeleteTemp();
CloseLocalFile ( this );
- this->filePath.clear();
+ this->ClearFilePath();
this->handler = 0;
this->format = kXMP_UnknownFile;
@@ -819,7 +1080,7 @@ XMPFiles::CloseFile ( XMP_OptionBits closeFlags /* = 0 */ )
// Clear the XMPFiles member variables.
CloseLocalFile ( this );
- this->filePath.clear();
+ this->ClearFilePath();
this->handler = 0;
this->format = kXMP_UnknownFile;
@@ -829,6 +1090,7 @@ XMPFiles::CloseFile ( XMP_OptionBits closeFlags /* = 0 */ )
if ( this->tempPtr != 0 ) free ( this->tempPtr ); // ! Must have been malloc-ed!
this->tempPtr = 0;
this->tempUI32 = 0;
+ XMP_FILES_END1 ( kXMPErrSev_FileFatal )
} // XMPFiles::CloseFile
@@ -841,6 +1103,7 @@ XMPFiles::GetFileInfo ( XMP_StringPtr * filePath /* = 0 */,
XMP_FileFormat * format /* = 0 */,
XMP_OptionBits * handlerFlags /* = 0 */ ) const
{
+ XMP_FILES_START
if ( this->handler == 0 ) return false;
XMPFileHandler * handler = this->handler;
@@ -855,7 +1118,7 @@ XMPFiles::GetFileInfo ( XMP_StringPtr * filePath /* = 0 */,
*openFlags = this->openFlags;
*format = this->format;
*handlerFlags = this->handler->handlerFlags;
-
+ XMP_FILES_END1 ( kXMPErrSev_FileFatal )
return true;
} // XMPFiles::GetFileInfo
@@ -866,10 +1129,12 @@ void
XMPFiles::SetAbortProc ( XMP_AbortProc abortProc,
void * abortArg )
{
+ XMP_FILES_START
this->abortProc = abortProc;
this->abortArg = abortArg;
XMP_Assert ( (abortProc != (XMP_AbortProc)0) || (abortArg != (void*)(unsigned long long)0xDEADBEEFULL) ); // Hack to test the assert callback.
+ XMP_FILES_END1 ( kXMPErrSev_OperationFatal )
} // XMPFiles::SetAbortProc
// =================================================================================================
@@ -906,6 +1171,7 @@ XMPFiles::GetXMP ( SXMPMeta * xmpObj /* = 0 */,
XMP_StringLen * xmpPacketLen /* = 0 */,
XMP_PacketInfo * packetInfo /* = 0 */ )
{
+ XMP_FILES_START
if ( this->handler == 0 ) XMP_Throw ( "XMPFiles::GetXMP - No open file", kXMPErr_BadObject );
XMP_OptionBits applyTemplateFlags = kXMPTemplate_AddNewProperties | kXMPTemplate_IncludeInternalProperties;
@@ -944,7 +1210,7 @@ XMPFiles::GetXMP ( SXMPMeta * xmpObj /* = 0 */,
if ( xmpPacketLen != 0 ) *xmpPacketLen = (XMP_StringLen) this->handler->xmpPacket.size();
SetClientPacketInfo ( packetInfo, this->handler->packetInfo,
this->handler->xmpPacket, this->handler->needsUpdate );
-
+ XMP_FILES_END1 ( kXMPErrSev_FileFatal )
return true;
} // XMPFiles::GetXMP
@@ -1038,7 +1304,9 @@ DoPutXMP ( XMPFiles * thiz, const SXMPMeta & xmpObj, const bool doIt )
void
XMPFiles::PutXMP ( const SXMPMeta & xmpObj )
{
+ XMP_FILES_START
(void) DoPutXMP ( this, xmpObj, true );
+ XMP_FILES_END1 ( kXMPErrSev_FileFatal )
} // XMPFiles::PutXMP
@@ -1048,9 +1316,12 @@ void
XMPFiles::PutXMP ( XMP_StringPtr xmpPacket,
XMP_StringLen xmpPacketLen /* = kXMP_UseNullTermination */ )
{
- SXMPMeta xmpObj ( xmpPacket, xmpPacketLen );
+ XMP_FILES_START
+ SXMPMeta xmpObj;
+ xmpObj.SetErrorCallback ( ErrorCallbackForXMPMeta, &errorCallback );
+ xmpObj.ParseFromBuffer ( xmpPacket, xmpPacketLen );
this->PutXMP ( xmpObj );
-
+ XMP_FILES_END1 ( kXMPErrSev_FileFatal )
} // XMPFiles::PutXMP
// =================================================================================================
@@ -1058,6 +1329,7 @@ XMPFiles::PutXMP ( XMP_StringPtr xmpPacket,
bool
XMPFiles::CanPutXMP ( const SXMPMeta & xmpObj )
{
+ XMP_FILES_START
if ( this->handler == 0 ) XMP_Throw ( "XMPFiles::CanPutXMP - No open file", kXMPErr_BadObject );
if ( ! (this->openFlags & kXMPFiles_OpenForUpdate) ) return false;
@@ -1067,6 +1339,8 @@ XMPFiles::CanPutXMP ( const SXMPMeta & xmpObj )
if ( this->handler->handlerFlags & kXMPFiles_CanExpand ) return true;
return DoPutXMP ( this, xmpObj, false );
+ XMP_FILES_END1 ( kXMPErrSev_FileFatal )
+ return false;
} // XMPFiles::CanPutXMP
@@ -1076,9 +1350,157 @@ bool
XMPFiles::CanPutXMP ( XMP_StringPtr xmpPacket,
XMP_StringLen xmpPacketLen /* = kXMP_UseNullTermination */ )
{
- SXMPMeta xmpObj ( xmpPacket, xmpPacketLen );
+ XMP_FILES_START
+ SXMPMeta xmpObj;
+ xmpObj.SetErrorCallback ( ErrorCallbackForXMPMeta, &errorCallback );
+ xmpObj.ParseFromBuffer ( xmpPacket, xmpPacketLen );
return this->CanPutXMP ( xmpObj );
+ XMP_FILES_END1 ( kXMPErrSev_FileFatal )
+ return false;
} // XMPFiles::CanPutXMP
// =================================================================================================
+
+/* class-static */
+void
+XMPFiles::SetDefaultProgressCallback ( const XMP_ProgressTracker::CallbackInfo & cbInfo )
+{
+ XMP_FILES_STATIC_START
+ XMP_Assert ( cbInfo.wrapperProc != 0 ); // ! Should be provided by the glue code.
+
+ sProgressDefault = cbInfo;
+ XMP_FILES_STATIC_END1 ( kXMPErrSev_OperationFatal )
+
+} // XMPFiles::SetDefaultProgressCallback
+
+// =================================================================================================
+
+void
+XMPFiles::SetProgressCallback ( const XMP_ProgressTracker::CallbackInfo & cbInfo )
+{
+ XMP_FILES_START
+ XMP_Assert ( cbInfo.wrapperProc != 0 ); // ! Should be provided by the glue code.
+
+ if ( (this->handler != 0) && this->UsesClientIO() ) return; // Can't use progress tracking.
+
+ if ( this->progressTracker != 0 ) {
+ delete this->progressTracker; // ! Delete any existing tracker.
+ this->progressTracker = 0;
+ }
+
+ if ( cbInfo.clientProc != 0 ) {
+ this->progressTracker = new XMP_ProgressTracker ( cbInfo );
+ }
+ XMP_FILES_END1 ( kXMPErrSev_OperationFatal )
+
+} // XMPFiles::SetProgressCallback
+
+// =================================================================================================
+// Error notifications
+// ===================
+
+// -------------------------------------------------------------------------------------------------
+// SetDefaultErrorCallback
+// -----------------------
+
+/* class-static */
+void XMPFiles::SetDefaultErrorCallback ( XMPFiles_ErrorCallbackWrapper wrapperProc,
+ XMPFiles_ErrorCallbackProc clientProc,
+ void * context,
+ XMP_Uns32 limit )
+{
+ XMP_FILES_STATIC_START
+ XMP_Assert ( wrapperProc != 0 ); // Must always be set by the glue;
+
+ sDefaultErrorCallback.wrapperProc = wrapperProc;
+ sDefaultErrorCallback.clientProc = clientProc;
+ sDefaultErrorCallback.context = context;
+ sDefaultErrorCallback.limit = limit;
+ XMP_FILES_STATIC_END1 ( kXMPErrSev_OperationFatal )
+} // SetDefaultErrorCallback
+
+// -------------------------------------------------------------------------------------------------
+// SetErrorCallback
+// ----------------
+
+void XMPFiles::SetErrorCallback ( XMPFiles_ErrorCallbackWrapper wrapperProc,
+ XMPFiles_ErrorCallbackProc clientProc,
+ void * context,
+ XMP_Uns32 limit )
+{
+ XMP_FILES_START
+ XMP_Assert ( wrapperProc != 0 ); // Must always be set by the glue;
+
+ this->errorCallback.Clear();
+ this->errorCallback.wrapperProc = wrapperProc;
+ this->errorCallback.clientProc = clientProc;
+ this->errorCallback.context = context;
+ this->errorCallback.limit = limit;
+ XMP_FILES_END1 ( kXMPErrSev_OperationFatal )
+} // SetErrorCallback
+
+// -------------------------------------------------------------------------------------------------
+// ResetErrorCallbackLimit
+// -----------------------
+
+void XMPFiles::ResetErrorCallbackLimit ( XMP_Uns32 limit ) {
+ XMP_FILES_START
+ this->errorCallback.limit = limit;
+ this->errorCallback.notifications = 0;
+ this->errorCallback.topSeverity = kXMPErrSev_Recoverable;
+ XMP_FILES_END1 ( kXMPErrSev_OperationFatal )
+} // ResetErrorCallbackLimit
+
+// -------------------------------------------------------------------------------------------------
+// ErrorCallbackInfo::CanNotify
+// -------------------------------
+//
+// This is const just to be usable from const XMPMeta functions.
+
+bool
+ XMPFiles::ErrorCallbackInfo::CanNotify() const
+{
+ XMP_Assert ( (this->clientProc == 0) || (this->wrapperProc != 0) );
+ return ( this->clientProc != 0 );
+}
+
+// -------------------------------------------------------------------------------------------------
+// ErrorCallbackInfo::ClientCallbackWrapper
+// -------------------------------
+//
+// This is const just to be usable from const XMPMeta functions.
+
+bool
+ XMPFiles::ErrorCallbackInfo::ClientCallbackWrapper ( XMP_StringPtr filePath,
+ XMP_ErrorSeverity severity,
+ XMP_Int32 cause,
+ XMP_StringPtr messsage ) const
+{
+
+ XMP_StringPtr filePathPtr = filePath;
+ if ( filePathPtr == 0 ) {
+ filePathPtr = this->filePath.c_str();
+ }
+
+ XMP_Bool retValue = (*this->wrapperProc) ( this->clientProc, this->context, filePathPtr, severity, cause, messsage );
+ return ConvertXMP_BoolToBool(retValue);
+}
+
+// -------------------------------------------------------------------------------------------------
+// ErrorCallbackForXMPMeta
+// -------------------------------
+
+bool
+ ErrorCallbackForXMPMeta ( void * context, XMP_ErrorSeverity severity,
+ XMP_Int32 cause,
+ XMP_StringPtr message)
+{
+ //typecast context to GenericErrorCallback
+ GenericErrorCallback * callback = ( GenericErrorCallback * ) context;
+ XMP_Error error ( cause, message );
+ callback->NotifyClient ( severity, error );
+ return !kXMP_Bool_False;
+}
+
+// =================================================================================================
diff --git a/XMPFiles/source/XMPFiles.hpp b/XMPFiles/source/XMPFiles.hpp
index f4f2e96..70ab020 100644
--- a/XMPFiles/source/XMPFiles.hpp
+++ b/XMPFiles/source/XMPFiles.hpp
@@ -18,6 +18,8 @@
#include "public/include/XMP_IO.hpp"
+#include "source/XMP_ProgressTracker.hpp"
+
class XMPFileHandler;
namespace Common{ struct XMPFileHandlerInfo; }
@@ -57,10 +59,7 @@ namespace Common{ struct XMPFileHandlerInfo; }
// Destructor:
// - Decrement the ref count, return if greater than zero.
// - Call LFA_Close if necessary.
-//
-// UnlockLib & UnlockObj:
-// - Release the thread lock. Same for now, no per-object lock.
-//
+////
// GetFormatInfo:
// - Return the flags for the registered handler.
//
@@ -137,7 +136,7 @@ namespace Common{ struct XMPFileHandlerInfo; }
// file needs to be updated. The handler's destructor must only close the file, not update it.
// The handler can optionally have a WriteFile method, if it can rewrite the entire file.
//
-// The handler is free to use its best judgement about caching parts of the file in memory. Overall
+// The handler is free to use its best judgment about caching parts of the file in memory. Overall
// speed of a single open/get/put/close cycle is probably the best goal, assuming a modern processor
// with a reasonable (significant but not enormous) amount of RAM.
//
@@ -161,92 +160,133 @@ namespace Common{ struct XMPFileHandlerInfo; }
class XMPFiles {
public:
+ static bool Initialize(XMP_OptionBits options, const char* pluginFolder, const char* plugins = NULL);
+ static void Terminate();
- static void GetVersionInfo ( XMP_VersionInfo * info );
+ static void GetVersionInfo(XMP_VersionInfo * info);
- static bool Initialize ( XMP_OptionBits options, const char* pluginFolder, const char* plugins = NULL );
- static void Terminate();
-
- XMPFiles();
- virtual ~XMPFiles();
+ static bool GetFormatInfo(XMP_FileFormat format, XMP_OptionBits * flags = 0);
+
+ static bool GetFileModDate(
+ XMP_StringPtr filePath,
+ XMP_DateTime * modDate,
+ XMP_FileFormat * format = 0,
+ XMP_OptionBits options = 0);
- static bool GetFormatInfo ( XMP_FileFormat format,
- XMP_OptionBits * flags = 0 );
+ static XMP_FileFormat CheckFileFormat(XMP_StringPtr filePath);
+ static XMP_FileFormat CheckPackageFormat(XMP_StringPtr folderPath);
- static XMP_FileFormat CheckFileFormat ( XMP_StringPtr filePath );
- static XMP_FileFormat CheckPackageFormat ( XMP_StringPtr folderPath );
-
- static bool GetFileModDate ( XMP_StringPtr filePath,
- XMP_DateTime * modDate,
- XMP_FileFormat * format = 0, // ! Can be null.
- XMP_OptionBits options = 0 );
+ static bool GetAssociatedResources (
+ XMP_StringPtr filePath,
+ std::vector<std::string> * resourceList,
+ XMP_FileFormat format = kXMP_UnknownFile ,
+ XMP_OptionBits options = 0 );
- bool OpenFile ( XMP_StringPtr filePath,
- XMP_FileFormat format = kXMP_UnknownFile,
- XMP_OptionBits openFlags = 0 );
+ static bool IsMetadataWritable (
+ XMP_StringPtr filePath,
+ XMP_Bool * writable,
+ XMP_FileFormat format = kXMP_UnknownFile ,
+ XMP_OptionBits options = 0 );
- #if XMP_StaticBuild // ! Client XMP_IO objects can only be used in static builds.
- bool OpenFile ( XMP_IO * clientIO,
- XMP_FileFormat format = kXMP_UnknownFile,
- XMP_OptionBits openFlags = 0 );
- #endif
+ static void SetDefaultProgressCallback(const XMP_ProgressTracker::CallbackInfo & cbInfo);
+ static void SetDefaultErrorCallback(XMPFiles_ErrorCallbackWrapper wrapperProc,
+ XMPFiles_ErrorCallbackProc clientProc,
+ void * context,
+ XMP_Uns32 limit);
- bool OpenFile( const Common::XMPFileHandlerInfo& hdlInfo,
- XMP_StringPtr filePath,
- XMP_OptionBits openFlags = 0 );
+ XMPFiles();
+ virtual ~XMPFiles();
+
+ bool OpenFile(XMP_StringPtr filePath, XMP_FileFormat format = kXMP_UnknownFile, XMP_OptionBits openFlags = 0);
+ bool OpenFile(const Common::XMPFileHandlerInfo & hdlInfo, XMP_StringPtr filePath, XMP_OptionBits openFlags = 0);
#if XMP_StaticBuild // ! Client XMP_IO objects can only be used in static builds.
- bool OpenFile( const Common::XMPFileHandlerInfo& hdlInfo,
- XMP_IO* clientIO,
- XMP_OptionBits openFlags = 0 );
+ bool OpenFile(XMP_IO * clientIO, XMP_FileFormat format = kXMP_UnknownFile, XMP_OptionBits openFlags = 0);
+ bool OpenFile(const Common::XMPFileHandlerInfo & hdlInfo, XMP_IO * clientIO, XMP_OptionBits openFlags = 0);
#endif
- void CloseFile ( XMP_OptionBits closeFlags = 0 );
+ void CloseFile(XMP_OptionBits closeFlags = 0);
- bool GetFileInfo ( XMP_StringPtr * filePath = 0,
- XMP_StringLen * filePathLen = 0,
- XMP_OptionBits * openFlags = 0,
- XMP_FileFormat * format = 0,
- XMP_OptionBits * handlerFlags = 0 ) const;
+ bool GetFileInfo(
+ XMP_StringPtr * filePath = 0,
+ XMP_StringLen * filePathLen = 0,
+ XMP_OptionBits * openFlags = 0,
+ XMP_FileFormat * format = 0,
+ XMP_OptionBits * handlerFlags = 0 ) const;
- void SetAbortProc ( XMP_AbortProc abortProc,
- void * abortArg );
+ bool GetXMP(
+ SXMPMeta * xmpObj = 0,
+ XMP_StringPtr * xmpPacket = 0,
+ XMP_StringLen * xmpPacketLen = 0,
+ XMP_PacketInfo * packetInfo = 0);
- bool GetXMP ( SXMPMeta * xmpObj = 0,
- XMP_StringPtr * xmpPacket = 0,
- XMP_StringLen * xmpPacketLen = 0,
- XMP_PacketInfo * packetInfo = 0 );
+ void PutXMP(const SXMPMeta & xmpObj);
+ void PutXMP(XMP_StringPtr xmpPacket, XMP_StringLen xmpPacketLen = kXMP_UseNullTermination);
- void PutXMP ( const SXMPMeta & xmpObj );
+ bool CanPutXMP(const SXMPMeta & xmpObj);
+ bool CanPutXMP(XMP_StringPtr xmpPacket, XMP_StringLen xmpPacketLen = kXMP_UseNullTermination);
- void PutXMP ( XMP_StringPtr xmpPacket,
- XMP_StringLen xmpPacketLen = kXMP_UseNullTermination );
+ void SetAbortProc(XMP_AbortProc abortProc, void * abortArg);
- bool CanPutXMP ( const SXMPMeta & xmpObj );
+ void SetProgressCallback(const XMP_ProgressTracker::CallbackInfo & cbInfo);
- bool CanPutXMP ( XMP_StringPtr xmpPacket,
- XMP_StringLen xmpPacketLen = kXMP_UseNullTermination );
+ void SetErrorCallback(
+ XMPFiles_ErrorCallbackWrapper wrapperProc,
+ XMPFiles_ErrorCallbackProc clientProc,
+ void * context,
+ XMP_Uns32 limit);
+ void ResetErrorCallbackLimit(XMP_Uns32 limit);
- inline bool UsesClientIO() { return this->filePath.empty(); };
- inline bool UsesLocalIO() { return ( ! this->UsesClientIO() ); };
+ class ErrorCallbackInfo : public GenericErrorCallback {
+ public:
+ XMPFiles_ErrorCallbackWrapper wrapperProc;
+ XMPFiles_ErrorCallbackProc clientProc;
+ void * context;
+ std::string filePath;
- // Leave this data public so file handlers can see it.
+ ErrorCallbackInfo()
+ : wrapperProc(0)
+ , clientProc(0)
+ , context(0) {};
- XMP_Int32 clientRefs; // ! Must be signed to allow decrement from zero.
- XMP_ReadWriteLock lock;
+ void Clear() {
+ this->wrapperProc = 0; this->clientProc = 0; this->context = 0;
+ GenericErrorCallback::Clear();
+ };
- XMP_FileFormat format;
- XMP_IO * ioRef; // Non-zero if a file is open.
- std::string filePath; // Empty for client-managed I/O.
- XMP_OptionBits openFlags;
- XMPFileHandler * handler; // Non-null if a file is open.
+ bool CanNotify() const;
- void * tempPtr; // For use between the CheckProc and handler creation.
- XMP_Uns32 tempUI32;
+ bool ClientCallbackWrapper(
+ XMP_StringPtr filePath,
+ XMP_ErrorSeverity severity,
+ XMP_Int32 cause,
+ XMP_StringPtr messsage) const;
+ };
- XMP_AbortProc abortProc;
- void * abortArg;
+ inline bool UsesClientIO() { return this->filePath.empty(); };
+ inline bool UsesLocalIO() { return ( ! this->UsesClientIO() ); };
+ inline void SetFilePath(XMP_StringPtr _filePath) { filePath = _filePath; errorCallback.filePath = _filePath; }
+ inline void ClearFilePath() { filePath.clear(); errorCallback.filePath.clear(); }
+ inline const std::string& GetFilePath() { return filePath; }
-}; // XMPFiles
+ // Leave this data public so file handlers can see it.
+ XMP_Int32 clientRefs; // ! Must be signed to allow decrement from zero.
+ XMP_ReadWriteLock lock;
+ XMP_FileFormat format;
+ XMP_IO * ioRef; // Non-zero if a file is open.
+ XMP_OptionBits openFlags;
+ XMPFileHandler * handler; // Non-null if a file is open.
+ void * tempPtr; // For use between the CheckProc and handler creation.
+ XMP_Uns32 tempUI32;
+ XMP_AbortProc abortProc;
+ void * abortArg;
+ XMP_ProgressTracker * progressTracker;
+ ErrorCallbackInfo errorCallback;
+
+private:
+ std::string filePath; // Empty for client-managed I/O.
+};
+
+bool ErrorCallbackForXMPMeta(void * context, XMP_ErrorSeverity severity, XMP_Int32 cause, XMP_StringPtr message);
#endif /* __XMPFiles_hpp__ */
diff --git a/XMPFiles/source/XMPFiles_Impl.cpp b/XMPFiles/source/XMPFiles_Impl.cpp
index 92b2ea3..471c48d 100644
--- a/XMPFiles/source/XMPFiles_Impl.cpp
+++ b/XMPFiles/source/XMPFiles_Impl.cpp
@@ -44,6 +44,10 @@ using namespace std;
///
// =================================================================================================
+#if ! XMP_StaticBuild
+ #include "public/include/XMP.incl_cpp"
+#endif
+
#if XMP_WinBuild
#pragma warning ( disable : 4290 ) // C++ exception specification ignored except to indicate a function is not __declspec(nothrow)
#pragma warning ( disable : 4800 ) // forcing value to bool 'true' or 'false' (performance warning)
@@ -52,6 +56,15 @@ using namespace std;
bool ignoreLocalText = false;
XMP_FileFormat voidFileFormat = 0; // Used as sink for unwanted output parameters.
+
+#if ! XMP_StaticBuild
+ XMP_PacketInfo voidPacketInfo;
+ void * voidVoidPtr = 0;
+ XMP_StringPtr voidStringPtr = 0;
+ XMP_StringLen voidStringLen = 0;
+ XMP_OptionBits voidOptionBits = 0;
+#endif
+
// =================================================================================================
// Add all known mappings, multiple mappings (tif, tiff) are OK.
@@ -89,6 +102,7 @@ const FileExtMapping kFileExtMap[] =
{ "wma", kXMP_WMAVFile },
{ "wmv", kXMP_WMAVFile },
{ "mxf", kXMP_MXFFile },
+ { "r3d", kXMP_REDFile },
{ "mpg", kXMP_MPEGFile },
{ "mpeg", kXMP_MPEGFile },
@@ -165,7 +179,6 @@ const char * kKnownRejectedFiles[] =
// UCF subformats
"air",
// Others
- "r3d",
0 }; // ! Keep a 0 sentinel at the end.
// =================================================================================================
@@ -369,15 +382,90 @@ bool XMPFileHandler::GetFileModDate ( XMP_DateTime * modDate )
XMP_Throw ( "Base implementation of GetFileModDate only for typical embedding handlers", kXMPErr_InternalFailure );
}
- if ( this->parent->filePath.empty() ) {
+ if ( this->parent->GetFilePath().empty() ) {
XMP_Throw ( "GetFileModDate cannot be used with client-provided I/O", kXMPErr_InternalFailure );
}
- return Host_IO::GetModifyDate ( this->parent->filePath.c_str(), modDate );
+ return Host_IO::GetModifyDate ( this->parent->GetFilePath().c_str(), modDate );
} // XMPFileHandler::GetFileModDate
// =================================================================================================
+// XMPFileHandler::FillMetadataFiles
+// ==============================
+//
+// The base implementation is only for files having embedded metadata for which the same file will
+// be returned.
+
+void XMPFileHandler::FillMetadataFiles ( std::vector<std::string> * metadataFiles )
+{
+ XMP_OptionBits flags = this->handlerFlags;
+ if ( (flags & kXMPFiles_HandlerOwnsFile) ||
+ (flags & kXMPFiles_UsesSidecarXMP) ||
+ (flags & kXMPFiles_FolderBasedFormat) ) {
+ XMP_Throw ( "Base implementation of FillMetadataFiles only for typical embedding handlers", kXMPErr_InternalFailure );
+ }
+
+ if ( this->parent->GetFilePath().empty() ) {
+ XMP_Throw ( "FillMetadataFiles cannot be used with client-provided I/O", kXMPErr_InternalFailure );
+ }
+
+ metadataFiles->push_back ( std::string ( this->parent->GetFilePath().c_str() ) );
+} // XMPFileHandler::FillMetadataFiles
+
+// =================================================================================================
+// XMPFileHandler::FillAssociatedResources
+// =======================================
+//
+// The base implementation is only for files having embedded metadata for which the same file will
+// be returned as the associated resource.
+//
+
+void XMPFileHandler::FillAssociatedResources ( std::vector<std::string> * metadataFiles )
+{
+ XMP_OptionBits flags = this->handlerFlags;
+ if ( (flags & kXMPFiles_HandlerOwnsFile) ||
+ (flags & kXMPFiles_UsesSidecarXMP) ||
+ (flags & kXMPFiles_FolderBasedFormat) ) {
+ XMP_Throw ( "GetAssociatedResources is not implemented for this file format", kXMPErr_InternalFailure );
+ }
+
+ if ( this->parent->GetFilePath().empty() ) {
+ XMP_Throw ( "GetAssociatedResources cannot be used with client-provided I/O", kXMPErr_InternalFailure );
+ }
+
+ metadataFiles->push_back ( std::string ( this->parent->GetFilePath().c_str() ) );
+} // XMPFileHandler::FillAssociatedResources
+
+// =================================================================================================
+// XMPFileHandler::IsMetadataWritable
+// =======================================
+//
+// The base implementation is only for files having embedded metadata and it checks whether that
+// file is writable or no.Returns true if the file is writable.
+//
+bool XMPFileHandler::IsMetadataWritable ( )
+{
+ XMP_OptionBits flags = this->handlerFlags;
+ if ( (flags & kXMPFiles_HandlerOwnsFile) ||
+ (flags & kXMPFiles_UsesSidecarXMP) ||
+ (flags & kXMPFiles_FolderBasedFormat) ) {
+ XMP_Throw ( "IsMetadataWritable is not implemented for this file format", kXMPErr_InternalFailure );
+ }
+
+ if ( this->parent->GetFilePath().empty() ) {
+ XMP_Throw ( "IsMetadataWritable cannot be used with client-provided I/O", kXMPErr_InternalFailure );
+ }
+
+ try {
+ return Host_IO::Writable( this->parent->GetFilePath().c_str() );
+ } catch ( ... ) {
+
+ }
+ return false;
+}
+
+// =================================================================================================
// XMPFileHandler::ProcessXMP
// ==========================
//
diff --git a/XMPFiles/source/XMPFiles_Impl.hpp b/XMPFiles/source/XMPFiles_Impl.hpp
index 689aa43..6e21c23 100644
--- a/XMPFiles/source/XMPFiles_Impl.hpp
+++ b/XMPFiles/source/XMPFiles_Impl.hpp
@@ -75,6 +75,14 @@ extern bool ignoreLocalText;
#define EnablePacketScanning 1
#endif
+#ifndef EnablePluginManager
+ #if XMP_iOSBuild
+ #define EnablePluginManager 0
+ #else
+ #define EnablePluginManager 1
+ #endif
+#endif
+
extern XMP_Int32 sXMPFilesInitCount;
#ifndef GatherPerformanceData
@@ -157,7 +165,15 @@ extern const FileExtMapping kFileExtMap[];
extern const char * kKnownScannedFiles[];
extern const char * kKnownRejectedFiles[];
-typedef std::map < const char *, const char * > ID3GenreMap;
+class CharStarLess { // Comparison object for the genre code maps.
+public:
+ bool operator() ( const char * left, const char * right ) const {
+ int order = strcmp ( left, right );
+ return order < 0;
+ }
+};
+
+typedef std::map < const char *, const char *, CharStarLess > ID3GenreMap;
extern ID3GenreMap* kMapID3GenreCodeToName; // Storage defined in ID3_Support.cpp.
extern ID3GenreMap* kMapID3GenreNameToCode;
@@ -266,11 +282,17 @@ public:
containsXMP(false), processedXMP(false), needsUpdate(false)
XMPFileHandler() : parent(0), DefaultCTorPresets {};
- XMPFileHandler (XMPFiles * _parent) : parent(_parent), DefaultCTorPresets {};
+ XMPFileHandler (XMPFiles * _parent) : parent(_parent), DefaultCTorPresets
+ {
+ xmpObj.SetErrorCallback(ErrorCallbackForXMPMeta, &parent->errorCallback);
+ };
virtual ~XMPFileHandler() {}; // ! The specific handler is responsible for tnailInfo.tnailImage.
virtual bool GetFileModDate ( XMP_DateTime * modDate ); // The default implementation is for embedding handlers.
+ virtual void FillMetadataFiles ( std::vector<std::string> * metadataFiles );
+ virtual void FillAssociatedResources ( std::vector<std::string> * resourceList );
+ virtual bool IsMetadataWritable ( );
virtual void CacheFileData() = 0;
virtual void ProcessXMP(); // The default implementation just parses the XMP.