summaryrefslogtreecommitdiff
path: root/XMPFiles/source/FormatSupport/TIFF_Support.hpp
blob: f82bbc20cb0e2cdbd4fc45261d7a3f08766c28ff (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
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
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
#ifndef __TIFF_Support_hpp__
#define __TIFF_Support_hpp__	1

// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
// Copyright 2006 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 <map>
#include <stdlib.h>
#include <string.h>

#include "public/include/XMP_Const.h"
#include "public/include/XMP_IO.hpp"

#include "XMPFiles/source/XMPFiles_Impl.hpp"

#include "source/EndianUtils.hpp"

#include "source/Endian.h"

#if SUNOS_SPARC || XMP_IOS_ARM
        static const IEndian &IE = BigEndian::getInstance();
#else
        static const IEndian &IE = LittleEndian::getInstance();
#endif //#if SUNOS_SPARC || XMP_IOS_ARM

// =================================================================================================
/// \file TIFF_Support.hpp
/// \brief XMPFiles support for TIFF streams.
///
/// This header provides TIFF stream support specific to the needs of XMPFiles. This is not intended
/// for general purpose TIFF processing. TIFF_Manager is an abstract base class with 2 concrete
/// derived classes, TIFF_MemoryReader and TIFF_FileWriter.
///
/// TIFF_MemoryReader provides read-only support for TIFF streams that are small enough to be kept
/// entirely in memory. This allows optimizations to reduce heap usage and processing code. It is
/// sufficient for browsing access to the Exif metadata in JPEG and Photoshop files. Think of
/// TIFF_MemoryReader as "memory-based AND read-only". Since the entire TIFF stream is available,
/// GetTag will return information about any tag in the stream.
///
/// TIFF_FileWriter is for cases where updates are needed or the TIFF stream is too large to be kept
/// entirely in memory. Think of TIFF_FileWriter as "file-based OR read-write". TIFF_FileWriter only
/// maintains information for tags of interest as metadata.
///
/// The needs of XMPFiles are well defined metadata access. Only 4 IFDs are processed:
/// \li The 0th IFD, for the primary image, the first one in the outer list of IFDs.
/// \li The Exif general metadata IFD, from tag 34665 in the primary image IFD.
/// \li The Exif GPS Info metadata IFD, from tag 34853 in the primary image IFD.
/// \li The Exif Interoperability IFD, from tag 40965 in the Exif general metadata IFD.
///
/// \note These classes are for use only when directly compiled and linked. They should not be
/// packaged in a DLL by themselves. They do not provide any form of C++ ABI protection.
// =================================================================================================


// =================================================================================================
// TIFF IFD and type constants
// ===========================
//
// These aren't inside TIFF_Manager because static data members can't be initialized there.

enum {	// Constants for the recognized IFDs.
	kTIFF_PrimaryIFD    = 0,	// The primary image IFD, also called the 0th IFD.
	kTIFF_TNailIFD      = 1,	// The thumbnail image IFD also called the 1st IFD. (not used)
	kTIFF_ExifIFD       = 2,	// The Exif general metadata IFD.
	kTIFF_GPSInfoIFD    = 3,	// The Exif GPS Info IFD.
	kTIFF_InteropIFD    = 4,	// The Exif Interoperability IFD.
	kTIFF_LastRealIFD   = 4,
	kTIFF_KnownIFDCount = 5,
	kTIFF_KnownIFD      = 9	// The IFD that a tag is known to belong in.
};

enum {	// Constants for the type field of a tag, as defined by TIFF.
	kTIFF_ShortOrLongType =  0,	// ! Not part of the TIFF spec, never in a tag!
	kTIFF_ByteType        =  1,
	kTIFF_ASCIIType       =  2,
	kTIFF_ShortType       =  3,
	kTIFF_LongType        =  4,
	kTIFF_RationalType    =  5,
	kTIFF_SByteType       =  6,
	kTIFF_UndefinedType   =  7,
	kTIFF_SShortType      =  8,
	kTIFF_SLongType       =  9,
	kTIFF_SRationalType   = 10,
	kTIFF_FloatType       = 11,
	kTIFF_DoubleType      = 12,
	kTIFF_IFDType		  = 13,
	kTIFF_LastType		  = kTIFF_IFDType
};

static const size_t kTIFF_TypeSizes[]    = { 0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8, 4 };

static const bool kTIFF_IsIntegerType[]  = { 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0 };
static const bool kTIFF_IsRationalType[] = { 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0 };
static const bool kTIFF_IsFloatType[]    = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1 };

enum {	// Encodings for SetTag_EncodedString.
	kTIFF_EncodeUndefined = 0,
	kTIFF_EncodeASCII     = 1,
	kTIFF_EncodeUnicode   = 2,	// UTF-16 in the endianness of the TIFF stream.
	kTIFF_EncodeJIS       = 3,	// Exif 2.2 uses JIS X 208-1990.
	kTIFF_EncodeUnknown   = 9
};

// =================================================================================================
// Recognized TIFF tags
// ====================

// -----------------------------------------------------------------------------------------------
// An enum of IDs for all of the tags as potential interest as metadata. The numberical order does
// not matter. These are mostly listed in the order of the Exif specification tables for convenience
// of checking correspondence.

enum {

	// General 0th IFD tags. Some of these can also be in the thumbnail IFD.

	// General tags from Exif 2.3 table 4:
	kTIFF_ImageWidth = 256,
	kTIFF_ImageLength = 257,
	kTIFF_BitsPerSample = 258,
	kTIFF_Compression = 259,
	kTIFF_PhotometricInterpretation = 262,
	kTIFF_Orientation = 274,
	kTIFF_SamplesPerPixel = 277,
	kTIFF_PlanarConfiguration = 284,
	kTIFF_YCbCrCoefficients = 529,
	kTIFF_YCbCrSubSampling = 530,
	kTIFF_XResolution = 282,
	kTIFF_YResolution = 283,
	kTIFF_ResolutionUnit = 296,
	kTIFF_TransferFunction = 301,
	kTIFF_WhitePoint = 318,
	kTIFF_PrimaryChromaticities = 319,
	kTIFF_YCbCrPositioning = 531,
	kTIFF_ReferenceBlackWhite = 532,
	kTIFF_DateTime = 306,
	kTIFF_ImageDescription = 270,
	kTIFF_Make = 271,
	kTIFF_Model = 272,
	kTIFF_Software = 305,
	kTIFF_Artist = 315,
	kTIFF_Copyright = 33432,
	
