summaryrefslogtreecommitdiff
path: root/XMPFiles/source/XMPFiles.hpp
blob: d6d280b4475c64e683ebf3a382cd04357ef159a6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
#ifndef __XMPFiles_hpp__
#define __XMPFiles_hpp__	1

// =================================================================================================
// Copyright Adobe
// Copyright 2004 Adobe
// 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. If you have received this file from a source other 
// than Adobe, then your use, modification, or distribution of it requires the prior written permission
// of Adobe.
// =================================================================================================

#include "public/include/XMP_Environment.h"	// ! This must be the first include.

#include <string>
#define TXMP_STRING_TYPE std::string
#include "public/include/XMP.hpp"

#include "public/include/XMP_IO.hpp"
#include "source/SafeStringAPIs.h"
#include "source/XMP_ProgressTracker.hpp"

class XMPFileHandler;
namespace Common{ struct XMPFileHandlerInfo; }

// =================================================================================================
/// \file XMPFiles.hpp
/// \brief High level support to access metadata in files of interest to Adobe applications.
///
/// This header ...
///
// =================================================================================================

// =================================================================================================
// *** Usage Notes (eventually to become Doxygen comments) ***
// ===========================================================
//
// This is the main part of the internal (DLL side) implementation of XMPFiles. Other parts are
// the entry point wrappers and the file format handlers. The XMPFiles class distills the client
// API from TXMPFiles.hpp, removing convenience overloads and substituting a pointer/length pair
// for output strings.
//
// The wrapper functions provide a stable binary interface and perform minor impedance correction
// between the client template API from TDocMeta.hpp and the DLL's XMPFiles class. The details of
// the wrappers should be considered private.
//
// File handlers are registered during DLL initialization with hard coded calls in Init_XMPFiles.
// Each file handler provides 2 standalone functions, CheckFormatProc and DocMetaHandlerCTor, plus a
// class derived from DocMetaHandler. The format and capability flags are passed when registering.
// This allows the same physical handler to be registered for multiple formats.
//
// -------------------------------------------------------------------------------------------------
//
// Basic outlines of the processing by the XMPFiles methods:
//
//	Constructor:
//		- Minimal work to create an empty XMPFiles object, set the ref count to 1.
//
//	Destructor:
//		- Decrement the ref count, return if greater than zero.
//		- Call LFA_Close if necessary.
////
//	GetFormatInfo:
//		- Return the flags for the registered handler.
//
//	OpenFile:
//		- The physical file is opened via LFA_OpenFile.
//		- A handler is selected by calling the registered format checkers.
//		- The handler object is created by calling the registered constructor proc.
//
//	CloseFile:
//		- Return if there is no open file (not an error).
//		- If not a crash-safe update (includes read-only or no update), or the handler owns the file:
//			- Throw an exception if the handler owns the file but does not support safe update.
//			- If the file needs updating, call the handler's UpdateFile method.
//		- else:
//			- If the handler supports file rewrite:
//				- *** This might not preserve ownership and permissions.
//				- Create an empty temp file.
//				- Call the handler's WriteFile method, writing to the temp file.
//			- else
//				- *** This preserves ownership, permissions, and Mac resources.
//				- Copy the original file to a temp name (Mac data fork only).
//				- Rename the original file to a different temp name.
//				- Rename the copy file back to the original name.
//				- Call the handler's UpdateFile method for the "original as temp" file.
//			- Close both the original and temp files.
//			- Delete the file with the original name.
//			- Rename the temp file to the original name.
//		- Delete the handler object.
//		- Call LFA_Close if necessary.
//
//	GetFileInfo:
//		- Return the file info from the XMPFiles member variables.
//
//	GetXMP:
//		- Throw an exception if there is no open file.
//		- Call the handler's GetXMP method.
//
//	PutXMP:
//		- Throw an exception if there is no open file.
//		- Call the handler's PutXMP method.
//
//	CanPutXMP:
//		- Implement roughly as shown in TXMPFiles.hpp, there is no handler CanPutXMP method.
//
// -------------------------------------------------------------------------------------------------
//
// The format checker should do nothing but the minimal work to identify the overall file format.
// In particular it should not look for XMP or other metadata. Note that the format checker has no
// means to carry state forward, it just returns a yes/no answer about a particular file format.
//
// The format checker and file handler should use the LFA_* functions for all I/O. They should not
// open or close the file themselves unless the handler sets the "handler-owns-file" flag.
//
// The format checker is passed the format being checked, allowing one checker to handle multiple
// formats. It is passed the LFA file ref so that it can do additional reads if necessary. The
// buffer is from the start of the file, the file will be positioned to the byte following the
// buffer. The buffer length will be at least 4K, unless the file is smaller in which case it will
// be the length of the file. This buffer may be reused for additional reads.
//
// Identifying some file formats can require checking variable length strings. Doing seeks and reads
// for each is suboptimal. There are utilities to maintain a rolling buffer and ensure that a given
// amount of data is available. See the template file handler code for details.
//
// -------------------------------------------------------------------------------------------------
//
// The file handler has no explicit open and close methods. These are implicit in the handler's
// constructor and destructor. The file handler should use the XMPFiles member variables for the
// active file ref (and path if necessary), unless it owns the file. Note that these might change
// between the open and close in the case of crash-safe updates. Don't copy the XMPFiles member
// variables in the handler's constructor, save the pointer to the XMPFiles object and access
// directly as needed.
//
// The handler should have an UpdateFile method. This is called from XMPFiles::CloseFile if the
// file needs to be updated. The handler's destructor must only close the file, not update it.
// The handler can optionally have a WriteFile method, if it can rewrite the entire file.
//
// The handler is free to use its best judgment about caching parts of the file in memory. Overall
// speed of a single open/get/put/close cycle is probably the best goal, assuming a modern processor
// with a reasonable (significant but not enormous) amount of RAM.
//
// The handler methods will be called in a per-object thread safe manner. Concurrent access might
// occur for different objects, but not for the same object. The handler's constructor and destructor
// will always be globally serialized, so they can safely modify global data structures.
//
// (Testing issue: What about separate XMPFiles objects accessing the same file?)
//
// Handler's must not have any global objects that are heap allocated. Use pointers to objects that
// are allocated and deleted during the XMPFiles initialization and termination process. Some
// client apps are very picky about what they detect as memory leaks.
//
//    static char gSomeBuffer [10*1000];          // OK, not from the heap.
//    static std::string gSomeString;             // Not OK, content from the heap.
//    static std::vector<int> gSomeVector;        // Not OK, content from the heap.
//    static std::string * gSomeString = 0;       // OK, alloc at init, delete at term.
//    static std::vector<int> * gSomeVector = 0;  // OK, alloc at init, delete at term.
//
// =================================================================================================

