File: TIFF_Support.cpp

package info (click to toggle)
exempi 2.6.3-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 11,764 kB
  • sloc: cpp: 79,738; sh: 4,510; ansic: 538; makefile: 383
file content (460 lines) | stat: -rw-r--r-- 14,575 bytes parent folder | download | duplicates (2)
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
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
// =================================================================================================
// Copyright Adobe
// Copyright 2006 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. 
// =================================================================================================

#include "public/include/XMP_Environment.h"	// ! XMP_Environment.h must be the first included header.
#include "public/include/XMP_Const.h"

#include "XMPFiles/source/FormatSupport/TIFF_Support.hpp"
#include "source/EndianUtils.hpp"
#include "source/XIO.hpp"

#include "source/UnicodeConversions.hpp"

static const char * kTIFF_TypeNames[] = { "ShortOrLong", "BYTE", "ASCII", "SHORT", "LONG", "RATIONAL",
										  "SBYTE", "UNDEFINED", "SSHORT", "SLONG", "SRATIONAL",
										  "FLOAT", "DOUBLE" };

// =================================================================================================
/// \file TIFF_Support.cpp
/// \brief Manager for parsing and serializing TIFF streams.
///
// =================================================================================================

// =================================================================================================
// TIFF_Manager::TIFF_Manager
// ==========================

static bool sFirstCTor = true;

TIFF_Manager::TIFF_Manager()
	: GetUns16(0), GetUns32(0), GetFloat(0), GetDouble(0),
	  PutUns16(0), PutUns32(0), PutFloat(0), PutDouble(0),
	  bigEndian(false), nativeEndian(false), errorCallbackPtr( NULL )
{

	if ( sFirstCTor ) {
		sFirstCTor = false;
		#if XMP_DebugBuild
			for ( int ifd = 0; ifd < kTIFF_KnownIFDCount; ++ifd ) {	// Make sure the known tag arrays are sorted.
				for ( const XMP_Uns16* idPtr = sKnownTags[ifd]; *idPtr != 0xFFFF; ++idPtr ) {
					XMP_Assert ( *idPtr < *(idPtr+1) );
				}
			}
		#endif
	}

}	// TIFF_Manager::TIFF_Manager

// =================================================================================================
// TIFF_Manager::CheckTIFFHeader
// =============================
//
// Checks the 4 byte TIFF prefix for validity and endianness. Sets the endian flags and the Get
// function pointers. Returns the 0th IFD offset.

XMP_Uns32 TIFF_Manager::CheckTIFFHeader ( const XMP_Uns8* tiffPtr, XMP_Uns32 length )
{
	if ( length < kEmptyTIFFLength ) XMP_Throw ( "The TIFF is too small", kXMPErr_BadTIFF );

	XMP_Uns32 tiffPrefix = (tiffPtr[0] << 24) | (tiffPtr[1] << 16) | (tiffPtr[2] << 8) | (tiffPtr[3]);

	if ( tiffPrefix == kBigEndianPrefix ) {
		this->bigEndian = true;
	} else if ( tiffPrefix == kLittleEndianPrefix ) {
		this->bigEndian = false;
	} else {
		XMP_Throw ( "Unrecognized TIFF prefix", kXMPErr_BadTIFF );
	}

	this->nativeEndian = (this->bigEndian == kBigEndianHost);

	if ( this->bigEndian ) {

		this->GetUns16  = GetUns16BE;
		this->GetUns32  = GetUns32BE;
		this->GetFloat  = GetFloatBE;
		this->GetDouble = GetDoubleBE;

		this->PutUns16  = PutUns16BE;
		this->PutUns32  = PutUns32BE;
		this->PutFloat  = PutFloatBE;
		this->PutDouble = PutDoubleBE;

	} else {

		this->GetUns16  = GetUns16LE;
		this->GetUns32  = GetUns32LE;
		this->GetFloat  = GetFloatLE;
		this->GetDouble = GetDoubleLE;

		this->PutUns16  = PutUns16LE;
		this->PutUns32  = PutUns32LE;
		this->PutFloat  = PutFloatLE;
		this->PutDouble = PutDoubleLE;

	}

	XMP_Uns32 mainIFDOffset = this->GetUns32 ( tiffPtr+4 );	// ! Do this after setting the Get/Put procs!

	if ( mainIFDOffset != 0 ) {	// Tolerate empty TIFF even though formally invalid.
		if ( (length < (kEmptyTIFFLength + kEmptyIFDLength)) ||
			 (mainIFDOffset < kEmptyTIFFLength) || (mainIFDOffset > (length - kEmptyIFDLength)) ) {
			XMP_Throw ( "Invalid primary IFD offset", kXMPErr_BadTIFF );
		}
	}

	return mainIFDOffset;

}	// TIFF_Manager::CheckTIFFHeader

