File indexing completed on 2025-01-19 03:54:57

0001 /*****************************************************************************/
0002 // Copyright 2006-2019 Adobe Systems Incorporated
0003 // All Rights Reserved.
0004 //
0005 // NOTICE:  Adobe permits you to use, modify, and distribute this file in
0006 // accordance with the terms of the Adobe license agreement accompanying it.
0007 /*****************************************************************************/
0008 
0009 #include "dng_exif.h"
0010 
0011 #include "dng_tag_codes.h"
0012 #include "dng_tag_types.h"
0013 #include "dng_parse_utils.h"
0014 #include "dng_globals.h"
0015 #include "dng_exceptions.h"
0016 #include "dng_tag_values.h"
0017 #include "dng_utils.h"
0018 
0019 /*****************************************************************************/
0020 
0021 dng_exif::dng_exif ()
0022 
0023     :   fImageDescription ()
0024     ,   fMake             ()
0025     ,   fModel            ()
0026     ,   fSoftware         ()
0027     ,   fArtist           ()
0028     ,   fCopyright        ()
0029     ,   fCopyright2       ()
0030     ,   fUserComment      ()
0031 
0032     ,   fDateTime            ()
0033     ,   fDateTimeStorageInfo ()
0034 
0035     ,   fDateTimeOriginal            ()
0036     ,   fDateTimeOriginalStorageInfo ()
0037 
0038     ,   fDateTimeDigitized            ()
0039     ,   fDateTimeDigitizedStorageInfo ()
0040 
0041     ,   fTIFF_EP_StandardID (0)
0042     ,   fExifVersion        (0)
0043     ,   fFlashPixVersion    (0)
0044 
0045     ,   fExposureTime      ()
0046     ,   fFNumber           ()
0047     ,   fShutterSpeedValue ()
0048     ,   fApertureValue     ()
0049     ,   fBrightnessValue   ()
0050     ,   fExposureBiasValue ()
0051     ,   fMaxApertureValue  ()
0052     ,   fFocalLength       ()
0053     ,   fDigitalZoomRatio  ()
0054     ,   fExposureIndex     ()
0055     ,   fSubjectDistance   ()
0056     ,   fGamma             ()
0057 
0058     ,   fBatteryLevelR ()
0059     ,   fBatteryLevelA ()
0060 
0061     ,   fExposureProgram      (0xFFFFFFFF)
0062     ,   fMeteringMode         (0xFFFFFFFF)
0063     ,   fLightSource          (0xFFFFFFFF)
0064     ,   fFlash                (0xFFFFFFFF)
0065     ,   fFlashMask            (0x0000FFFF)
0066     ,   fSensingMethod        (0xFFFFFFFF)
0067     ,   fColorSpace           (0xFFFFFFFF)
0068     ,   fFileSource           (0xFFFFFFFF)
0069     ,   fSceneType            (0xFFFFFFFF)
0070     ,   fCustomRendered       (0xFFFFFFFF)
0071     ,   fExposureMode         (0xFFFFFFFF)
0072     ,   fWhiteBalance         (0xFFFFFFFF)
0073     ,   fSceneCaptureType     (0xFFFFFFFF)
0074     ,   fGainControl          (0xFFFFFFFF)
0075     ,   fContrast             (0xFFFFFFFF)
0076     ,   fSaturation           (0xFFFFFFFF)
0077     ,   fSharpness            (0xFFFFFFFF)
0078     ,   fSubjectDistanceRange (0xFFFFFFFF)
0079     ,   fSelfTimerMode        (0xFFFFFFFF)
0080     ,   fImageNumber          (0xFFFFFFFF)
0081 
0082     ,   fFocalLengthIn35mmFilm (0)
0083 
0084     ,   fSensitivityType           (0)
0085     ,   fStandardOutputSensitivity (0)
0086     ,   fRecommendedExposureIndex  (0)
0087     ,   fISOSpeed                  (0)
0088     ,   fISOSpeedLatitudeyyy       (0)
0089     ,   fISOSpeedLatitudezzz       (0)
0090 
0091     ,   fSubjectAreaCount (0)
0092 
0093     ,   fComponentsConfiguration (0)
0094 
0095     ,   fCompresssedBitsPerPixel ()
0096 
0097     ,   fPixelXDimension (0)
0098     ,   fPixelYDimension (0)
0099 
0100     ,   fFocalPlaneXResolution ()
0101     ,   fFocalPlaneYResolution ()
0102 
0103     ,   fFocalPlaneResolutionUnit (0xFFFFFFFF)
0104 
0105     ,   fCFARepeatPatternRows (0)
0106     ,   fCFARepeatPatternCols (0)
0107 
0108     ,   fImageUniqueID ()
0109 
0110     ,   fGPSVersionID         (0)
0111     ,   fGPSLatitudeRef       ()
0112     ,   fGPSLongitudeRef      ()
0113     ,   fGPSAltitudeRef       (0xFFFFFFFF)
0114     ,   fGPSAltitude          ()
0115     ,   fGPSSatellites        ()
0116     ,   fGPSStatus            ()
0117     ,   fGPSMeasureMode       ()
0118     ,   fGPSDOP               ()
0119     ,   fGPSSpeedRef          ()
0120     ,   fGPSSpeed             ()
0121     ,   fGPSTrackRef          ()
0122     ,   fGPSTrack             ()
0123     ,   fGPSImgDirectionRef   ()
0124     ,   fGPSImgDirection      ()
0125     ,   fGPSMapDatum          ()
0126     ,   fGPSDestLatitudeRef   ()
0127     ,   fGPSDestLongitudeRef  ()
0128     ,   fGPSDestBearingRef    ()
0129     ,   fGPSDestBearing       ()
0130     ,   fGPSDestDistanceRef   ()
0131     ,   fGPSDestDistance      ()
0132     ,   fGPSProcessingMethod  ()
0133     ,   fGPSAreaInformation   ()
0134     ,   fGPSDateStamp         ()
0135     ,   fGPSDifferential      (0xFFFFFFFF)
0136     ,   fGPSHPositioningError ()
0137 
0138     ,   fInteroperabilityIndex ()
0139 
0140     ,   fInteroperabilityVersion (0)
0141 
0142     ,   fRelatedImageFileFormat ()
0143 
0144     ,   fRelatedImageWidth  (0)
0145     ,   fRelatedImageLength (0)
0146 
0147     ,   fCameraSerialNumber ()
0148 
0149     ,   fLensID           ()
0150     ,   fLensMake         ()
0151     ,   fLensName         ()
0152     ,   fLensSerialNumber ()
0153 
0154     ,   fLensNameWasReadFromExif (false)
0155 
0156     ,   fApproxFocusDistance ()
0157 
0158     ,   fFlashCompensation ()
0159 
0160     ,   fOwnerName ()
0161     ,   fFirmware  ()
0162 
0163     ,   fTemperature          ()
0164     ,   fHumidity             ()
0165     ,   fPressure             ()
0166     ,   fWaterDepth           ()
0167     ,   fAcceleration         ()
0168     ,   fCameraElevationAngle ()
0169 
0170     ,   fTitle ()
0171 
0172     {
0173 
0174     uint32 j;
0175     uint32 k;
0176 
0177     fISOSpeedRatings [0] = 0;
0178     fISOSpeedRatings [1] = 0;
0179     fISOSpeedRatings [2] = 0;
0180 
0181     for (j = 0; j < kMaxCFAPattern; j++)
0182         for (k = 0; k < kMaxCFAPattern; k++)
0183             {
0184             fCFAPattern [j] [k] = 255;
0185             }
0186 
0187     memset (fLensDistortInfo, 0, sizeof (fLensDistortInfo));
0188 
0189     }
0190 
0191 /*****************************************************************************/
0192 
0193 dng_exif::~dng_exif ()
0194     {
0195 
0196     }
0197 
0198 /*****************************************************************************/
0199 
0200 dng_exif * dng_exif::Clone () const
0201     {
0202 
0203     dng_exif *result = new dng_exif (*this);
0204 
0205     if (!result)
0206         {
0207         ThrowMemoryFull ();
0208         }
0209 
0210     return result;
0211 
0212     }
0213 
0214 /*****************************************************************************/
0215 
0216 void dng_exif::SetEmpty ()
0217     {
0218 
0219     *this = dng_exif ();
0220 
0221     }
0222 
0223 /*****************************************************************************/
0224 
0225 void dng_exif::CopyGPSFrom (const dng_exif &exif)
0226     {
0227 
0228     fGPSVersionID         = exif.fGPSVersionID;
0229     fGPSLatitudeRef       = exif.fGPSLatitudeRef;
0230     fGPSLatitude [0]      = exif.fGPSLatitude [0];
0231     fGPSLatitude [1]      = exif.fGPSLatitude [1];
0232     fGPSLatitude [2]      = exif.fGPSLatitude [2];
0233     fGPSLongitudeRef      = exif.fGPSLongitudeRef;
0234     fGPSLongitude [0]     = exif.fGPSLongitude [0];
0235     fGPSLongitude [1]     = exif.fGPSLongitude [1];
0236     fGPSLongitude [2]     = exif.fGPSLongitude [2];
0237     fGPSAltitudeRef       = exif.fGPSAltitudeRef;
0238     fGPSAltitude          = exif.fGPSAltitude;
0239     fGPSTimeStamp [0]     = exif.fGPSTimeStamp [0];
0240     fGPSTimeStamp [1]     = exif.fGPSTimeStamp [1];
0241     fGPSTimeStamp [2]     = exif.fGPSTimeStamp [2];
0242     fGPSSatellites        = exif.fGPSSatellites;
0243     fGPSStatus            = exif.fGPSStatus;
0244     fGPSMeasureMode       = exif.fGPSMeasureMode;
0245     fGPSDOP               = exif.fGPSDOP;
0246     fGPSSpeedRef          = exif.fGPSSpeedRef;
0247     fGPSSpeed             = exif.fGPSSpeed;
0248     fGPSTrackRef          = exif.fGPSTrackRef;
0249     fGPSTrack             = exif.fGPSTrack;
0250     fGPSImgDirectionRef   = exif.fGPSImgDirectionRef;
0251     fGPSImgDirection      = exif.fGPSImgDirection;
0252     fGPSMapDatum          = exif.fGPSMapDatum;
0253     fGPSDestLatitudeRef   = exif.fGPSDestLatitudeRef;
0254     fGPSDestLatitude [0]  = exif.fGPSDestLatitude [0];
0255     fGPSDestLatitude [1]  = exif.fGPSDestLatitude [1];
0256     fGPSDestLatitude [2]  = exif.fGPSDestLatitude [2];
0257     fGPSDestLongitudeRef  = exif.fGPSDestLongitudeRef;
0258     fGPSDestLongitude [0] = exif.fGPSDestLongitude [0];
0259     fGPSDestLongitude [1] = exif.fGPSDestLongitude [1];
0260     fGPSDestLongitude [2] = exif.fGPSDestLongitude [2];
0261     fGPSDestBearingRef    = exif.fGPSDestBearingRef;
0262     fGPSDestBearing       = exif.fGPSDestBearing;
0263     fGPSDestDistanceRef   = exif.fGPSDestDistanceRef;
0264     fGPSDestDistance      = exif.fGPSDestDistance;
0265     fGPSProcessingMethod  = exif.fGPSProcessingMethod;
0266     fGPSAreaInformation   = exif.fGPSAreaInformation;
0267     fGPSDateStamp         = exif.fGPSDateStamp;
0268     fGPSDifferential      = exif.fGPSDifferential;
0269     fGPSHPositioningError = exif.fGPSHPositioningError;
0270 
0271     }
0272 
0273 /*****************************************************************************/
0274 
0275 // Fix up common errors and rounding issues with EXIF exposure times.
0276 
0277 real64 dng_exif::SnapExposureTime (real64 et)
0278     {
0279 
0280     // Protection against invalid values.
0281 
0282     if (et <= 0.0)
0283         return 0.0;
0284 
0285     // If near a standard shutter speed, snap to it.
0286 
0287     static const real64 kStandardSpeed [] =
0288         {
0289         30.0,
0290         25.0,
0291         20.0,
0292         15.0,
0293         13.0,
0294         10.0,
0295         8.0,
0296         6.0,
0297         5.0,
0298         4.0,
0299         3.2,
0300         3.0,
0301         2.5,
0302         2.0,
0303         1.6,
0304         1.5,
0305         1.3,
0306         1.0,
0307         0.8,
0308         0.7,
0309         0.6,
0310         0.5,
0311         0.4,
0312         0.3,
0313         1.0 / 4.0,
0314         1.0 / 5.0,
0315         1.0 / 6.0,
0316         1.0 / 8.0,
0317         1.0 / 10.0,
0318         1.0 / 13.0,
0319         1.0 / 15.0,
0320         1.0 / 20.0,
0321         1.0 / 25.0,
0322         1.0 / 30.0,
0323         1.0 / 40.0,
0324         1.0 / 45.0,
0325         1.0 / 50.0,
0326         1.0 / 60.0,
0327         1.0 / 80.0,
0328         1.0 / 90.0,
0329         1.0 / 100.0,
0330         1.0 / 125.0,
0331         1.0 / 160.0,
0332         1.0 / 180.0,
0333         1.0 / 200.0,
0334         1.0 / 250.0,
0335         1.0 / 320.0,
0336         1.0 / 350.0,
0337         1.0 / 400.0,
0338         1.0 / 500.0,
0339         1.0 / 640.0,
0340         1.0 / 750.0,
0341         1.0 / 800.0,
0342         1.0 / 1000.0,
0343         1.0 / 1250.0,
0344         1.0 / 1500.0,
0345         1.0 / 1600.0,
0346         1.0 / 2000.0,
0347         1.0 / 2500.0,
0348         1.0 / 3000.0,
0349         1.0 / 3200.0,
0350         1.0 / 4000.0,
0351         1.0 / 5000.0,
0352         1.0 / 6000.0,
0353         1.0 / 6400.0,
0354         1.0 / 8000.0,
0355         1.0 / 10000.0,
0356         1.0 / 12000.0,
0357         1.0 / 12800.0,
0358         1.0 / 16000.0
0359         };
0360 
0361     uint32 count = sizeof (kStandardSpeed    ) /
0362                    sizeof (kStandardSpeed [0]);
0363 
0364     for (uint32 fudge = 0; fudge <= 1; fudge++)
0365         {
0366 
0367         real64 testSpeed = et;
0368 
0369         if (fudge == 1)
0370             {
0371 
0372             // Often APEX values are rounded to a power of two,
0373             // which results in non-standard shutter speeds.
0374 
0375             if (et >= 0.1)
0376                 {
0377 
0378                 // No fudging slower than 1/10 second
0379 
0380                 break;
0381 
0382                 }
0383 
0384             else if (et >= 0.01)
0385                 {
0386 
0387                 // Between 1/10 and 1/100 the commonly misrounded
0388                 // speeds are 1/15, 1/30, 1/60, which are often encoded as
0389                 // 1/16, 1/32, 1/64.  Try fudging and see if we get
0390                 // near a standard speed.
0391 
0392                 testSpeed *= 16.0 / 15.0;
0393 
0394                 }
0395 
0396             else
0397                 {
0398 
0399                 // Faster than 1/100, the commonly misrounded
0400                 // speeds are 1/125, 1/250, 1/500, etc., which
0401                 // are often encoded as 1/128, 1/256, 1/512.
0402 
0403                 testSpeed *= 128.0 / 125.0;
0404 
0405                 }
0406 
0407             }
0408 
0409         for (uint32 index = 0; index < count; index++)
0410             {
0411 
0412             if (testSpeed >= kStandardSpeed [index] * 0.98 &&
0413                 testSpeed <= kStandardSpeed [index] * 1.02)
0414                 {
0415 
0416                 return kStandardSpeed [index];
0417 
0418                 }
0419 
0420             }
0421 
0422         }
0423 
0424     // We are not near any standard speeds.  Round the non-standard value to something
0425     // that looks reasonable.
0426 
0427     if (et >= 10.0)
0428         {
0429 
0430         // Round to nearest second.
0431 
0432         et = floor (et + 0.5);
0433 
0434         }
0435 
0436     else if (et >= 0.5)
0437         {
0438 
0439         // Round to nearest 1/10 second
0440 
0441         et = floor (et * 10.0 + 0.5) * 0.1;
0442 
0443         }
0444 
0445     else if (et >= 1.0 / 20.0)
0446         {
0447 
0448         // Round to an exact inverse.
0449 
0450         et = 1.0 / floor (1.0 / et + 0.5);
0451 
0452         }
0453 
0454     else if (et >= 1.0 / 130.0)
0455         {
0456 
0457         // Round inverse to multiple of 5
0458 
0459         et = 0.2 / floor (0.2 / et + 0.5);
0460 
0461         }
0462 
0463     else if (et >= 1.0 / 750.0)
0464         {
0465 
0466         // Round inverse to multiple of 10
0467 
0468         et = 0.1 / floor (0.1 / et + 0.5);
0469 
0470         }
0471 
0472     else if (et >= 1.0 / 1300.0)
0473         {
0474 
0475         // Round inverse to multiple of 50
0476 
0477         et = 0.02 / floor (0.02 / et + 0.5);
0478 
0479         }
0480 
0481     else if (et >= 1.0 / 15000.0)
0482         {
0483 
0484         // Round inverse to multiple of 100
0485 
0486         et = 0.01 / floor (0.01 / et + 0.5);
0487 
0488         }
0489 
0490     else
0491         {
0492 
0493         // Round inverse to multiple of 1000
0494 
0495         et = 0.001 / floor (0.001 / et + 0.5);
0496 
0497         }
0498 
0499     return et;
0500 
0501     }
0502 
0503 /*****************************************************************************/
0504 
0505 void dng_exif::SetExposureTime (real64 et, bool snap)
0506     {
0507 
0508     fExposureTime.Clear ();
0509 
0510     fShutterSpeedValue.Clear ();
0511 
0512     if (snap)
0513         {
0514 
0515         et = SnapExposureTime (et);
0516 
0517         }
0518 
0519     if (et >= 1.0 / 1073741824.0 && et <= 1073741824.0)
0520         {
0521 
0522         if (et >= 100.0)
0523             {
0524 
0525             fExposureTime.Set_real64 (et, 1);
0526 
0527             }
0528 
0529         else if (et >= 1.0)
0530             {
0531 
0532             fExposureTime.Set_real64 (et, 10);
0533 
0534             fExposureTime.ReduceByFactor (10);
0535 
0536             }
0537 
0538         else if (et <= 0.1)
0539             {
0540 
0541             fExposureTime = dng_urational (1, Round_uint32 (1.0 / et));
0542 
0543             }
0544 
0545         else
0546             {
0547 
0548             fExposureTime.Set_real64 (et, 100);
0549 
0550             fExposureTime.ReduceByFactor (10);
0551 
0552             for (uint32 f = 2; f <= 9; f++)
0553                 {
0554 
0555                 real64 z = 1.0 / (real64) f / et;
0556 
0557                 if (z >= 0.99 && z <= 1.01)
0558                     {
0559 
0560                     fExposureTime = dng_urational (1, f);
0561 
0562                     break;
0563 
0564                     }
0565 
0566                 }
0567 
0568             }
0569 
0570         // Now mirror this value to the ShutterSpeedValue field.
0571 
0572         et = fExposureTime.As_real64 ();
0573 
0574         fShutterSpeedValue.Set_real64 (-log (et) / log (2.0), 1000000);
0575 
0576         fShutterSpeedValue.ReduceByFactor (10);
0577         fShutterSpeedValue.ReduceByFactor (10);
0578         fShutterSpeedValue.ReduceByFactor (10);
0579         fShutterSpeedValue.ReduceByFactor (10);
0580         fShutterSpeedValue.ReduceByFactor (10);
0581         fShutterSpeedValue.ReduceByFactor (10);
0582 
0583         }
0584 
0585     }
0586 
0587 /*****************************************************************************/
0588 
0589 void dng_exif::SetShutterSpeedValue (real64 ss)
0590     {
0591 
0592     if (fExposureTime.NotValid ())
0593         {
0594 
0595         real64 et = pow (2.0, -ss);
0596 
0597         SetExposureTime (et, true);
0598 
0599         }
0600 
0601     }
0602 
0603 /******************************************************************************/
0604 
0605 dng_urational dng_exif::EncodeFNumber (real64 fs)
0606     {
0607 
0608     dng_urational y;
0609 
0610     if (fs > 10.0)
0611         {
0612 
0613         y.Set_real64 (fs, 1);
0614 
0615         }
0616 
0617     else if (fs < 1.0)
0618         {
0619 
0620         y.Set_real64 (fs, 100);
0621 
0622         y.ReduceByFactor (10);
0623         y.ReduceByFactor (10);
0624 
0625         }
0626 
0627     else
0628         {
0629 
0630         y.Set_real64 (fs, 10);
0631 
0632         y.ReduceByFactor (10);
0633 
0634         }
0635 
0636     return y;
0637 
0638     }
0639 
0640 /*****************************************************************************/
0641 
0642 void dng_exif::SetFNumber (real64 fs)
0643     {
0644 
0645     fFNumber.Clear ();
0646 
0647     fApertureValue.Clear ();
0648 
0649     // Allow f-number values less than 1.0 (e.g., f/0.95), even though they would
0650     // correspond to negative APEX values, which the EXIF specification does not
0651     // support (ApertureValue is a rational, not srational). The ApertureValue tag
0652     // will be omitted in the case where fs < 1.0.
0653 
0654     if (fs > 0.0 && fs <= 32768.0)
0655         {
0656 
0657         fFNumber = EncodeFNumber (fs);
0658 
0659         // Now mirror this value to the ApertureValue field.
0660 
0661         real64 av = FNumberToApertureValue (fFNumber);
0662 
0663         if (av >= 0.0 && av <= 99.99)
0664             {
0665 
0666             fApertureValue.Set_real64 (av, 1000000);
0667 
0668             fApertureValue.ReduceByFactor (10);
0669             fApertureValue.ReduceByFactor (10);
0670             fApertureValue.ReduceByFactor (10);
0671             fApertureValue.ReduceByFactor (10);
0672             fApertureValue.ReduceByFactor (10);
0673             fApertureValue.ReduceByFactor (10);
0674 
0675             }
0676 
0677         }
0678 
0679     }
0680 
0681 /*****************************************************************************/
0682 
0683 void dng_exif::SetApertureValue (real64 av)
0684     {
0685 
0686     if (fFNumber.NotValid ())
0687         {
0688 
0689         SetFNumber (ApertureValueToFNumber (av));
0690 
0691         }
0692 
0693     }
0694 
0695 /*****************************************************************************/
0696 
0697 real64 dng_exif::ApertureValueToFNumber (real64 av)
0698     {
0699 
0700     return pow (2.0, 0.5 * av);
0701 
0702     }
0703 
0704 /*****************************************************************************/
0705 
0706 real64 dng_exif::ApertureValueToFNumber (const dng_urational &av)
0707     {
0708 
0709     return ApertureValueToFNumber (av.As_real64 ());
0710 
0711     }
0712 
0713 /*****************************************************************************/
0714 
0715 real64 dng_exif::FNumberToApertureValue (real64 fNumber)
0716     {
0717 
0718     return 2.0 * log (fNumber) / log (2.0);
0719 
0720     }
0721 
0722 /*****************************************************************************/
0723 
0724 real64 dng_exif::FNumberToApertureValue (const dng_urational &fNumber)
0725     {
0726 
0727     return FNumberToApertureValue (fNumber.As_real64 ());
0728 
0729     }
0730 
0731 /*****************************************************************************/
0732 
0733 void dng_exif::UpdateDateTime (const dng_date_time_info &dt)
0734     {
0735 
0736     fDateTime = dt;
0737 
0738     }
0739 
0740 /*****************************************************************************/
0741 
0742 bool dng_exif::AtLeastVersion0230 () const
0743     {
0744 
0745     return fExifVersion >= DNG_CHAR4 ('0','2','3','0');
0746 
0747     }
0748 
0749 /*****************************************************************************/
0750 
0751 bool dng_exif::AtLeastVersion0231 () const
0752     {
0753 
0754     return fExifVersion >= DNG_CHAR4 ('0','2','3','1');
0755 
0756     }
0757 
0758 /*****************************************************************************/
0759 
0760 void dng_exif::SetVersion0231 ()
0761     {
0762 
0763     fExifVersion = DNG_CHAR4 ('0','2','3','1');
0764 
0765     }
0766 
0767 /*****************************************************************************/
0768 
0769 bool dng_exif::HasLensDistortInfo () const
0770     {
0771 
0772     return (fLensDistortInfo [0] . IsValid () &&
0773             fLensDistortInfo [1] . IsValid () &&
0774             fLensDistortInfo [2] . IsValid () &&
0775             fLensDistortInfo [3] . IsValid ());
0776 
0777     }
0778 
0779 /*****************************************************************************/
0780 
0781 void dng_exif::SetLensDistortInfo (const dng_vector &params)
0782     {
0783 
0784     if (params.Count () != 4)
0785         {
0786         return;
0787         }
0788 
0789     fLensDistortInfo [0] . Set_real64 (params [0]);
0790     fLensDistortInfo [1] . Set_real64 (params [1]);
0791     fLensDistortInfo [2] . Set_real64 (params [2]);
0792     fLensDistortInfo [3] . Set_real64 (params [3]);
0793 
0794     }
0795 
0796 /*****************************************************************************/
0797 
0798 bool dng_exif::ParseTag (dng_stream &stream,
0799                          dng_shared &shared,
0800                          uint32 parentCode,
0801                          bool isMainIFD,
0802                          uint32 tagCode,
0803                          uint32 tagType,
0804                          uint32 tagCount,
0805                          uint64 tagOffset)
0806     {
0807 
0808     if (parentCode == 0)
0809         {
0810 
0811         if (Parse_ifd0 (stream,
0812                         shared,
0813                         parentCode,
0814                         tagCode,
0815                         tagType,
0816                         tagCount,
0817                         tagOffset))
0818             {
0819 
0820             return true;
0821 
0822             }
0823 
0824         }
0825 
0826     if (parentCode == 0 || isMainIFD)
0827         {
0828 
0829         if (Parse_ifd0_main (stream,
0830                              shared,
0831                              parentCode,
0832                              tagCode,
0833                              tagType,
0834                              tagCount,
0835                              tagOffset))
0836             {
0837 
0838             return true;
0839 
0840             }
0841 
0842         }
0843 
0844     if (parentCode == 0 ||
0845         parentCode == tcExifIFD)
0846         {
0847 
0848         if (Parse_ifd0_exif (stream,
0849                              shared,
0850                              parentCode,
0851                              tagCode,
0852                              tagType,
0853                              tagCount,
0854                              tagOffset))
0855             {
0856 
0857             return true;
0858 
0859             }
0860 
0861         }
0862 
0863     if (parentCode == tcGPSInfo)
0864         {
0865 
0866         if (Parse_gps (stream,
0867                        shared,
0868                        parentCode,
0869                        tagCode,
0870                        tagType,
0871                        tagCount,
0872                        tagOffset))
0873             {
0874 
0875             return true;
0876 
0877             }
0878 
0879         }
0880 
0881     if (parentCode == tcInteroperabilityIFD)
0882         {
0883 
0884         if (Parse_interoperability (stream,
0885                                     shared,
0886                                     parentCode,
0887                                     tagCode,
0888                                     tagType,
0889                                     tagCount,
0890                                     tagOffset))
0891             {
0892 
0893             return true;
0894 
0895             }
0896 
0897         }
0898 
0899     return false;
0900 
0901     }
0902 
0903 /*****************************************************************************/
0904 
0905 // Parses tags that should only appear in IFD 0.
0906 
0907 bool dng_exif::Parse_ifd0 (dng_stream &stream,
0908                            dng_shared & /* shared */,
0909                            uint32 parentCode,
0910                            uint32 tagCode,
0911                            uint32 tagType,
0912                            uint32 tagCount,
0913                            uint64 /* tagOffset */)
0914     {
0915 
0916     switch (tagCode)
0917         {
0918 
0919         case tcImageDescription:
0920             {
0921 
0922             CheckTagType (parentCode, tagCode, tagType, ttAscii);
0923 
0924             ParseStringTag (stream,
0925                             parentCode,
0926                             tagCode,
0927                             tagCount,
0928                             fImageDescription);
0929 
0930             #if qDNGValidate
0931 
0932             if (gVerbose)
0933                 {
0934 
0935                 printf ("ImageDescription: ");
0936 
0937                 DumpString (fImageDescription);
0938 
0939                 printf ("\n");
0940 
0941                 }
0942 
0943             #endif
0944 
0945             break;
0946 
0947             }
0948 
0949         case tcMake:
0950             {
0951 
0952             CheckTagType (parentCode, tagCode, tagType, ttAscii);
0953 
0954             ParseStringTag (stream,
0955                             parentCode,
0956                             tagCode,
0957                             tagCount,
0958                             fMake);
0959 
0960             #if qDNGValidate
0961 
0962             if (gVerbose)
0963                 {
0964 
0965                 printf ("Make: ");
0966 
0967                 DumpString (fMake);
0968 
0969                 printf ("\n");
0970 
0971                 }
0972 
0973             #endif
0974 
0975             break;
0976 
0977             }
0978 
0979         case tcModel:
0980             {
0981 
0982             CheckTagType (parentCode, tagCode, tagType, ttAscii);
0983 
0984             ParseStringTag (stream,
0985                             parentCode,
0986                             tagCode,
0987                             tagCount,
0988                             fModel);
0989 
0990             #if qDNGValidate
0991 
0992             if (gVerbose)
0993                 {
0994 
0995                 printf ("Model: ");
0996 
0997                 DumpString (fModel);
0998 
0999                 printf ("\n");
1000 
1001                 }
1002 
1003             #endif
1004 
1005             break;
1006 
1007             }
1008 
1009         case tcSoftware:
1010             {
1011 
1012             CheckTagType (parentCode, tagCode, tagType, ttAscii);
1013 
1014             ParseStringTag (stream,
1015                             parentCode,
1016                             tagCode,
1017                             tagCount,
1018                             fSoftware);
1019 
1020             #if qDNGValidate
1021 
1022             if (gVerbose)
1023                 {
1024 
1025                 printf ("Software: ");
1026 
1027                 DumpString (fSoftware);
1028 
1029                 printf ("\n");
1030 
1031                 }
1032 
1033             #endif
1034 
1035             break;
1036 
1037             }
1038 
1039         case tcDateTime:
1040             {
1041 
1042             uint64 tagPosition = stream.PositionInOriginalFile ();
1043 
1044             dng_date_time dt;
1045 
1046             if (!ParseDateTimeTag (stream,
1047                                    parentCode,
1048                                    tagCode,
1049                                    tagType,
1050                                    tagCount,
1051                                    dt))
1052                 {
1053                 return false;
1054                 }
1055 
1056             fDateTime.SetDateTime (dt);
1057 
1058             fDateTimeStorageInfo = dng_date_time_storage_info (tagPosition,
1059                                                                dng_date_time_format_exif);
1060 
1061             #if qDNGValidate
1062 
1063             if (gVerbose)
1064                 {
1065 
1066                 printf ("DateTime: ");
1067 
1068                 DumpDateTime (fDateTime.DateTime ());
1069 
1070                 printf ("\n");
1071 
1072                 }
1073 
1074             #endif
1075 
1076             break;
1077 
1078             }
1079 
1080         case tcArtist:
1081             {
1082 
1083             CheckTagType (parentCode, tagCode, tagType, ttAscii);
1084 
1085             ParseStringTag (stream,
1086                             parentCode,
1087                             tagCode,
1088                             tagCount,
1089                             fArtist);
1090 
1091             #if qDNGValidate
1092 
1093             if (gVerbose)
1094                 {
1095 
1096                 printf ("Artist: ");
1097 
1098                 DumpString (fArtist);
1099 
1100                 printf ("\n");
1101 
1102                 }
1103 
1104             #endif
1105 
1106             break;
1107 
1108             }
1109 
1110         case tcCopyright:
1111             {
1112 
1113             CheckTagType (parentCode, tagCode, tagType, ttAscii);
1114 
1115             ParseDualStringTag (stream,
1116                                 parentCode,
1117                                 tagCode,
1118                                 tagCount,
1119                                 fCopyright,
1120                                 fCopyright2);
1121 
1122             #if qDNGValidate
1123 
1124             if (gVerbose)
1125                 {
1126 
1127                 printf ("Copyright: ");
1128 
1129                 DumpString (fCopyright);
1130 
1131                 if (fCopyright2.Get () [0] != 0)
1132                     {
1133 
1134                     printf (" ");
1135 
1136                     DumpString (fCopyright2);
1137 
1138                     }
1139 
1140                 printf ("\n");
1141 
1142                 }
1143 
1144             #endif
1145 
1146             break;
1147 
1148             }
1149 
1150         case tcTIFF_EP_StandardID:
1151             {
1152 
1153             CheckTagType (parentCode, tagCode, tagType, ttByte);
1154 
1155             CheckTagCount (parentCode, tagCode, tagCount, 4);
1156 
1157             uint32 b0 = stream.Get_uint8 ();
1158             uint32 b1 = stream.Get_uint8 ();
1159             uint32 b2 = stream.Get_uint8 ();
1160             uint32 b3 = stream.Get_uint8 ();
1161 
1162             fTIFF_EP_StandardID = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
1163 
1164             #if qDNGValidate
1165 
1166             if (gVerbose)
1167                 {
1168                 printf ("TIFF/EPStandardID: %u.%u.%u.%u\n",
1169                         (unsigned) b0,
1170                         (unsigned) b1,
1171                         (unsigned) b2,
1172                         (unsigned) b3);
1173                 }
1174 
1175             #endif
1176 
1177             break;
1178 
1179             }
1180 
1181         case tcCameraSerialNumber:
1182         case tcKodakCameraSerialNumber:     // Kodak uses a very similar tag.
1183             {
1184 
1185             CheckTagType (parentCode, tagCode, tagType, ttAscii);
1186 
1187             ParseStringTag (stream,
1188                             parentCode,
1189                             tagCode,
1190                             tagCount,
1191                             fCameraSerialNumber);
1192 
1193             #if qDNGValidate
1194 
1195             if (gVerbose)
1196                 {
1197 
1198                 printf ("%s: ", LookupTagCode (parentCode, tagCode));
1199 
1200                 DumpString (fCameraSerialNumber);
1201 
1202                 printf ("\n");
1203 
1204                 }
1205 
1206             #endif
1207 
1208             break;
1209 
1210             }
1211 
1212         case tcLensInfo:
1213             {
1214 
1215             CheckTagType (parentCode, tagCode, tagType, ttRational);
1216 
1217             if (!CheckTagCount (parentCode, tagCode, tagCount, 4))
1218                 return false;
1219 
1220             fLensInfo [0] = stream.TagValue_urational (tagType);
1221             fLensInfo [1] = stream.TagValue_urational (tagType);
1222             fLensInfo [2] = stream.TagValue_urational (tagType);
1223             fLensInfo [3] = stream.TagValue_urational (tagType);
1224 
1225             // Some third party software wrote zero rather and undefined values
1226             // for unknown entries.  Work around this bug.
1227 
1228             for (uint32 j = 0; j < 4; j++)
1229                 {
1230 
1231                 if (fLensInfo [j].IsValid () && fLensInfo [j].As_real64 () <= 0.0)
1232                     {
1233 
1234                     fLensInfo [j] = dng_urational (0, 0);
1235 
1236                     #if qDNGValidate
1237 
1238                     ReportWarning ("Zero entry in LensInfo tag--should be undefined");
1239 
1240                     #endif
1241 
1242                     }
1243 
1244                 }
1245 
1246             #if qDNGValidate
1247 
1248             if (gVerbose)
1249                 {
1250 
1251                 printf ("LensInfo: ");
1252 
1253                 real64 minFL = fLensInfo [0].As_real64 ();
1254                 real64 maxFL = fLensInfo [1].As_real64 ();
1255 
1256                 if (minFL == maxFL)
1257                     printf ("%0.1f mm", minFL);
1258                 else
1259                     printf ("%0.1f-%0.1f mm", minFL, maxFL);
1260 
1261                 if (fLensInfo [2].d)
1262                     {
1263 
1264                     real64 minFS = fLensInfo [2].As_real64 ();
1265                     real64 maxFS = fLensInfo [3].As_real64 ();
1266 
1267                     if (minFS == maxFS)
1268                         printf (" f/%0.1f", minFS);
1269                     else
1270                         printf (" f/%0.1f-%0.1f", minFS, maxFS);
1271 
1272                     }
1273 
1274                 printf ("\n");
1275 
1276                 }
1277 
1278             #endif
1279 
1280             break;
1281 
1282             }
1283 
1284         default:
1285             {
1286 
1287             return false;
1288 
1289             }
1290 
1291         }
1292 
1293     return true;
1294 
1295     }
1296 
1297 /*****************************************************************************/
1298 
1299 // Parses tags that should only appear in IFD 0 or the main image IFD.
1300 
1301 bool dng_exif::Parse_ifd0_main (dng_stream &stream,
1302                                 dng_shared & /* shared */,
1303                                 uint32 parentCode,
1304                                 uint32 tagCode,
1305                                 uint32 tagType,
1306                                 uint32 tagCount,
1307                                 uint64 /* tagOffset */)
1308     {
1309 
1310     switch (tagCode)
1311         {
1312 
1313         case tcFocalPlaneXResolution:
1314             {
1315 
1316             CheckTagType (parentCode, tagCode, tagType, ttRational);
1317 
1318             CheckTagCount (parentCode, tagCode, tagCount, 1);
1319 
1320             fFocalPlaneXResolution = stream.TagValue_urational (tagType);
1321 
1322             #if qDNGValidate
1323 
1324             if (gVerbose)
1325                 {
1326 
1327                 printf ("FocalPlaneXResolution: %0.4f\n",
1328                         fFocalPlaneXResolution.As_real64 ());
1329 
1330                 }
1331 
1332             #endif
1333 
1334             break;
1335 
1336             }
1337 
1338         case tcFocalPlaneYResolution:
1339             {
1340 
1341             CheckTagType (parentCode, tagCode, tagType, ttRational);
1342 
1343             CheckTagCount (parentCode, tagCode, tagCount, 1);
1344 
1345             fFocalPlaneYResolution = stream.TagValue_urational (tagType);
1346 
1347             #if qDNGValidate
1348 
1349             if (gVerbose)
1350                 {
1351 
1352                 printf ("FocalPlaneYResolution: %0.4f\n",
1353                         fFocalPlaneYResolution.As_real64 ());
1354 
1355                 }
1356 
1357             #endif
1358 
1359             break;
1360 
1361             }
1362 
1363         case tcFocalPlaneResolutionUnit:
1364             {
1365 
1366             CheckTagType (parentCode, tagCode, tagType, ttShort);
1367 
1368             CheckTagCount (parentCode, tagCode, tagCount, 1);
1369 
1370             fFocalPlaneResolutionUnit = stream.TagValue_uint32 (tagType);
1371 
1372             #if qDNGValidate
1373 
1374             if (gVerbose)
1375                 {
1376 
1377                 printf ("FocalPlaneResolutionUnit: %s\n",
1378                         LookupResolutionUnit (fFocalPlaneResolutionUnit));
1379 
1380                 }
1381 
1382             #endif
1383 
1384             break;
1385 
1386             }
1387 
1388         case tcSensingMethod:
1389             {
1390 
1391             CheckTagType (parentCode, tagCode, tagType, ttShort);
1392 
1393             CheckTagCount (parentCode, tagCode, tagCount, 1);
1394 
1395             fSensingMethod = stream.TagValue_uint32 (tagType);
1396 
1397             #if qDNGValidate
1398 
1399             if (gVerbose)
1400                 {
1401 
1402                 printf ("SensingMethod: %s\n",
1403                         LookupSensingMethod (fSensingMethod));
1404 
1405                 }
1406 
1407             #endif
1408 
1409             break;
1410 
1411             }
1412 
1413         default:
1414             {
1415 
1416             return false;
1417 
1418             }
1419 
1420         }
1421 
1422     return true;
1423 
1424     }
1425 
1426 /*****************************************************************************/
1427 
1428 // Parses tags that should only appear in IFD 0 or EXIF IFD.
1429 
1430 bool dng_exif::Parse_ifd0_exif (dng_stream &stream,
1431                                 dng_shared & /* shared */,
1432                                 uint32 parentCode,
1433                                 uint32 tagCode,
1434                                 uint32 tagType,
1435                                 uint32 tagCount,
1436                                 uint64 /* tagOffset */)
1437     {
1438 
1439     switch (tagCode)
1440         {
1441 
1442         case tcBatteryLevel:
1443             {
1444 
1445             CheckTagType (parentCode, tagCode, tagType, ttRational, ttAscii);
1446 
1447             if (tagType == ttAscii)
1448                 {
1449 
1450                 ParseStringTag (stream,
1451                                 parentCode,
1452                                 tagCode,
1453                                 tagCount,
1454                                 fBatteryLevelA);
1455 
1456                 }
1457 
1458             else
1459                 {
1460 
1461                 CheckTagCount (parentCode, tagCode, tagCount, 1);
1462 
1463                 fBatteryLevelR = stream.TagValue_urational (tagType);
1464 
1465                 }
1466 
1467             #if qDNGValidate
1468 
1469             if (gVerbose)
1470                 {
1471 
1472                 printf ("BatteryLevel: ");
1473 
1474                 if (tagType == ttAscii)
1475                     {
1476 
1477                     DumpString (fBatteryLevelA);
1478 
1479                     }
1480 
1481                 else
1482                     {
1483 
1484                     printf ("%0.2f", fBatteryLevelR.As_real64 ());
1485 
1486                     }
1487 
1488                 printf ("\n");
1489 
1490                 }
1491 
1492             #endif
1493 
1494             break;
1495 
1496             }
1497 
1498         case tcExposureTime:
1499             {
1500 
1501             CheckTagType (parentCode, tagCode, tagType, ttRational);
1502 
1503             CheckTagCount (parentCode, tagCode, tagCount, 1);
1504 
1505             dng_urational et = stream.TagValue_urational (tagType);
1506 
1507             SetExposureTime (et.As_real64 (), true);
1508 
1509             #if qDNGValidate
1510 
1511             if (gVerbose)
1512                 {
1513 
1514                 printf ("ExposureTime: ");
1515 
1516                 DumpExposureTime (et.As_real64 ());
1517 
1518                 printf ("\n");
1519 
1520                 }
1521 
1522             if (et.As_real64 () <= 0.0)
1523                 {
1524 
1525                 ReportWarning ("The ExposureTime is <= 0");
1526 
1527                 }
1528 
1529             #endif
1530 
1531             break;
1532 
1533             }
1534 
1535         case tcFNumber:
1536             {
1537 
1538             CheckTagType (parentCode, tagCode, tagType, ttRational);
1539 
1540             CheckTagCount (parentCode, tagCode, tagCount, 1);
1541 
1542             dng_urational fs = stream.TagValue_urational (tagType);
1543 
1544             // Sometimes "unknown" is recorded as zero.
1545 
1546             if (fs.As_real64 () <= 0.0)
1547                 {
1548                 fs.Clear ();
1549                 }
1550 
1551             #if qDNGValidate
1552 
1553             if (gVerbose)
1554                 {
1555 
1556                 printf ("FNumber: f/%0.2f\n",
1557                         fs.As_real64 ());
1558 
1559                 }
1560 
1561             #endif
1562 
1563             SetFNumber (fs.As_real64 ());
1564 
1565             break;
1566 
1567             }
1568 
1569         case tcExposureProgram:
1570             {
1571 
1572             CheckTagType (parentCode, tagCode, tagType, ttShort);
1573 
1574             CheckTagCount (parentCode, tagCode, tagCount, 1);
1575 
1576             fExposureProgram = stream.TagValue_uint32 (tagType);
1577 
1578             #if qDNGValidate
1579 
1580             if (gVerbose)
1581                 {
1582 
1583                 printf ("ExposureProgram: %s\n",
1584                         LookupExposureProgram (fExposureProgram));
1585 
1586                 }
1587 
1588             #endif
1589 
1590             break;
1591 
1592             }
1593 
1594         case tcISOSpeedRatings:
1595             {
1596 
1597             CheckTagType (parentCode, tagCode, tagType, ttShort);
1598 
1599             CheckTagCount (parentCode, tagCode, tagCount, 1, 3);
1600 
1601             for (uint32 j = 0; j < tagCount && j < 3; j++)
1602                 {
1603 
1604                 fISOSpeedRatings [j] = stream.TagValue_uint32 (tagType);
1605 
1606                 }
1607 
1608             #if qDNGValidate
1609 
1610             if (gVerbose)
1611                 {
1612 
1613                 printf ("ISOSpeedRatings:");
1614 
1615                 for (uint32 j = 0; j < tagCount && j < 3; j++)
1616                     {
1617 
1618                     printf (" %u", (unsigned) fISOSpeedRatings [j]);
1619 
1620                     }
1621 
1622                 printf ("\n");
1623 
1624                 }
1625 
1626             #endif
1627 
1628             break;
1629 
1630             }
1631 
1632         case tcSensitivityType:
1633             {
1634 
1635             CheckTagType (parentCode, tagCode, tagType, ttShort);
1636 
1637             CheckTagCount (parentCode, tagCode, tagCount, 1);
1638 
1639             fSensitivityType = (uint32) stream.Get_uint16 ();
1640 
1641             #if qDNGValidate
1642 
1643             if (gVerbose)
1644                 {
1645 
1646                 printf ("SensitivityType: %s\n",
1647                         LookupSensitivityType (fSensitivityType));
1648 
1649                 }
1650 
1651             #endif
1652 
1653             break;
1654 
1655             }
1656 
1657         case tcStandardOutputSensitivity:
1658             {
1659 
1660             CheckTagType (parentCode, tagCode, tagType, ttLong);
1661 
1662             CheckTagCount (parentCode, tagCode, tagCount, 1);
1663 
1664             fStandardOutputSensitivity = stream.TagValue_uint32 (tagType);
1665 
1666             #if qDNGValidate
1667 
1668             if (gVerbose)
1669                 {
1670 
1671                 printf ("StandardOutputSensitivity: %u\n",
1672                         (unsigned) fStandardOutputSensitivity);
1673 
1674                 }
1675 
1676             #endif
1677 
1678             break;
1679 
1680             }
1681 
1682         case tcRecommendedExposureIndex:
1683             {
1684 
1685             CheckTagType (parentCode, tagCode, tagType, ttLong);
1686 
1687             CheckTagCount (parentCode, tagCode, tagCount, 1);
1688 
1689             fRecommendedExposureIndex = stream.TagValue_uint32 (tagType);
1690 
1691             #if qDNGValidate
1692 
1693             if (gVerbose)
1694                 {
1695 
1696                 printf ("RecommendedExposureIndex: %u\n",
1697                         (unsigned) fRecommendedExposureIndex);
1698 
1699                 }
1700 
1701             #endif
1702 
1703             break;
1704 
1705             }
1706 
1707         case tcISOSpeed:
1708             {
1709 
1710             CheckTagType (parentCode, tagCode, tagType, ttLong);
1711 
1712             CheckTagCount (parentCode, tagCode, tagCount, 1);
1713 
1714             fISOSpeed = stream.TagValue_uint32 (tagType);
1715 
1716             #if qDNGValidate
1717 
1718             if (gVerbose)
1719                 {
1720 
1721                 printf ("ISOSpeed: %u\n",
1722                         (unsigned) fISOSpeed);
1723 
1724                 }
1725 
1726             #endif
1727 
1728             break;
1729 
1730             }
1731 
1732         case tcISOSpeedLatitudeyyy:
1733             {
1734 
1735             CheckTagType (parentCode, tagCode, tagType, ttLong);
1736 
1737             CheckTagCount (parentCode, tagCode, tagCount, 1);
1738 
1739             fISOSpeedLatitudeyyy = stream.TagValue_uint32 (tagType);
1740 
1741             #if qDNGValidate
1742 
1743             if (gVerbose)
1744                 {
1745 
1746                 printf ("ISOSpeedLatitudeyyy: %u\n",
1747                         (unsigned) fISOSpeedLatitudeyyy);
1748 
1749                 }
1750 
1751             #endif
1752 
1753             break;
1754 
1755             }
1756 
1757         case tcISOSpeedLatitudezzz:
1758             {
1759 
1760             CheckTagType (parentCode, tagCode, tagType, ttLong);
1761 
1762             CheckTagCount (parentCode, tagCode, tagCount, 1);
1763 
1764             fISOSpeedLatitudezzz = stream.TagValue_uint32 (tagType);
1765 
1766             #if qDNGValidate
1767 
1768             if (gVerbose)
1769                 {
1770 
1771                 printf ("ISOSpeedLatitudezzz: %u\n",
1772                         (unsigned) fISOSpeedLatitudezzz);
1773 
1774                 }
1775 
1776             #endif
1777 
1778             break;
1779 
1780             }
1781 
1782         case tcTimeZoneOffset:
1783             {
1784 
1785             CheckTagType (parentCode, tagCode, tagType, ttSShort);
1786 
1787             CheckTagCount (parentCode, tagCode, tagCount, 1, 2);
1788 
1789             dng_time_zone zoneOriginal;
1790 
1791             zoneOriginal.SetOffsetHours (stream.TagValue_int32 (tagType));
1792 
1793             fDateTimeOriginal.SetZone (zoneOriginal);
1794 
1795             #if 0   // MWG: Not filling in time zones unless we are sure.
1796 
1797             // Note that there is no "TimeZoneOffsetDigitized" field, so
1798             // we assume the same tone zone as the original.
1799 
1800             fDateTimeDigitized.SetZone (zoneOriginal);
1801 
1802             #endif
1803 
1804             dng_time_zone zoneCurrent;
1805 
1806             if (tagCount >= 2)
1807                 {
1808 
1809                 zoneCurrent.SetOffsetHours (stream.TagValue_int32 (tagType));
1810 
1811                 fDateTime.SetZone (zoneCurrent);
1812 
1813                 }
1814 
1815             #if qDNGValidate
1816 
1817             if (gVerbose)
1818                 {
1819 
1820                 printf ("TimeZoneOffset: DateTimeOriginal = %d",
1821                         (int) zoneOriginal.ExactHourOffset ());
1822 
1823                 if (tagCount >= 2)
1824                     {
1825 
1826                     printf (", DateTime = %d",
1827                             (int) zoneCurrent.ExactHourOffset ());
1828 
1829                     }
1830 
1831                 printf ("\n");
1832 
1833                 }
1834 
1835             #endif
1836 
1837             break;
1838 
1839             }
1840 
1841         case tcSelfTimerMode:
1842             {
1843 
1844             CheckTagType (parentCode, tagCode, tagType, ttShort);
1845 
1846             CheckTagCount (parentCode, tagCode, tagCount, 1);
1847 
1848             fSelfTimerMode = stream.TagValue_uint32 (tagType);
1849 
1850             #if qDNGValidate
1851 
1852             if (gVerbose)
1853                 {
1854 
1855                 printf ("SelfTimerMode: ");
1856 
1857                 if (fSelfTimerMode)
1858                     {
1859 
1860                     printf ("%u sec", (unsigned) fSelfTimerMode);
1861 
1862                     }
1863 
1864                 else
1865                     {
1866 
1867                     printf ("Off");
1868 
1869                     }
1870 
1871                 printf ("\n");
1872 
1873                 }
1874 
1875             #endif
1876 
1877             break;
1878 
1879             }
1880 
1881         case tcExifVersion:
1882             {
1883 
1884             CheckTagType (parentCode, tagCode, tagType, ttUndefined);
1885 
1886             CheckTagCount (parentCode, tagCode, tagCount, 4);
1887 
1888             uint32 b0 = stream.Get_uint8 ();
1889             uint32 b1 = stream.Get_uint8 ();
1890             uint32 b2 = stream.Get_uint8 ();
1891             uint32 b3 = stream.Get_uint8 ();
1892 
1893             fExifVersion = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
1894 
1895             #if qDNGValidate
1896 
1897             if (gVerbose)
1898                 {
1899 
1900                 real64 x = (b0 - '0') * 10.00 +
1901                            (b1 - '0') *  1.00 +
1902                            (b2 - '0') *  0.10 +
1903                            (b3 - '0') *  0.01;
1904 
1905                 printf ("ExifVersion: %0.2f\n", x);
1906 
1907                 }
1908 
1909             #endif
1910 
1911             break;
1912 
1913             }
1914 
1915         case tcDateTimeOriginal:
1916             {
1917 
1918             uint64 tagPosition = stream.PositionInOriginalFile ();
1919 
1920             dng_date_time dt;
1921 
1922             if (!ParseDateTimeTag (stream,
1923                                    parentCode,
1924                                    tagCode,
1925                                    tagType,
1926                                    tagCount,
1927                                    dt))
1928                 {
1929                 return false;
1930                 }
1931 
1932             fDateTimeOriginal.SetDateTime (dt);
1933 
1934             fDateTimeOriginalStorageInfo = dng_date_time_storage_info (tagPosition,
1935                                                                        dng_date_time_format_exif);
1936 
1937             #if qDNGValidate
1938 
1939             if (gVerbose)
1940                 {
1941 
1942                 printf ("DateTimeOriginal: ");
1943 
1944                 DumpDateTime (fDateTimeOriginal.DateTime ());
1945 
1946                 printf ("\n");
1947 
1948                 }
1949 
1950             #endif
1951 
1952             break;
1953 
1954             }
1955 
1956         case tcDateTimeDigitized:
1957             {
1958 
1959             uint64 tagPosition = stream.PositionInOriginalFile ();
1960 
1961             dng_date_time dt;
1962 
1963             if (!ParseDateTimeTag (stream,
1964                                    parentCode,
1965                                    tagCode,
1966                                    tagType,
1967                                    tagCount,
1968                                    dt))
1969                 {
1970                 return false;
1971                 }
1972 
1973             fDateTimeDigitized.SetDateTime (dt);
1974 
1975             fDateTimeDigitizedStorageInfo = dng_date_time_storage_info (tagPosition,
1976                                                                         dng_date_time_format_exif);
1977 
1978             #if qDNGValidate
1979 
1980             if (gVerbose)
1981                 {
1982 
1983                 printf ("DateTimeDigitized: ");
1984 
1985                 DumpDateTime (fDateTimeDigitized.DateTime ());
1986 
1987                 printf ("\n");
1988 
1989                 }
1990 
1991             #endif
1992 
1993             break;
1994 
1995             }
1996 
1997         case tcOffsetTime:
1998             {
1999 
2000             CheckTagType (parentCode, tagCode, tagType, ttAscii);
2001 
2002             dng_string offsetTime;
2003 
2004             ParseStringTag (stream,
2005                             parentCode,
2006                             tagCode,
2007                             tagCount,
2008                             offsetTime);
2009 
2010             fDateTime.SetOffsetTime (offsetTime);
2011 
2012             // The offset time tags were added to EXIF spec 2.3.1.
2013             // We need EXIF spec version to figure out legacy fake time
2014             // zones in XMP, so force a correct exif spec version if
2015             // these EXIF tags are used.
2016 
2017             if (!AtLeastVersion0231 ())
2018                 {
2019                 SetVersion0231 ();
2020                 }
2021 
2022             #if qDNGValidate
2023 
2024             if (gVerbose)
2025                 {
2026 
2027                 printf ("OffsetTime: ");
2028 
2029                 DumpString (offsetTime);
2030 
2031                 printf ("\n");
2032 
2033                 }
2034 
2035             #endif
2036 
2037             break;
2038 
2039             }
2040 
2041         case tcOffsetTimeOriginal:
2042             {
2043 
2044             CheckTagType (parentCode, tagCode, tagType, ttAscii);
2045 
2046             dng_string offsetTime;
2047 
2048             ParseStringTag (stream,
2049                             parentCode,
2050                             tagCode,
2051                             tagCount,
2052                             offsetTime);
2053 
2054             fDateTimeOriginal.SetOffsetTime (offsetTime);
2055 
2056             // The offset time tags were added to EXIF spec 2.3.1.
2057             // We need EXIF spec version to figure out legacy fake time
2058             // zones in XMP, so force a correct exif spec version if
2059             // these EXIF tags are used.
2060 
2061             if (!AtLeastVersion0231 ())
2062                 {
2063                 SetVersion0231 ();
2064                 }
2065 
2066             #if qDNGValidate
2067 
2068             if (gVerbose)
2069                 {
2070 
2071                 printf ("OffsetTimeOriginal: ");
2072 
2073                 DumpString (offsetTime);
2074 
2075                 printf ("\n");
2076 
2077                 }
2078 
2079             #endif
2080 
2081             break;
2082 
2083             }
2084 
2085         case tcOffsetTimeDigitized:
2086             {
2087 
2088             CheckTagType (parentCode, tagCode, tagType, ttAscii);
2089 
2090             dng_string offsetTime;
2091 
2092             ParseStringTag (stream,
2093                             parentCode,
2094                             tagCode,
2095                             tagCount,
2096                             offsetTime);
2097 
2098             fDateTimeDigitized.SetOffsetTime (offsetTime);
2099 
2100             // The offset time tags were added to EXIF spec 2.3.1.
2101             // We need EXIF spec version to figure out legacy fake time
2102             // zones in XMP, so force a correct exif spec version if
2103             // these EXIF tags are used.
2104 
2105             if (!AtLeastVersion0231 ())
2106                 {
2107                 SetVersion0231 ();
2108                 }
2109 
2110             #if qDNGValidate
2111 
2112             if (gVerbose)
2113                 {
2114 
2115                 printf ("OffsetTimeDigitized: ");
2116 
2117                 DumpString (offsetTime);
2118 
2119                 printf ("\n");
2120 
2121                 }
2122 
2123             #endif
2124 
2125             break;
2126 
2127             }
2128 
2129         case tcComponentsConfiguration:
2130             {
2131 
2132             CheckTagType (parentCode, tagCode, tagType, ttUndefined);
2133 
2134             CheckTagCount (parentCode, tagCode, tagCount, 4);
2135 
2136             uint32 b0 = stream.Get_uint8 ();
2137             uint32 b1 = stream.Get_uint8 ();
2138             uint32 b2 = stream.Get_uint8 ();
2139             uint32 b3 = stream.Get_uint8 ();
2140 
2141             fComponentsConfiguration = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
2142 
2143             #if qDNGValidate
2144 
2145             if (gVerbose)
2146                 {
2147 
2148                 printf ("ComponentsConfiguration: %s %s %s %s\n",
2149                         LookupComponent (b0),
2150                         LookupComponent (b1),
2151                         LookupComponent (b2),
2152                         LookupComponent (b3));
2153 
2154                 }
2155 
2156             #endif
2157 
2158             break;
2159 
2160             }
2161 
2162         case tcCompressedBitsPerPixel:
2163             {
2164 
2165             CheckTagType (parentCode, tagCode, tagType, ttRational);
2166 
2167             CheckTagCount (parentCode, tagCode, tagCount, 1);
2168 
2169             fCompresssedBitsPerPixel = stream.TagValue_urational (tagType);
2170 
2171             #if qDNGValidate
2172 
2173             if (gVerbose)
2174                 {
2175 
2176                 printf ("CompressedBitsPerPixel: %0.2f\n",
2177                         fCompresssedBitsPerPixel.As_real64 ());
2178 
2179                 }
2180 
2181             #endif
2182 
2183             break;
2184 
2185             }
2186 
2187         case tcShutterSpeedValue:
2188             {
2189 
2190             CheckTagType (parentCode, tagCode, tagType, ttSRational);
2191 
2192             CheckTagCount (parentCode, tagCode, tagCount, 1);
2193 
2194             dng_srational ss = stream.TagValue_srational (tagType);
2195 
2196             #if qDNGValidate
2197 
2198             if (gVerbose)
2199                 {
2200 
2201                 printf ("ShutterSpeedValue: ");
2202 
2203                 real64 x = pow (2.0, -ss.As_real64 ());
2204 
2205                 DumpExposureTime (x);
2206 
2207                 printf ("\n");
2208 
2209                 }
2210 
2211             // The ExposureTime and ShutterSpeedValue tags should be consistent.
2212 
2213             if (fExposureTime.IsValid ())
2214                 {
2215 
2216                 real64 et = fExposureTime.As_real64 ();
2217 
2218                 real64 tv1 = -1.0 * log (et) / log (2.0);
2219 
2220                 real64 tv2 = ss.As_real64 ();
2221 
2222                 // Make sure they are within 0.25 APEX values.
2223 
2224                 if (Abs_real64 (tv1 - tv2) > 0.25)
2225                     {
2226 
2227                     ReportWarning ("The ExposureTime and ShutterSpeedValue tags have conflicting values");
2228 
2229                     }
2230 
2231                 }
2232 
2233             #endif
2234 
2235             SetShutterSpeedValue (ss.As_real64 ());
2236 
2237             break;
2238 
2239             }
2240 
2241         case tcApertureValue:
2242             {
2243 
2244             CheckTagType (parentCode, tagCode, tagType, ttRational);
2245 
2246             CheckTagCount (parentCode, tagCode, tagCount, 1);
2247 
2248             dng_urational av = stream.TagValue_urational (tagType);
2249 
2250             #if qDNGValidate
2251 
2252             if (gVerbose)
2253                 {
2254 
2255                 real64 x = pow (2.0, 0.5 * av.As_real64 ());
2256 
2257                 printf ("ApertureValue: f/%0.2f\n", x);
2258 
2259                 }
2260 
2261             // The FNumber and ApertureValue tags should be consistent.
2262 
2263             if (fFNumber.IsValid () && av.IsValid ())
2264                 {
2265 
2266                 real64 fs = fFNumber.As_real64 ();
2267 
2268                 real64 av1 = FNumberToApertureValue (fs);
2269 
2270                 real64 av2 = av.As_real64 ();
2271 
2272                 if (Abs_real64 (av1 - av2) > 0.25)
2273                     {
2274 
2275                     ReportWarning ("The FNumber and ApertureValue tags have conflicting values");
2276 
2277                     }
2278 
2279                 }
2280 
2281             #endif
2282 
2283             SetApertureValue (av.As_real64 ());
2284 
2285             break;
2286 
2287             }
2288 
2289         case tcBrightnessValue:
2290             {
2291 
2292             CheckTagType (parentCode, tagCode, tagType, ttSRational);
2293 
2294             CheckTagCount (parentCode, tagCode, tagCount, 1);
2295 
2296             fBrightnessValue = stream.TagValue_srational (tagType);
2297 
2298             #if qDNGValidate
2299 
2300             if (gVerbose)
2301                 {
2302 
2303                 printf ("BrightnessValue: %0.2f\n",
2304                         fBrightnessValue.As_real64 ());
2305 
2306                 }
2307 
2308             #endif
2309 
2310             break;
2311 
2312             }
2313 
2314         case tcExposureBiasValue:
2315             {
2316 
2317             CheckTagType (parentCode, tagCode, tagType, ttSRational);
2318 
2319             CheckTagCount (parentCode, tagCode, tagCount, 1);
2320 
2321             fExposureBiasValue = stream.TagValue_srational (tagType);
2322 
2323             #if qDNGValidate
2324 
2325             if (gVerbose)
2326                 {
2327 
2328                 printf ("ExposureBiasValue: %0.2f\n",
2329                         fExposureBiasValue.As_real64 ());
2330 
2331                 }
2332 
2333             #endif
2334 
2335             break;
2336 
2337             }
2338 
2339         case tcMaxApertureValue:
2340             {
2341 
2342             CheckTagType (parentCode, tagCode, tagType, ttRational);
2343 
2344             CheckTagCount (parentCode, tagCode, tagCount, 1);
2345 
2346             fMaxApertureValue = stream.TagValue_urational (tagType);
2347 
2348             #if qDNGValidate
2349 
2350             if (gVerbose)
2351                 {
2352 
2353                 real64 x = pow (2.0, 0.5 * fMaxApertureValue.As_real64 ());
2354 
2355                 printf ("MaxApertureValue: f/%0.1f\n", x);
2356 
2357                 }
2358 
2359             #endif
2360 
2361             break;
2362 
2363             }
2364 
2365         case tcSubjectDistance:
2366             {
2367 
2368             CheckTagType (parentCode, tagCode, tagType, ttRational);
2369 
2370             CheckTagCount (parentCode, tagCode, tagCount, 1);
2371 
2372             fSubjectDistance = stream.TagValue_urational (tagType);
2373 
2374             fApproxFocusDistance = fSubjectDistance;
2375 
2376             #if qDNGValidate
2377 
2378             if (gVerbose)
2379                 {
2380 
2381                 printf ("SubjectDistance: %u/%u\n",
2382                         (unsigned) fSubjectDistance.n,
2383                         (unsigned) fSubjectDistance.d);
2384 
2385                 }
2386 
2387             #endif
2388 
2389             break;
2390 
2391             }
2392 
2393         case tcMeteringMode:
2394             {
2395 
2396             CheckTagType (parentCode, tagCode, tagType, ttShort);
2397 
2398             CheckTagCount (parentCode, tagCode, tagCount, 1);
2399 
2400             fMeteringMode = stream.TagValue_uint32 (tagType);
2401 
2402             #if qDNGValidate
2403 
2404             if (gVerbose)
2405                 {
2406 
2407                 printf ("MeteringMode: %s\n",
2408                         LookupMeteringMode (fMeteringMode));
2409 
2410                 }
2411 
2412             #endif
2413 
2414             break;
2415 
2416             }
2417 
2418         case tcLightSource:
2419             {
2420 
2421             CheckTagType (parentCode, tagCode, tagType, ttShort);
2422 
2423             CheckTagCount (parentCode, tagCode, tagCount, 1);
2424 
2425             fLightSource = stream.TagValue_uint32 (tagType);
2426 
2427             #if qDNGValidate
2428 
2429             if (gVerbose)
2430                 {
2431 
2432                 printf ("LightSource: %s\n",
2433                         LookupLightSource (fLightSource));
2434 
2435                 }
2436 
2437             #endif
2438 
2439             break;
2440 
2441             }
2442 
2443         case tcFlash:
2444             {
2445 
2446             CheckTagType (parentCode, tagCode, tagType, ttShort);
2447 
2448             CheckTagCount (parentCode, tagCode, tagCount, 1);
2449 
2450             fFlash = stream.TagValue_uint32 (tagType);
2451 
2452             #if qDNGValidate
2453 
2454             if (gVerbose)
2455                 {
2456 
2457                 printf ("Flash: %u\n", (unsigned) fFlash);
2458 
2459                 if ((fFlash >> 5) & 1)
2460                     {
2461                     printf ("    No flash function\n");
2462                     }
2463 
2464                 else
2465                     {
2466 
2467                     if (fFlash & 0x1)
2468                         {
2469 
2470                         printf ("    Flash fired\n");
2471 
2472                         switch ((fFlash >> 1) & 0x3)
2473                             {
2474 
2475                             case 2:
2476                                 printf ("    Strobe return light not detected\n");
2477                                 break;
2478 
2479                             case 3:
2480                                 printf ("    Strobe return light detected\n");
2481                                 break;
2482 
2483                             }
2484 
2485                         }
2486 
2487                     else
2488                         {
2489                         printf ("    Flash did not fire\n");
2490                         }
2491 
2492                     switch ((fFlash >> 3) & 0x3)
2493                         {
2494 
2495                         case 1:
2496                             printf ("    Compulsory flash firing\n");
2497                             break;
2498 
2499                         case 2:
2500                             printf ("    Compulsory flash suppression\n");
2501                             break;
2502 
2503                         case 3:
2504                             printf ("    Auto mode\n");
2505                             break;
2506 
2507                         }
2508 
2509                     if ((fFlash >> 6) & 1)
2510                         {
2511                         printf ("    Red-eye reduction supported\n");
2512                         }
2513 
2514                     }
2515 
2516                 }
2517 
2518             #endif
2519 
2520             break;
2521 
2522             }
2523 
2524         case tcFocalLength:
2525             {
2526 
2527             CheckTagType (parentCode, tagCode, tagType, ttRational);
2528 
2529             CheckTagCount (parentCode, tagCode, tagCount, 1);
2530 
2531             fFocalLength = stream.TagValue_urational (tagType);
2532 
2533             // Sometimes "unknown" is recorded as zero.
2534 
2535             if (fFocalLength.As_real64 () <= 0.0)
2536                 {
2537                 fFocalLength.Clear ();
2538                 }
2539 
2540             #if qDNGValidate
2541 
2542             if (gVerbose)
2543                 {
2544 
2545                 printf ("FocalLength: %0.1f mm\n",
2546                         fFocalLength.As_real64 ());
2547 
2548                 }
2549 
2550             #endif
2551 
2552             break;
2553 
2554             }
2555 
2556         case tcImageNumber:
2557             {
2558 
2559             CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong);
2560 
2561             CheckTagCount (parentCode, tagCode, tagCount, 1);
2562 
2563             fImageNumber = stream.TagValue_uint32 (tagType);
2564 
2565             #if qDNGValidate
2566 
2567             if (gVerbose)
2568                 {
2569                 printf ("ImageNumber: %u\n", (unsigned) fImageNumber);
2570                 }
2571 
2572             #endif
2573 
2574             break;
2575 
2576             }
2577 
2578         case tcExposureIndex:
2579         case tcExposureIndexExif:
2580             {
2581 
2582             CheckTagType (parentCode, tagCode, tagType, ttRational);
2583 
2584             CheckTagCount (parentCode, tagCode, tagCount, 1);
2585 
2586             fExposureIndex = stream.TagValue_urational (tagType);
2587 
2588             #if qDNGValidate
2589 
2590             if (gVerbose)
2591                 {
2592 
2593                 printf ("%s: ISO %0.1f\n",
2594                         LookupTagCode (parentCode, tagCode),
2595                         fExposureIndex.As_real64 ());
2596 
2597                 }
2598 
2599             #endif
2600 
2601             break;
2602 
2603             }
2604 
2605         case tcUserComment:
2606             {
2607 
2608             CheckTagType (parentCode, tagCode, tagType, ttUndefined);
2609 
2610             ParseEncodedStringTag (stream,
2611                                    parentCode,
2612                                    tagCode,
2613                                    tagCount,
2614                                    fUserComment);
2615 
2616             #if qDNGValidate
2617 
2618             if (gVerbose)
2619                 {
2620 
2621                 printf ("UserComment: ");
2622 
2623                 DumpString (fUserComment);
2624 
2625                 printf ("\n");
2626 
2627                 }
2628 
2629             #endif
2630 
2631             break;
2632 
2633             }
2634 
2635         case tcSubsecTime:
2636             {
2637 
2638             CheckTagType (parentCode, tagCode, tagType, ttAscii);
2639 
2640             dng_string subsecs;
2641 
2642             ParseStringTag (stream,
2643                             parentCode,
2644                             tagCode,
2645                             tagCount,
2646                             subsecs);
2647 
2648             fDateTime.SetSubseconds (subsecs);
2649 
2650             #if qDNGValidate
2651 
2652             if (gVerbose)
2653                 {
2654 
2655                 printf ("SubsecTime: ");
2656 
2657                 DumpString (subsecs);
2658 
2659                 printf ("\n");
2660 
2661                 }
2662 
2663             #endif
2664 
2665             break;
2666 
2667             }
2668 
2669         case tcSubsecTimeOriginal:
2670             {
2671 
2672             CheckTagType (parentCode, tagCode, tagType, ttAscii);
2673 
2674             dng_string subsecs;
2675 
2676             ParseStringTag (stream,
2677                             parentCode,
2678                             tagCode,
2679                             tagCount,
2680                             subsecs);
2681 
2682             fDateTimeOriginal.SetSubseconds (subsecs);
2683 
2684             #if qDNGValidate
2685 
2686             if (gVerbose)
2687                 {
2688 
2689                 printf ("SubsecTimeOriginal: ");
2690 
2691                 DumpString (subsecs);
2692 
2693                 printf ("\n");
2694 
2695                 }
2696 
2697             #endif
2698 
2699             break;
2700 
2701             }
2702 
2703         case tcSubsecTimeDigitized:
2704             {
2705 
2706             CheckTagType (parentCode, tagCode, tagType, ttAscii);
2707 
2708             dng_string subsecs;
2709 
2710             ParseStringTag (stream,
2711                             parentCode,
2712                             tagCode,
2713                             tagCount,
2714                             subsecs);
2715 
2716             fDateTimeDigitized.SetSubseconds (subsecs);
2717 
2718             #if qDNGValidate
2719 
2720             if (gVerbose)
2721                 {
2722 
2723                 printf ("SubsecTimeDigitized: ");
2724 
2725                 DumpString (subsecs);
2726 
2727                 printf ("\n");
2728 
2729                 }
2730 
2731             #endif
2732 
2733             break;
2734 
2735             }
2736 
2737         case tcTemperature:
2738             {
2739 
2740             CheckTagType (parentCode, tagCode, tagType, ttSRational);
2741 
2742             CheckTagCount (parentCode, tagCode, tagCount, 1);
2743 
2744             fTemperature = stream.TagValue_srational (tagType);
2745 
2746             if (!AtLeastVersion0231 ())
2747                 {
2748                 SetVersion0231 ();
2749                 }
2750 
2751             #if qDNGValidate
2752 
2753             if (gVerbose)
2754                 {
2755 
2756                 printf ("Temperature: %0.1f\n",
2757                         fTemperature.As_real64 ());
2758 
2759                 }
2760 
2761             #endif
2762 
2763             break;
2764 
2765             }
2766 
2767         case tcHumidity:
2768             {
2769 
2770             CheckTagType (parentCode, tagCode, tagType, ttRational);
2771 
2772             CheckTagCount (parentCode, tagCode, tagCount, 1);
2773 
2774             fHumidity = stream.TagValue_urational (tagType);
2775 
2776             if (!AtLeastVersion0231 ())
2777                 {
2778                 SetVersion0231 ();
2779                 }
2780 
2781             #if qDNGValidate
2782 
2783             if (gVerbose)
2784                 {
2785 
2786                 printf ("Humidity: %0.1f\n",
2787                         fHumidity.As_real64 ());
2788 
2789                 }
2790 
2791             #endif
2792 
2793             break;
2794 
2795             }
2796 
2797         case tcPressure:
2798             {
2799 
2800             CheckTagType (parentCode, tagCode, tagType, ttRational);
2801 
2802             CheckTagCount (parentCode, tagCode, tagCount, 1);
2803 
2804             fPressure = stream.TagValue_urational (tagType);
2805 
2806             if (!AtLeastVersion0231 ())
2807                 {
2808                 SetVersion0231 ();
2809                 }
2810 
2811             #if qDNGValidate
2812 
2813             if (gVerbose)
2814                 {
2815 
2816                 printf ("Pressure: %0.1f\n",
2817                         fPressure.As_real64 ());
2818 
2819                 }
2820 
2821             #endif
2822 
2823             break;
2824 
2825             }
2826 
2827         case tcWaterDepth:
2828             {
2829 
2830             CheckTagType (parentCode, tagCode, tagType, ttSRational);
2831 
2832             CheckTagCount (parentCode, tagCode, tagCount, 1);
2833 
2834             fWaterDepth = stream.TagValue_srational (tagType);
2835 
2836             if (!AtLeastVersion0231 ())
2837                 {
2838                 SetVersion0231 ();
2839                 }
2840 
2841             #if qDNGValidate
2842 
2843             if (gVerbose)
2844                 {
2845 
2846                 printf ("WaterDepth: %0.1f\n",
2847                         fWaterDepth.As_real64 ());
2848 
2849                 }
2850 
2851             #endif
2852 
2853             break;
2854 
2855             }
2856 
2857         case tcAcceleration:
2858             {
2859 
2860             CheckTagType (parentCode, tagCode, tagType, ttRational);
2861 
2862             CheckTagCount (parentCode, tagCode, tagCount, 1);
2863 
2864             fAcceleration = stream.TagValue_urational (tagType);
2865 
2866             if (!AtLeastVersion0231 ())
2867                 {
2868                 SetVersion0231 ();
2869                 }
2870 
2871             #if qDNGValidate
2872 
2873             if (gVerbose)
2874                 {
2875 
2876                 printf ("Acceleration: %0.1f\n",
2877                         fAcceleration.As_real64 ());
2878 
2879                 }
2880 
2881             #endif
2882 
2883             break;
2884 
2885             }
2886 
2887         case tcCameraElevationAngle:
2888             {
2889 
2890             CheckTagType (parentCode, tagCode, tagType, ttSRational);
2891 
2892             CheckTagCount (parentCode, tagCode, tagCount, 1);
2893 
2894             fCameraElevationAngle = stream.TagValue_srational (tagType);
2895 
2896             if (!AtLeastVersion0231 ())
2897                 {
2898                 SetVersion0231 ();
2899                 }
2900 
2901             #if qDNGValidate
2902 
2903             if (gVerbose)
2904                 {
2905 
2906                 printf ("CameraElevationAngle: %0.1f\n",
2907                         fCameraElevationAngle.As_real64 ());
2908 
2909                 }
2910 
2911             #endif
2912 
2913             break;
2914 
2915             }
2916 
2917         case tcFlashPixVersion:
2918             {
2919 
2920             CheckTagType (parentCode, tagCode, tagType, ttUndefined);
2921 
2922             CheckTagCount (parentCode, tagCode, tagCount, 4);
2923 
2924             uint32 b0 = stream.Get_uint8 ();
2925             uint32 b1 = stream.Get_uint8 ();
2926             uint32 b2 = stream.Get_uint8 ();
2927             uint32 b3 = stream.Get_uint8 ();
2928 
2929             fFlashPixVersion = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
2930 
2931             #if qDNGValidate
2932 
2933             if (gVerbose)
2934                 {
2935 
2936                 real64 x = (b0 - '0') * 10.00 +
2937                            (b1 - '0') *  1.00 +
2938                            (b2 - '0') *  0.10 +
2939                            (b3 - '0') *  0.01;
2940 
2941                 printf ("FlashPixVersion: %0.2f\n", x);
2942 
2943                 }
2944 
2945             #endif
2946 
2947             break;
2948 
2949             }
2950 
2951         case tcColorSpace:
2952             {
2953 
2954             CheckTagType (parentCode, tagCode, tagType, ttShort);
2955 
2956             CheckTagCount (parentCode, tagCode, tagCount, 1);
2957 
2958             fColorSpace = stream.TagValue_uint32 (tagType);
2959 
2960             #if qDNGValidate
2961 
2962             if (gVerbose)
2963                 {
2964 
2965                 printf ("ColorSpace: %s\n",
2966                         LookupColorSpace (fColorSpace));
2967 
2968                 }
2969 
2970             #endif
2971 
2972             break;
2973 
2974             }
2975 
2976         case tcPixelXDimension:
2977             {
2978 
2979             CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong);
2980 
2981             CheckTagCount (parentCode, tagCode, tagCount, 1);
2982 
2983             fPixelXDimension = stream.TagValue_uint32 (tagType);
2984 
2985             #if qDNGValidate
2986 
2987             if (gVerbose)
2988                 {
2989                 printf ("PixelXDimension: %u\n", (unsigned) fPixelXDimension);
2990                 }
2991 
2992             #endif
2993 
2994             break;
2995 
2996             }
2997 
2998         case tcPixelYDimension:
2999             {
3000 
3001             CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong);
3002 
3003             CheckTagCount (parentCode, tagCode, tagCount, 1);
3004 
3005             fPixelYDimension = stream.TagValue_uint32 (tagType);
3006 
3007             #if qDNGValidate
3008 
3009             if (gVerbose)
3010                 {
3011                 printf ("PixelYDimension: %u\n", (unsigned) fPixelYDimension);
3012                 }
3013 
3014             #endif
3015 
3016             break;
3017 
3018             }
3019 
3020         case tcFocalPlaneXResolutionExif:
3021             {
3022 
3023             CheckTagType (parentCode, tagCode, tagType, ttRational);
3024 
3025             CheckTagCount (parentCode, tagCode, tagCount, 1);
3026 
3027             fFocalPlaneXResolution = stream.TagValue_urational (tagType);
3028 
3029             #if qDNGValidate
3030 
3031             if (gVerbose)
3032                 {
3033 
3034                 printf ("FocalPlaneXResolutionExif: %0.4f\n",
3035                         fFocalPlaneXResolution.As_real64 ());
3036 
3037                 }
3038 
3039             #endif
3040 
3041             break;
3042 
3043             }
3044 
3045         case tcFocalPlaneYResolutionExif:
3046             {
3047 
3048             CheckTagType (parentCode, tagCode, tagType, ttRational);
3049 
3050             CheckTagCount (parentCode, tagCode, tagCount, 1);
3051 
3052             fFocalPlaneYResolution = stream.TagValue_urational (tagType);
3053 
3054             #if qDNGValidate
3055 
3056             if (gVerbose)
3057                 {
3058 
3059                 printf ("FocalPlaneYResolutionExif: %0.4f\n",
3060                         fFocalPlaneYResolution.As_real64 ());
3061 
3062                 }
3063 
3064             #endif
3065 
3066             break;
3067 
3068             }
3069 
3070         case tcFocalPlaneResolutionUnitExif:
3071             {
3072 
3073             CheckTagType (parentCode, tagCode, tagType, ttShort);
3074 
3075             CheckTagCount (parentCode, tagCode, tagCount, 1);
3076 
3077             fFocalPlaneResolutionUnit = stream.TagValue_uint32 (tagType);
3078 
3079             #if qDNGValidate
3080 
3081             if (gVerbose)
3082                 {
3083 
3084                 printf ("FocalPlaneResolutionUnitExif: %s\n",
3085                         LookupResolutionUnit (fFocalPlaneResolutionUnit));
3086 
3087                 }
3088 
3089             #endif
3090 
3091             break;
3092 
3093             }
3094 
3095         case tcSensingMethodExif:
3096             {
3097 
3098             CheckTagType (parentCode, tagCode, tagType, ttShort);
3099 
3100             CheckTagCount (parentCode, tagCode, tagCount, 1);
3101 
3102             fSensingMethod = stream.TagValue_uint32 (tagType);
3103 
3104             #if qDNGValidate
3105 
3106             if (gVerbose)
3107                 {
3108 
3109                 printf ("SensingMethodExif: %s\n",
3110                         LookupSensingMethod (fSensingMethod));
3111 
3112                 }
3113 
3114             #endif
3115 
3116             break;
3117 
3118             }
3119 
3120         case tcFileSource:
3121             {
3122 
3123             CheckTagType (parentCode, tagCode, tagType, ttUndefined);
3124 
3125             CheckTagCount (parentCode, tagCode, tagCount, 1);
3126 
3127             fFileSource = stream.Get_uint8 ();
3128 
3129             #if qDNGValidate
3130 
3131             if (gVerbose)
3132                 {
3133 
3134                 printf ("FileSource: %s\n",
3135                         LookupFileSource (fFileSource));
3136 
3137                 }
3138 
3139             #endif
3140 
3141             break;
3142 
3143             }
3144 
3145         case tcSceneType:
3146             {
3147 
3148             CheckTagType (parentCode, tagCode, tagType, ttUndefined);
3149 
3150             CheckTagCount (parentCode, tagCode, tagCount, 1);
3151 
3152             fSceneType = stream.Get_uint8 ();
3153 
3154             #if qDNGValidate
3155 
3156             if (gVerbose)
3157                 {
3158 
3159                 printf ("SceneType: %s\n",
3160                         LookupSceneType (fSceneType));
3161 
3162                 }
3163 
3164             #endif
3165 
3166             break;
3167 
3168             }
3169 
3170         case tcCFAPatternExif:
3171             {
3172 
3173             CheckTagType (parentCode, tagCode, tagType, ttUndefined);
3174 
3175             if (tagCount <= 4)
3176                 {
3177                 return false;
3178                 }
3179 
3180             uint32 cols = stream.Get_uint16 ();
3181             uint32 rows = stream.Get_uint16 ();
3182 
3183             if (tagCount != 4 + cols * rows)
3184                 {
3185                 return false;
3186                 }
3187 
3188             if (cols < 1 || cols > kMaxCFAPattern ||
3189                 rows < 1 || rows > kMaxCFAPattern)
3190                 {
3191                 return false;
3192                 }
3193 
3194             fCFARepeatPatternCols = cols;
3195             fCFARepeatPatternRows = rows;
3196 
3197             // Note that the Exif spec stores this array in a different
3198             // scan order than the TIFF-EP spec.
3199 
3200             for (uint32 j = 0; j < fCFARepeatPatternCols; j++)
3201                 for (uint32 k = 0; k < fCFARepeatPatternRows; k++)
3202                     {
3203 
3204                     fCFAPattern [k] [j] = stream.Get_uint8 ();
3205 
3206                     }
3207 
3208             #if qDNGValidate
3209 
3210             if (gVerbose)
3211                 {
3212 
3213                 printf ("CFAPatternExif:\n");
3214 
3215                 for (uint32 j = 0; j < fCFARepeatPatternRows; j++)
3216                     {
3217 
3218                     int32 spaces = 4;
3219 
3220                     for (uint32 k = 0; k < fCFARepeatPatternCols; k++)
3221                         {
3222 
3223                         while (spaces-- > 0)
3224                             {
3225                             printf (" ");
3226                             }
3227 
3228                         const char *name = LookupCFAColor (fCFAPattern [j] [k]);
3229 
3230                         spaces = 9 - (int32) strlen (name);
3231 
3232                         printf ("%s", name);
3233 
3234                         }
3235 
3236                     printf ("\n");
3237 
3238                     }
3239 
3240                 }
3241 
3242             #endif
3243 
3244             break;
3245 
3246             }
3247 
3248         case tcCustomRendered:
3249             {
3250 
3251             CheckTagType (parentCode, tagCode, tagType, ttShort);
3252 
3253             CheckTagCount (parentCode, tagCode, tagCount, 1);
3254 
3255             fCustomRendered = stream.TagValue_uint32 (tagType);
3256 
3257             #if qDNGValidate
3258 
3259             if (gVerbose)
3260                 {
3261 
3262                 printf ("CustomRendered: %s\n",
3263                         LookupCustomRendered (fCustomRendered));
3264 
3265                 }
3266 
3267             #endif
3268 
3269             break;
3270 
3271             }
3272 
3273         case tcExposureMode:
3274             {
3275 
3276             CheckTagType (parentCode, tagCode, tagType, ttShort);
3277 
3278             CheckTagCount (parentCode, tagCode, tagCount, 1);
3279 
3280             fExposureMode = stream.TagValue_uint32 (tagType);
3281 
3282             #if qDNGValidate
3283 
3284             if (gVerbose)
3285                 {
3286 
3287                 printf ("ExposureMode: %s\n",
3288                         LookupExposureMode (fExposureMode));
3289 
3290                 }
3291 
3292             #endif
3293 
3294             break;
3295 
3296             }
3297 
3298         case tcWhiteBalance:
3299             {
3300 
3301             CheckTagType (parentCode, tagCode, tagType, ttShort);
3302 
3303             CheckTagCount (parentCode, tagCode, tagCount, 1);
3304 
3305             fWhiteBalance = stream.TagValue_uint32 (tagType);
3306 
3307             #if qDNGValidate
3308 
3309             if (gVerbose)
3310                 {
3311 
3312                 printf ("WhiteBalance: %s\n",
3313                         LookupWhiteBalance (fWhiteBalance));
3314 
3315                 }
3316 
3317             #endif
3318 
3319             break;
3320 
3321             }
3322 
3323         case tcDigitalZoomRatio:
3324             {
3325 
3326             CheckTagType (parentCode, tagCode, tagType, ttRational);
3327 
3328             CheckTagCount (parentCode, tagCode, tagCount, 1);
3329 
3330             fDigitalZoomRatio = stream.TagValue_urational (tagType);
3331 
3332             #if qDNGValidate
3333 
3334             if (gVerbose)
3335                 {
3336 
3337                 printf ("DigitalZoomRatio: ");
3338 
3339                 if (fDigitalZoomRatio.n == 0 ||
3340                     fDigitalZoomRatio.d == 0)
3341                     {
3342 
3343                     printf ("Not used\n");
3344 
3345                     }
3346 
3347                 else
3348                     {
3349 
3350                     printf ("%0.2f\n", fDigitalZoomRatio.As_real64 ());
3351 
3352                     }
3353 
3354                 }
3355 
3356             #endif
3357 
3358             break;
3359 
3360             }
3361 
3362         case tcFocalLengthIn35mmFilm:
3363             {
3364 
3365             CheckTagType (parentCode, tagCode, tagType, ttShort);
3366 
3367             CheckTagCount (parentCode, tagCode, tagCount, 1);
3368 
3369             fFocalLengthIn35mmFilm = stream.TagValue_uint32 (tagType);
3370 
3371             #if qDNGValidate
3372 
3373             if (gVerbose)
3374                 {
3375 
3376                 printf ("FocalLengthIn35mmFilm: %u mm\n",
3377                         (unsigned) fFocalLengthIn35mmFilm);
3378 
3379                 }
3380 
3381             #endif
3382 
3383             break;
3384 
3385             }
3386 
3387         case tcSceneCaptureType:
3388             {
3389 
3390             CheckTagType (parentCode, tagCode, tagType, ttShort);
3391 
3392             CheckTagCount (parentCode, tagCode, tagCount, 1);
3393 
3394             fSceneCaptureType = stream.TagValue_uint32 (tagType);
3395 
3396             #if qDNGValidate
3397 
3398             if (gVerbose)
3399                 {
3400 
3401                 printf ("SceneCaptureType: %s\n",
3402                         LookupSceneCaptureType (fSceneCaptureType));
3403 
3404                 }
3405 
3406             #endif
3407 
3408             break;
3409 
3410             }
3411 
3412         case tcGainControl:
3413             {
3414 
3415             CheckTagType (parentCode, tagCode, tagType, ttShort);
3416 
3417             CheckTagCount (parentCode, tagCode, tagCount, 1);
3418 
3419             fGainControl = stream.TagValue_uint32 (tagType);
3420 
3421             #if qDNGValidate
3422 
3423             if (gVerbose)
3424                 {
3425 
3426                 printf ("GainControl: %s\n",
3427                         LookupGainControl (fGainControl));
3428 
3429                 }
3430 
3431             #endif
3432 
3433             break;
3434 
3435             }
3436 
3437         case tcContrast:
3438             {
3439 
3440             CheckTagType (parentCode, tagCode, tagType, ttShort);
3441 
3442             CheckTagCount (parentCode, tagCode, tagCount, 1);
3443 
3444             fContrast = stream.TagValue_uint32 (tagType);
3445 
3446             #if qDNGValidate
3447 
3448             if (gVerbose)
3449                 {
3450 
3451                 printf ("Contrast: %s\n",
3452                         LookupContrast (fContrast));
3453 
3454                 }
3455 
3456             #endif
3457 
3458             break;
3459 
3460             }
3461 
3462         case tcSaturation:
3463             {
3464 
3465             CheckTagType (parentCode, tagCode, tagType, ttShort);
3466 
3467             CheckTagCount (parentCode, tagCode, tagCount, 1);
3468 
3469             fSaturation = stream.TagValue_uint32 (tagType);
3470 
3471             #if qDNGValidate
3472 
3473             if (gVerbose)
3474                 {
3475 
3476                 printf ("Saturation: %s\n",
3477                         LookupSaturation (fSaturation));
3478 
3479                 }
3480 
3481             #endif
3482 
3483             break;
3484 
3485             }
3486 
3487         case tcSharpness:
3488             {
3489 
3490             CheckTagType (parentCode, tagCode, tagType, ttShort);
3491 
3492             CheckTagCount (parentCode, tagCode, tagCount, 1);
3493 
3494             fSharpness = stream.TagValue_uint32 (tagType);
3495 
3496             #if qDNGValidate
3497 
3498             if (gVerbose)
3499                 {
3500 
3501                 printf ("Sharpness: %s\n",
3502                         LookupSharpness (fSharpness));
3503 
3504                 }
3505 
3506             #endif
3507 
3508             break;
3509 
3510             }
3511 
3512         case tcSubjectDistanceRange:
3513             {
3514 
3515             CheckTagType (parentCode, tagCode, tagType, ttShort);
3516 
3517             CheckTagCount (parentCode, tagCode, tagCount, 1);
3518 
3519             fSubjectDistanceRange = stream.TagValue_uint32 (tagType);
3520 
3521             #if qDNGValidate
3522 
3523             if (gVerbose)
3524                 {
3525 
3526                 printf ("SubjectDistanceRange: %s\n",
3527                         LookupSubjectDistanceRange (fSubjectDistanceRange));
3528 
3529                 }
3530 
3531             #endif
3532 
3533             break;
3534 
3535             }
3536 
3537         case tcSubjectArea:
3538         case tcSubjectLocation:
3539             {
3540 
3541             CheckTagType (parentCode, tagCode, tagType, ttShort);
3542 
3543             if (!CheckTagCount (parentCode, tagCode, tagCount, 2, 4))
3544                 {
3545                 return false;
3546                 }
3547 
3548             if (tagCode == tcSubjectLocation)
3549                 {
3550                 CheckTagCount (parentCode, tagCode, tagCount, 2);
3551                 }
3552 
3553             fSubjectAreaCount = tagCount;
3554 
3555             for (uint32 j = 0; j < tagCount; j++)
3556                 {
3557 
3558                 fSubjectArea [j] = stream.TagValue_uint32 (tagType);
3559 
3560                 }
3561 
3562             #if qDNGValidate
3563 
3564             if (gVerbose)
3565                 {
3566 
3567                 printf ("%s:", LookupTagCode (parentCode, tagCode));
3568 
3569                 for (uint32 j = 0; j < fSubjectAreaCount; j++)
3570                     {
3571 
3572                     printf (" %u", (unsigned) fSubjectArea [j]);
3573 
3574                     }
3575 
3576                 printf ("\n");
3577 
3578                 }
3579 
3580             #endif
3581 
3582             break;
3583 
3584             }
3585 
3586         case tcGamma:
3587             {
3588 
3589             CheckTagType (parentCode, tagCode, tagType, ttRational);
3590 
3591             CheckTagCount (parentCode, tagCode, tagCount, 1);
3592 
3593             fGamma = stream.TagValue_urational (tagType);
3594 
3595             #if qDNGValidate
3596 
3597             if (gVerbose)
3598                 {
3599 
3600                 printf ("Gamma: %0.2f\n",
3601                         fGamma.As_real64 ());
3602 
3603                 }
3604 
3605             #endif
3606 
3607             break;
3608 
3609             }
3610 
3611         case tcImageUniqueID:
3612             {
3613 
3614             if (!CheckTagType (parentCode, tagCode, tagType, ttAscii))
3615                 return false;
3616 
3617             if (!CheckTagCount (parentCode, tagCode, tagCount, 33))
3618                 return false;
3619 
3620             dng_string s;
3621 
3622             ParseStringTag (stream,
3623                             parentCode,
3624                             tagCode,
3625                             tagCount,
3626                             s);
3627 
3628             if (s.Length () != 32)
3629                 return false;
3630 
3631             dng_fingerprint f;
3632 
3633             for (uint32 j = 0; j < 32; j++)
3634                 {
3635 
3636                 char c = ForceUppercase (s.Get () [j]);
3637 
3638                 uint32 digit;
3639 
3640                 if (c >= '0' && c <= '9')
3641                     {
3642                     digit = c - '0';
3643                     }
3644 
3645                 else if (c >= 'A' && c <= 'F')
3646                     {
3647                     digit = c - 'A' + 10;
3648                     }
3649 
3650                 else
3651                     return false;
3652 
3653                 f.data [j >> 1] *= 16;
3654                 f.data [j >> 1] += (uint8) digit;
3655 
3656                 }
3657 
3658             fImageUniqueID = f;
3659 
3660             #if qDNGValidate
3661 
3662             if (gVerbose)
3663                 {
3664 
3665                 printf ("ImageUniqueID: ");
3666 
3667                 DumpFingerprint (fImageUniqueID);
3668 
3669                 printf ("\n");
3670 
3671                 }
3672 
3673             #endif
3674 
3675             break;
3676 
3677             }
3678 
3679         case tcCameraOwnerNameExif:
3680             {
3681 
3682             CheckTagType (parentCode, tagCode, tagType, ttAscii);
3683 
3684             ParseStringTag (stream,
3685                             parentCode,
3686                             tagCode,
3687                             tagCount,
3688                             fOwnerName);
3689 
3690             #if qDNGValidate
3691 
3692             if (gVerbose)
3693                 {
3694 
3695                 printf ("CameraOwnerName: ");
3696 
3697                 DumpString (fOwnerName);
3698 
3699                 printf ("\n");
3700 
3701                 }
3702 
3703             #endif
3704 
3705             break;
3706 
3707             }
3708 
3709         case tcCameraSerialNumberExif:
3710             {
3711 
3712             CheckTagType (parentCode, tagCode, tagType, ttAscii);
3713 
3714             ParseStringTag (stream,
3715                             parentCode,
3716                             tagCode,
3717                             tagCount,
3718                             fCameraSerialNumber);
3719 
3720             #if qDNGValidate
3721 
3722             if (gVerbose)
3723                 {
3724 
3725                 printf ("%s: ", LookupTagCode (parentCode, tagCode));
3726 
3727                 DumpString (fCameraSerialNumber);
3728 
3729                 printf ("\n");
3730 
3731                 }
3732 
3733             #endif
3734 
3735             break;
3736 
3737             }
3738 
3739         case tcLensSpecificationExif:
3740             {
3741 
3742             CheckTagType (parentCode, tagCode, tagType, ttRational);
3743 
3744             if (!CheckTagCount (parentCode, tagCode, tagCount, 4))
3745                 return false;
3746 
3747             fLensInfo [0] = stream.TagValue_urational (tagType);
3748             fLensInfo [1] = stream.TagValue_urational (tagType);
3749             fLensInfo [2] = stream.TagValue_urational (tagType);
3750             fLensInfo [3] = stream.TagValue_urational (tagType);
3751 
3752             // Some third party software wrote zero rather than undefined values for
3753             // unknown entries. Work around this bug.
3754 
3755             for (uint32 j = 0; j < 4; j++)
3756                 {
3757 
3758                 if (fLensInfo [j].IsValid () && fLensInfo [j].As_real64 () <= 0.0)
3759                     {
3760 
3761                     fLensInfo [j] = dng_urational (0, 0);
3762 
3763                     #if qDNGValidate
3764 
3765                     ReportWarning ("Zero entry in LensSpecification tag--should be undefined");
3766 
3767                     #endif
3768 
3769                     }
3770 
3771                 }
3772 
3773             #if qDNGValidate
3774 
3775             if (gVerbose)
3776                 {
3777 
3778                 printf ("LensSpecificationExif: ");
3779 
3780                 real64 minFL = fLensInfo [0].As_real64 ();
3781                 real64 maxFL = fLensInfo [1].As_real64 ();
3782 
3783                 if (minFL == maxFL)
3784                     printf ("%0.1f mm", minFL);
3785                 else
3786                     printf ("%0.1f-%0.1f mm", minFL, maxFL);
3787 
3788                 if (fLensInfo [2].d)
3789                     {
3790 
3791                     real64 minFS = fLensInfo [2].As_real64 ();
3792                     real64 maxFS = fLensInfo [3].As_real64 ();
3793 
3794                     if (minFS == maxFS)
3795                         printf (" f/%0.1f", minFS);
3796                     else
3797                         printf (" f/%0.1f-%0.1f", minFS, maxFS);
3798 
3799                     }
3800 
3801                 printf ("\n");
3802 
3803                 }
3804 
3805             #endif
3806 
3807             break;
3808 
3809             }
3810 
3811         case tcLensMakeExif:
3812             {
3813 
3814             CheckTagType (parentCode, tagCode, tagType, ttAscii);
3815 
3816             ParseStringTag (stream,
3817                             parentCode,
3818                             tagCode,
3819                             tagCount,
3820                             fLensMake);
3821 
3822             #if qDNGValidate
3823 
3824             if (gVerbose)
3825                 {
3826 
3827                 printf ("%s: ", LookupTagCode (parentCode, tagCode));
3828 
3829                 DumpString (fLensMake);
3830 
3831                 printf ("\n");
3832 
3833                 }
3834 
3835             #endif
3836 
3837             break;
3838 
3839             }
3840 
3841         case tcLensModelExif:
3842             {
3843 
3844             CheckTagType (parentCode, tagCode, tagType, ttAscii);
3845 
3846             ParseStringTag (stream,
3847                             parentCode,
3848                             tagCode,
3849                             tagCount,
3850                             fLensName);
3851 
3852             fLensNameWasReadFromExif = fLensName.NotEmpty ();
3853 
3854             #if qDNGValidate
3855 
3856             if (gVerbose)
3857                 {
3858 
3859                 printf ("%s: ", LookupTagCode (parentCode, tagCode));
3860 
3861                 DumpString (fLensName);
3862 
3863                 printf ("\n");
3864 
3865                 }
3866 
3867             #endif
3868 
3869             break;
3870 
3871             }
3872 
3873         case tcLensSerialNumberExif:
3874             {
3875 
3876             CheckTagType (parentCode, tagCode, tagType, ttAscii);
3877 
3878             ParseStringTag (stream,
3879                             parentCode,
3880                             tagCode,
3881                             tagCount,
3882                             fLensSerialNumber);
3883 
3884             #if qDNGValidate
3885 
3886             if (gVerbose)
3887                 {
3888 
3889                 printf ("%s: ", LookupTagCode (parentCode, tagCode));
3890 
3891                 DumpString (fLensSerialNumber);
3892 
3893                 printf ("\n");
3894 
3895                 }
3896 
3897             #endif
3898 
3899             break;
3900 
3901             }
3902 
3903         default:
3904             {
3905 
3906             return false;
3907 
3908             }
3909 
3910         }
3911 
3912     return true;
3913 
3914     }
3915 
3916 /*****************************************************************************/
3917 
3918 // Parses tags that should only appear in GPS IFD
3919 
3920 bool dng_exif::Parse_gps (dng_stream &stream,
3921                           dng_shared & /* shared */,
3922                           uint32 parentCode,
3923                           uint32 tagCode,
3924                           uint32 tagType,
3925                           uint32 tagCount,
3926                           uint64 /* tagOffset */)
3927     {
3928 
3929     switch (tagCode)
3930         {
3931 
3932         case tcGPSVersionID:
3933             {
3934 
3935             CheckTagType (parentCode, tagCode, tagType, ttByte);
3936 
3937             CheckTagCount (parentCode, tagCode, tagCount, 4);
3938 
3939             uint32 b0 = stream.Get_uint8 ();
3940             uint32 b1 = stream.Get_uint8 ();
3941             uint32 b2 = stream.Get_uint8 ();
3942             uint32 b3 = stream.Get_uint8 ();
3943 
3944             fGPSVersionID = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
3945 
3946             #if qDNGValidate
3947 
3948             if (gVerbose)
3949                 {
3950                 printf ("GPSVersionID: %u.%u.%u.%u\n",
3951                         (unsigned) b0,
3952                         (unsigned) b1,
3953                         (unsigned) b2,
3954                         (unsigned) b3);
3955                 }
3956 
3957             #endif
3958 
3959             break;
3960 
3961             }
3962 
3963         case tcGPSLatitudeRef:
3964         case tcGPSLongitudeRef:
3965         case tcGPSSatellites:
3966         case tcGPSStatus:
3967         case tcGPSMeasureMode:
3968         case tcGPSSpeedRef:
3969         case tcGPSTrackRef:
3970         case tcGPSImgDirectionRef:
3971         case tcGPSMapDatum:
3972         case tcGPSDestLatitudeRef:
3973         case tcGPSDestLongitudeRef:
3974         case tcGPSDestBearingRef:
3975         case tcGPSDestDistanceRef:
3976         case tcGPSDateStamp:
3977             {
3978 
3979             if (!CheckTagType (parentCode, tagCode, tagType, ttAscii))
3980                 return false;
3981 
3982             dng_string *s;
3983 
3984             switch (tagCode)
3985                 {
3986 
3987                 case tcGPSLatitudeRef:
3988                     s = &fGPSLatitudeRef;
3989                     break;
3990 
3991                 case tcGPSLongitudeRef:
3992                     s = &fGPSLongitudeRef;
3993                     break;
3994 
3995                 case tcGPSSatellites:
3996                     s = &fGPSSatellites;
3997                     break;
3998 
3999                 case tcGPSStatus:
4000                     s = &fGPSStatus;
4001                     break;
4002 
4003                 case tcGPSMeasureMode:
4004                     s = &fGPSMeasureMode;
4005                     break;
4006 
4007                 case tcGPSSpeedRef:
4008                     s = &fGPSSpeedRef;
4009                     break;
4010 
4011                 case tcGPSTrackRef:
4012                     s = &fGPSTrackRef;
4013                     break;
4014 
4015                 case tcGPSImgDirectionRef:
4016                     s = &fGPSImgDirectionRef;
4017                     break;
4018 
4019                 case tcGPSMapDatum:
4020                     s = &fGPSMapDatum;
4021                     break;
4022 
4023                 case tcGPSDestLatitudeRef:
4024                     s = &fGPSDestLatitudeRef;
4025                     break;
4026 
4027                 case tcGPSDestLongitudeRef:
4028                     s = &fGPSDestLongitudeRef;
4029                     break;
4030 
4031                 case tcGPSDestBearingRef:
4032                     s = &fGPSDestBearingRef;
4033                     break;
4034 
4035                 case tcGPSDestDistanceRef:
4036                     s = &fGPSDestDistanceRef;
4037                     break;
4038 
4039                 case tcGPSDateStamp:
4040                     s = &fGPSDateStamp;
4041                     break;
4042 
4043                 default:
4044                     return false;
4045 
4046                 }
4047 
4048             ParseStringTag (stream,
4049                             parentCode,
4050                             tagCode,
4051                             tagCount,
4052                             *s);
4053 
4054             #if qDNGValidate
4055 
4056             if (gVerbose)
4057                 {
4058 
4059                 printf ("%s: ", LookupTagCode (parentCode, tagCode));
4060 
4061                 DumpString (*s);
4062 
4063                 printf ("\n");
4064 
4065                 }
4066 
4067             #endif
4068 
4069             break;
4070 
4071             }
4072 
4073         case tcGPSLatitude:
4074         case tcGPSLongitude:
4075         case tcGPSTimeStamp:
4076         case tcGPSDestLatitude:
4077         case tcGPSDestLongitude:
4078             {
4079 
4080             // Should really be ttRational per EXIF spec, but allow
4081             // ttSRational too because some JPEGs from Nexus 5
4082             // apparently use ttSRational type.
4083 
4084             if (!CheckTagType (parentCode, tagCode, tagType, ttRational) &&
4085                 !CheckTagType (parentCode, tagCode, tagType, ttSRational))
4086                 return false;
4087 
4088             if (!CheckTagCount (parentCode, tagCode, tagCount, 3))
4089                 return false;
4090 
4091             dng_urational *u;
4092 
4093             switch (tagCode)
4094                 {
4095 
4096                 case tcGPSLatitude:
4097                     u = fGPSLatitude;
4098                     break;
4099 
4100                 case tcGPSLongitude:
4101                     u = fGPSLongitude;
4102                     break;
4103 
4104                 case tcGPSTimeStamp:
4105                     u = fGPSTimeStamp;
4106                     break;
4107 
4108                 case tcGPSDestLatitude:
4109                     u = fGPSDestLatitude;
4110                     break;
4111 
4112                 case tcGPSDestLongitude:
4113                     u = fGPSDestLongitude;
4114                     break;
4115 
4116                 default:
4117                     return false;
4118 
4119                 }
4120 
4121             u [0] = stream.TagValue_urational (tagType);
4122             u [1] = stream.TagValue_urational (tagType);
4123             u [2] = stream.TagValue_urational (tagType);
4124 
4125             #if qDNGValidate
4126 
4127             if (gVerbose)
4128                 {
4129 
4130                 printf ("%s:", LookupTagCode (parentCode, tagCode));
4131 
4132                 for (uint32 j = 0; j < 3; j++)
4133                     {
4134 
4135                     if (u [j].d == 0)
4136                         printf (" -");
4137 
4138                     else
4139                         printf (" %0.4f", u [j].As_real64 ());
4140 
4141                     }
4142 
4143                 printf ("\n");
4144 
4145                 }
4146 
4147             #endif
4148 
4149             break;
4150 
4151             }
4152 
4153         case tcGPSAltitudeRef:
4154             {
4155 
4156             CheckTagType (parentCode, tagCode, tagType, ttByte);
4157 
4158             CheckTagCount (parentCode, tagCode, tagCount, 1);
4159 
4160             fGPSAltitudeRef = stream.TagValue_uint32 (tagType);
4161 
4162             #if qDNGValidate
4163 
4164             if (gVerbose)
4165                 {
4166 
4167                 printf ("GPSAltitudeRef: ");
4168 
4169                 switch (fGPSAltitudeRef)
4170                     {
4171 
4172                     case 0:
4173                         printf ("Sea level");
4174                         break;
4175 
4176                     case 1:
4177                         printf ("Sea level reference (negative value)");
4178                         break;
4179 
4180                     default:
4181                         printf ("%u", (unsigned) fGPSAltitudeRef);
4182                         break;
4183 
4184                     }
4185 
4186                 printf ("\n");
4187 
4188                 }
4189 
4190             #endif
4191 
4192             break;
4193 
4194             }
4195 
4196         case tcGPSAltitude:
4197         case tcGPSDOP:
4198         case tcGPSSpeed:
4199         case tcGPSTrack:
4200         case tcGPSImgDirection:
4201         case tcGPSDestBearing:
4202         case tcGPSDestDistance:
4203         case tcGPSHPositioningError:
4204             {
4205 
4206             if (!CheckTagType (parentCode, tagCode, tagType, ttRational))
4207                 return false;
4208 
4209             CheckTagCount (parentCode, tagCode, tagCount, 1);
4210 
4211             dng_urational *u;
4212 
4213             switch (tagCode)
4214                 {
4215 
4216                 case tcGPSAltitude:
4217                     u = &fGPSAltitude;
4218                     break;
4219 
4220                 case tcGPSDOP:
4221                     u = &fGPSDOP;
4222                     break;
4223 
4224                 case tcGPSSpeed:
4225                     u = &fGPSSpeed;
4226                     break;
4227 
4228                 case tcGPSTrack:
4229                     u = &fGPSTrack;
4230                     break;
4231 
4232                 case tcGPSImgDirection:
4233                     u = &fGPSImgDirection;
4234                     break;
4235 
4236                 case tcGPSDestBearing:
4237                     u = &fGPSDestBearing;
4238                     break;
4239 
4240                 case tcGPSDestDistance:
4241                     u = &fGPSDestDistance;
4242                     break;
4243 
4244                 case tcGPSHPositioningError:
4245                     u = &fGPSHPositioningError;
4246                     break;
4247 
4248                 default:
4249                     return false;
4250 
4251                 }
4252 
4253             *u = stream.TagValue_urational (tagType);
4254 
4255             #if qDNGValidate
4256 
4257             if (gVerbose)
4258                 {
4259 
4260                 printf ("%s:", LookupTagCode (parentCode, tagCode));
4261 
4262                 if (u->d == 0)
4263                     printf (" -");
4264 
4265                 else
4266                     printf (" %0.4f", u->As_real64 ());
4267 
4268                 printf ("\n");
4269 
4270                 }
4271 
4272             #endif
4273 
4274             break;
4275 
4276             }
4277 
4278         case tcGPSProcessingMethod:
4279         case tcGPSAreaInformation:
4280             {
4281 
4282             if (!CheckTagType (parentCode, tagCode, tagType, ttUndefined))
4283                 return false;
4284 
4285             dng_string *s;
4286 
4287             switch (tagCode)
4288                 {
4289 
4290                 case tcGPSProcessingMethod:
4291                     s = &fGPSProcessingMethod;
4292                     break;
4293 
4294                 case tcGPSAreaInformation:
4295                     s = &fGPSAreaInformation;
4296                     break;
4297 
4298                 default:
4299                     return false;
4300 
4301                 }
4302 
4303             ParseEncodedStringTag (stream,
4304                                    parentCode,
4305                                    tagCode,
4306                                    tagCount,
4307                                    *s);
4308 
4309             #if qDNGValidate
4310 
4311             if (gVerbose)
4312                 {
4313 
4314                 printf ("%s: ", LookupTagCode (parentCode, tagCode));
4315 
4316                 DumpString (*s);
4317 
4318                 printf ("\n");
4319 
4320                 }
4321 
4322             #endif
4323 
4324             break;
4325 
4326             }
4327 
4328         case tcGPSDifferential:
4329             {
4330 
4331             CheckTagType (parentCode, tagCode, tagType, ttShort);
4332 
4333             CheckTagCount (parentCode, tagCode, tagCount, 1);
4334 
4335             fGPSDifferential = stream.TagValue_uint32 (tagType);
4336 
4337             #if qDNGValidate
4338 
4339             if (gVerbose)
4340                 {
4341 
4342                 printf ("GPSDifferential: ");
4343 
4344                 switch (fGPSDifferential)
4345                     {
4346 
4347                     case 0:
4348                         printf ("Measurement without differential correction");
4349                         break;
4350 
4351                     case 1:
4352                         printf ("Differential correction applied");
4353                         break;
4354 
4355                     default:
4356                         printf ("%u", (unsigned) fGPSDifferential);
4357 
4358                     }
4359 
4360                 printf ("\n");
4361 
4362                 }
4363 
4364             #endif
4365 
4366             break;
4367 
4368             }
4369 
4370         default:
4371             {
4372 
4373             return false;
4374 
4375             }
4376 
4377         }
4378 
4379     return true;
4380 
4381     }
4382 
4383 /*****************************************************************************/
4384 
4385 // Parses tags that should only appear in Interoperability IFD
4386 
4387 bool dng_exif::Parse_interoperability (dng_stream &stream,
4388                                        dng_shared & /* shared */,
4389                                        uint32 parentCode,
4390                                        uint32 tagCode,
4391                                        uint32 tagType,
4392                                        uint32 tagCount,
4393                                        uint64 /* tagOffset */)
4394     {
4395 
4396     switch (tagCode)
4397         {
4398 
4399         case tcInteroperabilityIndex:
4400             {
4401 
4402             CheckTagType (parentCode, tagCode, tagType, ttAscii);
4403 
4404             CheckTagCount (parentCode, tagCode, tagCount, 4);
4405 
4406             ParseStringTag (stream,
4407                             parentCode,
4408                             tagCode,
4409                             tagCount,
4410                             fInteroperabilityIndex);
4411 
4412             #if qDNGValidate
4413 
4414             if (gVerbose)
4415                 {
4416 
4417                 printf ("InteroperabilityIndex: ");
4418 
4419                 DumpString (fInteroperabilityIndex);
4420 
4421                 printf ("\n");
4422 
4423                 }
4424 
4425             #endif
4426 
4427             break;
4428 
4429             }
4430 
4431         case tcInteroperabilityVersion:
4432             {
4433 
4434             CheckTagType (parentCode, tagCode, tagType, ttUndefined);
4435 
4436             CheckTagCount (parentCode, tagCode, tagCount, 4);
4437 
4438             uint32 b0 = stream.Get_uint8 ();
4439             uint32 b1 = stream.Get_uint8 ();
4440             uint32 b2 = stream.Get_uint8 ();
4441             uint32 b3 = stream.Get_uint8 ();
4442 
4443             fInteroperabilityVersion = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
4444 
4445             #if qDNGValidate
4446 
4447             if (gVerbose)
4448                 {
4449 
4450                 real64 x = (b0 - '0') * 10.00 +
4451                            (b1 - '0') *  1.00 +
4452                            (b2 - '0') *  0.10 +
4453                            (b3 - '0') *  0.01;
4454 
4455                 printf ("InteroperabilityVersion: %0.2f\n", x);
4456 
4457                 }
4458 
4459             #endif
4460 
4461             break;
4462 
4463             }
4464 
4465         case tcRelatedImageFileFormat:
4466             {
4467 
4468             CheckTagType (parentCode, tagCode, tagType, ttAscii);
4469 
4470             ParseStringTag (stream,
4471                             parentCode,
4472                             tagCode,
4473                             tagCount,
4474                             fRelatedImageFileFormat);
4475 
4476             #if qDNGValidate
4477 
4478             if (gVerbose)
4479                 {
4480 
4481                 printf ("RelatedImageFileFormat: ");
4482 
4483                 DumpString (fRelatedImageFileFormat);
4484 
4485                 printf ("\n");
4486 
4487                 }
4488 
4489             #endif
4490 
4491             break;
4492 
4493             }
4494 
4495         case tcRelatedImageWidth:
4496             {
4497 
4498             CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong);
4499 
4500             CheckTagCount (parentCode, tagCode, tagCount, 1);
4501 
4502             fRelatedImageWidth = stream.TagValue_uint32 (tagType);
4503 
4504             #if qDNGValidate
4505 
4506             if (gVerbose)
4507                 {
4508                 printf ("RelatedImageWidth: %u\n", (unsigned) fRelatedImageWidth);
4509                 }
4510 
4511             #endif
4512 
4513             break;
4514 
4515             }
4516 
4517         case tcRelatedImageLength:
4518             {
4519 
4520             CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong);
4521 
4522             CheckTagCount (parentCode, tagCode, tagCount, 1);
4523 
4524             fRelatedImageLength = stream.TagValue_uint32 (tagType);
4525 
4526             #if qDNGValidate
4527 
4528             if (gVerbose)
4529                 {
4530                 printf ("RelatedImageLength: %u\n", (unsigned) fRelatedImageLength);
4531                 }
4532 
4533             #endif
4534 
4535             break;
4536 
4537             }
4538 
4539         default:
4540             {
4541 
4542             return false;
4543 
4544             }
4545 
4546         }
4547 
4548     return true;
4549 
4550     }
4551 
4552 /*****************************************************************************/
4553 
4554 void dng_exif::PostParse (dng_host & /* host */,
4555                           dng_shared & /* shared */)
4556     {
4557 
4558     #if qDNGValidate
4559 
4560     const real64 kAPEX_Slop = 0.25;
4561 
4562     // Sanity check on MaxApertureValue.
4563 
4564     if (fMaxApertureValue.d)
4565         {
4566 
4567         real64 mav = fMaxApertureValue.As_real64 ();
4568 
4569         // Compare against ApertureValue or FNumber.
4570 
4571         real64 av = mav;
4572 
4573         if (fApertureValue.d)
4574             {
4575 
4576             av = fApertureValue.As_real64 ();
4577 
4578             }
4579 
4580         else if (fFNumber.d)
4581             {
4582 
4583             real64 fs = fFNumber.As_real64 ();
4584 
4585             if (fs >= 1.0)
4586                 {
4587 
4588                 av = FNumberToApertureValue (fs);
4589 
4590                 }
4591 
4592             }
4593 
4594         if (mav > av + kAPEX_Slop)
4595             {
4596 
4597             ReportWarning ("MaxApertureValue conflicts with ApertureValue and/or FNumber");
4598 
4599             }
4600 
4601         // Compare against LensInfo
4602 
4603         if (fLensInfo [2].d && fLensInfo [3].d)
4604             {
4605 
4606             real64 fs1 = fLensInfo [2].As_real64 ();
4607             real64 fs2 = fLensInfo [3].As_real64 ();
4608 
4609             if (fs1 >= 1.0 && fs2 >= 1.0 && fs2 >= fs1)
4610                 {
4611 
4612                 real64 av1 = FNumberToApertureValue (fs1);
4613                 real64 av2 = FNumberToApertureValue (fs2);
4614 
4615                 // Wide angle adapters might create an effective
4616                 // wide FS, and tele-extenders always result
4617                 // in a higher FS.
4618 
4619                 if (mav < av1 - kAPEX_Slop - 1.0 ||
4620                     mav > av2 + kAPEX_Slop + 2.0)
4621                     {
4622 
4623                     ReportWarning ("Possible MaxApertureValue conflict with LensInfo");
4624 
4625                     }
4626 
4627                 }
4628 
4629             }
4630 
4631         }
4632 
4633     // Sanity check on FocalLength.
4634 
4635     if (fFocalLength.d)
4636         {
4637 
4638         real64 fl = fFocalLength.As_real64 ();
4639 
4640         if (fl < 1.0)
4641             {
4642 
4643             ReportWarning ("FocalLength is less than 1.0 mm (legal but unlikely)");
4644 
4645             }
4646 
4647         else if (fLensInfo [0].d && fLensInfo [1].d)
4648             {
4649 
4650             real64 minFL = fLensInfo [0].As_real64 ();
4651             real64 maxFL = fLensInfo [1].As_real64 ();
4652 
4653             // Allow for wide-angle converters and tele-extenders.
4654 
4655             if (fl < minFL * 0.6 ||
4656                 fl > maxFL * 2.1)
4657                 {
4658 
4659                 ReportWarning ("Possible FocalLength conflict with LensInfo");
4660 
4661                 }
4662 
4663             }
4664 
4665         }
4666 
4667     #endif
4668 
4669     // Mirror DateTimeOriginal to DateTime.
4670 
4671     if (fDateTime.NotValid () && fDateTimeOriginal.IsValid ())
4672         {
4673 
4674         fDateTime = fDateTimeOriginal;
4675 
4676         }
4677 
4678     // Mirror EXIF 2.3 sensitivity tags to ISOSpeedRatings.
4679 
4680     if (fISOSpeedRatings [0] == 0 || fISOSpeedRatings [0] == 65535)
4681         {
4682 
4683         // Prefer Recommended Exposure Index, then Standard Output Sensitivity, then
4684         // ISO Speed, then Exposure Index.
4685 
4686         if (fRecommendedExposureIndex != 0 &&
4687             (fSensitivityType == stRecommendedExposureIndex ||
4688              fSensitivityType == stSOSandREI                ||
4689              fSensitivityType == stREIandISOSpeed           ||
4690              fSensitivityType == stSOSandREIandISOSpeed))
4691             {
4692 
4693             fISOSpeedRatings [0] = fRecommendedExposureIndex;
4694 
4695             }
4696 
4697         else if (fStandardOutputSensitivity != 0 &&
4698                  (fSensitivityType == stStandardOutputSensitivity ||
4699                   fSensitivityType == stSOSandREI                 ||
4700                   fSensitivityType == stSOSandISOSpeed            ||
4701                   fSensitivityType == stSOSandREIandISOSpeed))
4702             {
4703 
4704             fISOSpeedRatings [0] = fStandardOutputSensitivity;
4705 
4706             }
4707 
4708         else if (fISOSpeed != 0 &&
4709                  (fSensitivityType == stISOSpeed       ||
4710                   fSensitivityType == stSOSandISOSpeed ||
4711                   fSensitivityType == stREIandISOSpeed ||
4712                   fSensitivityType == stSOSandREIandISOSpeed))
4713             {
4714 
4715             fISOSpeedRatings [0] = fISOSpeed;
4716 
4717             }
4718 
4719         }
4720 
4721     // Mirror ExposureIndex to ISOSpeedRatings.
4722 
4723     if (fExposureIndex.IsValid () && fISOSpeedRatings [0] == 0)
4724         {
4725 
4726         fISOSpeedRatings [0] = Round_uint32 (fExposureIndex.As_real64 ());
4727 
4728         }
4729 
4730     // Kodak sets the GPSAltitudeRef without setting the GPSAltitude.
4731 
4732     if (fGPSAltitude.NotValid ())
4733         {
4734 
4735         fGPSAltitudeRef = 0xFFFFFFFF;
4736 
4737         }
4738 
4739     // If there is no valid GPS data, clear the GPS version number.
4740 
4741     if (fGPSLatitude  [0].NotValid () &&
4742         fGPSLongitude [0].NotValid () &&
4743         fGPSAltitude     .NotValid () &&
4744         fGPSTimeStamp [0].NotValid () &&
4745         fGPSDateStamp    .IsEmpty  ())
4746         {
4747 
4748         fGPSVersionID = 0;
4749 
4750         }
4751 
4752     }
4753 
4754 /*****************************************************************************/