class XMPFiles {
public:
	static bool Initialize(XMP_OptionBits options, const char* pluginFolder, const char* plugins = NULL);
	static void Terminate();

	static void GetVersionInfo(XMP_VersionInfo * info);

	static bool GetFormatInfo(XMP_FileFormat format, XMP_OptionBits * flags = 0);

	static bool GetFileModDate(
		XMP_StringPtr filePath,
		XMP_DateTime * modDate,
		XMP_FileFormat * format = 0,
		XMP_OptionBits options = 0);

	static bool GetFileModDate(
		const Common::XMPFileHandlerInfo& hdlInfo,
		XMP_StringPtr    clientPath,
		XMP_DateTime *   modDate,
		XMP_OptionBits   options = 0 );

	static XMP_FileFormat CheckFileFormat(XMP_StringPtr filePath);
	static XMP_FileFormat CheckPackageFormat(XMP_StringPtr folderPath);

	static bool GetAssociatedResources ( 
		XMP_StringPtr              filePath,
        std::vector<std::string> * resourceList,
        XMP_FileFormat             format  = kXMP_UnknownFile , 
        XMP_OptionBits             options  = 0 );

	static bool GetAssociatedResources (
		const Common::XMPFileHandlerInfo& hdlInfo,
		XMP_StringPtr              filePath,
        std::vector<std::string> * resourceList,
        XMP_OptionBits             options  = 0 );

	static bool IsMetadataWritable ( 
		XMP_StringPtr  filePath,
        XMP_Bool *     writable,    
        XMP_FileFormat format  = kXMP_UnknownFile ,
        XMP_OptionBits options  = 0 );

	static bool IsMetadataWritable (
		const Common::XMPFileHandlerInfo& hdlInfo,
		XMP_StringPtr  filePath,
        XMP_Bool *     writable,    
        XMP_OptionBits options  = 0 );