// =================================================================================================
// TIFF_Manager::SetTag_Integer
// ============================

void TIFF_Manager::SetTag_Integer ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns32 data32 )
{

	if ( data32 > 0xFFFF ) {
		this->SetTag ( ifd, id, kTIFF_LongType, 1, &data32 );
	} else {
		XMP_Uns16 data16 = (XMP_Uns16)data32;
		this->SetTag ( ifd, id, kTIFF_ShortType, 1, &data16 );
	}

}	// TIFF_Manager::SetTag_Integer

// =================================================================================================
// TIFF_Manager::SetTag_Byte
// =========================

void TIFF_Manager::SetTag_Byte ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns8 data )
{

	this->SetTag ( ifd, id, kTIFF_ByteType, 1, &data );

}	// TIFF_Manager::SetTag_Byte

// =================================================================================================
// TIFF_Manager::SetTag_SByte
// ==========================

void TIFF_Manager::SetTag_SByte ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Int8 data )
{

	this->SetTag ( ifd, id, kTIFF_SByteType, 1, &data );

}	// TIFF_Manager::SetTag_SByte

// =================================================================================================
// TIFF_Manager::SetTag_Short
// ==========================

void TIFF_Manager::SetTag_Short ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns16 clientData )
{
	XMP_Uns16 streamData;

	this->PutUns16 ( clientData, &streamData );
	this->SetTag ( ifd, id, kTIFF_ShortType, 1, &streamData );

}	// TIFF_Manager::SetTag_Short

// =================================================================================================
// TIFF_Manager::SetTag_SShort
// ===========================

 void TIFF_Manager::SetTag_SShort ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Int16 clientData )
{
	XMP_Int16 streamData;

	this->PutUns16 ( (XMP_Uns16)clientData, &streamData );
	this->SetTag ( ifd, id, kTIFF_SShortType, 1, &streamData );

}	// TIFF_Manager::SetTag_SShort

// =================================================================================================
// TIFF_Manager::SetTag_Long
// =========================

 void TIFF_Manager::SetTag_Long ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns32 clientData )
{
	XMP_Uns32 streamData;

	this->PutUns32 ( clientData, &streamData );
	this->SetTag ( ifd, id, kTIFF_LongType, 1, &streamData );

}	// TIFF_Manager::SetTag_Long

// =================================================================================================
// TIFF_Manager::SetTag_SLong
// ==========================

 void TIFF_Manager::SetTag_SLong ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Int32 clientData )
{
	XMP_Int32 streamData;

	this->PutUns32 ( (XMP_Uns32)clientData, &streamData );
	this->SetTag ( ifd, id, kTIFF_SLongType, 1, &streamData );

}	// TIFF_Manager::SetTag_SLong

// =================================================================================================
// TIFF_Manager::SetTag_Rational
// =============================

struct StreamRational { XMP_Uns32 num, denom; };

 void TIFF_Manager::SetTag_Rational ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns32 clientNum, XMP_Uns32 clientDenom )
{
	StreamRational streamData;

	this->PutUns32 ( clientNum, &streamData.num );
	this->PutUns32 ( clientDenom, &streamData.denom );
	this->SetTag ( ifd, id, kTIFF_RationalType, 1, &streamData );

}	// TIFF_Manager::SetTag_Rational

