Home | History | Annotate | Download | only in source
      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 
     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 
    189 dng_exif::~dng_exif ()
    190 	{
    191 
    192 	}
    193 
    194 /*****************************************************************************/
    195 
    196 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 
    212 void dng_exif::SetEmpty ()
    213 	{
    214 
    215 	*this = dng_exif ();
    216 
    217 	}
    218 
    219 /*****************************************************************************/
    220 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    693 real64 dng_exif::ApertureValueToFNumber (real64 av)
    694 	{
    695 
    696 	return pow (2.0, 0.5 * av);
    697 
    698 	}
    699 
    700 /*****************************************************************************/
    701 
    702 real64 dng_exif::ApertureValueToFNumber (const dng_urational &av)
    703 	{
    704 
    705 	return ApertureValueToFNumber (av.As_real64 ());
    706 
    707 	}
    708 
    709 /*****************************************************************************/
    710 
    711 real64 dng_exif::FNumberToApertureValue (real64 fNumber)
    712 	{
    713 
    714 	return 2.0 * log (fNumber) / log (2.0);
    715 
    716 	}
    717 
    718 /*****************************************************************************/
    719 
    720 real64 dng_exif::FNumberToApertureValue (const dng_urational &fNumber)
    721 	{
    722 
    723 	return FNumberToApertureValue (fNumber.As_real64 ());
    724 
    725 	}
    726 
    727 /*****************************************************************************/
    728 
    729 void dng_exif::UpdateDateTime (const dng_date_time_info &dt)
    730 	{
    731 
    732 	fDateTime = dt;
    733 
    734 	}
    735 
    736 /*****************************************************************************/
    737 
    738 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 
    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 
    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 
   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 
   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 
   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 
   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 
   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