	// Tags defined by Adobe:
	kTIFF_XMP = 700,
	kTIFF_IPTC = 33723,
	kTIFF_PSIR = 34377,
	kTIFF_DNGVersion = 50706,
	kTIFF_DNGBackwardVersion = 50707,

	// Additional thumbnail IFD tags. We also care about 256, 257, and 259 in thumbnails.
	kTIFF_JPEGInterchangeFormat = 513,
	kTIFF_JPEGInterchangeFormatLength = 514,

	// Tags that need special handling when rewriting memory-based TIFF.
	kTIFF_StripOffsets = 273,
	kTIFF_StripByteCounts = 279,
	kTIFF_FreeOffsets = 288,
	kTIFF_FreeByteCounts = 289,
	kTIFF_TileOffsets = 324,
	kTIFF_TileByteCounts = 325,
	kTIFF_SubIFDs = 330,
	kTIFF_JPEGQTables = 519,
	kTIFF_JPEGDCTables = 520,
	kTIFF_JPEGACTables = 521,

	// Exif IFD tags defined in Exif 2.3 table 7.

	kTIFF_ExifVersion = 36864,
	kTIFF_FlashpixVersion = 40960,
	kTIFF_ColorSpace = 40961,
	kTIFF_Gamma = 42240,
	kTIFF_ComponentsConfiguration = 37121,
	kTIFF_CompressedBitsPerPixel = 37122,
	kTIFF_PixelXDimension = 40962,
	kTIFF_PixelYDimension = 40963,
	kTIFF_MakerNote = 37500, // Gets deleted when rewriting memory-based TIFF.
	kTIFF_UserComment = 37510,
	kTIFF_RelatedSoundFile = 40964,
	kTIFF_DateTimeOriginal = 36867,
	kTIFF_DateTimeDigitized = 36868,
	kTIFF_SubSecTime = 37520,
	kTIFF_SubSecTimeOriginal = 37521,
	kTIFF_SubSecTimeDigitized = 37522,
	kTIFF_ImageUniqueID = 42016,
	kTIFF_CameraOwnerName = 42032,
	kTIFF_BodySerialNumber = 42033,
	kTIFF_LensSpecification = 42034,
	kTIFF_LensMake = 42035,
	kTIFF_LensModel = 42036,
	kTIFF_LensSerialNumber = 42037,

	// Exif IFD tags defined in Exif 2.3 table 8.

	kTIFF_ExposureTime = 33434,
	kTIFF_FNumber = 33437,
	kTIFF_ExposureProgram = 34850,
	kTIFF_SpectralSensitivity = 34852,
	kTIFF_PhotographicSensitivity = 34855,	// ! Called kTIFF_ISOSpeedRatings before Exif 2.3.
	kTIFF_OECF = 34856,
	kTIFF_SensitivityType = 34864,
	kTIFF_StandardOutputSensitivity = 34865,
	kTIFF_RecommendedExposureIndex = 34866,
	kTIFF_ISOSpeed = 34867,
	kTIFF_ISOSpeedLatitudeyyy = 34868,
	kTIFF_ISOSpeedLatitudezzz = 34869,
	kTIFF_ShutterSpeedValue = 37377,
	kTIFF_ApertureValue = 37378,
	kTIFF_BrightnessValue = 37379,
	kTIFF_ExposureBiasValue = 37380,
	kTIFF_MaxApertureValue = 37381,
	kTIFF_SubjectDistance = 37382,
	kTIFF_MeteringMode = 37383,
	kTIFF_LightSource = 37384,
	kTIFF_Flash = 37385,
	kTIFF_FocalLength = 37386,
	kTIFF_SubjectArea = 37396,
	kTIFF_FlashEnergy = 41483,
	kTIFF_SpatialFrequencyResponse = 41484,
	kTIFF_FocalPlaneXResolution = 41486,
	kTIFF_FocalPlaneYResolution = 41487,
	kTIFF_FocalPlaneResolutionUnit = 41488,
	kTIFF_SubjectLocation = 41492,
	kTIFF_ExposureIndex = 41493,
	kTIFF_SensingMethod = 41495,
	kTIFF_FileSource = 41728,
	kTIFF_SceneType = 41729,
	kTIFF_CFAPattern = 41730,
	kTIFF_CustomRendered = 41985,
	kTIFF_ExposureMode = 41986,
	kTIFF_WhiteBalance = 41987,
	kTIFF_DigitalZoomRatio = 41988,
	kTIFF_FocalLengthIn35mmFilm = 41989,
	kTIFF_SceneCaptureType = 41990,
	kTIFF_GainControl = 41991,
	kTIFF_Contrast = 41992,
	kTIFF_Saturation = 41993,
	kTIFF_Sharpness = 41994,
	kTIFF_DeviceSettingDescription = 41995,
	kTIFF_SubjectDistanceRange = 41996,

	// GPS IFD tags.