// =================================================================================================
// TIFF_Manager::SetTag_SRational
// ==============================

 void TIFF_Manager::SetTag_SRational ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Int32 clientNum, XMP_Int32 clientDenom )
{
	StreamRational streamData;

	this->PutUns32 ( (XMP_Uns32)clientNum, &streamData.num );
	this->PutUns32 ( (XMP_Uns32)clientDenom, &streamData.denom );
	this->SetTag ( ifd, id, kTIFF_SRationalType, 1, &streamData );

}	// TIFF_Manager::SetTag_SRational

// =================================================================================================
// TIFF_Manager::SetTag_Float
// ==========================

 void TIFF_Manager::SetTag_Float ( XMP_Uns8 ifd, XMP_Uns16 id, float clientData )
{
	float streamData;

	this->PutFloat ( clientData, &streamData );
	this->SetTag ( ifd, id, kTIFF_FloatType, 1, &streamData );

}	// TIFF_Manager::SetTag_Float

// =================================================================================================
// TIFF_Manager::SetTag_Double
// ===========================

 void TIFF_Manager::SetTag_Double ( XMP_Uns8 ifd, XMP_Uns16 id, double clientData )
{
	double streamData;

	this->PutDouble ( clientData, &streamData );
	this->SetTag ( ifd, id, kTIFF_DoubleType, 1, &streamData );

}	// TIFF_Manager::SetTag_Double

// =================================================================================================
// TIFF_Manager::SetTag_ASCII
// ===========================

void TIFF_Manager::SetTag_ASCII ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_StringPtr data )
{

	this->SetTag ( ifd, id, kTIFF_ASCIIType, (XMP_Uns32)(strlen(data) + 1), data );	// ! Include trailing nul.

}	// TIFF_Manager::SetTag_ASCII

// =================================================================================================
// UTF16_to_UTF8
// =============

static void UTF16_to_UTF8 ( const UTF16Unit * utf16Ptr, size_t utf16Len, bool bigEndian, std::string * outStr )
{
	UTF16_to_UTF8_Proc ToUTF8 = 0;
	if ( bigEndian ) {
		ToUTF8 = UTF16BE_to_UTF8;
	} else {
		ToUTF8 = UTF16LE_to_UTF8;
	}

	UTF8Unit buffer [1000];
	size_t inCount, outCount;

	outStr->erase();
	outStr->reserve ( utf16Len * 2 );	// As good a guess as any.

	while ( utf16Len > 0 ) {
		ToUTF8 ( utf16Ptr, utf16Len, buffer, sizeof(buffer), &inCount, &outCount );
		outStr->append ( (XMP_StringPtr)buffer, outCount );
		utf16Ptr += inCount;
		utf16Len -= inCount;
	}

}	// UTF16_to_UTF8

// =================================================================================================
// TIFF_Manager::DecodeString
// ==========================
//
// Convert an explicitly encoded string to UTF-8. The input must be encoded according to table 6 of
// the Exif 2.2 specification. The input pointer is to the start of the 8 byte header, the length is
// the full length. In other words, the pointer and length from the IFD entry. Returns true if the
// encoding is supported and the conversion succeeds.

// *** JIS encoding is not supported yet. Need a static mapping table from JIS X 208-1990 to Unicode.

bool TIFF_Manager::DecodeString ( const void * encodedPtr, size_t encodedLen, std::string* utf8Str ) const
{
	utf8Str->erase();
	if ( encodedLen < 8 ) return false;	// Need to have at least the 8 byte header.
	
	XMP_StringPtr typePtr  = (XMP_StringPtr)encodedPtr;
	XMP_StringPtr valuePtr = typePtr + 8;
	size_t valueLen = encodedLen - 8;

	if ( *typePtr == 'A' ) {

		utf8Str->assign ( valuePtr, valueLen );
		return true;

	} else if ( *typePtr == 'U' ) {

		try {

			const UTF16Unit * utf16Ptr = (const UTF16Unit *) valuePtr;
			size_t utf16Len = valueLen >> 1;	// The number of UTF-16 storage units, not bytes.
			if ( utf16Len == 0 ) return false;
			bool isBigEndian = this->bigEndian;	// Default to stream endian, unless there is a BOM ...
			if ( (*utf16Ptr == 0xFEFF) || (*utf16Ptr == 0xFFFE) ) {	// Check for an explicit BOM
				isBigEndian = (*((XMP_Uns8*)utf16Ptr) == 0xFE);
				utf16Ptr += 1;	// Don't translate the BOM.
				utf16Len -= 1;
				if ( utf16Len == 0 ) return false;
			}
			UTF16_to_UTF8 ( utf16Ptr, utf16Len, isBigEndian, utf8Str );
			return true;

		} catch ( ... ) {
			return false; // Ignore the tag if there are conversion errors.
		}

	} else if ( *typePtr == 'J' ) {

		return false;	// ! Ignore JIS for now.

	}

	return false;	// ! Ignore all other encodings.

}	// TIFF_Manager::DecodeString

