1 // =================================================================================================
2 // ADOBE SYSTEMS INCORPORATED
3 // Copyright 2006-2008 Adobe Systems Incorporated
4 // All Rights Reserved
5 //
6 // NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
7 // of the Adobe license agreement accompanying it.
8 // =================================================================================================
9 
10 #include "XMP_Environment.h"	// ! This must be the first include.
11 
12 #include "Reconcile_Impl.hpp"
13 
14 #include "UnicodeConversions.hpp"
15 
16 #include <inttypes.h>
17 #include <stdio.h>
18 #if XMP_WinBuild
19 	#define snprintf _snprintf
20 #endif
21 
22 #if XMP_WinBuild
23 	#pragma warning ( disable : 4996 )	// '...' was declared deprecated
24 #endif
25 
26 // =================================================================================================
27 /// \file ReconcileTIFF.cpp
28 /// \brief Utilities to reconcile between XMP and legacy TIFF/Exif metadata.
29 ///
30 // =================================================================================================
31 
32 // =================================================================================================
33 
34 // =================================================================================================
35 // Tables of the TIFF/Exif tags that are mapped into XMP. For the most part, the tags have obvious
36 // mappings based on their IFD, tag number, type and count. These tables do not list tags that are
37 // mapped as subsidiary parts of others, e.g. TIFF SubSecTime or GPS Info GPSDateStamp. Tags that
38 // have special mappings are marked by having an empty string for the XMP property name.
39 
40 // ! These tables have the tags listed in the order of tables 3, 4, 5, and 12 of Exif 2.2, with the
41 // ! exception of ImageUniqueID (which is listed at the end of the Exif mappings). This order is
42 // ! very important to consistent checking of the legacy status. The NativeDigest properties list
43 // ! all possible mapped tags in this order. The NativeDigest strings are compared as a whole, so
44 // ! the same tags listed in a different order would compare as different.
45 
46 // ! The sentinel tag value can't be 0, that is a valid GPS Info tag, 0xFFFF is unused so far.
47 
48 struct TIFF_MappingToXMP {
49 	XMP_Uns16    id;
50 	XMP_Uns16    type;
51 	XMP_Uns32    count;	// Zero means any.
52 	const char * name;	// The name of the mapped XMP property. The namespace is implicit.
53 };
54 
55 enum { kAnyCount = 0 };
56 
57 static const TIFF_MappingToXMP sPrimaryIFDMappings[] = {
58 	{ /*   256 */ kTIFF_ImageWidth,                kTIFF_ShortOrLongType, 1,         "ImageWidth" },
59 	{ /*   257 */ kTIFF_ImageLength,               kTIFF_ShortOrLongType, 1,         "ImageLength" },
60 	{ /*   258 */ kTIFF_BitsPerSample,             kTIFF_ShortType,       3,         "BitsPerSample" },
61 	{ /*   259 */ kTIFF_Compression,               kTIFF_ShortType,       1,         "Compression" },
62 	{ /*   262 */ kTIFF_PhotometricInterpretation, kTIFF_ShortType,       1,         "PhotometricInterpretation" },
63 	{ /*   274 */ kTIFF_Orientation,               kTIFF_ShortType,       1,         "Orientation" },
64 	{ /*   277 */ kTIFF_SamplesPerPixel,           kTIFF_ShortType,       1,         "SamplesPerPixel" },
65 	{ /*   284 */ kTIFF_PlanarConfiguration,       kTIFF_ShortType,       1,         "PlanarConfiguration" },
66 	{ /*   530 */ kTIFF_YCbCrSubSampling,          kTIFF_ShortType,       2,         "YCbCrSubSampling" },
67 	{ /*   531 */ kTIFF_YCbCrPositioning,          kTIFF_ShortType,       1,         "YCbCrPositioning" },
68 	{ /*   282 */ kTIFF_XResolution,               kTIFF_RationalType,    1,         "XResolution" },
69 	{ /*   283 */ kTIFF_YResolution,               kTIFF_RationalType,    1,         "YResolution" },
70 	{ /*   296 */ kTIFF_ResolutionUnit,            kTIFF_ShortType,       1,         "ResolutionUnit" },
71 	{ /*   301 */ kTIFF_TransferFunction,          kTIFF_ShortType,       3*256,     "TransferFunction" },
72 	{ /*   318 */ kTIFF_WhitePoint,                kTIFF_RationalType,    2,         "WhitePoint" },
73 	{ /*   319 */ kTIFF_PrimaryChromaticities,     kTIFF_RationalType,    6,         "PrimaryChromaticities" },
74 	{ /*   529 */ kTIFF_YCbCrCoefficients,         kTIFF_RationalType,    3,         "YCbCrCoefficients" },
75 	{ /*   532 */ kTIFF_ReferenceBlackWhite,       kTIFF_RationalType,    6,         "ReferenceBlackWhite" },
76 	{ /*   306 */ kTIFF_DateTime,                  kTIFF_ASCIIType,       20,        "" },	// ! Has a special mapping.
77 	{ /*   270 */ kTIFF_ImageDescription,          kTIFF_ASCIIType,       kAnyCount, "" },	// ! Has a special mapping.
78 	{ /*   271 */ kTIFF_Make,                      kTIFF_ASCIIType,       kAnyCount, "Make" },
79 	{ /*   272 */ kTIFF_Model,                     kTIFF_ASCIIType,       kAnyCount, "Model" },
80 	{ /*   305 */ kTIFF_Software,                  kTIFF_ASCIIType,       kAnyCount, "Software" },	// Has alias to xmp:CreatorTool.
81 	{ /*   315 */ kTIFF_Artist,                    kTIFF_ASCIIType,       kAnyCount, "" },	// ! Has a special mapping.
82 	{ /* 33432 */ kTIFF_Copyright,                 kTIFF_ASCIIType,       kAnyCount, "" },
83 	{ 0xFFFF, 0, 0, 0 }	// ! Must end with sentinel.
84 };
85 
86 static const TIFF_MappingToXMP sExifIFDMappings[] = {
87 	{ /* 36864 */ kTIFF_ExifVersion,               kTIFF_UndefinedType,   4,         "" },	// ! Has a special mapping.
88 	{ /* 40960 */ kTIFF_FlashpixVersion,           kTIFF_UndefinedType,   4,         "" },	// ! Has a special mapping.
89 	{ /* 40961 */ kTIFF_ColorSpace,                kTIFF_ShortType,       1,         "ColorSpace" },
90 	{ /* 37121 */ kTIFF_ComponentsConfiguration,   kTIFF_UndefinedType,   4,         "" },	// ! Has a special mapping.
91 	{ /* 37122 */ kTIFF_CompressedBitsPerPixel,    kTIFF_RationalType,    1,         "CompressedBitsPerPixel" },
92 	{ /* 40962 */ kTIFF_PixelXDimension,           kTIFF_ShortOrLongType, 1,         "PixelXDimension" },
93 	{ /* 40963 */ kTIFF_PixelYDimension,           kTIFF_ShortOrLongType, 1,         "PixelYDimension" },
94 	{ /* 37510 */ kTIFF_UserComment,               kTIFF_UndefinedType,   kAnyCount, "" },	// ! Has a special mapping.
95 	{ /* 40964 */ kTIFF_RelatedSoundFile,          kTIFF_ASCIIType,       13,        "RelatedSoundFile" },
96 	{ /* 36867 */ kTIFF_DateTimeOriginal,          kTIFF_ASCIIType,       20,        "" },	// ! Has a special mapping.
97 	{ /* 36868 */ kTIFF_DateTimeDigitized,         kTIFF_ASCIIType,       20,        "" },	// ! Has a special mapping.
98 	{ /* 33434 */ kTIFF_ExposureTime,              kTIFF_RationalType,    1,         "ExposureTime" },
99 	{ /* 33437 */ kTIFF_FNumber,                   kTIFF_RationalType,    1,         "FNumber" },
100 	{ /* 34850 */ kTIFF_ExposureProgram,           kTIFF_ShortType,       1,         "ExposureProgram" },
101 	{ /* 34852 */ kTIFF_SpectralSensitivity,       kTIFF_ASCIIType,       kAnyCount, "SpectralSensitivity" },
102 	{ /* 34855 */ kTIFF_ISOSpeedRatings,           kTIFF_ShortType,       kAnyCount, "ISOSpeedRatings" },
103 	{ /* 34856 */ kTIFF_OECF,                      kTIFF_UndefinedType,   kAnyCount, "" },	// ! Has a special mapping.
104 	{ /* 37377 */ kTIFF_ShutterSpeedValue,         kTIFF_SRationalType,   1,         "ShutterSpeedValue" },
105 	{ /* 37378 */ kTIFF_ApertureValue,             kTIFF_RationalType,    1,         "ApertureValue" },
106 	{ /* 37379 */ kTIFF_BrightnessValue,           kTIFF_SRationalType,   1,         "BrightnessValue" },
107 	{ /* 37380 */ kTIFF_ExposureBiasValue,         kTIFF_SRationalType,   1,         "ExposureBiasValue" },
108 	{ /* 37381 */ kTIFF_MaxApertureValue,          kTIFF_RationalType,    1,         "MaxApertureValue" },
109 	{ /* 37382 */ kTIFF_SubjectDistance,           kTIFF_RationalType,    1,         "SubjectDistance" },
110 	{ /* 37383 */ kTIFF_MeteringMode,              kTIFF_ShortType,       1,         "MeteringMode" },
111 	{ /* 37384 */ kTIFF_LightSource,               kTIFF_ShortType,       1,         "LightSource" },
112 	{ /* 37385 */ kTIFF_Flash,                     kTIFF_ShortType,       1,         "" },	// ! Has a special mapping.
113 	{ /* 37386 */ kTIFF_FocalLength,               kTIFF_RationalType,    1,         "FocalLength" },
114 	{ /* 37396 */ kTIFF_SubjectArea,               kTIFF_ShortType,       kAnyCount, "SubjectArea" },	// ! Actually 2..4.
115 	{ /* 41483 */ kTIFF_FlashEnergy,               kTIFF_RationalType,    1,         "FlashEnergy" },
116 	{ /* 41484 */ kTIFF_SpatialFrequencyResponse,  kTIFF_UndefinedType,   kAnyCount, "" },	// ! Has a special mapping.
117 	{ /* 41486 */ kTIFF_FocalPlaneXResolution,     kTIFF_RationalType,    1,         "FocalPlaneXResolution" },
118 	{ /* 41487 */ kTIFF_FocalPlaneYResolution,     kTIFF_RationalType,    1,         "FocalPlaneYResolution" },
119 	{ /* 41488 */ kTIFF_FocalPlaneResolutionUnit,  kTIFF_ShortType,       1,         "FocalPlaneResolutionUnit" },
120 	{ /* 41492 */ kTIFF_SubjectLocation,           kTIFF_ShortType,       2,         "SubjectLocation" },
121 	{ /* 41493 */ kTIFF_ExposureIndex,             kTIFF_RationalType,    1,         "ExposureIndex" },
122 	{ /* 41495 */ kTIFF_SensingMethod,             kTIFF_ShortType,       1,         "SensingMethod" },
123 	{ /* 41728 */ kTIFF_FileSource,                kTIFF_UndefinedType,   1,         "" },	// ! Has a special mapping.
124 	{ /* 41729 */ kTIFF_SceneType,                 kTIFF_UndefinedType,   1,         "" },	// ! Has a special mapping.
125 	{ /* 41730 */ kTIFF_CFAPattern,                kTIFF_UndefinedType,   kAnyCount, "" },	// ! Has a special mapping.
126 	{ /* 41985 */ kTIFF_CustomRendered,            kTIFF_ShortType,       1,         "CustomRendered" },
127 	{ /* 41986 */ kTIFF_ExposureMode,              kTIFF_ShortType,       1,         "ExposureMode" },
128 	{ /* 41987 */ kTIFF_WhiteBalance,              kTIFF_ShortType,       1,         "WhiteBalance" },
129 	{ /* 41988 */ kTIFF_DigitalZoomRatio,          kTIFF_RationalType,    1,         "DigitalZoomRatio" },
130 	{ /* 41989 */ kTIFF_FocalLengthIn35mmFilm,     kTIFF_ShortType,       1,         "FocalLengthIn35mmFilm" },
131 	{ /* 41990 */ kTIFF_SceneCaptureType,          kTIFF_ShortType,       1,         "SceneCaptureType" },
132 	{ /* 41991 */ kTIFF_GainControl,               kTIFF_ShortType,       1,         "GainControl" },
133 	{ /* 41992 */ kTIFF_Contrast,                  kTIFF_ShortType,       1,         "Contrast" },
134 	{ /* 41993 */ kTIFF_Saturation,                kTIFF_ShortType,       1,         "Saturation" },
135 	{ /* 41994 */ kTIFF_Sharpness,                 kTIFF_ShortType,       1,         "Sharpness" },
136 	{ /* 41995 */ kTIFF_DeviceSettingDescription,  kTIFF_UndefinedType,   kAnyCount, "" },	// ! Has a special mapping.
137 	{ /* 41996 */ kTIFF_SubjectDistanceRange,      kTIFF_ShortType,       1,         "SubjectDistanceRange" },
138 	{ /* 42016 */ kTIFF_ImageUniqueID,             kTIFF_ASCIIType,       33,        "ImageUniqueID" },
139 	{ 0xFFFF, 0, 0, 0 }	// ! Must end with sentinel.
140 };
141 
142 static const TIFF_MappingToXMP sGPSInfoIFDMappings[] = {
143 	{ /*     0 */ kTIFF_GPSVersionID,              kTIFF_ByteType,        4,         "" },	// ! Has a special mapping.
144 	{ /*     2 */ kTIFF_GPSLatitude,               kTIFF_RationalType,    3,         "" },	// ! Has a special mapping.
145 	{ /*     4 */ kTIFF_GPSLongitude,              kTIFF_RationalType,    3,         "" },	// ! Has a special mapping.
146 	{ /*     5 */ kTIFF_GPSAltitudeRef,            kTIFF_ByteType,        1,         "GPSAltitudeRef" },
147 	{ /*     6 */ kTIFF_GPSAltitude,               kTIFF_RationalType,    1,         "GPSAltitude" },
148 	{ /*     7 */ kTIFF_GPSTimeStamp,              kTIFF_RationalType,    3,         "" },	// ! Has a special mapping.
149 	{ /*     8 */ kTIFF_GPSSatellites,             kTIFF_ASCIIType,       kAnyCount, "GPSSatellites" },
150 	{ /*     9 */ kTIFF_GPSStatus,                 kTIFF_ASCIIType,       2,         "GPSStatus" },
151 	{ /*    10 */ kTIFF_GPSMeasureMode,            kTIFF_ASCIIType,       2,         "GPSMeasureMode" },
152 	{ /*    11 */ kTIFF_GPSDOP,                    kTIFF_RationalType,    1,         "GPSDOP" },
153 	{ /*    12 */ kTIFF_GPSSpeedRef,               kTIFF_ASCIIType,       2,         "GPSSpeedRef" },
154 	{ /*    13 */ kTIFF_GPSSpeed,                  kTIFF_RationalType,    1,         "GPSSpeed" },
155 	{ /*    14 */ kTIFF_GPSTrackRef,               kTIFF_ASCIIType,       2,         "GPSTrackRef" },
156 	{ /*    15 */ kTIFF_GPSTrack,                  kTIFF_RationalType,    1,         "GPSTrack" },
157 	{ /*    16 */ kTIFF_GPSImgDirectionRef,        kTIFF_ASCIIType,       2,         "GPSImgDirectionRef" },
158 	{ /*    17 */ kTIFF_GPSImgDirection,           kTIFF_RationalType,    1,         "GPSImgDirection" },
159 	{ /*    18 */ kTIFF_GPSMapDatum,               kTIFF_ASCIIType,       kAnyCount, "GPSMapDatum" },
160 	{ /*    20 */ kTIFF_GPSDestLatitude,           kTIFF_RationalType,    3,         "" },	// ! Has a special mapping.
161 	{ /*    22 */ kTIFF_GPSDestLongitude,          kTIFF_RationalType,    3,         "" },	// ! Has a special mapping.
162 	{ /*    23 */ kTIFF_GPSDestBearingRef,         kTIFF_ASCIIType,       2,         "GPSDestBearingRef" },
163 	{ /*    24 */ kTIFF_GPSDestBearing,            kTIFF_RationalType,    1,         "GPSDestBearing" },
164 	{ /*    25 */ kTIFF_GPSDestDistanceRef,        kTIFF_ASCIIType,       2,         "GPSDestDistanceRef" },
165 	{ /*    26 */ kTIFF_GPSDestDistance,           kTIFF_RationalType,    1,         "GPSDestDistance" },
166 	{ /*    27 */ kTIFF_GPSProcessingMethod,       kTIFF_UndefinedType,   kAnyCount, "" },	// ! Has a special mapping.
167 	{ /*    28 */ kTIFF_GPSAreaInformation,        kTIFF_UndefinedType,   kAnyCount, "" },	// ! Has a special mapping.
168 	{ /*    30 */ kTIFF_GPSDifferential,           kTIFF_ShortType,       1,         "GPSDifferential" },
169 	{ 0xFFFF, 0, 0, 0 }	// ! Must end with sentinel.
170 };
171 
172 // =================================================================================================
173 
GatherInt(const char * strPtr,size_t count)174 static XMP_Uns32 GatherInt ( const char * strPtr, size_t count )
175 {
176 	XMP_Uns32 value = 0;
177 	const char * strEnd = strPtr + count;
178 
179 	while ( strPtr < strEnd ) {
180 		char ch = *strPtr;
181 		if ( (ch < '0') || (ch > '9') ) break;
182 		value = value*10 + (ch - '0');
183 		++strPtr;
184 	}
185 
186 	return value;
187 
188 }
189 
190 // =================================================================================================
191 // =================================================================================================
192 
193 // =================================================================================================
194 // ComputeTIFFDigest
195 // =================
196 //
197 // Compute a 128 bit (16 byte) MD5 digest of the mapped TIFF tags and format it as a string like:
198 //    256,257,...;A0FCE844924381619820B6F7117C8B83
199 // The first portion is a decimal list of the tags from sPrimaryIFDMappings, the last part is the
200 // MD5 digest as 32 hex digits using capital A-F.
201 
202 // ! The order of listing for the tags is crucial for the change comparisons to work!
203 
204 static void
ComputeTIFFDigest(const TIFF_Manager & tiff,std::string * digestStr)205 ComputeTIFFDigest ( const TIFF_Manager & tiff, std::string * digestStr )
206 {
207 	MD5_CTX    context;
208 	MD5_Digest digest;
209 	char       buffer[40];
210 	size_t     in, out;
211 
212 	TIFF_Manager::TagInfo tagInfo;
213 
214 	MD5Init ( &context );
215 	digestStr->clear();
216 	digestStr->reserve ( 160 );	// The current length is 134.
217 
218 	for ( size_t i = 0; sPrimaryIFDMappings[i].id != 0xFFFF; ++i ) {
219 		snprintf ( buffer, sizeof(buffer), "%d,", sPrimaryIFDMappings[i].id );	// AUDIT: Use of sizeof(buffer) is safe.
220 		digestStr->append ( buffer );
221 		bool found = tiff.GetTag ( kTIFF_PrimaryIFD, sPrimaryIFDMappings[i].id, &tagInfo );
222 		if ( found ) MD5Update ( &context, (XMP_Uns8*)tagInfo.dataPtr, tagInfo.dataLen );
223 	}
224 
225 	size_t endPos = digestStr->size() - 1;
226 	(*digestStr)[endPos] = ';';
227 
228 	MD5Final ( digest, &context );
229 
230 	for ( in = 0, out = 0; in < 16; in += 1, out += 2 ) {
231 		XMP_Uns8 byte = digest[in];
232 		buffer[out]   = ReconcileUtils::kHexDigits [ byte >> 4 ];
233 		buffer[out+1] = ReconcileUtils::kHexDigits [ byte & 0xF ];
234 	}
235 	buffer[32] = 0;
236 
237 	digestStr->append ( buffer );
238 
239 }	// ComputeTIFFDigest;
240 
241 // =================================================================================================
242 // ComputeExifDigest
243 // =================
244 //
245 // Compute a 128 bit (16 byte) MD5 digest of the mapped Exif andf GPS tags and format it as a string like:
246 //    36864,40960,...;A0FCE844924381619820B6F7117C8B83
247 // The first portion is a decimal list of the tags, the last part is the MD5 digest as 32 hex
248 // digits using capital A-F. The listed tags are those from sExifIFDMappings followed by those from
249 // sGPSInfoIFDMappings.
250 
251 // ! The order of listing for the tags is crucial for the change comparisons to work!
252 
253 static void
ComputeExifDigest(const TIFF_Manager & exif,std::string * digestStr)254 ComputeExifDigest ( const TIFF_Manager & exif, std::string * digestStr )
255 {
256 	MD5_CTX    context;
257 	MD5_Digest digest;
258 	char       buffer[40];
259 	size_t     in, out;
260 
261 	TIFF_Manager::TagInfo tagInfo;
262 
263 	MD5Init ( &context );
264 	digestStr->clear();
265 	digestStr->reserve ( 440 );	// The current length is 414.
266 
267 	for ( size_t i = 0; sExifIFDMappings[i].id != 0xFFFF; ++i ) {
268 		snprintf ( buffer, sizeof(buffer), "%d,", sExifIFDMappings[i].id );	// AUDIT: Use of sizeof(buffer) is safe.
269 		digestStr->append ( buffer );
270 		bool found = exif.GetTag ( kTIFF_ExifIFD, sExifIFDMappings[i].id, &tagInfo );
271 		if ( found ) MD5Update ( &context, (XMP_Uns8*)tagInfo.dataPtr, tagInfo.dataLen );
272 	}
273 
274 	for ( size_t i = 0; sGPSInfoIFDMappings[i].id != 0xFFFF; ++i ) {
275 		snprintf ( buffer, sizeof(buffer), "%d,", sGPSInfoIFDMappings[i].id );	// AUDIT: Use of sizeof(buffer) is safe.
276 		digestStr->append ( buffer );
277 		bool found = exif.GetTag ( kTIFF_GPSInfoIFD, sGPSInfoIFDMappings[i].id, &tagInfo );
278 		if ( found ) MD5Update ( &context, (XMP_Uns8*)tagInfo.dataPtr, tagInfo.dataLen );
279 	}
280 
281 	size_t endPos = digestStr->size() - 1;
282 	(*digestStr)[endPos] = ';';
283 
284 	MD5Final ( digest, &context );
285 
286 	for ( in = 0, out = 0; in < 16; in += 1, out += 2 ) {
287 		XMP_Uns8 byte = digest[in];
288 		buffer[out]   = ReconcileUtils::kHexDigits [ byte >> 4 ];
289 		buffer[out+1] = ReconcileUtils::kHexDigits [ byte & 0xF ];
290 	}
291 	buffer[32] = 0;
292 
293 	digestStr->append ( buffer );
294 
295 }	// ComputeExifDigest;
296 
297 // =================================================================================================
298 // ReconcileUtils::CheckTIFFDigest
299 // ===============================
300 
301 int
CheckTIFFDigest(const TIFF_Manager & tiff,const SXMPMeta & xmp)302 ReconcileUtils::CheckTIFFDigest ( const TIFF_Manager & tiff, const SXMPMeta & xmp )
303 {
304 	std::string newDigest, oldDigest;
305 
306 	ComputeTIFFDigest ( tiff, &newDigest );
307 	bool found = xmp.GetProperty ( kXMP_NS_TIFF, "NativeDigest", &oldDigest, 0 );
308 
309 	if ( ! found ) return kDigestMissing;
310 	if ( newDigest == oldDigest ) return kDigestMatches;
311 	return kDigestDiffers;
312 
313 }	// ReconcileUtils::CheckTIFFDigest;
314 
315 // =================================================================================================
316 // ReconcileUtils::CheckExifDigest
317 // ===============================
318 
319 int
CheckExifDigest(const TIFF_Manager & tiff,const SXMPMeta & xmp)320 ReconcileUtils::CheckExifDigest ( const TIFF_Manager & tiff, const SXMPMeta & xmp )
321 {
322 	std::string newDigest, oldDigest;
323 
324 	ComputeExifDigest ( tiff, &newDigest );
325 	bool found = xmp.GetProperty ( kXMP_NS_EXIF, "NativeDigest", &oldDigest, 0 );
326 
327 	if ( ! found ) return kDigestMissing;
328 	if ( newDigest == oldDigest ) return kDigestMatches;
329 	return kDigestDiffers;
330 
331 }	// ReconcileUtils::CheckExifDigest;
332 
333 // =================================================================================================
334 // ReconcileUtils::SetTIFFDigest
335 // =============================
336 
337 void
SetTIFFDigest(const TIFF_Manager & tiff,SXMPMeta * xmp)338 ReconcileUtils::SetTIFFDigest ( const TIFF_Manager & tiff, SXMPMeta * xmp )
339 {
340 	std::string newDigest;
341 
342 	ComputeTIFFDigest ( tiff, &newDigest );
343 	xmp->SetProperty ( kXMP_NS_TIFF, "NativeDigest", newDigest.c_str() );
344 
345 }	// ReconcileUtils::SetTIFFDigest;
346 
347 // =================================================================================================
348 // ReconcileUtils::SetExifDigest
349 // =============================
350 
351 void
SetExifDigest(const TIFF_Manager & tiff,SXMPMeta * xmp)352 ReconcileUtils::SetExifDigest ( const TIFF_Manager & tiff, SXMPMeta * xmp )
353 {
354 	std::string newDigest;
355 
356 	ComputeExifDigest ( tiff, &newDigest );
357 	xmp->SetProperty ( kXMP_NS_EXIF, "NativeDigest", newDigest.c_str() );
358 
359 }	// ReconcileUtils::SetExifDigest;
360 
361 // =================================================================================================
362 // =================================================================================================
363 
364 // =================================================================================================
365 // ImportSingleTIFF_Short
366 // ======================
367 
368 static void
ImportSingleTIFF_Short(const TIFF_Manager::TagInfo & tagInfo,const bool nativeEndian,SXMPMeta * xmp,const char * xmpNS,const char * xmpProp)369 ImportSingleTIFF_Short ( const TIFF_Manager::TagInfo & tagInfo, const bool nativeEndian,
370 						 SXMPMeta * xmp, const char * xmpNS, const char * xmpProp )
371 {
372 	try {	// Don't let errors with one stop the others.
373 
374 		XMP_Uns16 binValue = *((XMP_Uns16*)tagInfo.dataPtr);
375 		if ( ! nativeEndian ) binValue = Flip2 ( binValue );
376 
377 		char strValue[20];
378 		snprintf ( strValue, sizeof(strValue), "%hu", binValue );	// AUDIT: Using sizeof(strValue) is safe.
379 
380 		xmp->SetProperty ( xmpNS, xmpProp, strValue );
381 
382 	} catch ( ... ) {
383 		// Do nothing, let other imports proceed.
384 		// ? Notify client?
385 	}
386 
387 }	// ImportSingleTIFF_Short
388 
389 // =================================================================================================
390 // ImportSingleTIFF_Long
391 // =====================
392 
393 static void
ImportSingleTIFF_Long(const TIFF_Manager::TagInfo & tagInfo,const bool nativeEndian,SXMPMeta * xmp,const char * xmpNS,const char * xmpProp)394 ImportSingleTIFF_Long ( const TIFF_Manager::TagInfo & tagInfo, const bool nativeEndian,
395 						SXMPMeta * xmp, const char * xmpNS, const char * xmpProp )
396 {
397 	try {	// Don't let errors with one stop the others.
398 
399 		XMP_Uns32 binValue = *((XMP_Uns32*)tagInfo.dataPtr);
400 		if ( ! nativeEndian ) binValue = Flip4 ( binValue );
401 
402 		char strValue[20];
403 		snprintf ( strValue, sizeof(strValue), "%"PRIu32, binValue );	// AUDIT: Using sizeof(strValue) is safe.
404 
405 		xmp->SetProperty ( xmpNS, xmpProp, strValue );
406 
407 	} catch ( ... ) {
408 		// Do nothing, let other imports proceed.
409 		// ? Notify client?
410 	}
411 
412 }	// ImportSingleTIFF_Long
413 
414 // =================================================================================================
415 // ImportSingleTIFF_Rational
416 // =========================
417 
418 static void
ImportSingleTIFF_Rational(const TIFF_Manager::TagInfo & tagInfo,const bool nativeEndian,SXMPMeta * xmp,const char * xmpNS,const char * xmpProp)419 ImportSingleTIFF_Rational ( const TIFF_Manager::TagInfo & tagInfo, const bool nativeEndian,
420 							SXMPMeta * xmp, const char * xmpNS, const char * xmpProp )
421 {
422 	try {	// Don't let errors with one stop the others.
423 
424 		XMP_Uns32 * binPtr = (XMP_Uns32*)tagInfo.dataPtr;
425 		XMP_Uns32 binNum   = binPtr[0];
426 		XMP_Uns32 binDenom = binPtr[1];
427 		if ( ! nativeEndian ) {
428 			binNum = Flip4 ( binNum );
429 			binDenom = Flip4 ( binDenom );
430 		}
431 
432 		char strValue[40];
433 		snprintf ( strValue, sizeof(strValue), "%"PRIu32"/%"PRIu32, binNum, binDenom );	// AUDIT: Using sizeof(strValue) is safe.
434 
435 		xmp->SetProperty ( xmpNS, xmpProp, strValue );
436 
437 	} catch ( ... ) {
438 		// Do nothing, let other imports proceed.
439 		// ? Notify client?
440 	}
441 
442 }	// ImportSingleTIFF_Rational
443 
444 // =================================================================================================
445 // ImportSingleTIFF_SRational
446 // ==========================
447 
448 static void
ImportSingleTIFF_SRational(const TIFF_Manager::TagInfo & tagInfo,const bool nativeEndian,SXMPMeta * xmp,const char * xmpNS,const char * xmpProp)449 ImportSingleTIFF_SRational ( const TIFF_Manager::TagInfo & tagInfo, const bool nativeEndian,
450 							 SXMPMeta * xmp, const char * xmpNS, const char * xmpProp )
451 {
452 	try {	// Don't let errors with one stop the others.
453 
454 		XMP_Int32 * binPtr = (XMP_Int32*)tagInfo.dataPtr;
455 		XMP_Int32 binNum   = binPtr[0];
456 		XMP_Int32 binDenom = binPtr[1];
457 		if ( ! nativeEndian ) {
458 			Flip4 ( &binNum );
459 			Flip4 ( &binDenom );
460 		}
461 
462 		char strValue[40];
463 		snprintf ( strValue, sizeof(strValue), "%"PRId32"/%"PRId32, binNum, binDenom );	// AUDIT: Using sizeof(strValue) is safe.
464 
465 		xmp->SetProperty ( xmpNS, xmpProp, strValue );
466 
467 	} catch ( ... ) {
468 		// Do nothing, let other imports proceed.
469 		// ? Notify client?
470 	}
471 
472 }	// ImportSingleTIFF_SRational
473 
474 // =================================================================================================
475 // ImportSingleTIFF_ASCII
476 // ======================
477 
478 static void
ImportSingleTIFF_ASCII(const TIFF_Manager::TagInfo & tagInfo,SXMPMeta * xmp,const char * xmpNS,const char * xmpProp)479 ImportSingleTIFF_ASCII ( const TIFF_Manager::TagInfo & tagInfo,
480 						 SXMPMeta * xmp, const char * xmpNS, const char * xmpProp )
481 {
482 	try {	// Don't let errors with one stop the others.
483 
484 		const char * chPtr  = (const char *)tagInfo.dataPtr;
485 		const bool   hasNul = (chPtr[tagInfo.dataLen-1] == 0);
486 		const bool   isUTF8 = ReconcileUtils::IsUTF8 ( chPtr, tagInfo.dataLen );
487 
488 		if ( isUTF8 && hasNul ) {
489 			xmp->SetProperty ( xmpNS, xmpProp, chPtr );
490 		} else {
491 			std::string strValue;
492 			if ( isUTF8 ) {
493 				strValue.assign ( chPtr, tagInfo.dataLen );
494 			} else {
495 				#if ! XMP_UNIXBuild
496 					ReconcileUtils::LocalToUTF8 ( chPtr, tagInfo.dataLen, &strValue );
497 				#else
498 					return;	// ! Hack until legacy-as-local issues are resolved for generic UNIX.
499 				#endif
500 			}
501 			xmp->SetProperty ( xmpNS, xmpProp, strValue.c_str() );
502 		}
503 
504 	} catch ( ... ) {
505 		// Do nothing, let other imports proceed.
506 		// ? Notify client?
507 	}
508 
509 }	// ImportSingleTIFF_ASCII
510 
511 // =================================================================================================
512 // ImportSingleTIFF_Byte
513 // =====================
514 
515 static void
ImportSingleTIFF_Byte(const TIFF_Manager::TagInfo & tagInfo,SXMPMeta * xmp,const char * xmpNS,const char * xmpProp)516 ImportSingleTIFF_Byte ( const TIFF_Manager::TagInfo & tagInfo,
517 						SXMPMeta * xmp, const char * xmpNS, const char * xmpProp )
518 {
519 	try {	// Don't let errors with one stop the others.
520 
521 		XMP_Uns8 binValue = *((XMP_Uns8*)tagInfo.dataPtr);
522 
523 		char strValue[20];
524 		snprintf ( strValue, sizeof(strValue), "%hhu", binValue );	// AUDIT: Using sizeof(strValue) is safe.
525 
526 		xmp->SetProperty ( xmpNS, xmpProp, strValue );
527 
528 	} catch ( ... ) {
529 		// Do nothing, let other imports proceed.
530 		// ? Notify client?
531 	}
532 
533 }	// ImportSingleTIFF_Byte
534 
535 // =================================================================================================
536 // ImportSingleTIFF_SByte
537 // ======================
538 
539 static void
ImportSingleTIFF_SByte(const TIFF_Manager::TagInfo & tagInfo,SXMPMeta * xmp,const char * xmpNS,const char * xmpProp)540 ImportSingleTIFF_SByte ( const TIFF_Manager::TagInfo & tagInfo,
541 						 SXMPMeta * xmp, const char * xmpNS, const char * xmpProp )
542 {
543 	try {	// Don't let errors with one stop the others.
544 
545 		XMP_Int8 binValue = *((XMP_Int8*)tagInfo.dataPtr);
546 
547 		char strValue[20];
548 		snprintf ( strValue, sizeof(strValue), "%hhd", binValue );	// AUDIT: Using sizeof(strValue) is safe.
549 
550 		xmp->SetProperty ( xmpNS, xmpProp, strValue );
551 
552 	} catch ( ... ) {
553 		// Do nothing, let other imports proceed.
554 		// ? Notify client?
555 	}
556 
557 }	// ImportSingleTIFF_SByte
558 
559 // =================================================================================================
560 // ImportSingleTIFF_SShort
561 // =======================
562 
563 static void
ImportSingleTIFF_SShort(const TIFF_Manager::TagInfo & tagInfo,const bool nativeEndian,SXMPMeta * xmp,const char * xmpNS,const char * xmpProp)564 ImportSingleTIFF_SShort ( const TIFF_Manager::TagInfo & tagInfo, const bool nativeEndian,
565 						  SXMPMeta * xmp, const char * xmpNS, const char * xmpProp )
566 {
567 	try {	// Don't let errors with one stop the others.
568 
569 		XMP_Int16 binValue = *((XMP_Int16*)tagInfo.dataPtr);
570 		if ( ! nativeEndian ) Flip2 ( &binValue );
571 
572 		char strValue[20];
573 		snprintf ( strValue, sizeof(strValue), "%hd", binValue );	// AUDIT: Using sizeof(strValue) is safe.
574 
575 		xmp->SetProperty ( xmpNS, xmpProp, strValue );
576 
577 	} catch ( ... ) {
578 		// Do nothing, let other imports proceed.
579 		// ? Notify client?
580 	}
581 
582 }	// ImportSingleTIFF_SShort
583 
584 // =================================================================================================
585 // ImportSingleTIFF_SLong
586 // ======================
587 
588 static void
ImportSingleTIFF_SLong(const TIFF_Manager::TagInfo & tagInfo,const bool nativeEndian,SXMPMeta * xmp,const char * xmpNS,const char * xmpProp)589 ImportSingleTIFF_SLong ( const TIFF_Manager::TagInfo & tagInfo, const bool nativeEndian,
590 						 SXMPMeta * xmp, const char * xmpNS, const char * xmpProp )
591 {
592 	try {	// Don't let errors with one stop the others.
593 
594 		XMP_Int32 binValue = *((XMP_Int32*)tagInfo.dataPtr);
595 		if ( ! nativeEndian ) Flip4 ( &binValue );
596 
597 		char strValue[20];
598 		snprintf ( strValue, sizeof(strValue), "%"PRId32, binValue );	// AUDIT: Using sizeof(strValue) is safe.
599 
600 		xmp->SetProperty ( xmpNS, xmpProp, strValue );
601 
602 	} catch ( ... ) {
603 		// Do nothing, let other imports proceed.
604 		// ? Notify client?
605 	}
606 
607 }	// ImportSingleTIFF_SLong
608 
609 // =================================================================================================
610 // ImportSingleTIFF_Float
611 // ======================
612 
613 static void
ImportSingleTIFF_Float(const TIFF_Manager::TagInfo & tagInfo,const bool nativeEndian,SXMPMeta * xmp,const char * xmpNS,const char * xmpProp)614 ImportSingleTIFF_Float ( const TIFF_Manager::TagInfo & tagInfo, const bool nativeEndian,
615 						 SXMPMeta * xmp, const char * xmpNS, const char * xmpProp )
616 {
617 	try {	// Don't let errors with one stop the others.
618 
619 		float binValue = *((float*)tagInfo.dataPtr);
620 		if ( ! nativeEndian ) Flip4 ( &binValue );
621 
622 		xmp->SetProperty_Float ( xmpNS, xmpProp, binValue );
623 
624 	} catch ( ... ) {
625 		// Do nothing, let other imports proceed.
626 		// ? Notify client?
627 	}
628 
629 }	// ImportSingleTIFF_Float
630 
631 // =================================================================================================
632 // ImportSingleTIFF_Double
633 // =======================
634 
635 static void
ImportSingleTIFF_Double(const TIFF_Manager::TagInfo & tagInfo,const bool nativeEndian,SXMPMeta * xmp,const char * xmpNS,const char * xmpProp)636 ImportSingleTIFF_Double ( const TIFF_Manager::TagInfo & tagInfo, const bool nativeEndian,
637 						  SXMPMeta * xmp, const char * xmpNS, const char * xmpProp )
638 {
639 	try {	// Don't let errors with one stop the others.
640 
641 		double binValue = *((double*)tagInfo.dataPtr);
642 		if ( ! nativeEndian ) Flip8 ( &binValue );
643 
644 		xmp->SetProperty_Float ( xmpNS, xmpProp, binValue );	// ! Yes, SetProperty_Float.
645 
646 	} catch ( ... ) {
647 		// Do nothing, let other imports proceed.
648 		// ? Notify client?
649 	}
650 
651 }	// ImportSingleTIFF_Double
652 
653 // =================================================================================================
654 // ImportSingleTIFF
655 // ================
656 
657 static void
ImportSingleTIFF(const TIFF_Manager::TagInfo & tagInfo,const bool nativeEndian,SXMPMeta * xmp,const char * xmpNS,const char * xmpProp)658 ImportSingleTIFF ( const TIFF_Manager::TagInfo & tagInfo, const bool nativeEndian,
659 				   SXMPMeta * xmp, const char * xmpNS, const char * xmpProp )
660 {
661 
662 	// We've got a tag to map to XMP, decide how based on actual type and the expected count. Using
663 	// the actual type eliminates a ShortOrLong case. Using the expected count is needed to know
664 	// whether to create an XMP array. The actual count for an array could be 1. Put the most
665 	// common cases first for better iCache utilization.
666 
667 	switch ( tagInfo.type ) {
668 
669 		case kTIFF_ShortType :
670 			ImportSingleTIFF_Short ( tagInfo, nativeEndian, xmp, xmpNS, xmpProp );
671 			break;
672 
673 		case kTIFF_LongType :
674 			ImportSingleTIFF_Long ( tagInfo, nativeEndian, xmp, xmpNS, xmpProp );
675 			break;
676 
677 		case kTIFF_RationalType :
678 			ImportSingleTIFF_Rational ( tagInfo, nativeEndian, xmp, xmpNS, xmpProp );
679 			break;
680 
681 		case kTIFF_SRationalType :
682 			ImportSingleTIFF_SRational ( tagInfo, nativeEndian, xmp, xmpNS, xmpProp );
683 			break;
684 
685 		case kTIFF_ASCIIType :
686 			ImportSingleTIFF_ASCII ( tagInfo, xmp, xmpNS, xmpProp );
687 			break;
688 
689 		case kTIFF_ByteType :
690 			ImportSingleTIFF_Byte ( tagInfo, xmp, xmpNS, xmpProp );
691 			break;
692 
693 		case kTIFF_SByteType :
694 			ImportSingleTIFF_SByte ( tagInfo, xmp, xmpNS, xmpProp );
695 			break;
696 
697 		case kTIFF_SShortType :
698 			ImportSingleTIFF_SShort ( tagInfo, nativeEndian, xmp, xmpNS, xmpProp );
699 			break;
700 
701 		case kTIFF_SLongType :
702 			ImportSingleTIFF_SLong ( tagInfo, nativeEndian, xmp, xmpNS, xmpProp );
703 			break;
704 
705 		case kTIFF_FloatType :
706 			ImportSingleTIFF_Float ( tagInfo, nativeEndian, xmp, xmpNS, xmpProp );
707 			break;
708 
709 		case kTIFF_DoubleType :
710 			ImportSingleTIFF_Double ( tagInfo, nativeEndian, xmp, xmpNS, xmpProp );
711 			break;
712 
713 	}
714 
715 }	// ImportSingleTIFF
716 
717 // =================================================================================================
718 // =================================================================================================
719 
720 // =================================================================================================
721 // ImportArrayTIFF_Short
722 // =====================
723 
724 static void
ImportArrayTIFF_Short(const TIFF_Manager::TagInfo & tagInfo,const bool nativeEndian,SXMPMeta * xmp,const char * xmpNS,const char * xmpProp)725 ImportArrayTIFF_Short ( const TIFF_Manager::TagInfo & tagInfo, const bool nativeEndian,
726 						SXMPMeta * xmp, const char * xmpNS, const char * xmpProp )
727 {
728 	try {	// Don't let errors with one stop the others.
729 
730 		XMP_Uns16 * binPtr = (XMP_Uns16*)tagInfo.dataPtr;
731 
732 		for ( size_t i = 0; i < tagInfo.count; ++i, ++binPtr ) {
733 
734 			XMP_Uns16 binValue = *binPtr;
735 			if ( ! nativeEndian ) binValue = Flip2 ( binValue );
736 
737 			char strValue[20];
738 			snprintf ( strValue, sizeof(strValue), "%hu", binValue );	// AUDIT: Using sizeof(strValue) is safe.
739 
740 			xmp->AppendArrayItem ( xmpNS, xmpProp, kXMP_PropArrayIsOrdered, strValue );
741 
742 		}
743 
744 	} catch ( ... ) {
745 		// Do nothing, let other imports proceed.
746 		// ? Notify client?
747 	}
748 
749 }	// ImportArrayTIFF_Short
750 
751 // =================================================================================================
752 // ImportArrayTIFF_Long
753 // ====================
754 
755 static void
ImportArrayTIFF_Long(const TIFF_Manager::TagInfo & tagInfo,const bool nativeEndian,SXMPMeta * xmp,const char * xmpNS,const char * xmpProp)756 ImportArrayTIFF_Long ( const TIFF_Manager::TagInfo & tagInfo, const bool nativeEndian,
757 					   SXMPMeta * xmp, const char * xmpNS, const char * xmpProp )
758 {
759 	try {	// Don't let errors with one stop the others.
760 
761 		XMP_Uns32 * binPtr = (XMP_Uns32*)tagInfo.dataPtr;
762 
763 		for ( size_t i = 0; i < tagInfo.count; ++i, ++binPtr ) {
764 
765 			XMP_Uns32 binValue = *binPtr;
766 			if ( ! nativeEndian ) binValue = Flip4 ( binValue );
767 
768 			char strValue[20];
769 			snprintf ( strValue, sizeof(strValue), "%"PRIu32, binValue );	// AUDIT: Using sizeof(strValue) is safe.
770 
771 			xmp->AppendArrayItem ( xmpNS, xmpProp, kXMP_PropArrayIsOrdered, strValue );
772 
773 		}
774 
775 	} catch ( ... ) {
776 		// Do nothing, let other imports proceed.
777 		// ? Notify client?
778 	}
779 
780 }	// ImportArrayTIFF_Long
781 
782 // =================================================================================================
783 // ImportArrayTIFF_Rational
784 // ========================
785 
786 static void
ImportArrayTIFF_Rational(const TIFF_Manager::TagInfo & tagInfo,const bool nativeEndian,SXMPMeta * xmp,const char * xmpNS,const char * xmpProp)787 ImportArrayTIFF_Rational ( const TIFF_Manager::TagInfo & tagInfo, const bool nativeEndian,
788 						   SXMPMeta * xmp, const char * xmpNS, const char * xmpProp )
789 {
790 	try {	// Don't let errors with one stop the others.
791 
792 		XMP_Uns32 * binPtr = (XMP_Uns32*)tagInfo.dataPtr;
793 
794 		for ( size_t i = 0; i < tagInfo.count; ++i, binPtr += 2 ) {
795 
796 			XMP_Uns32 binNum   = binPtr[0];
797 			XMP_Uns32 binDenom = binPtr[1];
798 			if ( ! nativeEndian ) {
799 				binNum = Flip4 ( binNum );
800 				binDenom = Flip4 ( binDenom );
801 			}
802 
803 			char strValue[40];
804 			snprintf ( strValue, sizeof(strValue), "%"PRIu32"/%"PRIu32, binNum, binDenom );	// AUDIT: Using sizeof(strValue) is safe.
805 
806 			xmp->AppendArrayItem ( xmpNS, xmpProp, kXMP_PropArrayIsOrdered, strValue );
807 
808 		}
809 
810 	} catch ( ... ) {
811 		// Do nothing, let other imports proceed.
812 		// ? Notify client?
813 	}
814 
815 }	// ImportArrayTIFF_Rational
816 
817 // =================================================================================================
818 // ImportArrayTIFF_SRational
819 // =========================
820 
821 static void
ImportArrayTIFF_SRational(const TIFF_Manager::TagInfo & tagInfo,const bool nativeEndian,SXMPMeta * xmp,const char * xmpNS,const char * xmpProp)822 ImportArrayTIFF_SRational ( const TIFF_Manager::TagInfo & tagInfo, const bool nativeEndian,
823 							SXMPMeta * xmp, const char * xmpNS, const char * xmpProp )
824 {
825 	try {	// Don't let errors with one stop the others.
826 
827 		XMP_Int32 * binPtr = (XMP_Int32*)tagInfo.dataPtr;
828 
829 		for ( size_t i = 0; i < tagInfo.count; ++i, binPtr += 2 ) {
830 
831 			XMP_Int32 binNum   = binPtr[0];
832 			XMP_Int32 binDenom = binPtr[1];
833 			if ( ! nativeEndian ) {
834 				Flip4 ( &binNum );
835 				Flip4 ( &binDenom );
836 			}
837 
838 			char strValue[40];
839 			snprintf ( strValue, sizeof(strValue), "%"PRId32"/%"PRId32, binNum, binDenom );	// AUDIT: Using sizeof(strValue) is safe.
840 
841 			xmp->AppendArrayItem ( xmpNS, xmpProp, kXMP_PropArrayIsOrdered, strValue );
842 
843 		}
844 
845 	} catch ( ... ) {
846 		// Do nothing, let other imports proceed.
847 		// ? Notify client?
848 	}
849 
850 }	// ImportArrayTIFF_SRational
851 
852 // =================================================================================================
853 // ImportArrayTIFF_ASCII
854 // =====================
855 
856 static void
ImportArrayTIFF_ASCII(const TIFF_Manager::TagInfo & tagInfo,SXMPMeta * xmp,const char * xmpNS,const char * xmpProp)857 ImportArrayTIFF_ASCII ( const TIFF_Manager::TagInfo & tagInfo,
858 						SXMPMeta * xmp, const char * xmpNS, const char * xmpProp )
859 {
860 	try {	// Don't let errors with one stop the others.
861 
862 		const char * chPtr  = (const char *)tagInfo.dataPtr;
863 		const char * chEnd  = chPtr + tagInfo.dataLen;
864 		const bool   hasNul = (chPtr[tagInfo.dataLen-1] == 0);
865 		const bool   isUTF8 = ReconcileUtils::IsUTF8 ( chPtr, tagInfo.dataLen );
866 
867 		std::string  strValue;
868 
869 		if ( (! isUTF8) || (! hasNul) ) {
870 			if ( isUTF8 ) {
871 				strValue.assign ( chPtr, tagInfo.dataLen );
872 			} else {
873 				#if ! XMP_UNIXBuild
874 					ReconcileUtils::LocalToUTF8 ( chPtr, tagInfo.dataLen, &strValue );
875 				#else
876 					return;	// ! Hack until legacy-as-local issues are resolved for generic UNIX.
877 				#endif
878 			}
879 			chPtr = strValue.c_str();
880 			chEnd = chPtr + strValue.size();
881 		}
882 
883 		for ( ; chPtr < chEnd; chPtr += (strlen(chPtr) + 1) ) {
884 			xmp->AppendArrayItem ( xmpNS, xmpProp, kXMP_PropArrayIsOrdered, chPtr );
885 		}
886 
887 	} catch ( ... ) {
888 		// Do nothing, let other imports proceed.
889 		// ? Notify client?
890 	}
891 
892 }	// ImportArrayTIFF_ASCII
893 
894 // =================================================================================================
895 // ImportArrayTIFF_Byte
896 // ====================
897 
898 static void
ImportArrayTIFF_Byte(const TIFF_Manager::TagInfo & tagInfo,SXMPMeta * xmp,const char * xmpNS,const char * xmpProp)899 ImportArrayTIFF_Byte ( const TIFF_Manager::TagInfo & tagInfo,
900 					   SXMPMeta * xmp, const char * xmpNS, const char * xmpProp )
901 {
902 	try {	// Don't let errors with one stop the others.
903 
904 		XMP_Uns8 * binPtr = (XMP_Uns8*)tagInfo.dataPtr;
905 
906 		for ( size_t i = 0; i < tagInfo.count; ++i, ++binPtr ) {
907 
908 			XMP_Uns8 binValue = *binPtr;
909 
910 			char strValue[20];
911 			snprintf ( strValue, sizeof(strValue), "%hhu", binValue );	// AUDIT: Using sizeof(strValue) is safe.
912 
913 			xmp->AppendArrayItem ( xmpNS, xmpProp, kXMP_PropArrayIsOrdered, strValue );
914 
915 		}
916 
917 	} catch ( ... ) {
918 		// Do nothing, let other imports proceed.
919 		// ? Notify client?
920 	}
921 
922 }	// ImportArrayTIFF_Byte
923 
924 // =================================================================================================
925 // ImportArrayTIFF_SByte
926 // =====================
927 
928 static void
ImportArrayTIFF_SByte(const TIFF_Manager::TagInfo & tagInfo,SXMPMeta * xmp,const char * xmpNS,const char * xmpProp)929 ImportArrayTIFF_SByte ( const TIFF_Manager::TagInfo & tagInfo,
930 						SXMPMeta * xmp, const char * xmpNS, const char * xmpProp )
931 {
932 	try {	// Don't let errors with one stop the others.
933 
934 		XMP_Int8 * binPtr = (XMP_Int8*)tagInfo.dataPtr;
935 
936 		for ( size_t i = 0; i < tagInfo.count; ++i, ++binPtr ) {
937 
938 			XMP_Int8 binValue = *binPtr;
939 
940 			char strValue[20];
941 			snprintf ( strValue, sizeof(strValue), "%hhd", binValue );	// AUDIT: Using sizeof(strValue) is safe.
942 
943 			xmp->AppendArrayItem ( xmpNS, xmpProp, kXMP_PropArrayIsOrdered, strValue );
944 
945 		}
946 
947 	} catch ( ... ) {
948 		// Do nothing, let other imports proceed.
949 		// ? Notify client?
950 	}
951 
952 }	// ImportArrayTIFF_SByte
953 
954 // =================================================================================================
955 // ImportArrayTIFF_SShort
956 // ======================
957 
958 static void
ImportArrayTIFF_SShort(const TIFF_Manager::TagInfo & tagInfo,const bool nativeEndian,SXMPMeta * xmp,const char * xmpNS,const char * xmpProp)959 ImportArrayTIFF_SShort ( const TIFF_Manager::TagInfo & tagInfo, const bool nativeEndian,
960 						 SXMPMeta * xmp, const char * xmpNS, const char * xmpProp )
961 {
962 	try {	// Don't let errors with one stop the others.
963 
964 		XMP_Int16 * binPtr = (XMP_Int16*)tagInfo.dataPtr;
965 
966 		for ( size_t i = 0; i < tagInfo.count; ++i, ++binPtr ) {
967 
968 			XMP_Int16 binValue = *binPtr;
969 			if ( ! nativeEndian ) Flip2 ( &binValue );
970 
971 			char strValue[20];
972 			snprintf ( strValue, sizeof(strValue), "%hd", binValue );	// AUDIT: Using sizeof(strValue) is safe.
973 
974 			xmp->AppendArrayItem ( xmpNS, xmpProp, kXMP_PropArrayIsOrdered, strValue );
975 
976 		}
977 
978 	} catch ( ... ) {
979 		// Do nothing, let other imports proceed.
980 		// ? Notify client?
981 	}
982 
983 }	// ImportArrayTIFF_SShort
984 
985 // =================================================================================================
986 // ImportArrayTIFF_SLong
987 // =====================
988 
989 static void
ImportArrayTIFF_SLong(const TIFF_Manager::TagInfo & tagInfo,const bool nativeEndian,SXMPMeta * xmp,const char * xmpNS,const char * xmpProp)990 ImportArrayTIFF_SLong ( const TIFF_Manager::TagInfo & tagInfo, const bool nativeEndian,
991 						SXMPMeta * xmp, const char * xmpNS, const char * xmpProp )
992 {
993 	try {	// Don't let errors with one stop the others.
994 
995 		XMP_Int32 * binPtr = (XMP_Int32*)tagInfo.dataPtr;
996 
997 		for ( size_t i = 0; i < tagInfo.count; ++i, ++binPtr ) {
998 
999 			XMP_Int32 binValue = *binPtr;
1000 			if ( ! nativeEndian ) Flip4 ( &binValue );
1001 
1002 			char strValue[20];
1003 			snprintf ( strValue, sizeof(strValue), "%"PRId32, binValue );	// AUDIT: Using sizeof(strValue) is safe.
1004 
1005 			xmp->AppendArrayItem ( xmpNS, xmpProp, kXMP_PropArrayIsOrdered, strValue );
1006 
1007 		}
1008 
1009 	} catch ( ... ) {
1010 		// Do nothing, let other imports proceed.
1011 		// ? Notify client?
1012 	}
1013 
1014 }	// ImportArrayTIFF_SLong
1015 
1016 // =================================================================================================
1017 // ImportArrayTIFF_Float
1018 // =====================
1019 
1020 static void
ImportArrayTIFF_Float(const TIFF_Manager::TagInfo & tagInfo,const bool nativeEndian,SXMPMeta * xmp,const char * xmpNS,const char * xmpProp)1021 ImportArrayTIFF_Float ( const TIFF_Manager::TagInfo & tagInfo, const bool nativeEndian,
1022 						SXMPMeta * xmp, const char * xmpNS, const char * xmpProp )
1023 {
1024 	try {	// Don't let errors with one stop the others.
1025 
1026 		float * binPtr = (float*)tagInfo.dataPtr;
1027 
1028 		for ( size_t i = 0; i < tagInfo.count; ++i, ++binPtr ) {
1029 
1030 			float binValue = *binPtr;
1031 			if ( ! nativeEndian ) Flip4 ( &binValue );
1032 
1033 			std::string strValue;
1034 			SXMPUtils::ConvertFromFloat ( binValue, "", &strValue );
1035 
1036 			xmp->AppendArrayItem ( xmpNS, xmpProp, kXMP_PropArrayIsOrdered, strValue.c_str() );
1037 
1038 		}
1039 
1040 	} catch ( ... ) {
1041 		// Do nothing, let other imports proceed.
1042 		// ? Notify client?
1043 	}
1044 
1045 }	// ImportArrayTIFF_Float
1046 
1047 // =================================================================================================
1048 // ImportArrayTIFF_Double
1049 // ======================
1050 
1051 static void
ImportArrayTIFF_Double(const TIFF_Manager::TagInfo & tagInfo,const bool nativeEndian,SXMPMeta * xmp,const char * xmpNS,const char * xmpProp)1052 ImportArrayTIFF_Double ( const TIFF_Manager::TagInfo & tagInfo, const bool nativeEndian,
1053 						 SXMPMeta * xmp, const char * xmpNS, const char * xmpProp )
1054 {
1055 	try {	// Don't let errors with one stop the others.
1056 
1057 		double * binPtr = (double*)tagInfo.dataPtr;
1058 
1059 		for ( size_t i = 0; i < tagInfo.count; ++i, ++binPtr ) {
1060 
1061 			double binValue = *binPtr;
1062 			if ( ! nativeEndian ) Flip8 ( &binValue );
1063 
1064 			std::string strValue;
1065 			SXMPUtils::ConvertFromFloat ( binValue, "", &strValue );	// ! Yes, ConvertFromFloat.
1066 
1067 			xmp->AppendArrayItem ( xmpNS, xmpProp, kXMP_PropArrayIsOrdered, strValue.c_str() );
1068 
1069 		}
1070 
1071 	} catch ( ... ) {
1072 		// Do nothing, let other imports proceed.
1073 		// ? Notify client?
1074 	}
1075 
1076 }	// ImportArrayTIFF_Double
1077 
1078 // =================================================================================================
1079 // ImportArrayTIFF
1080 // ===============
1081 
1082 static void
ImportArrayTIFF(const TIFF_Manager::TagInfo & tagInfo,const bool nativeEndian,SXMPMeta * xmp,const char * xmpNS,const char * xmpProp)1083 ImportArrayTIFF ( const TIFF_Manager::TagInfo & tagInfo, const bool nativeEndian,
1084 				  SXMPMeta * xmp, const char * xmpNS, const char * xmpProp )
1085 {
1086 
1087 	// We've got a tag to map to XMP, decide how based on actual type and the expected count. Using
1088 	// the actual type eliminates a ShortOrLong case. Using the expected count is needed to know
1089 	// whether to create an XMP array. The actual count for an array could be 1. Put the most
1090 	// common cases first for better iCache utilization.
1091 
1092 	switch ( tagInfo.type ) {
1093 
1094 		case kTIFF_ShortType :
1095 			ImportArrayTIFF_Short ( tagInfo, nativeEndian, xmp, xmpNS, xmpProp );
1096 			break;
1097 
1098 		case kTIFF_LongType :
1099 			ImportArrayTIFF_Long ( tagInfo, nativeEndian, xmp, xmpNS, xmpProp );
1100 			break;
1101 
1102 		case kTIFF_RationalType :
1103 			ImportArrayTIFF_Rational ( tagInfo, nativeEndian, xmp, xmpNS, xmpProp );
1104 			break;
1105 
1106 		case kTIFF_SRationalType :
1107 			ImportArrayTIFF_SRational ( tagInfo, nativeEndian, xmp, xmpNS, xmpProp );
1108 			break;
1109 
1110 		case kTIFF_ASCIIType :
1111 			ImportArrayTIFF_ASCII ( tagInfo, xmp, xmpNS, xmpProp );
1112 			break;
1113 
1114 		case kTIFF_ByteType :
1115 			ImportArrayTIFF_Byte ( tagInfo, xmp, xmpNS, xmpProp );
1116 			break;
1117 
1118 		case kTIFF_SByteType :
1119 			ImportArrayTIFF_SByte ( tagInfo, xmp, xmpNS, xmpProp );
1120 			break;
1121 
1122 		case kTIFF_SShortType :
1123 			ImportArrayTIFF_SShort ( tagInfo, nativeEndian, xmp, xmpNS, xmpProp );
1124 			break;
1125 
1126 		case kTIFF_SLongType :
1127 			ImportArrayTIFF_SLong ( tagInfo, nativeEndian, xmp, xmpNS, xmpProp );
1128 			break;
1129 
1130 		case kTIFF_FloatType :
1131 			ImportArrayTIFF_Float ( tagInfo, nativeEndian, xmp, xmpNS, xmpProp );
1132 			break;
1133 
1134 		case kTIFF_DoubleType :
1135 			ImportArrayTIFF_Double ( tagInfo, nativeEndian, xmp, xmpNS, xmpProp );
1136 			break;
1137 
1138 	}
1139 
1140 }	// ImportArrayTIFF
1141 
1142 // =================================================================================================
1143 // ImportTIFF_VerifyImport
1144 // =======================
1145 //
1146 // Decide whether to proceed with the import based on the digest state and presence of the legacy
1147 // and XMP. Will also delete existing XMP if appropriate.
1148 
1149 static bool
ImportTIFF_VerifyImport(const TIFF_Manager & tiff,SXMPMeta * xmp,int digestState,XMP_Uns8 tiffIFD,XMP_Uns16 tiffID,const char * xmpNS,const char * xmpProp,TIFF_Manager::TagInfo * tagInfo)1150 ImportTIFF_VerifyImport ( const TIFF_Manager & tiff, SXMPMeta * xmp, int digestState,
1151 						  XMP_Uns8 tiffIFD, XMP_Uns16 tiffID, const char * xmpNS, const char * xmpProp,
1152 						  TIFF_Manager::TagInfo * tagInfo )
1153 {
1154 	bool found = false;
1155 
1156 	try {	// Don't let errors with one stop the others.
1157 
1158 		if ( digestState == kDigestDiffers ) {
1159 			xmp->DeleteProperty ( xmpNS, xmpProp );
1160 		} else {
1161 			XMP_Assert ( digestState == kDigestMissing );
1162 			if ( xmp->DoesPropertyExist ( xmpNS, xmpProp ) ) return false;
1163 		}
1164 
1165 		found = tiff.GetTag ( tiffIFD, tiffID, tagInfo );
1166 
1167 	} catch ( ... ) {
1168 		found = false;
1169 	}
1170 
1171 	return found;
1172 
1173 }	// ImportTIFF_VerifyImport
1174 
1175 // =================================================================================================
1176 // ImportTIFF_CheckStandardMapping
1177 // ===============================
1178 
1179 static bool
ImportTIFF_CheckStandardMapping(const TIFF_Manager::TagInfo & tagInfo,const TIFF_MappingToXMP & mapInfo)1180 ImportTIFF_CheckStandardMapping ( const TIFF_Manager::TagInfo & tagInfo,
1181 								  const TIFF_MappingToXMP & mapInfo )
1182 {
1183 	XMP_Assert ( (kTIFF_ByteType <= tagInfo.type) && (tagInfo.type <= kTIFF_LastType) );
1184 	XMP_Assert ( mapInfo.type <= kTIFF_LastType );
1185 
1186 	if ( (tagInfo.type < kTIFF_ByteType) || (tagInfo.type > kTIFF_LastType) ) return false;
1187 
1188 	if ( tagInfo.type != mapInfo.type ) {
1189 		if ( mapInfo.type != kTIFF_ShortOrLongType ) return false;
1190 		if ( (tagInfo.type != kTIFF_ShortType) && (tagInfo.type != kTIFF_LongType) ) return false;
1191 	}
1192 
1193 	if ( (tagInfo.count != mapInfo.count) && (mapInfo.count != kAnyCount) ) return false;
1194 
1195 	return true;
1196 
1197 }	// ImportTIFF_CheckStandardMapping
1198 
1199 // =================================================================================================
1200 // ImportTIFF_StandardMappings
1201 // ===========================
1202 
1203 static void
ImportTIFF_StandardMappings(XMP_Uns8 ifd,const TIFF_Manager & tiff,SXMPMeta * xmp,int digestState)1204 ImportTIFF_StandardMappings ( XMP_Uns8 ifd, const TIFF_Manager & tiff, SXMPMeta * xmp, int digestState )
1205 {
1206 	const bool nativeEndian = tiff.IsNativeEndian();
1207 	TIFF_Manager::TagInfo tagInfo;
1208 
1209 	const TIFF_MappingToXMP * mappings = 0;
1210 	const char * xmpNS = 0;
1211 
1212 	if ( ifd == kTIFF_PrimaryIFD ) {
1213 		mappings = sPrimaryIFDMappings;
1214 		xmpNS    = kXMP_NS_TIFF;
1215 	} else if ( ifd == kTIFF_ExifIFD ) {
1216 		mappings = sExifIFDMappings;
1217 		xmpNS    = kXMP_NS_EXIF;
1218 	} else if ( ifd == kTIFF_GPSInfoIFD ) {
1219 		mappings = sGPSInfoIFDMappings;
1220 		xmpNS    = kXMP_NS_EXIF;	// ! Yes, the GPS Info tags go into the exif: namespace.
1221 	} else {
1222 		XMP_Throw ( "Invalid IFD for standard mappings", kXMPErr_InternalFailure );
1223 	}
1224 
1225 	for ( size_t i = 0; mappings[i].id != 0xFFFF; ++i ) {
1226 
1227 		try {	// Don't let errors with one stop the others.
1228 
1229 			const TIFF_MappingToXMP & mapInfo =  mappings[i];
1230 			const bool mapSingle = ((mapInfo.count == 1) || (mapInfo.type == kTIFF_ASCIIType));
1231 
1232 			// Skip tags that have special mappings, they are handled individually later. Delete any
1233 			// existing XMP property before going further. But after the special mapping check since we
1234 			// don't have the XMP property name for those. This lets legacy deletions propagate and
1235 			// eliminates any problems with existing XMP property form. Make sure the actual tag has
1236 			// the expected type and count, ignore it (pretend it is not present) if not.
1237 
1238 			if ( mapInfo.name[0] == 0 ) continue;	// Skip special mappings.
1239 
1240 			bool ok = ImportTIFF_VerifyImport ( tiff, xmp, digestState, ifd, mapInfo.id, xmpNS, mapInfo.name, &tagInfo );
1241 			if (! ok ) continue;
1242 
1243 			XMP_Assert ( tagInfo.type != kTIFF_UndefinedType );	// These have a special mapping.
1244 			if ( tagInfo.type == kTIFF_UndefinedType ) continue;
1245 			if ( ! ImportTIFF_CheckStandardMapping ( tagInfo, mapInfo ) ) continue;
1246 
1247 			if ( mapSingle ) {
1248 				ImportSingleTIFF ( tagInfo, nativeEndian, xmp, xmpNS, mapInfo.name );
1249 			} else {
1250 				ImportArrayTIFF ( tagInfo, nativeEndian, xmp, xmpNS, mapInfo.name );
1251 			}
1252 
1253 		} catch ( ... ) {
1254 
1255 			// Do nothing, let other imports proceed.
1256 			// ? Notify client?
1257 
1258 		}
1259 
1260 	}
1261 
1262 }	// ImportTIFF_StandardMappings
1263 
1264 // =================================================================================================
1265 // =================================================================================================
1266 
1267 // =================================================================================================
1268 // ImportTIFF_Date
1269 // ===============
1270 //
1271 // Convert an Exif 2.2 master date/time tag plus associated fractional seconds to an XMP date/time.
1272 // The Exif date/time part is a 20 byte ASCII value formatted as "YYYY:MM:DD HH:MM:SS" with a
1273 // terminating nul. Any of the numeric portions can be blanks if unknown. The fractional seconds
1274 // are a nul terminated ASCII string with possible space padding. They are literally the fractional
1275 // part, the digits that would be to the right of the decimal point.
1276 
1277 static void
ImportTIFF_Date(const TIFF_Manager & tiff,const TIFF_Manager::TagInfo & dateInfo,XMP_Uns16 secID,SXMPMeta * xmp,const char * xmpNS,const char * xmpProp)1278 ImportTIFF_Date ( const TIFF_Manager & tiff, const TIFF_Manager::TagInfo & dateInfo, XMP_Uns16 secID,
1279 				  SXMPMeta * xmp, const char * xmpNS, const char * xmpProp )
1280 {
1281 	try {	// Don't let errors with one stop the others.
1282 
1283 		const char * dateStr = (const char *) dateInfo.dataPtr;
1284 		if ( (dateStr[4] != ':')  || (dateStr[7] != ':')  ||
1285 			 (dateStr[10] != ' ') || (dateStr[13] != ':') || (dateStr[16] != ':') ) return;
1286 
1287 		XMP_DateTime binValue;
1288 
1289 		binValue.year   = GatherInt ( &dateStr[0], 4 );
1290 		binValue.month  = GatherInt ( &dateStr[5], 2 );
1291 		binValue.day    = GatherInt ( &dateStr[8], 2 );
1292 		binValue.hour   = GatherInt ( &dateStr[11], 2 );
1293 		binValue.minute = GatherInt ( &dateStr[14], 2 );
1294 		binValue.second = GatherInt ( &dateStr[17], 2 );
1295 		binValue.nanoSecond = 0;	// Get the fractional seconds later.
1296 		binValue.tzSign = binValue.tzHour = binValue.tzMinute = 0;
1297 		SXMPUtils::SetTimeZone ( &binValue );	// Assume local time.
1298 
1299 		TIFF_Manager::TagInfo secInfo;
1300 		bool found = tiff.GetTag ( kTIFF_ExifIFD, secID, &secInfo );
1301 
1302 		if ( found && (secInfo.type == kTIFF_ASCIIType) ) {
1303 			const char * fracPtr = (const char *) secInfo.dataPtr;
1304 			binValue.nanoSecond = GatherInt ( fracPtr, secInfo.dataLen );
1305 			size_t digits = 0;
1306 			for ( ; (('0' <= *fracPtr) && (*fracPtr <= '9')); ++fracPtr ) ++digits;
1307 			for ( ; digits < 9; ++digits ) binValue.nanoSecond *= 10;
1308 		}
1309 
1310 		xmp->SetProperty_Date ( xmpNS, xmpProp, binValue );
1311 
1312 	} catch ( ... ) {
1313 		// Do nothing, let other imports proceed.
1314 		// ? Notify client?
1315 	}
1316 
1317 }	// ImportTIFF_Date
1318 
1319 // =================================================================================================
1320 // ImportTIFF_LocTextASCII
1321 // =======================
1322 
1323 static void
ImportTIFF_LocTextASCII(const TIFF_Manager & tiff,XMP_Uns8 ifd,XMP_Uns16 tagID,SXMPMeta * xmp,const char * xmpNS,const char * xmpProp)1324 ImportTIFF_LocTextASCII ( const TIFF_Manager & tiff, XMP_Uns8 ifd, XMP_Uns16 tagID,
1325 						  SXMPMeta * xmp, const char * xmpNS, const char * xmpProp )
1326 {
1327 	try {	// Don't let errors with one stop the others.
1328 
1329 		TIFF_Manager::TagInfo tagInfo;
1330 
1331 		bool found = tiff.GetTag ( ifd, tagID, &tagInfo );
1332 		if ( (! found) || (tagInfo.type != kTIFF_ASCIIType) ) return;
1333 
1334 		const char * chPtr  = (const char *)tagInfo.dataPtr;
1335 		const bool   hasNul = (chPtr[tagInfo.dataLen-1] == 0);
1336 		const bool   isUTF8 = ReconcileUtils::IsUTF8 ( chPtr, tagInfo.dataLen );
1337 
1338 		if ( isUTF8 && hasNul ) {
1339 			xmp->SetLocalizedText ( xmpNS, xmpProp, "", "x-default", chPtr );
1340 		} else {
1341 			std::string strValue;
1342 			if ( isUTF8 ) {
1343 				strValue.assign ( chPtr, tagInfo.dataLen );
1344 			} else {
1345 				#if ! XMP_UNIXBuild
1346 					ReconcileUtils::LocalToUTF8 ( chPtr, tagInfo.dataLen, &strValue );
1347 				#else
1348 					return;	// ! Hack until legacy-as-local issues are resolved for generic UNIX.
1349 				#endif
1350 			}
1351 			xmp->SetLocalizedText ( xmpNS, xmpProp, "", "x-default", strValue.c_str() );
1352 		}
1353 
1354 	} catch ( ... ) {
1355 		// Do nothing, let other imports proceed.
1356 		// ? Notify client?
1357 	}
1358 
1359 }	// ImportTIFF_LocTextASCII
1360 
1361 // =================================================================================================
1362 // ImportTIFF_EncodedString
1363 // ========================
1364 
1365 static void
ImportTIFF_EncodedString(const TIFF_Manager & tiff,const TIFF_Manager::TagInfo & tagInfo,SXMPMeta * xmp,const char * xmpNS,const char * xmpProp,bool isLangAlt=false)1366 ImportTIFF_EncodedString ( const TIFF_Manager & tiff, const TIFF_Manager::TagInfo & tagInfo,
1367 						   SXMPMeta * xmp, const char * xmpNS, const char * xmpProp, bool isLangAlt = false )
1368 {
1369 	try {	// Don't let errors with one stop the others.
1370 
1371 		std::string strValue;
1372 
1373 		bool ok = tiff.DecodeString ( tagInfo.dataPtr, tagInfo.dataLen, &strValue );
1374 		if ( ! ok ) return;
1375 
1376 		if ( ! isLangAlt ) {
1377 			xmp->SetProperty ( xmpNS, xmpProp, strValue.c_str() );
1378 		} else {
1379 			xmp->SetLocalizedText ( xmpNS, xmpProp, "", "x-default", strValue.c_str() );
1380 		}
1381 
1382 	} catch ( ... ) {
1383 		// Do nothing, let other imports proceed.
1384 		// ? Notify client?
1385 	}
1386 
1387 }	// ImportTIFF_EncodedString
1388 
1389 // =================================================================================================
1390 // ImportTIFF_Flash
1391 // ================
1392 
1393 static void
ImportTIFF_Flash(const TIFF_Manager::TagInfo & tagInfo,bool nativeEndian,SXMPMeta * xmp,const char * xmpNS,const char * xmpProp)1394 ImportTIFF_Flash ( const TIFF_Manager::TagInfo & tagInfo, bool nativeEndian,
1395 				   SXMPMeta * xmp, const char * xmpNS, const char * xmpProp )
1396 {
1397 	try {	// Don't let errors with one stop the others.
1398 
1399 		XMP_Uns16 binValue = *((XMP_Uns16*)tagInfo.dataPtr);
1400 		if ( ! nativeEndian ) binValue = Flip2 ( binValue );
1401 
1402 		bool fired    = (bool)(binValue & 1);	// Avoid implicit 0/1 conversion.
1403 		int  rtrn     = (binValue >> 1) & 3;
1404 		int  mode     = (binValue >> 3) & 3;
1405 		bool function = (bool)((binValue >> 5) & 1);
1406 		bool redEye   = (bool)((binValue >> 6) & 1);
1407 
1408 		static const char * sTwoBits[] = { "0", "1", "2", "3" };
1409 
1410 		xmp->SetStructField ( kXMP_NS_EXIF, "Flash", kXMP_NS_EXIF, "Fired", (fired ? kXMP_TrueStr : kXMP_FalseStr) );
1411 		xmp->SetStructField ( kXMP_NS_EXIF, "Flash", kXMP_NS_EXIF, "Return", sTwoBits[rtrn] );
1412 		xmp->SetStructField ( kXMP_NS_EXIF, "Flash", kXMP_NS_EXIF, "Mode", sTwoBits[mode] );
1413 		xmp->SetStructField ( kXMP_NS_EXIF, "Flash", kXMP_NS_EXIF, "Function", (function ? kXMP_TrueStr : kXMP_FalseStr) );
1414 		xmp->SetStructField ( kXMP_NS_EXIF, "Flash", kXMP_NS_EXIF, "RedEyeMode", (redEye ? kXMP_TrueStr : kXMP_FalseStr) );
1415 
1416 	} catch ( ... ) {
1417 		// Do nothing, let other imports proceed.
1418 		// ? Notify client?
1419 	}
1420 
1421 }	// ImportTIFF_Flash
1422 
1423 // =================================================================================================
1424 // ImportTIFF_OECFTable
1425 // ====================
1426 //
1427 // Although the XMP for the OECF and SFR tables is the same, the Exif is not. The OECF table has
1428 // signed rational values and the SFR table has unsigned.
1429 
1430 static void
ImportTIFF_OECFTable(const TIFF_Manager::TagInfo & tagInfo,bool nativeEndian,SXMPMeta * xmp,const char * xmpNS,const char * xmpProp)1431 ImportTIFF_OECFTable ( const TIFF_Manager::TagInfo & tagInfo, bool nativeEndian,
1432 					   SXMPMeta * xmp, const char * xmpNS, const char * xmpProp )
1433 {
1434 	try {	// Don't let errors with one stop the others.
1435 
1436 		const XMP_Uns8 * bytePtr = (XMP_Uns8*)tagInfo.dataPtr;
1437 		const XMP_Uns8 * byteEnd = bytePtr + tagInfo.dataLen;
1438 
1439 		XMP_Uns16 columns = *((XMP_Uns16*)bytePtr);
1440 		XMP_Uns16 rows    = *((XMP_Uns16*)(bytePtr+2));
1441 		if ( ! nativeEndian ) {
1442 			columns = Flip2 ( columns );
1443 			rows = Flip2 ( rows );
1444 		}
1445 
1446 		char buffer[40];
1447 
1448 		snprintf ( buffer, sizeof(buffer), "%d", columns );	// AUDIT: Use of sizeof(buffer) is safe.
1449 		xmp->SetStructField ( xmpNS, xmpProp, kXMP_NS_EXIF, "Columns", buffer );
1450 		snprintf ( buffer, sizeof(buffer), "%d", rows );	// AUDIT: Use of sizeof(buffer) is safe.
1451 		xmp->SetStructField ( xmpNS, xmpProp, kXMP_NS_EXIF, "Rows", buffer );
1452 
1453 		std::string arrayPath;
1454 
1455 		SXMPUtils::ComposeStructFieldPath ( xmpNS, xmpProp, kXMP_NS_EXIF, "Names", &arrayPath );
1456 
1457 		bytePtr += 4;	// Move to the list of names.
1458 		for ( size_t i = columns; i > 0; --i ) {
1459 			size_t nameLen = strlen((XMP_StringPtr)bytePtr) + 1;	// ! Include the terminating nul.
1460 			if ( (bytePtr + nameLen) > byteEnd ) { xmp->DeleteProperty ( xmpNS, xmpProp ); return; };
1461 			xmp->AppendArrayItem ( xmpNS, arrayPath.c_str(), kXMP_PropArrayIsOrdered, (XMP_StringPtr)bytePtr );
1462 			bytePtr += nameLen;
1463 		}
1464 
1465 		if ( (byteEnd - bytePtr) != (8 * columns * rows) ) { xmp->DeleteProperty ( xmpNS, xmpProp ); return; };	// Make sure the values are present.
1466 		SXMPUtils::ComposeStructFieldPath ( xmpNS, xmpProp, kXMP_NS_EXIF, "Values", &arrayPath );
1467 
1468 		XMP_Int32 * binPtr = (XMP_Int32*)bytePtr;
1469 		for ( size_t i = (columns * rows); i > 0; --i, binPtr += 2 ) {
1470 
1471 			XMP_Int32 binNum   = binPtr[0];
1472 			XMP_Int32 binDenom = binPtr[1];
1473 			if ( ! nativeEndian ) {
1474 				Flip4 ( &binNum );
1475 				Flip4 ( &binDenom );
1476 			}
1477 
1478 			snprintf ( buffer, sizeof(buffer), "%"PRId32"/%"PRId32, binNum, binDenom );	// AUDIT: Use of sizeof(buffer) is safe.
1479 
1480 			xmp->AppendArrayItem ( xmpNS, arrayPath.c_str(), kXMP_PropArrayIsOrdered, buffer );
1481 
1482 		}
1483 
1484 		return;
1485 
1486 	} catch ( ... ) {
1487 		// Do nothing, let other imports proceed.
1488 		// ? Notify client?
1489 	}
1490 
1491 }	// ImportTIFF_OECFTable
1492 
1493 // =================================================================================================
1494 // ImportTIFF_SFRTable
1495 // ===================
1496 //
1497 // Although the XMP for the OECF and SFR tables is the same, the Exif is not. The OECF table has
1498 // signed rational values and the SFR table has unsigned.
1499 
1500 static void
ImportTIFF_SFRTable(const TIFF_Manager::TagInfo & tagInfo,bool nativeEndian,SXMPMeta * xmp,const char * xmpNS,const char * xmpProp)1501 ImportTIFF_SFRTable ( const TIFF_Manager::TagInfo & tagInfo, bool nativeEndian,
1502 					  SXMPMeta * xmp, const char * xmpNS, const char * xmpProp )
1503 {
1504 	try {	// Don't let errors with one stop the others.
1505 
1506 		const XMP_Uns8 * bytePtr = (XMP_Uns8*)tagInfo.dataPtr;
1507 		const XMP_Uns8 * byteEnd = bytePtr + tagInfo.dataLen;
1508 
1509 		XMP_Uns16 columns = *((XMP_Uns16*)bytePtr);
1510 		XMP_Uns16 rows    = *((XMP_Uns16*)(bytePtr+2));
1511 		if ( ! nativeEndian ) {
1512 			columns = Flip2 ( columns );
1513 			rows = Flip2 ( rows );
1514 		}
1515 
1516 		char buffer[40];
1517 
1518 		snprintf ( buffer, sizeof(buffer), "%d", columns );	// AUDIT: Use of sizeof(buffer) is safe.
1519 		xmp->SetStructField ( xmpNS, xmpProp, kXMP_NS_EXIF, "Columns", buffer );
1520 		snprintf ( buffer, sizeof(buffer), "%d", rows );	// AUDIT: Use of sizeof(buffer) is safe.
1521 		xmp->SetStructField ( xmpNS, xmpProp, kXMP_NS_EXIF, "Rows", buffer );
1522 
1523 		std::string arrayPath;
1524 
1525 		SXMPUtils::ComposeStructFieldPath ( xmpNS, xmpProp, kXMP_NS_EXIF, "Names", &arrayPath );
1526 
1527 		bytePtr += 4;	// Move to the list of names.
1528 		for ( size_t i = columns; i > 0; --i ) {
1529 			size_t nameLen = strlen((XMP_StringPtr)bytePtr) + 1;	// ! Include the terminating nul.
1530 			if ( (bytePtr + nameLen) > byteEnd ) { xmp->DeleteProperty ( xmpNS, xmpProp ); return; };
1531 			xmp->AppendArrayItem ( xmpNS, arrayPath.c_str(), kXMP_PropArrayIsOrdered, (XMP_StringPtr)bytePtr );
1532 			bytePtr += nameLen;
1533 		}
1534 
1535 		if ( (byteEnd - bytePtr) != (8 * columns * rows) ) { xmp->DeleteProperty ( xmpNS, xmpProp ); return; };	// Make sure the values are present.
1536 		SXMPUtils::ComposeStructFieldPath ( xmpNS, xmpProp, kXMP_NS_EXIF, "Values", &arrayPath );
1537 
1538 		XMP_Uns32 * binPtr = (XMP_Uns32*)bytePtr;
1539 		for ( size_t i = (columns * rows); i > 0; --i, binPtr += 2 ) {
1540 
1541 			XMP_Uns32 binNum   = binPtr[0];
1542 			XMP_Uns32 binDenom = binPtr[1];
1543 			if ( ! nativeEndian ) {
1544 				binNum   = Flip4 ( binNum );
1545 				binDenom = Flip4 ( binDenom );
1546 			}
1547 
1548 			snprintf ( buffer, sizeof(buffer), "%"PRIu32"/%"PRIu32, binNum, binDenom );	// AUDIT: Use of sizeof(buffer) is safe.
1549 
1550 			xmp->AppendArrayItem ( xmpNS, arrayPath.c_str(), kXMP_PropArrayIsOrdered, buffer );
1551 
1552 		}
1553 
1554 		return;
1555 
1556 	} catch ( ... ) {
1557 		// Do nothing, let other imports proceed.
1558 		// ? Notify client?
1559 	}
1560 
1561 }	// ImportTIFF_SFRTable
1562 
1563 // =================================================================================================
1564 // ImportTIFF_CFATable
1565 // ===================
1566 
1567 static void
ImportTIFF_CFATable(const TIFF_Manager::TagInfo & tagInfo,bool nativeEndian,SXMPMeta * xmp,const char * xmpNS,const char * xmpProp)1568 ImportTIFF_CFATable ( const TIFF_Manager::TagInfo & tagInfo, bool nativeEndian,
1569 					  SXMPMeta * xmp, const char * xmpNS, const char * xmpProp )
1570 {
1571 	try {	// Don't let errors with one stop the others.
1572 
1573 		const XMP_Uns8 * bytePtr = (XMP_Uns8*)tagInfo.dataPtr;
1574 		const XMP_Uns8 * byteEnd = bytePtr + tagInfo.dataLen;
1575 
1576 		XMP_Uns16 columns = *((XMP_Uns16*)bytePtr);
1577 		XMP_Uns16 rows    = *((XMP_Uns16*)(bytePtr+2));
1578 		if ( ! nativeEndian ) {
1579 			columns = Flip2 ( columns );
1580 			rows = Flip2 ( rows );
1581 		}
1582 
1583 		char buffer[20];
1584 		std::string arrayPath;
1585 
1586 		snprintf ( buffer, sizeof(buffer), "%d", columns );	// AUDIT: Use of sizeof(buffer) is safe.
1587 		xmp->SetStructField ( xmpNS, xmpProp, kXMP_NS_EXIF, "Columns", buffer );
1588 		snprintf ( buffer, sizeof(buffer), "%d", rows );	// AUDIT: Use of sizeof(buffer) is safe.
1589 		xmp->SetStructField ( xmpNS, xmpProp, kXMP_NS_EXIF, "Rows", buffer );
1590 
1591 		bytePtr += 4;	// Move to the matrix of values.
1592 		if ( (byteEnd - bytePtr) != (columns * rows) ) goto BadExif;	// Make sure the values are present.
1593 
1594 		SXMPUtils::ComposeStructFieldPath ( xmpNS, xmpProp, kXMP_NS_EXIF, "Values", &arrayPath );
1595 
1596 		for ( size_t i = (columns * rows); i > 0; --i, ++bytePtr ) {
1597 			snprintf ( buffer, sizeof(buffer), "%hhu", *bytePtr );	// AUDIT: Use of sizeof(buffer) is safe.
1598 			xmp->AppendArrayItem ( xmpNS, arrayPath.c_str(), kXMP_PropArrayIsOrdered, buffer );
1599 		}
1600 
1601 		return;
1602 
1603 	BadExif:	// Ignore the tag if the table is ill-formed.
1604 		xmp->DeleteProperty ( xmpNS, xmpProp );
1605 		return;
1606 
1607 	} catch ( ... ) {
1608 		// Do nothing, let other imports proceed.
1609 		// ? Notify client?
1610 	}
1611 
1612 }	// ImportTIFF_CFATable
1613 
1614 // =================================================================================================
1615 // ImportTIFF_DSDTable
1616 // ===================
1617 
1618 static void
ImportTIFF_DSDTable(const TIFF_Manager & tiff,const TIFF_Manager::TagInfo & tagInfo,SXMPMeta * xmp,const char * xmpNS,const char * xmpProp)1619 ImportTIFF_DSDTable ( const TIFF_Manager & tiff, const TIFF_Manager::TagInfo & tagInfo,
1620 					  SXMPMeta * xmp, const char * xmpNS, const char * xmpProp )
1621 {
1622 	try {	// Don't let errors with one stop the others.
1623 
1624 		const XMP_Uns8 * bytePtr = (XMP_Uns8*)tagInfo.dataPtr;
1625 		const XMP_Uns8 * byteEnd = bytePtr + tagInfo.dataLen;
1626 
1627 		XMP_Uns16 columns = *((XMP_Uns16*)bytePtr);
1628 		XMP_Uns16 rows    = *((XMP_Uns16*)(bytePtr+2));
1629 		if ( ! tiff.IsNativeEndian() ) {
1630 			columns = Flip2 ( columns );
1631 			rows = Flip2 ( rows );
1632 		}
1633 
1634 		char buffer[20];
1635 
1636 		snprintf ( buffer, sizeof(buffer), "%d", columns );	// AUDIT: Use of sizeof(buffer) is safe.
1637 		xmp->SetStructField ( xmpNS, xmpProp, kXMP_NS_EXIF, "Columns", buffer );
1638 		snprintf ( buffer, sizeof(buffer), "%d", rows );	// AUDIT: Use of sizeof(buffer) is safe.
1639 		xmp->SetStructField ( xmpNS, xmpProp, kXMP_NS_EXIF, "Rows", buffer );
1640 
1641 		std::string arrayPath;
1642 		SXMPUtils::ComposeStructFieldPath ( xmpNS, xmpProp, kXMP_NS_EXIF, "Settings", &arrayPath );
1643 
1644 		bytePtr += 4;	// Move to the list of settings.
1645 		UTF16Unit * utf16Ptr = (UTF16Unit*)bytePtr;
1646 		UTF16Unit * utf16End = (UTF16Unit*)byteEnd;
1647 
1648 		std::string utf8;
1649 
1650 		// Figure 17 in the Exif 2.2 spec is unclear. It has counts for rows and columns, but the
1651 		// settings are listed as 1..n, not as a rectangular matrix. So, ignore the counts and copy
1652 		// strings until the end of the Exif value.
1653 
1654 		while ( utf16Ptr < utf16End ) {
1655 
1656 			size_t nameLen = 0;
1657 			while ( utf16Ptr[nameLen] != 0 ) ++nameLen;
1658 			++nameLen;	// ! Include the terminating nul.
1659 			if ( (utf16Ptr + nameLen) > utf16End ) goto BadExif;
1660 
1661 			try {
1662 				FromUTF16 ( utf16Ptr, nameLen, &utf8, tiff.IsBigEndian() );
1663 			} catch ( ... ) {
1664 				goto BadExif; // Ignore the tag if there are conversion errors.
1665 			}
1666 
1667 			xmp->AppendArrayItem ( xmpNS, arrayPath.c_str(), kXMP_PropArrayIsOrdered, utf8.c_str() );
1668 
1669 			utf16Ptr += nameLen;
1670 
1671 		}
1672 
1673 		return;
1674 
1675 	BadExif:	// Ignore the tag if the table is ill-formed.
1676 		xmp->DeleteProperty ( xmpNS, xmpProp );
1677 		return;
1678 
1679 	} catch ( ... ) {
1680 		// Do nothing, let other imports proceed.
1681 		// ? Notify client?
1682 	}
1683 
1684 }	// ImportTIFF_DSDTable
1685 
1686 // =================================================================================================
1687 // ImportTIFF_GPSCoordinate
1688 // ========================
1689 
1690 static void
ImportTIFF_GPSCoordinate(const TIFF_Manager & tiff,const TIFF_Manager::TagInfo & posInfo,SXMPMeta * xmp,const char * xmpNS,const char * xmpProp)1691 ImportTIFF_GPSCoordinate ( const TIFF_Manager & tiff, const TIFF_Manager::TagInfo & posInfo,
1692 						   SXMPMeta * xmp, const char * xmpNS, const char * xmpProp )
1693 {
1694 	try {	// Don't let errors with one stop the others.
1695 
1696 		const bool nativeEndian = tiff.IsNativeEndian();
1697 
1698 		XMP_Uns16 refID = posInfo.id - 1;	// ! The GPS refs and coordinates are all tag n and n+1.
1699 		TIFF_Manager::TagInfo refInfo;
1700 		bool found = tiff.GetTag ( kTIFF_GPSInfoIFD, refID, &refInfo );
1701 		if ( (! found) || (refInfo.type != kTIFF_ASCIIType) || (refInfo.count != 2) ) return;
1702 		char ref = *((char*)refInfo.dataPtr);
1703 
1704 		XMP_Uns32 * binPtr = (XMP_Uns32*)posInfo.dataPtr;
1705 		XMP_Uns32 degNum   = binPtr[0];
1706 		XMP_Uns32 degDenom = binPtr[1];
1707 		XMP_Uns32 minNum   = binPtr[2];
1708 		XMP_Uns32 minDenom = binPtr[3];
1709 		XMP_Uns32 secNum   = binPtr[4];
1710 		XMP_Uns32 secDenom = binPtr[5];
1711 		if ( ! nativeEndian ) {
1712 			degNum = Flip4 ( degNum );
1713 			degDenom = Flip4 ( degDenom );
1714 			minNum = Flip4 ( minNum );
1715 			minDenom = Flip4 ( minDenom );
1716 			secNum = Flip4 ( secNum );
1717 			secDenom = Flip4 ( secDenom );
1718 		}
1719 
1720 		char buffer[40];
1721 
1722 		if ( (degDenom == 1) && (minDenom == 1) && (secDenom == 1) ) {
1723 
1724 			snprintf ( buffer, sizeof(buffer), "%"PRIu32",%"PRIu32",%"PRIu32"%c", degNum, minNum, secNum, ref );	// AUDIT: Using sizeof(buffer is safe.
1725 
1726 		} else {
1727 
1728 			XMP_Uns32 maxDenom = degDenom;
1729 			if ( minDenom > degDenom ) maxDenom = minDenom;
1730 			if ( secDenom > degDenom ) maxDenom = secDenom;
1731 
1732 			int fracDigits = 1;
1733 			while ( maxDenom > 10 ) { ++fracDigits; maxDenom = maxDenom/10; }
1734 
1735 			double temp    = (double)degNum / (double)degDenom;
1736 			double degrees = (double)((XMP_Uns32)temp);	// Just the integral number of degrees.
1737 			double minutes = ((temp - degrees) * 60.0) +
1738 							 ((double)minNum / (double)minDenom) +
1739 							 (((double)secNum / (double)secDenom) / 60.0);
1740 
1741 			snprintf ( buffer, sizeof(buffer), "%.0f,%.*f%c", degrees, fracDigits, minutes, ref );	// AUDIT: Using sizeof(buffer is safe.
1742 
1743 		}
1744 
1745 		xmp->SetProperty ( xmpNS, xmpProp, buffer );
1746 
1747 	} catch ( ... ) {
1748 		// Do nothing, let other imports proceed.
1749 		// ? Notify client?
1750 	}
1751 
1752 }	// ImportTIFF_GPSCoordinate
1753 
1754 // =================================================================================================
1755 // ImportTIFF_GPSTimeStamp
1756 // =======================
1757 
1758 static void
ImportTIFF_GPSTimeStamp(const TIFF_Manager & tiff,const TIFF_Manager::TagInfo & timeInfo,SXMPMeta * xmp,const char * xmpNS,const char * xmpProp)1759 ImportTIFF_GPSTimeStamp ( const TIFF_Manager & tiff, const TIFF_Manager::TagInfo & timeInfo,
1760 						  SXMPMeta * xmp, const char * xmpNS, const char * xmpProp )
1761 {
1762 	try {	// Don't let errors with one stop the others.
1763 
1764 		const bool nativeEndian = tiff.IsNativeEndian();
1765 
1766 		bool haveDate;
1767 		TIFF_Manager::TagInfo dateInfo;
1768 		haveDate = tiff.GetTag ( kTIFF_GPSInfoIFD, kTIFF_GPSDateStamp, &dateInfo );
1769 		if ( ! haveDate ) haveDate = tiff.GetTag ( kTIFF_ExifIFD, kTIFF_DateTimeOriginal, &dateInfo );
1770 		if ( ! haveDate ) haveDate = tiff.GetTag ( kTIFF_ExifIFD, kTIFF_DateTimeDigitized, &dateInfo );
1771 		if ( ! haveDate ) return;
1772 
1773 		const char * dateStr = (const char *) dateInfo.dataPtr;
1774 		if ( (dateStr[4] != ':') || (dateStr[7] != ':') ) return;
1775 		if ( (dateStr[10] != 0)  && (dateStr[10] != ' ') ) return;
1776 
1777 		XMP_Uns32 * binPtr = (XMP_Uns32*)timeInfo.dataPtr;
1778 		XMP_Uns32 hourNum   = binPtr[0];
1779 		XMP_Uns32 hourDenom = binPtr[1];
1780 		XMP_Uns32 minNum    = binPtr[2];
1781 		XMP_Uns32 minDenom  = binPtr[3];
1782 		XMP_Uns32 secNum    = binPtr[4];
1783 		XMP_Uns32 secDenom  = binPtr[5];
1784 		if ( ! nativeEndian ) {
1785 			hourNum = Flip4 ( hourNum );
1786 			hourDenom = Flip4 ( hourDenom );
1787 			minNum = Flip4 ( minNum );
1788 			minDenom = Flip4 ( minDenom );
1789 			secNum = Flip4 ( secNum );
1790 			secDenom = Flip4 ( secDenom );
1791 		}
1792 
1793 		double fHour, fMin, fSec, fNano, temp;
1794 		fSec  =  (double)secNum / (double)secDenom;
1795 		temp  =  (double)minNum / (double)minDenom;
1796 		fMin  =  (double)((XMP_Uns32)temp);
1797 		fSec  += (temp - fMin) * 60.0;
1798 		temp  =  (double)hourNum / (double)hourDenom;
1799 		fHour =  (double)((XMP_Uns32)temp);
1800 		fSec  += (temp - fHour) * 3600.0;
1801 		temp  =  (double)((XMP_Uns32)fSec);
1802 		fNano =  (fSec - temp) * (1000.0*1000.0*1000.0);
1803 		fSec  =  temp;
1804 
1805 		XMP_DateTime binStamp;
1806 		binStamp.tzSign = kXMP_TimeIsUTC;
1807 		binStamp.tzHour = binStamp.tzMinute = 0;
1808 		binStamp.year   = GatherInt ( dateStr, 4 );
1809 		binStamp.month  = GatherInt ( dateStr+5, 2 );
1810 		binStamp.day    = GatherInt ( dateStr+8, 2 );
1811 		binStamp.hour   = (XMP_Int32)fHour;
1812 		binStamp.minute = (XMP_Int32)fMin;
1813 		binStamp.second = (XMP_Int32)fSec;
1814 		binStamp.nanoSecond = (XMP_Int32)fNano;
1815 
1816 		xmp->SetProperty_Date ( xmpNS, xmpProp, binStamp );
1817 
1818 	} catch ( ... ) {
1819 		// Do nothing, let other imports proceed.
1820 		// ? Notify client?
1821 	}
1822 
1823 }	// ImportTIFF_GPSTimeStamp
1824 
1825 // =================================================================================================
1826 // =================================================================================================
1827 
1828 // =================================================================================================
1829 // ReconcileUtils::ImportTIFF
1830 // ==========================
1831 
1832 void
ImportTIFF(const TIFF_Manager & tiff,SXMPMeta * xmp,int digestState,XMP_FileFormat srcFormat)1833 ReconcileUtils::ImportTIFF ( const TIFF_Manager & tiff, SXMPMeta * xmp, int digestState, XMP_FileFormat srcFormat )
1834 {
1835 	TIFF_Manager::TagInfo tagInfo;
1836 	bool ok;
1837 
1838 	ImportTIFF_StandardMappings ( kTIFF_PrimaryIFD, tiff, xmp, digestState );
1839 
1840 	// 306 DateTime is a date master with 37520 SubSecTime and is mapped to xmp:ModifyDate.
1841 	ok = ImportTIFF_VerifyImport ( tiff, xmp, digestState, kTIFF_PrimaryIFD, kTIFF_DateTime,
1842 								   kXMP_NS_XMP, "ModifyDate", &tagInfo );
1843 	if ( ok && (tagInfo.type == kTIFF_ASCIIType) && (tagInfo.count == 20) ) {
1844 		ImportTIFF_Date ( tiff, tagInfo, kTIFF_SubSecTime, xmp, kXMP_NS_XMP, "ModifyDate" );
1845 	}
1846 
1847 	if ( srcFormat != kXMP_PhotoshopFile ) {
1848 
1849 		// ! TIFF tags 270, 315, and 33432 are ignored for Photoshop files.
1850 
1851 		XMP_Assert ( (srcFormat == kXMP_JPEGFile) || (srcFormat == kXMP_TIFFFile) );
1852 
1853 		// 270 ImageDescription is an ASCII tag and is mapped to dc:description["x-default"].
1854 		// Call ImportTIFF_VerifyImport using the x-default item path, don't delete the whole array.
1855 		ok = ImportTIFF_VerifyImport ( tiff, xmp, digestState, kTIFF_PrimaryIFD, kTIFF_ImageDescription,
1856 									   kXMP_NS_DC, "description[?xml:lang='x-default']", &tagInfo );
1857 		if ( ok ) ImportTIFF_LocTextASCII ( tiff, kTIFF_PrimaryIFD, kTIFF_ImageDescription,
1858 											xmp, kXMP_NS_DC, "description" );
1859 
1860 		// 315 Artist is an ASCII tag and is mapped to dc:creator[*].
1861 		ok = ImportTIFF_VerifyImport ( tiff, xmp, digestState, kTIFF_PrimaryIFD, kTIFF_Artist,
1862 									   kXMP_NS_DC, "creator", &tagInfo );
1863 		if ( ok && (tagInfo.type == kTIFF_ASCIIType) ) {
1864 			ImportArrayTIFF_ASCII ( tagInfo, xmp, kXMP_NS_DC, "creator" );
1865 		}
1866 
1867 		// 33432 Copyright is mapped to dc:rights["x-default"].
1868 		// Call ImportTIFF_VerifyImport using the x-default item path, don't delete the whole array.
1869 		ok = ImportTIFF_VerifyImport ( tiff, xmp, digestState, kTIFF_PrimaryIFD, kTIFF_Copyright,
1870 									   kXMP_NS_DC, "rights[?xml:lang='x-default']", &tagInfo );
1871 		if ( ok ) ImportTIFF_LocTextASCII ( tiff, kTIFF_PrimaryIFD, kTIFF_Copyright, xmp, kXMP_NS_DC, "rights" );
1872 
1873 	}
1874 
1875 }	// ReconcileUtils::ImportTIFF;
1876 
1877 // =================================================================================================
1878 // ReconcileUtils::ImportExif
1879 // ==========================
1880 
1881 void
ImportExif(const TIFF_Manager & tiff,SXMPMeta * xmp,int digestState)1882 ReconcileUtils::ImportExif ( const TIFF_Manager & tiff, SXMPMeta * xmp, int digestState )
1883 {
1884 	const bool nativeEndian = tiff.IsNativeEndian();
1885 
1886 	TIFF_Manager::TagInfo tagInfo;
1887 	bool ok;
1888 
1889 	ImportTIFF_StandardMappings ( kTIFF_ExifIFD, tiff, xmp, digestState );
1890 	ImportTIFF_StandardMappings ( kTIFF_GPSInfoIFD, tiff, xmp, digestState );
1891 
1892 	// ------------------------------------------------------
1893 	// Here are the Exif IFD tags that have special mappings:
1894 
1895 	// 36864 ExifVersion is 4 "undefined" ASCII characters.
1896 	ok = ImportTIFF_VerifyImport ( tiff, xmp, digestState, kTIFF_ExifIFD, kTIFF_ExifVersion,
1897 								   kXMP_NS_EXIF, "ExifVersion", &tagInfo );
1898 	if ( ok && (tagInfo.type == kTIFF_UndefinedType) && (tagInfo.count == 4) ) {
1899 		char str[5];
1900 		*((XMP_Uns32*)str) = *((XMP_Uns32*)tagInfo.dataPtr);
1901 		str[4] = 0;
1902 		xmp->SetProperty ( kXMP_NS_EXIF, "ExifVersion", str );
1903 	}
1904 
1905 	// 40960 FlashpixVersion is 4 "undefined" ASCII characters.
1906 	ok = ImportTIFF_VerifyImport ( tiff, xmp, digestState, kTIFF_ExifIFD, kTIFF_FlashpixVersion,
1907 								   kXMP_NS_EXIF, "FlashpixVersion", &tagInfo );
1908 	if ( ok && (tagInfo.type == kTIFF_UndefinedType) && (tagInfo.count == 4) ) {
1909 		char str[5];
1910 		*((XMP_Uns32*)str) = *((XMP_Uns32*)tagInfo.dataPtr);
1911 		str[4] = 0;
1912 		xmp->SetProperty ( kXMP_NS_EXIF, "FlashpixVersion", str );
1913 	}
1914 
1915 	// 37121 ComponentsConfiguration is an array of 4 "undefined" UInt8 bytes.
1916 	ok = ImportTIFF_VerifyImport ( tiff, xmp, digestState, kTIFF_ExifIFD, kTIFF_ComponentsConfiguration,
1917 								   kXMP_NS_EXIF, "ComponentsConfiguration", &tagInfo );
1918 	if ( ok && (tagInfo.type == kTIFF_UndefinedType) && (tagInfo.count == 4) ) {
1919 		ImportArrayTIFF_Byte ( tagInfo, xmp, kXMP_NS_EXIF, "ComponentsConfiguration" );
1920 	}
1921 
1922 	// 37510 UserComment is a string with explicit encoding.
1923 	ok = ImportTIFF_VerifyImport ( tiff, xmp, digestState, kTIFF_ExifIFD, kTIFF_UserComment,
1924 								   kXMP_NS_EXIF, "UserComment", &tagInfo );
1925 	if ( ok ) {
1926 		ImportTIFF_EncodedString ( tiff, tagInfo, xmp, kXMP_NS_EXIF, "UserComment", true /* isLangAlt */ );
1927 	}
1928 
1929 	// 36867 DateTimeOriginal is a date master with 37521 SubSecTimeOriginal.
1930 	ok = ImportTIFF_VerifyImport ( tiff, xmp, digestState, kTIFF_ExifIFD, kTIFF_DateTimeOriginal,
1931 								   kXMP_NS_EXIF, "DateTimeOriginal", &tagInfo );
1932 	if ( ok && (tagInfo.type == kTIFF_ASCIIType) && (tagInfo.count == 20) ) {
1933 		ImportTIFF_Date ( tiff, tagInfo, kTIFF_SubSecTimeOriginal, xmp, kXMP_NS_EXIF, "DateTimeOriginal" );
1934 	}
1935 	if ( ! xmp->DoesPropertyExist ( kXMP_NS_XMP, "CreateDate" ) ) {
1936 		std::string exifDate;
1937 		ok = xmp->GetProperty ( kXMP_NS_EXIF, "DateTimeOriginal", &exifDate, 0 );
1938 		if ( ok ) xmp->SetProperty ( kXMP_NS_XMP, "CreateDate", exifDate.c_str() );
1939 	}
1940 
1941 	// 36868 DateTimeDigitized is a date master with 37522 SubSecTimeDigitized.
1942 	ok = ImportTIFF_VerifyImport ( tiff, xmp, digestState, kTIFF_ExifIFD, kTIFF_DateTimeDigitized,
1943 								   kXMP_NS_EXIF, "DateTimeDigitized", &tagInfo );
1944 	if ( ok && (tagInfo.type == kTIFF_ASCIIType) && (tagInfo.count == 20) ) {
1945 		ImportTIFF_Date ( tiff, tagInfo, kTIFF_SubSecTimeDigitized, xmp, kXMP_NS_EXIF, "DateTimeDigitized" );
1946 	}
1947 
1948 	// 34856 OECF is an OECF/SFR table.
1949 	ok = ImportTIFF_VerifyImport ( tiff, xmp, digestState, kTIFF_ExifIFD, kTIFF_OECF,
1950 								   kXMP_NS_EXIF, "OECF", &tagInfo );
1951 	if ( ok ) {
1952 		ImportTIFF_OECFTable ( tagInfo, nativeEndian, xmp, kXMP_NS_EXIF, "OECF" );
1953 	}
1954 
1955 	// 37385 Flash is a UInt16 collection of bit fields and is mapped to a struct in XMP.
1956 	ok = ImportTIFF_VerifyImport ( tiff, xmp, digestState, kTIFF_ExifIFD, kTIFF_Flash,
1957 								   kXMP_NS_EXIF, "Flash", &tagInfo );
1958 	if ( ok && (tagInfo.type == kTIFF_ShortType) && (tagInfo.count == 1) ) {
1959 		ImportTIFF_Flash ( tagInfo, nativeEndian, xmp, kXMP_NS_EXIF, "Flash" );
1960 	}
1961 
1962 	// 41484 SpatialFrequencyResponse is an OECF/SFR table.
1963 	ok = ImportTIFF_VerifyImport ( tiff, xmp, digestState, kTIFF_ExifIFD, kTIFF_SpatialFrequencyResponse,
1964 								   kXMP_NS_EXIF, "SpatialFrequencyResponse", &tagInfo );
1965 	if ( ok ) {
1966 		ImportTIFF_SFRTable ( tagInfo, nativeEndian, xmp, kXMP_NS_EXIF, "SpatialFrequencyResponse" );
1967 	}
1968 
1969 	// 41728 FileSource is an "undefined" UInt8.
1970 	ok = ImportTIFF_VerifyImport ( tiff, xmp, digestState, kTIFF_ExifIFD, kTIFF_FileSource,
1971 								   kXMP_NS_EXIF, "FileSource", &tagInfo );
1972 	if ( ok && (tagInfo.type == kTIFF_UndefinedType) && (tagInfo.count == 1) ) {
1973 		ImportSingleTIFF_Byte ( tagInfo, xmp, kXMP_NS_EXIF, "FileSource" );
1974 	}
1975 
1976 	// 41729 SceneType is an "undefined" UInt8.
1977 	ok = ImportTIFF_VerifyImport ( tiff, xmp, digestState, kTIFF_ExifIFD, kTIFF_SceneType,
1978 								   kXMP_NS_EXIF, "SceneType", &tagInfo );
1979 	if ( ok && (tagInfo.type == kTIFF_UndefinedType) && (tagInfo.count == 1) ) {
1980 		ImportSingleTIFF_Byte ( tagInfo, xmp, kXMP_NS_EXIF, "SceneType" );
1981 	}
1982 
1983 	// 41730 CFAPattern is a custom table.
1984 	ok = ImportTIFF_VerifyImport ( tiff, xmp, digestState, kTIFF_ExifIFD, kTIFF_CFAPattern,
1985 								   kXMP_NS_EXIF, "CFAPattern", &tagInfo );
1986 	if ( ok ) {
1987 		ImportTIFF_CFATable ( tagInfo, nativeEndian, xmp, kXMP_NS_EXIF, "CFAPattern" );
1988 	}
1989 
1990 	// 41995 DeviceSettingDescription is a custom table.
1991 	ok = ImportTIFF_VerifyImport ( tiff, xmp, digestState, kTIFF_ExifIFD, kTIFF_DeviceSettingDescription,
1992 								   kXMP_NS_EXIF, "DeviceSettingDescription", &tagInfo );
1993 	if ( ok ) {
1994 		ImportTIFF_DSDTable ( tiff, tagInfo, xmp, kXMP_NS_EXIF, "DeviceSettingDescription" );
1995 	}
1996 
1997 	// ----------------------------------------------------------
1998 	// Here are the GPS Info IFD tags that have special mappings:
1999 
2000 	// 0 GPSVersionID is 4 UInt8 bytes and mapped as "n.n.n.n".
2001 	ok = ImportTIFF_VerifyImport ( tiff, xmp, digestState, kTIFF_GPSInfoIFD, kTIFF_GPSVersionID,
2002 								   kXMP_NS_EXIF, "GPSVersionID", &tagInfo );
2003 	if ( ok && (tagInfo.type == kTIFF_ByteType) && (tagInfo.count == 4) ) {
2004 		const char * strIn = (const char *) tagInfo.dataPtr;
2005 		char strOut[8];
2006 		strOut[0] = strIn[0];
2007 		strOut[2] = strIn[1];
2008 		strOut[4] = strIn[2];
2009 		strOut[6] = strIn[3];
2010 		strOut[1] = strOut[3] = strOut[5] = '.';
2011 		strOut[7] = 0;
2012 		xmp->SetProperty ( kXMP_NS_EXIF, "GPSVersionID", strOut );
2013 	}
2014 
2015 	// 2 GPSLatitude is a GPS coordinate master.
2016 	ok = ImportTIFF_VerifyImport ( tiff, xmp, digestState, kTIFF_GPSInfoIFD, kTIFF_GPSLatitude,
2017 								   kXMP_NS_EXIF, "GPSLatitude", &tagInfo );
2018 	if ( ok ) {
2019 		ImportTIFF_GPSCoordinate ( tiff, tagInfo, xmp, kXMP_NS_EXIF, "GPSLatitude" );
2020 	}
2021 
2022 	// 4 GPSLongitude is a GPS coordinate master.
2023 	ok = ImportTIFF_VerifyImport ( tiff, xmp, digestState, kTIFF_GPSInfoIFD, kTIFF_GPSLongitude,
2024 								   kXMP_NS_EXIF, "GPSLongitude", &tagInfo );
2025 	if ( ok ) {
2026 		ImportTIFF_GPSCoordinate ( tiff, tagInfo, xmp, kXMP_NS_EXIF, "GPSLongitude" );
2027 	}
2028 
2029 	// 7 GPSTimeStamp is a UTC time as 3 rationals, mated with the optional GPSDateStamp.
2030 	ok = ImportTIFF_VerifyImport ( tiff, xmp, digestState, kTIFF_GPSInfoIFD, kTIFF_GPSTimeStamp,
2031 								   kXMP_NS_EXIF, "GPSTimeStamp", &tagInfo );
2032 	if ( ok && (tagInfo.type == kTIFF_RationalType) && (tagInfo.count == 3) ) {
2033 		ImportTIFF_GPSTimeStamp ( tiff, tagInfo, xmp, kXMP_NS_EXIF, "GPSTimeStamp" );
2034 	}
2035 
2036 	// 20 GPSDestLatitude is a GPS coordinate master.
2037 	ok = ImportTIFF_VerifyImport ( tiff, xmp, digestState, kTIFF_GPSInfoIFD, kTIFF_GPSDestLatitude,
2038 								   kXMP_NS_EXIF, "GPSDestLatitude", &tagInfo );
2039 	if ( ok ) {
2040 		ImportTIFF_GPSCoordinate ( tiff, tagInfo, xmp, kXMP_NS_EXIF, "GPSDestLatitude" );
2041 	}
2042 
2043 	// 22 GPSDestLongitude is a GPS coordinate master.
2044 	ok = ImportTIFF_VerifyImport ( tiff, xmp, digestState, kTIFF_GPSInfoIFD, kTIFF_GPSDestLongitude,
2045 								   kXMP_NS_EXIF, "GPSDestLongitude", &tagInfo );
2046 	if ( ok ) {
2047 		ImportTIFF_GPSCoordinate ( tiff, tagInfo, xmp, kXMP_NS_EXIF, "GPSDestLongitude" );
2048 	}
2049 
2050 	// 27 GPSProcessingMethod is a string with explicit encoding.
2051 	ok = ImportTIFF_VerifyImport ( tiff, xmp, digestState, kTIFF_GPSInfoIFD, kTIFF_GPSProcessingMethod,
2052 								   kXMP_NS_EXIF, "GPSProcessingMethod", &tagInfo );
2053 	if ( ok ) {
2054 		ImportTIFF_EncodedString ( tiff, tagInfo, xmp, kXMP_NS_EXIF, "GPSProcessingMethod" );
2055 	}
2056 
2057 	// 28 GPSAreaInformation is a string with explicit encoding.
2058 	ok = ImportTIFF_VerifyImport ( tiff, xmp, digestState, kTIFF_GPSInfoIFD, kTIFF_GPSAreaInformation,
2059 								   kXMP_NS_EXIF, "GPSAreaInformation", &tagInfo );
2060 	if ( ok ) {
2061 		ImportTIFF_EncodedString ( tiff, tagInfo, xmp, kXMP_NS_EXIF, "GPSAreaInformation" );
2062 	}
2063 
2064 }	// ReconcileUtils::ImportExif;
2065 
2066 // =================================================================================================
2067 // =================================================================================================
2068 
2069 // =================================================================================================
2070 // ExportSingleTIFF_Short
2071 // ======================
2072 
2073 static void
ExportSingleTIFF_Short(const SXMPMeta & xmp,const char * xmpNS,const char * xmpProp,TIFF_Manager * tiff,XMP_Uns8 ifd,XMP_Uns16 id)2074 ExportSingleTIFF_Short ( const SXMPMeta & xmp, const char * xmpNS, const char * xmpProp,
2075 						 TIFF_Manager * tiff, XMP_Uns8 ifd, XMP_Uns16 id )
2076 {
2077 	try {	// Don't let errors with one stop the others.
2078 
2079 		long xmpValue;
2080 
2081 		bool foundXMP = xmp.GetProperty_Int ( xmpNS, xmpProp, &xmpValue, 0 );
2082 		if ( ! foundXMP ) {
2083 			tiff->DeleteTag ( ifd, id );
2084 			return;
2085 		}
2086 
2087 		if ( (xmpValue < 0) || (xmpValue > 0xFFFF) ) return;	// ? Complain? Peg to limit? Delete the tag?
2088 
2089 		tiff->SetTag_Short ( ifd, id, (XMP_Uns16)xmpValue );
2090 
2091 	} catch ( ... ) {
2092 		// Do nothing, let other exports proceed.
2093 		// ? Notify client?
2094 	}
2095 
2096 }	// ExportSingleTIFF_Short
2097 
2098 // =================================================================================================
2099 // ExportSingleTIFF_Rational
2100 // =========================
2101 //
2102 // An XMP (unsigned) rational is supposed to be written as a string "num/denom".
2103 
2104 static void
ExportSingleTIFF_Rational(const SXMPMeta & xmp,const char * xmpNS,const char * xmpProp,TIFF_Manager * tiff,XMP_Uns8 ifd,XMP_Uns16 id)2105 ExportSingleTIFF_Rational ( const SXMPMeta & xmp, const char * xmpNS, const char * xmpProp,
2106 							TIFF_Manager * tiff, XMP_Uns8 ifd, XMP_Uns16 id )
2107 {
2108 	try {	// Don't let errors with one stop the others.
2109 
2110 		std::string    strValue;
2111 		XMP_OptionBits xmpFlags;
2112 
2113 		bool foundXMP = xmp.GetProperty ( xmpNS, xmpProp, &strValue, &xmpFlags );
2114 		if ( ! foundXMP ) {
2115 			tiff->DeleteTag ( ifd, id );
2116 			return;
2117 		}
2118 
2119 		if ( ! XMP_PropIsSimple ( xmpFlags ) ) return;	// ? Complain? Delete the tag?
2120 
2121 		XMP_Uns32 newNum, newDenom;
2122 		const char* partPtr;
2123 		size_t partLen;
2124 
2125 		partPtr = strValue.c_str();
2126 		for ( partLen = 0; partPtr[partLen] != 0; ++partLen ) {
2127 			if ( (partPtr[partLen] < '0') || (partPtr[partLen] > '9') ) break;
2128 		}
2129 		if ( partLen == 0 ) return;	// ? Complain? Delete the tag?
2130 		newNum = GatherInt ( partPtr, partLen );
2131 
2132 		if ( partPtr[partLen] == 0 ) {
2133 			newDenom = 1;	// Tolerate bad XMP that just has the numerator.
2134 		} else if ( partPtr[partLen] != '/' ) {
2135 			return;	// ? Complain? Delete the tag?
2136 		} else {
2137 			partPtr += partLen+1;
2138 			for ( partLen = 0; partPtr[partLen] != 0; ++partLen ) {
2139 				if ( (partPtr[partLen] < '0') || (partPtr[partLen] > '9') ) break;
2140 			}
2141 			if ( (partLen == 0) || (partPtr[partLen] != 0) ) return;	// ? Complain? Delete the tag?
2142 			newDenom = GatherInt ( partPtr, partLen );
2143 		}
2144 
2145 		tiff->SetTag_Rational ( ifd, id, newNum, newDenom );
2146 
2147 	} catch ( ... ) {
2148 		// Do nothing, let other exports proceed.
2149 		// ? Notify client?
2150 	}
2151 
2152 }	// ExportSingleTIFF_Rational
2153 
2154 // =================================================================================================
2155 // ExportSingleTIFF_ASCII
2156 // ======================
2157 
2158 static void
ExportSingleTIFF_ASCII(const SXMPMeta & xmp,const char * xmpNS,const char * xmpProp,TIFF_Manager * tiff,XMP_Uns8 ifd,XMP_Uns16 id)2159 ExportSingleTIFF_ASCII ( const SXMPMeta & xmp, const char * xmpNS, const char * xmpProp,
2160 						 TIFF_Manager * tiff, XMP_Uns8 ifd, XMP_Uns16 id )
2161 {
2162 	try {	// Don't let errors with one stop the others.
2163 
2164 		std::string    xmpValue;
2165 		XMP_OptionBits xmpFlags;
2166 
2167 		bool foundXMP = xmp.GetProperty ( xmpNS, xmpProp, &xmpValue, &xmpFlags );
2168 		if ( ! foundXMP ) {
2169 			tiff->DeleteTag ( ifd, id );
2170 			return;
2171 		}
2172 
2173 		if ( ! XMP_PropIsSimple ( xmpFlags ) ) return;	// ? Complain? Delete the tag?
2174 
2175 		tiff->SetTag ( ifd, id, kTIFF_ASCIIType, (XMP_Uns32)( xmpValue.size()+1 ), xmpValue.c_str() );
2176 
2177 	} catch ( ... ) {
2178 		// Do nothing, let other exports proceed.
2179 		// ? Notify client?
2180 	}
2181 
2182 }	// ExportSingleTIFF_ASCII
2183 
2184 // =================================================================================================
2185 // ExportArrayTIFF_ASCII
2186 // =====================
2187 //
2188 // Catenate all of the XMP array values into a string with separating nul characters.
2189 
2190 static void
ExportArrayTIFF_ASCII(const SXMPMeta & xmp,const char * xmpNS,const char * xmpProp,TIFF_Manager * tiff,XMP_Uns8 ifd,XMP_Uns16 id)2191 ExportArrayTIFF_ASCII ( const SXMPMeta & xmp, const char * xmpNS, const char * xmpProp,
2192 						TIFF_Manager * tiff, XMP_Uns8 ifd, XMP_Uns16 id )
2193 {
2194 	try {	// Don't let errors with one stop the others.
2195 
2196 		std::string    itemValue, fullValue;
2197 		XMP_OptionBits xmpFlags;
2198 
2199 		bool foundXMP = xmp.GetProperty ( xmpNS, xmpProp, 0, &xmpFlags );
2200 		if ( ! foundXMP ) {
2201 			tiff->DeleteTag ( ifd, id );
2202 			return;
2203 		}
2204 
2205 		if ( ! XMP_PropIsArray ( xmpFlags ) ) return;	// ? Complain? Delete the tag?
2206 
2207 		size_t count = xmp.CountArrayItems ( xmpNS, xmpProp );
2208 		for ( size_t i = 1; i <= count; ++i ) {	// ! XMP arrays are indexed from 1.
2209 			(void) xmp.GetArrayItem ( xmpNS, xmpProp, (XMP_Index)i, &itemValue, &xmpFlags );
2210 			if ( ! XMP_PropIsSimple ( xmpFlags ) ) continue;	// ? Complain?
2211 			fullValue.append ( itemValue );
2212 			fullValue.append ( 1, '\x0' );
2213 		}
2214 
2215 		tiff->SetTag ( ifd, id, kTIFF_ASCIIType, (XMP_Uns32)fullValue.size(), fullValue.c_str() );	// ! Already have trailing nul.
2216 
2217 	} catch ( ... ) {
2218 		// Do nothing, let other exports proceed.
2219 		// ? Notify client?
2220 	}
2221 
2222 }	// ExportArrayTIFF_ASCII
2223 
2224 // =================================================================================================
2225 // ExportTIFF_Date
2226 // ===============
2227 //
2228 // Convert  an XMP date/time to an Exif 2.2 master date/time tag plus associated fractional seconds.
2229 // The Exif date/time part is a 20 byte ASCII value formatted as "YYYY:MM:DD HH:MM:SS" with a
2230 // terminating nul. The fractional seconds are a nul terminated ASCII string with possible space
2231 // padding. They are literally the fractional part, the digits that would be to the right of the
2232 // decimal point.
2233 
2234 static void
ExportTIFF_Date(const SXMPMeta & xmp,const char * xmpNS,const char * xmpProp,TIFF_Manager * tiff,XMP_Uns8 mainIFD,XMP_Uns16 mainID,XMP_Uns8 fracIFD,XMP_Uns16 fracID)2235 ExportTIFF_Date ( const SXMPMeta & xmp, const char * xmpNS, const char * xmpProp,
2236 				  TIFF_Manager * tiff, XMP_Uns8 mainIFD, XMP_Uns16 mainID, XMP_Uns8 fracIFD, XMP_Uns16 fracID )
2237 {
2238 	try {	// Don't let errors with one stop the others.
2239 
2240 		XMP_DateTime xmpValue;
2241 
2242 		bool foundXMP = xmp.GetProperty_Date ( xmpNS, xmpProp, &xmpValue, 0 );
2243 		if ( ! foundXMP ) {
2244 			tiff->DeleteTag ( mainIFD, mainID );
2245 			tiff->DeleteTag ( fracIFD, fracID );
2246 			return;
2247 		}
2248 
2249 		char buffer[24];
2250 		snprintf ( buffer, sizeof(buffer), "%.4d:%.2d:%.2d %.2d:%.2d:%.2d",	// AUDIT: Use of sizeof(buffer) is safe.
2251 				   xmpValue.year, xmpValue.month, xmpValue.day, xmpValue.hour, xmpValue.minute, xmpValue.second );
2252 
2253 		tiff->SetTag_ASCII ( mainIFD, mainID, buffer );
2254 
2255 		if ( xmpValue.nanoSecond != 0 ) {
2256 
2257 			snprintf ( buffer, sizeof(buffer), "%09d", xmpValue.nanoSecond );	// AUDIT: Use of sizeof(buffer) is safe.
2258 			for ( size_t i = strlen(buffer)-1; i > 0; --i ) {
2259 				if ( buffer[i] != '0' ) break;
2260 				buffer[i] = 0;	// Strip trailing zero digits.
2261 			}
2262 
2263 			tiff->SetTag_ASCII ( fracIFD, fracID, buffer );
2264 
2265 		}
2266 
2267 	} catch ( ... ) {
2268 		// Do nothing, let other exports proceed.
2269 		// ? Notify client?
2270 	}
2271 
2272 }	// ExportTIFF_Date
2273 
2274 // =================================================================================================
2275 // ExportTIFF_LocTextASCII
2276 // ======================
2277 
2278 static void
ExportTIFF_LocTextASCII(const SXMPMeta & xmp,const char * xmpNS,const char * xmpProp,TIFF_Manager * tiff,XMP_Uns8 ifd,XMP_Uns16 id)2279 ExportTIFF_LocTextASCII ( const SXMPMeta & xmp, const char * xmpNS, const char * xmpProp,
2280 						  TIFF_Manager * tiff, XMP_Uns8 ifd, XMP_Uns16 id )
2281 {
2282 	try {	// Don't let errors with one stop the others.
2283 
2284 		std::string xmpValue;
2285 
2286 		bool foundXMP = xmp.GetLocalizedText ( xmpNS, xmpProp, "", "x-default", 0, &xmpValue, 0 );
2287 		if ( ! foundXMP ) {
2288 			tiff->DeleteTag ( ifd, id );
2289 			return;
2290 		}
2291 
2292 		tiff->SetTag ( ifd, id, kTIFF_ASCIIType, (XMP_Uns32)( xmpValue.size()+1 ), xmpValue.c_str() );
2293 
2294 	} catch ( ... ) {
2295 		// Do nothing, let other exports proceed.
2296 		// ? Notify client?
2297 	}
2298 
2299 }	// ExportTIFF_LocTextASCII
2300 
2301 // =================================================================================================
2302 // ExportTIFF_EncodedString
2303 // ========================
2304 
2305 static void
ExportTIFF_EncodedString(const SXMPMeta & xmp,const char * xmpNS,const char * xmpProp,TIFF_Manager * tiff,XMP_Uns8 ifd,XMP_Uns16 id,bool isLangAlt=false)2306 ExportTIFF_EncodedString ( const SXMPMeta & xmp, const char * xmpNS, const char * xmpProp,
2307 						   TIFF_Manager * tiff, XMP_Uns8 ifd, XMP_Uns16 id, bool isLangAlt = false )
2308 {
2309 	try {	// Don't let errors with one stop the others.
2310 
2311 		std::string    xmpValue;
2312 		XMP_OptionBits xmpFlags;
2313 
2314 		bool foundXMP = xmp.GetProperty ( xmpNS, xmpProp, &xmpValue, &xmpFlags );
2315 		if ( ! foundXMP ) {
2316 			tiff->DeleteTag ( ifd, id );
2317 			return;
2318 		}
2319 
2320 		if ( ! isLangAlt ) {
2321 			if ( ! XMP_PropIsSimple ( xmpFlags ) ) return;	// ? Complain? Delete the tag?
2322 		} else {
2323 			if ( ! XMP_ArrayIsAltText ( xmpFlags ) ) return;	// ? Complain? Delete the tag?
2324 			bool ok = xmp.GetLocalizedText ( xmpNS, xmpProp, "", "x-default", 0, &xmpValue, 0 );
2325 			if ( ! ok ) return;	// ? Complain? Delete the tag?
2326 		}
2327 
2328 		XMP_Uns8 encoding = kTIFF_EncodeASCII;
2329 		for ( size_t i = 0; i < xmpValue.size(); ++i ) {
2330 			if ( xmpValue[i] >= 0x80 ) {
2331 				encoding = kTIFF_EncodeUnicode;
2332 				break;
2333 			}
2334 		}
2335 
2336 		tiff->SetTag_EncodedString ( ifd, id, xmpValue.c_str(), encoding );
2337 
2338 	} catch ( ... ) {
2339 		// Do nothing, let other exports proceed.
2340 		// ? Notify client?
2341 	}
2342 
2343 }	// ExportTIFF_EncodedString
2344 
2345 // =================================================================================================
2346 // ExportTIFF_GPSCoordinate
2347 // ========================
2348 //
2349 // The XMP format is either "deg,min,secR" or "deg,min.fracR", where 'R' is the reference direction,
2350 // 'N', 'S', 'E', or 'W'. The location gets output as ( deg/1, min/1, sec/1 ) for the first form,
2351 // and ( deg/1, minFrac/denom, 0/1 ) for the second form.
2352 
2353 // ! We arbitrarily limit the number of fractional minute digits to 6 to avoid overflow in the
2354 // ! combined numerator. But we don't otherwise check for overflow or range errors.
2355 
2356 static void
ExportTIFF_GPSCoordinate(const SXMPMeta & xmp,const char * xmpNS,const char * xmpProp,TIFF_Manager * tiff,XMP_Uns8 ifd,XMP_Uns16 _id)2357 ExportTIFF_GPSCoordinate ( const SXMPMeta & xmp, const char * xmpNS, const char * xmpProp,
2358 						   TIFF_Manager * tiff, XMP_Uns8 ifd, XMP_Uns16 _id )
2359 {
2360 	XMP_Uns16 refID = _id-1;	// ! The GPS refs and locations are all tag N-1 and N pairs.
2361 	XMP_Uns16 locID = _id;
2362 
2363 	XMP_Assert ( (locID & 1) == 0 );
2364 
2365 	try {	// Don't let errors with one stop the others.
2366 
2367 		std::string xmpValue;
2368 		XMP_OptionBits xmpFlags;
2369 
2370 		bool foundXMP = xmp.GetProperty ( xmpNS, xmpProp, &xmpValue, &xmpFlags );
2371 		if ( ! foundXMP ) {
2372 			tiff->DeleteTag ( ifd, refID );
2373 			tiff->DeleteTag ( ifd, locID );
2374 			return;
2375 		}
2376 
2377 		if ( ! XMP_PropIsSimple ( xmpFlags ) ) return;
2378 
2379 		const char * chPtr = xmpValue.c_str();
2380 
2381 		XMP_Uns32 deg=0, minNum=0, minDenom=1, sec=0;
2382 
2383 		for ( ; ('0' <= *chPtr) && (*chPtr <= '9'); ++chPtr ) deg = deg*10 + (*chPtr - '0');
2384 		if ( *chPtr != ',' ) return;	// Bad XMP string.
2385 		++chPtr;	// Skip the comma.
2386 
2387 		for ( ; ('0' <= *chPtr) && (*chPtr <= '9'); ++chPtr ) minNum = minNum*10 + (*chPtr - '0');
2388 		if ( (*chPtr != ',') && (*chPtr != '.') ) return;	// Bad XMP string.
2389 
2390 		if ( *chPtr == ',' ) {
2391 
2392 			++chPtr;	// Skip the comma.
2393 			for ( ; ('0' <= *chPtr) && (*chPtr <= '9'); ++chPtr ) sec = sec*10 + (*chPtr - '0');
2394 
2395 		} else {
2396 
2397 			XMP_Assert ( *chPtr == '.' );
2398 			++chPtr;	// Skip the period.
2399 			for ( ; ('0' <= *chPtr) && (*chPtr <= '9'); ++chPtr ) {
2400 				if ( minDenom > 100*1000 ) continue;	// Don't accumulate any more digits.
2401 				minDenom *= 10;
2402 				minNum = minNum*10 + (*chPtr - '0');
2403 			}
2404 
2405 		}
2406 
2407 		if ( *(chPtr+1) != 0 ) return;	// Bad XMP string.
2408 
2409 		char ref[2];
2410 		ref[0] = *chPtr;
2411 		ref[1] = 0;
2412 
2413 		tiff->SetTag ( ifd, refID, kTIFF_ASCIIType, 2, &ref[0] );
2414 
2415 		XMP_Uns32 loc[6];
2416 		tiff->PutUns32 ( deg, &loc[0] );
2417 		tiff->PutUns32 ( 1,   &loc[1] );
2418 		tiff->PutUns32 ( minNum,   &loc[2] );
2419 		tiff->PutUns32 ( minDenom, &loc[3] );
2420 		tiff->PutUns32 ( sec, &loc[4] );
2421 		tiff->PutUns32 ( 1,   &loc[5] );
2422 
2423 		tiff->SetTag ( ifd, locID, kTIFF_RationalType, 3, &loc[0] );
2424 
2425 	} catch ( ... ) {
2426 		// Do nothing, let other exports proceed.
2427 		// ? Notify client?
2428 	}
2429 
2430 }	// ExportTIFF_GPSCoordinate
2431 
2432 // =================================================================================================
2433 // =================================================================================================
2434 
2435 // =================================================================================================
2436 // ReconcileUtils::ExportTIFF
2437 // ==========================
2438 //
2439 // Only a few tags are written back from XMP to the primary IFD, they are each handled explicitly.
2440 // The writeback tags are:
2441 //   270 - ImageDescription
2442 //   274 - Orientation
2443 //   282 - XResolution
2444 //   283 - YResolution
2445 //   296 - ResolutionUnit
2446 //   305 - Software
2447 //   306 - DateTime
2448 //   315 - Artist
2449 //   33432 - Copyright
2450 
2451 // *** need to determine if the XMP has changed - only export when necessary
2452 
2453 void
ExportTIFF(const SXMPMeta & xmp,TIFF_Manager * tiff)2454 ReconcileUtils::ExportTIFF ( const SXMPMeta & xmp, TIFF_Manager * tiff )
2455 {
2456 
2457 	ExportTIFF_LocTextASCII ( xmp, kXMP_NS_DC, "description",
2458 							  tiff, kTIFF_PrimaryIFD, kTIFF_ImageDescription );
2459 
2460 	ExportSingleTIFF_Short ( xmp, kXMP_NS_TIFF, "Orientation",
2461 							 tiff, kTIFF_PrimaryIFD, kTIFF_Orientation );
2462 
2463 	ExportSingleTIFF_Rational ( xmp, kXMP_NS_TIFF, "XResolution",
2464 								tiff, kTIFF_PrimaryIFD, kTIFF_XResolution );
2465 
2466 	ExportSingleTIFF_Rational ( xmp, kXMP_NS_TIFF, "YResolution",
2467 								tiff, kTIFF_PrimaryIFD, kTIFF_YResolution );
2468 
2469 	ExportSingleTIFF_Short ( xmp, kXMP_NS_TIFF, "ResolutionUnit",
2470 							 tiff, kTIFF_PrimaryIFD, kTIFF_ResolutionUnit );
2471 
2472 	ExportSingleTIFF_ASCII ( xmp, kXMP_NS_XMP, "CreatorTool",
2473 							 tiff, kTIFF_PrimaryIFD, kTIFF_Software );
2474 
2475 	ExportTIFF_Date ( xmp, kXMP_NS_XMP, "ModifyDate",
2476 					  tiff, kTIFF_PrimaryIFD, kTIFF_DateTime, kTIFF_ExifIFD, kTIFF_SubSecTime );
2477 
2478 	ExportArrayTIFF_ASCII ( xmp, kXMP_NS_DC, "creator",
2479 							tiff, kTIFF_PrimaryIFD, kTIFF_Artist );
2480 
2481 	ExportTIFF_LocTextASCII ( xmp, kXMP_NS_DC, "rights",
2482 							  tiff, kTIFF_PrimaryIFD, kTIFF_Copyright );
2483 
2484 }	// ReconcileUtils::ExportTIFF;
2485 
2486 // =================================================================================================
2487 // ReconcileUtils::ExportExif
2488 // ==========================
2489 //
2490 // Only a few tags are written back from XMP to the Exif and GPS IFDs, they are each handled
2491 // explicitly. The Exif writeback tags are:
2492 //   36867 - DateTimeOriginal (plus 37521 SubSecTimeOriginal)
2493 //   36868 - DateTimeDigitized (plus 37522 SubSecTimeDigitized)
2494 //   37510 - UserComment
2495 //   40964 - RelatedSoundFile
2496 // The GPS writeback tags are:
2497 //   1 - GPSLatitudeRef
2498 //   2 - GPSLatitude
2499 //   3 - GPSLongitudeRef
2500 //   4 - GPSLongitude
2501 
2502 // ! Older versions of Photoshop did not import the UserComment or RelatedSoundFile tags. Don't
2503 // ! export the current XMP unless the original XMP had the tag or the current XMP has the tag.
2504 // ! That is, don't delete the Exif tag if the XMP never had the property.
2505 
2506 void
ExportExif(const SXMPMeta & xmp,TIFF_Manager * tiff)2507 ReconcileUtils::ExportExif ( const SXMPMeta & xmp, TIFF_Manager * tiff )
2508 {
2509 
2510 	if ( xmp.DoesPropertyExist ( kXMP_NS_EXIF, "DateTimeOriginal" ) ) {
2511 		ExportTIFF_Date ( xmp, kXMP_NS_EXIF, "DateTimeOriginal",
2512 						  tiff, kTIFF_ExifIFD, kTIFF_DateTimeOriginal, kTIFF_ExifIFD, kTIFF_SubSecTimeOriginal );
2513 	}
2514 
2515 	if ( xmp.DoesPropertyExist ( kXMP_NS_EXIF, "DateTimeDigitized" ) ) {
2516 		ExportTIFF_Date ( xmp, kXMP_NS_EXIF, "DateTimeDigitized",
2517 						  tiff, kTIFF_ExifIFD, kTIFF_DateTimeDigitized, kTIFF_ExifIFD, kTIFF_SubSecTimeDigitized );
2518 	}
2519 
2520 	if ( tiff->xmpHadUserComment || xmp.DoesPropertyExist ( kXMP_NS_EXIF, "UserComment" ) ) {
2521 		ExportTIFF_EncodedString ( xmp, kXMP_NS_EXIF, "UserComment",
2522 								   tiff, kTIFF_ExifIFD, kTIFF_UserComment, true /* isLangAlt */ );
2523 	}
2524 
2525 	if ( tiff->xmpHadRelatedSoundFile || xmp.DoesPropertyExist ( kXMP_NS_EXIF, "RelatedSoundFile" ) ) {
2526 		ExportSingleTIFF_ASCII ( xmp, kXMP_NS_EXIF, "RelatedSoundFile",
2527 								 tiff, kTIFF_ExifIFD, kTIFF_RelatedSoundFile );
2528 	}
2529 
2530 	if ( xmp.DoesPropertyExist ( kXMP_NS_EXIF, "GPSLatitude" ) ) {
2531 		ExportTIFF_GPSCoordinate ( xmp, kXMP_NS_EXIF, "GPSLatitude", tiff, kTIFF_GPSInfoIFD, kTIFF_GPSLatitude );
2532 	}
2533 
2534 	if ( xmp.DoesPropertyExist ( kXMP_NS_EXIF, "GPSLongitude" ) ) {
2535 		ExportTIFF_GPSCoordinate ( xmp, kXMP_NS_EXIF, "GPSLongitude", tiff, kTIFF_GPSInfoIFD, kTIFF_GPSLongitude );
2536 	}
2537 
2538 }	// ReconcileUtils::ExportExif;
2539