	kTIFF_GPSVersionID = 0,
	kTIFF_GPSLatitudeRef = 1,
	kTIFF_GPSLatitude = 2,
	kTIFF_GPSLongitudeRef = 3,
	kTIFF_GPSLongitude = 4,
	kTIFF_GPSAltitudeRef = 5,
	kTIFF_GPSAltitude = 6,
	kTIFF_GPSTimeStamp = 7,
	kTIFF_GPSSatellites = 8,
	kTIFF_GPSStatus = 9,
	kTIFF_GPSMeasureMode = 10,
	kTIFF_GPSDOP = 11,
	kTIFF_GPSSpeedRef = 12,
	kTIFF_GPSSpeed = 13,
	kTIFF_GPSTrackRef = 14,
	kTIFF_GPSTrack = 15,
	kTIFF_GPSImgDirectionRef = 16,
	kTIFF_GPSImgDirection = 17,
	kTIFF_GPSMapDatum = 18,
	kTIFF_GPSDestLatitudeRef = 19,
	kTIFF_GPSDestLatitude = 20,
	kTIFF_GPSDestLongitudeRef = 21,
	kTIFF_GPSDestLongitude = 22,
	kTIFF_GPSDestBearingRef = 23,
	kTIFF_GPSDestBearing = 24,
	kTIFF_GPSDestDistanceRef = 25,
	kTIFF_GPSDestDistance = 26,
	kTIFF_GPSProcessingMethod = 27,
	kTIFF_GPSAreaInformation = 28,
	kTIFF_GPSDateStamp = 29,
	kTIFF_GPSDifferential = 30,
	kTIFF_GPSHPositioningError = 31,

	// Special tags that are links to other IFDs.
	
	kTIFF_ExifIFDPointer = 34665,				// Found in 0th IFD
	kTIFF_GPSInfoIFDPointer = 34853,			// Found in 0th IFD
	kTIFF_InteroperabilityIFDPointer = 40965	// Found in Exif IFD

};

// *** Temporary hack:
#define kTIFF_ISOSpeedRatings	kTIFF_PhotographicSensitivity

// ------------------------------------------------------------------
// Sorted arrays of the tags that are recognized in the various IFDs.

static const XMP_Uns16 sKnownPrimaryIFDTags[] =
{
	kTIFF_ImageWidth,					//   256
	kTIFF_ImageLength,					//   257
	kTIFF_BitsPerSample,				//   258
	kTIFF_Compression,					//   259
	kTIFF_PhotometricInterpretation,	//   262
	kTIFF_ImageDescription,				//   270
	kTIFF_Make,							//   271
	kTIFF_Model,						//   272
	kTIFF_Orientation,					//   274
	kTIFF_SamplesPerPixel,				//   277
	kTIFF_XResolution,					//   282
	kTIFF_YResolution,					//   283
	kTIFF_PlanarConfiguration,			//   284
	kTIFF_ResolutionUnit,				//   296
	kTIFF_TransferFunction,				//   301
	kTIFF_Software,						//   305
	kTIFF_DateTime,						//   306
	kTIFF_Artist,						//   315
	kTIFF_WhitePoint,					//   318
	kTIFF_PrimaryChromaticities,		//   319
	kTIFF_YCbCrCoefficients,			//   529
	kTIFF_YCbCrSubSampling,				//   530
	kTIFF_YCbCrPositioning,				//   531
	kTIFF_ReferenceBlackWhite,			//   532
	kTIFF_XMP,							//   700
	kTIFF_Copyright,					// 33432
	kTIFF_IPTC,							// 33723
	kTIFF_PSIR,							// 34377
	kTIFF_ExifIFDPointer,				// 34665
	kTIFF_GPSInfoIFDPointer,			// 34853
	kTIFF_DNGVersion,					// 50706
	kTIFF_DNGBackwardVersion,			// 50707
	0xFFFF	// Must be last as a sentinel.
};

static const XMP_Uns16 sKnownThumbnailIFDTags[] =
{
	kTIFF_ImageWidth,					// 256
	kTIFF_ImageLength,					// 257
	kTIFF_Compression,					// 259
	kTIFF_JPEGInterchangeFormat,		// 513
	kTIFF_JPEGInterchangeFormatLength,	// 514
	0xFFFF	// Must be last as a sentinel.
};

static const XMP_Uns16 sKnownExifIFDTags[] =
{
	kTIFF_ExposureTime,					// 33434
	kTIFF_FNumber,						// 33437
	kTIFF_ExposureProgram,				// 34850
	kTIFF_SpectralSensitivity,			// 34852
	kTIFF_PhotographicSensitivity,		// 34855
	kTIFF_OECF,							// 34856
	kTIFF_SensitivityType,				// 34864
	kTIFF_StandardOutputSensitivity,	// 34865
	kTIFF_RecommendedExposureIndex,		// 34866
	kTIFF_ISOSpeed,						// 34867
	kTIFF_ISOSpeedLatitudeyyy,			// 34868
	kTIFF_ISOSpeedLatitudezzz,			// 34869
	kTIFF_ExifVersion,					// 36864
	kTIFF_DateTimeOriginal,				// 36867
	kTIFF_DateTimeDigitized,			// 36868
	kTIFF_ComponentsConfiguration,		// 37121
	kTIFF_CompressedBitsPerPixel,		// 37122
	kTIFF_ShutterSpeedValue,			// 37377
	kTIFF_ApertureValue,				// 37378
	kTIFF_BrightnessValue,				// 37379
	kTIFF_ExposureBiasValue,			// 37380
	kTIFF_MaxApertureValue,				// 37381
	kTIFF_SubjectDistance,				// 37382
	kTIFF_MeteringMode,					// 37383
	kTIFF_LightSource,					// 37384
	kTIFF_Flash,						// 37385
	kTIFF_FocalLength,					// 37386
	kTIFF_SubjectArea,					// 37396
	kTIFF_UserComment,					// 37510
	kTIFF_SubSecTime,					// 37520
	kTIFF_SubSecTimeOriginal,			// 37521
	kTIFF_SubSecTimeDigitized,			// 37522
	kTIFF_FlashpixVersion,				// 40960
	kTIFF_ColorSpace,					// 40961
	kTIFF_PixelXDimension,				// 40962
	kTIFF_PixelYDimension,				// 40963
	kTIFF_RelatedSoundFile,				// 40964
	kTIFF_FlashEnergy,					// 41483
	kTIFF_SpatialFrequencyResponse,		// 41484
	kTIFF_FocalPlaneXResolution,		// 41486
	kTIFF_FocalPlaneYResolution,		// 41487
	kTIFF_FocalPlaneResolutionUnit,		// 41488
	kTIFF_SubjectLocation,				// 41492
	kTIFF_ExposureIndex,				// 41493
	kTIFF_SensingMethod,				// 41495
	kTIFF_FileSource,					// 41728
	kTIFF_SceneType,					// 41729
	kTIFF_CFAPattern,					// 41730
	kTIFF_CustomRendered,				// 41985
	kTIFF_ExposureMode,					// 41986
	kTIFF_WhiteBalance,					// 41987
	kTIFF_DigitalZoomRatio,				// 41988
	kTIFF_FocalLengthIn35mmFilm,		// 41989
	kTIFF_SceneCaptureType,				// 41990
	kTIFF_GainControl,					// 41991
	kTIFF_Contrast,						// 41992
	kTIFF_Saturation,					// 41993
	kTIFF_Sharpness,					// 41994
	kTIFF_DeviceSettingDescription,		// 41995
	kTIFF_SubjectDistanceRange,			// 41996
	kTIFF_ImageUniqueID,				// 42016
	kTIFF_CameraOwnerName,				// 42032
	kTIFF_BodySerialNumber,				// 42033
	kTIFF_LensSpecification,			// 42034
	kTIFF_LensMake,						// 42035
	kTIFF_LensModel,					// 42036
	kTIFF_LensSerialNumber,				// 42037
	kTIFF_Gamma,						// 42240
	0xFFFF	// Must be last as a sentinel.
};

