• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*****************************************************************************/
2 // Copyright 2006-2008 Adobe Systems Incorporated
3 // All Rights Reserved.
4 //
5 // NOTICE:  Adobe permits you to use, modify, and distribute this file in
6 // accordance with the terms of the Adobe license agreement accompanying it.
7 /*****************************************************************************/
8 
9 /* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_exif.cpp#1 $ */
10 /* $DateTime: 2012/05/30 13:28:51 $ */
11 /* $Change: 832332 $ */
12 /* $Author: tknoll $ */
13 
14 /*****************************************************************************/
15 
16 #include "dng_exif.h"
17 
18 #include "dng_tag_codes.h"
19 #include "dng_tag_types.h"
20 #include "dng_parse_utils.h"
21 #include "dng_globals.h"
22 #include "dng_exceptions.h"
23 #include "dng_tag_values.h"
24 #include "dng_utils.h"
25 
26 /*****************************************************************************/
27 
dng_exif()28 dng_exif::dng_exif ()
29 
30 	:	fImageDescription ()
31 	,	fMake             ()
32 	,	fModel            ()
33 	,	fSoftware         ()
34 	,	fArtist           ()
35 	,	fCopyright        ()
36 	,	fCopyright2       ()
37 	,	fUserComment      ()
38 
39 	,	fDateTime            ()
40 	,	fDateTimeStorageInfo ()
41 
42 	,	fDateTimeOriginal  ()
43 	,	fDateTimeOriginalStorageInfo ()
44 
45 	,	fDateTimeDigitized ()
46 	,	fDateTimeDigitizedStorageInfo ()
47 
48 	,	fTIFF_EP_StandardID (0)
49 	,	fExifVersion        (0)
50 	,	fFlashPixVersion	(0)
51 
52 	,	fExposureTime      ()
53 	,	fFNumber           ()
54 	,	fShutterSpeedValue ()
55 	,	fApertureValue     ()
56 	,	fBrightnessValue   ()
57 	,	fExposureBiasValue ()
58 	,	fMaxApertureValue  ()
59 	,	fFocalLength       ()
60 	,	fDigitalZoomRatio  ()
61 	,	fExposureIndex     ()
62 	,	fSubjectDistance   ()
63 	,	fGamma             ()
64 
65 	,	fBatteryLevelR ()
66 	,	fBatteryLevelA ()
67 
68 	,	fExposureProgram  	  (0xFFFFFFFF)
69 	,	fMeteringMode     	  (0xFFFFFFFF)
70 	,	fLightSource      	  (0xFFFFFFFF)
71 	,	fFlash			  	  (0xFFFFFFFF)
72 	,	fFlashMask 			  (0x0000FFFF)
73 	,	fSensingMethod    	  (0xFFFFFFFF)
74 	,	fColorSpace       	  (0xFFFFFFFF)
75 	,	fFileSource       	  (0xFFFFFFFF)
76 	,	fSceneType		  	  (0xFFFFFFFF)
77 	,	fCustomRendered   	  (0xFFFFFFFF)
78 	,	fExposureMode	  	  (0xFFFFFFFF)
79 	,	fWhiteBalance     	  (0xFFFFFFFF)
80 	,	fSceneCaptureType 	  (0xFFFFFFFF)
81 	,	fGainControl 		  (0xFFFFFFFF)
82 	,	fContrast 			  (0xFFFFFFFF)
83 	,	fSaturation 		  (0xFFFFFFFF)
84 	,	fSharpness 			  (0xFFFFFFFF)
85 	,	fSubjectDistanceRange (0xFFFFFFFF)
86 	,	fSelfTimerMode		  (0xFFFFFFFF)
87 	,	fImageNumber		  (0xFFFFFFFF)
88 
89 	,	fFocalLengthIn35mmFilm (0)
90 
91 	,	fSensitivityType		   (0)
92 	,	fStandardOutputSensitivity (0)
93 	,	fRecommendedExposureIndex  (0)
94 	,	fISOSpeed				   (0)
95 	,	fISOSpeedLatitudeyyy	   (0)
96 	,	fISOSpeedLatitudezzz	   (0)
97 
98 	,	fSubjectAreaCount (0)
99 
100 	,	fComponentsConfiguration (0)
101 
102 	,	fCompresssedBitsPerPixel ()
103 
104 	,	fPixelXDimension (0)
105 	,	fPixelYDimension (0)
106 
107 	,	fFocalPlaneXResolution ()
108 	,	fFocalPlaneYResolution ()
109 
110 	,	fFocalPlaneResolutionUnit (0xFFFFFFFF)
111 
112 	,	fCFARepeatPatternRows (0)
113 	,	fCFARepeatPatternCols (0)
114 
115 	,	fImageUniqueID ()
116 
117 	,	fGPSVersionID		  (0)
118 	,	fGPSLatitudeRef		  ()
119 	,	fGPSLongitudeRef	  ()
120 	,	fGPSAltitudeRef		  (0xFFFFFFFF)
121 	,	fGPSAltitude		  ()
122 	,	fGPSSatellites		  ()
123 	,	fGPSStatus			  ()
124 	,	fGPSMeasureMode		  ()
125 	,	fGPSDOP				  ()
126 	,	fGPSSpeedRef		  ()
127 	,	fGPSSpeed			  ()
128 	,	fGPSTrackRef		  ()
129 	,	fGPSTrack			  ()
130 	,	fGPSImgDirectionRef	  ()
131 	,	fGPSImgDirection	  ()
132 	,	fGPSMapDatum		  ()
133 	,	fGPSDestLatitudeRef	  ()
134 	,	fGPSDestLongitudeRef  ()
135 	,	fGPSDestBearingRef	  ()
136 	,	fGPSDestBearing		  ()
137 	,	fGPSDestDistanceRef	  ()
138 	,	fGPSDestDistance	  ()
139 	,	fGPSProcessingMethod  ()
140 	,	fGPSAreaInformation	  ()
141 	,	fGPSDateStamp		  ()
142 	,	fGPSDifferential	  (0xFFFFFFFF)
143 	,	fGPSHPositioningError ()
144 
145 	,	fInteroperabilityIndex ()
146 
147 	,	fInteroperabilityVersion (0)
148 
149 	,	fRelatedImageFileFormat ()
150 
151 	,	fRelatedImageWidth  (0)
152 	,	fRelatedImageLength (0)
153 
154 	,	fCameraSerialNumber ()
155 
156 	,	fLensID			  ()
157 	,	fLensMake		  ()
158 	,	fLensName		  ()
159 	,	fLensSerialNumber ()
160 
161 	,	fLensNameWasReadFromExif (false)
162 
163 	,	fApproxFocusDistance ()
164 
165 	,	fFlashCompensation ()
166 
167 	,	fOwnerName ()
168 	,	fFirmware  ()
169 
170 	{
171 
172 	uint32 j;
173 	uint32 k;
174 
175 	fISOSpeedRatings [0] = 0;
176 	fISOSpeedRatings [1] = 0;
177 	fISOSpeedRatings [2] = 0;
178 
179 	for (j = 0; j < kMaxCFAPattern; j++)
180 		for (k = 0; k < kMaxCFAPattern; k++)
181 			{
182 			fCFAPattern [j] [k] = 255;
183 			}
184 
185 	}
186 
187 /*****************************************************************************/
188 
~dng_exif()189 dng_exif::~dng_exif ()
190 	{
191 
192 	}
193 
194 /*****************************************************************************/
195 
Clone() const196 dng_exif * dng_exif::Clone () const
197 	{
198 
199 	dng_exif *result = new dng_exif (*this);
200 
201 	if (!result)
202 		{
203 		ThrowMemoryFull ();
204 		}
205 
206 	return result;
207 
208 	}
209 
210 /*****************************************************************************/
211 
SetEmpty()212 void dng_exif::SetEmpty ()
213 	{
214 
215 	*this = dng_exif ();
216 
217 	}
218 
219 /*****************************************************************************/
220 
CopyGPSFrom(const dng_exif & exif)221 void dng_exif::CopyGPSFrom (const dng_exif &exif)
222 	{
223 
224 	fGPSVersionID         = exif.fGPSVersionID;
225 	fGPSLatitudeRef       = exif.fGPSLatitudeRef;
226 	fGPSLatitude [0]	  = exif.fGPSLatitude [0];
227 	fGPSLatitude [1]	  = exif.fGPSLatitude [1];
228 	fGPSLatitude [2]	  = exif.fGPSLatitude [2];
229 	fGPSLongitudeRef      = exif.fGPSLongitudeRef;
230 	fGPSLongitude [0]	  = exif.fGPSLongitude [0];
231 	fGPSLongitude [1]	  = exif.fGPSLongitude [1];
232 	fGPSLongitude [2]	  = exif.fGPSLongitude [2];
233 	fGPSAltitudeRef       = exif.fGPSAltitudeRef;
234 	fGPSAltitude          = exif.fGPSAltitude;
235 	fGPSTimeStamp [0]     = exif.fGPSTimeStamp [0];
236 	fGPSTimeStamp [1]     = exif.fGPSTimeStamp [1];
237 	fGPSTimeStamp [2]     = exif.fGPSTimeStamp [2];
238 	fGPSSatellites        = exif.fGPSSatellites;
239 	fGPSStatus            = exif.fGPSStatus;
240 	fGPSMeasureMode       = exif.fGPSMeasureMode;
241 	fGPSDOP               = exif.fGPSDOP;
242 	fGPSSpeedRef          = exif.fGPSSpeedRef;
243 	fGPSSpeed             = exif.fGPSSpeed;
244 	fGPSTrackRef          = exif.fGPSTrackRef;
245 	fGPSTrack             = exif.fGPSTrack;
246 	fGPSImgDirectionRef   = exif.fGPSImgDirectionRef;
247 	fGPSImgDirection      = exif.fGPSImgDirection;
248 	fGPSMapDatum          = exif.fGPSMapDatum;
249 	fGPSDestLatitudeRef   = exif.fGPSDestLatitudeRef;
250 	fGPSDestLatitude [0]  = exif.fGPSDestLatitude [0];
251 	fGPSDestLatitude [1]  = exif.fGPSDestLatitude [1];
252 	fGPSDestLatitude [2]  = exif.fGPSDestLatitude [2];
253 	fGPSDestLongitudeRef  = exif.fGPSDestLongitudeRef;
254 	fGPSDestLongitude [0] = exif.fGPSDestLongitude [0];
255 	fGPSDestLongitude [1] = exif.fGPSDestLongitude [1];
256 	fGPSDestLongitude [2] = exif.fGPSDestLongitude [2];
257 	fGPSDestBearingRef    = exif.fGPSDestBearingRef;
258 	fGPSDestBearing       = exif.fGPSDestBearing;
259 	fGPSDestDistanceRef   = exif.fGPSDestDistanceRef;
260 	fGPSDestDistance      = exif.fGPSDestDistance;
261 	fGPSProcessingMethod  = exif.fGPSProcessingMethod;
262 	fGPSAreaInformation   = exif.fGPSAreaInformation;
263 	fGPSDateStamp         = exif.fGPSDateStamp;
264 	fGPSDifferential      = exif.fGPSDifferential;
265 	fGPSHPositioningError = exif.fGPSHPositioningError;
266 
267 	}
268 
269 /*****************************************************************************/
270 
271 // Fix up common errors and rounding issues with EXIF exposure times.
272 
SnapExposureTime(real64 et)273 real64 dng_exif::SnapExposureTime (real64 et)
274 	{
275 
276 	// Protection against invalid values.
277 
278 	if (et <= 0.0)
279 		return 0.0;
280 
281 	// If near a standard shutter speed, snap to it.
282 
283 	static const real64 kStandardSpeed [] =
284 		{
285 		30.0,
286 		25.0,
287 		20.0,
288 		15.0,
289 		13.0,
290 		10.0,
291 		8.0,
292 		6.0,
293 		5.0,
294 		4.0,
295 		3.2,
296 		3.0,
297 		2.5,
298 		2.0,
299 		1.6,
300 		1.5,
301 		1.3,
302 		1.0,
303 		0.8,
304 		0.7,
305 		0.6,
306 		0.5,
307 		0.4,
308 		0.3,
309 		1.0 / 4.0,
310 		1.0 / 5.0,
311 		1.0 / 6.0,
312 		1.0 / 8.0,
313 		1.0 / 10.0,
314 		1.0 / 13.0,
315 		1.0 / 15.0,
316 		1.0 / 20.0,
317 		1.0 / 25.0,
318 		1.0 / 30.0,
319 		1.0 / 40.0,
320 		1.0 / 45.0,
321 		1.0 / 50.0,
322 		1.0 / 60.0,
323 		1.0 / 80.0,
324 		1.0 / 90.0,
325 		1.0 / 100.0,
326 		1.0 / 125.0,
327 		1.0 / 160.0,
328 		1.0 / 180.0,
329 		1.0 / 200.0,
330 		1.0 / 250.0,
331 		1.0 / 320.0,
332 		1.0 / 350.0,
333 		1.0 / 400.0,
334 		1.0 / 500.0,
335 		1.0 / 640.0,
336 		1.0 / 750.0,
337 		1.0 / 800.0,
338 		1.0 / 1000.0,
339 		1.0 / 1250.0,
340 		1.0 / 1500.0,
341 		1.0 / 1600.0,
342 		1.0 / 2000.0,
343 		1.0 / 2500.0,
344 		1.0 / 3000.0,
345 		1.0 / 3200.0,
346 		1.0 / 4000.0,
347 		1.0 / 5000.0,
348 		1.0 / 6000.0,
349 		1.0 / 6400.0,
350 		1.0 / 8000.0,
351 		1.0 / 10000.0,
352 		1.0 / 12000.0,
353 		1.0 / 12800.0,
354 		1.0 / 16000.0
355 		};
356 
357 	uint32 count = sizeof (kStandardSpeed    ) /
358 				   sizeof (kStandardSpeed [0]);
359 
360 	for (uint32 fudge = 0; fudge <= 1; fudge++)
361 		{
362 
363 		real64 testSpeed = et;
364 
365 		if (fudge == 1)
366 			{
367 
368 			// Often APEX values are rounded to a power of two,
369 			// which results in non-standard shutter speeds.
370 
371 			if (et >= 0.1)
372 				{
373 
374 				// No fudging slower than 1/10 second
375 
376 				break;
377 
378 				}
379 
380 			else if (et >= 0.01)
381 				{
382 
383 				// Between 1/10 and 1/100 the commonly misrounded
384 				// speeds are 1/15, 1/30, 1/60, which are often encoded as
385 				// 1/16, 1/32, 1/64.  Try fudging and see if we get
386 				// near a standard speed.
387 
388 				testSpeed *= 16.0 / 15.0;
389 
390 				}
391 
392 			else
393 				{
394 
395 				// Faster than 1/100, the commonly misrounded
396 				// speeds are 1/125, 1/250, 1/500, etc., which
397 				// are often encoded as 1/128, 1/256, 1/512.
398 
399 				testSpeed *= 128.0 / 125.0;
400 
401 				}
402 
403 			}
404 
405 		for (uint32 index = 0; index < count; index++)
406 			{
407 
408 			if (testSpeed >= kStandardSpeed [index] * 0.98 &&
409 				testSpeed <= kStandardSpeed [index] * 1.02)
410 				{
411 
412 				return kStandardSpeed [index];
413 
414 				}
415 
416 			}
417 
418 		}
419 
420 	// We are not near any standard speeds.  Round the non-standard value to something
421 	// that looks reasonable.
422 
423 	if (et >= 10.0)
424 		{
425 
426 		// Round to nearest second.
427 
428 		et = floor (et + 0.5);
429 
430 		}
431 
432 	else if (et >= 0.5)
433 		{
434 
435 		// Round to nearest 1/10 second
436 
437 		et = floor (et * 10.0 + 0.5) * 0.1;
438 
439 		}
440 
441 	else if (et >= 1.0 / 20.0)
442 		{
443 
444 		// Round to an exact inverse.
445 
446 		et = 1.0 / floor (1.0 / et + 0.5);
447 
448 		}
449 
450 	else if (et >= 1.0 / 130.0)
451 		{
452 
453 		// Round inverse to multiple of 5
454 
455 		et = 0.2 / floor (0.2 / et + 0.5);
456 
457 		}
458 
459 	else if (et >= 1.0 / 750.0)
460 		{
461 
462 		// Round inverse to multiple of 10
463 
464 		et = 0.1 / floor (0.1 / et + 0.5);
465 
466 		}
467 
468 	else if (et >= 1.0 / 1300.0)
469 		{
470 
471 		// Round inverse to multiple of 50
472 
473 		et = 0.02 / floor (0.02 / et + 0.5);
474 
475 		}
476 
477 	else if (et >= 1.0 / 15000.0)
478 		{
479 
480 		// Round inverse to multiple of 100
481 
482 		et = 0.01 / floor (0.01 / et + 0.5);
483 
484 		}
485 
486 	else
487 		{
488 
489 		// Round inverse to multiple of 1000
490 
491 		et = 0.001 / floor (0.001 / et + 0.5);
492 
493 		}
494 
495 	return et;
496 
497 	}
498 
499 /*****************************************************************************/
500 
SetExposureTime(real64 et,bool snap)501 void dng_exif::SetExposureTime (real64 et, bool snap)
502 	{
503 
504 	fExposureTime.Clear ();
505 
506 	fShutterSpeedValue.Clear ();
507 
508 	if (snap)
509 		{
510 
511 		et = SnapExposureTime (et);
512 
513 		}
514 
515 	if (et >= 1.0 / 32768.0 && et <= 32768.0)
516 		{
517 
518 		if (et >= 100.0)
519 			{
520 
521 			fExposureTime.Set_real64 (et, 1);
522 
523 			}
524 
525 		else if (et >= 1.0)
526 			{
527 
528 			fExposureTime.Set_real64 (et, 10);
529 
530 			fExposureTime.ReduceByFactor (10);
531 
532 			}
533 
534 		else if (et <= 0.1)
535 			{
536 
537 			fExposureTime = dng_urational (1, Round_uint32 (1.0 / et));
538 
539 			}
540 
541 		else
542 			{
543 
544 			fExposureTime.Set_real64 (et, 100);
545 
546 			fExposureTime.ReduceByFactor (10);
547 
548 			for (uint32 f = 2; f <= 9; f++)
549 				{
550 
551 				real64 z = 1.0 / (real64) f / et;
552 
553 				if (z >= 0.99 && z <= 1.01)
554 					{
555 
556 					fExposureTime = dng_urational (1, f);
557 
558 					break;
559 
560 					}
561 
562 				}
563 
564 			}
565 
566 		// Now mirror this value to the ShutterSpeedValue field.
567 
568 		et = fExposureTime.As_real64 ();
569 
570 		fShutterSpeedValue.Set_real64 (-log (et) / log (2.0), 1000000);
571 
572 		fShutterSpeedValue.ReduceByFactor (10);
573 		fShutterSpeedValue.ReduceByFactor (10);
574 		fShutterSpeedValue.ReduceByFactor (10);
575 		fShutterSpeedValue.ReduceByFactor (10);
576 		fShutterSpeedValue.ReduceByFactor (10);
577 		fShutterSpeedValue.ReduceByFactor (10);
578 
579 		}
580 
581 	}
582 
583 /*****************************************************************************/
584 
SetShutterSpeedValue(real64 ss)585 void dng_exif::SetShutterSpeedValue (real64 ss)
586 	{
587 
588 	if (fExposureTime.NotValid ())
589 		{
590 
591 		real64 et = pow (2.0, -ss);
592 
593 		SetExposureTime (et, true);
594 
595 		}
596 
597 	}
598 
599 /******************************************************************************/
600 
EncodeFNumber(real64 fs)601 dng_urational dng_exif::EncodeFNumber (real64 fs)
602 	{
603 
604 	dng_urational y;
605 
606 	if (fs > 10.0)
607 		{
608 
609 		y.Set_real64 (fs, 1);
610 
611 		}
612 
613 	else if (fs < 1.0)
614 		{
615 
616 		y.Set_real64 (fs, 100);
617 
618 		y.ReduceByFactor (10);
619 		y.ReduceByFactor (10);
620 
621 		}
622 
623 	else
624 		{
625 
626 		y.Set_real64 (fs, 10);
627 
628 		y.ReduceByFactor (10);
629 
630 		}
631 
632 	return y;
633 
634 	}
635 
636 /*****************************************************************************/
637 
SetFNumber(real64 fs)638 void dng_exif::SetFNumber (real64 fs)
639 	{
640 
641 	fFNumber.Clear ();
642 
643 	fApertureValue.Clear ();
644 
645 	// Allow f-number values less than 1.0 (e.g., f/0.95), even though they would
646 	// correspond to negative APEX values, which the EXIF specification does not
647 	// support (ApertureValue is a rational, not srational). The ApertureValue tag
648 	// will be omitted in the case where fs < 1.0.
649 
650 	if (fs > 0.0 && fs <= 32768.0)
651 		{
652 
653 		fFNumber = EncodeFNumber (fs);
654 
655 		// Now mirror this value to the ApertureValue field.
656 
657 		real64 av = FNumberToApertureValue (fFNumber);
658 
659 		if (av >= 0.0 && av <= 99.99)
660 			{
661 
662 			fApertureValue.Set_real64 (av, 1000000);
663 
664 			fApertureValue.ReduceByFactor (10);
665 			fApertureValue.ReduceByFactor (10);
666 			fApertureValue.ReduceByFactor (10);
667 			fApertureValue.ReduceByFactor (10);
668 			fApertureValue.ReduceByFactor (10);
669 			fApertureValue.ReduceByFactor (10);
670 
671 			}
672 
673 		}
674 
675 	}
676 
677 /*****************************************************************************/
678 
SetApertureValue(real64 av)679 void dng_exif::SetApertureValue (real64 av)
680 	{
681 
682 	if (fFNumber.NotValid ())
683 		{
684 
685 		SetFNumber (ApertureValueToFNumber (av));
686 
687 		}
688 
689 	}
690 
691 /*****************************************************************************/
692 
ApertureValueToFNumber(real64 av)693 real64 dng_exif::ApertureValueToFNumber (real64 av)
694 	{
695 
696 	return pow (2.0, 0.5 * av);
697 
698 	}
699 
700 /*****************************************************************************/
701 
ApertureValueToFNumber(const dng_urational & av)702 real64 dng_exif::ApertureValueToFNumber (const dng_urational &av)
703 	{
704 
705 	return ApertureValueToFNumber (av.As_real64 ());
706 
707 	}
708 
709 /*****************************************************************************/
710 
FNumberToApertureValue(real64 fNumber)711 real64 dng_exif::FNumberToApertureValue (real64 fNumber)
712 	{
713 
714 	return 2.0 * log (fNumber) / log (2.0);
715 
716 	}
717 
718 /*****************************************************************************/
719 
FNumberToApertureValue(const dng_urational & fNumber)720 real64 dng_exif::FNumberToApertureValue (const dng_urational &fNumber)
721 	{
722 
723 	return FNumberToApertureValue (fNumber.As_real64 ());
724 
725 	}
726 
727 /*****************************************************************************/
728 
UpdateDateTime(const dng_date_time_info & dt)729 void dng_exif::UpdateDateTime (const dng_date_time_info &dt)
730 	{
731 
732 	fDateTime = dt;
733 
734 	}
735 
736 /*****************************************************************************/
737 
AtLeastVersion0230() const738 bool dng_exif::AtLeastVersion0230 () const
739 	{
740 
741 	uint32 b0 = (fExifVersion >> 24) & 0xff;
742 	uint32 b1 = (fExifVersion >> 16) & 0xff;
743 	uint32 b2 = (fExifVersion >>  8) & 0xff;
744 
745 	return (b0 > 0) || (b1 >= 2 && b2 >= 3);
746 
747 	}
748 
749 /*****************************************************************************/
750 
ParseTag(dng_stream & stream,dng_shared & shared,uint32 parentCode,bool isMainIFD,uint32 tagCode,uint32 tagType,uint32 tagCount,uint64 tagOffset)751 bool dng_exif::ParseTag (dng_stream &stream,
752 						 dng_shared &shared,
753 						 uint32 parentCode,
754 						 bool isMainIFD,
755 						 uint32 tagCode,
756 						 uint32 tagType,
757 						 uint32 tagCount,
758 						 uint64 tagOffset)
759 	{
760 
761 	if (parentCode == 0)
762 		{
763 
764 		if (Parse_ifd0 (stream,
765 		 				shared,
766 						parentCode,
767 						tagCode,
768 						tagType,
769 						tagCount,
770 						tagOffset))
771 			{
772 
773 			return true;
774 
775 			}
776 
777 		}
778 
779 	if (parentCode == 0 || isMainIFD)
780 		{
781 
782 		if (Parse_ifd0_main (stream,
783 		 				     shared,
784 						 	 parentCode,
785 						 	 tagCode,
786 						 	 tagType,
787 						 	 tagCount,
788 						 	 tagOffset))
789 			{
790 
791 			return true;
792 
793 			}
794 
795 		}
796 
797 	if (parentCode == 0 ||
798 		parentCode == tcExifIFD)
799 		{
800 
801 		if (Parse_ifd0_exif (stream,
802 		 				     shared,
803 						 	 parentCode,
804 						 	 tagCode,
805 						 	 tagType,
806 						 	 tagCount,
807 						 	 tagOffset))
808 			{
809 
810 			return true;
811 
812 			}
813 
814 		}
815 
816 	if (parentCode == tcGPSInfo)
817 		{
818 
819 		if (Parse_gps (stream,
820 		 			   shared,
821 					   parentCode,
822 					   tagCode,
823 					   tagType,
824 					   tagCount,
825 					   tagOffset))
826 			{
827 
828 			return true;
829 
830 			}
831 
832 		}
833 
834 	if (parentCode == tcInteroperabilityIFD)
835 		{
836 
837 		if (Parse_interoperability (stream,
838 		 			   				shared,
839 									parentCode,
840 									tagCode,
841 									tagType,
842 									tagCount,
843 									tagOffset))
844 			{
845 
846 			return true;
847 
848 			}
849 
850 		}
851 
852 	return false;
853 
854 	}
855 
856 /*****************************************************************************/
857 
858 // Parses tags that should only appear in IFD 0.
859 
Parse_ifd0(dng_stream & stream,dng_shared &,uint32 parentCode,uint32 tagCode,uint32 tagType,uint32 tagCount,uint64)860 bool dng_exif::Parse_ifd0 (dng_stream &stream,
861 		 			   	   dng_shared & /* shared */,
862 						   uint32 parentCode,
863 						   uint32 tagCode,
864 						   uint32 tagType,
865 						   uint32 tagCount,
866 						   uint64 /* tagOffset */)
867 	{
868 
869 	switch (tagCode)
870 		{
871 
872 		case tcImageDescription:
873 			{
874 
875 			CheckTagType (parentCode, tagCode, tagType, ttAscii);
876 
877 			ParseStringTag (stream,
878 							parentCode,
879 							tagCode,
880 							tagCount,
881 							fImageDescription);
882 
883 			#if qDNGValidate
884 
885 			if (gVerbose)
886 				{
887 
888 				printf ("ImageDescription: ");
889 
890 				DumpString (fImageDescription);
891 
892 				printf ("\n");
893 
894 				}
895 
896 			#endif
897 
898 			break;
899 
900 			}
901 
902 		case tcMake:
903 			{
904 
905 			CheckTagType (parentCode, tagCode, tagType, ttAscii);
906 
907 			ParseStringTag (stream,
908 							parentCode,
909 							tagCode,
910 							tagCount,
911 							fMake);
912 
913 			#if qDNGValidate
914 
915 			if (gVerbose)
916 				{
917 
918 				printf ("Make: ");
919 
920 				DumpString (fMake);
921 
922 				printf ("\n");
923 
924 				}
925 
926 			#endif
927 
928 			break;
929 
930 			}
931 
932 		case tcModel:
933 			{
934 
935 			CheckTagType (parentCode, tagCode, tagType, ttAscii);
936 
937 			ParseStringTag (stream,
938 							parentCode,
939 							tagCode,
940 							tagCount,
941 							fModel);
942 
943 			#if qDNGValidate
944 
945 			if (gVerbose)
946 				{
947 
948 				printf ("Model: ");
949 
950 				DumpString (fModel);
951 
952 				printf ("\n");
953 
954 				}
955 
956 			#endif
957 
958 			break;
959 
960 			}
961 
962 		case tcSoftware:
963 			{
964 
965 			CheckTagType (parentCode, tagCode, tagType, ttAscii);
966 
967 			ParseStringTag (stream,
968 							parentCode,
969 							tagCode,
970 							tagCount,
971 							fSoftware);
972 
973 			#if qDNGValidate
974 
975 			if (gVerbose)
976 				{
977 
978 				printf ("Software: ");
979 
980 				DumpString (fSoftware);
981 
982 				printf ("\n");
983 
984 				}
985 
986 			#endif
987 
988 			break;
989 
990 			}
991 
992 		case tcDateTime:
993 			{
994 
995 			uint64 tagPosition = stream.PositionInOriginalFile ();
996 
997 			dng_date_time dt;
998 
999 			if (!ParseDateTimeTag (stream,
1000 								   parentCode,
1001 								   tagCode,
1002 								   tagType,
1003 								   tagCount,
1004 								   dt))
1005 				{
1006 				return false;
1007 				}
1008 
1009 			fDateTime.SetDateTime (dt);
1010 
1011 			fDateTimeStorageInfo = dng_date_time_storage_info (tagPosition,
1012 															   dng_date_time_format_exif);
1013 
1014 			#if qDNGValidate
1015 
1016 			if (gVerbose)
1017 				{
1018 
1019 				printf ("DateTime: ");
1020 
1021 				DumpDateTime (fDateTime.DateTime ());
1022 
1023 				printf ("\n");
1024 
1025 				}
1026 
1027 			#endif
1028 
1029 			break;
1030 
1031 			}
1032 
1033 		case tcArtist:
1034 			{
1035 
1036 			CheckTagType (parentCode, tagCode, tagType, ttAscii);
1037 
1038 			ParseStringTag (stream,
1039 							parentCode,
1040 							tagCode,
1041 							tagCount,
1042 							fArtist);
1043 
1044 			#if qDNGValidate
1045 
1046 			if (gVerbose)
1047 				{
1048 
1049 				printf ("Artist: ");
1050 
1051 				DumpString (fArtist);
1052 
1053 				printf ("\n");
1054 
1055 				}
1056 
1057 			#endif
1058 
1059 			break;
1060 
1061 			}
1062 
1063 		case tcCopyright:
1064 			{
1065 
1066 			CheckTagType (parentCode, tagCode, tagType, ttAscii);
1067 
1068 			ParseDualStringTag (stream,
1069 								parentCode,
1070 								tagCode,
1071 								tagCount,
1072 								fCopyright,
1073 								fCopyright2);
1074 
1075 			#if qDNGValidate
1076 
1077 			if (gVerbose)
1078 				{
1079 
1080 				printf ("Copyright: ");
1081 
1082 				DumpString (fCopyright);
1083 
1084 				if (fCopyright2.Get () [0] != 0)
1085 					{
1086 
1087 					printf (" ");
1088 
1089 					DumpString (fCopyright2);
1090 
1091 					}
1092 
1093 				printf ("\n");
1094 
1095 				}
1096 
1097 			#endif
1098 
1099 			break;
1100 
1101 			}
1102 
1103 		case tcTIFF_EP_StandardID:
1104 			{
1105 
1106 			CheckTagType (parentCode, tagCode, tagType, ttByte);
1107 
1108 			CheckTagCount (parentCode, tagCode, tagCount, 4);
1109 
1110 			uint32 b0 = stream.Get_uint8 ();
1111 			uint32 b1 = stream.Get_uint8 ();
1112 			uint32 b2 = stream.Get_uint8 ();
1113 			uint32 b3 = stream.Get_uint8 ();
1114 
1115 			fTIFF_EP_StandardID = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
1116 
1117 			#if qDNGValidate
1118 
1119 			if (gVerbose)
1120 				{
1121 				printf ("TIFF/EPStandardID: %u.%u.%u.%u\n",
1122 						(unsigned) b0,
1123 						(unsigned) b1,
1124 						(unsigned) b2,
1125 						(unsigned) b3);
1126 				}
1127 
1128 			#endif
1129 
1130 			break;
1131 
1132 			}
1133 
1134 		case tcCameraSerialNumber:
1135 		case tcKodakCameraSerialNumber:		// Kodak uses a very similar tag.
1136 			{
1137 
1138 			CheckTagType (parentCode, tagCode, tagType, ttAscii);
1139 
1140 			ParseStringTag (stream,
1141 							parentCode,
1142 							tagCode,
1143 							tagCount,
1144 							fCameraSerialNumber);
1145 
1146 			#if qDNGValidate
1147 
1148 			if (gVerbose)
1149 				{
1150 
1151 				printf ("%s: ", LookupTagCode (parentCode, tagCode));
1152 
1153 				DumpString (fCameraSerialNumber);
1154 
1155 				printf ("\n");
1156 
1157 				}
1158 
1159 			#endif
1160 
1161 			break;
1162 
1163 			}
1164 
1165 		case tcLensInfo:
1166 			{
1167 
1168 			CheckTagType (parentCode, tagCode, tagType, ttRational);
1169 
1170 			if (!CheckTagCount (parentCode, tagCode, tagCount, 4))
1171 				return false;
1172 
1173 			fLensInfo [0] = stream.TagValue_urational (tagType);
1174 			fLensInfo [1] = stream.TagValue_urational (tagType);
1175 			fLensInfo [2] = stream.TagValue_urational (tagType);
1176 			fLensInfo [3] = stream.TagValue_urational (tagType);
1177 
1178 			// Some third party software wrote zero rather and undefined values
1179 			// for unknown entries.  Work around this bug.
1180 
1181 			for (uint32 j = 0; j < 4; j++)
1182 				{
1183 
1184 				if (fLensInfo [j].IsValid () && fLensInfo [j].As_real64 () <= 0.0)
1185 					{
1186 
1187 					fLensInfo [j] = dng_urational (0, 0);
1188 
1189 					#if qDNGValidate
1190 
1191 					ReportWarning ("Zero entry in LensInfo tag--should be undefined");
1192 
1193 					#endif
1194 
1195 					}
1196 
1197 				}
1198 
1199 			#if qDNGValidate
1200 
1201 			if (gVerbose)
1202 				{
1203 
1204 				printf ("LensInfo: ");
1205 
1206 				real64 minFL = fLensInfo [0].As_real64 ();
1207 				real64 maxFL = fLensInfo [1].As_real64 ();
1208 
1209 				if (minFL == maxFL)
1210 					printf ("%0.1f mm", minFL);
1211 				else
1212 					printf ("%0.1f-%0.1f mm", minFL, maxFL);
1213 
1214 				if (fLensInfo [2].d)
1215 					{
1216 
1217 					real64 minFS = fLensInfo [2].As_real64 ();
1218 					real64 maxFS = fLensInfo [3].As_real64 ();
1219 
1220 					if (minFS == maxFS)
1221 						printf (" f/%0.1f", minFS);
1222 					else
1223 						printf (" f/%0.1f-%0.1f", minFS, maxFS);
1224 
1225 					}
1226 
1227 				printf ("\n");
1228 
1229 				}
1230 
1231 			#endif
1232 
1233 			break;
1234 
1235 			}
1236 
1237 		default:
1238 			{
1239 
1240 			return false;
1241 
1242 			}
1243 
1244 		}
1245 
1246 	return true;
1247 
1248 	}
1249 
1250 /*****************************************************************************/
1251 
1252 // Parses tags that should only appear in IFD 0 or the main image IFD.
1253 
Parse_ifd0_main(dng_stream & stream,dng_shared &,uint32 parentCode,uint32 tagCode,uint32 tagType,uint32 tagCount,uint64)1254 bool dng_exif::Parse_ifd0_main (dng_stream &stream,
1255 		 			   	        dng_shared & /* shared */,
1256 						  	    uint32 parentCode,
1257 						  	    uint32 tagCode,
1258 						  	    uint32 tagType,
1259 						  	    uint32 tagCount,
1260 						  	    uint64 /* tagOffset */)
1261 	{
1262 
1263 	switch (tagCode)
1264 		{
1265 
1266 		case tcFocalPlaneXResolution:
1267 			{
1268 
1269 			CheckTagType (parentCode, tagCode, tagType, ttRational);
1270 
1271 			CheckTagCount (parentCode, tagCode, tagCount, 1);
1272 
1273 			fFocalPlaneXResolution = stream.TagValue_urational (tagType);
1274 
1275 			#if qDNGValidate
1276 
1277 			if (gVerbose)
1278 				{
1279 
1280 				printf ("FocalPlaneXResolution: %0.4f\n",
1281 						fFocalPlaneXResolution.As_real64 ());
1282 
1283 				}
1284 
1285 			#endif
1286 
1287 			break;
1288 
1289 			}
1290 
1291 		case tcFocalPlaneYResolution:
1292 			{
1293 
1294 			CheckTagType (parentCode, tagCode, tagType, ttRational);
1295 
1296 			CheckTagCount (parentCode, tagCode, tagCount, 1);
1297 
1298 			fFocalPlaneYResolution = stream.TagValue_urational (tagType);
1299 
1300 			#if qDNGValidate
1301 
1302 			if (gVerbose)
1303 				{
1304 
1305 				printf ("FocalPlaneYResolution: %0.4f\n",
1306 						fFocalPlaneYResolution.As_real64 ());
1307 
1308 				}
1309 
1310 			#endif
1311 
1312 			break;
1313 
1314 			}
1315 
1316 		case tcFocalPlaneResolutionUnit:
1317 			{
1318 
1319 			CheckTagType (parentCode, tagCode, tagType, ttShort);
1320 
1321 			CheckTagCount (parentCode, tagCode, tagCount, 1);
1322 
1323 			fFocalPlaneResolutionUnit = stream.TagValue_uint32 (tagType);
1324 
1325 			#if qDNGValidate
1326 
1327 			if (gVerbose)
1328 				{
1329 
1330 				printf ("FocalPlaneResolutionUnit: %s\n",
1331 					    LookupResolutionUnit (fFocalPlaneResolutionUnit));
1332 
1333 				}
1334 
1335 			#endif
1336 
1337 			break;
1338 
1339 			}
1340 
1341 		case tcSensingMethod:
1342 			{
1343 
1344 			CheckTagType (parentCode, tagCode, tagType, ttShort);
1345 
1346 			CheckTagCount (parentCode, tagCode, tagCount, 1);
1347 
1348 			fSensingMethod = stream.TagValue_uint32 (tagType);
1349 
1350 			#if qDNGValidate
1351 
1352 			if (gVerbose)
1353 				{
1354 
1355 				printf ("SensingMethod: %s\n",
1356 						LookupSensingMethod (fSensingMethod));
1357 
1358 				}
1359 
1360 			#endif
1361 
1362 			break;
1363 
1364 			}
1365 
1366 		default:
1367 			{
1368 
1369 			return false;
1370 
1371 			}
1372 
1373 		}
1374 
1375 	return true;
1376 
1377 	}
1378 
1379 /*****************************************************************************/
1380 
1381 // Parses tags that should only appear in IFD 0 or EXIF IFD.
1382 
Parse_ifd0_exif(dng_stream & stream,dng_shared &,uint32 parentCode,uint32 tagCode,uint32 tagType,uint32 tagCount,uint64)1383 bool dng_exif::Parse_ifd0_exif (dng_stream &stream,
1384 								dng_shared & /* shared */,
1385 						  	   	uint32 parentCode,
1386 						  	    uint32 tagCode,
1387 						  	    uint32 tagType,
1388 						  	    uint32 tagCount,
1389 						  	    uint64 /* tagOffset */)
1390 	{
1391 
1392 	switch (tagCode)
1393 		{
1394 
1395 		case tcBatteryLevel:
1396 			{
1397 
1398 			CheckTagType (parentCode, tagCode, tagType, ttRational, ttAscii);
1399 
1400 			if (tagType == ttAscii)
1401 				{
1402 
1403 				ParseStringTag (stream,
1404 								parentCode,
1405 								tagCode,
1406 								tagCount,
1407 								fBatteryLevelA);
1408 
1409 				}
1410 
1411 			else
1412 				{
1413 
1414 				CheckTagCount (parentCode, tagCode, tagCount, 1);
1415 
1416 				fBatteryLevelR = stream.TagValue_urational (tagType);
1417 
1418 				}
1419 
1420 			#if qDNGValidate
1421 
1422 			if (gVerbose)
1423 				{
1424 
1425 				printf ("BatteryLevel: ");
1426 
1427 				if (tagType == ttAscii)
1428 					{
1429 
1430 					DumpString (fBatteryLevelA);
1431 
1432 					}
1433 
1434 				else
1435 					{
1436 
1437 					printf ("%0.2f", fBatteryLevelR.As_real64 ());
1438 
1439 					}
1440 
1441 				printf ("\n");
1442 
1443 				}
1444 
1445 			#endif
1446 
1447 			break;
1448 
1449 			}
1450 
1451 		case tcExposureTime:
1452 			{
1453 
1454 			CheckTagType (parentCode, tagCode, tagType, ttRational);
1455 
1456 			CheckTagCount (parentCode, tagCode, tagCount, 1);
1457 
1458 			dng_urational et = stream.TagValue_urational (tagType);
1459 
1460 			SetExposureTime (et.As_real64 (), true);
1461 
1462 			#if qDNGValidate
1463 
1464 			if (gVerbose)
1465 				{
1466 
1467 				printf ("ExposureTime: ");
1468 
1469 				DumpExposureTime (et.As_real64 ());
1470 
1471 				printf ("\n");
1472 
1473 				}
1474 
1475 			if (et.As_real64 () <= 0.0)
1476 				{
1477 
1478 				ReportWarning ("The ExposureTime is <= 0");
1479 
1480 				}
1481 
1482 			#endif
1483 
1484 			break;
1485 
1486 			}
1487 
1488 		case tcFNumber:
1489 			{
1490 
1491 			CheckTagType (parentCode, tagCode, tagType, ttRational);
1492 
1493 			CheckTagCount (parentCode, tagCode, tagCount, 1);
1494 
1495 			dng_urational fs = stream.TagValue_urational (tagType);
1496 
1497 			// Sometimes "unknown" is recorded as zero.
1498 
1499 			if (fs.As_real64 () <= 0.0)
1500 				{
1501 				fs.Clear ();
1502 				}
1503 
1504 			#if qDNGValidate
1505 
1506 			if (gVerbose)
1507 				{
1508 
1509 				printf ("FNumber: f/%0.2f\n",
1510 						fs.As_real64 ());
1511 
1512 				}
1513 
1514 			#endif
1515 
1516 			SetFNumber (fs.As_real64 ());
1517 
1518 			break;
1519 
1520 			}
1521 
1522 		case tcExposureProgram:
1523 			{
1524 
1525 			CheckTagType (parentCode, tagCode, tagType, ttShort);
1526 
1527 			CheckTagCount (parentCode, tagCode, tagCount, 1);
1528 
1529 			fExposureProgram = stream.TagValue_uint32 (tagType);
1530 
1531 			#if qDNGValidate
1532 
1533 			if (gVerbose)
1534 				{
1535 
1536 				printf ("ExposureProgram: %s\n",
1537 						LookupExposureProgram (fExposureProgram));
1538 
1539 				}
1540 
1541 			#endif
1542 
1543 			break;
1544 
1545 			}
1546 
1547 		case tcISOSpeedRatings:
1548 			{
1549 
1550 			CheckTagType (parentCode, tagCode, tagType, ttShort);
1551 
1552 			CheckTagCount (parentCode, tagCode, tagCount, 1, 3);
1553 
1554 			for (uint32 j = 0; j < tagCount && j < 3; j++)
1555 				{
1556 
1557 				fISOSpeedRatings [j] = stream.TagValue_uint32 (tagType);
1558 
1559 				}
1560 
1561 			#if qDNGValidate
1562 
1563 			if (gVerbose)
1564 				{
1565 
1566 				printf ("ISOSpeedRatings:");
1567 
1568 				for (uint32 j = 0; j < tagCount && j < 3; j++)
1569 					{
1570 
1571 					printf (" %u", (unsigned) fISOSpeedRatings [j]);
1572 
1573 					}
1574 
1575 				printf ("\n");
1576 
1577 				}
1578 
1579 			#endif
1580 
1581 			break;
1582 
1583 			}
1584 
1585 		case tcSensitivityType:
1586 			{
1587 
1588 			CheckTagType (parentCode, tagCode, tagType, ttShort);
1589 
1590 			CheckTagCount (parentCode, tagCode, tagCount, 1);
1591 
1592 			fSensitivityType = (uint32) stream.Get_uint16 ();
1593 
1594 			#if qDNGValidate
1595 
1596 			if (gVerbose)
1597 				{
1598 
1599 				printf ("SensitivityType: %s\n",
1600 						LookupSensitivityType (fSensitivityType));
1601 
1602 				}
1603 
1604 			#endif
1605 
1606 			break;
1607 
1608 			}
1609 
1610 		case tcStandardOutputSensitivity:
1611 			{
1612 
1613 			CheckTagType (parentCode, tagCode, tagType, ttLong);
1614 
1615 			CheckTagCount (parentCode, tagCode, tagCount, 1);
1616 
1617 			fStandardOutputSensitivity = stream.TagValue_uint32 (tagType);
1618 
1619 			#if qDNGValidate
1620 
1621 			if (gVerbose)
1622 				{
1623 
1624 				printf ("StandardOutputSensitivity: %u\n",
1625 						(unsigned) fStandardOutputSensitivity);
1626 
1627 				}
1628 
1629 			#endif
1630 
1631 			break;
1632 
1633 			}
1634 
1635 		case tcRecommendedExposureIndex:
1636 			{
1637 
1638 			CheckTagType (parentCode, tagCode, tagType, ttLong);
1639 
1640 			CheckTagCount (parentCode, tagCode, tagCount, 1);
1641 
1642 			fRecommendedExposureIndex = stream.TagValue_uint32 (tagType);
1643 
1644 			#if qDNGValidate
1645 
1646 			if (gVerbose)
1647 				{
1648 
1649 				printf ("RecommendedExposureIndex: %u\n",
1650 						(unsigned) fRecommendedExposureIndex);
1651 
1652 				}
1653 
1654 			#endif
1655 
1656 			break;
1657 
1658 			}
1659 
1660 		case tcISOSpeed:
1661 			{
1662 
1663 			CheckTagType (parentCode, tagCode, tagType, ttLong);
1664 
1665 			CheckTagCount (parentCode, tagCode, tagCount, 1);
1666 
1667 			fISOSpeed = stream.TagValue_uint32 (tagType);
1668 
1669 			#if qDNGValidate
1670 
1671 			if (gVerbose)
1672 				{
1673 
1674 				printf ("ISOSpeed: %u\n",
1675 						(unsigned) fISOSpeed);
1676 
1677 				}
1678 
1679 			#endif
1680 
1681 			break;
1682 
1683 			}
1684 
1685 		case tcISOSpeedLatitudeyyy:
1686 			{
1687 
1688 			CheckTagType (parentCode, tagCode, tagType, ttLong);
1689 
1690 			CheckTagCount (parentCode, tagCode, tagCount, 1);
1691 
1692 			fISOSpeedLatitudeyyy = stream.TagValue_uint32 (tagType);
1693 
1694 			#if qDNGValidate
1695 
1696 			if (gVerbose)
1697 				{
1698 
1699 				printf ("ISOSpeedLatitudeyyy: %u\n",
1700 						(unsigned) fISOSpeedLatitudeyyy);
1701 
1702 				}
1703 
1704 			#endif
1705 
1706 			break;
1707 
1708 			}
1709 
1710 		case tcISOSpeedLatitudezzz:
1711 			{
1712 
1713 			CheckTagType (parentCode, tagCode, tagType, ttLong);
1714 
1715 			CheckTagCount (parentCode, tagCode, tagCount, 1);
1716 
1717 			fISOSpeedLatitudezzz = stream.TagValue_uint32 (tagType);
1718 
1719 			#if qDNGValidate
1720 
1721 			if (gVerbose)
1722 				{
1723 
1724 				printf ("ISOSpeedLatitudezzz: %u\n",
1725 						(unsigned) fISOSpeedLatitudezzz);
1726 
1727 				}
1728 
1729 			#endif
1730 
1731 			break;
1732 
1733 			}
1734 
1735 		case tcTimeZoneOffset:
1736 			{
1737 
1738 			CheckTagType (parentCode, tagCode, tagType, ttSShort);
1739 
1740 			CheckTagCount (parentCode, tagCode, tagCount, 1, 2);
1741 
1742 			dng_time_zone zoneOriginal;
1743 
1744 			zoneOriginal.SetOffsetHours (stream.TagValue_int32 (tagType));
1745 
1746 			fDateTimeOriginal.SetZone (zoneOriginal);
1747 
1748 			#if 0	// MWG: Not filling in time zones unless we are sure.
1749 
1750 			// Note that there is no "TimeZoneOffsetDigitized" field, so
1751 			// we assume the same tone zone as the original.
1752 
1753 			fDateTimeDigitized.SetZone (zoneOriginal);
1754 
1755 			#endif
1756 
1757 			dng_time_zone zoneCurrent;
1758 
1759 			if (tagCount >= 2)
1760 				{
1761 
1762 				zoneCurrent.SetOffsetHours (stream.TagValue_int32 (tagType));
1763 
1764 				fDateTime.SetZone (zoneCurrent);
1765 
1766 				}
1767 
1768 			#if qDNGValidate
1769 
1770 			if (gVerbose)
1771 				{
1772 
1773 				printf ("TimeZoneOffset: DateTimeOriginal = %d",
1774 						(int) zoneOriginal.ExactHourOffset ());
1775 
1776 				if (tagCount >= 2)
1777 					{
1778 
1779 					printf (", DateTime = %d",
1780 							(int) zoneCurrent.ExactHourOffset ());
1781 
1782 					}
1783 
1784 				printf ("\n");
1785 
1786 				}
1787 
1788 			#endif
1789 
1790 			break;
1791 
1792 			}
1793 
1794 		case tcSelfTimerMode:
1795 			{
1796 
1797 			CheckTagType (parentCode, tagCode, tagType, ttShort);
1798 
1799 			CheckTagCount (parentCode, tagCode, tagCount, 1);
1800 
1801 			fSelfTimerMode = stream.TagValue_uint32 (tagType);
1802 
1803 			#if qDNGValidate
1804 
1805 			if (gVerbose)
1806 				{
1807 
1808 				printf ("SelfTimerMode: ");
1809 
1810 				if (fSelfTimerMode)
1811 					{
1812 
1813 					printf ("%u sec", (unsigned) fSelfTimerMode);
1814 
1815 					}
1816 
1817 				else
1818 					{
1819 
1820 					printf ("Off");
1821 
1822 					}
1823 
1824 				printf ("\n");
1825 
1826 				}
1827 
1828 			#endif
1829 
1830 			break;
1831 
1832 			}
1833 
1834 		case tcExifVersion:
1835 			{
1836 
1837 			CheckTagType (parentCode, tagCode, tagType, ttUndefined);
1838 
1839 			CheckTagCount (parentCode, tagCode, tagCount, 4);
1840 
1841 			uint32 b0 = stream.Get_uint8 ();
1842 			uint32 b1 = stream.Get_uint8 ();
1843 			uint32 b2 = stream.Get_uint8 ();
1844 			uint32 b3 = stream.Get_uint8 ();
1845 
1846 			fExifVersion = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
1847 
1848 			#if qDNGValidate
1849 
1850 			if (gVerbose)
1851 				{
1852 
1853 				real64 x = (b0 - '0') * 10.00 +
1854 						   (b1 - '0') *  1.00 +
1855 						   (b2 - '0') *  0.10 +
1856 						   (b3 - '0') *  0.01;
1857 
1858 				printf ("ExifVersion: %0.2f\n", x);
1859 
1860 				}
1861 
1862 			#endif
1863 
1864 			break;
1865 
1866 			}
1867 
1868 		case tcDateTimeOriginal:
1869 			{
1870 
1871 			uint64 tagPosition = stream.PositionInOriginalFile ();
1872 
1873 			dng_date_time dt;
1874 
1875 			if (!ParseDateTimeTag (stream,
1876 								   parentCode,
1877 								   tagCode,
1878 								   tagType,
1879 								   tagCount,
1880 								   dt))
1881 				{
1882 				return false;
1883 				}
1884 
1885 			fDateTimeOriginal.SetDateTime (dt);
1886 
1887 			fDateTimeOriginalStorageInfo = dng_date_time_storage_info (tagPosition,
1888 																	   dng_date_time_format_exif);
1889 
1890 			#if qDNGValidate
1891 
1892 			if (gVerbose)
1893 				{
1894 
1895 				printf ("DateTimeOriginal: ");
1896 
1897 				DumpDateTime (fDateTimeOriginal.DateTime ());
1898 
1899 				printf ("\n");
1900 
1901 				}
1902 
1903 			#endif
1904 
1905 			break;
1906 
1907 			}
1908 
1909 		case tcDateTimeDigitized:
1910 			{
1911 
1912 			uint64 tagPosition = stream.PositionInOriginalFile ();
1913 
1914 			dng_date_time dt;
1915 
1916 			if (!ParseDateTimeTag (stream,
1917 								   parentCode,
1918 								   tagCode,
1919 								   tagType,
1920 								   tagCount,
1921 								   dt))
1922 				{
1923 				return false;
1924 				}
1925 
1926 			fDateTimeDigitized.SetDateTime (dt);
1927 
1928 			fDateTimeDigitizedStorageInfo = dng_date_time_storage_info (tagPosition,
1929 																	    dng_date_time_format_exif);
1930 
1931 			#if qDNGValidate
1932 
1933 			if (gVerbose)
1934 				{
1935 
1936 				printf ("DateTimeDigitized: ");
1937 
1938 				DumpDateTime (fDateTimeDigitized.DateTime ());
1939 
1940 				printf ("\n");
1941 
1942 				}
1943 
1944 			#endif
1945 
1946 			break;
1947 
1948 			}
1949 
1950 		case tcComponentsConfiguration:
1951 			{
1952 
1953 			CheckTagType (parentCode, tagCode, tagType, ttUndefined);
1954 
1955 			CheckTagCount (parentCode, tagCode, tagCount, 4);
1956 
1957 			uint32 b0 = stream.Get_uint8 ();
1958 			uint32 b1 = stream.Get_uint8 ();
1959 			uint32 b2 = stream.Get_uint8 ();
1960 			uint32 b3 = stream.Get_uint8 ();
1961 
1962 			fComponentsConfiguration = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
1963 
1964 			#if qDNGValidate
1965 
1966 			if (gVerbose)
1967 				{
1968 
1969 				printf ("ComponentsConfiguration: %s %s %s %s\n",
1970 						LookupComponent (b0),
1971 						LookupComponent (b1),
1972 						LookupComponent (b2),
1973 						LookupComponent (b3));
1974 
1975 				}
1976 
1977 			#endif
1978 
1979 			break;
1980 
1981 			}
1982 
1983 		case tcCompressedBitsPerPixel:
1984 			{
1985 
1986 			CheckTagType (parentCode, tagCode, tagType, ttRational);
1987 
1988 			CheckTagCount (parentCode, tagCode, tagCount, 1);
1989 
1990 			fCompresssedBitsPerPixel = stream.TagValue_urational (tagType);
1991 
1992 			#if qDNGValidate
1993 
1994 			if (gVerbose)
1995 				{
1996 
1997 				printf ("CompressedBitsPerPixel: %0.2f\n",
1998 						fCompresssedBitsPerPixel.As_real64 ());
1999 
2000 				}
2001 
2002 			#endif
2003 
2004 			break;
2005 
2006 			}
2007 
2008 		case tcShutterSpeedValue:
2009 			{
2010 
2011 			CheckTagType (parentCode, tagCode, tagType, ttSRational);
2012 
2013 			CheckTagCount (parentCode, tagCode, tagCount, 1);
2014 
2015 			dng_srational ss = stream.TagValue_srational (tagType);
2016 
2017 			#if qDNGValidate
2018 
2019 			if (gVerbose)
2020 				{
2021 
2022 				printf ("ShutterSpeedValue: ");
2023 
2024 				real64 x = pow (2.0, -ss.As_real64 ());
2025 
2026 				DumpExposureTime (x);
2027 
2028 				printf ("\n");
2029 
2030 				}
2031 
2032 			// The ExposureTime and ShutterSpeedValue tags should be consistent.
2033 
2034 			if (fExposureTime.IsValid ())
2035 				{
2036 
2037 				real64 et = fExposureTime.As_real64 ();
2038 
2039 				real64 tv1 = -1.0 * log (et) / log (2.0);
2040 
2041 				real64 tv2 = ss.As_real64 ();
2042 
2043 				// Make sure they are within 0.25 APEX values.
2044 
2045 				if (Abs_real64 (tv1 - tv2) > 0.25)
2046 					{
2047 
2048 					ReportWarning ("The ExposureTime and ShutterSpeedValue tags have conflicting values");
2049 
2050 					}
2051 
2052 				}
2053 
2054 			#endif
2055 
2056 			SetShutterSpeedValue (ss.As_real64 ());
2057 
2058 			break;
2059 
2060 			}
2061 
2062 		case tcApertureValue:
2063 			{
2064 
2065 			CheckTagType (parentCode, tagCode, tagType, ttRational);
2066 
2067 			CheckTagCount (parentCode, tagCode, tagCount, 1);
2068 
2069 			dng_urational av = stream.TagValue_urational (tagType);
2070 
2071 			#if qDNGValidate
2072 
2073 			if (gVerbose)
2074 				{
2075 
2076 				real64 x = pow (2.0, 0.5 * av.As_real64 ());
2077 
2078 				printf ("ApertureValue: f/%0.2f\n", x);
2079 
2080 				}
2081 
2082 			// The FNumber and ApertureValue tags should be consistent.
2083 
2084 			if (fFNumber.IsValid () && av.IsValid ())
2085 				{
2086 
2087 				real64 fs = fFNumber.As_real64 ();
2088 
2089 				real64 av1 = FNumberToApertureValue (fs);
2090 
2091 				real64 av2 = av.As_real64 ();
2092 
2093 				if (Abs_real64 (av1 - av2) > 0.25)
2094 					{
2095 
2096 					ReportWarning ("The FNumber and ApertureValue tags have conflicting values");
2097 
2098 					}
2099 
2100 				}
2101 
2102 			#endif
2103 
2104 			SetApertureValue (av.As_real64 ());
2105 
2106 			break;
2107 
2108 			}
2109 
2110 		case tcBrightnessValue:
2111 			{
2112 
2113 			CheckTagType (parentCode, tagCode, tagType, ttSRational);
2114 
2115 			CheckTagCount (parentCode, tagCode, tagCount, 1);
2116 
2117 			fBrightnessValue = stream.TagValue_srational (tagType);
2118 
2119 			#if qDNGValidate
2120 
2121 			if (gVerbose)
2122 				{
2123 
2124 				printf ("BrightnessValue: %0.2f\n",
2125 						fBrightnessValue.As_real64 ());
2126 
2127 				}
2128 
2129 			#endif
2130 
2131 			break;
2132 
2133 			}
2134 
2135 		case tcExposureBiasValue:
2136 			{
2137 
2138 			CheckTagType (parentCode, tagCode, tagType, ttSRational);
2139 
2140 			CheckTagCount (parentCode, tagCode, tagCount, 1);
2141 
2142 			fExposureBiasValue = stream.TagValue_srational (tagType);
2143 
2144 			#if qDNGValidate
2145 
2146 			if (gVerbose)
2147 				{
2148 
2149 				printf ("ExposureBiasValue: %0.2f\n",
2150 						fExposureBiasValue.As_real64 ());
2151 
2152 				}
2153 
2154 			#endif
2155 
2156 			break;
2157 
2158 			}
2159 
2160 		case tcMaxApertureValue:
2161 			{
2162 
2163 			CheckTagType (parentCode, tagCode, tagType, ttRational);
2164 
2165 			CheckTagCount (parentCode, tagCode, tagCount, 1);
2166 
2167 			fMaxApertureValue = stream.TagValue_urational (tagType);
2168 
2169 			#if qDNGValidate
2170 
2171 			if (gVerbose)
2172 				{
2173 
2174 				real64 x = pow (2.0, 0.5 * fMaxApertureValue.As_real64 ());
2175 
2176 				printf ("MaxApertureValue: f/%0.1f\n", x);
2177 
2178 				}
2179 
2180 			#endif
2181 
2182 			break;
2183 
2184 			}
2185 
2186 		case tcSubjectDistance:
2187 			{
2188 
2189 			CheckTagType (parentCode, tagCode, tagType, ttRational);
2190 
2191 			CheckTagCount (parentCode, tagCode, tagCount, 1);
2192 
2193 			fSubjectDistance = stream.TagValue_urational (tagType);
2194 
2195 			fApproxFocusDistance = fSubjectDistance;
2196 
2197 			#if qDNGValidate
2198 
2199 			if (gVerbose)
2200 				{
2201 
2202 				printf ("SubjectDistance: %u/%u\n",
2203 						(unsigned) fSubjectDistance.n,
2204 						(unsigned) fSubjectDistance.d);
2205 
2206 				}
2207 
2208 			#endif
2209 
2210 			break;
2211 
2212 			}
2213 
2214 		case tcMeteringMode:
2215 			{
2216 
2217 			CheckTagType (parentCode, tagCode, tagType, ttShort);
2218 
2219 			CheckTagCount (parentCode, tagCode, tagCount, 1);
2220 
2221 			fMeteringMode = stream.TagValue_uint32 (tagType);
2222 
2223 			#if qDNGValidate
2224 
2225 			if (gVerbose)
2226 				{
2227 
2228 				printf ("MeteringMode: %s\n",
2229 						LookupMeteringMode (fMeteringMode));
2230 
2231 				}
2232 
2233 			#endif
2234 
2235 			break;
2236 
2237 			}
2238 
2239 		case tcLightSource:
2240 			{
2241 
2242 			CheckTagType (parentCode, tagCode, tagType, ttShort);
2243 
2244 			CheckTagCount (parentCode, tagCode, tagCount, 1);
2245 
2246 			fLightSource = stream.TagValue_uint32 (tagType);
2247 
2248 			#if qDNGValidate
2249 
2250 			if (gVerbose)
2251 				{
2252 
2253 				printf ("LightSource: %s\n",
2254 						LookupLightSource (fLightSource));
2255 
2256 				}
2257 
2258 			#endif
2259 
2260 			break;
2261 
2262 			}
2263 
2264 		case tcFlash:
2265 			{
2266 
2267 			CheckTagType (parentCode, tagCode, tagType, ttShort);
2268 
2269 			CheckTagCount (parentCode, tagCode, tagCount, 1);
2270 
2271 			fFlash = stream.TagValue_uint32 (tagType);
2272 
2273 			#if qDNGValidate
2274 
2275 			if (gVerbose)
2276 				{
2277 
2278 				printf ("Flash: %u\n", (unsigned) fFlash);
2279 
2280 				if ((fFlash >> 5) & 1)
2281 					{
2282 					printf ("    No flash function\n");
2283 					}
2284 
2285 				else
2286 					{
2287 
2288 					if (fFlash & 0x1)
2289 						{
2290 
2291 						printf ("    Flash fired\n");
2292 
2293 						switch ((fFlash >> 1) & 0x3)
2294 							{
2295 
2296 							case 2:
2297 								printf ("    Strobe return light not detected\n");
2298 								break;
2299 
2300 							case 3:
2301 								printf ("    Strobe return light detected\n");
2302 								break;
2303 
2304 							}
2305 
2306 						}
2307 
2308 					else
2309 						{
2310 						printf ("    Flash did not fire\n");
2311 						}
2312 
2313 					switch ((fFlash >> 3) & 0x3)
2314 						{
2315 
2316 						case 1:
2317 							printf ("    Compulsory flash firing\n");
2318 							break;
2319 
2320 						case 2:
2321 							printf ("    Compulsory flash suppression\n");
2322 							break;
2323 
2324 						case 3:
2325 							printf ("    Auto mode\n");
2326 							break;
2327 
2328 						}
2329 
2330 					if ((fFlash >> 6) & 1)
2331 						{
2332 						printf ("    Red-eye reduction supported\n");
2333 						}
2334 
2335 					}
2336 
2337 				}
2338 
2339 			#endif
2340 
2341 			break;
2342 
2343 			}
2344 
2345 		case tcFocalLength:
2346 			{
2347 
2348 			CheckTagType (parentCode, tagCode, tagType, ttRational);
2349 
2350 			CheckTagCount (parentCode, tagCode, tagCount, 1);
2351 
2352 			fFocalLength = stream.TagValue_urational (tagType);
2353 
2354 			// Sometimes "unknown" is recorded as zero.
2355 
2356 			if (fFocalLength.As_real64 () <= 0.0)
2357 				{
2358 				fFocalLength.Clear ();
2359 				}
2360 
2361 			#if qDNGValidate
2362 
2363 			if (gVerbose)
2364 				{
2365 
2366 				printf ("FocalLength: %0.1f mm\n",
2367 						fFocalLength.As_real64 ());
2368 
2369 				}
2370 
2371 			#endif
2372 
2373 			break;
2374 
2375 			}
2376 
2377 		case tcImageNumber:
2378 			{
2379 
2380 			CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong);
2381 
2382 			CheckTagCount (parentCode, tagCode, tagCount, 1);
2383 
2384 			fImageNumber = stream.TagValue_uint32 (tagType);
2385 
2386 			#if qDNGValidate
2387 
2388 			if (gVerbose)
2389 				{
2390 				printf ("ImageNumber: %u\n", (unsigned) fImageNumber);
2391 				}
2392 
2393 			#endif
2394 
2395 			break;
2396 
2397 			}
2398 
2399 		case tcExposureIndex:
2400 		case tcExposureIndexExif:
2401 			{
2402 
2403 			CheckTagType (parentCode, tagCode, tagType, ttRational);
2404 
2405 			CheckTagCount (parentCode, tagCode, tagCount, 1);
2406 
2407 			fExposureIndex = stream.TagValue_urational (tagType);
2408 
2409 			#if qDNGValidate
2410 
2411 			if (gVerbose)
2412 				{
2413 
2414 				printf ("%s: ISO %0.1f\n",
2415 						LookupTagCode (parentCode, tagCode),
2416 						fExposureIndex.As_real64 ());
2417 
2418 				}
2419 
2420 			#endif
2421 
2422 			break;
2423 
2424 			}
2425 
2426 		case tcUserComment:
2427 			{
2428 
2429 			CheckTagType (parentCode, tagCode, tagType, ttUndefined);
2430 
2431 			ParseEncodedStringTag (stream,
2432 								   parentCode,
2433 								   tagCode,
2434 				    			   tagCount,
2435 				    			   fUserComment);
2436 
2437 			#if qDNGValidate
2438 
2439 			if (gVerbose)
2440 				{
2441 
2442 				printf ("UserComment: ");
2443 
2444 				DumpString (fUserComment);
2445 
2446 				printf ("\n");
2447 
2448 				}
2449 
2450 			#endif
2451 
2452 			break;
2453 
2454 			}
2455 
2456 		case tcSubsecTime:
2457 			{
2458 
2459 			CheckTagType (parentCode, tagCode, tagType, ttAscii);
2460 
2461 			dng_string subsecs;
2462 
2463 			ParseStringTag (stream,
2464 							parentCode,
2465 							tagCode,
2466 							tagCount,
2467 							subsecs);
2468 
2469 			fDateTime.SetSubseconds (subsecs);
2470 
2471 			#if qDNGValidate
2472 
2473 			if (gVerbose)
2474 				{
2475 
2476 				printf ("SubsecTime: ");
2477 
2478 				DumpString (subsecs);
2479 
2480 				printf ("\n");
2481 
2482 				}
2483 
2484 			#endif
2485 
2486 			break;
2487 
2488 			}
2489 
2490 		case tcSubsecTimeOriginal:
2491 			{
2492 
2493 			CheckTagType (parentCode, tagCode, tagType, ttAscii);
2494 
2495 			dng_string subsecs;
2496 
2497 			ParseStringTag (stream,
2498 							parentCode,
2499 							tagCode,
2500 							tagCount,
2501 							subsecs);
2502 
2503 			fDateTimeOriginal.SetSubseconds (subsecs);
2504 
2505 			#if qDNGValidate
2506 
2507 			if (gVerbose)
2508 				{
2509 
2510 				printf ("SubsecTimeOriginal: ");
2511 
2512 				DumpString (subsecs);
2513 
2514 				printf ("\n");
2515 
2516 				}
2517 
2518 			#endif
2519 
2520 			break;
2521 
2522 			}
2523 
2524 		case tcSubsecTimeDigitized:
2525 			{
2526 
2527 			CheckTagType (parentCode, tagCode, tagType, ttAscii);
2528 
2529 			dng_string subsecs;
2530 
2531 			ParseStringTag (stream,
2532 							parentCode,
2533 							tagCode,
2534 							tagCount,
2535 							subsecs);
2536 
2537 			fDateTimeDigitized.SetSubseconds (subsecs);
2538 
2539 			#if qDNGValidate
2540 
2541 			if (gVerbose)
2542 				{
2543 
2544 				printf ("SubsecTimeDigitized: ");
2545 
2546 				DumpString (subsecs);
2547 
2548 				printf ("\n");
2549 
2550 				}
2551 
2552 			#endif
2553 
2554 			break;
2555 
2556 			}
2557 
2558 		case tcFlashPixVersion:
2559 			{
2560 
2561 			CheckTagType (parentCode, tagCode, tagType, ttUndefined);
2562 
2563 			CheckTagCount (parentCode, tagCode, tagCount, 4);
2564 
2565 			uint32 b0 = stream.Get_uint8 ();
2566 			uint32 b1 = stream.Get_uint8 ();
2567 			uint32 b2 = stream.Get_uint8 ();
2568 			uint32 b3 = stream.Get_uint8 ();
2569 
2570 			fFlashPixVersion = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
2571 
2572 			#if qDNGValidate
2573 
2574 			if (gVerbose)
2575 				{
2576 
2577 				real64 x = (b0 - '0') * 10.00 +
2578 						   (b1 - '0') *  1.00 +
2579 						   (b2 - '0') *  0.10 +
2580 						   (b3 - '0') *  0.01;
2581 
2582 				printf ("FlashPixVersion: %0.2f\n", x);
2583 
2584 				}
2585 
2586 			#endif
2587 
2588 			break;
2589 
2590 			}
2591 
2592 		case tcColorSpace:
2593 			{
2594 
2595 			CheckTagType (parentCode, tagCode, tagType, ttShort);
2596 
2597 			CheckTagCount (parentCode, tagCode, tagCount, 1);
2598 
2599 			fColorSpace = stream.TagValue_uint32 (tagType);
2600 
2601 			#if qDNGValidate
2602 
2603 			if (gVerbose)
2604 				{
2605 
2606 				printf ("ColorSpace: %s\n",
2607 						LookupColorSpace (fColorSpace));
2608 
2609 				}
2610 
2611 			#endif
2612 
2613 			break;
2614 
2615 			}
2616 
2617 		case tcPixelXDimension:
2618 			{
2619 
2620 			CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong);
2621 
2622 			CheckTagCount (parentCode, tagCode, tagCount, 1);
2623 
2624 			fPixelXDimension = stream.TagValue_uint32 (tagType);
2625 
2626 			#if qDNGValidate
2627 
2628 			if (gVerbose)
2629 				{
2630 				printf ("PixelXDimension: %u\n", (unsigned) fPixelXDimension);
2631 				}
2632 
2633 			#endif
2634 
2635 			break;
2636 
2637 			}
2638 
2639 		case tcPixelYDimension:
2640 			{
2641 
2642 			CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong);
2643 
2644 			CheckTagCount (parentCode, tagCode, tagCount, 1);
2645 
2646 			fPixelYDimension = stream.TagValue_uint32 (tagType);
2647 
2648 			#if qDNGValidate
2649 
2650 			if (gVerbose)
2651 				{
2652 				printf ("PixelYDimension: %u\n", (unsigned) fPixelYDimension);
2653 				}
2654 
2655 			#endif
2656 
2657 			break;
2658 
2659 			}
2660 
2661 		case tcFocalPlaneXResolutionExif:
2662 			{
2663 
2664 			CheckTagType (parentCode, tagCode, tagType, ttRational);
2665 
2666 			CheckTagCount (parentCode, tagCode, tagCount, 1);
2667 
2668 			fFocalPlaneXResolution = stream.TagValue_urational (tagType);
2669 
2670 			#if qDNGValidate
2671 
2672 			if (gVerbose)
2673 				{
2674 
2675 				printf ("FocalPlaneXResolutionExif: %0.4f\n",
2676 						fFocalPlaneXResolution.As_real64 ());
2677 
2678 				}
2679 
2680 			#endif
2681 
2682 			break;
2683 
2684 			}
2685 
2686 		case tcFocalPlaneYResolutionExif:
2687 			{
2688 
2689 			CheckTagType (parentCode, tagCode, tagType, ttRational);
2690 
2691 			CheckTagCount (parentCode, tagCode, tagCount, 1);
2692 
2693 			fFocalPlaneYResolution = stream.TagValue_urational (tagType);
2694 
2695 			#if qDNGValidate
2696 
2697 			if (gVerbose)
2698 				{
2699 
2700 				printf ("FocalPlaneYResolutionExif: %0.4f\n",
2701 						fFocalPlaneYResolution.As_real64 ());
2702 
2703 				}
2704 
2705 			#endif
2706 
2707 			break;
2708 
2709 			}
2710 
2711 		case tcFocalPlaneResolutionUnitExif:
2712 			{
2713 
2714 			CheckTagType (parentCode, tagCode, tagType, ttShort);
2715 
2716 			CheckTagCount (parentCode, tagCode, tagCount, 1);
2717 
2718 			fFocalPlaneResolutionUnit = stream.TagValue_uint32 (tagType);
2719 
2720 			#if qDNGValidate
2721 
2722 			if (gVerbose)
2723 				{
2724 
2725 				printf ("FocalPlaneResolutionUnitExif: %s\n",
2726 					    LookupResolutionUnit (fFocalPlaneResolutionUnit));
2727 
2728 				}
2729 
2730 			#endif
2731 
2732 			break;
2733 
2734 			}
2735 
2736 		case tcSensingMethodExif:
2737 			{
2738 
2739 			CheckTagType (parentCode, tagCode, tagType, ttShort);
2740 
2741 			CheckTagCount (parentCode, tagCode, tagCount, 1);
2742 
2743 			fSensingMethod = stream.TagValue_uint32 (tagType);
2744 
2745 			#if qDNGValidate
2746 
2747 			if (gVerbose)
2748 				{
2749 
2750 				printf ("SensingMethodExif: %s\n",
2751 						LookupSensingMethod (fSensingMethod));
2752 
2753 				}
2754 
2755 			#endif
2756 
2757 			break;
2758 
2759 			}
2760 
2761 		case tcFileSource:
2762 			{
2763 
2764 			CheckTagType (parentCode, tagCode, tagType, ttUndefined);
2765 
2766 			CheckTagCount (parentCode, tagCode, tagCount, 1);
2767 
2768 			fFileSource = stream.Get_uint8 ();
2769 
2770 			#if qDNGValidate
2771 
2772 			if (gVerbose)
2773 				{
2774 
2775 				printf ("FileSource: %s\n",
2776 						LookupFileSource (fFileSource));
2777 
2778 				}
2779 
2780 			#endif
2781 
2782 			break;
2783 
2784 			}
2785 
2786 		case tcSceneType:
2787 			{
2788 
2789 			CheckTagType (parentCode, tagCode, tagType, ttUndefined);
2790 
2791 			CheckTagCount (parentCode, tagCode, tagCount, 1);
2792 
2793 			fSceneType = stream.Get_uint8 ();
2794 
2795 			#if qDNGValidate
2796 
2797 			if (gVerbose)
2798 				{
2799 
2800 				printf ("SceneType: %s\n",
2801 						LookupSceneType (fSceneType));
2802 
2803 				}
2804 
2805 			#endif
2806 
2807 			break;
2808 
2809 			}
2810 
2811 		case tcCFAPatternExif:
2812 			{
2813 
2814 			CheckTagType (parentCode, tagCode, tagType, ttUndefined);
2815 
2816 			if (tagCount <= 4)
2817 				{
2818 				return false;
2819 				}
2820 
2821 			uint32 cols = stream.Get_uint16 ();
2822 			uint32 rows = stream.Get_uint16 ();
2823 
2824 			if (tagCount != 4 + cols * rows)
2825 				{
2826 				return false;
2827 				}
2828 
2829 			if (cols < 1 || cols > kMaxCFAPattern ||
2830 				rows < 1 || rows > kMaxCFAPattern)
2831 				{
2832 				return false;
2833 				}
2834 
2835 			fCFARepeatPatternCols = cols;
2836 			fCFARepeatPatternRows = rows;
2837 
2838 			// Note that the Exif spec stores this array in a different
2839 			// scan order than the TIFF-EP spec.
2840 
2841 			for (uint32 j = 0; j < fCFARepeatPatternCols; j++)
2842 				for (uint32 k = 0; k < fCFARepeatPatternRows; k++)
2843 					{
2844 
2845 					fCFAPattern [k] [j] = stream.Get_uint8 ();
2846 
2847 					}
2848 
2849 			#if qDNGValidate
2850 
2851 			if (gVerbose)
2852 				{
2853 
2854 				printf ("CFAPatternExif:\n");
2855 
2856 				for (uint32 j = 0; j < fCFARepeatPatternRows; j++)
2857 					{
2858 
2859 					int32 spaces = 4;
2860 
2861 					for (uint32 k = 0; k < fCFARepeatPatternCols; k++)
2862 						{
2863 
2864 						while (spaces-- > 0)
2865 							{
2866 							printf (" ");
2867 							}
2868 
2869 						const char *name = LookupCFAColor (fCFAPattern [j] [k]);
2870 
2871 						spaces = 9 - (int32) strlen (name);
2872 
2873 						printf ("%s", name);
2874 
2875 						}
2876 
2877 					printf ("\n");
2878 
2879 					}
2880 
2881 				}
2882 
2883 			#endif
2884 
2885 			break;
2886 
2887 			}
2888 
2889 		case tcCustomRendered:
2890 			{
2891 
2892 			CheckTagType (parentCode, tagCode, tagType, ttShort);
2893 
2894 			CheckTagCount (parentCode, tagCode, tagCount, 1);
2895 
2896 			fCustomRendered = stream.TagValue_uint32 (tagType);
2897 
2898 			#if qDNGValidate
2899 
2900 			if (gVerbose)
2901 				{
2902 
2903 				printf ("CustomRendered: %s\n",
2904 						LookupCustomRendered (fCustomRendered));
2905 
2906 				}
2907 
2908 			#endif
2909 
2910 			break;
2911 
2912 			}
2913 
2914 		case tcExposureMode:
2915 			{
2916 
2917 			CheckTagType (parentCode, tagCode, tagType, ttShort);
2918 
2919 			CheckTagCount (parentCode, tagCode, tagCount, 1);
2920 
2921 			fExposureMode = stream.TagValue_uint32 (tagType);
2922 
2923 			#if qDNGValidate
2924 
2925 			if (gVerbose)
2926 				{
2927 
2928 				printf ("ExposureMode: %s\n",
2929 						LookupExposureMode (fExposureMode));
2930 
2931 				}
2932 
2933 			#endif
2934 
2935 			break;
2936 
2937 			}
2938 
2939 		case tcWhiteBalance:
2940 			{
2941 
2942 			CheckTagType (parentCode, tagCode, tagType, ttShort);
2943 
2944 			CheckTagCount (parentCode, tagCode, tagCount, 1);
2945 
2946 			fWhiteBalance = stream.TagValue_uint32 (tagType);
2947 
2948 			#if qDNGValidate
2949 
2950 			if (gVerbose)
2951 				{
2952 
2953 				printf ("WhiteBalance: %s\n",
2954 						LookupWhiteBalance (fWhiteBalance));
2955 
2956 				}
2957 
2958 			#endif
2959 
2960 			break;
2961 
2962 			}
2963 
2964 		case tcDigitalZoomRatio:
2965 			{
2966 
2967 			CheckTagType (parentCode, tagCode, tagType, ttRational);
2968 
2969 			CheckTagCount (parentCode, tagCode, tagCount, 1);
2970 
2971 			fDigitalZoomRatio = stream.TagValue_urational (tagType);
2972 
2973 			#if qDNGValidate
2974 
2975 			if (gVerbose)
2976 				{
2977 
2978 				printf ("DigitalZoomRatio: ");
2979 
2980 				if (fDigitalZoomRatio.n == 0 ||
2981 					fDigitalZoomRatio.d == 0)
2982 					{
2983 
2984 					printf ("Not used\n");
2985 
2986 					}
2987 
2988 				else
2989 					{
2990 
2991 					printf ("%0.2f\n", fDigitalZoomRatio.As_real64 ());
2992 
2993 					}
2994 
2995 				}
2996 
2997 			#endif
2998 
2999 			break;
3000 
3001 			}
3002 
3003 		case tcFocalLengthIn35mmFilm:
3004 			{
3005 
3006 			CheckTagType (parentCode, tagCode, tagType, ttShort);
3007 
3008 			CheckTagCount (parentCode, tagCode, tagCount, 1);
3009 
3010 			fFocalLengthIn35mmFilm = stream.TagValue_uint32 (tagType);
3011 
3012 			#if qDNGValidate
3013 
3014 			if (gVerbose)
3015 				{
3016 
3017 				printf ("FocalLengthIn35mmFilm: %u mm\n",
3018 						(unsigned) fFocalLengthIn35mmFilm);
3019 
3020 				}
3021 
3022 			#endif
3023 
3024 			break;
3025 
3026 			}
3027 
3028 		case tcSceneCaptureType:
3029 			{
3030 
3031 			CheckTagType (parentCode, tagCode, tagType, ttShort);
3032 
3033 			CheckTagCount (parentCode, tagCode, tagCount, 1);
3034 
3035 			fSceneCaptureType = stream.TagValue_uint32 (tagType);
3036 
3037 			#if qDNGValidate
3038 
3039 			if (gVerbose)
3040 				{
3041 
3042 				printf ("SceneCaptureType: %s\n",
3043 						LookupSceneCaptureType (fSceneCaptureType));
3044 
3045 				}
3046 
3047 			#endif
3048 
3049 			break;
3050 
3051 			}
3052 
3053 		case tcGainControl:
3054 			{
3055 
3056 			CheckTagType (parentCode, tagCode, tagType, ttShort);
3057 
3058 			CheckTagCount (parentCode, tagCode, tagCount, 1);
3059 
3060 			fGainControl = stream.TagValue_uint32 (tagType);
3061 
3062 			#if qDNGValidate
3063 
3064 			if (gVerbose)
3065 				{
3066 
3067 				printf ("GainControl: %s\n",
3068 						LookupGainControl (fGainControl));
3069 
3070 				}
3071 
3072 			#endif
3073 
3074 			break;
3075 
3076 			}
3077 
3078 		case tcContrast:
3079 			{
3080 
3081 			CheckTagType (parentCode, tagCode, tagType, ttShort);
3082 
3083 			CheckTagCount (parentCode, tagCode, tagCount, 1);
3084 
3085 			fContrast = stream.TagValue_uint32 (tagType);
3086 
3087 			#if qDNGValidate
3088 
3089 			if (gVerbose)
3090 				{
3091 
3092 				printf ("Contrast: %s\n",
3093 						LookupContrast (fContrast));
3094 
3095 				}
3096 
3097 			#endif
3098 
3099 			break;
3100 
3101 			}
3102 
3103 		case tcSaturation:
3104 			{
3105 
3106 			CheckTagType (parentCode, tagCode, tagType, ttShort);
3107 
3108 			CheckTagCount (parentCode, tagCode, tagCount, 1);
3109 
3110 			fSaturation = stream.TagValue_uint32 (tagType);
3111 
3112 			#if qDNGValidate
3113 
3114 			if (gVerbose)
3115 				{
3116 
3117 				printf ("Saturation: %s\n",
3118 						LookupSaturation (fSaturation));
3119 
3120 				}
3121 
3122 			#endif
3123 
3124 			break;
3125 
3126 			}
3127 
3128 		case tcSharpness:
3129 			{
3130 
3131 			CheckTagType (parentCode, tagCode, tagType, ttShort);
3132 
3133 			CheckTagCount (parentCode, tagCode, tagCount, 1);
3134 
3135 			fSharpness = stream.TagValue_uint32 (tagType);
3136 
3137 			#if qDNGValidate
3138 
3139 			if (gVerbose)
3140 				{
3141 
3142 				printf ("Sharpness: %s\n",
3143 						LookupSharpness (fSharpness));
3144 
3145 				}
3146 
3147 			#endif
3148 
3149 			break;
3150 
3151 			}
3152 
3153 		case tcSubjectDistanceRange:
3154 			{
3155 
3156 			CheckTagType (parentCode, tagCode, tagType, ttShort);
3157 
3158 			CheckTagCount (parentCode, tagCode, tagCount, 1);
3159 
3160 			fSubjectDistanceRange = stream.TagValue_uint32 (tagType);
3161 
3162 			#if qDNGValidate
3163 
3164 			if (gVerbose)
3165 				{
3166 
3167 				printf ("SubjectDistanceRange: %s\n",
3168 						LookupSubjectDistanceRange (fSubjectDistanceRange));
3169 
3170 				}
3171 
3172 			#endif
3173 
3174 			break;
3175 
3176 			}
3177 
3178 		case tcSubjectArea:
3179 		case tcSubjectLocation:
3180 			{
3181 
3182 			CheckTagType (parentCode, tagCode, tagType, ttShort);
3183 
3184 			if (!CheckTagCount (parentCode, tagCode, tagCount, 2, 4))
3185 				{
3186 				return false;
3187 				}
3188 
3189 			if (tagCode == tcSubjectLocation)
3190 				{
3191 				CheckTagCount (parentCode, tagCode, tagCount, 2);
3192 				}
3193 
3194 			fSubjectAreaCount = tagCount;
3195 
3196 			for (uint32 j = 0; j < tagCount; j++)
3197 				{
3198 
3199 				fSubjectArea [j] = stream.TagValue_uint32 (tagType);
3200 
3201 				}
3202 
3203 			#if qDNGValidate
3204 
3205 			if (gVerbose)
3206 				{
3207 
3208 				printf ("%s:", LookupTagCode (parentCode, tagCode));
3209 
3210 				for (uint32 j = 0; j < fSubjectAreaCount; j++)
3211 					{
3212 
3213 					printf (" %u", (unsigned) fSubjectArea [j]);
3214 
3215 					}
3216 
3217 				printf ("\n");
3218 
3219 				}
3220 
3221 			#endif
3222 
3223 			break;
3224 
3225 			}
3226 
3227 		case tcGamma:
3228 			{
3229 
3230 			CheckTagType (parentCode, tagCode, tagType, ttRational);
3231 
3232 			CheckTagCount (parentCode, tagCode, tagCount, 1);
3233 
3234 			fGamma = stream.TagValue_urational (tagType);
3235 
3236 			#if qDNGValidate
3237 
3238 			if (gVerbose)
3239 				{
3240 
3241 				printf ("Gamma: %0.2f\n",
3242 						fGamma.As_real64 ());
3243 
3244 				}
3245 
3246 			#endif
3247 
3248 			break;
3249 
3250 			}
3251 
3252 		case tcImageUniqueID:
3253 			{
3254 
3255 			if (!CheckTagType (parentCode, tagCode, tagType, ttAscii))
3256 				return false;
3257 
3258 			if (!CheckTagCount (parentCode, tagCode, tagCount, 33))
3259 				return false;
3260 
3261 			dng_string s;
3262 
3263 			ParseStringTag (stream,
3264 							parentCode,
3265 							tagCode,
3266 							tagCount,
3267 							s);
3268 
3269 			if (s.Length () != 32)
3270 				return false;
3271 
3272 			dng_fingerprint f;
3273 
3274 			for (uint32 j = 0; j < 32; j++)
3275 				{
3276 
3277 				char c = ForceUppercase (s.Get () [j]);
3278 
3279 				uint32 digit;
3280 
3281 				if (c >= '0' && c <= '9')
3282 					{
3283 					digit = c - '0';
3284 					}
3285 
3286 				else if (c >= 'A' && c <= 'F')
3287 					{
3288 					digit = c - 'A' + 10;
3289 					}
3290 
3291 				else
3292 					return false;
3293 
3294 				f.data [j >> 1] *= 16;
3295 				f.data [j >> 1] += (uint8) digit;
3296 
3297 				}
3298 
3299 			fImageUniqueID = f;
3300 
3301 			#if qDNGValidate
3302 
3303 			if (gVerbose)
3304 				{
3305 
3306 				printf ("ImageUniqueID: ");
3307 
3308 				DumpFingerprint (fImageUniqueID);
3309 
3310 				printf ("\n");
3311 
3312 				}
3313 
3314 			#endif
3315 
3316 			break;
3317 
3318 			}
3319 
3320 		case tcCameraOwnerNameExif:
3321 			{
3322 
3323 			CheckTagType (parentCode, tagCode, tagType, ttAscii);
3324 
3325 			ParseStringTag (stream,
3326 							parentCode,
3327 							tagCode,
3328 							tagCount,
3329 							fOwnerName);
3330 
3331 			#if qDNGValidate
3332 
3333 			if (gVerbose)
3334 				{
3335 
3336 				printf ("CameraOwnerName: ");
3337 
3338 				DumpString (fOwnerName);
3339 
3340 				printf ("\n");
3341 
3342 				}
3343 
3344 			#endif
3345 
3346 			break;
3347 
3348 			}
3349 
3350 		case tcCameraSerialNumberExif:
3351 			{
3352 
3353 			CheckTagType (parentCode, tagCode, tagType, ttAscii);
3354 
3355 			ParseStringTag (stream,
3356 							parentCode,
3357 							tagCode,
3358 							tagCount,
3359 							fCameraSerialNumber);
3360 
3361 			#if qDNGValidate
3362 
3363 			if (gVerbose)
3364 				{
3365 
3366 				printf ("%s: ", LookupTagCode (parentCode, tagCode));
3367 
3368 				DumpString (fCameraSerialNumber);
3369 
3370 				printf ("\n");
3371 
3372 				}
3373 
3374 			#endif
3375 
3376 			break;
3377 
3378 			}
3379 
3380 		case tcLensSpecificationExif:
3381 			{
3382 
3383 			CheckTagType (parentCode, tagCode, tagType, ttRational);
3384 
3385 			if (!CheckTagCount (parentCode, tagCode, tagCount, 4))
3386 				return false;
3387 
3388 			fLensInfo [0] = stream.TagValue_urational (tagType);
3389 			fLensInfo [1] = stream.TagValue_urational (tagType);
3390 			fLensInfo [2] = stream.TagValue_urational (tagType);
3391 			fLensInfo [3] = stream.TagValue_urational (tagType);
3392 
3393 			// Some third party software wrote zero rather than undefined values for
3394 			// unknown entries. Work around this bug.
3395 
3396 			for (uint32 j = 0; j < 4; j++)
3397 				{
3398 
3399 				if (fLensInfo [j].IsValid () && fLensInfo [j].As_real64 () <= 0.0)
3400 					{
3401 
3402 					fLensInfo [j] = dng_urational (0, 0);
3403 
3404 					#if qDNGValidate
3405 
3406 					ReportWarning ("Zero entry in LensSpecification tag--should be undefined");
3407 
3408 					#endif
3409 
3410 					}
3411 
3412 				}
3413 
3414 			#if qDNGValidate
3415 
3416 			if (gVerbose)
3417 				{
3418 
3419 				printf ("LensSpecificationExif: ");
3420 
3421 				real64 minFL = fLensInfo [0].As_real64 ();
3422 				real64 maxFL = fLensInfo [1].As_real64 ();
3423 
3424 				if (minFL == maxFL)
3425 					printf ("%0.1f mm", minFL);
3426 				else
3427 					printf ("%0.1f-%0.1f mm", minFL, maxFL);
3428 
3429 				if (fLensInfo [2].d)
3430 					{
3431 
3432 					real64 minFS = fLensInfo [2].As_real64 ();
3433 					real64 maxFS = fLensInfo [3].As_real64 ();
3434 
3435 					if (minFS == maxFS)
3436 						printf (" f/%0.1f", minFS);
3437 					else
3438 						printf (" f/%0.1f-%0.1f", minFS, maxFS);
3439 
3440 					}
3441 
3442 				printf ("\n");
3443 
3444 				}
3445 
3446 			#endif
3447 
3448 			break;
3449 
3450 			}
3451 
3452 		case tcLensMakeExif:
3453 			{
3454 
3455 			CheckTagType (parentCode, tagCode, tagType, ttAscii);
3456 
3457 			ParseStringTag (stream,
3458 							parentCode,
3459 							tagCode,
3460 							tagCount,
3461 							fLensMake);
3462 
3463 			#if qDNGValidate
3464 
3465 			if (gVerbose)
3466 				{
3467 
3468 				printf ("%s: ", LookupTagCode (parentCode, tagCode));
3469 
3470 				DumpString (fLensMake);
3471 
3472 				printf ("\n");
3473 
3474 				}
3475 
3476 			#endif
3477 
3478 			break;
3479 
3480 			}
3481 
3482 		case tcLensModelExif:
3483 			{
3484 
3485 			CheckTagType (parentCode, tagCode, tagType, ttAscii);
3486 
3487 			ParseStringTag (stream,
3488 							parentCode,
3489 							tagCode,
3490 							tagCount,
3491 							fLensName);
3492 
3493 			fLensNameWasReadFromExif = fLensName.NotEmpty ();
3494 
3495 			#if qDNGValidate
3496 
3497 			if (gVerbose)
3498 				{
3499 
3500 				printf ("%s: ", LookupTagCode (parentCode, tagCode));
3501 
3502 				DumpString (fLensName);
3503 
3504 				printf ("\n");
3505 
3506 				}
3507 
3508 			#endif
3509 
3510 			break;
3511 
3512 			}
3513 
3514 		case tcLensSerialNumberExif:
3515 			{
3516 
3517 			CheckTagType (parentCode, tagCode, tagType, ttAscii);
3518 
3519 			ParseStringTag (stream,
3520 							parentCode,
3521 							tagCode,
3522 							tagCount,
3523 							fLensSerialNumber);
3524 
3525 			#if qDNGValidate
3526 
3527 			if (gVerbose)
3528 				{
3529 
3530 				printf ("%s: ", LookupTagCode (parentCode, tagCode));
3531 
3532 				DumpString (fLensSerialNumber);
3533 
3534 				printf ("\n");
3535 
3536 				}
3537 
3538 			#endif
3539 
3540 			break;
3541 
3542 			}
3543 
3544 		default:
3545 			{
3546 
3547 			return false;
3548 
3549 			}
3550 
3551 		}
3552 
3553 	return true;
3554 
3555 	}
3556 
3557 /*****************************************************************************/
3558 
3559 // Parses tags that should only appear in GPS IFD
3560 
Parse_gps(dng_stream & stream,dng_shared &,uint32 parentCode,uint32 tagCode,uint32 tagType,uint32 tagCount,uint64)3561 bool dng_exif::Parse_gps (dng_stream &stream,
3562 						  dng_shared & /* shared */,
3563 						  uint32 parentCode,
3564 						  uint32 tagCode,
3565 						  uint32 tagType,
3566 						  uint32 tagCount,
3567 						  uint64 /* tagOffset */)
3568 	{
3569 
3570 	switch (tagCode)
3571 		{
3572 
3573 		case tcGPSVersionID:
3574 			{
3575 
3576 			CheckTagType (parentCode, tagCode, tagType, ttByte);
3577 
3578 			CheckTagCount (parentCode, tagCode, tagCount, 4);
3579 
3580 			uint32 b0 = stream.Get_uint8 ();
3581 			uint32 b1 = stream.Get_uint8 ();
3582 			uint32 b2 = stream.Get_uint8 ();
3583 			uint32 b3 = stream.Get_uint8 ();
3584 
3585 			fGPSVersionID = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
3586 
3587 			#if qDNGValidate
3588 
3589 			if (gVerbose)
3590 				{
3591 				printf ("GPSVersionID: %u.%u.%u.%u\n",
3592 						(unsigned) b0,
3593 						(unsigned) b1,
3594 						(unsigned) b2,
3595 						(unsigned) b3);
3596 				}
3597 
3598 			#endif
3599 
3600 			break;
3601 
3602 			}
3603 
3604 		case tcGPSLatitudeRef:
3605 		case tcGPSLongitudeRef:
3606 		case tcGPSSatellites:
3607 		case tcGPSStatus:
3608 		case tcGPSMeasureMode:
3609 		case tcGPSSpeedRef:
3610 		case tcGPSTrackRef:
3611 		case tcGPSImgDirectionRef:
3612 		case tcGPSMapDatum:
3613 		case tcGPSDestLatitudeRef:
3614 		case tcGPSDestLongitudeRef:
3615 		case tcGPSDestBearingRef:
3616 		case tcGPSDestDistanceRef:
3617 		case tcGPSDateStamp:
3618 			{
3619 
3620 			if (!CheckTagType (parentCode, tagCode, tagType, ttAscii))
3621 				return false;
3622 
3623 			dng_string *s;
3624 
3625 			switch (tagCode)
3626 				{
3627 
3628 				case tcGPSLatitudeRef:
3629 					s = &fGPSLatitudeRef;
3630 					break;
3631 
3632 				case tcGPSLongitudeRef:
3633 					s = &fGPSLongitudeRef;
3634 					break;
3635 
3636 				case tcGPSSatellites:
3637 					s = &fGPSSatellites;
3638 					break;
3639 
3640 				case tcGPSStatus:
3641 					s = &fGPSStatus;
3642 					break;
3643 
3644 				case tcGPSMeasureMode:
3645 					s = &fGPSMeasureMode;
3646 					break;
3647 
3648 				case tcGPSSpeedRef:
3649 					s = &fGPSSpeedRef;
3650 					break;
3651 
3652 				case tcGPSTrackRef:
3653 					s = &fGPSTrackRef;
3654 					break;
3655 
3656 				case tcGPSImgDirectionRef:
3657 					s = &fGPSImgDirectionRef;
3658 					break;
3659 
3660 				case tcGPSMapDatum:
3661 					s = &fGPSMapDatum;
3662 					break;
3663 
3664 				case tcGPSDestLatitudeRef:
3665 					s = &fGPSDestLatitudeRef;
3666 					break;
3667 
3668 				case tcGPSDestLongitudeRef:
3669 					s = &fGPSDestLongitudeRef;
3670 					break;
3671 
3672 				case tcGPSDestBearingRef:
3673 					s = &fGPSDestBearingRef;
3674 					break;
3675 
3676 				case tcGPSDestDistanceRef:
3677 					s = &fGPSDestDistanceRef;
3678 					break;
3679 
3680 				case tcGPSDateStamp:
3681 					s = &fGPSDateStamp;
3682 					break;
3683 
3684 				default:
3685 					return false;
3686 
3687 				}
3688 
3689 			ParseStringTag (stream,
3690 							parentCode,
3691 							tagCode,
3692 							tagCount,
3693 							*s);
3694 
3695 			#if qDNGValidate
3696 
3697 			if (gVerbose)
3698 				{
3699 
3700 				printf ("%s: ", LookupTagCode (parentCode, tagCode));
3701 
3702 				DumpString (*s);
3703 
3704 				printf ("\n");
3705 
3706 				}
3707 
3708 			#endif
3709 
3710 			break;
3711 
3712 			}
3713 
3714 		case tcGPSLatitude:
3715 		case tcGPSLongitude:
3716 		case tcGPSTimeStamp:
3717 		case tcGPSDestLatitude:
3718 		case tcGPSDestLongitude:
3719 			{
3720 
3721 			if (!CheckTagType (parentCode, tagCode, tagType, ttRational))
3722 				return false;
3723 
3724 			if (!CheckTagCount (parentCode, tagCode, tagCount, 3))
3725 				return false;
3726 
3727 			dng_urational *u;
3728 
3729 			switch (tagCode)
3730 				{
3731 
3732 				case tcGPSLatitude:
3733 					u = fGPSLatitude;
3734 					break;
3735 
3736 				case tcGPSLongitude:
3737 					u = fGPSLongitude;
3738 					break;
3739 
3740 				case tcGPSTimeStamp:
3741 					u = fGPSTimeStamp;
3742 					break;
3743 
3744 				case tcGPSDestLatitude:
3745 					u = fGPSDestLatitude;
3746 					break;
3747 
3748 				case tcGPSDestLongitude:
3749 					u = fGPSDestLongitude;
3750 					break;
3751 
3752 				default:
3753 					return false;
3754 
3755 				}
3756 
3757 			u [0] = stream.TagValue_urational (tagType);
3758 			u [1] = stream.TagValue_urational (tagType);
3759 			u [2] = stream.TagValue_urational (tagType);
3760 
3761 			#if qDNGValidate
3762 
3763 			if (gVerbose)
3764 				{
3765 
3766 				printf ("%s:", LookupTagCode (parentCode, tagCode));
3767 
3768 				for (uint32 j = 0; j < 3; j++)
3769 					{
3770 
3771 					if (u [j].d == 0)
3772 						printf (" -");
3773 
3774 					else
3775 						printf (" %0.4f", u [j].As_real64 ());
3776 
3777 					}
3778 
3779 				printf ("\n");
3780 
3781 				}
3782 
3783 			#endif
3784 
3785 			break;
3786 
3787 			}
3788 
3789 		case tcGPSAltitudeRef:
3790 			{
3791 
3792 			CheckTagType (parentCode, tagCode, tagType, ttByte);
3793 
3794 			CheckTagCount (parentCode, tagCode, tagCount, 1);
3795 
3796 			fGPSAltitudeRef = stream.TagValue_uint32 (tagType);
3797 
3798 			#if qDNGValidate
3799 
3800 			if (gVerbose)
3801 				{
3802 
3803 				printf ("GPSAltitudeRef: ");
3804 
3805 				switch (fGPSAltitudeRef)
3806 					{
3807 
3808 					case 0:
3809 						printf ("Sea level");
3810 						break;
3811 
3812 					case 1:
3813 						printf ("Sea level reference (negative value)");
3814 						break;
3815 
3816 					default:
3817 						printf ("%u", (unsigned) fGPSAltitudeRef);
3818 						break;
3819 
3820 					}
3821 
3822 				printf ("\n");
3823 
3824 				}
3825 
3826 			#endif
3827 
3828 			break;
3829 
3830 			}
3831 
3832 		case tcGPSAltitude:
3833 		case tcGPSDOP:
3834 		case tcGPSSpeed:
3835 		case tcGPSTrack:
3836 		case tcGPSImgDirection:
3837 		case tcGPSDestBearing:
3838 		case tcGPSDestDistance:
3839 		case tcGPSHPositioningError:
3840 			{
3841 
3842 			if (!CheckTagType (parentCode, tagCode, tagType, ttRational))
3843 				return false;
3844 
3845 			CheckTagCount (parentCode, tagCode, tagCount, 1);
3846 
3847 			dng_urational *u;
3848 
3849 			switch (tagCode)
3850 				{
3851 
3852 				case tcGPSAltitude:
3853 					u = &fGPSAltitude;
3854 					break;
3855 
3856 				case tcGPSDOP:
3857 					u = &fGPSDOP;
3858 					break;
3859 
3860 				case tcGPSSpeed:
3861 					u = &fGPSSpeed;
3862 					break;
3863 
3864 				case tcGPSTrack:
3865 					u = &fGPSTrack;
3866 					break;
3867 
3868 				case tcGPSImgDirection:
3869 					u = &fGPSImgDirection;
3870 					break;
3871 
3872 				case tcGPSDestBearing:
3873 					u = &fGPSDestBearing;
3874 					break;
3875 
3876 				case tcGPSDestDistance:
3877 					u = &fGPSDestDistance;
3878 					break;
3879 
3880 				case tcGPSHPositioningError:
3881 					u = &fGPSHPositioningError;
3882 					break;
3883 
3884 				default:
3885 					return false;
3886 
3887 				}
3888 
3889 			*u = stream.TagValue_urational (tagType);
3890 
3891 			#if qDNGValidate
3892 
3893 			if (gVerbose)
3894 				{
3895 
3896 				printf ("%s:", LookupTagCode (parentCode, tagCode));
3897 
3898 				if (u->d == 0)
3899 					printf (" -");
3900 
3901 				else
3902 					printf (" %0.4f", u->As_real64 ());
3903 
3904 				printf ("\n");
3905 
3906 				}
3907 
3908 			#endif
3909 
3910 			break;
3911 
3912 			}
3913 
3914 		case tcGPSProcessingMethod:
3915 		case tcGPSAreaInformation:
3916 			{
3917 
3918 			if (!CheckTagType (parentCode, tagCode, tagType, ttUndefined))
3919 				return false;
3920 
3921 			dng_string *s;
3922 
3923 			switch (tagCode)
3924 				{
3925 
3926 				case tcGPSProcessingMethod:
3927 					s = &fGPSProcessingMethod;
3928 					break;
3929 
3930 				case tcGPSAreaInformation:
3931 					s = &fGPSAreaInformation;
3932 					break;
3933 
3934 				default:
3935 					return false;
3936 
3937 				}
3938 
3939 			ParseEncodedStringTag (stream,
3940 								   parentCode,
3941 								   tagCode,
3942 				    			   tagCount,
3943 				    		       *s);
3944 
3945 			#if qDNGValidate
3946 
3947 			if (gVerbose)
3948 				{
3949 
3950 				printf ("%s: ", LookupTagCode (parentCode, tagCode));
3951 
3952 				DumpString (*s);
3953 
3954 				printf ("\n");
3955 
3956 				}
3957 
3958 			#endif
3959 
3960 			break;
3961 
3962 			}
3963 
3964 		case tcGPSDifferential:
3965 			{
3966 
3967 			CheckTagType (parentCode, tagCode, tagType, ttShort);
3968 
3969 			CheckTagCount (parentCode, tagCode, tagCount, 1);
3970 
3971 			fGPSDifferential = stream.TagValue_uint32 (tagType);
3972 
3973 			#if qDNGValidate
3974 
3975 			if (gVerbose)
3976 				{
3977 
3978 				printf ("GPSDifferential: ");
3979 
3980 				switch (fGPSDifferential)
3981 					{
3982 
3983 					case 0:
3984 						printf ("Measurement without differential correction");
3985 						break;
3986 
3987 					case 1:
3988 						printf ("Differential correction applied");
3989 						break;
3990 
3991 					default:
3992 						printf ("%u", (unsigned) fGPSDifferential);
3993 
3994 					}
3995 
3996 				printf ("\n");
3997 
3998 				}
3999 
4000 			#endif
4001 
4002 			break;
4003 
4004 			}
4005 
4006 		default:
4007 			{
4008 
4009 			return false;
4010 
4011 			}
4012 
4013 		}
4014 
4015 	return true;
4016 
4017 	}
4018 
4019 /*****************************************************************************/
4020 
4021 // Parses tags that should only appear in Interoperability IFD
4022 
Parse_interoperability(dng_stream & stream,dng_shared &,uint32 parentCode,uint32 tagCode,uint32 tagType,uint32 tagCount,uint64)4023 bool dng_exif::Parse_interoperability (dng_stream &stream,
4024 						  			   dng_shared & /* shared */,
4025 									   uint32 parentCode,
4026 									   uint32 tagCode,
4027 									   uint32 tagType,
4028 									   uint32 tagCount,
4029 									   uint64 /* tagOffset */)
4030 	{
4031 
4032 	switch (tagCode)
4033 		{
4034 
4035 		case tcInteroperabilityIndex:
4036 			{
4037 
4038 			CheckTagType (parentCode, tagCode, tagType, ttAscii);
4039 
4040 			CheckTagCount (parentCode, tagCode, tagCount, 4);
4041 
4042 			ParseStringTag (stream,
4043 							parentCode,
4044 							tagCode,
4045 							tagCount,
4046 							fInteroperabilityIndex);
4047 
4048 			#if qDNGValidate
4049 
4050 			if (gVerbose)
4051 				{
4052 
4053 				printf ("InteroperabilityIndex: ");
4054 
4055 				DumpString (fInteroperabilityIndex);
4056 
4057 				printf ("\n");
4058 
4059 				}
4060 
4061 			#endif
4062 
4063 			break;
4064 
4065 			}
4066 
4067 		case tcInteroperabilityVersion:
4068 			{
4069 
4070 			CheckTagType (parentCode, tagCode, tagType, ttUndefined);
4071 
4072 			CheckTagCount (parentCode, tagCode, tagCount, 4);
4073 
4074 			uint32 b0 = stream.Get_uint8 ();
4075 			uint32 b1 = stream.Get_uint8 ();
4076 			uint32 b2 = stream.Get_uint8 ();
4077 			uint32 b3 = stream.Get_uint8 ();
4078 
4079 			fInteroperabilityVersion = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
4080 
4081 			#if qDNGValidate
4082 
4083 			if (gVerbose)
4084 				{
4085 
4086 				real64 x = (b0 - '0') * 10.00 +
4087 						   (b1 - '0') *  1.00 +
4088 						   (b2 - '0') *  0.10 +
4089 						   (b3 - '0') *  0.01;
4090 
4091 				printf ("InteroperabilityVersion: %0.2f\n", x);
4092 
4093 				}
4094 
4095 			#endif
4096 
4097 			break;
4098 
4099 			}
4100 
4101 		case tcRelatedImageFileFormat:
4102 			{
4103 
4104 			CheckTagType (parentCode, tagCode, tagType, ttAscii);
4105 
4106 			ParseStringTag (stream,
4107 							parentCode,
4108 							tagCode,
4109 							tagCount,
4110 							fRelatedImageFileFormat);
4111 
4112 			#if qDNGValidate
4113 
4114 			if (gVerbose)
4115 				{
4116 
4117 				printf ("RelatedImageFileFormat: ");
4118 
4119 				DumpString (fRelatedImageFileFormat);
4120 
4121 				printf ("\n");
4122 
4123 				}
4124 
4125 			#endif
4126 
4127 			break;
4128 
4129 			}
4130 
4131 		case tcRelatedImageWidth:
4132 			{
4133 
4134 			CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong);
4135 
4136 			CheckTagCount (parentCode, tagCode, tagCount, 1);
4137 
4138 			fRelatedImageWidth = stream.TagValue_uint32 (tagType);
4139 
4140 			#if qDNGValidate
4141 
4142 			if (gVerbose)
4143 				{
4144 				printf ("RelatedImageWidth: %u\n", (unsigned) fRelatedImageWidth);
4145 				}
4146 
4147 			#endif
4148 
4149 			break;
4150 
4151 			}
4152 
4153 		case tcRelatedImageLength:
4154 			{
4155 
4156 			CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong);
4157 
4158 			CheckTagCount (parentCode, tagCode, tagCount, 1);
4159 
4160 			fRelatedImageLength = stream.TagValue_uint32 (tagType);
4161 
4162 			#if qDNGValidate
4163 
4164 			if (gVerbose)
4165 				{
4166 				printf ("RelatedImageLength: %u\n", (unsigned) fRelatedImageLength);
4167 				}
4168 
4169 			#endif
4170 
4171 			break;
4172 
4173 			}
4174 
4175 		default:
4176 			{
4177 
4178 			return false;
4179 
4180 			}
4181 
4182 		}
4183 
4184 	return true;
4185 
4186 	}
4187 
4188 /*****************************************************************************/
4189 
PostParse(dng_host &,dng_shared &)4190 void dng_exif::PostParse (dng_host & /* host */,
4191 						  dng_shared & /* shared */)
4192 	{
4193 
4194 	#if qDNGValidate
4195 
4196 	const real64 kAPEX_Slop = 0.25;
4197 
4198 	// Sanity check on MaxApertureValue.
4199 
4200 	if (fMaxApertureValue.d)
4201 		{
4202 
4203 		real64 mav = fMaxApertureValue.As_real64 ();
4204 
4205 		// Compare against ApertureValue or FNumber.
4206 
4207 		real64 av = mav;
4208 
4209 		if (fApertureValue.d)
4210 			{
4211 
4212 			av = fApertureValue.As_real64 ();
4213 
4214 			}
4215 
4216 		else if (fFNumber.d)
4217 			{
4218 
4219 			real64 fs = fFNumber.As_real64 ();
4220 
4221 			if (fs >= 1.0)
4222 				{
4223 
4224 				av = FNumberToApertureValue (fs);
4225 
4226 				}
4227 
4228 			}
4229 
4230 		if (mav > av + kAPEX_Slop)
4231 			{
4232 
4233 			ReportWarning ("MaxApertureValue conflicts with ApertureValue and/or FNumber");
4234 
4235 			}
4236 
4237 		// Compare against LensInfo
4238 
4239 		if (fLensInfo [2].d && fLensInfo [3].d)
4240 			{
4241 
4242 			real64 fs1 = fLensInfo [2].As_real64 ();
4243 			real64 fs2 = fLensInfo [3].As_real64 ();
4244 
4245 			if (fs1 >= 1.0 && fs2 >= 1.0 && fs2 >= fs1)
4246 				{
4247 
4248 				real64 av1 = FNumberToApertureValue (fs1);
4249 				real64 av2 = FNumberToApertureValue (fs2);
4250 
4251 				// Wide angle adapters might create an effective
4252 				// wide FS, and tele-extenders always result
4253 				// in a higher FS.
4254 
4255 				if (mav < av1 - kAPEX_Slop - 1.0 ||
4256 					mav > av2 + kAPEX_Slop + 2.0)
4257 					{
4258 
4259 					ReportWarning ("Possible MaxApertureValue conflict with LensInfo");
4260 
4261 					}
4262 
4263 				}
4264 
4265 			}
4266 
4267 		}
4268 
4269 	// Sanity check on FocalLength.
4270 
4271 	if (fFocalLength.d)
4272 		{
4273 
4274 		real64 fl = fFocalLength.As_real64 ();
4275 
4276 		if (fl < 1.0)
4277 			{
4278 
4279 			ReportWarning ("FocalLength is less than 1.0 mm (legal but unlikely)");
4280 
4281 			}
4282 
4283 		else if (fLensInfo [0].d && fLensInfo [1].d)
4284 			{
4285 
4286 			real64 minFL = fLensInfo [0].As_real64 ();
4287 			real64 maxFL = fLensInfo [1].As_real64 ();
4288 
4289 			// Allow for wide-angle converters and tele-extenders.
4290 
4291 			if (fl < minFL * 0.6 ||
4292 			    fl > maxFL * 2.1)
4293 				{
4294 
4295 				ReportWarning ("Possible FocalLength conflict with LensInfo");
4296 
4297 				}
4298 
4299 			}
4300 
4301 		}
4302 
4303 	#endif
4304 
4305 	// Mirror DateTimeOriginal to DateTime.
4306 
4307 	if (fDateTime.NotValid () && fDateTimeOriginal.IsValid ())
4308 		{
4309 
4310 		fDateTime = fDateTimeOriginal;
4311 
4312 		}
4313 
4314 	// Mirror EXIF 2.3 sensitivity tags to ISOSpeedRatings.
4315 
4316 	if (fISOSpeedRatings [0] == 0 || fISOSpeedRatings [0] == 65535)
4317 		{
4318 
4319 		// Prefer Recommended Exposure Index, then Standard Output Sensitivity, then
4320 		// ISO Speed, then Exposure Index.
4321 
4322 		if (fRecommendedExposureIndex != 0 &&
4323 			(fSensitivityType == stRecommendedExposureIndex ||
4324 			 fSensitivityType == stSOSandREI				||
4325 			 fSensitivityType == stREIandISOSpeed			||
4326 			 fSensitivityType == stSOSandREIandISOSpeed))
4327 			{
4328 
4329 			fISOSpeedRatings [0] = fRecommendedExposureIndex;
4330 
4331 			}
4332 
4333 		else if (fStandardOutputSensitivity != 0 &&
4334 				 (fSensitivityType == stStandardOutputSensitivity ||
4335 				  fSensitivityType == stSOSandREI				  ||
4336 				  fSensitivityType == stSOSandISOSpeed			  ||
4337 				  fSensitivityType == stSOSandREIandISOSpeed))
4338 			{
4339 
4340 			fISOSpeedRatings [0] = fStandardOutputSensitivity;
4341 
4342 			}
4343 
4344 		else if (fISOSpeed != 0 &&
4345 				 (fSensitivityType == stISOSpeed	   ||
4346 				  fSensitivityType == stSOSandISOSpeed ||
4347 				  fSensitivityType == stREIandISOSpeed ||
4348 				  fSensitivityType == stSOSandREIandISOSpeed))
4349 			{
4350 
4351 			fISOSpeedRatings [0] = fISOSpeed;
4352 
4353 			}
4354 
4355 		}
4356 
4357 	// Mirror ExposureIndex to ISOSpeedRatings.
4358 
4359 	if (fExposureIndex.IsValid () && fISOSpeedRatings [0] == 0)
4360 		{
4361 
4362 		fISOSpeedRatings [0] = Round_uint32 (fExposureIndex.As_real64 ());
4363 
4364 		}
4365 
4366 	// Kodak sets the GPSAltitudeRef without setting the GPSAltitude.
4367 
4368 	if (fGPSAltitude.NotValid ())
4369 		{
4370 
4371 		fGPSAltitudeRef = 0xFFFFFFFF;
4372 
4373 		}
4374 
4375 	// If there is no valid GPS data, clear the GPS version number.
4376 
4377 	if (fGPSLatitude  [0].NotValid () &&
4378 		fGPSLongitude [0].NotValid () &&
4379 		fGPSAltitude     .NotValid () &&
4380 		fGPSTimeStamp [0].NotValid () &&
4381 		fGPSDateStamp    .IsEmpty  ())
4382 		{
4383 
4384 		fGPSVersionID = 0;
4385 
4386 		}
4387 
4388 	}
4389 
4390 /*****************************************************************************/
4391