// =================================================================================================
// UTF8_to_UTF16
// =============

static void UTF8_to_UTF16 ( const UTF8Unit * utf8Ptr, size_t utf8Len, bool bigEndian, std::string * outStr )
{
	UTF8_to_UTF16_Proc ToUTF16 = 0;
	if ( bigEndian ) {
		ToUTF16 = UTF8_to_UTF16BE;
	} else {
		ToUTF16 = UTF8_to_UTF16LE;
	}

	enum { kUTF16Len = 1000 };
	UTF16Unit buffer [kUTF16Len];
	size_t inCount, outCount;

	outStr->erase();
	outStr->reserve ( utf8Len * 2 );	// As good a guess as any.

	while ( utf8Len > 0 ) {
		ToUTF16 ( utf8Ptr, utf8Len, buffer, kUTF16Len, &inCount, &outCount );
		outStr->append ( (XMP_StringPtr)buffer, outCount*2 );
		utf8Ptr += inCount;
		utf8Len -= inCount;
	}

}	// UTF8_to_UTF16

XMP_Bool IsOffsetValid( XMP_Uns32 offset, XMP_Uns32 lowerBound, XMP_Uns32 upperBound )
{
	if ( (lowerBound <= offset) && (offset < upperBound) )
		return true;
	return false;
}

// =================================================================================================
// TIFF_Manager::EncodeString
// ==========================
//
// Convert a UTF-8 string to an explicitly encoded form according to table 6 of the Exif 2.2
// specification. Returns true if the encoding is supported and the conversion succeeds.

// *** JIS encoding is not supported yet. Need a static mapping table to JIS X 208-1990 from Unicode.

bool TIFF_Manager::EncodeString ( const std::string& utf8Str, XMP_Uns8 encoding, std::string* encodedStr )
{
	encodedStr -> erase();

	if ( encoding == kTIFF_EncodeASCII ) {

		encodedStr->assign ( "ASCII\0\0\0", 8 );
		XMP_Assert (encodedStr->size() == 8 );

		encodedStr->append ( utf8Str );	// ! Let the caller filter the value. (?)

		return true;

	} else if ( encoding == kTIFF_EncodeUnicode ) {

		encodedStr->assign ( "UNICODE\0", 8 );
		XMP_Assert (encodedStr->size() == 8 );

		try {
			std::string temp;
			UTF8_to_UTF16 ( (const UTF8Unit*)utf8Str.c_str(), utf8Str.size(), this->bigEndian, &temp );
			encodedStr->append ( temp );
			return true;
		} catch ( ... ) {
			return false; // Ignore the tag if there are conversion errors.
		}

	} else if ( encoding == kTIFF_EncodeJIS ) {

		XMP_Throw ( "Encoding to JIS is not implemented", kXMPErr_Unimplemented );

		// encodedStr->assign ( "JIS\0\0\0\0\0", 8 );
		// XMP_Assert (encodedStr->size() == 8 );

		// ...

		// return true;

	} else {

		XMP_Throw ( "Invalid TIFF string encoding", kXMPErr_BadParam );

	}

	return false;	// ! Should never get here.

}	// TIFF_Manager::EncodeString

void TIFF_Manager::NotifyClient( XMP_ErrorSeverity severity, XMP_Error & error )
{
	if (this->errorCallbackPtr)
		this->errorCallbackPtr->NotifyClient( severity, error );
	else {
		if ( severity != kXMPErrSev_Recoverable )
			throw error;
	}
}

// =================================================================================================