static const XMP_Uns16 sKnownGPSInfoIFDTags[] =
{
	kTIFF_GPSVersionID,			//  0
	kTIFF_GPSLatitudeRef,		//  1
	kTIFF_GPSLatitude,			//  2
	kTIFF_GPSLongitudeRef,		//  3
	kTIFF_GPSLongitude,			//  4
	kTIFF_GPSAltitudeRef,		//  5
	kTIFF_GPSAltitude,			//  6
	kTIFF_GPSTimeStamp,			//  7
	kTIFF_GPSSatellites,		//  8
	kTIFF_GPSStatus,			//  9
	kTIFF_GPSMeasureMode,		// 10
	kTIFF_GPSDOP,				// 11
	kTIFF_GPSSpeedRef,			// 12
	kTIFF_GPSSpeed,				// 13
	kTIFF_GPSTrackRef,			// 14
	kTIFF_GPSTrack,				// 15
	kTIFF_GPSImgDirectionRef,	// 16
	kTIFF_GPSImgDirection,		// 17
	kTIFF_GPSMapDatum,			// 18
	kTIFF_GPSDestLatitudeRef,	// 19
	kTIFF_GPSDestLatitude,		// 20
	kTIFF_GPSDestLongitudeRef,	// 21
	kTIFF_GPSDestLongitude,		// 22
	kTIFF_GPSDestBearingRef,	// 23
	kTIFF_GPSDestBearing,		// 24
	kTIFF_GPSDestDistanceRef,	// 25
	kTIFF_GPSDestDistance,		// 26
	kTIFF_GPSProcessingMethod,	// 27
	kTIFF_GPSAreaInformation,	// 28
	kTIFF_GPSDateStamp,			// 29
	kTIFF_GPSDifferential,		// 30
	kTIFF_GPSHPositioningError,	// 31
	0xFFFF	// Must be last as a sentinel.
};

static const XMP_Uns16 sKnownInteroperabilityIFDTags[] =
{
	// ! Yes, there are none at present.
	0xFFFF	// Must be last as a sentinel.
};

// ! Make sure these are in the same order as the IFD enum!
static const XMP_Uns16* sKnownTags[kTIFF_KnownIFDCount] = { sKnownPrimaryIFDTags,
															sKnownThumbnailIFDTags,
															sKnownExifIFDTags,
															sKnownGPSInfoIFDTags,
															sKnownInteroperabilityIFDTags };


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


// =================================================================================================
// TIFF_Manager
// ============

class TIFF_Manager {	// The abstract base class.
public:

	// ---------------------------------------------------------------------------------------------
	// Types and constants

	static const XMP_Uns32 kBigEndianPrefix    = 0x4D4D002AUL;
	static const XMP_Uns32 kLittleEndianPrefix = 0x49492A00UL;

	static const size_t kEmptyTIFFLength = 8;		// Just the header.
	static const size_t kEmptyIFDLength  = 2 + 4;	// Entry count and next-IFD offset.
	static const size_t kIFDEntryLength  = 12;

	struct TagInfo {
		XMP_Uns16   id;
		XMP_Uns16   type;
		XMP_Uns32   count;
		const void* dataPtr;	// ! The data must not be changed! Stream endian format!
		XMP_Uns32   dataLen;	// The length in bytes.
		TagInfo() : id(0), type(0), count(0), dataPtr(0), dataLen(0) {};
		TagInfo ( XMP_Uns16 _id, XMP_Uns16 _type, XMP_Uns32 _count, const void* _dataPtr, XMP_Uns32 _dataLen )
			: id(_id), type(_type), count(_count), dataPtr(_dataPtr), dataLen(_dataLen) {};
	};

	typedef std::map<XMP_Uns16,TagInfo> TagInfoMap;

	struct Rational  { XMP_Uns32 num, denom; };
	struct SRational { XMP_Int32 num, denom; };

	// ---------------------------------------------------------------------------------------------
	// The IsXyzEndian methods return the external endianness of the original parsed TIFF stream.
	// The \c GetTag methods return native endian values, the \c SetTag methods take native values.
	// The original endianness is preserved in output.

	bool IsBigEndian() const { return this->bigEndian; };
	bool IsLittleEndian() const { return (! this->bigEndian); };
	bool IsNativeEndian() const { return this->nativeEndian; };

