summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHubert Figuière <hub@figuiere.net>2013-06-29 22:05:20 -0400
committerHubert Figuière <hub@figuiere.net>2013-06-29 22:08:01 -0400
commit4652015fe779e12fb06ff8fa56bf70e373cd3894 (patch)
treede8d78fb704c3eb86802c07eace17eaea0f2ddde
parent81a4c6bcb1879cb321246590faca595e9746f8e5 (diff)
Update to XMP SDK CC 2013.06
-rw-r--r--BSD-License.txt2
-rw-r--r--XMP-Toolkit-SDK-Overview.pdfbin1261934 -> 105907 bytes
-rw-r--r--XMPCore/build/CMakeLists.txt252
-rw-r--r--XMPCore/resource/ios/expat_config.h (renamed from build/gcc/expat_config.h)5
-rw-r--r--XMPCore/resource/linux/XMPCore.exp (renamed from build/gcc/XMPCore.exp)5
-rw-r--r--XMPCore/resource/linux/expat_config.h120
-rw-r--r--XMPCore/resource/mac/XMPCore.exp101
-rw-r--r--XMPCore/resource/mac/XMPCore.plist28
-rw-r--r--XMPCore/resource/mac/XMPCorePList.h13
-rw-r--r--XMPCore/resource/mac/expat_config.h (renamed from build/xcode/expat_config.h)0
-rw-r--r--XMPCore/resource/win/XMPCore.def143
-rw-r--r--XMPCore/resource/win/XMPCore.rc58
-rw-r--r--XMPCore/resource/win/expat_config.h (renamed from build/vc/expat_config.h)4
-rw-r--r--XMPCore/source/ExpatAdapter.cpp23
-rw-r--r--XMPCore/source/ParseRDF.cpp704
-rw-r--r--XMPCore/source/WXMPMeta.cpp47
-rw-r--r--XMPCore/source/XMPCore_Impl.cpp6
-rw-r--r--XMPCore/source/XMPCore_Impl.hpp4
-rw-r--r--XMPCore/source/XMPMeta-GetSet.cpp2
-rw-r--r--XMPCore/source/XMPMeta-Parse.cpp459
-rw-r--r--XMPCore/source/XMPMeta-Serialize.cpp2
-rw-r--r--XMPCore/source/XMPMeta.cpp97
-rw-r--r--XMPCore/source/XMPMeta.hpp46
-rw-r--r--XMPCore/source/XMPUtils-FileInfo.cpp2
-rw-r--r--XMPCore/source/XMPUtils.cpp6
-rw-r--r--XMPFiles/build/CMakeLists.txt382
-rw-r--r--XMPFiles/resource/linux/XMPFiles.exp (renamed from build/gcc/XMPFiles.exp)9
-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.cpp267
-rw-r--r--XMPFiles/source/FormatSupport/TIFF_MemoryReader.cpp136
-rw-r--r--XMPFiles/source/FormatSupport/TIFF_Support.cpp19
-rw-r--r--XMPFiles/source/FormatSupport/TIFF_Support.hpp33
-rw-r--r--XMPFiles/source/FormatSupport/WAVE/BEXTMetadata.cpp8
-rw-r--r--XMPFiles/source/FormatSupport/WAVE/CartMetadata.cpp8
-rw-r--r--XMPFiles/source/FormatSupport/WAVE/Cr8rMetadata.cpp8
-rw-r--r--XMPFiles/source/FormatSupport/WAVE/INFOMetadata.cpp2
-rw-r--r--XMPFiles/source/FormatSupport/WAVE/PrmLMetadata.cpp8
-rw-r--r--XMPFiles/source/FormatSupport/WAVE/WAVEBehavior.h8
-rw-r--r--XMPFiles/source/FormatSupport/XDCAM_Support.cpp30
-rw-r--r--XMPFiles/source/HandlerRegistry.cpp25
-rw-r--r--XMPFiles/source/PluginHandler/FileHandler.h8
-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
-rw-r--r--XMPFilesPlugins/PDF_Handler/.DS_Storebin6148 -> 0 bytes
-rw-r--r--XMPFilesPlugins/PDF_Handler/i80386linux/gcc4.1.2/PDF_Handler.xpibin153444 -> 0 bytes
-rw-r--r--XMPFilesPlugins/PDF_Handler/i80386linux/gcc4.1.2_x64/PDF_Handler.xpibin154128 -> 0 bytes
-rw-r--r--XMPFilesPlugins/PDF_Handler/i80386linux/i80386linux/PDF_Handler.resources/MODULE_IDENTIFIER.txt (renamed from XMPFilesPlugins/PDF_Handler/i80386linux/gcc4.1.2/PDF_Handler.resources/MODULE_IDENTIFIER.txt)0
-rw-r--r--XMPFilesPlugins/PDF_Handler/i80386linux/i80386linux/PDF_Handler.resources/XMPPLUGINUIDS.txt (renamed from XMPFilesPlugins/PDF_Handler/i80386linux/gcc4.1.2/PDF_Handler.resources/XMPPLUGINUIDS.txt)1
-rw-r--r--XMPFilesPlugins/PDF_Handler/i80386linux/i80386linux/PDF_Handler.xpibin0 -> 401496 bytes
-rw-r--r--XMPFilesPlugins/PDF_Handler/i80386linux/i80386linux/libMiniPDFL.so (renamed from XMPFilesPlugins/PDF_Handler/i80386linux/gcc4.1.2/libMiniPDFL.so)bin3847272 -> 3847272 bytes
-rw-r--r--XMPFilesPlugins/PDF_Handler/i80386linux/i80386linux_x64/PDF_Handler.resources/MODULE_IDENTIFIER.txt (renamed from XMPFilesPlugins/PDF_Handler/i80386linux/gcc4.1.2_x64/PDF_Handler.resources/MODULE_IDENTIFIER.txt)0
-rw-r--r--XMPFilesPlugins/PDF_Handler/i80386linux/i80386linux_x64/PDF_Handler.resources/XMPPLUGINUIDS.txt (renamed from XMPFilesPlugins/PDF_Handler/i80386linux/gcc4.1.2_x64/PDF_Handler.resources/XMPPLUGINUIDS.txt)1
-rw-r--r--XMPFilesPlugins/PDF_Handler/i80386linux/i80386linux_x64/PDF_Handler.xpibin0 -> 412336 bytes
-rw-r--r--XMPFilesPlugins/PDF_Handler/i80386linux/i80386linux_x64/libMiniPDFL.so (renamed from XMPFilesPlugins/PDF_Handler/i80386linux/gcc4.1.2_x64/libMiniPDFL.so)bin3928328 -> 3940664 bytes
l---------[-rw-r--r--]XMPFilesPlugins/PDF_Handler/macintosh/intel/PDF_Handler.xpi/PDF_Handlerbin154812 -> 28 bytes
l---------XMPFilesPlugins/PDF_Handler/macintosh/intel/PDF_Handler.xpi/Resources1
-rw-r--r--XMPFilesPlugins/PDF_Handler/macintosh/intel/PDF_Handler.xpi/Resources/MODULE_IDENTIFIER.txt1
-rw-r--r--XMPFilesPlugins/PDF_Handler/macintosh/intel/PDF_Handler.xpi/Resources/MiniPDFL.framework/MiniPDFLbin2468320 -> 0 bytes
-rw-r--r--XMPFilesPlugins/PDF_Handler/macintosh/intel/PDF_Handler.xpi/Resources/MiniPDFL.framework/Versions/A/MiniPDFLbin2468320 -> 0 bytes
-rw-r--r--XMPFilesPlugins/PDF_Handler/macintosh/intel/PDF_Handler.xpi/Resources/MiniPDFL.framework/Versions/Current/MiniPDFLbin2468320 -> 0 bytes
-rw-r--r--XMPFilesPlugins/PDF_Handler/macintosh/intel/PDF_Handler.xpi/Resources/XMPPLUGINUIDS.txt30
-rwxr-xr-x[-rw-r--r--]XMPFilesPlugins/PDF_Handler/macintosh/intel/PDF_Handler.xpi/Versions/A/PDF_Handlerbin154812 -> 691984 bytes
-rwxr-xr-xXMPFilesPlugins/PDF_Handler/macintosh/intel/PDF_Handler.xpi/Versions/A/Resources/Info.plist44
-rwxr-xr-x[-rw-r--r--]XMPFilesPlugins/PDF_Handler/macintosh/intel/PDF_Handler.xpi/Versions/A/Resources/MODULE_IDENTIFIER.txt0
l---------[-rw-r--r--]XMPFilesPlugins/PDF_Handler/macintosh/intel/PDF_Handler.xpi/Versions/A/Resources/MiniPDFL.framework/MiniPDFLbin2468320 -> 25 bytes
-rwxr-xr-x[-rw-r--r--]XMPFilesPlugins/PDF_Handler/macintosh/intel/PDF_Handler.xpi/Versions/A/Resources/MiniPDFL.framework/Versions/A/MiniPDFLbin2468320 -> 2264448 bytes
-rwxr-xr-xXMPFilesPlugins/PDF_Handler/macintosh/intel/PDF_Handler.xpi/Versions/A/Resources/MiniPDFL.framework/Versions/A/Resources/Info.plist33
-rwxr-xr-xXMPFilesPlugins/PDF_Handler/macintosh/intel/PDF_Handler.xpi/Versions/A/Resources/MiniPDFL.framework/Versions/A/_CodeSignature/CodeResources34
l---------XMPFilesPlugins/PDF_Handler/macintosh/intel/PDF_Handler.xpi/Versions/A/Resources/MiniPDFL.framework/Versions/Current1
-rw-r--r--XMPFilesPlugins/PDF_Handler/macintosh/intel/PDF_Handler.xpi/Versions/A/Resources/MiniPDFL.framework/Versions/Current/MiniPDFLbin2468320 -> 0 bytes
-rwxr-xr-x[-rw-r--r--]XMPFilesPlugins/PDF_Handler/macintosh/intel/PDF_Handler.xpi/Versions/A/Resources/XMPPLUGINUIDS.txt61
-rwxr-xr-xXMPFilesPlugins/PDF_Handler/macintosh/intel/PDF_Handler.xpi/Versions/A/_CodeSignature/CodeResources54
l---------XMPFilesPlugins/PDF_Handler/macintosh/intel/PDF_Handler.xpi/Versions/Current1
-rw-r--r--XMPFilesPlugins/PDF_Handler/macintosh/intel/PDF_Handler.xpi/Versions/Current/PDF_Handlerbin154812 -> 0 bytes
-rw-r--r--XMPFilesPlugins/PDF_Handler/macintosh/intel/PDF_Handler.xpi/Versions/Current/Resources/MODULE_IDENTIFIER.txt1
-rw-r--r--XMPFilesPlugins/PDF_Handler/macintosh/intel/PDF_Handler.xpi/Versions/Current/Resources/MiniPDFL.framework/MiniPDFLbin2468320 -> 0 bytes
-rw-r--r--XMPFilesPlugins/PDF_Handler/macintosh/intel/PDF_Handler.xpi/Versions/Current/Resources/MiniPDFL.framework/Versions/A/MiniPDFLbin2468320 -> 0 bytes
-rw-r--r--XMPFilesPlugins/PDF_Handler/macintosh/intel/PDF_Handler.xpi/Versions/Current/Resources/MiniPDFL.framework/Versions/Current/MiniPDFLbin2468320 -> 0 bytes
-rw-r--r--XMPFilesPlugins/PDF_Handler/macintosh/intel/PDF_Handler.xpi/Versions/Current/Resources/XMPPLUGINUIDS.txt30
l---------[-rw-r--r--]XMPFilesPlugins/PDF_Handler/macintosh/intel_64/PDF_Handler.xpi/PDF_Handlerbin153376 -> 28 bytes
l---------XMPFilesPlugins/PDF_Handler/macintosh/intel_64/PDF_Handler.xpi/Resources1
-rw-r--r--XMPFilesPlugins/PDF_Handler/macintosh/intel_64/PDF_Handler.xpi/Resources/MODULE_IDENTIFIER.txt1
-rw-r--r--XMPFilesPlugins/PDF_Handler/macintosh/intel_64/PDF_Handler.xpi/Resources/MiniPDFL.framework/MiniPDFLbin2525968 -> 0 bytes
-rw-r--r--XMPFilesPlugins/PDF_Handler/macintosh/intel_64/PDF_Handler.xpi/Resources/MiniPDFL.framework/Versions/A/MiniPDFLbin2525968 -> 0 bytes
-rw-r--r--XMPFilesPlugins/PDF_Handler/macintosh/intel_64/PDF_Handler.xpi/Resources/MiniPDFL.framework/Versions/Current/MiniPDFLbin2525968 -> 0 bytes
-rw-r--r--XMPFilesPlugins/PDF_Handler/macintosh/intel_64/PDF_Handler.xpi/Resources/XMPPLUGINUIDS.txt30
-rwxr-xr-x[-rw-r--r--]XMPFilesPlugins/PDF_Handler/macintosh/intel_64/PDF_Handler.xpi/Versions/A/PDF_Handlerbin153376 -> 732976 bytes
-rwxr-xr-xXMPFilesPlugins/PDF_Handler/macintosh/intel_64/PDF_Handler.xpi/Versions/A/Resources/Info.plist44
-rwxr-xr-x[-rw-r--r--]XMPFilesPlugins/PDF_Handler/macintosh/intel_64/PDF_Handler.xpi/Versions/A/Resources/MODULE_IDENTIFIER.txt0
l---------[-rw-r--r--]XMPFilesPlugins/PDF_Handler/macintosh/intel_64/PDF_Handler.xpi/Versions/A/Resources/MiniPDFL.framework/MiniPDFLbin2525968 -> 25 bytes
-rwxr-xr-x[-rw-r--r--]XMPFilesPlugins/PDF_Handler/macintosh/intel_64/PDF_Handler.xpi/Versions/A/Resources/MiniPDFL.framework/Versions/A/MiniPDFLbin2525968 -> 2294208 bytes
-rwxr-xr-xXMPFilesPlugins/PDF_Handler/macintosh/intel_64/PDF_Handler.xpi/Versions/A/Resources/MiniPDFL.framework/Versions/A/Resources/Info.plist33
-rwxr-xr-xXMPFilesPlugins/PDF_Handler/macintosh/intel_64/PDF_Handler.xpi/Versions/A/Resources/MiniPDFL.framework/Versions/A/_CodeSignature/CodeResources34
l---------XMPFilesPlugins/PDF_Handler/macintosh/intel_64/PDF_Handler.xpi/Versions/A/Resources/MiniPDFL.framework/Versions/Current1
-rw-r--r--XMPFilesPlugins/PDF_Handler/macintosh/intel_64/PDF_Handler.xpi/Versions/A/Resources/MiniPDFL.framework/Versions/Current/MiniPDFLbin2525968 -> 0 bytes
-rwxr-xr-x[-rw-r--r--]XMPFilesPlugins/PDF_Handler/macintosh/intel_64/PDF_Handler.xpi/Versions/A/Resources/XMPPLUGINUIDS.txt61
-rwxr-xr-xXMPFilesPlugins/PDF_Handler/macintosh/intel_64/PDF_Handler.xpi/Versions/A/_CodeSignature/CodeResources54
l---------XMPFilesPlugins/PDF_Handler/macintosh/intel_64/PDF_Handler.xpi/Versions/Current1
-rw-r--r--XMPFilesPlugins/PDF_Handler/macintosh/intel_64/PDF_Handler.xpi/Versions/Current/PDF_Handlerbin153376 -> 0 bytes
-rw-r--r--XMPFilesPlugins/PDF_Handler/macintosh/intel_64/PDF_Handler.xpi/Versions/Current/Resources/MODULE_IDENTIFIER.txt1
-rw-r--r--XMPFilesPlugins/PDF_Handler/macintosh/intel_64/PDF_Handler.xpi/Versions/Current/Resources/MiniPDFL.framework/MiniPDFLbin2525968 -> 0 bytes
-rw-r--r--XMPFilesPlugins/PDF_Handler/macintosh/intel_64/PDF_Handler.xpi/Versions/Current/Resources/MiniPDFL.framework/Versions/A/MiniPDFLbin2525968 -> 0 bytes
-rw-r--r--XMPFilesPlugins/PDF_Handler/macintosh/intel_64/PDF_Handler.xpi/Versions/Current/Resources/MiniPDFL.framework/Versions/Current/MiniPDFLbin2525968 -> 0 bytes
-rw-r--r--XMPFilesPlugins/PDF_Handler/macintosh/intel_64/PDF_Handler.xpi/Versions/Current/Resources/XMPPLUGINUIDS.txt30
-rw-r--r--XMPFilesPlugins/PDF_Handler/windows/windows/MiniPDFL.dllbin2810368 -> 2816904 bytes
-rw-r--r--XMPFilesPlugins/PDF_Handler/windows/windows/PDF_Handler.xpibin83456 -> 215944 bytes
-rw-r--r--XMPFilesPlugins/PDF_Handler/windows/windows_x64/MiniPDFL.dllbin4060160 -> 4066696 bytes
-rw-r--r--XMPFilesPlugins/PDF_Handler/windows/windows_x64/PDF_Handler.xpibin128000 -> 299400 bytes
-rw-r--r--XMPFilesPlugins/PluginTemplate/build/CMakeLists.txt185
-rwxr-xr-xXMPFilesPlugins/PluginTemplate/build/GeneratePluginTemplate_mac.sh81
-rwxr-xr-xXMPFilesPlugins/PluginTemplate/build/GeneratePluginTemplate_win.bat100
-rwxr-xr-xXMPFilesPlugins/PluginTemplate/build/Makefile65
-rw-r--r--XMPFilesPlugins/PluginTemplate/build/README.txt20
-rw-r--r--XMPFilesPlugins/PluginTemplate/build/gcc/PluginCommon.mak271
-rw-r--r--XMPFilesPlugins/PluginTemplate/build/gcc/PluginTemplate.mak21
-rw-r--r--XMPFilesPlugins/PluginTemplate/build/vc/PluginTemplate-32.rc8
-rw-r--r--XMPFilesPlugins/PluginTemplate/build/vc/PluginTemplate-64.rc8
-rw-r--r--XMPFilesPlugins/PluginTemplate/build/vc/PluginTemplate.vcxproj282
-rw-r--r--XMPFilesPlugins/PluginTemplate/build/vc/PluginTemplate.vcxproj.filters81
-rw-r--r--XMPFilesPlugins/PluginTemplate/build/xcode/Debug32.xcconfig12
-rw-r--r--XMPFilesPlugins/PluginTemplate/build/xcode/Debug64.xcconfig12
-rw-r--r--XMPFilesPlugins/PluginTemplate/build/xcode/PluginCommon.xcconfig87
-rw-r--r--XMPFilesPlugins/PluginTemplate/build/xcode/PluginTemplate.xcodeproj/project.pbxproj524
-rw-r--r--XMPFilesPlugins/PluginTemplate/build/xcode/PluginTemplate.xcodeproj/project.xcworkspace/contents.xcworkspacedata7
-rw-r--r--XMPFilesPlugins/PluginTemplate/build/xcode/PluginTemplate.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings8
-rw-r--r--XMPFilesPlugins/PluginTemplate/build/xcode/PluginTemplate.xcodeproj/project.xcworkspace/xcuserdata/jehrlich.xcuserdatad/WorkspaceSettings.xcsettings8
-rw-r--r--XMPFilesPlugins/PluginTemplate/build/xcode/PluginTemplate.xcodeproj/xcshareddata/xcschemes/PluginTemplate Debug32.xcscheme57
-rw-r--r--XMPFilesPlugins/PluginTemplate/build/xcode/PluginTemplate.xcodeproj/xcshareddata/xcschemes/PluginTemplate Debug64.xcscheme57
-rw-r--r--XMPFilesPlugins/PluginTemplate/build/xcode/PluginTemplate.xcodeproj/xcshareddata/xcschemes/PluginTemplate Release32.xcscheme57
-rw-r--r--XMPFilesPlugins/PluginTemplate/build/xcode/PluginTemplate.xcodeproj/xcshareddata/xcschemes/PluginTemplate Release64.xcscheme57
-rw-r--r--XMPFilesPlugins/PluginTemplate/build/xcode/PluginTemplateCommon.xcconfig5
-rw-r--r--XMPFilesPlugins/PluginTemplate/build/xcode/Release32.xcconfig12
-rw-r--r--XMPFilesPlugins/PluginTemplate/build/xcode/Release64.xcconfig12
-rw-r--r--XMPFilesPlugins/PluginTemplate/resource/win/PluginTemplate-32.rc8
-rw-r--r--XMPFilesPlugins/PluginTemplate/resource/win/PluginTemplate-64.rc8
-rw-r--r--XMPFilesPlugins/PluginTemplate/source/Template_Handler.cpp15
-rw-r--r--XMPFilesPlugins/api/source/HostAPI.h86
-rw-r--r--XMPFilesPlugins/api/source/HostAPIAccess.cpp133
-rw-r--r--XMPFilesPlugins/api/source/HostAPIAccess.h29
-rw-r--r--XMPFilesPlugins/api/source/PluginAPIImpl.cpp222
-rw-r--r--XMPFilesPlugins/api/source/PluginBase.cpp138
-rw-r--r--XMPFilesPlugins/api/source/PluginBase.h83
-rw-r--r--XMPFilesPlugins/api/source/PluginConst.h41
-rw-r--r--XMPFilesPlugins/api/source/PluginHandler.h112
-rw-r--r--XMPFilesPlugins/api/source/PluginRegistry.h10
-rw-r--r--build/CMakeLists.txt96
-rwxr-xr-xbuild/GenerateXMPToolkitSDK_mac.sh117
-rwxr-xr-xbuild/GenerateXMPToolkitSDK_win.bat108
-rwxr-xr-xbuild/Makefile89
-rw-r--r--build/README.txt23
-rw-r--r--build/XMP_BuildInfo.h4
-rw-r--r--build/XMP_Config.cmake521
-rw-r--r--build/XMP_Ios.cmake109
-rw-r--r--build/XMP_Linux.cmake143
-rw-r--r--build/XMP_Mac.cmake129
-rw-r--r--build/XMP_Win.cmake127
-rw-r--r--build/cmake.bat29
-rw-r--r--build/cmake.command30
-rw-r--r--build/cmake_all.bat35
-rw-r--r--build/gcc/XMPCore-acc.exp98
-rw-r--r--build/gcc/XMPCore-vacpp.exp98
-rw-r--r--build/gcc/XMPCore.mak310
-rw-r--r--build/gcc/XMPFiles-acc.exp18
-rw-r--r--build/gcc/XMPFiles-vacpp.exp17
-rw-r--r--build/gcc/XMPFiles.mak409
-rw-r--r--build/gcc/XMPToolkit.mak109
-rw-r--r--build/gcc/usage_examples.txt29
-rwxr-xr-xbuild/shared/CMakeUtils.bat134
-rw-r--r--build/shared/CMakeUtils.sh124
-rw-r--r--build/shared/SharedConfig.cmake64
-rw-r--r--build/shared/SharedConfig_Common.cmake223
-rw-r--r--build/shared/SharedConfig_Ios.cmake233
-rw-r--r--build/shared/SharedConfig_Linux.cmake152
-rw-r--r--build/shared/SharedConfig_Mac.cmake246
-rw-r--r--build/shared/SharedConfig_Win.cmake152
-rw-r--r--build/shared/ToolchainGCC.cmake57
-rw-r--r--build/shared/ToolchainLLVM.cmake18
-rw-r--r--build/shared/Toolchain_ios.cmake145
-rw-r--r--build/vc/XMPCore.vcxproj247
-rw-r--r--build/vc/XMPCore.vcxproj.filters91
-rw-r--r--build/vc/XMPFiles.vcxproj309
-rw-r--r--build/vc/XMPFiles.vcxproj.filters317
-rw-r--r--build/vc/XMPToolkitSDK.sln35
-rw-r--r--build/xcode/XMPToolkitSDK-Common.xcconfig64
-rw-r--r--build/xcode/XMPToolkitSDK-Debug.xcconfig12
-rw-r--r--build/xcode/XMPToolkitSDK-Release.xcconfig12
-rw-r--r--build/xcode/XMPToolkitSDK.xcodeproj/project.pbxproj1266
-rw-r--r--build/xcode/XMPToolkitSDK.xcodeproj/project.xcworkspace/contents.xcworkspacedata7
-rw-r--r--build/xcode/XMPToolkitSDK.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings8
-rw-r--r--build/xcode/XMPToolkitSDK.xcodeproj/xcshareddata/xcschemes/Build All.xcscheme99
-rw-r--r--build/xcode/XMPToolkitSDK.xcodeproj/xcshareddata/xcschemes/XMPCore Debug.xcscheme57
-rw-r--r--build/xcode/XMPToolkitSDK.xcodeproj/xcshareddata/xcschemes/XMPCore Release.xcscheme57
-rw-r--r--build/xcode/XMPToolkitSDK.xcodeproj/xcshareddata/xcschemes/XMPFiles Debug.xcscheme57
-rw-r--r--build/xcode/XMPToolkitSDK.xcodeproj/xcshareddata/xcschemes/XMPFiles Release.xcscheme57
-rw-r--r--docs/API/TXMPFiles_8hpp.html4
-rw-r--r--docs/API/TXMPFiles_8hpp_source.html237
-rw-r--r--docs/API/TXMPFiles_8incl__cpp.html4
-rw-r--r--docs/API/TXMPIterator_8hpp.html4
-rw-r--r--docs/API/TXMPIterator_8hpp_source.html4
-rw-r--r--docs/API/TXMPIterator_8incl__cpp.html4
-rw-r--r--docs/API/TXMPMeta_8hpp.html4
-rw-r--r--docs/API/TXMPMeta_8hpp_source.html53
-rw-r--r--docs/API/TXMPMeta_8incl__cpp.html4
-rw-r--r--docs/API/TXMPUtils_8hpp.html4
-rw-r--r--docs/API/TXMPUtils_8hpp_source.html4
-rw-r--r--docs/API/TXMPUtils_8incl__cpp.html4
-rw-r--r--docs/API/XMP_8incl__cpp.html5
-rw-r--r--docs/API/XMP_8incl__cpp__incl.pngbin3656 -> 4118 bytes
-rw-r--r--docs/API/XMP__Const_8h.html584
-rw-r--r--docs/API/XMP__Const_8h__incl.pngbin3544 -> 4011 bytes
-rw-r--r--docs/API/XMP__Const_8h_source.html1390
-rw-r--r--docs/API/annotated.html4
-rw-r--r--docs/API/classTXMPFiles-members.html11
-rw-r--r--docs/API/classTXMPFiles.html376
-rw-r--r--docs/API/classTXMPIterator-members.html4
-rw-r--r--docs/API/classTXMPIterator.html4
-rw-r--r--docs/API/classTXMPMeta-members.html7
-rw-r--r--docs/API/classTXMPMeta.html133
-rw-r--r--docs/API/classTXMPUtils-members.html4
-rw-r--r--docs/API/classTXMPUtils.html4
-rw-r--r--docs/API/classXMP__Error-members.html7
-rw-r--r--docs/API/classXMP__Error.html17
-rw-r--r--docs/API/classes.html4
-rw-r--r--docs/API/files.html4
-rw-r--r--docs/API/functions.html47
-rw-r--r--docs/API/functions_func.html42
-rw-r--r--docs/API/functions_vars.html7
-rw-r--r--docs/API/globals.html128
-rw-r--r--docs/API/globals_0x73.html4
-rw-r--r--docs/API/globals_0x78.html19
-rw-r--r--docs/API/globals_defs.html4
-rw-r--r--docs/API/globals_enum.html4
-rw-r--r--docs/API/globals_eval.html128
-rw-r--r--docs/API/globals_type.html19
-rw-r--r--docs/API/graph_legend.html4
-rw-r--r--docs/API/index.html8
-rw-r--r--docs/API/structXMP__DateTime-members.html4
-rw-r--r--docs/API/structXMP__DateTime.html4
-rw-r--r--docs/API/structXMP__PacketInfo-members.html4
-rw-r--r--docs/API/structXMP__PacketInfo.html4
-rw-r--r--docs/API/structXMP__VersionInfo-members.html4
-rw-r--r--docs/API/structXMP__VersionInfo.html4
-rw-r--r--docs/XMPFilesPlugin.pdfbin278138 -> 0 bytes
-rw-r--r--docs/XMPFilesPluginSDK.pdfbin0 -> 212721 bytes
-rw-r--r--docs/XMPProgrammersGuide.pdfbin1055704 -> 582109 bytes
-rw-r--r--docs/XMPSpecificationPart3.pdfbin773113 -> 497375 bytes
-rw-r--r--public/include/TXMPFiles.hpp313
-rw-r--r--public/include/TXMPMeta.hpp61
-rw-r--r--public/include/XMP_Const.h199
-rw-r--r--public/include/XMP_Environment.h38
-rw-r--r--public/include/XMP_Version.h12
-rw-r--r--public/include/client-glue/TXMPFiles.incl_cpp107
-rw-r--r--public/include/client-glue/TXMPMeta.incl_cpp31
-rw-r--r--public/include/client-glue/TXMPUtils.incl_cpp7
-rw-r--r--public/include/client-glue/WXMPFiles.hpp95
-rw-r--r--public/include/client-glue/WXMPMeta.hpp47
-rw-r--r--public/include/client-glue/WXMP_Common.hpp1
-rwxr-xr-xsamples/build/GenerateSamples_mac.sh179
-rwxr-xr-xsamples/build/GenerateSamples_win.bat113
-rwxr-xr-xsamples/build/Makefile88
-rw-r--r--samples/build/cmake/CMakeLists.txt65
-rw-r--r--samples/build/cmake/CustomSchema/CMakeLists.txt50
-rw-r--r--samples/build/cmake/DumpFile/CMakeLists.txt53
-rw-r--r--samples/build/cmake/DumpMainXMP/CMakeLists.txt53
-rw-r--r--samples/build/cmake/DumpScannedXMP/CMakeLists.txt53
-rw-r--r--samples/build/cmake/ModifyingXMP/CMakeLists.txt53
-rw-r--r--samples/build/cmake/ReadingXMP/CMakeLists.txt53
-rw-r--r--samples/build/cmake/SharedConfig.cmake164
-rw-r--r--samples/build/cmake/XMPCommand/CMakeLists.txt53
-rw-r--r--samples/build/cmake/XMPCoreCoverage/CMakeLists.txt53
-rw-r--r--samples/build/cmake/XMPFilesCoverage/CMakeLists.txt53
-rw-r--r--samples/build/cmake/XMPIterations/CMakeLists.txt53
-rw-r--r--samples/build/gcc/Sample.mak319
-rw-r--r--samples/build/readme.txt20
-rw-r--r--samples/build/vc/CustomSchema.vcxproj179
-rw-r--r--samples/build/vc/CustomSchema.vcxproj.filters14
-rw-r--r--samples/build/vc/DumpFile.vcxproj189
-rw-r--r--samples/build/vc/DumpFile.vcxproj.filters31
-rw-r--r--samples/build/vc/DumpMainXMP.vcxproj179
-rw-r--r--samples/build/vc/DumpMainXMP.vcxproj.filters14
-rw-r--r--samples/build/vc/DumpScannedXMP.vcxproj180
-rw-r--r--samples/build/vc/DumpScannedXMP.vcxproj.filters14
-rw-r--r--samples/build/vc/ModifyingXMP.vcxproj178
-rw-r--r--samples/build/vc/ModifyingXMP.vcxproj.filters14
-rw-r--r--samples/build/vc/ReadingXMP.vcxproj179
-rw-r--r--samples/build/vc/ReadingXMP.vcxproj.filters14
-rw-r--r--samples/build/vc/XMP-Toolkit-SDK-Samples.sln115
-rw-r--r--samples/build/vc/XMPCommand.vcxproj188
-rw-r--r--samples/build/vc/XMPCommand.vcxproj.filters31
-rw-r--r--samples/build/vc/XMPCoreCoverage.vcxproj179
-rw-r--r--samples/build/vc/XMPCoreCoverage.vcxproj.filters14
-rw-r--r--samples/build/vc/XMPFilesCoverage.vcxproj179
-rw-r--r--samples/build/vc/XMPFilesCoverage.vcxproj.filters14
-rw-r--r--samples/build/vc/XMPIterations.vcxproj179
-rw-r--r--samples/build/vc/XMPIterations.vcxproj.filters14
-rw-r--r--samples/build/xcode/XMPSamples-Common.xcconfig43
-rw-r--r--samples/build/xcode/XMPSamples-Debug.xcconfig15
-rw-r--r--samples/build/xcode/XMPSamples-Release.xcconfig15
-rw-r--r--samples/build/xcode/XMPSamples.xcodeproj/project.pbxproj1332
-rw-r--r--samples/build/xcode/XMPSamples.xcodeproj/project.xcworkspace/contents.xcworkspacedata7
-rw-r--r--samples/build/xcode/XMPSamples.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings8
-rw-r--r--samples/build/xcode/XMPSamples.xcodeproj/xcshareddata/xcschemes/Build All.xcscheme323
-rw-r--r--samples/build/xcode/XMPSamples.xcodeproj/xcshareddata/xcschemes/CustomSchema Debug.xcscheme84
-rw-r--r--samples/build/xcode/XMPSamples.xcodeproj/xcshareddata/xcschemes/CustomSchema Release.xcscheme84
-rw-r--r--samples/build/xcode/XMPSamples.xcodeproj/xcshareddata/xcschemes/DumpMainXMP Debug.xcscheme84
-rw-r--r--samples/build/xcode/XMPSamples.xcodeproj/xcshareddata/xcschemes/DumpMainXMP Release.xcscheme84
-rw-r--r--samples/build/xcode/XMPSamples.xcodeproj/xcshareddata/xcschemes/DumpScannedXMP Debug.xcscheme84
-rw-r--r--samples/build/xcode/XMPSamples.xcodeproj/xcshareddata/xcschemes/DumpScannedXMP Release.xcscheme84
-rw-r--r--samples/build/xcode/XMPSamples.xcodeproj/xcshareddata/xcschemes/Dumpfile Debug.xcscheme84
-rw-r--r--samples/build/xcode/XMPSamples.xcodeproj/xcshareddata/xcschemes/Dumpfile Release.xcscheme84
-rw-r--r--samples/build/xcode/XMPSamples.xcodeproj/xcshareddata/xcschemes/ModifyingXMP Debug.xcscheme84
-rw-r--r--samples/build/xcode/XMPSamples.xcodeproj/xcshareddata/xcschemes/ModifyingXMP Release.xcscheme84
-rw-r--r--samples/build/xcode/XMPSamples.xcodeproj/xcshareddata/xcschemes/ReadingXMP Debug.xcscheme84
-rw-r--r--samples/build/xcode/XMPSamples.xcodeproj/xcshareddata/xcschemes/ReadingXMP Release.xcscheme84
-rw-r--r--samples/build/xcode/XMPSamples.xcodeproj/xcshareddata/xcschemes/XMPCommand Debug.xcscheme84
-rw-r--r--samples/build/xcode/XMPSamples.xcodeproj/xcshareddata/xcschemes/XMPCommand Release.xcscheme84
-rw-r--r--samples/build/xcode/XMPSamples.xcodeproj/xcshareddata/xcschemes/XMPCoreCoverage Debug.xcscheme84
-rw-r--r--samples/build/xcode/XMPSamples.xcodeproj/xcshareddata/xcschemes/XMPCoreCoverage Release.xcscheme84
-rw-r--r--samples/build/xcode/XMPSamples.xcodeproj/xcshareddata/xcschemes/XMPFilesCoverage Debug.xcscheme84
-rw-r--r--samples/build/xcode/XMPSamples.xcodeproj/xcshareddata/xcschemes/XMPFilesCoverage Release.xcscheme84
-rw-r--r--samples/build/xcode/XMPSamples.xcodeproj/xcshareddata/xcschemes/XMPIterations Debug.xcscheme84
-rw-r--r--samples/build/xcode/XMPSamples.xcodeproj/xcshareddata/xcschemes/XMPIterations Release.xcscheme84
-rw-r--r--samples/source/CustomSchema.cpp3
-rw-r--r--samples/source/DumpMainXMP.cpp10
-rw-r--r--samples/source/DumpScannedXMP.cpp18
-rw-r--r--samples/source/ModifyingXMP.cpp3
-rw-r--r--samples/source/ReadingXMP.cpp3
-rw-r--r--samples/source/UnicodeCorrectness.cpp2813
-rw-r--r--samples/source/UnicodeParseSerialize.cpp510
-rw-r--r--samples/source/UnicodePerformance.cpp308
-rw-r--r--samples/source/XMPCoreCoverage.cpp10
-rw-r--r--samples/source/XMPFilesCoverage.cpp6
-rw-r--r--samples/source/XMPIterations.cpp241
-rw-r--r--samples/source/common/DumpFile.cpp177
-rw-r--r--samples/source/common/DumpFile.h2
-rw-r--r--samples/source/common/LargeFileAccess.cpp4
-rw-r--r--samples/source/common/TagTree.cpp18
-rw-r--r--samples/source/common/globals.h6
-rw-r--r--samples/source/dumpfile/main.cpp2
-rw-r--r--samples/source/xmpcommand/Actions.cpp6
-rw-r--r--samples/source/xmpcommand/XMPCommand.cpp4
-rw-r--r--source/EndianUtils.hpp135
-rw-r--r--source/Host_IO-Mac.cpp526
-rw-r--r--source/Host_IO-POSIX.cpp127
-rw-r--r--source/Host_IO-UNIX.cpp454
-rw-r--r--source/Host_IO-Win.cpp372
-rw-r--r--source/Host_IO.hpp11
-rw-r--r--source/IOUtils.cpp118
-rw-r--r--source/IOUtils.hpp31
-rw-r--r--source/PerfUtils.cpp147
-rw-r--r--source/PerfUtils.hpp44
-rw-r--r--source/UnicodeConversions.cpp30
-rw-r--r--source/UnicodeConversions.hpp13
-rw-r--r--source/XIO.cpp2
-rw-r--r--source/XIO.hpp6
-rw-r--r--source/XMLParserAdapter.hpp20
-rw-r--r--source/XMPFiles_IO.cpp132
-rw-r--r--source/XMPFiles_IO.hpp79
-rw-r--r--source/XMP_LibUtils.cpp86
-rw-r--r--source/XMP_LibUtils.hpp58
-rw-r--r--source/XMP_ProgressTracker.cpp163
-rw-r--r--source/XMP_ProgressTracker.hpp69
-rw-r--r--third-party/expat/ReadMe.txt114
-rwxr-xr-x[-rw-r--r--]third-party/zlib/ReadMe.txt26
-rw-r--r--tools/cmake/ReadMe.txt17
440 files changed, 24318 insertions, 16479 deletions
diff --git a/BSD-License.txt b/BSD-License.txt
index 43029dd..0e6675f 100644
--- a/BSD-License.txt
+++ b/BSD-License.txt
@@ -1,6 +1,6 @@
The BSD License
-Copyright (c) 1999 - 2010, Adobe Systems Incorporated
+Copyright (c) 1999 - 2013, Adobe Systems Incorporated
All rights reserved.
Redistribution and use in source and binary forms, with or
diff --git a/XMP-Toolkit-SDK-Overview.pdf b/XMP-Toolkit-SDK-Overview.pdf
index 46bca14..9ea53c9 100644
--- a/XMP-Toolkit-SDK-Overview.pdf
+++ b/XMP-Toolkit-SDK-Overview.pdf
Binary files differ
diff --git a/XMPCore/build/CMakeLists.txt b/XMPCore/build/CMakeLists.txt
new file mode 100644
index 0000000..45dce9c
--- /dev/null
+++ b/XMPCore/build/CMakeLists.txt
@@ -0,0 +1,252 @@
+# =================================================================================================
+# 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 XMPCore)
+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)
+
+# ==============================================================================
+# Project specific config starts here
+# ==============================================================================
+if(UNIX)
+ if(APPLE)
+ # OSX -------------------------------------------
+ if(XMP_BUILD_STATIC)
+ set(XMPCORE_LIB "XMPCoreStatic")
+ else(XMP_BUILD_STATIC)
+ set(XMPCORE_LIB "XMPCore")
+ endif()
+ else(APPLE)
+ # Linux -------------------------------------------
+ if(XMP_BUILD_STATIC)
+ set(XMPCORE_LIB "staticXMPCore")
+ else(XMP_BUILD_STATIC)
+ set(XMPCORE_LIB "XMPCore")
+ endif()
+ endif(APPLE)
+else(UNIX)
+ if(WIN32)
+ # Windows -------------------------------------------
+ if(XMP_BUILD_STATIC)
+ set(XMPCORE_LIB "XMPCoreStatic")
+ else(XMP_BUILD_STATIC)
+ set(XMPCORE_LIB "XMPCore")
+ 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
+# ==============================================================================
+
+list (APPEND INTERNAL_HEADER_FILES
+ ${XMPROOT_DIR}/source/ExpatAdapter.hpp
+ ${XMPROOT_DIR}/source/UnicodeConversions.hpp
+ ${XMPROOT_DIR}/source/UnicodeInlines.incl_cpp
+ ${XMPROOT_DIR}/source/XMLParserAdapter.hpp
+ ${XMPROOT_DIR}/build/XMP_BuildInfo.h
+ ${SOURCE_ROOT}/XMPCore_Impl.hpp
+ ${SOURCE_ROOT}/XMPIterator.hpp
+ ${SOURCE_ROOT}/XMPMeta.hpp
+ ${SOURCE_ROOT}/XMPUtils.hpp
+ )
+source_group("Header Files\\Internal Headers" FILES ${INTERNAL_HEADER_FILES})
+
+file (GLOB PUBLIC_CLIENTGLUE_HEADER_FILES ${XMPROOT_DIR}/public/include/client-glue/*.*)
+list (REMOVE_ITEM PUBLIC_CLIENTGLUE_HEADER_FILES
+ ${XMPROOT_DIR}/public/include/client-glue/TXMPFiles.incl_cpp
+ ${XMPROOT_DIR}/public/include/client-glue/WXMPFiles.hpp
+ )
+source_group("Header Files\\Public Headers\\Client Glue" FILES ${PUBLIC_CLIENTGLUE_HEADER_FILES})
+
+file (GLOB PUBLIC_HEADER_FILES ${XMPROOT_DIR}/public/include/*.*)
+list (REMOVE_ITEM PUBLIC_HEADER_FILES
+ ${XMPROOT_DIR}/public/include/TXMPFiles.hpp
+ ${XMPROOT_DIR}/public/include/XMP_IO.hpp
+ )
+source_group("Header Files\\Public Headers" FILES ${PUBLIC_HEADER_FILES})
+
+file (GLOB ABIWRAPPER_SOURCE_FILES ${SOURCE_ROOT}/WXMP*.cpp)
+source_group("Source Files\\ABI Wrappers" FILES ${ABIWRAPPER_SOURCE_FILES})
+
+#XMP Core classes
+file (GLOB CORECLASS_SOURCE_FILES ${SOURCE_ROOT}/XMP*.cpp)
+list (REMOVE_ITEM CORECLASS_SOURCE_FILES ${SOURCE_ROOT}/XMPCore_Impl.cpp)
+source_group("Source Files\\Core Classes" FILES ${CORECLASS_SOURCE_FILES})
+
+file (GLOB THIRDPARTY_SOURCE_FILES ${XMPROOT_DIR}/third-party/zuid/interfaces/*.cpp)
+list (APPEND THIRDPARTY_SOURCE_FILES
+ ${PROJECT_ROOT}/../../third-party/expat/lib/xmlparse.c
+ ${PROJECT_ROOT}/../../third-party/expat/lib/xmlrole.c
+ ${PROJECT_ROOT}/../../third-party/expat/lib/xmltok.c
+ )
+list (APPEND THIRDPARTY_SOURCE_FILES ${RESOURCE_ROOT}/${XMP_PLATFORM_SHORT}/expat_config.h)
+source_group("Source Files\\Third Party" FILES ${THIRDPARTY_SOURCE_FILES})
+
+list (APPEND UTILITY_SOURCE_FILES
+ ${SOURCE_ROOT}/ExpatAdapter.cpp
+ ${SOURCE_ROOT}/ParseRDF.cpp
+ ${SOURCE_ROOT}/XMPCore_Impl.cpp
+ ${XMPROOT_DIR}/source/UnicodeConversions.cpp
+ ${XMPROOT_DIR}/source/XML_Node.cpp
+ ${XMPROOT_DIR}/source/XMP_LibUtils.cpp
+ )
+source_group("Source Files\\Utilities" FILES ${UTILITY_SOURCE_FILES})
+
+#resource files
+file (GLOB RESOURCE_FILES ${RESOURCE_ROOT}/${XMP_PLATFORM_SHORT}/*.*)
+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})
+
+list(APPEND SOURCE_FILES
+ ${PUBLIC_CLIENTGLUE_HEADER_FILES}
+ ${PUBLIC_HEADER_FILES}
+ ${INTERNAL_HEADER_FILES}
+ ${ABIWRAPPER_SOURCE_FILES}
+ ${ABIWRAPPER_SOURCE_FILES}
+ ${CORECLASS_SOURCE_FILES}
+ ${THIRDPARTY_SOURCE_FILES}
+ ${UTILITY_SOURCE_FILES}
+ ${RESOURCE_FILES}
+ )
+# include directories
+include_directories(${XMPROOT_DIR})
+include_directories(${XMPROOT_DIR}/public/include)
+include_directories(${XMPROOT_DIR}/third-party/expat/public/lib)
+include_directories(${RESOURCE_ROOT}/${XMP_PLATFORM_SHORT})
+
+
+
+# ==============================================================================
+# Define what to do, lib, exe, etc
+if (UNIX AND APPLE AND NOT ${XMP_BUILD_STATIC})
+ # preprocess Info.plist
+ #if (${CMAKE_BUILD_TYPE} strequal "Debug")
+ if((${CMAKE_BUILD_TYPE} MATCHES "Debug") OR (${CMAKE_BUILD_TYPE} MATCHES "debug"))
+ set (INFO_PLIST_FLAGS "-DDEBUG=1")
+ else()
+ set (INFO_PLIST_FLAGS "-DNDEBUG=1")
+ endif()
+
+ 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")
+else ()
+ set(DEPENDENCY_LIST "")
+endif()
+
+AddLibraryAndDependencies(${TARGET_NAME} ${XMP_BUILD_STATIC} YES "SHARED" SOURCE_FILES DEPENDENCY_LIST)
+
+#set the output name
+set_target_properties(${TARGET_NAME} PROPERTIES OUTPUT_NAME ${XMPCORE_LIB})
+
+# ==============================================================================
+# Link dependencies
+set(FRAMEWORK_LIST "Mac:CoreFoundation" "Mac:CoreServices")
+AddMacFramework(${TARGET_NAME} FRAMEWORK_LIST)
+
+if(WIN32)
+ SetWinLinkFlags(${TARGET_NAME} "rpcrt4.lib" "${XMPCORE_LIB}")
+else(WIN32)
+ if(UNIX AND 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}/XMPCore.exp\"" "${XMPCORE_LIB}")
+ endif()
+endif()
+
+# ==============================================================================
+# Define output
+SetOutputPath(${OUTPUT_DIR} 0)
+
+# ==============================================================================
+# Post build
+# ==============================================================================
+if(UNIX)
+ if (NOT APPLE)
+ if (${XMP_BUILD_STATIC})
+ add_custom_command (TARGET ${TARGET_NAME}
+ POST_BUILD
+ COMMAND mv ${OUTPUT_DIR}/lib${XMPCORE_LIB}.a ${OUTPUT_DIR}/${XMPCORE_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${XMPCORE_LIB}.so
+ )
+ else()
+ add_custom_command (TARGET ${TARGET_NAME}
+ POST_BUILD
+ COMMAND strip ${OUTPUT_DIR}/lib${XMPCORE_LIB}.so
+ COMMAND ls -l ${OUTPUT_DIR}/lib${XMPCORE_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/build/gcc/expat_config.h b/XMPCore/resource/ios/expat_config.h
index 53bcd9a..7d147e7 100644
--- a/build/gcc/expat_config.h
+++ b/XMPCore/resource/ios/expat_config.h
@@ -27,6 +27,11 @@
/* #undef HAVE_MMAP */
/* #undef HAVE_STDINT_H */
#define const
+#elif TargetOS == ia64hpux
+ #define WORDS_BIGENDIAN 1
+ /* #undef HAVE_MMAP */
+ /* #undef HAVE_STDINT_H */
+ #define const
#else
#error "Unknown target OS"
#endif
diff --git a/build/gcc/XMPCore.exp b/XMPCore/resource/linux/XMPCore.exp
index 901f3de..d1c7b1b 100644
--- a/build/gcc/XMPCore.exp
+++ b/XMPCore/resource/linux/XMPCore.exp
@@ -59,6 +59,10 @@ global:
WXMPMeta_ParseFromBuffer_1;
WXMPMeta_SerializeToBuffer_1;
+ WXMPMeta_SetDefaultErrorCallback_1;
+ WXMPMeta_SetErrorCallback_1;
+ WXMPMeta_ResetErrorCallbackLimit_1;
+
WXMPIterator_PropCTor_1;
WXMPIterator_TableCTor_1;
WXMPIterator_IncrementRefCount_1;
@@ -98,7 +102,6 @@ global:
WXMPUtils_DuplicateSubtree_1;
XMP_NewExpatAdapter;
- XMP_GetAssertNotify;
local:
diff --git a/XMPCore/resource/linux/expat_config.h b/XMPCore/resource/linux/expat_config.h
new file mode 100644
index 0000000..7d147e7
--- /dev/null
+++ b/XMPCore/resource/linux/expat_config.h
@@ -0,0 +1,120 @@
+/* expat_config.h. Generated by configure. */
+/* expat_config.h.in. Generated from configure.in by autoheader. */
+
+/* *** Tweaked by hand for generic UNIX builds **** */
+
+#ifdef WORDS_BIGENDIAN
+ #error "WORDS_BIGENDIAN must be initially undefined"
+#endif
+
+#if TargetOS == i80386linux
+ /* #undef WORDS_BIGENDIAN */
+ #define HAVE_MMAP 1
+ #define HAVE_STDINT_H 1
+ /* #undef const */
+#elif TargetOS == sparcsolaris
+ #define WORDS_BIGENDIAN 1
+ #define HAVE_MMAP 1
+ /* #undef HAVE_STDINT_H */
+ /* #undef const */
+#elif TargetOS == rs60000aix
+ #define WORDS_BIGENDIAN 1
+ /* #undef HAVE_MMAP */
+ /* #undef HAVE_STDINT_H */
+ /* #undef const */
+#elif TargetOS == hppahpux
+ #define WORDS_BIGENDIAN 1
+ /* #undef HAVE_MMAP */
+ /* #undef HAVE_STDINT_H */
+ #define const
+#elif TargetOS == ia64hpux
+ #define WORDS_BIGENDIAN 1
+ /* #undef HAVE_MMAP */
+ /* #undef HAVE_STDINT_H */
+ #define const
+#else
+ #error "Unknown target OS"
+#endif
+
+/* 1234 = LIL_ENDIAN, 4321 = BIGENDIAN */
+#if WORDS_BIGENDIAN
+ #define BYTEORDER 4321
+#else
+ #define BYTEORDER 1234
+#endif
+
+/* Define to 1 if you have the `bcopy' function. */
+#define HAVE_BCOPY 1
+
+/* Define to 1 if you have the <check.h> header file. */
+/* #undef HAVE_CHECK_H */
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#define HAVE_DLFCN_H 1
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#define HAVE_FCNTL_H 1
+
+/* Define to 1 if you have the `getpagesize' function. */
+#define HAVE_GETPAGESIZE 1
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the `memmove' function. */
+#define HAVE_MEMMOVE 1
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT "expat-bugs@mail.libexpat.org"
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "expat"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "expat 1.95.8"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "expat"
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "1.95.8"
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Define to specify how much context to retain around the current parse
+ point. */
+#define XML_CONTEXT_BYTES 1024
+
+/* Define to make parameter entity parsing functionality available. */
+/* #define XML_DTD 1 */
+
+/* Define to make XML Namespaces functionality available. */
+#define XML_NS 1
+
+/* Define to `long' if <sys/types.h> does not define. */
+/* #undef off_t */
+
+/* Define to `unsigned' if <sys/types.h> does not define. */
+/* #undef size_t */
diff --git a/XMPCore/resource/mac/XMPCore.exp b/XMPCore/resource/mac/XMPCore.exp
new file mode 100644
index 0000000..fad61af
--- /dev/null
+++ b/XMPCore/resource/mac/XMPCore.exp
@@ -0,0 +1,101 @@
+_WXMPMeta_GetVersionInfo_1
+_WXMPMeta_Initialize_1
+_WXMPMeta_Terminate_1
+
+_WXMPMeta_CTor_1
+_WXMPMeta_IncrementRefCount_1
+_WXMPMeta_DecrementRefCount_1
+
+_WXMPMeta_GetGlobalOptions_1
+_WXMPMeta_SetGlobalOptions_1
+_WXMPMeta_DumpNamespaces_1
+_WXMPMeta_RegisterNamespace_1
+_WXMPMeta_GetNamespacePrefix_1
+_WXMPMeta_GetNamespaceURI_1
+_WXMPMeta_DeleteNamespace_1
+
+_WXMPMeta_GetProperty_1
+_WXMPMeta_GetArrayItem_1
+_WXMPMeta_GetStructField_1
+_WXMPMeta_GetQualifier_1
+_WXMPMeta_SetProperty_1
+_WXMPMeta_SetArrayItem_1
+_WXMPMeta_AppendArrayItem_1
+_WXMPMeta_SetStructField_1
+_WXMPMeta_SetQualifier_1
+_WXMPMeta_DeleteProperty_1
+_WXMPMeta_DeleteArrayItem_1
+_WXMPMeta_DeleteStructField_1
+_WXMPMeta_DeleteQualifier_1
+_WXMPMeta_DoesPropertyExist_1
+_WXMPMeta_DoesArrayItemExist_1
+_WXMPMeta_DoesStructFieldExist_1
+_WXMPMeta_DoesQualifierExist_1
+_WXMPMeta_GetLocalizedText_1
+_WXMPMeta_SetLocalizedText_1
+_WXMPMeta_DeleteLocalizedText_1
+_WXMPMeta_GetProperty_Bool_1
+_WXMPMeta_GetProperty_Int_1
+_WXMPMeta_GetProperty_Int64_1
+_WXMPMeta_GetProperty_Float_1
+_WXMPMeta_GetProperty_Date_1
+_WXMPMeta_SetProperty_Bool_1
+_WXMPMeta_SetProperty_Int_1
+_WXMPMeta_SetProperty_Int64_1
+_WXMPMeta_SetProperty_Float_1
+_WXMPMeta_SetProperty_Date_1
+_WXMPMeta_GetObjectName_1
+_WXMPMeta_SetObjectName_1
+_WXMPMeta_GetObjectOptions_1
+_WXMPMeta_SetObjectOptions_1
+_WXMPMeta_Sort_1
+_WXMPMeta_Erase_1
+_WXMPMeta_Clone_1
+_WXMPMeta_CountArrayItems_1
+_WXMPMeta_DumpObject_1
+_WXMPMeta_ParseFromBuffer_1
+_WXMPMeta_SerializeToBuffer_1
+
+_WXMPMeta_SetDefaultErrorCallback_1
+_WXMPMeta_SetErrorCallback_1
+_WXMPMeta_ResetErrorCallbackLimit_1
+
+_WXMPIterator_PropCTor_1
+_WXMPIterator_TableCTor_1
+_WXMPIterator_IncrementRefCount_1
+_WXMPIterator_DecrementRefCount_1
+_WXMPIterator_Next_1
+_WXMPIterator_Skip_1
+
+_WXMPUtils_ComposeArrayItemPath_1
+_WXMPUtils_ComposeStructFieldPath_1
+_WXMPUtils_ComposeQualifierPath_1
+_WXMPUtils_ComposeLangSelector_1
+_WXMPUtils_ComposeFieldSelector_1
+_WXMPUtils_ConvertFromBool_1
+_WXMPUtils_ConvertFromInt_1
+_WXMPUtils_ConvertFromInt64_1
+_WXMPUtils_ConvertFromFloat_1
+_WXMPUtils_ConvertFromDate_1
+_WXMPUtils_ConvertToBool_1
+_WXMPUtils_ConvertToInt_1
+_WXMPUtils_ConvertToInt64_1
+_WXMPUtils_ConvertToFloat_1
+_WXMPUtils_ConvertToDate_1
+_WXMPUtils_CurrentDateTime_1
+_WXMPUtils_SetTimeZone_1
+_WXMPUtils_ConvertToUTCTime_1
+_WXMPUtils_ConvertToLocalTime_1
+_WXMPUtils_CompareDateTime_1
+_WXMPUtils_EncodeToBase64_1
+_WXMPUtils_DecodeFromBase64_1
+_WXMPUtils_PackageForJPEG_1
+_WXMPUtils_MergeFromJPEG_1
+
+_WXMPUtils_CatenateArrayItems_1
+_WXMPUtils_SeparateArrayItems_1
+_WXMPUtils_ApplyTemplate_1
+_WXMPUtils_RemoveProperties_1
+_WXMPUtils_DuplicateSubtree_1
+
+_XMP_NewExpatAdapter
diff --git a/XMPCore/resource/mac/XMPCore.plist b/XMPCore/resource/mac/XMPCore.plist
new file mode 100644
index 0000000..67dc908
--- /dev/null
+++ b/XMPCore/resource/mac/XMPCore.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>XMPCore</string>
+ <key>CFBundleIdentifier</key>
+ <string>com.xmp.XMPCore</string>
+ <key>CFBundleName</key>
+ <string>XMPCore</string>
+ <key>CFBundleGetInfoString</key>
+ <string>XMP Core kBasicVersion, kXMP_Copyright</string>
+ <key>CFBundleShortVersionString</key>
+ <string>XMP Core 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 Core</string>
+ <key>ProductVersion</key>
+ <string>XMPCORE_API_VERSION</string>
+</dict>
+</plist>
diff --git a/XMPCore/resource/mac/XMPCorePList.h b/XMPCore/resource/mac/XMPCorePList.h
new file mode 100644
index 0000000..4d51148
--- /dev/null
+++ b/XMPCore/resource/mac/XMPCorePList.h
@@ -0,0 +1,13 @@
+#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 XMPCORE_API_VERSION
+#else
+ #define kConfig Debug
+ #define kDebugSuffix (debug)
+ #define kBasicVersion XMPCORE_API_VERSION kDebugSuffix
+#endif
+
diff --git a/build/xcode/expat_config.h b/XMPCore/resource/mac/expat_config.h
index a21de6e..a21de6e 100644
--- a/build/xcode/expat_config.h
+++ b/XMPCore/resource/mac/expat_config.h
diff --git a/XMPCore/resource/win/XMPCore.def b/XMPCore/resource/win/XMPCore.def
new file mode 100644
index 0000000..505edef
--- /dev/null
+++ b/XMPCore/resource/win/XMPCore.def
@@ -0,0 +1,143 @@
+; Declares the entry points for the DLL.
+; Highest index: 126 - WXMPMeta_ResetErrorCallbackLimit_1
+
+LIBRARY XMPCore
+
+EXPORTS
+
+ WXMPMeta_GetVersionInfo_1 @1
+ WXMPMeta_Initialize_1 @2
+ WXMPMeta_Terminate_1 @3
+
+ WXMPMeta_CTor_1 @4
+ WXMPMeta_IncrementRefCount_1 @5
+ WXMPMeta_DecrementRefCount_1 @6
+
+ WXMPMeta_GetGlobalOptions_1 @7
+ WXMPMeta_SetGlobalOptions_1 @8
+; unused @9
+; unused @10
+; unused @11
+; unused @12
+ WXMPMeta_DumpNamespaces_1 @13
+; unused @14
+ WXMPMeta_RegisterNamespace_1 @15
+ WXMPMeta_GetNamespacePrefix_1 @16
+ WXMPMeta_GetNamespaceURI_1 @17
+ WXMPMeta_DeleteNamespace_1 @18
+; unused @19
+
+ WXMPMeta_GetProperty_1 @20
+ WXMPMeta_GetArrayItem_1 @21
+ WXMPMeta_GetStructField_1 @22
+ WXMPMeta_GetQualifier_1 @23
+ WXMPMeta_SetProperty_1 @24
+ WXMPMeta_SetArrayItem_1 @25
+ WXMPMeta_AppendArrayItem_1 @26
+ WXMPMeta_SetStructField_1 @27
+ WXMPMeta_SetQualifier_1 @28
+ WXMPMeta_DeleteProperty_1 @29
+ WXMPMeta_DeleteArrayItem_1 @30
+ WXMPMeta_DeleteStructField_1 @31
+ WXMPMeta_DeleteQualifier_1 @32
+ WXMPMeta_DoesPropertyExist_1 @33
+ WXMPMeta_DoesArrayItemExist_1 @34
+ WXMPMeta_DoesStructFieldExist_1 @35
+ WXMPMeta_DoesQualifierExist_1 @36
+ WXMPMeta_GetLocalizedText_1 @37
+ WXMPMeta_SetLocalizedText_1 @38
+ WXMPMeta_DeleteLocalizedText_1 @39
+ WXMPMeta_GetProperty_Bool_1 @40
+ WXMPMeta_GetProperty_Int_1 @41
+ WXMPMeta_GetProperty_Int64_1 @42
+ WXMPMeta_GetProperty_Float_1 @43
+ WXMPMeta_GetProperty_Date_1 @44
+ WXMPMeta_SetProperty_Bool_1 @45
+ WXMPMeta_SetProperty_Int_1 @46
+ WXMPMeta_SetProperty_Int64_1 @47
+ WXMPMeta_SetProperty_Float_1 @48
+ WXMPMeta_SetProperty_Date_1 @49
+ WXMPMeta_GetObjectName_1 @50
+ WXMPMeta_SetObjectName_1 @51
+ WXMPMeta_GetObjectOptions_1 @52
+ WXMPMeta_SetObjectOptions_1 @53
+ WXMPMeta_Sort_1 @54
+ WXMPMeta_Erase_1 @55
+ WXMPMeta_Clone_1 @56
+ WXMPMeta_CountArrayItems_1 @57
+; unused @58
+ WXMPMeta_DumpObject_1 @59
+ WXMPMeta_ParseFromBuffer_1 @60
+ WXMPMeta_SerializeToBuffer_1 @61
+
+ WXMPMeta_SetDefaultErrorCallback_1 @124
+ WXMPMeta_SetErrorCallback_1 @125
+ WXMPMeta_ResetErrorCallbackLimit_1 @126
+
+ WXMPIterator_PropCTor_1 @62
+ WXMPIterator_TableCTor_1 @63
+ WXMPIterator_IncrementRefCount_1 @64
+ WXMPIterator_DecrementRefCount_1 @65
+ WXMPIterator_Next_1 @66
+ WXMPIterator_Skip_1 @67
+
+ WXMPUtils_ComposeArrayItemPath_1 @68
+ WXMPUtils_ComposeStructFieldPath_1 @69
+ WXMPUtils_ComposeQualifierPath_1 @70
+ WXMPUtils_ComposeLangSelector_1 @71
+ WXMPUtils_ComposeFieldSelector_1 @72
+ WXMPUtils_ConvertFromBool_1 @73
+ WXMPUtils_ConvertFromInt_1 @74
+ WXMPUtils_ConvertFromInt64_1 @75
+ WXMPUtils_ConvertFromFloat_1 @76
+ WXMPUtils_ConvertFromDate_1 @77
+ WXMPUtils_ConvertToBool_1 @78
+ WXMPUtils_ConvertToInt_1 @79
+ WXMPUtils_ConvertToInt64_1 @80
+ WXMPUtils_ConvertToFloat_1 @81
+ WXMPUtils_ConvertToDate_1 @82
+ WXMPUtils_CurrentDateTime_1 @83
+ WXMPUtils_SetTimeZone_1 @84
+ WXMPUtils_ConvertToUTCTime_1 @85
+ WXMPUtils_ConvertToLocalTime_1 @86
+ WXMPUtils_CompareDateTime_1 @87
+ WXMPUtils_EncodeToBase64_1 @88
+ WXMPUtils_DecodeFromBase64_1 @89
+ WXMPUtils_PackageForJPEG_1 @90
+ WXMPUtils_MergeFromJPEG_1 @91
+
+ WXMPUtils_CatenateArrayItems_1 @92
+ WXMPUtils_SeparateArrayItems_1 @93
+ WXMPUtils_ApplyTemplate_1 @123
+ WXMPUtils_RemoveProperties_1 @94
+; unused @95
+ WXMPUtils_DuplicateSubtree_1 @96
+; unused @97
+; unused @98
+; unused @99
+; unused @100
+; unused @101
+; unused @102
+
+; unused @103
+; unused @104
+
+; unused @105
+; unused @106
+; unused @107
+; unused @108
+; unused @109
+; unused @110
+; unused @111
+; unused @112
+; unused @113
+; unused @114
+; unused @115
+; unused @116
+; unused @117
+; unused @118
+; unused @119
+; unused @120
+
+ XMP_NewExpatAdapter @121
+; unused @122
diff --git a/XMPCore/resource/win/XMPCore.rc b/XMPCore/resource/win/XMPCore.rc
new file mode 100644
index 0000000..8e89260
--- /dev/null
+++ b/XMPCore/resource/win/XMPCore.rc
@@ -0,0 +1,58 @@
+// =================================================================================================
+// Copyright 2002-2008 Adobe Systems Incorporated
+// All Rights Reserved.
+//
+// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
+// of the Adobe license agreement accompanying it.
+// =================================================================================================
+
+#include "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 DEBUG
+ #define kConfig "Debug"
+ #define kDebugSuffix "debug "
+#else
+ #define kConfig "Release"
+ #define kDebugSuffix ""
+#endif
+
+#if _WIN64
+ #define BINTYPE "64"
+#else
+ #define BINTYPE "32"
+#endif
+
+#define kEngVersion XMPCORE_API_VERSION_STRING " ( " BINTYPE " bit " kDebugSuffix ")"
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION XMPCORE_API_VERSION_MAJOR,XMPCORE_API_VERSION_MINOR
+ PRODUCTVERSION XMPCORE_API_VERSION_MAJOR,XMPCORE_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 Core " kEngVersion "\0"
+ VALUE "FileVersion", kEngVersion "\0"
+ VALUE "InternalName", "XMPCore\0"
+ VALUE "LegalCopyright", kXMP_CopyrightStr "\0"
+ VALUE "OriginalFilename", "XMPCore.dll\0"
+ VALUE "ProductName", "XMP Core\0"
+ VALUE "ProductVersion", XMPCORE_API_VERSION_STRING "\0"
+ VALUE "BinType", BINTYPE "\0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x800, 1200
+ END
+END
diff --git a/build/vc/expat_config.h b/XMPCore/resource/win/expat_config.h
index 945d0bd..e6d2f63 100644
--- a/build/vc/expat_config.h
+++ b/XMPCore/resource/win/expat_config.h
@@ -1,3 +1,7 @@
+#if WIN_ENV
+ #pragma warning ( disable : 4244 ) // possible loss of data (temporary for 64 bit builds)
+#endif
+
/* expat_config.h. Generated by configure. */
/* expat_config.h.in. Generated from configure.in by autoheader. */
diff --git a/XMPCore/source/ExpatAdapter.cpp b/XMPCore/source/ExpatAdapter.cpp
index f8c702d..6f20157 100644
--- a/XMPCore/source/ExpatAdapter.cpp
+++ b/XMPCore/source/ExpatAdapter.cpp
@@ -81,7 +81,10 @@ ExpatAdapter::ExpatAdapter ( bool useGlobalNamespaces ) : parser(0), registeredN
#endif
this->parser = XML_ParserCreateNS ( 0, FullNameSeparator );
- if ( this->parser == 0 ) XMP_Throw ( "Failure creating Expat parser", kXMPErr_ExternalFailure );
+ if ( this->parser == 0 ) {
+ XMP_Error error(kXMPErr_NoMemory, "Failure creating Expat parser" );
+ this->NotifyClient ( kXMPErrSev_ProcessFatal, error );
+ }
if ( useGlobalNamespaces ) {
this->registeredNamespaces = sRegisteredNamespaces;
@@ -143,7 +146,10 @@ void ExpatAdapter::ParseBuffer ( const void * buffer, size_t length, bool last /
status = XML_Parse ( this->parser, (const char *)buffer, length, last );
#if BanAllEntityUsage
- if ( this->isAborted ) XMP_Throw ( "DOCTYPE is not allowed", kXMPErr_BadXML );
+ if ( this->isAborted ) {
+ XMP_Error error(kXMPErr_BadXML, "DOCTYPE is not allowed" )
+ this->NotifyClient ( kXMPErrSev_Recoverable, error );
+ }
#endif
if ( status != XML_STATUS_OK ) {
@@ -171,7 +177,8 @@ void ExpatAdapter::ParseBuffer ( const void * buffer, size_t length, bool last /
#endif
- XMP_Throw ( errMsg, kXMPErr_BadXML );
+ XMP_Error error(kXMPErr_BadXML, errMsg);
+ this->NotifyClient ( kXMPErrSev_Recoverable, error );
}
@@ -218,7 +225,10 @@ static void SetQualName ( ExpatAdapter * thiz, XMP_StringPtr fullName, XML_Node
if ( node->ns == "http://purl.org/dc/1.1/" ) node->ns = "http://purl.org/dc/elements/1.1/";
bool found = thiz->registeredNamespaces->GetPrefix ( node->ns.c_str(), &prefix, &prefixLen );
- if ( ! found ) XMP_Throw ( "Unknown URI in Expat full name", kXMPErr_ExternalFailure );
+ if ( ! found ) {
+ XMP_Error error(kXMPErr_ExternalFailure, "Unknown URI in Expat full name" );
+ thiz->NotifyClient ( kXMPErrSev_OperationFatal, error );
+ }
node->nsPrefixLen = prefixLen; // ! Includes the ':'.
node->name = prefix;
@@ -302,7 +312,10 @@ static void StartElementHandler ( void * userData, XMP_StringPtr name, XMP_Strin
size_t attrCount = 0;
for ( XMP_StringPtr* a = attrs; *a != 0; ++a ) ++attrCount;
- if ( (attrCount & 1) != 0 ) XMP_Throw ( "Expat attribute info has odd length", kXMPErr_ExternalFailure );
+ if ( (attrCount & 1) != 0 ) {
+ XMP_Error error(kXMPErr_ExternalFailure, "Expat attribute info has odd length");
+ thiz->NotifyClient ( kXMPErrSev_OperationFatal, error );
+ }
attrCount = attrCount/2; // They are name/value pairs.
#if XMP_DebugBuild & DumpXMLParseEvents
diff --git a/XMPCore/source/ParseRDF.cpp b/XMPCore/source/ParseRDF.cpp
index ae65d29..7eeecb3 100644
--- a/XMPCore/source/ParseRDF.cpp
+++ b/XMPCore/source/ParseRDF.cpp
@@ -8,6 +8,7 @@
#include "public/include/XMP_Environment.h" // ! This must be the first include!
#include "XMPCore/source/XMPCore_Impl.hpp"
+#include "XMPCore/source/XMPMeta.hpp"
#include "source/ExpatAdapter.hpp"
#include <cstring>
@@ -161,48 +162,57 @@ using namespace std;
// =========================
//
// Each of these is responsible for recognizing an RDF syntax production and adding the appropriate
-// structure to the XMP tree. They simply return for success, failures will throw an exception.
+// structure to the XMP tree. They simply return for success, failures will throw an exception. The
+// class exists only to provide access to the error notification object.
-static void
-RDF_RDF ( XMP_Node * xmpTree, const XML_Node & xmlNode );
+class RDF_Parser {
+public:
-static void
-RDF_NodeElementList ( XMP_Node * xmpParent, const XML_Node & xmlParent, bool isTopLevel );
+ void RDF ( XMP_Node * xmpTree, const XML_Node & xmlNode );
-static void
-RDF_NodeElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel );
+ void NodeElementList ( XMP_Node * xmpParent, const XML_Node & xmlParent, bool isTopLevel );
-static void
-RDF_NodeElementAttrs ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel );
+ void NodeElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel );
-static void
-RDF_PropertyElementList ( XMP_Node * xmpParent, const XML_Node & xmlParent, bool isTopLevel );
-enum { kIsTopLevel = true, kNotTopLevel = false };
+ void NodeElementAttrs ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel );
-static void
-RDF_PropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel );
+ void PropertyElementList ( XMP_Node * xmpParent, const XML_Node & xmlParent, bool isTopLevel );
-static void
-RDF_ResourcePropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel );
+ void PropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel );
-static void
-RDF_LiteralPropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel );
+ void ResourcePropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel );
-static void
-RDF_ParseTypeLiteralPropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel );
+ void LiteralPropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel );
-static void
-RDF_ParseTypeResourcePropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel );
+ void ParseTypeLiteralPropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel );
-static void
-RDF_ParseTypeCollectionPropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel );
+ void ParseTypeResourcePropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel );
-static void
-RDF_ParseTypeOtherPropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel );
+ void ParseTypeCollectionPropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel );
-static void
-RDF_EmptyPropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel );
+ void ParseTypeOtherPropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel );
+
+ void EmptyPropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel );
+
+ RDF_Parser ( XMPMeta::ErrorCallbackInfo * ec ) : errorCallback(ec) {};
+
+private:
+
+ RDF_Parser() {}; // Hidden on purpose.
+
+ XMPMeta::ErrorCallbackInfo * errorCallback;
+
+ XMP_Node * AddChildNode ( XMP_Node * xmpParent, const XML_Node & xmlNode, const XMP_StringPtr value, bool isTopLevel );
+
+ XMP_Node * AddQualifierNode ( XMP_Node * xmpParent, const XMP_VarString & name, const XMP_VarString & value );
+
+ XMP_Node * AddQualifierNode ( XMP_Node * xmpParent, const XML_Node & attr );
+
+ void FixupQualifiedNode ( XMP_Node * xmpParent );
+
+};
+enum { kIsTopLevel = true, kNotTopLevel = false };
// =================================================================================================
@@ -298,9 +308,37 @@ GetRDFTermKind ( const XMP_VarString & name )
} // GetRDFTermKind
-
// =================================================================================================
+static void
+RemoveChild ( XMP_Node * xmpParent, size_t index )
+{
+ XMP_Node * child = xmpParent->children[index];
+ xmpParent->children.erase ( xmpParent->children.begin() + index );
+ delete child;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+static void
+RemoveQualifier ( XMP_Node * xmpParent, size_t index )
+{
+ XMP_Node * qualifier = xmpParent->qualifiers[index];
+ xmpParent->qualifiers.erase ( xmpParent->qualifiers.begin() + index );
+ delete qualifier;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+static void
+RemoveQualifier ( XMP_Node * xmpParent, XMP_NodePtrPos pos )
+{
+ XMP_Node * qualifier = *pos;
+ xmpParent->qualifiers.erase ( pos );
+ delete qualifier;
+}
+
+// =================================================================================================
// -------------------------------------------------------------------------------------------------
// IsCoreSyntaxTerm
@@ -312,11 +350,9 @@ GetRDFTermKind ( const XMP_VarString & name )
static bool
IsCoreSyntaxTerm ( RDFTermKind term )
{
-
if ( (kRDFTerm_FirstCore <= term) && (term <= kRDFTerm_LastCore) ) return true;
return false;
-
-} // IsCoreSyntaxTerm
+}
// -------------------------------------------------------------------------------------------------
// IsSyntaxTerm
@@ -328,11 +364,9 @@ IsCoreSyntaxTerm ( RDFTermKind term )
static bool
IsSyntaxTerm ( RDFTermKind term )
{
-
if ( (kRDFTerm_FirstSyntax <= term) && (term <= kRDFTerm_LastSyntax) ) return true;
return false;
-
-} // IsSyntaxTerm
+}
// -------------------------------------------------------------------------------------------------
// IsOldTerm
@@ -344,11 +378,9 @@ IsSyntaxTerm ( RDFTermKind term )
static bool
IsOldTerm ( RDFTermKind term )
{
-
if ( (kRDFTerm_FirstOld <= term) && (term <= kRDFTerm_LastOld) ) return true;
return false;
-
-} // IsOldTerm
+}
// -------------------------------------------------------------------------------------------------
// IsNodeElementName
@@ -360,11 +392,9 @@ IsOldTerm ( RDFTermKind term )
static bool
IsNodeElementName ( RDFTermKind term )
{
-
if ( (term == kRDFTerm_li) || IsOldTerm ( term ) ) return false;
return (! IsCoreSyntaxTerm ( term ));
-
-} // IsNodeElementName
+}
// -------------------------------------------------------------------------------------------------
// IsPropertyElementName
@@ -376,11 +406,9 @@ IsNodeElementName ( RDFTermKind term )
static bool
IsPropertyElementName ( RDFTermKind term )
{
-
if ( (term == kRDFTerm_Description) || IsOldTerm ( term ) ) return false;
return (! IsCoreSyntaxTerm ( term ));
-
-} // IsPropertyElementName
+}
// -------------------------------------------------------------------------------------------------
// IsPropertyAttributeName
@@ -392,36 +420,47 @@ IsPropertyElementName ( RDFTermKind term )
static bool
IsPropertyAttributeName ( RDFTermKind term )
{
-
if ( (term == kRDFTerm_Description) || (term == kRDFTerm_li) || IsOldTerm ( term ) ) return false;
return (! IsCoreSyntaxTerm ( term ));
+}
-} // IsPropertyAttributeName
+// -------------------------------------------------------------------------------------------------
+// IsNumberedArrayItemName
+// -----------------------
+//
+// Return true for a name of the form "rdf:_n", where n is a decimal integer. We're not strict about
+// the integer part, it just has to be characters in the range '0'..'9'.
+static bool
+IsNumberedArrayItemName ( const std::string & name )
+{
+ if ( name.size() <= 5 ) return false;
+ if ( strncmp ( name.c_str(), "rdf:_", 5 ) != 0 ) return false;
+ for ( size_t i = 5; i < name.size(); ++i ) {
+ if ( (name[i] < '0') | (name[i] > '9') ) return false;
+ }
+ return true;
+}
// =================================================================================================
-// AddChildNode
-// ============
+// RDF_Parser::AddChildNode
+// ========================
-static XMP_Node *
-AddChildNode ( XMP_Node * xmpParent, const XML_Node & xmlNode, const XMP_StringPtr value, bool isTopLevel )
+XMP_Node * RDF_Parser::AddChildNode ( XMP_Node * xmpParent, const XML_Node & xmlNode, const XMP_StringPtr value, bool isTopLevel )
{
- #if 0
- cout << "AddChildNode, parent = " << xmpParent->name << ", child = " << xmlNode.name;
- cout << ", value = \"" << value << '"';
- if ( isTopLevel ) cout << ", top level";
- cout << endl;
- #endif
if ( xmlNode.ns.empty() ) {
- XMP_Throw ( "XML namespace required for all elements and attributes", kXMPErr_BadRDF );
+ XMP_Error error ( kXMPErr_BadRDF, "XML namespace required for all elements and attributes" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+ return 0;
}
- XMP_StringPtr childName = xmlNode.name.c_str();
- const bool isArrayItem = (xmlNode.name == "rdf:li");
- const bool isValueNode = (xmlNode.name == "rdf:value");
+ bool isArrayParent = (xmpParent->options & kXMP_PropValueIsArray);
+ bool isArrayItem = (xmlNode.name == "rdf:li");
+ bool isValueNode = (xmlNode.name == "rdf:value");
XMP_OptionBits childOptions = 0;
-
+ XMP_StringPtr childName = xmlNode.name.c_str();
+
if ( isTopLevel ) {
// Lookup the schema node, adjust the XMP parent pointer.
@@ -438,13 +477,49 @@ AddChildNode ( XMP_Node * xmpParent, const XML_Node & xmlNode, const XMP_StringP
}
}
+
+ // Check use of rdf:li and rdf:_n names. Must be done before calling FindChildNode!
+ if ( isArrayItem ) {
+
+ // rdf:li can only be used for array children.
+ if ( ! isArrayParent ) {
+ XMP_Error error ( kXMPErr_BadRDF, "Misplaced rdf:li element" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+ return 0;
+ }
+ childName = kXMP_ArrayItemName;
+
+ } else if ( isArrayParent ) {
+
+ // Tolerate use of rdf:_n, don't verify order.
+ if ( IsNumberedArrayItemName ( xmlNode.name ) ) {
+ childName = kXMP_ArrayItemName;
+ isArrayItem = true;
+ } else {
+ XMP_Error error ( kXMPErr_BadRDF, "Array items cannot have arbitrary child names" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+ return 0;
+ }
+
+ }
// Make sure that this is not a duplicate of a named node.
if ( ! (isArrayItem | isValueNode) ) {
if ( FindChildNode ( xmpParent, childName, kXMP_ExistingOnly ) != 0 ) {
- XMP_Throw ( "Duplicate property or field node", kXMPErr_BadXMP );
+ XMP_Error error ( kXMPErr_BadXMP, "Duplicate property or field node" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+ return 0;
}
-
+ }
+
+ // Make sure an rdf:value node is used properly.
+ if ( isValueNode ) {
+ if ( isTopLevel || (! (xmpParent->options & kXMP_PropValueIsStruct)) ) {
+ XMP_Error error ( kXMPErr_BadRDF, "Misplaced rdf:value element" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+ return 0;
+ }
+ xmpParent->options |= kRDF_HasValueElem;
}
// Add the new child to the XMP parent node.
@@ -454,36 +529,17 @@ AddChildNode ( XMP_Node * xmpParent, const XML_Node & xmlNode, const XMP_StringP
} else {
xmpParent->children.insert ( xmpParent->children.begin(), newChild );
}
- if ( isValueNode ) {
- if ( isTopLevel || (! (xmpParent->options & kXMP_PropValueIsStruct)) ) XMP_Throw ( "Misplaced rdf:value element", kXMPErr_BadRDF );
- xmpParent->options |= kRDF_HasValueElem;
- }
-
- if ( isArrayItem ) {
- if ( ! (xmpParent->options & kXMP_PropValueIsArray) ) XMP_Throw ( "Misplaced rdf:li element", kXMPErr_BadRDF );
- newChild->name = kXMP_ArrayItemName;
- #if 0 // *** XMP_DebugBuild
- newChild->_namePtr = newChild->name.c_str();
- #endif
- }
return newChild;
-} // AddChildNode
-
+} // RDF_Parser::AddChildNode
// =================================================================================================
-// AddQualifierNode
-// ================
+// RDF_Parser::AddQualifierNode
+// ============================
-static XMP_Node *
-AddQualifierNode ( XMP_Node * xmpParent, const XMP_VarString & name, const XMP_VarString & value )
+XMP_Node * RDF_Parser::AddQualifierNode ( XMP_Node * xmpParent, const XMP_VarString & name, const XMP_VarString & value )
{
-
- #if 0
- cout << "AddQualifierNode, parent = " << xmpParent->name << ", name = " << name;
- cout << ", value = \"" << value << '"' << endl;
- #endif
const bool isLang = (name == "xml:lang");
const bool isType = (name == "rdf:type");
@@ -517,36 +573,34 @@ AddQualifierNode ( XMP_Node * xmpParent, const XMP_VarString & name, const XMP_V
return newQual;
-} // AddQualifierNode
-
+} // RDF_Parser::AddQualifierNode
// =================================================================================================
-// AddQualifierNode
-// ================
+// RDF_Parser::AddQualifierNode
+// ============================
-static XMP_Node *
-AddQualifierNode ( XMP_Node * xmpParent, const XML_Node & attr )
+XMP_Node * RDF_Parser::AddQualifierNode ( XMP_Node * xmpParent, const XML_Node & attr )
{
if ( attr.ns.empty() ) {
- XMP_Throw ( "XML namespace required for all elements and attributes", kXMPErr_BadRDF );
+ XMP_Error error ( kXMPErr_BadRDF, "XML namespace required for all elements and attributes" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+ return 0;
}
- return AddQualifierNode ( xmpParent, attr.name, attr.value );
-
-} // AddQualifierNode
+ return this->AddQualifierNode ( xmpParent, attr.name, attr.value );
+} // RDF_Parser::AddQualifierNode
// =================================================================================================
-// FixupQualifiedNode
-// ==================
+// RDF_Parser::FixupQualifiedNode
+// ==============================
//
// The parent is an RDF pseudo-struct containing an rdf:value field. Fix the XMP data model. The
// rdf:value node must be the first child, the other children are qualifiers. The form, value, and
// children of the rdf:value node are the real ones. The rdf:value node's qualifiers must be added
// to the others.
-static void
-FixupQualifiedNode ( XMP_Node * xmpParent )
+void RDF_Parser::FixupQualifiedNode ( XMP_Node * xmpParent )
{
size_t qualNum, qualLim;
size_t childNum, childLim;
@@ -559,22 +613,26 @@ FixupQualifiedNode ( XMP_Node * xmpParent )
xmpParent->qualifiers.reserve ( xmpParent->qualifiers.size() + xmpParent->children.size() + valueNode->qualifiers.size() );
// Move the qualifiers on the value node to the parent. Make sure an xml:lang qualifier stays at
- // the front. Check for duplicate names between the value node's qualifiers and the parent's
- // children. The parent's children are about to become qualifiers. Check here, between the
- // groups. Intra-group duplicates are caught by AddChildNode.
+ // the front.
qualNum = 0;
qualLim = valueNode->qualifiers.size();
if ( valueNode->options & kXMP_PropHasLang ) {
- if ( xmpParent->options & kXMP_PropHasLang ) XMP_Throw ( "Redundant xml:lang for rdf:value element", kXMPErr_BadXMP );
+ if ( xmpParent->options & kXMP_PropHasLang ) {
+ XMP_Error error ( kXMPErr_BadXMP, "Duplicate xml:lang for rdf:value element" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+ XMP_Assert ( xmpParent->qualifiers[0]->name == "xml:lang" );
+ RemoveQualifier ( xmpParent, 0 ); // Use the rdf:value node's language.
+ }
XMP_Node * langQual = valueNode->qualifiers[0];
XMP_Assert ( langQual->name == "xml:lang" );
langQual->parent = xmpParent;
xmpParent->options |= kXMP_PropHasLang;
+ XMP_ClearOption ( valueNode->options, kXMP_PropHasLang );
if ( xmpParent->qualifiers.empty() ) {
xmpParent->qualifiers.push_back ( langQual ); // *** Should use utilities to add qual & set parent.
@@ -590,8 +648,13 @@ FixupQualifiedNode ( XMP_Node * xmpParent )
for ( ; qualNum != qualLim; ++qualNum ) {
XMP_Node * currQual = valueNode->qualifiers[qualNum];
- if ( FindChildNode ( xmpParent, currQual->name.c_str(), kXMP_ExistingOnly ) != 0 ) {
- XMP_Throw ( "Duplicate qualifier node", kXMPErr_BadXMP );
+ XMP_NodePtrPos existingPos;
+ XMP_Node * existingQual = FindQualifierNode ( xmpParent, currQual->name.c_str(), kXMP_ExistingOnly, &existingPos );
+
+ if ( existingQual != 0 ) {
+ XMP_Error error ( kXMPErr_BadXMP, "Duplicate qualifier node" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+ RemoveQualifier ( xmpParent, existingPos ); // Use the rdf:value node's qualifier.
}
currQual->parent = xmpParent;
@@ -610,22 +673,31 @@ FixupQualifiedNode ( XMP_Node * xmpParent )
XMP_Node * currQual = xmpParent->children[childNum];
bool isLang = (currQual->name == "xml:lang");
- currQual->options |= kXMP_PropIsQualifier;
- currQual->parent = xmpParent;
-
- if ( isLang ) {
- if ( xmpParent->options & kXMP_PropHasLang ) XMP_Throw ( "Duplicate xml:lang qualifier", kXMPErr_BadXMP );
- xmpParent->options |= kXMP_PropHasLang;
- } else if ( currQual->name == "rdf:type" ) {
- xmpParent->options |= kXMP_PropHasType;
- }
+ if ( FindQualifierNode ( xmpParent, currQual->name.c_str(), kXMP_ExistingOnly ) != 0 ) {
+ XMP_Error error ( kXMPErr_BadXMP, "Duplicate qualifier" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+ delete currQual;
- if ( (! isLang) || xmpParent->qualifiers.empty() ) {
- xmpParent->qualifiers.push_back ( currQual );
} else {
- xmpParent->qualifiers.insert ( xmpParent->qualifiers.begin(), currQual );
+
+ currQual->options |= kXMP_PropIsQualifier;
+ currQual->parent = xmpParent;
+
+ if ( isLang ) {
+ xmpParent->options |= kXMP_PropHasLang;
+ } else if ( currQual->name == "rdf:type" ) {
+ xmpParent->options |= kXMP_PropHasType;
+ }
+
+ if ( (! isLang) || xmpParent->qualifiers.empty() ) {
+ xmpParent->qualifiers.push_back ( currQual );
+ } else {
+ xmpParent->qualifiers.insert ( xmpParent->qualifiers.begin(), currQual );
+ }
+
}
- xmpParent->children[childNum] = 0; // We just moved it to the qualifers.
+
+ xmpParent->children[childNum] = 0; // We just moved it to the qualifers, or ignored it.
}
@@ -639,9 +711,6 @@ FixupQualifiedNode ( XMP_Node * xmpParent )
xmpParent->options |= valueNode->options;
xmpParent->value.swap ( valueNode->value );
- #if 0 // *** XMP_DebugBuild
- xmpParent->_valuePtr = xmpParent->value.c_str();
- #endif
xmpParent->children[0] = 0; // ! Remove the value node itself before the swap.
xmpParent->children.swap ( valueNode->children );
@@ -653,30 +722,11 @@ FixupQualifiedNode ( XMP_Node * xmpParent )
delete valueNode;
-} // FixupQualifiedNode
-
-
-// =================================================================================================
-// ProcessRDF
-// ==========
-//
-// Parse the XML tree of the RDF and build the corresponding XMP tree.
-
-// *** Throw an exception if no XMP is found? By option?
-// *** Do parsing exceptions cause the partial tree to be deleted?
-
-void ProcessRDF ( XMP_Node * xmpTree, const XML_Node & rdfNode, XMP_OptionBits options )
-{
- IgnoreParam(options);
-
- RDF_RDF ( xmpTree, rdfNode );
-
-} // ProcessRDF
-
+} // RDF_Parser::FixupQualifiedNode
// =================================================================================================
-// RDF_RDF
-// =======
+// RDF_Parser::RDF
+// ===============
//
// 7.2.9 RDF
// start-element ( URI == rdf:RDF, attributes == set() )
@@ -686,25 +736,25 @@ void ProcessRDF ( XMP_Node * xmpTree, const XML_Node & rdfNode, XMP_OptionBits o
// The top level rdf:RDF node. It can only have xmlns attributes, which have already been removed
// during construction of the XML tree.
-static void
-RDF_RDF ( XMP_Node * xmpTree, const XML_Node & xmlNode )
+void RDF_Parser::RDF ( XMP_Node * xmpTree, const XML_Node & xmlNode )
{
- if ( ! xmlNode.attrs.empty() ) XMP_Throw ( "Invalid attributes of rdf:RDF element", kXMPErr_BadRDF );
- RDF_NodeElementList ( xmpTree, xmlNode, kIsTopLevel );
-
-} // RDF_RDF
+ if ( ! xmlNode.attrs.empty() ) {
+ XMP_Error error ( kXMPErr_BadRDF, "Invalid attributes of rdf:RDF element" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+ }
+ this->NodeElementList ( xmpTree, xmlNode, kIsTopLevel ); // ! Attributes are ignored.
+} // RDF_Parser::RDF
// =================================================================================================
-// RDF_NodeElementList
-// ===================
+// RDF_Parser::NodeElementList
+// ===========================
//
// 7.2.10 nodeElementList
// ws* ( nodeElement ws* )*
-static void
-RDF_NodeElementList ( XMP_Node * xmpParent, const XML_Node & xmlParent, bool isTopLevel )
+void RDF_Parser::NodeElementList ( XMP_Node * xmpParent, const XML_Node & xmlParent, bool isTopLevel )
{
XMP_Assert ( isTopLevel );
@@ -713,15 +763,14 @@ RDF_NodeElementList ( XMP_Node * xmpParent, const XML_Node & xmlParent, bool isT
for ( ; currChild != endChild; ++currChild ) {
if ( (*currChild)->IsWhitespaceNode() ) continue;
- RDF_NodeElement ( xmpParent, **currChild, isTopLevel );
+ this->NodeElement ( xmpParent, **currChild, isTopLevel );
}
-} // RDF_NodeElementList
-
+} // RDF_Parser::NodeElementList
// =================================================================================================
-// RDF_NodeElement
-// ===============
+// RDF_Parser::NodeElement
+// =======================
//
// 7.2.5 nodeElementURIs
// anyURI - ( coreSyntaxTerms | rdf:li | oldTerms )
@@ -734,27 +783,25 @@ RDF_NodeElementList ( XMP_Node * xmpParent, const XML_Node & xmlParent, bool isT
//
// A node element URI is rdf:Description or anything else that is not an RDF term.
-static void
-RDF_NodeElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel )
+void RDF_Parser::NodeElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel )
{
RDFTermKind nodeTerm = GetRDFTermKind ( xmlNode.name );
if ( (nodeTerm != kRDFTerm_Description) && (nodeTerm != kRDFTerm_Other) ) {
- XMP_Throw ( "Node element must be rdf:Description or typedNode", kXMPErr_BadRDF );
- }
-
- if ( isTopLevel && (nodeTerm == kRDFTerm_Other) ) {
- XMP_Throw ( "Top level typedNode not allowed", kXMPErr_BadXMP );
+ XMP_Error error ( kXMPErr_BadRDF, "Node element must be rdf:Description or typedNode" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+ } else if ( isTopLevel && (nodeTerm == kRDFTerm_Other) ) {
+ XMP_Error error ( kXMPErr_BadXMP, "Top level typedNode not allowed" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
} else {
- RDF_NodeElementAttrs ( xmpParent, xmlNode, isTopLevel );
- RDF_PropertyElementList ( xmpParent, xmlNode, isTopLevel );
+ this->NodeElementAttrs ( xmpParent, xmlNode, isTopLevel );
+ this->PropertyElementList ( xmpParent, xmlNode, isTopLevel );
}
-} // RDF_NodeElement
-
+} // RDF_Parser::NodeElement
// =================================================================================================
-// RDF_NodeElementAttrs
-// ====================
+// RDF_Parser::NodeElementAttrs
+// ============================
//
// 7.2.7 propertyAttributeURIs
// anyURI - ( coreSyntaxTerms | rdf:Description | rdf:li | oldTerms )
@@ -771,8 +818,7 @@ RDF_NodeElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLeve
static const XMP_OptionBits kExclusiveAttrMask = (kRDFMask_ID | kRDFMask_nodeID | kRDFMask_about);
-static void
-RDF_NodeElementAttrs ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel )
+void RDF_Parser::NodeElementAttrs ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel )
{
XMP_OptionBits exclusiveAttrs = 0; // Used to detect attributes that are mutually exclusive.
@@ -789,7 +835,11 @@ RDF_NodeElementAttrs ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTo
case kRDFTerm_nodeID :
case kRDFTerm_about :
- if ( exclusiveAttrs & kExclusiveAttrMask ) XMP_Throw ( "Mutally exclusive about, ID, nodeID attributes", kXMPErr_BadRDF );
+ if ( exclusiveAttrs & kExclusiveAttrMask ) {
+ XMP_Error error ( kXMPErr_BadRDF, "Mutally exclusive about, ID, nodeID attributes" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+ continue; // Skip the later mutually exclusive attributes.
+ }
exclusiveAttrs |= (1 << attrTerm);
if ( isTopLevel && (attrTerm == kRDFTerm_about) ) {
@@ -799,35 +849,40 @@ RDF_NodeElementAttrs ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTo
if ( xmpParent->name.empty() ) {
xmpParent->name = (*currAttr)->value;
} else if ( ! (*currAttr)->value.empty() ) {
- if ( xmpParent->name != (*currAttr)->value ) XMP_Throw ( "Mismatched top level rdf:about values", kXMPErr_BadXMP );
+ if ( xmpParent->name != (*currAttr)->value ) {
+ XMP_Error error ( kXMPErr_BadXMP, "Mismatched top level rdf:about values" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+ }
}
}
break;
case kRDFTerm_Other :
- AddChildNode ( xmpParent, **currAttr, (*currAttr)->value.c_str(), isTopLevel );
+ this->AddChildNode ( xmpParent, **currAttr, (*currAttr)->value.c_str(), isTopLevel );
break;
default :
- XMP_Throw ( "Invalid nodeElement attribute", kXMPErr_BadRDF );
+ {
+ XMP_Error error ( kXMPErr_BadRDF, "Invalid nodeElement attribute" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+ }
+ continue;
}
}
-} // RDF_NodeElementAttrs
-
+} // RDF_Parser::NodeElementAttrs
// =================================================================================================
-// RDF_PropertyElementList
-// =======================
+// RDF_Parser::PropertyElementList
+// ===============================
//
// 7.2.13 propertyEltList
// ws* ( propertyElt ws* )*
-static void
-RDF_PropertyElementList ( XMP_Node * xmpParent, const XML_Node & xmlParent, bool isTopLevel )
+void RDF_Parser::PropertyElementList ( XMP_Node * xmpParent, const XML_Node & xmlParent, bool isTopLevel )
{
XML_cNodePos currChild = xmlParent.content.begin();
XML_cNodePos endChild = xmlParent.content.end();
@@ -835,17 +890,18 @@ RDF_PropertyElementList ( XMP_Node * xmpParent, const XML_Node & xmlParent, bool
for ( ; currChild != endChild; ++currChild ) {
if ( (*currChild)->IsWhitespaceNode() ) continue;
if ( (*currChild)->kind != kElemNode ) {
- XMP_Throw ( "Expected property element node not found", kXMPErr_BadRDF );
+ XMP_Error error ( kXMPErr_BadRDF, "Expected property element node not found" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+ continue;
}
- RDF_PropertyElement ( xmpParent, **currChild, isTopLevel );
+ this->PropertyElement ( xmpParent, **currChild, isTopLevel );
}
-} // RDF_PropertyElementList
-
+} // RDF_Parser::PropertyElementList
// =================================================================================================
-// RDF_PropertyElement
-// ===================
+// RDF_Parser::PropertyElement
+// ===========================
//
// 7.2.14 propertyElt
// resourcePropertyElt | literalPropertyElt | parseTypeLiteralPropertyElt |
@@ -893,16 +949,19 @@ RDF_PropertyElementList ( XMP_Node * xmpParent, const XML_Node & xmlParent, bool
// NOTE: The RDF syntax does not explicitly include the xml:lang attribute although it can appear in
// many of these. We have to allow for it in the attibute counts below.
-static void
-RDF_PropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel )
+void RDF_Parser::PropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel )
{
RDFTermKind nodeTerm = GetRDFTermKind ( xmlNode.name );
- if ( ! IsPropertyElementName ( nodeTerm ) ) XMP_Throw ( "Invalid property element name", kXMPErr_BadRDF );
+ if ( ! IsPropertyElementName ( nodeTerm ) ) {
+ XMP_Error error ( kXMPErr_BadRDF, "Invalid property element name" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+ return;
+ }
if ( xmlNode.attrs.size() > 3 ) {
// Only an emptyPropertyElt can have more than 3 attributes.
- RDF_EmptyPropertyElement ( xmpParent, xmlNode, isTopLevel );
+ this->EmptyPropertyElement ( xmpParent, xmlNode, isTopLevel );
} else {
@@ -924,17 +983,17 @@ RDF_PropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTop
XMP_VarString& attrValue = (*currAttr)->value;
if ( *attrName == "rdf:datatype" ) {
- RDF_LiteralPropertyElement ( xmpParent, xmlNode, isTopLevel );
+ this->LiteralPropertyElement ( xmpParent, xmlNode, isTopLevel );
} else if ( *attrName != "rdf:parseType" ) {
- RDF_EmptyPropertyElement ( xmpParent, xmlNode, isTopLevel );
+ this->EmptyPropertyElement ( xmpParent, xmlNode, isTopLevel );
} else if ( attrValue == "Literal" ) {
- RDF_ParseTypeLiteralPropertyElement ( xmpParent, xmlNode, isTopLevel );
+ this->ParseTypeLiteralPropertyElement ( xmpParent, xmlNode, isTopLevel );
} else if ( attrValue == "Resource" ) {
- RDF_ParseTypeResourcePropertyElement ( xmpParent, xmlNode, isTopLevel );
+ this->ParseTypeResourcePropertyElement ( xmpParent, xmlNode, isTopLevel );
} else if ( attrValue == "Collection" ) {
- RDF_ParseTypeCollectionPropertyElement ( xmpParent, xmlNode, isTopLevel );
+ this->ParseTypeCollectionPropertyElement ( xmpParent, xmlNode, isTopLevel );
} else {
- RDF_ParseTypeOtherPropertyElement ( xmpParent, xmlNode, isTopLevel );
+ this->ParseTypeOtherPropertyElement ( xmpParent, xmlNode, isTopLevel );
}
} else {
@@ -944,7 +1003,7 @@ RDF_PropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTop
if ( xmlNode.content.empty() ) {
- RDF_EmptyPropertyElement ( xmpParent, xmlNode, isTopLevel );
+ this->EmptyPropertyElement ( xmpParent, xmlNode, isTopLevel );
} else {
@@ -956,9 +1015,9 @@ RDF_PropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTop
}
if ( currChild == endChild ) {
- RDF_LiteralPropertyElement ( xmpParent, xmlNode, isTopLevel );
+ this->LiteralPropertyElement ( xmpParent, xmlNode, isTopLevel );
} else {
- RDF_ResourcePropertyElement ( xmpParent, xmlNode, isTopLevel );
+ this->ResourcePropertyElement ( xmpParent, xmlNode, isTopLevel );
}
}
@@ -967,12 +1026,11 @@ RDF_PropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTop
}
-} // RDF_PropertyElement
-
+} // RDF_Parser::PropertyElement
// =================================================================================================
-// RDF_ResourcePropertyElement
-// ===========================
+// RDF_Parser::ResourcePropertyElement
+// ===================================
//
// 7.2.15 resourcePropertyElt
// start-element ( URI == propertyElementURIs, attributes == set ( idAttr? ) )
@@ -982,12 +1040,12 @@ RDF_PropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTop
// This handles structs using an rdf:Description node, arrays using rdf:Bag/Seq/Alt, and Typed Nodes.
// It also catches and cleans up qualified properties written with rdf:Description and rdf:value.
-static void
-RDF_ResourcePropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel )
+void RDF_Parser::ResourcePropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel )
{
if ( isTopLevel && (xmlNode.name == "iX:changes") ) return; // Strip old "punchcard" chaff.
- XMP_Node * newCompound = AddChildNode ( xmpParent, xmlNode, "", isTopLevel );
+ XMP_Node * newCompound = this->AddChildNode ( xmpParent, xmlNode, "", isTopLevel );
+ if ( newCompound == 0 ) return; // Ignore lower level errors.
XML_cNodePos currAttr = xmlNode.attrs.begin();
XML_cNodePos endAttr = xmlNode.attrs.end();
@@ -995,11 +1053,13 @@ RDF_ResourcePropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bo
for ( ; currAttr != endAttr; ++currAttr ) {
XMP_VarString & attrName = (*currAttr)->name;
if ( attrName == "xml:lang" ) {
- AddQualifierNode ( newCompound, **currAttr );
+ this->AddQualifierNode ( newCompound, **currAttr );
} else if ( attrName == "rdf:ID" ) {
continue; // Ignore all rdf:ID attributes.
} else {
- XMP_Throw ( "Invalid attribute for resource property element", kXMPErr_BadRDF );
+ XMP_Error error ( kXMPErr_BadRDF, "Invalid attribute for resource property element" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+ continue;
}
}
@@ -1009,8 +1069,16 @@ RDF_ResourcePropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bo
for ( ; currChild != endChild; ++currChild ) {
if ( ! (*currChild)->IsWhitespaceNode() ) break;
}
- if ( currChild == endChild ) XMP_Throw ( "Missing child of resource property element", kXMPErr_BadRDF );
- if ( (*currChild)->kind != kElemNode ) XMP_Throw ( "Children of resource property element must be XML elements", kXMPErr_BadRDF );
+ if ( currChild == endChild ) {
+ XMP_Error error ( kXMPErr_BadRDF, "Missing child of resource property element" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+ return;
+ }
+ if ( (*currChild)->kind != kElemNode ) {
+ XMP_Error error ( kXMPErr_BadRDF, "Children of resource property element must be XML elements" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+ return;
+ }
if ( (*currChild)->name == "rdf:Bag" ) {
newCompound->options |= kXMP_PropValueIsArray;
@@ -1020,34 +1088,41 @@ RDF_ResourcePropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bo
newCompound->options |= kXMP_PropValueIsArray | kXMP_PropArrayIsOrdered | kXMP_PropArrayIsAlternate;
} else {
// This is the Typed Node case. Add an rdf:type qualifier with a URI value.
- newCompound->options |= kXMP_PropValueIsStruct;
if ( (*currChild)->name != "rdf:Description" ) {
XMP_VarString typeName ( (*currChild)->ns );
- size_t colonPos = (*currChild)->name.find_first_of(':');
- if ( colonPos == XMP_VarString::npos ) XMP_Throw ( "All XML elements must be in a namespace", kXMPErr_BadXMP );
+ size_t colonPos = (*currChild)->name.find_first_of(':');
+ if ( colonPos == XMP_VarString::npos ) {
+ XMP_Error error ( kXMPErr_BadXMP, "All XML elements must be in a namespace" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+ return;
+ }
typeName.append ( (*currChild)->name, colonPos+1, XMP_VarString::npos ); // Append just the local name.
- XMP_Node * typeQual = AddQualifierNode ( newCompound, XMP_VarString("rdf:type"), typeName );
- typeQual->options |= kXMP_PropValueIsURI;
+ XMP_Node * typeQual = this->AddQualifierNode ( newCompound, XMP_VarString("rdf:type"), typeName );
+ if ( typeQual != 0 ) typeQual->options |= kXMP_PropValueIsURI;
}
+ newCompound->options |= kXMP_PropValueIsStruct;
}
- RDF_NodeElement ( newCompound, **currChild, kNotTopLevel );
+ this->NodeElement ( newCompound, **currChild, kNotTopLevel );
if ( newCompound->options & kRDF_HasValueElem ) {
- FixupQualifiedNode ( newCompound );
+ this->FixupQualifiedNode ( newCompound );
} else if ( newCompound->options & kXMP_PropArrayIsAlternate ) {
DetectAltText ( newCompound );
}
for ( ++currChild; currChild != endChild; ++currChild ) {
- if ( ! (*currChild)->IsWhitespaceNode() ) XMP_Throw ( "Invalid child of resource property element", kXMPErr_BadRDF );
+ if ( ! (*currChild)->IsWhitespaceNode() ) {
+ XMP_Error error ( kXMPErr_BadRDF, "Invalid child of resource property element" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+ break; // Don't bother looking for more trailing errors.
+ }
}
-} // RDF_ResourcePropertyElement
-
+} // RDF_Parser::ResourcePropertyElement
// =================================================================================================
-// RDF_LiteralPropertyElement
-// ==========================
+// RDF_Parser::LiteralPropertyElement
+// ==================================
//
// 7.2.16 literalPropertyElt
// start-element ( URI == propertyElementURIs, attributes == set ( idAttr?, datatypeAttr?) )
@@ -1056,10 +1131,10 @@ RDF_ResourcePropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bo
//
// Add a leaf node with the text value and qualifiers for the attributes.
-static void
-RDF_LiteralPropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel )
+void RDF_Parser::LiteralPropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel )
{
- XMP_Node * newChild = AddChildNode ( xmpParent, xmlNode, "", isTopLevel );
+ XMP_Node * newChild = this->AddChildNode ( xmpParent, xmlNode, "", isTopLevel );
+ if ( newChild == 0 ) return; // Ignore lower level errors.
XML_cNodePos currAttr = xmlNode.attrs.begin();
XML_cNodePos endAttr = xmlNode.attrs.end();
@@ -1067,21 +1142,27 @@ RDF_LiteralPropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, boo
for ( ; currAttr != endAttr; ++currAttr ) {
XMP_VarString & attrName = (*currAttr)->name;
if ( attrName == "xml:lang" ) {
- AddQualifierNode ( newChild, **currAttr );
+ this->AddQualifierNode ( newChild, **currAttr );
} else if ( (attrName == "rdf:ID") || (attrName == "rdf:datatype") ) {
- continue; // Ignore all rdf:ID and rdf:datatype attributes.
+ continue; // Ignore all rdf:ID and rdf:datatype attributes.
} else {
- XMP_Throw ( "Invalid attribute for literal property element", kXMPErr_BadRDF );
+ XMP_Error error ( kXMPErr_BadRDF, "Invalid attribute for literal property element" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+ continue;
}
}
XML_cNodePos currChild = xmlNode.content.begin();
XML_cNodePos endChild = xmlNode.content.end();
- size_t textSize = 0;
+ size_t textSize = 0;
for ( ; currChild != endChild; ++currChild ) {
- if ( (*currChild)->kind != kCDataNode ) XMP_Throw ( "Invalid child of literal property element", kXMPErr_BadRDF );
- textSize += (*currChild)->value.size();
+ if ( (*currChild)->kind == kCDataNode ) {
+ textSize += (*currChild)->value.size();
+ } else {
+ XMP_Error error ( kXMPErr_BadRDF, "Invalid child of literal property element" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+ }
}
newChild->value.reserve ( textSize );
@@ -1090,35 +1171,28 @@ RDF_LiteralPropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, boo
newChild->value += (*currChild)->value;
}
- #if 0 // *** XMP_DebugBuild
- newChild->_valuePtr = newChild->value.c_str();
- #endif
-
-} // RDF_LiteralPropertyElement
-
+} // RDF_Parser::LiteralPropertyElement
// =================================================================================================
-// RDF_ParseTypeLiteralPropertyElement
-// ===================================
+// RDF_Parser::ParseTypeLiteralPropertyElement
+// ===========================================
//
// 7.2.17 parseTypeLiteralPropertyElt
// start-element ( URI == propertyElementURIs, attributes == set ( idAttr?, parseLiteral ) )
// literal
// end-element()
-static void
-RDF_ParseTypeLiteralPropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel )
+void RDF_Parser::ParseTypeLiteralPropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel )
{
IgnoreParam(xmpParent); IgnoreParam(xmlNode); IgnoreParam(isTopLevel);
-
- XMP_Throw ( "ParseTypeLiteral property element not allowed", kXMPErr_BadXMP );
-
-} // RDF_ParseTypeLiteralPropertyElement
+ XMP_Error error ( kXMPErr_BadXMP, "ParseTypeLiteral property element not allowed" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+} // RDF_Parser::ParseTypeLiteralPropertyElement
// =================================================================================================
-// RDF_ParseTypeResourcePropertyElement
-// ====================================
+// RDF_Parser::ParseTypeResourcePropertyElement
+// ============================================
//
// 7.2.18 parseTypeResourcePropertyElt
// start-element ( URI == propertyElementURIs, attributes == set ( idAttr?, parseResource ) )
@@ -1128,11 +1202,10 @@ RDF_ParseTypeLiteralPropertyElement ( XMP_Node * xmpParent, const XML_Node & xml
// Add a new struct node with a qualifier for the possible rdf:ID attribute. Then process the XML
// child nodes to get the struct fields.
-static void
-RDF_ParseTypeResourcePropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel )
+void RDF_Parser::ParseTypeResourcePropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel )
{
-
- XMP_Node * newStruct = AddChildNode ( xmpParent, xmlNode, "", isTopLevel );
+ XMP_Node * newStruct = this->AddChildNode ( xmpParent, xmlNode, "", isTopLevel );
+ if ( newStruct == 0 ) return; // Ignore lower level errors.
newStruct->options |= kXMP_PropValueIsStruct;
XML_cNodePos currAttr = xmlNode.attrs.begin();
@@ -1143,64 +1216,61 @@ RDF_ParseTypeResourcePropertyElement ( XMP_Node * xmpParent, const XML_Node & xm
if ( attrName == "rdf:parseType" ) {
continue; // ! The caller ensured the value is "Resource".
} else if ( attrName == "xml:lang" ) {
- AddQualifierNode ( newStruct, **currAttr );
+ this->AddQualifierNode ( newStruct, **currAttr );
} else if ( attrName == "rdf:ID" ) {
continue; // Ignore all rdf:ID attributes.
} else {
- XMP_Throw ( "Invalid attribute for ParseTypeResource property element", kXMPErr_BadRDF );
+ XMP_Error error ( kXMPErr_BadRDF, "Invalid attribute for ParseTypeResource property element" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+ continue;
}
}
- RDF_PropertyElementList ( newStruct, xmlNode, kNotTopLevel );
+ this->PropertyElementList ( newStruct, xmlNode, kNotTopLevel );
- if ( newStruct->options & kRDF_HasValueElem ) FixupQualifiedNode ( newStruct );
+ if ( newStruct->options & kRDF_HasValueElem ) this->FixupQualifiedNode ( newStruct );
// *** Need to look for arrays using rdf:Description and rdf:type.
-} // RDF_ParseTypeResourcePropertyElement
-
+} // RDF_Parser::ParseTypeResourcePropertyElement
// =================================================================================================
-// RDF_ParseTypeCollectionPropertyElement
-// ======================================
+// RDF_Parser::ParseTypeCollectionPropertyElement
+// ==============================================
//
// 7.2.19 parseTypeCollectionPropertyElt
// start-element ( URI == propertyElementURIs, attributes == set ( idAttr?, parseCollection ) )
// nodeElementList
// end-element()
-static void
-RDF_ParseTypeCollectionPropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel )
+void RDF_Parser::ParseTypeCollectionPropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel )
{
IgnoreParam(xmpParent); IgnoreParam(xmlNode); IgnoreParam(isTopLevel);
+ XMP_Error error ( kXMPErr_BadXMP, "ParseTypeCollection property element not allowed" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
- XMP_Throw ( "ParseTypeCollection property element not allowed", kXMPErr_BadXMP );
-
-} // RDF_ParseTypeCollectionPropertyElement
-
+} // RDF_Parser::ParseTypeCollectionPropertyElement
// =================================================================================================
-// RDF_ParseTypeOtherPropertyElement
-// =================================
+// RDF_Parser::ParseTypeOtherPropertyElement
+// =========================================
//
// 7.2.20 parseTypeOtherPropertyElt
// start-element ( URI == propertyElementURIs, attributes == set ( idAttr?, parseOther ) )
// propertyEltList
// end-element()
-static void
-RDF_ParseTypeOtherPropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel )
+void RDF_Parser::ParseTypeOtherPropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel )
{
IgnoreParam(xmpParent); IgnoreParam(xmlNode); IgnoreParam(isTopLevel);
+ XMP_Error error ( kXMPErr_BadXMP, "ParseTypeOther property element not allowed" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
- XMP_Throw ( "ParseTypeOther property element not allowed", kXMPErr_BadXMP );
-
-} // RDF_ParseTypeOtherPropertyElement
-
+} // RDF_Parser::ParseTypeOtherPropertyElement
// =================================================================================================
-// RDF_EmptyPropertyElement
-// ========================
+// RDF_Parser::EmptyPropertyElement
+// ================================
//
// 7.2.21 emptyPropertyElt
// start-element ( URI == propertyElementURIs,
@@ -1231,8 +1301,7 @@ RDF_ParseTypeOtherPropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNo
// property with an empty value.
// 4. Otherwise this is a struct, the attributes other than xml:lang, rdf:ID, or rdf:nodeID are fields.
-static void
-RDF_EmptyPropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel )
+void RDF_Parser::EmptyPropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel )
{
bool hasPropertyAttrs = false;
bool hasResourceAttr = false;
@@ -1241,7 +1310,11 @@ RDF_EmptyPropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool
const XML_Node * valueNode = 0; // ! Can come from rdf:value or rdf:resource.
- if ( ! xmlNode.content.empty() ) XMP_Throw ( "Nested content not allowed with rdf:resource or property attributes", kXMPErr_BadRDF );
+ if ( ! xmlNode.content.empty() ) {
+ XMP_Error error ( kXMPErr_BadRDF, "Nested content not allowed with rdf:resource or property attributes" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+ return;
+ }
// First figure out what XMP this maps to and remember the XML node for a simple value.
@@ -1259,20 +1332,36 @@ RDF_EmptyPropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool
break;
case kRDFTerm_resource :
- if ( hasNodeIDAttr ) XMP_Throw ( "Empty property element can't have both rdf:resource and rdf:nodeID", kXMPErr_BadRDF );
- if ( hasValueAttr ) XMP_Throw ( "Empty property element can't have both rdf:value and rdf:resource", kXMPErr_BadXMP );
+ if ( hasNodeIDAttr ) {
+ XMP_Error error ( kXMPErr_BadRDF, "Empty property element can't have both rdf:resource and rdf:nodeID" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+ return;
+ }
+ if ( hasValueAttr ) {
+ XMP_Error error ( kXMPErr_BadXMP, "Empty property element can't have both rdf:value and rdf:resource" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+ return;
+ }
hasResourceAttr = true;
if ( ! hasValueAttr ) valueNode = *currAttr;
break;
case kRDFTerm_nodeID :
- if ( hasResourceAttr ) XMP_Throw ( "Empty property element can't have both rdf:resource and rdf:nodeID", kXMPErr_BadRDF );
+ if ( hasResourceAttr ) {
+ XMP_Error error ( kXMPErr_BadRDF, "Empty property element can't have both rdf:resource and rdf:nodeID" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+ return;
+ }
hasNodeIDAttr = true;
break;
case kRDFTerm_Other :
if ( (*currAttr)->name == "rdf:value" ) {
- if ( hasResourceAttr ) XMP_Throw ( "Empty property element can't have both rdf:value and rdf:resource", kXMPErr_BadXMP );
+ if ( hasResourceAttr ) {
+ XMP_Error error ( kXMPErr_BadXMP, "Empty property element can't have both rdf:value and rdf:resource" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+ return;
+ }
hasValueAttr = true;
valueNode = *currAttr;
} else if ( (*currAttr)->name != "xml:lang" ) {
@@ -1281,8 +1370,12 @@ RDF_EmptyPropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool
break;
default :
- XMP_Throw ( "Unrecognized attribute of empty property element", kXMPErr_BadRDF );
- break;
+ {
+ XMP_Error error ( kXMPErr_BadRDF, "Unrecognized attribute of empty property element" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+ }
+
+ return;
}
@@ -1292,7 +1385,8 @@ RDF_EmptyPropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool
// ! Because of implementation vagaries, the xmpParent is the tree root for top level properties.
// ! The schema is found, created if necessary, by AddChildNode.
- XMP_Node * childNode = AddChildNode ( xmpParent, xmlNode, "", isTopLevel );
+ XMP_Node * childNode = this->AddChildNode ( xmpParent, xmlNode, "", isTopLevel );
+ if ( childNode == 0 ) return; // Ignore lower level errors.
bool childIsStruct = false;
if ( hasValueAttr | hasResourceAttr ) {
@@ -1315,29 +1409,47 @@ RDF_EmptyPropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool
case kRDFTerm_ID :
case kRDFTerm_nodeID :
- break; // Ignore all rdf:ID and rdf:nodeID attributes.w
+ break; // Ignore all rdf:ID and rdf:nodeID attributes.
case kRDFTerm_resource :
- AddQualifierNode ( childNode, **currAttr );
+ this->AddQualifierNode ( childNode, **currAttr );
break;
case kRDFTerm_Other :
if ( (! childIsStruct) || (*currAttr)->name == "xml:lang" ) {
- AddQualifierNode ( childNode, **currAttr );
+ this->AddQualifierNode ( childNode, **currAttr );
} else {
- AddChildNode ( childNode, **currAttr, (*currAttr)->value.c_str(), false );
+ this->AddChildNode ( childNode, **currAttr, (*currAttr)->value.c_str(), false );
}
break;
default :
- XMP_Throw ( "Unrecognized attribute of empty property element", kXMPErr_BadRDF );
- break;
+ {
+ XMP_Error error ( kXMPErr_BadRDF, "Unrecognized attribute of empty property element" );
+ this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error );
+ }
+ continue;
}
}
-} // RDF_EmptyPropertyElement
+} // RDF_Parser::EmptyPropertyElement
+
+// =================================================================================================
+// XMPMeta::ProcessRDF
+// ===================
+//
+// Parse the XML tree of the RDF and build the corresponding XMP tree.
+
+void XMPMeta::ProcessRDF ( const XML_Node & rdfNode, XMP_OptionBits options )
+{
+ IgnoreParam(options);
+
+ RDF_Parser parser ( &this->errorCallback );
+
+ parser.RDF ( &this->tree, rdfNode );
+} // XMPMeta::ProcessRDF
// =================================================================================================
diff --git a/XMPCore/source/WXMPMeta.cpp b/XMPCore/source/WXMPMeta.cpp
index 8b33157..61f13f1 100644
--- a/XMPCore/source/WXMPMeta.cpp
+++ b/XMPCore/source/WXMPMeta.cpp
@@ -1137,6 +1137,53 @@ WXMPMeta_SerializeToBuffer_1 ( XMPMetaRef xmpObjRef,
XMP_EXIT
}
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPMeta_SetDefaultErrorCallback_1 ( XMPMeta_ErrorCallbackWrapper wrapperProc,
+ XMPMeta_ErrorCallbackProc clientProc,
+ void * context,
+ XMP_Uns32 limit,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_Static ( "WXMPMeta_SetDefaultErrorCallback_1" )
+
+ XMPMeta::SetDefaultErrorCallback ( wrapperProc, clientProc, context, limit );
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPMeta_SetErrorCallback_1 ( XMPMetaRef xmpObjRef,
+ XMPMeta_ErrorCallbackWrapper wrapperProc,
+ XMPMeta_ErrorCallbackProc clientProc,
+ void * context,
+ XMP_Uns32 limit,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_SetErrorCallback_1" )
+
+ thiz->SetErrorCallback ( wrapperProc, clientProc, context, limit );
+
+ XMP_EXIT
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void
+WXMPMeta_ResetErrorCallbackLimit_1 ( XMPMetaRef xmpObjRef,
+ XMP_Uns32 limit,
+ WXMP_Result * wResult )
+{
+ XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_ResetErrorCallbackLimit_1" )
+
+ thiz->ResetErrorCallbackLimit ( limit );
+
+ XMP_EXIT
+}
+
// =================================================================================================
#if __cplusplus
diff --git a/XMPCore/source/XMPCore_Impl.cpp b/XMPCore/source/XMPCore_Impl.cpp
index 7f791e5..ddae20a 100644
--- a/XMPCore/source/XMPCore_Impl.cpp
+++ b/XMPCore/source/XMPCore_Impl.cpp
@@ -25,7 +25,7 @@ using namespace std;
// *** Add debug codegen checks, e.g. that typical masking operations really work
// *** Make option constants 0x...UL.
-// Internal code should be using #if with XMP_MacBuild, XMP_WinBuild, or XMP_UNIXBuild.
+// Internal code should be using #if with XMP_MacBuild, XMP_WinBuild, XMP_UNIXBuild or XMP_iOSBuild.
// This is a sanity check in case of accidental use of *_ENV. Some clients use the poor
// practice of defining the *_ENV macro with an empty value.
#if defined ( MAC_ENV )
@@ -40,6 +40,10 @@ using namespace std;
#if ! UNIX_ENV
#error "UNIX_ENV must be defined so that \"#if UNIX_ENV\" is true"
#endif
+#elif defined ( IOS_ENV )
+ #if ! IOS_ENV
+ #error "IOS_ENV must be defined so that \"#if IOS_ENV\" is true"
+ #endif
#endif
// =================================================================================================
diff --git a/XMPCore/source/XMPCore_Impl.hpp b/XMPCore/source/XMPCore_Impl.hpp
index ea76393..180f581 100644
--- a/XMPCore/source/XMPCore_Impl.hpp
+++ b/XMPCore/source/XMPCore_Impl.hpp
@@ -14,6 +14,8 @@
#include "build/XMP_BuildInfo.h"
#include "source/XMP_LibUtils.hpp"
+// #include "XMPCore/source/XMPMeta.hpp"
+
#include "public/include/client-glue/WXMP_Common.hpp"
#include <vector>
@@ -385,8 +387,6 @@ public:
: nodePtr ( new XMP_Node ( _parent, _name, _value, _options ) ) {};
};
-extern void ProcessRDF ( XMP_Node * xmpTree, const XML_Node & xmlTree, XMP_OptionBits options );
-
// =================================================================================================
#endif // __XMPCore_Impl_hpp__
diff --git a/XMPCore/source/XMPMeta-GetSet.cpp b/XMPCore/source/XMPMeta-GetSet.cpp
index 6f3beb5..ed982de 100644
--- a/XMPCore/source/XMPMeta-GetSet.cpp
+++ b/XMPCore/source/XMPMeta-GetSet.cpp
@@ -841,7 +841,7 @@ XMPMeta::SetLocalizedText ( XMP_StringPtr schemaNS,
XMP_StringPtr itemValue,
XMP_OptionBits options )
{
- options = options; // Avoid unused parameter warning.
+ IgnoreParam(options);
XMP_Assert ( (schemaNS != 0) && (arrayName != 0) && (_genericLang != 0) && (_specificLang != 0) ); // Enforced by wrapper.
diff --git a/XMPCore/source/XMPMeta-Parse.cpp b/XMPCore/source/XMPMeta-Parse.cpp
index 634e48c..62b9404 100644
--- a/XMPCore/source/XMPMeta-Parse.cpp
+++ b/XMPCore/source/XMPMeta-Parse.cpp
@@ -105,6 +105,12 @@ static const char * kReplaceLatin1[128] =
// -------------------------------------------------------------------------------------------------
// PickBestRoot
// ------------
+//
+// Pick the first x:xmpmeta among multiple root candidates. If there aren't any, pick the first bare
+// rdf:RDF if that is allowed. The returned root is the rdf:RDF child if an x:xmpmeta element was
+// chosen. The search is breadth first, so a higher level candiate is chosen over a lower level one
+// that was textually earlier in the serialized XML.
+
static const XML_Node * PickBestRoot ( const XML_Node & xmlParent, XMP_OptionBits options )
{
@@ -145,71 +151,19 @@ static const XML_Node * PickBestRoot ( const XML_Node & xmlParent, XMP_OptionBit
//
// If there is a root node, try to extract the version of the previous XMP toolkit.
-static const XML_Node * FindRootNode ( XMPMeta * thiz, const XMLParserAdapter & xmlParser, XMP_OptionBits options )
+static const XML_Node * FindRootNode ( const XMLParserAdapter & xmlParser, XMP_OptionBits options )
{
const XML_Node * rootNode = xmlParser.rootNode;
if ( xmlParser.rootCount > 1 ) rootNode = PickBestRoot ( xmlParser.tree, options );
if ( rootNode == 0 ) return 0;
-
- // We have a root node. Try to extract previous toolkit version number.
-
- XMP_StringPtr verStr = "";
-
+
XMP_Assert ( rootNode->name == "rdf:RDF" );
if ( (options & kXMP_RequireXMPMeta) &&
((rootNode->parent == 0) ||
((rootNode->parent->name != "x:xmpmeta") && (rootNode->parent->name != "x:xapmeta"))) ) return 0;
-
- for ( size_t attrNum = 0, attrLim = rootNode->parent->attrs.size(); attrNum < attrLim; ++attrNum ) {
- const XML_Node * currAttr =rootNode->parent->attrs[attrNum];
- if ( (currAttr->name == "x:xmptk") || (currAttr->name == "x:xaptk") ) {
- verStr = currAttr->value.c_str();
- break;
- }
- }
- // Decode the version number into MMmmuubbb digits. If any part is too big, peg it at 99 or 999.
-
- unsigned long part;
- while ( (*verStr != 0) && ((*verStr < '0') || (*verStr > '9')) ) ++verStr;
-
- part = 0;
- while ( (*verStr != 0) && ('0' <= *verStr) && (*verStr <= '9') ) {
- part = (part * 10) + (*verStr - '0');
- ++verStr;
- }
- if ( part > 99 ) part = 99;
- thiz->prevTkVer = part * 100*100*1000;
-
- part = 0;
- if ( *verStr == '.' ) ++verStr;
- while ( (*verStr != 0) && ('0' <= *verStr) && (*verStr <= '9') ) {
- part = (part * 10) + (*verStr - '0');
- ++verStr;
- }
- if ( part > 99 ) part = 99;
- thiz->prevTkVer += part * 100*1000;
-
- part = 0;
- if ( *verStr == '.' ) ++verStr;
- while ( (*verStr != 0) && ('0' <= *verStr) && (*verStr <= '9') ) {
- part = (part * 10) + (*verStr - '0');
- ++verStr;
- }
- if ( part > 99 ) part = 99;
- thiz->prevTkVer += part * 1000;
-
- part = 0;
- if ( *verStr == '-' ) ++verStr;
- while ( (*verStr != 0) && ('0' <= *verStr) && (*verStr <= '9') ) {
- part = (part * 10) + (*verStr - '0');
- ++verStr;
- }
- if ( part > 999 ) part = 999;
- thiz->prevTkVer += part;
-
return rootNode;
} // FindRootNode
@@ -294,32 +248,41 @@ NormalizeDCArrays ( XMP_Node * xmpTree )
// *** aliases is a simple to x-default alias, the options and qualifiers obviously differ.
static void
-CompareAliasedSubtrees ( XMP_Node * aliasNode, XMP_Node * baseNode, bool outerCall = true )
+CompareAliasedSubtrees ( XMP_Node * aliasNode, XMP_Node * baseNode,
+ XMPMeta::ErrorCallbackInfo & errorCallback, bool outerCall = true )
{
// ! The outermost call is special. The names almost certainly differ. The qualifiers (and
// ! hence options) will differ for an alias to the x-default item of a langAlt array.
+
if ( (aliasNode->value != baseNode->value) ||
(aliasNode->children.size() != baseNode->children.size()) ) {
- XMP_Throw ( "Mismatch between alias and base nodes", kXMPErr_BadXMP );
+ // Keep things simple for now. Aliases are virtually unused, so this is very unlikely to
+ // happen. Recovery can be added later if it becomes important.
+ XMP_Error error(kXMPErr_BadXMP, "Mismatch between alias and base nodes");
+ errorCallback.NotifyClient ( kXMPErrSev_OperationFatal, error );
}
+
if ( ! outerCall ) {
if ( (aliasNode->name != baseNode->name) ||
(aliasNode->options != baseNode->options) ||
(aliasNode->qualifiers.size() != baseNode->qualifiers.size()) ) {
- XMP_Throw ( "Mismatch between alias and base nodes", kXMPErr_BadXMP );
+ // Keep things simple for now. Aliases are virtually unused, so this is very unlikely to
+ // happen. Recovery can be added later if it becomes important.
+ XMP_Error error(kXMPErr_BadXMP, "Mismatch between alias and base nodes");
+ errorCallback.NotifyClient ( kXMPErrSev_OperationFatal, error );
}
}
for ( size_t childNum = 0, childLim = aliasNode->children.size(); childNum < childLim; ++childNum ) {
XMP_Node * aliasChild = aliasNode->children[childNum];
XMP_Node * baseChild = baseNode->children[childNum];
- CompareAliasedSubtrees ( aliasChild, baseChild, false );
+ CompareAliasedSubtrees ( aliasChild, baseChild, errorCallback, false );
}
for ( size_t qualNum = 0, qualLim = aliasNode->qualifiers.size(); qualNum < qualLim; ++qualNum ) {
XMP_Node * aliasQual = aliasNode->qualifiers[qualNum];
XMP_Node * baseQual = baseNode->qualifiers[qualNum];
- CompareAliasedSubtrees ( aliasQual, baseQual, false );
+ CompareAliasedSubtrees ( aliasQual, baseQual, errorCallback, false );
}
} // CompareAliasedSubtrees
@@ -330,13 +293,17 @@ CompareAliasedSubtrees ( XMP_Node * aliasNode, XMP_Node * baseNode, bool outerCa
// ------------------------
static void
-TransplantArrayItemAlias ( XMP_Node * oldParent, size_t oldNum, XMP_Node * newParent )
+TransplantArrayItemAlias ( XMP_Node * oldParent, size_t oldNum, XMP_Node * newParent,
+ XMPMeta::ErrorCallbackInfo & errorCallback )
{
XMP_Node * childNode = oldParent->children[oldNum];
if ( newParent->options & kXMP_PropArrayIsAltText ) {
if ( childNode->options & kXMP_PropHasLang ) {
- XMP_Throw ( "Alias to x-default already has a language qualifier", kXMPErr_BadXMP ); // *** Allow x-default.
+ // Keep things simple for now. Aliases are virtually unused, so this is very unlikely to
+ // happen. Recovery can be added later if it becomes important.
+ XMP_Error error(kXMPErr_BadXMP, "Alias to x-default already has a language qualifier");
+ errorCallback.NotifyClient ( kXMPErrSev_OperationFatal, error ); // *** Allow x-default.
}
childNode->options |= (kXMP_PropHasQualifiers | kXMP_PropHasLang);
XMP_Node * langQual = new XMP_Node ( childNode, "xml:lang", "x-default", kXMP_PropIsQualifier ); // *** AddLangQual util?
@@ -381,7 +348,7 @@ TransplantNamedAlias ( XMP_Node * oldParent, size_t oldNum, XMP_Node * newParent
// -------------------
static void
-MoveExplicitAliases ( XMP_Node * tree, XMP_OptionBits parseOptions )
+MoveExplicitAliases ( XMP_Node * tree, XMP_OptionBits parseOptions, XMPMeta::ErrorCallbackInfo & errorCallback )
{
tree->options ^= kXMP_PropHasAliases;
const bool strictAliasing = ((parseOptions & kXMP_StrictAliasing) != 0);
@@ -426,14 +393,14 @@ MoveExplicitAliases ( XMP_Node * tree, XMP_OptionBits parseOptions )
// An alias to an array item, create the array and transplant the property.
baseNode = new XMP_Node ( baseSchema, basePath[kRootPropStep].step.c_str(), arrayOptions );
baseSchema->children.push_back ( baseNode );
- TransplantArrayItemAlias ( currSchema, propNum, baseNode );
+ TransplantArrayItemAlias ( currSchema, propNum, baseNode, errorCallback );
}
} else if ( basePath.size() == 2 ) {
// The base node does exist and this is a top-to-top alias. Check for conflicts if
// strict aliasing is on. Remove and delete the alias subtree.
- if ( strictAliasing ) CompareAliasedSubtrees ( currProp, baseNode );
+ if ( strictAliasing ) CompareAliasedSubtrees ( currProp, baseNode, errorCallback );
currSchema->children.erase ( currSchema->children.begin() + propNum );
delete currProp;
@@ -451,9 +418,9 @@ MoveExplicitAliases ( XMP_Node * tree, XMP_OptionBits parseOptions )
}
if ( itemNode == 0 ) {
- TransplantArrayItemAlias ( currSchema, propNum, baseNode );
+ TransplantArrayItemAlias ( currSchema, propNum, baseNode, errorCallback );
} else {
- if ( strictAliasing ) CompareAliasedSubtrees ( currProp, itemNode );
+ if ( strictAliasing ) CompareAliasedSubtrees ( currProp, itemNode, errorCallback );
currSchema->children.erase ( currSchema->children.begin() + propNum );
delete currProp;
}
@@ -664,7 +631,7 @@ RepairAltText ( XMP_Node & tree, XMP_StringPtr schemaNS, XMP_StringPtr arrayName
// ----------------
static void
-TouchUpDataModel ( XMPMeta * xmp )
+TouchUpDataModel ( XMPMeta * xmp, XMPMeta::ErrorCallbackInfo & errorCallback )
{
XMP_Node & tree = xmp->tree;
@@ -1062,229 +1029,245 @@ ProcessUTF8Portion ( XMLParserAdapter * xmlParser,
// -------------------------------------------------------------------------------------------------
-// ParseFromBuffer
-// ---------------
-//
-// Although most clients will probably parse everything in one call, we have a buffered API model
-// and need to support even the extreme case of 1 byte at a time parsing. This is considerably
-// complicated by some special cases for 8-bit input. Because of this, the first thing we do is
-// determine whether the input is 8-bit, UTF-16, or UTF-32.
+// ProcessXMLBuffer
+// ----------------
//
-// Both the 8-bit special cases and the encoding determination are easier to do with 8 bytes or more
-// of input. The XMLParserAdapter class has a pending-input buffer for this. At the start of parsing
-// we (moght) try to fill this buffer before determining the input character encoding. After that,
-// we (might) use this buffer with the current input to simplify the logic in Process8BitInput. The
-// "(might)" part means that we don't actually use the pending-input buffer unless we have to. In
-// particular, the common case of single-buffer parsing won't use it.
+// Process one buffer of XML. Returns false if this input is put into the pending input buffer.
-void
-XMPMeta::ParseFromBuffer ( XMP_StringPtr buffer,
- XMP_StringLen xmpSize,
- XMP_OptionBits options )
+bool XMPMeta::ProcessXMLBuffer ( XMP_StringPtr buffer, XMP_StringLen xmpSize, bool lastClientCall )
{
- if ( (buffer == 0) && (xmpSize != 0) ) XMP_Throw ( "Null parse buffer", kXMPErr_BadParam );
- if ( xmpSize == kXMP_UseNullTermination ) xmpSize = strlen ( buffer );
-
- const bool lastClientCall = ((options & kXMP_ParseMoreBuffers) == 0); // *** Could use FlagIsSet & FlagIsClear macros.
-
- this->tree.ClearNode(); // Make sure the target XMP object is totally empty.
- if ( this->xmlParser == 0 ) {
- if ( (xmpSize == 0) && lastClientCall ) return; // Tolerate empty parse. Expat complains if there are no XML elements.
- this->xmlParser = XMP_NewExpatAdapter ( ExpatAdapter::kUseGlobalNamespaces );
- }
-
- XMLParserAdapter& parser = *this->xmlParser;
-
- #if 0 // XMP_DebugBuild
- if ( parser.parseLog != 0 ) {
- char message [200]; // AUDIT: Using sizeof(message) below for snprintf length is safe.
- snprintf ( message, sizeof(message), "<!-- ParseFromBuffer, length = %d, options = %X%s -->", // AUDIT: See above.
- xmpSize, options, (lastClientCall ? " (last)" : "") );
- fwrite ( message, 1, strlen(message), parser.parseLog );
- fflush ( parser.parseLog );
- }
- #endif
-
- try { // Cleanup the tree and xmlParser if anything fails.
-
- // Determine the character encoding before doing any real parsing. This is needed to do the
- // 8-bit special processing.
+ // Determine the character encoding before doing any real parsing. This is needed to do the
+ // 8-bit special processing. This has to be checked on every call, not just the first, in
+ // order to handle the edge case of single byte buffers.
+
+ XMLParserAdapter & parser = *this->xmlParser;
- if ( parser.charEncoding == XMP_OptionBits(-1) ) {
+ if ( parser.charEncoding == XMP_OptionBits(-1) ) {
- if ( (parser.pendingCount == 0) && (xmpSize >= kXMLPendingInputMax) ) {
+ if ( (parser.pendingCount == 0) && (xmpSize >= kXMLPendingInputMax) ) {
- // This ought to be the common case, the first buffer is big enough.
- parser.charEncoding = DetermineInputEncoding ( (XMP_Uns8*)buffer, xmpSize );
+ // This ought to be the common case, the first buffer is big enough.
+ parser.charEncoding = DetermineInputEncoding ( (XMP_Uns8*)buffer, xmpSize );
- } else {
-
- // Try to fill the pendingInput buffer before calling DetermineInputEncoding.
+ } else {
+
+ // Try to fill the pendingInput buffer before calling DetermineInputEncoding.
- size_t pendingOverlap = kXMLPendingInputMax - parser.pendingCount;
- if ( pendingOverlap > xmpSize ) pendingOverlap = xmpSize;
+ size_t pendingOverlap = kXMLPendingInputMax - parser.pendingCount;
+ if ( pendingOverlap > xmpSize ) pendingOverlap = xmpSize;
- memcpy ( &parser.pendingInput[parser.pendingCount], buffer, pendingOverlap ); // AUDIT: Count is safe.
- buffer += pendingOverlap;
- xmpSize -= pendingOverlap;
- parser.pendingCount += pendingOverlap;
+ memcpy ( &parser.pendingInput[parser.pendingCount], buffer, pendingOverlap ); // AUDIT: Count is safe.
+ buffer += pendingOverlap;
+ xmpSize -= pendingOverlap;
+ parser.pendingCount += pendingOverlap;
- if ( (! lastClientCall) && (parser.pendingCount < kXMLPendingInputMax) ) return;
- parser.charEncoding = DetermineInputEncoding ( parser.pendingInput, parser.pendingCount );
-
- #if Trace_ParsingHackery
- fprintf ( stderr, "XMP Character encoding is %d\n", parser.charEncoding );
- #endif
+ if ( (! lastClientCall) && (parser.pendingCount < kXMLPendingInputMax) ) return false; // Wait for the next buffer.
+ parser.charEncoding = DetermineInputEncoding ( parser.pendingInput, parser.pendingCount );
- }
+ #if Trace_ParsingHackery
+ fprintf ( stderr, "XMP Character encoding is %d\n", parser.charEncoding );
+ #endif
+
+ }
+ }
+
+ // We have the character encoding. Process UTF-16 and UTF-32 as is. UTF-8 needs special
+ // handling to take care of things like ISO Latin-1 or unescaped ASCII controls.
+
+ XMP_Assert ( parser.charEncoding != XMP_OptionBits(-1) );
+
+ if ( parser.charEncoding != kXMP_EncodeUTF8 ) {
+
+ if ( parser.pendingCount > 0 ) {
+ // Might have pendingInput from the above portion to determine the character encoding.
+ parser.ParseBuffer ( parser.pendingInput, parser.pendingCount, false );
}
+ parser.ParseBuffer ( buffer, xmpSize, lastClientCall );
- // We have the character encoding. Process UTF-16 and UTF-32 as is. UTF-8 needs special
- // handling to take care of things like ISO Latin-1 or unescaped ASCII controls.
+ } else {
- XMP_Assert ( parser.charEncoding != XMP_OptionBits(-1) );
+ #if Trace_ParsingHackery
+ fprintf ( stderr, "Parsing %d bytes @ %.8X, %s, %d pending, context: %.8s\n",
+ xmpSize, buffer, (lastClientCall ? "last" : "not last"), parser.pendingCount, buffer );
+ #endif
- if ( parser.charEncoding != kXMP_EncodeUTF8 ) {
+ // The UTF-8 processing is a bit complex due to the need to tolerate ISO Latin-1 input.
+ // This is done by scanning the input for byte sequences that are not valid UTF-8,
+ // assuming they are Latin-1 characters in the range 0x80..0xFF. This requires saving a
+ // pending input buffer to handle partial UTF-8 sequences at the end of a buffer.
- if ( parser.pendingCount > 0 ) {
- // Might have pendingInput from the above portion to determine the character encoding.
- parser.ParseBuffer ( parser.pendingInput, parser.pendingCount, false );
- }
- parser.ParseBuffer ( buffer, xmpSize, lastClientCall );
+ while ( parser.pendingCount > 0 ) {
+
+ // We've got some leftover input, process it first then continue with the current
+ // buffer. Try to fill the pendingInput buffer before parsing further. We use a loop
+ // for wierd edge cases like a 2 byte input buffer, using 1 byte for pendingInput,
+ // then having a partial UTF-8 end and need to absorb more.
- } else {
+ size_t pendingOverlap = kXMLPendingInputMax - parser.pendingCount;
+ if ( pendingOverlap > xmpSize ) pendingOverlap = xmpSize;
+
+ memcpy ( &parser.pendingInput[parser.pendingCount], buffer, pendingOverlap ); // AUDIT: Count is safe.
+ parser.pendingCount += pendingOverlap;
+ buffer += pendingOverlap;
+ xmpSize -= pendingOverlap;
+
+ if ( (! lastClientCall) && (parser.pendingCount < kXMLPendingInputMax) ) return false; // Wait for the next buffer.
+ size_t bytesDone = ProcessUTF8Portion ( xmlParser, parser.pendingInput, parser.pendingCount, lastClientCall );
+ size_t bytesLeft = parser.pendingCount - bytesDone;
#if Trace_ParsingHackery
- fprintf ( stderr, "Parsing %d bytes @ %.8X, %s, %d pending, context: %.8s\n",
- xmpSize, buffer, (lastClientCall ? "last" : "not last"), parser.pendingCount, buffer );
+ fprintf ( stderr, " ProcessUTF8Portion handled %d pending bytes\n", bytesDone );
#endif
-
- // The UTF-8 processing is a bit complex due to the need to tolerate ISO Latin-1 input.
- // This is done by scanning the input for byte sequences that are not valid UTF-8,
- // assuming they are Latin-1 characters in the range 0x80..0xFF. This requires saving a
- // pending input buffer to handle partial UTF-8 sequences at the end of a buffer.
- while ( parser.pendingCount > 0 ) {
-
- // We've got some leftover input, process it first then continue with the current
- // buffer. Try to fill the pendingInput buffer before parsing further. We use a loop
- // for wierd edge cases like a 2 byte input buffer, using 1 byte for pendingInput,
- // then having a partial UTF-8 end and need to absorb more.
-
- size_t pendingOverlap = kXMLPendingInputMax - parser.pendingCount;
- if ( pendingOverlap > xmpSize ) pendingOverlap = xmpSize;
-
- memcpy ( &parser.pendingInput[parser.pendingCount], buffer, pendingOverlap ); // AUDIT: Count is safe.
- parser.pendingCount += pendingOverlap;
- buffer += pendingOverlap;
- xmpSize -= pendingOverlap;
-
- if ( (! lastClientCall) && (parser.pendingCount < kXMLPendingInputMax) ) return;
- size_t bytesDone = ProcessUTF8Portion ( &parser, parser.pendingInput, parser.pendingCount, lastClientCall );
- size_t bytesLeft = parser.pendingCount - bytesDone;
-
- #if Trace_ParsingHackery
- fprintf ( stderr, " ProcessUTF8Portion handled %d pending bytes\n", bytesDone );
- #endif
-
- if ( bytesDone == parser.pendingCount ) {
+ if ( bytesDone == parser.pendingCount ) {
- // Done with all of the pending input, move on to the current buffer.
- parser.pendingCount = 0;
+ // Done with all of the pending input, move on to the current buffer.
+ parser.pendingCount = 0;
- } else if ( bytesLeft <= pendingOverlap ) {
+ } else if ( bytesLeft <= pendingOverlap ) {
- // The leftover pending input all came from the current buffer. Exit this loop.
- buffer -= bytesLeft;
- xmpSize += bytesLeft;
- parser.pendingCount = 0;
+ // The leftover pending input all came from the current buffer. Exit this loop.
+ buffer -= bytesLeft;
+ xmpSize += bytesLeft;
+ parser.pendingCount = 0;
- } else if ( xmpSize > 0 ) {
+ } else if ( xmpSize > 0 ) {
- // Pull more of the current buffer into the pending input and try again.
- // Backup by this pass's overlap so the loop entry code runs OK.
- parser.pendingCount -= pendingOverlap;
- buffer -= pendingOverlap;
- xmpSize += pendingOverlap;
+ // Pull more of the current buffer into the pending input and try again.
+ // Backup by this pass's overlap so the loop entry code runs OK.
+ parser.pendingCount -= pendingOverlap;
+ buffer -= pendingOverlap;
+ xmpSize += pendingOverlap;
- } else {
+ } else {
- // There is no more of the current buffer. Wait for more. Partial sequences at
- // the end of the last buffer should be treated as Latin-1 by ProcessUTF8Portion.
- XMP_Assert ( ! lastClientCall );
- parser.pendingCount = bytesLeft;
- memcpy ( &parser.pendingInput[0], &parser.pendingInput[bytesDone], bytesLeft ); // AUDIT: Count is safe.
- return;
+ // There is no more of the current buffer. Wait for more. Partial sequences at
+ // the end of the last buffer should be treated as Latin-1 by ProcessUTF8Portion.
+ XMP_Assert ( ! lastClientCall );
+ parser.pendingCount = bytesLeft;
+ memcpy ( &parser.pendingInput[0], &parser.pendingInput[bytesDone], bytesLeft ); // AUDIT: Count is safe.
+ return false; // Wait for the next buffer.
- }
-
}
-
- // Done with the pending input, process the current buffer.
-
- size_t bytesDone = ProcessUTF8Portion ( &parser, (XMP_Uns8*)buffer, xmpSize, lastClientCall );
+
+ }
+
+ // Done with the pending input, process the current buffer.
- #if Trace_ParsingHackery
- fprintf ( stderr, " ProcessUTF8Portion handled %d additional bytes\n", bytesDone );
- #endif
-
- if ( bytesDone < xmpSize ) {
+ size_t bytesDone = ProcessUTF8Portion ( xmlParser, (XMP_Uns8*)buffer, xmpSize, lastClientCall );
- XMP_Assert ( ! lastClientCall );
- size_t bytesLeft = xmpSize - bytesDone;
- if ( bytesLeft > kXMLPendingInputMax ) XMP_Throw ( "Parser bytesLeft too large", kXMPErr_InternalFailure );
+ #if Trace_ParsingHackery
+ fprintf ( stderr, " ProcessUTF8Portion handled %d additional bytes\n", bytesDone );
+ #endif
+
+ if ( bytesDone < xmpSize ) {
- memcpy ( parser.pendingInput, &buffer[bytesDone], bytesLeft ); // AUDIT: Count is safe.
- parser.pendingCount = bytesLeft;
- return; // Wait for the next buffer.
+ XMP_Assert ( ! lastClientCall );
+ size_t bytesLeft = xmpSize - bytesDone;
+ if ( bytesLeft > kXMLPendingInputMax ) XMP_Throw ( "Parser bytesLeft too large", kXMPErr_InternalFailure );
- }
+ memcpy ( parser.pendingInput, &buffer[bytesDone], bytesLeft ); // AUDIT: Count is safe.
+ parser.pendingCount = bytesLeft;
+ return false; // Wait for the next buffer.
}
+
+ }
+
+ return true; // This buffer has been processed.
+
+} // ProcessXMLBuffer
+
+
+// -------------------------------------------------------------------------------------------------
+// ProcessXMLTree
+// --------------
+
+void XMPMeta::ProcessXMLTree ( XMP_OptionBits options )
+{
- if ( lastClientCall ) {
-
- #if XMP_DebugBuild && DumpXMLParseTree
- if ( parser.parseLog == 0 ) parser.parseLog = stdout;
- DumpXMLTree ( parser.parseLog, parser.tree, 0 );
- #endif
+ #if XMP_DebugBuild && DumpXMLParseTree
+ if ( this->xmlParser->parseLog == 0 ) this->xmlParser->parseLog = stdout;
+ DumpXMLTree ( this->xmlParser->parseLog, this->xmlParser->tree, 0 );
+ #endif
- const XML_Node * xmlRoot = FindRootNode ( this, *this->xmlParser, options );
+ const XML_Node * xmlRoot = FindRootNode ( *this->xmlParser, options );
- if ( xmlRoot != 0 ) {
+ if ( xmlRoot != 0 ) {
- ProcessRDF ( &this->tree, *xmlRoot, options );
- NormalizeDCArrays ( &this->tree );
- if ( this->tree.options & kXMP_PropHasAliases ) MoveExplicitAliases ( &this->tree, options );
- TouchUpDataModel ( this );
-
- // Delete empty schema nodes. Do this last, other cleanup can make empty schema.
- size_t schemaNum = 0;
- while ( schemaNum < this->tree.children.size() ) {
- XMP_Node * currSchema = this->tree.children[schemaNum];
- if ( currSchema->children.size() > 0 ) {
- ++schemaNum;
- } else {
- delete this->tree.children[schemaNum]; // ! Delete the schema node itself.
- this->tree.children.erase ( this->tree.children.begin() + schemaNum );
- }
- }
-
+ this->ProcessRDF ( *xmlRoot, options );
+
+ NormalizeDCArrays ( &this->tree );
+ if ( this->tree.options & kXMP_PropHasAliases ) MoveExplicitAliases ( &this->tree, options, this->errorCallback );
+ TouchUpDataModel ( this, this->errorCallback );
+
+ // Delete empty schema nodes. Do this last, other cleanup can make empty schema.
+ size_t schemaNum = 0;
+ while ( schemaNum < this->tree.children.size() ) {
+ XMP_Node * currSchema = this->tree.children[schemaNum];
+ if ( currSchema->children.size() > 0 ) {
+ ++schemaNum;
+ } else {
+ delete this->tree.children[schemaNum]; // ! Delete the schema node itself.
+ this->tree.children.erase ( this->tree.children.begin() + schemaNum );
}
+ }
+
+ }
+
+} // ProcessXMLTree
+
+
+// -------------------------------------------------------------------------------------------------
+// ParseFromBuffer
+// ---------------
+//
+// Although most clients will probably parse everything in one call, we have a buffered API model
+// and need to support even the extreme case of 1 byte at a time parsing. This is considerably
+// complicated by some special cases for 8-bit input. Because of this, the first thing we do is
+// determine whether the input is 8-bit, UTF-16, or UTF-32.
+//
+// Both the 8-bit special cases and the encoding determination are easier to do with 8 bytes or more
+// of input. The XMLParserAdapter class has a pending-input buffer for this. At the start of parsing
+// we (might) try to fill this buffer before determining the input character encoding. After that,
+// we (might) use this buffer with the current input to simplify the logic in Process8BitInput. The
+// "(might)" part means that we don't actually use the pending-input buffer unless we have to. In
+// particular, the common case of single-buffer parsing won't use it.
+void
+XMPMeta::ParseFromBuffer ( XMP_StringPtr buffer,
+ XMP_StringLen xmpSize,
+ XMP_OptionBits options )
+{
+ if ( (buffer == 0) && (xmpSize != 0) ) XMP_Throw ( "Null parse buffer", kXMPErr_BadParam );
+ if ( xmpSize == kXMP_UseNullTermination ) xmpSize = strlen ( buffer );
+
+ const bool lastClientCall = ((options & kXMP_ParseMoreBuffers) == 0); // *** Could use FlagIsSet & FlagIsClear macros.
+
+ if ( this->xmlParser == 0 ) {
+ this->tree.ClearNode(); // Make sure the target XMP object is totally empty.
+ if ( (xmpSize == 0) && lastClientCall ) return; // Tolerate empty parse. Expat complains if there are no XML elements.
+ this->xmlParser = XMP_NewExpatAdapter ( ExpatAdapter::kUseGlobalNamespaces );
+ this->xmlParser->SetErrorCallback ( &this->errorCallback );
+ }
+
+ try { // Cleanup the tree and xmlParser if anything fails.
+
+ bool done = this->ProcessXMLBuffer ( buffer, xmpSize, lastClientCall );
+ if ( ! done ) return; // Wait for the next buffer.
+
+ if ( lastClientCall ) {
+ this->ProcessXMLTree ( options );
delete this->xmlParser;
this->xmlParser = 0;
-
}
} catch ( ... ) {
delete this->xmlParser;
this->xmlParser = 0;
- prevTkVer = 0;
- this->tree.ClearNode();
throw;
}
diff --git a/XMPCore/source/XMPMeta-Serialize.cpp b/XMPCore/source/XMPMeta-Serialize.cpp
index df2112f..b7373c9 100644
--- a/XMPCore/source/XMPMeta-Serialize.cpp
+++ b/XMPCore/source/XMPMeta-Serialize.cpp
@@ -1167,6 +1167,7 @@ SerializeAsRDF ( const XMPMeta & xmpObj,
} // SerializeAsRDF
+
// -------------------------------------------------------------------------------------------------
// SerializeToBuffer
// -----------------
@@ -1244,6 +1245,7 @@ XMPMeta::SerializeToBuffer ( XMP_VarString * rdfString,
std::string tailStr;
SerializeAsRDF ( *this, *rdfString, tailStr, options, newline, indentStr, baseIndent );
+
if ( charEncoding == kXMP_EncodeUTF8 ) {
if ( options & kXMP_ExactPacketLength ) {
diff --git a/XMPCore/source/XMPMeta.cpp b/XMPCore/source/XMPMeta.cpp
index c1ff237..e1b51d6 100644
--- a/XMPCore/source/XMPMeta.cpp
+++ b/XMPCore/source/XMPMeta.cpp
@@ -52,6 +52,8 @@ using namespace std;
XMP_VarString * xdefaultName = 0; // Needed in XMPMeta-Parse.cpp, MoveExplicitAliases.
+static XMPMeta::ErrorCallbackInfo sDefaultErrorCallback;
+
// These are embedded version strings.
const char * kXMPCore_EmbeddedVersion = kXMPCore_VersionMessage;
@@ -636,12 +638,19 @@ RegisterStandardAliases()
// ============
-XMPMeta::XMPMeta() : tree(XMP_Node(0,"",0)), clientRefs(0), prevTkVer(0), xmlParser(0)
+XMPMeta::XMPMeta() : tree(XMP_Node(0,"",0)), clientRefs(0), xmlParser(0)
{
- // Nothing more to do, clientRefs is incremented in wrapper.
#if XMP_TraceCTorDTor
printf ( "Default construct XMPMeta @ %.8X\n", this );
#endif
+
+ if ( sDefaultErrorCallback.clientProc != 0 ) {
+ this->errorCallback.wrapperProc = sDefaultErrorCallback.wrapperProc;
+ this->errorCallback.clientProc = sDefaultErrorCallback.clientProc;
+ this->errorCallback.context = sDefaultErrorCallback.context;
+ this->errorCallback.limit = sDefaultErrorCallback.limit;
+ }
+
} // XMPMeta
// -------------------------------------------------------------------------------------------------
@@ -810,6 +819,7 @@ XMPMeta::Initialize()
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_Assert ( sizeof(XMP_OptionBits) == 4 ); // Check that option masking work on all 32 bits.
XMP_OptionBits flag = (XMP_OptionBits) (~0UL);
@@ -1212,7 +1222,6 @@ void
XMPMeta::Erase()
{
- this->prevTkVer = 0;
if ( this->xmlParser != 0 ) {
delete ( this->xmlParser );
this->xmlParser = 0;
@@ -1238,6 +1247,7 @@ XMPMeta::Clone ( XMPMeta * clone, XMP_OptionBits options ) const
clone->tree.options = this->tree.options;
clone->tree.name = this->tree.name;
clone->tree.value = this->tree.value;
+ clone->errorCallback = this->errorCallback;
#if 0 // *** XMP_DebugBuild
clone->tree._namePtr = clone->tree.name.c_str();
@@ -1280,3 +1290,84 @@ void XMP_Node::GetLocalURI ( XMP_StringPtr * uriStr, XMP_StringLen * uriSize ) c
}
// =================================================================================================
+// Error notifications
+// ===================
+
+// -------------------------------------------------------------------------------------------------
+// SetDefaultErrorCallback
+// -----------------------
+
+/* class-static */ void
+XMPMeta::SetDefaultErrorCallback ( XMPMeta_ErrorCallbackWrapper wrapperProc,
+ XMPMeta_ErrorCallbackProc clientProc,
+ void * context,
+ XMP_Uns32 limit )
+{
+ XMP_Assert ( wrapperProc != 0 ); // Must always be set by the glue;
+
+ sDefaultErrorCallback.wrapperProc = wrapperProc;
+ sDefaultErrorCallback.clientProc = clientProc;
+ sDefaultErrorCallback.context = context;
+ sDefaultErrorCallback.limit = limit;
+
+} // SetDefaultErrorCallback
+
+// -------------------------------------------------------------------------------------------------
+// SetErrorCallback
+// ----------------
+
+void
+XMPMeta::SetErrorCallback ( XMPMeta_ErrorCallbackWrapper wrapperProc,
+ XMPMeta_ErrorCallbackProc clientProc,
+ void * context,
+ XMP_Uns32 limit )
+{
+ 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;
+
+} // SetErrorCallback
+
+// -------------------------------------------------------------------------------------------------
+// ResetErrorCallbackLimit
+// -----------------------
+
+void
+XMPMeta::ResetErrorCallbackLimit ( XMP_Uns32 limit )
+{
+
+ this->errorCallback.limit = limit;
+ this->errorCallback.notifications = 0;
+ this->errorCallback.topSeverity = kXMPErrSev_Recoverable;
+
+} // ResetErrorCallbackLimit
+
+// -------------------------------------------------------------------------------------------------
+// ErrorCallbackInfo::CanNotify
+// -------------------------------
+//
+// This is const just to be usable from const XMPMeta functions.
+
+bool XMPMeta::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 XMPMeta::ErrorCallbackInfo::ClientCallbackWrapper ( XMP_StringPtr filePath, XMP_ErrorSeverity severity, XMP_Int32 cause, XMP_StringPtr messsage ) const
+{
+ XMP_Bool retValue = (*this->wrapperProc) ( this->clientProc, this->context, severity, cause, messsage );
+ return ConvertXMP_BoolToBool(retValue);
+}
+
+// =================================================================================================
diff --git a/XMPCore/source/XMPMeta.hpp b/XMPCore/source/XMPMeta.hpp
index 9d4cf32..4ca9250 100644
--- a/XMPCore/source/XMPMeta.hpp
+++ b/XMPCore/source/XMPMeta.hpp
@@ -336,6 +336,39 @@ public:
XMP_StringPtr indent,
XMP_Index baseIndent ) const;
+ // ---------------------------------------------------------------------------------------------
+
+ static void
+ SetDefaultErrorCallback ( XMPMeta_ErrorCallbackWrapper wrapperProc,
+ XMPMeta_ErrorCallbackProc clientProc,
+ void * context,
+ XMP_Uns32 limit );
+
+ void
+ SetErrorCallback ( XMPMeta_ErrorCallbackWrapper wrapperProc,
+ XMPMeta_ErrorCallbackProc clientProc,
+ void * context,
+ XMP_Uns32 limit );
+
+ void
+ ResetErrorCallbackLimit ( XMP_Uns32 limit );
+
+ class ErrorCallbackInfo : public GenericErrorCallback {
+ public:
+
+ XMPMeta_ErrorCallbackWrapper wrapperProc;
+ XMPMeta_ErrorCallbackProc clientProc;
+ void * context;
+
+ ErrorCallbackInfo() : wrapperProc(0), clientProc(0), context(0) {};
+
+ void Clear() { this->wrapperProc = 0; this->clientProc = 0; this->context = 0;
+ GenericErrorCallback::Clear(); };
+
+ bool CanNotify() const;
+ bool ClientCallbackWrapper ( XMP_StringPtr filePath, XMP_ErrorSeverity severity, XMP_Int32 cause, XMP_StringPtr messsage ) const;
+ };
+
// =============================================================================================
// ---------------------------------------------------------------------------------------------
@@ -368,10 +401,9 @@ public:
// ! Any data member changes must be propagted to the Clone function!
- XMP_Int32 prevTkVer; // Previous toolkit version as MMmmuubbb (major, minor, micro, build).
- XMP_Node tree;
-
+ XMP_Node tree;
XMLParserAdapter * xmlParser;
+ ErrorCallbackInfo errorCallback;
friend class XMPIterator;
friend class XMPUtils;
@@ -379,13 +411,17 @@ public:
private:
// ! These are hidden on purpose:
- XMPMeta ( const XMPMeta & /* original */ ) : tree(XMP_Node(0,"",0)), clientRefs(0), prevTkVer(0), xmlParser(0)
+ XMPMeta ( const XMPMeta & /* original */ ) : tree(XMP_Node(0,"",0)), clientRefs(0), xmlParser(0)
{ XMP_Throw ( "Call to hidden constructor", kXMPErr_InternalFailure ); };
void operator= ( const XMPMeta & /* rhs */ )
{ XMP_Throw ( "Call to hidden operator=", kXMPErr_InternalFailure ); };
-}; // class XMPMeta
+ // Special support routines for parsing, here to be able to access the errorCallback.
+ void ProcessXMLTree ( XMP_OptionBits options );
+ bool ProcessXMLBuffer ( XMP_StringPtr buffer, XMP_StringLen xmpSize, bool lastClientCall );
+ void ProcessRDF ( const XML_Node & xmlTree, XMP_OptionBits options );
+}; // class XMPMeta
// =================================================================================================
diff --git a/XMPCore/source/XMPUtils-FileInfo.cpp b/XMPCore/source/XMPUtils-FileInfo.cpp
index 7faf187..795a927 100644
--- a/XMPCore/source/XMPUtils-FileInfo.cpp
+++ b/XMPCore/source/XMPUtils-FileInfo.cpp
@@ -1348,7 +1348,7 @@ XMPUtils::DuplicateSubtree ( const XMPMeta & source,
XMP_StringPtr destRoot,
XMP_OptionBits options )
{
- options = options; // Avoid unused parameter warning.
+ IgnoreParam(options);
bool fullSourceTree = false;
bool fullDestTree = false;
diff --git a/XMPCore/source/XMPUtils.cpp b/XMPCore/source/XMPUtils.cpp
index f7a265d..8d7e043 100644
--- a/XMPCore/source/XMPUtils.cpp
+++ b/XMPCore/source/XMPUtils.cpp
@@ -43,10 +43,10 @@ static const char * sBase64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr
// ANSI Time Functions
// -------------------
//
-// A bit of hackery to use the best available time functions. Mac and UNIX have thread safe versions
+// A bit of hackery to use the best available time functions. Mac, UNIX and iOS have thread safe versions
// of gmtime and localtime.
-#if XMP_MacBuild | XMP_UNIXBuild
+#if XMP_MacBuild | XMP_UNIXBuild | XMP_iOSBuild
typedef time_t ansi_tt;
typedef struct tm ansi_tm;
@@ -1783,7 +1783,7 @@ XMPUtils::SetTimeZone ( XMP_DateTime * xmpTime )
if ( (ttx != -1) && (tty != -1) ) {
diffSecs = ansi_difftime ( ttx, tty );
} else {
- #if XMP_MacBuild
+ #if XMP_MacBuild | XMP_iOSBuild
// Looks like Apple's mktime is buggy - see W1140533. But the offset is visible.
diffSecs = tmLocal.tm_gmtoff;
#else
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/build/gcc/XMPFiles.exp b/XMPFiles/resource/linux/XMPFiles.exp
index 6dd2ba2..dea14a7 100644
--- a/build/gcc/XMPFiles.exp
+++ b/XMPFiles/resource/linux/XMPFiles.exp
@@ -11,7 +11,7 @@ global:
WXMPFiles_GetFormatInfo_1;
WXMPFiles_CheckFileFormat_1;
WXMPFiles_CheckPackageFormat_1;
- XMPFiles_GetFileModDate_1;
+ WXMPFiles_GetFileModDate_1;
WXMPFiles_OpenFile_1;
WXMPFiles_CloseFile_1;
WXMPFiles_GetFileInfo_1;
@@ -19,6 +19,13 @@ global:
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 295a7a1..8e692e9 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 a64b6df..51dd4b9 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 225c91f..5aa4435 100644
--- a/XMPFiles/source/FormatSupport/ReconcileTIFF.cpp
+++ b/XMPFiles/source/FormatSupport/ReconcileTIFF.cpp
@@ -19,6 +19,8 @@
#define snprintf _snprintf
#endif
+#include "source/EndianUtils.hpp"
+
#if XMP_WinBuild
#pragma warning ( disable : 4146 ) // unary minus operator applied to unsigned type
#pragma warning ( disable : 4800 ) // forcing value to bool 'true' or 'false'
@@ -387,7 +389,7 @@ ImportSingleTIFF_Short ( const TIFF_Manager::TagInfo & tagInfo, const bool nativ
{
try { // Don't let errors with one stop the others.
- XMP_Uns16 binValue = *((XMP_Uns16*)tagInfo.dataPtr);
+ XMP_Uns16 binValue = GetUns16AsIs ( tagInfo.dataPtr );
if ( ! nativeEndian ) binValue = Flip2 ( binValue );
char strValue[20];
@@ -412,7 +414,7 @@ ImportSingleTIFF_Long ( const TIFF_Manager::TagInfo & tagInfo, const bool native
{
try { // Don't let errors with one stop the others.
- XMP_Uns32 binValue = *((XMP_Uns32*)tagInfo.dataPtr);
+ XMP_Uns32 binValue = GetUns32AsIs ( tagInfo.dataPtr );
if ( ! nativeEndian ) binValue = Flip4 ( binValue );
char strValue[20];
@@ -438,8 +440,8 @@ ImportSingleTIFF_Rational ( const TIFF_Manager::TagInfo & tagInfo, const bool na
try { // Don't let errors with one stop the others.
XMP_Uns32 * binPtr = (XMP_Uns32*)tagInfo.dataPtr;
- XMP_Uns32 binNum = binPtr[0];
- XMP_Uns32 binDenom = binPtr[1];
+ XMP_Uns32 binNum = GetUns32AsIs ( &binPtr[0] );
+ XMP_Uns32 binDenom = GetUns32AsIs ( &binPtr[1] );
if ( ! nativeEndian ) {
binNum = Flip4 ( binNum );
binDenom = Flip4 ( binDenom );
@@ -467,9 +469,14 @@ ImportSingleTIFF_SRational ( const TIFF_Manager::TagInfo & tagInfo, const bool n
{
try { // Don't let errors with one stop the others.
- XMP_Int32 * binPtr = (XMP_Int32*)tagInfo.dataPtr;
- XMP_Int32 binNum = binPtr[0];
- XMP_Int32 binDenom = binPtr[1];
+#if SUNOS_SPARC
+ XMP_Uns32 binPtr[2];
+ memcpy(&binPtr, tagInfo.dataPtr, sizeof(XMP_Uns32)*2);
+#else
+ XMP_Uns32 * binPtr = (XMP_Uns32*)tagInfo.dataPtr;
+#endif //#if SUNOS_SPARC
+ XMP_Int32 binNum = GetUns32AsIs ( &binPtr[0] );
+ XMP_Int32 binDenom = GetUns32AsIs ( &binPtr[1] );
if ( ! nativeEndian ) {
Flip4 ( &binNum );
Flip4 ( &binDenom );
@@ -537,7 +544,7 @@ ImportSingleTIFF_Byte ( const TIFF_Manager::TagInfo & tagInfo,
XMP_Uns8 binValue = *((XMP_Uns8*)tagInfo.dataPtr);
char strValue[20];
- snprintf ( strValue, sizeof(strValue), "%hu", binValue ); // AUDIT: Using sizeof(strValue) is safe.
+ snprintf ( strValue, sizeof(strValue), "%hu", (XMP_Uns16)binValue ); // AUDIT: Using sizeof(strValue) is safe.
xmp->SetProperty ( xmpNS, xmpProp, strValue );
@@ -561,7 +568,7 @@ ImportSingleTIFF_SByte ( const TIFF_Manager::TagInfo & tagInfo,
XMP_Int8 binValue = *((XMP_Int8*)tagInfo.dataPtr);
char strValue[20];
- snprintf ( strValue, sizeof(strValue), "%hd", binValue ); // AUDIT: Using sizeof(strValue) is safe.
+ snprintf ( strValue, sizeof(strValue), "%hd", (short)binValue ); // AUDIT: Using sizeof(strValue) is safe.
xmp->SetProperty ( xmpNS, xmpProp, strValue );
@@ -815,8 +822,8 @@ ImportArrayTIFF_Rational ( const TIFF_Manager::TagInfo & tagInfo, const bool nat
for ( size_t i = 0; i < tagInfo.count; ++i, binPtr += 2 ) {
- XMP_Uns32 binNum = binPtr[0];
- XMP_Uns32 binDenom = binPtr[1];
+ XMP_Uns32 binNum = GetUns32AsIs ( &binPtr[0] );
+ XMP_Uns32 binDenom = GetUns32AsIs ( &binPtr[1] );
if ( ! nativeEndian ) {
binNum = Flip4 ( binNum );
binDenom = Flip4 ( binDenom );
@@ -936,7 +943,7 @@ ImportArrayTIFF_Byte ( const TIFF_Manager::TagInfo & tagInfo,
XMP_Uns8 binValue = *binPtr;
char strValue[20];
- snprintf ( strValue, sizeof(strValue), "%hu", binValue ); // AUDIT: Using sizeof(strValue) is safe.
+ snprintf ( strValue, sizeof(strValue), "%hu", (XMP_Uns16)binValue ); // AUDIT: Using sizeof(strValue) is safe.
xmp->AppendArrayItem ( xmpNS, xmpProp, kXMP_PropArrayIsOrdered, strValue );
@@ -968,7 +975,7 @@ ImportArrayTIFF_SByte ( const TIFF_Manager::TagInfo & tagInfo,
XMP_Int8 binValue = *binPtr;
char strValue[20];
- snprintf ( strValue, sizeof(strValue), "%hd", binValue ); // AUDIT: Using sizeof(strValue) is safe.
+ snprintf ( strValue, sizeof(strValue), "%hd", (short)binValue ); // AUDIT: Using sizeof(strValue) is safe.
xmp->AppendArrayItem ( xmpNS, xmpProp, kXMP_PropArrayIsOrdered, strValue );
@@ -1420,7 +1427,7 @@ ImportTIFF_Flash ( const TIFF_Manager::TagInfo & tagInfo, bool nativeEndian,
{
try { // Don't let errors with one stop the others.
- XMP_Uns16 binValue = *((XMP_Uns16*)tagInfo.dataPtr);
+ XMP_Uns16 binValue = GetUns16AsIs ( tagInfo.dataPtr );
if ( ! nativeEndian ) binValue = Flip2 ( binValue );
bool fired = (bool)(binValue & 1); // Avoid implicit 0/1 conversion.
@@ -1559,7 +1566,7 @@ ImportTIFF_CFATable ( const TIFF_Manager::TagInfo & tagInfo, bool nativeEndian,
SXMPUtils::ComposeStructFieldPath ( xmpNS, xmpProp, kXMP_NS_EXIF, "Values", &arrayPath );
for ( size_t i = (columns * rows); i > 0; --i, ++bytePtr ) {
- snprintf ( buffer, sizeof(buffer), "%hu", *bytePtr ); // AUDIT: Use of sizeof(buffer) is safe.
+ snprintf ( buffer, sizeof(buffer), "%hu", (XMP_Uns16)(*bytePtr) ); // AUDIT: Use of sizeof(buffer) is safe.
xmp->AppendArrayItem ( xmpNS, arrayPath.c_str(), kXMP_PropArrayIsOrdered, buffer );
}
@@ -1679,14 +1686,15 @@ ImportTIFF_GPSCoordinate ( const TIFF_Manager & tiff, const TIFF_Manager::TagInf
secDenom = Flip4 ( secDenom );
}
- degNum = binPtr[0];
- degDenom = binPtr[1];
+ degNum = GetUns32AsIs ( &binPtr[0] );
+ degDenom = GetUns32AsIs ( &binPtr[1] );
+
if ( posInfo.count >= 2 ) {
- minNum = binPtr[2];
- minDenom = binPtr[3];
+ minNum = GetUns32AsIs ( &binPtr[2] );
+ minDenom = GetUns32AsIs ( &binPtr[3] );
if ( posInfo.count >= 3 ) {
- secNum = binPtr[4];
- secDenom = binPtr[5];
+ secNum = GetUns32AsIs ( &binPtr[4] );
+ secDenom = GetUns32AsIs ( &binPtr[5] );
}
}
@@ -1790,12 +1798,12 @@ ImportTIFF_GPSTimeStamp ( const TIFF_Manager & tiff, const TIFF_Manager::TagInfo
if ( (dateStr[10] != 0) && (dateStr[10] != ' ') ) return;
XMP_Uns32 * binPtr = (XMP_Uns32*)timeInfo.dataPtr;
- XMP_Uns32 hourNum = binPtr[0];
- XMP_Uns32 hourDenom = binPtr[1];
- XMP_Uns32 minNum = binPtr[2];
- XMP_Uns32 minDenom = binPtr[3];
- XMP_Uns32 secNum = binPtr[4];
- XMP_Uns32 secDenom = binPtr[5];
+ XMP_Uns32 hourNum = GetUns32AsIs ( &binPtr[0] );
+ XMP_Uns32 hourDenom = GetUns32AsIs ( &binPtr[1] );
+ XMP_Uns32 minNum = GetUns32AsIs ( &binPtr[2] );
+ XMP_Uns32 minDenom = GetUns32AsIs ( &binPtr[3] );
+ XMP_Uns32 secNum = GetUns32AsIs ( &binPtr[4] );
+ XMP_Uns32 secDenom = GetUns32AsIs ( &binPtr[5] );
if ( ! nativeEndian ) {
hourNum = Flip4 ( hourNum );
hourDenom = Flip4 ( hourDenom );
@@ -2116,7 +2124,8 @@ PhotoDataUtils::Import2WayExif ( const TIFF_Manager & exif, SXMPMeta * xmp, int
found = exif.GetTag ( kTIFF_ExifIFD, kTIFF_ExifVersion, &tagInfo );
if ( found && (tagInfo.type == kTIFF_UndefinedType) && (tagInfo.count == 4) ) {
char str[5];
- *((XMP_Uns32*)str) = *((XMP_Uns32*)tagInfo.dataPtr);
+
+ *((XMP_Uns32*)str) = GetUns32AsIs ( tagInfo.dataPtr );
str[4] = 0;
xmp->SetProperty ( kXMP_NS_EXIF, "ExifVersion", str );
}
@@ -2125,7 +2134,8 @@ PhotoDataUtils::Import2WayExif ( const TIFF_Manager & exif, SXMPMeta * xmp, int
found = exif.GetTag ( kTIFF_ExifIFD, kTIFF_FlashpixVersion, &tagInfo );
if ( found && (tagInfo.type == kTIFF_UndefinedType) && (tagInfo.count == 4) ) {
char str[5];
- *((XMP_Uns32*)str) = *((XMP_Uns32*)tagInfo.dataPtr);
+
+ *((XMP_Uns32*)str) = GetUns32AsIs ( tagInfo.dataPtr );
str[4] = 0;
xmp->SetProperty ( kXMP_NS_EXIF, "FlashpixVersion", str );
}
diff --git a/XMPFiles/source/FormatSupport/SWF_Support.cpp b/XMPFiles/source/FormatSupport/SWF_Support.cpp
index ec7b9a6..5ff80f1 100644
--- a/XMPFiles/source/FormatSupport/SWF_Support.cpp
+++ b/XMPFiles/source/FormatSupport/SWF_Support.cpp
@@ -127,7 +127,7 @@ XMP_Int64 SWF_IO::DecompressFileToMemory ( XMP_IO * fileIn, RawDataBlock * dataO
XMP_Int32 ioCount;
XMP_Int64 offsetIn;
const XMP_Int64 lengthIn = fileIn->Length();
- XMP_Enforce ( (SWF_IO::HeaderPrefixSize <= lengthIn) && (lengthIn <= SWF_IO::MaxExpandedSize) );
+ XMP_Enforce ( ((XMP_Int64)SWF_IO::HeaderPrefixSize <= lengthIn) && (lengthIn <= SWF_IO::MaxExpandedSize) );
// Set the uncompressed part of the header. Save the expanded size from the file.
diff --git a/XMPFiles/source/FormatSupport/TIFF_FileWriter.cpp b/XMPFiles/source/FormatSupport/TIFF_FileWriter.cpp
index 40cb9bd..a3bf808 100644
--- a/XMPFiles/source/FormatSupport/TIFF_FileWriter.cpp
+++ b/XMPFiles/source/FormatSupport/TIFF_FileWriter.cpp
@@ -11,8 +11,11 @@
#include "public/include/XMP_Const.h"
#include "XMPFiles/source/FormatSupport/TIFF_Support.hpp"
+
#include "source/XIO.hpp"
+#include "source/EndianUtils.hpp"
+
// =================================================================================================
/// \file TIFF_FileWriter.cpp
/// \brief TIFF_FileWriter is used for memory-based read-write access and all file-based access.
@@ -586,15 +589,26 @@ void TIFF_FileWriter::ParseMemoryStream ( const void* data, XMP_Uns32 length, bo
memcpy ( this->memStream, data, length ); // AUDIT: Safe, malloc'ed length bytes above.
this->ownedStream = true;
}
-
+
this->tiffLength = length;
XMP_Uns32 ifdLimit = this->tiffLength - 6; // An IFD must start before this offset.
- // Find and process the primary, Exif, GPS, and Interoperability IFDs.
+ // Find and process the primary, thumbnail, Exif, GPS, and Interoperability IFDs.
XMP_Uns32 primaryIFDOffset = this->CheckTIFFHeader ( this->memStream, length );
- if ( primaryIFDOffset != 0 ) (void) this->ProcessMemoryIFD ( primaryIFDOffset, kTIFF_PrimaryIFD );
+ if ( primaryIFDOffset != 0 ) {
+ XMP_Uns32 tnailOffset = this->ProcessMemoryIFD ( primaryIFDOffset, kTIFF_PrimaryIFD );
+ if ( tnailOffset != 0 ) {
+ if ( IsOffsetValid ( tnailOffset, 8, ifdLimit ) ) { // Remove a bad Thumbnail IFD Offset
+ ( void ) this->ProcessMemoryIFD ( tnailOffset, kTIFF_TNailIFD );
+ } else {
+ XMP_Error error ( kXMPErr_BadTIFF, "Bad IFD offset" );
+ this->NotifyClient (kXMPErrSev_Recoverable, error );
+ this->DeleteTag ( kTIFF_PrimaryIFD, kTIFF_TNailIFD );
+ }
+ }
+ }
const InternalTagInfo* exifIFDTag = this->FindTagInIFD ( kTIFF_PrimaryIFD, kTIFF_ExifIFDPointer );
if ( (exifIFDTag != 0) && (exifIFDTag->type == kTIFF_LongType) && (exifIFDTag->dataLen == 4) ) {
@@ -605,9 +619,11 @@ void TIFF_FileWriter::ParseMemoryStream ( const void* data, XMP_Uns32 length, bo
const InternalTagInfo* gpsIFDTag = this->FindTagInIFD ( kTIFF_PrimaryIFD, kTIFF_GPSInfoIFDPointer );
if ( (gpsIFDTag != 0) && (gpsIFDTag->type == kTIFF_LongType) && (gpsIFDTag->dataLen == 4) ) {
XMP_Uns32 gpsOffset = this->GetUns32 ( gpsIFDTag->dataPtr );
- if ( (8 <= gpsOffset) && (gpsOffset < ifdLimit) ) { // Remove a bad GPS IFD offset.
+ if ( IsOffsetValid ( gpsOffset, 8, ifdLimit ) ) { // Remove a bad GPS IFD offset.
(void) this->ProcessMemoryIFD ( gpsOffset, kTIFF_GPSInfoIFD );
} else {
+ XMP_Error error ( kXMPErr_BadTIFF, "Bad IFD offset" );
+ this->NotifyClient (kXMPErrSev_Recoverable, error );
this->DeleteTag ( kTIFF_PrimaryIFD, kTIFF_GPSInfoIFDPointer );
}
}
@@ -615,9 +631,11 @@ void TIFF_FileWriter::ParseMemoryStream ( const void* data, XMP_Uns32 length, bo
const InternalTagInfo* interopIFDTag = this->FindTagInIFD ( kTIFF_ExifIFD, kTIFF_InteroperabilityIFDPointer );
if ( (interopIFDTag != 0) && (interopIFDTag->type == kTIFF_LongType) && (interopIFDTag->dataLen == 4) ) {
XMP_Uns32 interopOffset = this->GetUns32 ( interopIFDTag->dataPtr );
- if ( (8 <= interopOffset) && (interopOffset < ifdLimit) ) { // Remove a bad Interoperability IFD offset.
+ if ( IsOffsetValid ( interopOffset, 8, ifdLimit ) ) { // Remove a bad Interoperability IFD offset.
(void) this->ProcessMemoryIFD ( interopOffset, kTIFF_InteropIFD );
} else {
+ XMP_Error error ( kXMPErr_BadTIFF, "Bad IFD offset" );
+ this->NotifyClient (kXMPErrSev_Recoverable, error );
this->DeleteTag ( kTIFF_ExifIFD, kTIFF_InteroperabilityIFDPointer );
}
}
@@ -653,16 +671,22 @@ XMP_Uns32 TIFF_FileWriter::ProcessMemoryIFD ( XMP_Uns32 ifdOffset, XMP_Uns8 ifd
InternalIFDInfo& ifdInfo ( this->containedIFDs[ifd] );
if ( (ifdOffset < 8) || (ifdOffset > (this->tiffLength - kEmptyIFDLength)) ) {
- XMP_Throw ( "Bad IFD offset", kXMPErr_BadTIFF );
+ XMP_Error error ( kXMPErr_BadTIFF, "Bad IFD offset" );
+ this->NotifyClient ( kXMPErrSev_FileFatal, error );
}
XMP_Uns8* ifdPtr = this->memStream + ifdOffset;
XMP_Uns16 tagCount = this->GetUns16 ( ifdPtr );
RawIFDEntry* ifdEntries = (RawIFDEntry*)(ifdPtr+2);
- if ( tagCount >= 0x8000 ) XMP_Throw ( "Outrageous IFD count", kXMPErr_BadTIFF );
+ if ( tagCount >= 0x8000 ) {
+ XMP_Error error ( kXMPErr_BadTIFF, "Outrageous IFD count" );
+ this->NotifyClient ( kXMPErrSev_FileFatal, error );
+ }
+
if ( (XMP_Uns32)(2 + tagCount*12 + 4) > (this->tiffLength - ifdOffset) ) {
- XMP_Throw ( "Out of bounds IFD", kXMPErr_BadTIFF );
+ XMP_Error error ( kXMPErr_BadTIFF, "Out of bounds IFD" );
+ this->NotifyClient ( kXMPErrSev_FileFatal, error );
}
ifdInfo.origIFDOffset = ifdOffset;
@@ -682,7 +706,11 @@ XMP_Uns32 TIFF_FileWriter::ProcessMemoryIFD ( XMP_Uns32 ifdOffset, XMP_Uns8 ifd
InternalTagInfo& mapTag = newPos->second;
mapTag.dataLen = mapTag.origDataLen = mapTag.count * (XMP_Uns32)kTIFF_TypeSizes[mapTag.type];
- mapTag.smallValue = rawTag->dataOrOffset; // Keep the value or offset in stream byte ordering.
+#if SUNOS_SPARC
+ mapTag.smallValue = IE.getUns32(&rawTag->dataOrOffset);
+#else
+ mapTag.smallValue = GetUns32AsIs ( &rawTag->dataOrOffset ); // Keep the value or offset in stream byte ordering.
+#endif //#if SUNOS_SPARC
if ( mapTag.dataLen <= 4 ) {
mapTag.origDataOffset = ifdOffset + 2 + (12 * (XMP_Uns32)i) + 8; // Compute the data offset.
@@ -720,39 +748,50 @@ XMP_Uns32 TIFF_FileWriter::ProcessMemoryIFD ( XMP_Uns32 ifdOffset, XMP_Uns8 ifd
void TIFF_FileWriter::ParseFileStream ( XMP_IO* fileRef )
{
- bool ok;
- IOBuffer ioBuf;
this->DeleteExistingInfo();
this->fileParsed = true;
this->tiffLength = (XMP_Uns32) fileRef->Length();
- if ( this->tiffLength == 0 ) return;
+ if ( this->tiffLength < 8 ) return; // Ignore empty or impossibly short.
+ fileRef->Rewind ( );
XMP_Uns32 ifdLimit = this->tiffLength - 6; // An IFD must start before this offset.
// Find and process the primary, Exif, GPS, and Interoperability IFDs.
- ioBuf.filePos = 0;
- fileRef->Rewind ( );
- ok = CheckFileSpace ( fileRef, &ioBuf, 8 );
- if ( ! ok ) XMP_Throw ( "TIFF too small", kXMPErr_BadTIFF );
+ XMP_Uns8 tiffHeader [8];
+ fileRef->ReadAll ( tiffHeader, sizeof(tiffHeader) );
+ XMP_Uns32 primaryIFDOffset = this->CheckTIFFHeader ( tiffHeader, this->tiffLength );
- XMP_Uns32 primaryIFDOffset = this->CheckTIFFHeader ( ioBuf.ptr, this->tiffLength );
-
- if ( primaryIFDOffset != 0 ) (void) this->ProcessFileIFD ( kTIFF_PrimaryIFD, primaryIFDOffset, fileRef, &ioBuf );
+ if ( primaryIFDOffset == 0 ) {
+ return;
+ } else {
+ XMP_Uns32 tnailOffset = this->ProcessFileIFD ( kTIFF_PrimaryIFD, primaryIFDOffset, fileRef );
+ if ( tnailOffset != 0 ) {
+ if ( IsOffsetValid ( tnailOffset, 8, ifdLimit ) ) {
+ ( void ) this->ProcessFileIFD ( kTIFF_TNailIFD, tnailOffset, fileRef );
+ } else {
+ XMP_Error error ( kXMPErr_BadTIFF, "Bad IFD offset" );
+ this->NotifyClient ( kXMPErrSev_Recoverable, error );
+ this->DeleteTag( kTIFF_PrimaryIFD, kTIFF_TNailIFD );
+ }
+ }
+ }
const InternalTagInfo* exifIFDTag = this->FindTagInIFD ( kTIFF_PrimaryIFD, kTIFF_ExifIFDPointer );
if ( (exifIFDTag != 0) && (exifIFDTag->type == kTIFF_LongType) && (exifIFDTag->count == 1) ) {
XMP_Uns32 exifOffset = this->GetUns32 ( exifIFDTag->dataPtr );
- (void) this->ProcessFileIFD ( kTIFF_ExifIFD, exifOffset, fileRef, &ioBuf );
+ (void) this->ProcessFileIFD ( kTIFF_ExifIFD, exifOffset, fileRef );
}
const InternalTagInfo* gpsIFDTag = this->FindTagInIFD ( kTIFF_PrimaryIFD, kTIFF_GPSInfoIFDPointer );
if ( (gpsIFDTag != 0) && (gpsIFDTag->type == kTIFF_LongType) && (gpsIFDTag->count == 1) ) {
XMP_Uns32 gpsOffset = this->GetUns32 ( gpsIFDTag->dataPtr );
- if ( (8 <= gpsOffset) && (gpsOffset < ifdLimit) ) { // Remove a bad GPS IFD offset.
- (void) this->ProcessFileIFD ( kTIFF_GPSInfoIFD, gpsOffset, fileRef, &ioBuf );
+ if ( IsOffsetValid (gpsOffset, 8, ifdLimit ) ) { // Remove a bad GPS IFD offset.
+ (