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