	// ---------------------------------------------------------------------------------------------
	// The TIFF_Manager only keeps explicit knowledge of up to 4 IFDs:
	// - The primary image IFD, also known as the 0th IFD. This must be present.
	// - A possible Exif general metadata IFD, found from tag 34665 in the primary image IFD.
	// - A possible Exif GPS metadata IFD, found from tag 34853 in the primary image IFD.
	// - A possible Exif Interoperability IFD, found from tag 40965 in the Exif general metadata IFD.
	//
	// Parsing will silently forget about certain aspects of ill-formed streams. If any tags are
	// repeated in an IFD, only the last is kept. Any known tags that are in the wrong IFD are
	// removed. Parsing will sort the tags into ascending order, AppendTIFF and ComposeTIFF will
	// preserve the sorted order. These fixes do not cause IsChanged to return true, that only
	// happens if the client makes explicit changes using SetTag or DeleteTag.

	virtual bool HasExifIFD() const = 0;
	virtual bool HasGPSInfoIFD() const = 0;

	// ---------------------------------------------------------------------------------------------
	// These are the basic methods to get a map of all of the tags in an IFD, to get or set a tag,
	// or to delete a tag. The dataPtr returned by \c GetTag is consided read-only, the client must
	// not change it. The ifd parameter to \c GetIFD must be for one of the recognized actual IFDs.
	// The ifd parameter for the GetTag or SetTag methods can be a specific IFD of kTIFF_KnownIFD.
	// Using the specific IFD will be slightly faster, saving a lookup in the known tag map. An
	// exception is thrown if kTIFF_KnownIFD is passed to GetTag or SetTag and the tag is not known.
	// \c SetTag replaces an existing tag regardless of type or count. \c DeleteTag deletes a tag,
	// it is a no-op if the tag does not exist. \c GetValueOffset returns the offset within the
	// parsed stream of the tag's value. It returns 0 if the tag was not in the parsed input.

	virtual bool GetIFD ( XMP_Uns8 ifd, TagInfoMap* ifdMap ) const = 0;

	virtual bool GetTag ( XMP_Uns8 ifd, XMP_Uns16 id, TagInfo* info ) const = 0;

	virtual void SetTag ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns16 type, XMP_Uns32 count, const void* dataPtr ) = 0;

	virtual void DeleteTag ( XMP_Uns8 ifd, XMP_Uns16 id ) = 0;

	virtual XMP_Uns32 GetValueOffset ( XMP_Uns8 ifd, XMP_Uns16 id ) const = 0;

	// ---------------------------------------------------------------------------------------------
	// These methods are for tags whose type can be short or long, depending on the actual value.
	// \c GetTag_Integer returns false if an existing tag's type is not short, or long, or if the
	// count is not 1. \c SetTag_Integer replaces an existing tag regardless of type or count, the
	// new tag will have type short or long.

	virtual bool GetTag_Integer ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns32* data ) const = 0;

	void SetTag_Integer ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns32 data );

	// ---------------------------------------------------------------------------------------------
	// These are customized forms of GetTag that verify the type and return a typed value. False is
	// returned if the type does not match or if the count is not 1.

	// *** Should also add support for ASCII multi-strings?

	virtual bool GetTag_Byte   ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns8* data ) const = 0;
	virtual bool GetTag_SByte  ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Int8* data ) const = 0;
	virtual bool GetTag_Short  ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns16* data ) const = 0;
	virtual bool GetTag_SShort ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Int16* data ) const = 0;
	virtual bool GetTag_Long   ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns32* data ) const = 0;
	virtual bool GetTag_SLong  ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Int32* data ) const = 0;

	virtual bool GetTag_Rational  ( XMP_Uns8 ifd, XMP_Uns16 id, Rational* data ) const = 0;
	virtual bool GetTag_SRational ( XMP_Uns8 ifd, XMP_Uns16 id, SRational* data ) const = 0;

	virtual bool GetTag_Float  ( XMP_Uns8 ifd, XMP_Uns16 id, float* data ) const = 0;
	virtual bool GetTag_Double ( XMP_Uns8 ifd, XMP_Uns16 id, double* data ) const = 0;

	virtual bool GetTag_ASCII ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_StringPtr* dataPtr, XMP_StringLen* dataLen ) const = 0;

	// ---------------------------------------------------------------------------------------------

	void SetTag_Byte   ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns8 data );
	void SetTag_SByte  ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Int8 data );
	void SetTag_Short  ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns16 data );
	void SetTag_SShort ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Int16 data );
	void SetTag_Long   ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns32 data );
	void SetTag_SLong  ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Int32 data );

	void SetTag_Rational  ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns32 num, XMP_Uns32 denom );
	void SetTag_SRational ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Int32 num, XMP_Int32 denom );

	void SetTag_Float  ( XMP_Uns8 ifd, XMP_Uns16 id, float data );
	void SetTag_Double ( XMP_Uns8 ifd, XMP_Uns16 id, double data );

	void SetTag_ASCII ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_StringPtr dataPtr );

	// ---------------------------------------------------------------------------------------------

	virtual bool GetTag_EncodedString ( XMP_Uns8 ifd, XMP_Uns16 id, std::string* utf8Str ) const = 0;
	virtual void SetTag_EncodedString ( XMP_Uns8 ifd, XMP_Uns16 id, const std::string& utf8Str, XMP_Uns8 encoding ) = 0;

	bool DecodeString ( const void * encodedPtr, size_t encodedLen, std::string* utf8Str ) const;
	bool EncodeString ( const std::string& utf8Str, XMP_Uns8 encoding, std::string* encodedStr );

	// ---------------------------------------------------------------------------------------------
	// \c IsChanged returns true if a read-write stream has changes that need to be saved. This is
	// only the case when a \c SetTag method has been called. It is not true for changes related to
	// parsing normalization such as sorting of tags. \c IsChanged returns false for read-only streams.

	virtual bool IsChanged() = 0;

	// ---------------------------------------------------------------------------------------------
	// \c IsLegacyChanged returns true if a read-write stream has changes that need to be saved to
	// tags other than the XMP (tag 700). This only the case when a \c SetTag method has been
	// called. It is not true for changes related to parsing normalization such as sorting of tags.
	// \c IsLegacyChanged returns false for read-only streams.

	virtual bool IsLegacyChanged() = 0;

	// ---------------------------------------------------------------------------------------------
	// \c UpdateMemoryStream is mainly applicable to memory-based read-write streams. It recomposes
	// the memory stream to incorporate all changes. The new length and data pointer are returned.
	// \c UpdateMemoryStream can be used with a read-only memory stream to get the raw stream info.
	//
	// \c UpdateFileStream updates file-based TIFF. The client must guarantee that the TIFF portion
	// of the file matches that from the parse in the file-based constructor. Offsets saved from that
	// parse must still be valid. The open file reference need not be the same, e.g. the client can
	// be doing a crash-safe update into a temporary copy.
	//
	// Both \c UpdateMemoryStream and \c UpdateFileStream use an update-by-append model. Changes are
	// written in-place where they fit, anything requiring growth is appended to the end and the old
	// space is abandoned. The end for memory-based TIFF is the end of the data block, the end for
	// file-based TIFF is the end of the file. This update-by-append model has the advantage of not
	// perturbing any hidden offsets, a common feature of proprietary MakerNotes.
	//
	// The condenseStream parameter to UpdateMemoryStream can be used to rewrite the full stream
	// instead of appending. This will discard any MakerNote tags and risks breaking offsets that
	// are hidden. This can be necessary though to try to make the TIFF fit in a JPEG file.
	
	// isAlredyLittle is provided for case when data contain no information about Endianess, So need not to check for header
	virtual void ParseMemoryStream ( const void* data, XMP_Uns32 length, bool copyData = true, bool isAlreadyLittle = false ) = 0;
	virtual void ParseFileStream   ( XMP_IO* fileRef ) = 0;

	virtual void IntegrateFromPShop6 ( const void * buriedPtr, size_t buriedLen ) = 0;

	virtual XMP_Uns32 UpdateMemoryStream ( void** dataPtr, bool condenseStream = false ) = 0;
	virtual void      UpdateFileStream   ( XMP_IO* fileRef, XMP_ProgressTracker* progressTracker ) = 0;

	// ---------------------------------------------------------------------------------------------

	GetUns16_Proc  GetUns16;	// Get values from the TIFF stream.
	GetUns32_Proc  GetUns32;	// Always native endian on the outside, stream endian in the stream.
	GetFloat_Proc  GetFloat;
	GetDouble_Proc GetDouble;

	PutUns16_Proc  PutUns16;	// Put values into the TIFF stream.
	PutUns32_Proc  PutUns32;	// Always native endian on the outside, stream endian in the stream.
	PutFloat_Proc  PutFloat;
	PutDouble_Proc PutDouble;

	virtual ~TIFF_Manager() {};

	virtual void SetErrorCallback ( GenericErrorCallback * ec ) { this->errorCallbackPtr = ec; };

	virtual void NotifyClient ( XMP_ErrorSeverity severity, XMP_Error & error );