	static void SetDefaultProgressCallback(const XMP_ProgressTracker::CallbackInfo & cbInfo);
	static void SetDefaultErrorCallback(XMPFiles_ErrorCallbackWrapper wrapperProc,
		XMPFiles_ErrorCallbackProc clientProc,
		void * context,
		XMP_Uns32 limit);

	XMPFiles();
	virtual ~XMPFiles() NO_EXCEPT_FALSE;

	bool OpenFile(XMP_StringPtr filePath, XMP_FileFormat format = kXMP_UnknownFile, XMP_OptionBits openFlags = 0);
	bool OpenFile(const Common::XMPFileHandlerInfo & hdlInfo, XMP_StringPtr filePath, XMP_OptionBits openFlags = 0);

#if XMP_StaticBuild	// ! Client XMP_IO objects can only be used in static builds.
	bool OpenFile(XMP_IO * clientIO, XMP_FileFormat format = kXMP_UnknownFile, XMP_OptionBits openFlags = 0);
	bool OpenFile(const Common::XMPFileHandlerInfo & hdlInfo, XMP_IO * clientIO, XMP_OptionBits openFlags = 0);
#endif

	void CloseFile(XMP_OptionBits closeFlags = 0);

	bool GetFileInfo(
		XMP_StringPtr * filePath = 0,
		XMP_StringLen * filePathLen = 0,
		XMP_OptionBits * openFlags = 0,
		XMP_FileFormat * format = 0,
		XMP_OptionBits * handlerFlags = 0 ) const;

	bool GetXMP(
		SXMPMeta * xmpObj = 0,
		XMP_StringPtr * xmpPacket = 0,
		XMP_StringLen * xmpPacketLen = 0,
		XMP_PacketInfo * packetInfo = 0);

	void PutXMP(const SXMPMeta & xmpObj);
	void PutXMP(XMP_StringPtr xmpPacket, XMP_StringLen xmpPacketLen = kXMP_UseNullTermination);

	bool CanPutXMP(const SXMPMeta & xmpObj);
	bool CanPutXMP(XMP_StringPtr xmpPacket, XMP_StringLen xmpPacketLen = kXMP_UseNullTermination);
	void SetAbortProc(XMP_AbortProc abortProc, void * abortArg);

	void SetProgressCallback(const XMP_ProgressTracker::CallbackInfo & cbInfo);

	void SetErrorCallback(
		XMPFiles_ErrorCallbackWrapper wrapperProc,
		XMPFiles_ErrorCallbackProc clientProc,
		void * context,
		XMP_Uns32 limit);
	void ResetErrorCallbackLimit(XMP_Uns32 limit);

	class ErrorCallbackInfo : public GenericErrorCallback {
	public:
		XMPFiles_ErrorCallbackWrapper	wrapperProc;
		XMPFiles_ErrorCallbackProc		clientProc;
		void *							context;
		std::string						filePath;

		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;
	};

	inline bool UsesClientIO() { return this->filePath.empty(); };
	inline bool UsesLocalIO() { return ( ! this->UsesClientIO() ); };
	inline void SetFilePath(XMP_StringPtr _filePath) { filePath = _filePath; errorCallback.filePath = _filePath; }
	inline void ClearFilePath() { filePath.clear(); errorCallback.filePath.clear(); }
	inline const std::string& GetFilePath() { return filePath; }

	// Leave this data public so file handlers can see it.
	XMP_Int32				clientRefs;	// ! Must be signed to allow decrement from zero.
	XMP_ReadWriteLock		lock;
	XMP_FileFormat			format;
	XMP_IO *				ioRef;		// Non-zero if a file is open.
	XMP_OptionBits			openFlags;
	XMPFileHandler *		handler;	// Non-null if a file is open.
	void *					tempPtr;	// For use between the CheckProc and handler creation.
	XMP_Uns32				tempUI32;
	XMP_AbortProc			abortProc;
	void *					abortArg;
	XMP_ProgressTracker *	progressTracker;
	ErrorCallbackInfo		errorCallback;

private:
	std::string				filePath;	// Empty for client-managed I/O.
};

bool ErrorCallbackForXMPMeta(void * context, XMP_ErrorSeverity severity, XMP_Int32 cause, XMP_StringPtr message);

#endif /* __XMPFiles_hpp__ */