protected:

	bool bigEndian, nativeEndian;

	XMP_Uns32 CheckTIFFHeader ( const XMP_Uns8* tiffPtr, XMP_Uns32 length );
		// The pointer is to a buffer of the first 8 bytes. The length is the overall length, used
		// to check the primary IFD offset.

	TIFF_Manager();	// Force clients to use the reader or writer derived classes.

	struct RawIFDEntry {
		XMP_Uns16 id;
		XMP_Uns16 type;
		XMP_Uns32 count;
		XMP_Uns32 dataOrOffset;
	};

	GenericErrorCallback *errorCallbackPtr;

};	// TIFF_Manager


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


// =================================================================================================
// TIFF_MemoryReader
// =================

class TIFF_MemoryReader : public TIFF_Manager {	// The derived class for memory-based read-only access.
public:

	bool HasExifIFD() const { return (containedIFDs[kTIFF_ExifIFD].count != 0); };
	bool HasGPSInfoIFD() const { return (containedIFDs[kTIFF_GPSInfoIFD].count != 0); };

	bool GetIFD ( XMP_Uns8 ifd, TagInfoMap* ifdMap ) const;

	bool GetTag ( XMP_Uns8 ifd, XMP_Uns16 id, TagInfo* info ) const;

	void SetTag ( XMP_Uns8 /*ifd*/, XMP_Uns16 /*id*/, XMP_Uns16 /*type*/, XMP_Uns32 /*count*/, const void* /*dataPtr*/ ) { NotAppropriate(); };

	void DeleteTag ( XMP_Uns8 /*ifd*/, XMP_Uns16 /*id*/ ) { NotAppropriate(); };

	XMP_Uns32 GetValueOffset ( XMP_Uns8 ifd, XMP_Uns16 id ) const;

	bool GetTag_Integer ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns32* data ) const;

	bool GetTag_Byte   ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns8* data ) const;
	bool GetTag_SByte  ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Int8* data ) const;
	bool GetTag_Short  ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns16* data ) const;
	bool GetTag_SShort ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Int16* data ) const;
	bool GetTag_Long   ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns32* data ) const;
	bool GetTag_SLong  ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Int32* data ) const;

	bool GetTag_Rational  ( XMP_Uns8 ifd, XMP_Uns16 id, Rational* data ) const;
	bool GetTag_SRational ( XMP_Uns8 ifd, XMP_Uns16 id, SRational* data ) const;

	bool GetTag_Float  ( XMP_Uns8 ifd, XMP_Uns16 id, float* data ) const;
	bool GetTag_Double ( XMP_Uns8 ifd, XMP_Uns16 id, double* data ) const;

	bool GetTag_ASCII ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_StringPtr* dataPtr, XMP_StringLen* dataLen ) const;

	bool GetTag_EncodedString ( XMP_Uns8 ifd, XMP_Uns16 id, std::string* utf8Str ) const;

	void SetTag_EncodedString ( XMP_Uns8 /*ifd*/, XMP_Uns16 /*id*/, const std::string& /*utf8Str*/, XMP_Uns8 /*encoding*/ ) { NotAppropriate(); };

	bool IsChanged() { return false; };
	bool IsLegacyChanged() { return false; };

	// isAlredyLittle is provided for case when data contain no information about Endianess, So need not to check for header
	void ParseMemoryStream ( const void* data, XMP_Uns32 length, bool copyData = true, bool isAlreadyLittle = false );
	void ParseFileStream   ( XMP_IO* /*fileRef*/ ) { NotAppropriate(); };

	void IntegrateFromPShop6 ( const void * /*buriedPtr*/, size_t /*buriedLen*/ ) { NotAppropriate(); };

	XMP_Uns32 UpdateMemoryStream ( void** dataPtr, bool condenseStream = false ) { IgnoreParam(condenseStream); if ( dataPtr != 0 ) *dataPtr = tiffStream; return tiffLength; };
	void      UpdateFileStream   ( XMP_IO* /*fileRef*/, XMP_ProgressTracker* /*progressTracker*/ ) { NotAppropriate(); };

	TIFF_MemoryReader() : ownedStream(false), tiffStream(0), tiffLength(0) {};

	virtual ~TIFF_MemoryReader() { if ( this->ownedStream ) free ( this->tiffStream ); };

private:

	bool ownedStream;

	XMP_Uns8* tiffStream;
	XMP_Uns32 tiffLength;

	// Memory usage notes: TIFF_MemoryReader is for memory-based read-only usage (both apply). There
	// is no need to ever allocate separate blocks of memory, everything is used directly from the
	// TIFF stream. Data pointers are computed on the fly, the offset field is 4 bytes and pointers
	// will be 8 bytes for 64-bit platforms.

	struct TweakedIFDEntry {	// ! Most fields are in native byte order, dataOrPos is for offsets only.
		XMP_Uns16 id;
		XMP_Uns16 type;
		XMP_Uns32 bytes;
		XMP_Uns32 dataOrPos;
		TweakedIFDEntry() : id(0), type(0), bytes(0), dataOrPos(0) {};
	};

	struct TweakedIFDInfo {
		XMP_Uns16 count;
		TweakedIFDEntry* entries;
		TweakedIFDInfo() : count(0), entries(0) {};
	};

	TweakedIFDInfo containedIFDs[kTIFF_KnownIFDCount];

	static void SortIFD ( TweakedIFDInfo* thisIFD );

	// ModifiedInitialCheck is provided for case when data contain no header
	XMP_Uns32 ProcessOneIFD ( XMP_Uns32 ifdOffset, XMP_Uns8 ifd, bool ModifiedInitialCheck = false );

	const TweakedIFDEntry* FindTagInIFD ( XMP_Uns8 ifd, XMP_Uns16 id ) const;

	const inline void* GetDataPtr ( const TweakedIFDEntry* tifdEntry ) const
		{ if ( GetUns32AsIs(&tifdEntry->bytes) <= 4 ) {
		  	return &tifdEntry->dataOrPos;
		  } else {
			XMP_Uns32 pos = GetUns32AsIs(&tifdEntry->dataOrPos);
			if (pos + GetUns32AsIs (&tifdEntry->bytes) > this->tiffLength) {
				// Invalid file.
				// The data is past the length of the TIFF.
				return NULL;
			}
			return (this->tiffStream + pos);
		  }
		}

	static inline void NotAppropriate() { XMP_Throw ( "Not appropriate for TIFF_Reader", kXMPErr_InternalFailure ); };

};	// TIFF_MemoryReader


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


// =================================================================================================
// TIFF_FileWriter
// ===============

class TIFF_FileWriter : public TIFF_Manager {	// The derived class for file-based or read-write access.
public:

	bool HasExifIFD() const { return this->containedIFDs[kTIFF_ExifIFD].tagMap.size() != 0; };
	bool HasGPSInfoIFD() const { return this->containedIFDs[kTIFF_GPSInfoIFD].tagMap.size() != 0; };

	bool GetIFD ( XMP_Uns8 ifd, TagInfoMap* ifdMap ) const;

	bool GetTag ( XMP_Uns8 ifd, XMP_Uns16 id, TagInfo* info ) const;

	void SetTag ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns16 type, XMP_Uns32 count, const void* dataPtr );

	void DeleteTag ( XMP_Uns8 ifd, XMP_Uns16 id );

	XMP_Uns32 GetValueOffset ( XMP_Uns8 ifd, XMP_Uns16 id ) const;

	bool GetTag_Integer ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns32* data ) const;

	bool GetTag_Byte   ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns8* data ) const;
	bool GetTag_SByte  ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Int8* data ) const;
	bool GetTag_Short  ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns16* data ) const;
	bool GetTag_SShort ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Int16* data ) const;
	bool GetTag_Long   ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns32* data ) const;
	bool GetTag_SLong  ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Int32* data ) const;

	bool GetTag_Rational  ( XMP_Uns8 ifd, XMP_Uns16 id, Rational* data ) const;
	bool GetTag_SRational ( XMP_Uns8 ifd, XMP_Uns16 id, SRational* data ) const;

	bool GetTag_Float  ( XMP_Uns8 ifd, XMP_Uns16 id, float* data ) const;
	bool GetTag_Double ( XMP_Uns8 ifd, XMP_Uns16 id, double* data ) const;

	bool GetTag_ASCII ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_StringPtr* dataPtr, XMP_StringLen* dataLen ) const;

	bool GetTag_EncodedString ( XMP_Uns8 ifd, XMP_Uns16 id, std::string* utf8Str ) const;

	void SetTag_EncodedString ( XMP_Uns8 ifd, XMP_Uns16 id, const std::string& utf8Str, XMP_Uns8 encoding );

	bool IsChanged() { return this->changed; };

	bool IsLegacyChanged();

	enum { kDoNotCopyData = false };

	// isAlredyLittle is provided for case when data contain no information about Endianess, So need not to check for header
	void ParseMemoryStream ( const void* data, XMP_Uns32 length, bool copyData = true, bool isAlreadyLittle = false );
	void ParseFileStream   ( XMP_IO* fileRef );

	void IntegrateFromPShop6 ( const void * buriedPtr, size_t buriedLen );

	XMP_Uns32 UpdateMemoryStream ( void** dataPtr, bool condenseStream = false );
	void      UpdateFileStream   ( XMP_IO* fileRef, XMP_ProgressTracker* progressTracker );

	TIFF_FileWriter();

	virtual ~TIFF_FileWriter();

private:

	bool changed, legacyDeleted;
	bool memParsed, fileParsed;
	bool ownedStream;

	XMP_Uns8* memStream;
	XMP_Uns32 tiffLength;

	// Memory usage notes: TIFF_FileWriter is for file-based OR read/write usage. For memory-based
	// streams the dataPtr is initially into the stream, regardless of size. For file-based streams
	// the dataPtr is initially a separate allocation for large values (over 4 bytes), and points to
	// the smallValue field for small values. This is also the usage when a tag is changed (for both
	// memory and file cases), the dataPtr is a separate allocation for large values (over 4 bytes),
	// and points to the smallValue field for small values.

	// ! The working data values are always stream endian, no matter where stored. They are flipped
	// ! as necessary by GetTag and SetTag.

	static const bool kIsFileBased   = true;	// For use in the InternalTagInfo constructor.
	static const bool kIsMemoryBased = false;

	class InternalTagInfo {
	public:

		XMP_Uns16 id;
		XMP_Uns16 type;
		XMP_Uns32 count;
		XMP_Uns32 dataLen;
		XMP_Uns32 smallValue;		// Small value in stream endianness, but "left" justified.
		XMP_Uns8* dataPtr;			// Parsing captures all small values, only large ones that we care about.
		XMP_Uns32 origDataLen;		// The original (parse time) data length in bytes.
		XMP_Uns32 origDataOffset;	// The original data offset, regardless of length.
		bool      changed;
		bool      fileBased;

		inline void FreeData() {
			if ( this->fileBased || this->changed ) {
				if ( (this->dataLen > 4) && (this->dataPtr != 0) ) { free ( this->dataPtr ); this->dataPtr = 0; }
			}
		}

		InternalTagInfo ( XMP_Uns16 _id, XMP_Uns16 _type, XMP_Uns32 _count, bool _fileBased )
			: id(_id), type(_type), count(_count), dataLen(0), smallValue(0), dataPtr(0),
			  origDataLen(0), origDataOffset(0), changed(false), fileBased(_fileBased) {};
		~InternalTagInfo() { this->FreeData(); };

		void operator=  ( const InternalTagInfo & in )
		{
			// ! Gag! Transfer ownership of the dataPtr!
			this->FreeData();
			memcpy ( this, &in, sizeof(*this) );	// AUDIT: Use of sizeof(InternalTagInfo) is safe.
			if ( this->dataLen <= 4 ) {
				this->dataPtr = (XMP_Uns8*) &this->smallValue;	// Don't use the copied pointer.
			} else {
				*((XMP_Uns8**)&in.dataPtr) = 0;	// The pointer is now owned by "this".
			}
		};

	private:

		InternalTagInfo()	// Hidden on purpose, fileBased must be properly set.
			: id(0), type(0), count(0), dataLen(0), smallValue(0), dataPtr(0),
			  origDataLen(0), origDataOffset(0), changed(false), fileBased(false) {};

	};

	typedef std::map<XMP_Uns16,InternalTagInfo> InternalTagMap;

	struct InternalIFDInfo {
		bool changed;
		XMP_Uns16 origCount;		// Original number of IFD entries.
		XMP_Uns32 origIFDOffset;	// Original stream offset of the IFD.
		XMP_Uns32 origNextIFD;		// Original stream offset of the following IFD.
		InternalTagMap tagMap;
		InternalIFDInfo() : changed(false), origCount(0), origIFDOffset(0), origNextIFD(0) {};
		inline void clear()
		{
			this->changed = false;
			this->origCount = 0;
			this->origIFDOffset = this->origNextIFD = 0;
			this->tagMap.clear();
		};
	};

	InternalIFDInfo containedIFDs[kTIFF_KnownIFDCount];

	static XMP_Uns8 PickIFD ( XMP_Uns8 ifd, XMP_Uns16 id );
	const InternalTagInfo* FindTagInIFD ( XMP_Uns8 ifd, XMP_Uns16 id ) const;

	void DeleteExistingInfo();

	XMP_Uns32 ProcessMemoryIFD ( XMP_Uns32 ifdOffset, XMP_Uns8 ifd );
	XMP_Uns32 ProcessFileIFD   ( XMP_Uns8 ifd, XMP_Uns32 ifdOffset, XMP_IO* fileRef );

	void ProcessPShop6IFD ( const TIFF_MemoryReader& buriedExif, XMP_Uns8 ifd );

	void* CopyTagToMasterIFD ( const TagInfo& ps6Tag, InternalIFDInfo* masterIFD );

	void PreflightIFDLinkage();

	XMP_Uns32 DetermineVisibleLength();

	XMP_Uns32 DetermineAppendInfo ( XMP_Uns32 appendedOrigin,
									bool      appendedIFDs[kTIFF_KnownIFDCount],
									XMP_Uns32 newIFDOffsets[kTIFF_KnownIFDCount],
									bool      appendAll = false );

	void UpdateMemByAppend  ( XMP_Uns8** newStream_out, XMP_Uns32* newLength_out,
							  bool appendAll = false, XMP_Uns32 extraSpace = 0 );
	void UpdateMemByRewrite ( XMP_Uns8** newStream_out, XMP_Uns32* newLength_out );

	void WriteFileIFD ( XMP_IO* fileRef, InternalIFDInfo & thisIFD );

};	// TIFF_FileWriter

XMP_Bool IsOffsetValid( XMP_Uns32 offset, XMP_Uns32 lowerBound, XMP_Uns32 upperBound );

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

#endif	// __TIFF_Support_hpp__