Blame converter/other/exif.c

Packit 78deda
/*--------------------------------------------------------------------------
Packit 78deda
  This file contains subroutines for use by Jpegtopnm to handle the
Packit 78deda
  EXIF header.
Packit 78deda
Packit 78deda
  The code is adapted from the program Jhead by Matthaias Wandel
Packit 78deda
  December 1999 - August 2000, and contributed to the public domain.
Packit 78deda
  Bryan Henderson adapted it to Netpbm in September 2001.  Bryan
Packit 78deda
  added more of Wandel's code, from Jhead 1.9 dated December 2002 in
Packit 78deda
  January 2003.
Packit 78deda
Packit 78deda
  An EXIF header is a JFIF APP1 marker.  It is generated by some
Packit 78deda
  digital cameras and contains information about the circumstances of
Packit 78deda
  the creation of the image (camera settings, etc.).
Packit 78deda
Packit 78deda
  The EXIF header uses the TIFF format, only it contains only tag
Packit 78deda
  values and no actual image.
Packit 78deda
Packit 78deda
  Note that the image format called EXIF is simply JFIF with an EXIF
Packit 78deda
  header, i.e. a subformat of JFIF.
Packit 78deda
Packit 78deda
  See the EXIF specs at http://exif.org (2001.09.01).
Packit 78deda
Packit 78deda
--------------------------------------------------------------------------*/
Packit 78deda
#include "pm_config.h"
Packit 78deda
#include <stdio.h>
Packit 78deda
#include <stdlib.h>
Packit 78deda
#include <math.h>
Packit 78deda
#include <string.h>
Packit 78deda
#include <time.h>
Packit 78deda
#include <errno.h>
Packit 78deda
#include <limits.h>
Packit 78deda
#include <ctype.h>
Packit 78deda
Packit 78deda
#if MSVCRT
Packit 78deda
    #include <sys/utime.h>
Packit 78deda
#else
Packit 78deda
    #include <utime.h>
Packit 78deda
    #include <sys/types.h>
Packit 78deda
    #include <unistd.h>
Packit 78deda
    #include <errno.h>
Packit 78deda
#endif
Packit 78deda
Packit 78deda
#include "pm_c_util.h"
Packit 78deda
#include "pm.h"
Packit 78deda
#include "nstring.h"
Packit 78deda
Packit 78deda
#include "exif.h"
Packit 78deda
Packit 78deda
static const unsigned char * DirWithThumbnailPtrs;
Packit 78deda
static double FocalplaneXRes;
Packit 78deda
bool HaveXRes;
Packit 78deda
static double FocalplaneUnits;
Packit 78deda
static int ExifImageWidth;
Packit 78deda
Packit 78deda
typedef struct {
Packit 78deda
    unsigned short Tag;
Packit 78deda
    const char * Desc;
Packit 78deda
} TagTable;
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
/* Describes format descriptor */
Packit 78deda
static int const bytesPerFormat[] = {0,1,1,2,4,8,1,1,2,4,8,4,8};
Packit 78deda
#define NUM_FORMATS 12
Packit 78deda
Packit 78deda
#define FMT_BYTE       1 
Packit 78deda
#define FMT_STRING     2
Packit 78deda
#define FMT_USHORT     3
Packit 78deda
#define FMT_ULONG      4
Packit 78deda
#define FMT_URATIONAL  5
Packit 78deda
#define FMT_SBYTE      6
Packit 78deda
#define FMT_UNDEFINED  7
Packit 78deda
#define FMT_SSHORT     8
Packit 78deda
#define FMT_SLONG      9
Packit 78deda
#define FMT_SRATIONAL 10
Packit 78deda
#define FMT_SINGLE    11
Packit 78deda
#define FMT_DOUBLE    12
Packit 78deda
Packit 78deda
/* Describes tag values */
Packit 78deda
Packit 78deda
#define TAG_EXIF_OFFSET       0x8769
Packit 78deda
#define TAG_INTEROP_OFFSET    0xa005
Packit 78deda
Packit 78deda
#define TAG_MAKE              0x010F
Packit 78deda
#define TAG_MODEL             0x0110
Packit 78deda
Packit 78deda
#define TAG_ORIENTATION       0x0112
Packit 78deda
Packit 78deda
#define TAG_XRESOLUTION       0x011A
Packit 78deda
#define TAG_YRESOLUTION       0x011B
Packit 78deda
Packit 78deda
#define TAG_EXPOSURETIME      0x829A
Packit 78deda
#define TAG_FNUMBER           0x829D
Packit 78deda
Packit 78deda
#define TAG_SHUTTERSPEED      0x9201
Packit 78deda
#define TAG_APERTURE          0x9202
Packit 78deda
#define TAG_MAXAPERTURE       0x9205
Packit 78deda
#define TAG_FOCALLENGTH       0x920A
Packit 78deda
Packit 78deda
#define TAG_DATETIME_ORIGINAL 0x9003
Packit 78deda
#define TAG_USERCOMMENT       0x9286
Packit 78deda
Packit 78deda
#define TAG_SUBJECT_DISTANCE  0x9206
Packit 78deda
#define TAG_FLASH             0x9209
Packit 78deda
Packit 78deda
#define TAG_FOCALPLANEXRES    0xa20E
Packit 78deda
#define TAG_FOCALPLANEUNITS   0xa210
Packit 78deda
#define TAG_EXIF_IMAGEWIDTH   0xA002
Packit 78deda
#define TAG_EXIF_IMAGELENGTH  0xA003
Packit 78deda
Packit 78deda
/* the following is added 05-jan-2001 vcs */
Packit 78deda
#define TAG_EXPOSURE_BIAS     0x9204
Packit 78deda
#define TAG_WHITEBALANCE      0x9208
Packit 78deda
#define TAG_METERING_MODE     0x9207
Packit 78deda
#define TAG_EXPOSURE_PROGRAM  0x8822
Packit 78deda
#define TAG_ISO_EQUIVALENT    0x8827
Packit 78deda
#define TAG_COMPRESSION_LEVEL 0x9102
Packit 78deda
Packit 78deda
#define TAG_THUMBNAIL_OFFSET  0x0201
Packit 78deda
#define TAG_THUMBNAIL_LENGTH  0x0202
Packit 78deda
Packit 78deda
static TagTable const tagTable[] = {
Packit 78deda
  {   0x100,   "ImageWidth"},
Packit 78deda
  {   0x101,   "ImageLength"},
Packit 78deda
  {   0x102,   "BitsPerSample"},
Packit 78deda
  {   0x103,   "Compression"},
Packit 78deda
  {   0x106,   "PhotometricInterpretation"},
Packit 78deda
  {   0x10A,   "FillOrder"},
Packit 78deda
  {   0x10D,   "DocumentName"},
Packit 78deda
  {   0x10E,   "ImageDescription"},
Packit 78deda
  {   0x10F,   "Make"},
Packit 78deda
  {   0x110,   "Model"},
Packit 78deda
  {   0x111,   "StripOffsets"},
Packit 78deda
  {   0x112,   "Orientation"},
Packit 78deda
  {   0x115,   "SamplesPerPixel"},
Packit 78deda
  {   0x116,   "RowsPerStrip"},
Packit 78deda
  {   0x117,   "StripByteCounts"},
Packit 78deda
  {   0x11A,   "XResolution"},
Packit 78deda
  {   0x11B,   "YResolution"},
Packit 78deda
  {   0x11C,   "PlanarConfiguration"},
Packit 78deda
  {   0x128,   "ResolutionUnit"},
Packit 78deda
  {   0x12D,   "TransferFunction"},
Packit 78deda
  {   0x131,   "Software"},
Packit 78deda
  {   0x132,   "DateTime"},
Packit 78deda
  {   0x13B,   "Artist"},
Packit 78deda
  {   0x13E,   "WhitePoint"},
Packit 78deda
  {   0x13F,   "PrimaryChromaticities"},
Packit 78deda
  {   0x156,   "TransferRange"},
Packit 78deda
  {   0x200,   "JPEGProc"},
Packit 78deda
  {   0x201,   "ThumbnailOffset"},
Packit 78deda
  {   0x202,   "ThumbnailLength"},
Packit 78deda
  {   0x211,   "YCbCrCoefficients"},
Packit 78deda
  {   0x212,   "YCbCrSubSampling"},
Packit 78deda
  {   0x213,   "YCbCrPositioning"},
Packit 78deda
  {   0x214,   "ReferenceBlackWhite"},
Packit 78deda
  {   0x828D,  "CFARepeatPatternDim"},
Packit 78deda
  {   0x828E,  "CFAPattern"},
Packit 78deda
  {   0x828F,  "BatteryLevel"},
Packit 78deda
  {   0x8298,  "Copyright"},
Packit 78deda
  {   0x829A,  "ExposureTime"},
Packit 78deda
  {   0x829D,  "FNumber"},
Packit 78deda
  {   0x83BB,  "IPTC/NAA"},
Packit 78deda
  {   0x8769,  "ExifOffset"},
Packit 78deda
  {   0x8773,  "InterColorProfile"},
Packit 78deda
  {   0x8822,  "ExposureProgram"},
Packit 78deda
  {   0x8824,  "SpectralSensitivity"},
Packit 78deda
  {   0x8825,  "GPSInfo"},
Packit 78deda
  {   0x8827,  "ISOSpeedRatings"},
Packit 78deda
  {   0x8828,  "OECF"},
Packit 78deda
  {   0x9000,  "ExifVersion"},
Packit 78deda
  {   0x9003,  "DateTimeOriginal"},
Packit 78deda
  {   0x9004,  "DateTimeDigitized"},
Packit 78deda
  {   0x9101,  "ComponentsConfiguration"},
Packit 78deda
  {   0x9102,  "CompressedBitsPerPixel"},
Packit 78deda
  {   0x9201,  "ShutterSpeedValue"},
Packit 78deda
  {   0x9202,  "ApertureValue"},
Packit 78deda
  {   0x9203,  "BrightnessValue"},
Packit 78deda
  {   0x9204,  "ExposureBiasValue"},
Packit 78deda
  {   0x9205,  "MaxApertureValue"},
Packit 78deda
  {   0x9206,  "SubjectDistance"},
Packit 78deda
  {   0x9207,  "MeteringMode"},
Packit 78deda
  {   0x9208,  "LightSource"},
Packit 78deda
  {   0x9209,  "Flash"},
Packit 78deda
  {   0x920A,  "FocalLength"},
Packit 78deda
  {   0x927C,  "MakerNote"},
Packit 78deda
  {   0x9286,  "UserComment"},
Packit 78deda
  {   0x9290,  "SubSecTime"},
Packit 78deda
  {   0x9291,  "SubSecTimeOriginal"},
Packit 78deda
  {   0x9292,  "SubSecTimeDigitized"},
Packit 78deda
  {   0xA000,  "FlashPixVersion"},
Packit 78deda
  {   0xA001,  "ColorSpace"},
Packit 78deda
  {   0xA002,  "ExifImageWidth"},
Packit 78deda
  {   0xA003,  "ExifImageLength"},
Packit 78deda
  {   0xA005,  "InteroperabilityOffset"},
Packit 78deda
  {   0xA20B,  "FlashEnergy"},                 /* 0x920B in TIFF/EP */
Packit 78deda
  {   0xA20C,  "SpatialFrequencyResponse"},  /* 0x920C    -  - */
Packit 78deda
  {   0xA20E,  "FocalPlaneXResolution"},     /* 0x920E    -  - */
Packit 78deda
  {   0xA20F,  "FocalPlaneYResolution"},      /* 0x920F    -  - */
Packit 78deda
  {   0xA210,  "FocalPlaneResolutionUnit"},  /* 0x9210    -  - */
Packit 78deda
  {   0xA214,  "SubjectLocation"},             /* 0x9214    -  - */
Packit 78deda
  {   0xA215,  "ExposureIndex"},            /* 0x9215    -  - */
Packit 78deda
  {   0xA217,  "SensingMethod"},            /* 0x9217    -  - */
Packit 78deda
  {   0xA300,  "FileSource"},
Packit 78deda
  {   0xA301,  "SceneType"},
Packit 78deda
  {      0, NULL}
Packit 78deda
} ;
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
typedef enum { NORMAL, MOTOROLA } ByteOrder;
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static uint16_t
Packit 78deda
get16u(const void * const data,
Packit 78deda
       ByteOrder    const byteOrder) {
Packit 78deda
/*--------------------------------------------------------------------------
Packit 78deda
   Convert a 16 bit unsigned value from file's native byte order
Packit 78deda
--------------------------------------------------------------------------*/
Packit 78deda
    if (byteOrder == MOTOROLA){
Packit 78deda
        return (((const unsigned char *)data)[0] << 8) | 
Packit 78deda
            ((const unsigned char *)data)[1];
Packit 78deda
    }else{
Packit 78deda
        return (((const unsigned char *)data)[1] << 8) | 
Packit 78deda
            ((const unsigned char *)data)[0];
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static int32_t
Packit 78deda
get32s(const void * const data,
Packit 78deda
       ByteOrder    const byteOrder) {
Packit 78deda
/*--------------------------------------------------------------------------
Packit 78deda
   Convert a 32 bit signed value from file's native byte order
Packit 78deda
--------------------------------------------------------------------------*/
Packit 78deda
    if (byteOrder == MOTOROLA){
Packit 78deda
        return  
Packit 78deda
            (((const char *)data)[0] << 24) |
Packit 78deda
            (((const unsigned char *)data)[1] << 16) |
Packit 78deda
            (((const unsigned char *)data)[2] << 8 ) | 
Packit 78deda
            (((const unsigned char *)data)[3] << 0 );
Packit 78deda
    } else {
Packit 78deda
        return  
Packit 78deda
            (((const char *)data)[3] << 24) |
Packit 78deda
            (((const unsigned char *)data)[2] << 16) |
Packit 78deda
            (((const unsigned char *)data)[1] << 8 ) | 
Packit 78deda
            (((const unsigned char *)data)[0] << 0 );
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static uint32_t
Packit 78deda
get32u(const void * const data,
Packit 78deda
       ByteOrder    const byteOrder) {
Packit 78deda
/*--------------------------------------------------------------------------
Packit 78deda
   Convert a 32 bit unsigned value from file's native byte order
Packit 78deda
--------------------------------------------------------------------------*/
Packit 78deda
    return (uint32_t)get32s(data, byteOrder) & 0xffffffff;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
printFormatNumber(FILE *       const fileP, 
Packit 78deda
                  const void * const ValuePtr, 
Packit 78deda
                  int          const Format,
Packit 78deda
                  int          const ByteCount,
Packit 78deda
                  ByteOrder    const byteOrder) {
Packit 78deda
/*--------------------------------------------------------------------------
Packit 78deda
   Display a number as one of its many formats
Packit 78deda
--------------------------------------------------------------------------*/
Packit 78deda
    switch(Format){
Packit 78deda
    case FMT_SBYTE:
Packit 78deda
    case FMT_BYTE:
Packit 78deda
        fprintf(fileP, "%02x\n", *(unsigned char *)ValuePtr);
Packit 78deda
        break;
Packit 78deda
    case FMT_USHORT:
Packit 78deda
        fprintf(fileP, "%d\n",get16u(ValuePtr, byteOrder));
Packit 78deda
        break;
Packit 78deda
    case FMT_ULONG:     
Packit 78deda
    case FMT_SLONG:
Packit 78deda
        fprintf(fileP, "%d\n",get32s(ValuePtr, byteOrder));
Packit 78deda
        break;
Packit 78deda
    case FMT_SSHORT:    
Packit 78deda
        fprintf(fileP, "%hd\n",(signed short)get16u(ValuePtr, byteOrder));
Packit 78deda
        break;
Packit 78deda
    case FMT_URATIONAL:
Packit 78deda
    case FMT_SRATIONAL: 
Packit 78deda
        fprintf(fileP, "%d/%d\n",get32s(ValuePtr, byteOrder),
Packit 78deda
                get32s(4+(char *)ValuePtr, byteOrder));
Packit 78deda
        break;
Packit 78deda
    case FMT_SINGLE:    
Packit 78deda
        fprintf(fileP, "%f\n",(double)*(float *)ValuePtr);
Packit 78deda
        break;
Packit 78deda
    case FMT_DOUBLE:
Packit 78deda
        fprintf(fileP, "%f\n",*(double *)ValuePtr);
Packit 78deda
        break;
Packit 78deda
    default: 
Packit 78deda
        fprintf(fileP, "Unknown format %d:", Format);
Packit 78deda
        {
Packit 78deda
            unsigned int a;
Packit 78deda
            for (a = 0; a < ByteCount && a < 16; ++a)
Packit 78deda
                printf("%02x", ((unsigned char *)ValuePtr)[a]);
Packit 78deda
        }
Packit 78deda
        fprintf(fileP, "\n");
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static double
Packit 78deda
convertAnyFormat(const void * const ValuePtr,
Packit 78deda
                 int          const Format,
Packit 78deda
                 ByteOrder    const byteOrder) {
Packit 78deda
/*--------------------------------------------------------------------------
Packit 78deda
   Evaluate number, be it int, rational, or float from directory.
Packit 78deda
--------------------------------------------------------------------------*/
Packit 78deda
    double Value;
Packit 78deda
    Value = 0;
Packit 78deda
Packit 78deda
    switch(Format){
Packit 78deda
    case FMT_SBYTE:
Packit 78deda
        Value = *(signed char *)ValuePtr;
Packit 78deda
        break;
Packit 78deda
    case FMT_BYTE:
Packit 78deda
        Value = *(unsigned char *)ValuePtr;
Packit 78deda
        break;
Packit 78deda
    case FMT_USHORT:
Packit 78deda
        Value = get16u(ValuePtr, byteOrder);
Packit 78deda
        break;
Packit 78deda
    case FMT_ULONG:
Packit 78deda
        Value = get32u(ValuePtr, byteOrder);
Packit 78deda
        break;
Packit 78deda
    case FMT_URATIONAL:
Packit 78deda
    case FMT_SRATIONAL: {
Packit 78deda
        int num, den;
Packit 78deda
        num = get32s(ValuePtr, byteOrder);
Packit 78deda
        den = get32s(4+(char *)ValuePtr, byteOrder);
Packit 78deda
        Value = den == 0 ? 0 : (double)(num/den);
Packit 78deda
    } break;
Packit 78deda
    case FMT_SSHORT:
Packit 78deda
        Value = (signed short)get16u(ValuePtr, byteOrder);
Packit 78deda
        break;
Packit 78deda
    case FMT_SLONG:
Packit 78deda
        Value = get32s(ValuePtr, byteOrder);
Packit 78deda
        break;
Packit 78deda
Packit 78deda
    /* Not sure if this is correct (never seen float used in Exif format) */
Packit 78deda
    case FMT_SINGLE:
Packit 78deda
        Value = (double)*(float *)ValuePtr;
Packit 78deda
        break;
Packit 78deda
    case FMT_DOUBLE:
Packit 78deda
        Value = *(double *)ValuePtr;
Packit 78deda
        break;
Packit 78deda
    }
Packit 78deda
    return Value;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
traceTag(int                   const tag,
Packit 78deda
         int                   const format,
Packit 78deda
         const unsigned char * const valuePtr,
Packit 78deda
         unsigned int          const byteCount,
Packit 78deda
         ByteOrder             const byteOrder) {
Packit 78deda
             
Packit 78deda
    /* Show tag name */
Packit 78deda
    unsigned int a;
Packit 78deda
    bool found;
Packit 78deda
    for (a = 0, found = false; !found; ++a){
Packit 78deda
        if (tagTable[a].Tag == 0){
Packit 78deda
            fprintf(stderr, "  Unknown Tag %04x Value = ", tag);
Packit 78deda
            found = true;
Packit 78deda
        }
Packit 78deda
        if (tagTable[a].Tag == tag){
Packit 78deda
            fprintf(stderr, "    %s = ",tagTable[a].Desc);
Packit 78deda
            found = true;
Packit 78deda
        }
Packit 78deda
    }
Packit 78deda
Packit 78deda
    /* Show tag value. */
Packit 78deda
    switch(format){
Packit 78deda
Packit 78deda
    case FMT_UNDEFINED:
Packit 78deda
        /* Undefined is typically an ascii string. */
Packit 78deda
Packit 78deda
    case FMT_STRING: {
Packit 78deda
        /* String arrays printed without function call
Packit 78deda
           (different from int arrays)
Packit 78deda
        */
Packit 78deda
        bool noPrint;
Packit 78deda
        printf("\"");
Packit 78deda
        for (a = 0, noPrint = false; a < byteCount; ++a){
Packit 78deda
            if (ISPRINT((valuePtr)[a])){
Packit 78deda
                fprintf(stderr, "%c", valuePtr[a]);
Packit 78deda
                noPrint = false;
Packit 78deda
            } else {
Packit 78deda
                /* Avoiding indicating too many unprintable characters of
Packit 78deda
                   proprietary bits of binary information this program may not
Packit 78deda
                   know how to parse.
Packit 78deda
                */
Packit 78deda
                if (!noPrint){
Packit 78deda
                    fprintf(stderr, "?");
Packit 78deda
                    noPrint = true;
Packit 78deda
                }
Packit 78deda
            }
Packit 78deda
        }
Packit 78deda
        fprintf(stderr, "\"\n");
Packit 78deda
    } break;
Packit 78deda
Packit 78deda
    default:
Packit 78deda
        /* Handle arrays of numbers later (will there ever be?)*/
Packit 78deda
        printFormatNumber(stderr, valuePtr, format, byteCount, byteOrder);
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
/* Forward declaration for recursion */
Packit 78deda
Packit 78deda
static void 
Packit 78deda
processExifDir(const unsigned char *  const ExifData, 
Packit 78deda
               unsigned int           const ExifLength,
Packit 78deda
               unsigned int           const DirOffset,
Packit 78deda
               exif_ImageInfo *       const imageInfoP, 
Packit 78deda
               ByteOrder              const byteOrder,
Packit 78deda
               bool                   const wantTagTrace,
Packit 78deda
               const unsigned char ** const LastExifRefdP);
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
processDirEntry(const unsigned char *  const dirEntry,
Packit 78deda
                const unsigned char *  const exifData,
Packit 78deda
                unsigned int           const exifLength,
Packit 78deda
                ByteOrder              const byteOrder,
Packit 78deda
                bool                   const wantTagTrace,
Packit 78deda
                exif_ImageInfo *       const imageInfoP, 
Packit 78deda
                unsigned int *         const thumbnailOffsetP,
Packit 78deda
                unsigned int *         const thumbnailSizeP,
Packit 78deda
                bool *                 const haveThumbnailP,
Packit 78deda
                const unsigned char ** const lastExifRefdP) {
Packit 78deda
Packit 78deda
    int const tag        = get16u(&dirEntry[0], byteOrder);
Packit 78deda
    int const format     = get16u(&dirEntry[2], byteOrder);
Packit 78deda
    int const components = get32u(&dirEntry[4], byteOrder);
Packit 78deda
Packit 78deda
    const unsigned char * valuePtr;
Packit 78deda
        /* This actually can point to a variety of things; it must be cast to
Packit 78deda
           other types when used.  But we use it as a byte-by-byte cursor, so
Packit 78deda
           we declare it as a pointer to a generic byte here.
Packit 78deda
        */
Packit 78deda
    unsigned int byteCount;
Packit 78deda
Packit 78deda
    if ((format-1) >= NUM_FORMATS) {
Packit 78deda
        /* (-1) catches illegal zero case as unsigned underflows
Packit 78deda
           to positive large.  
Packit 78deda
        */
Packit 78deda
        pm_message("Illegal number format %d for tag %04x", format, tag);
Packit 78deda
        return;
Packit 78deda
    }
Packit 78deda
        
Packit 78deda
    byteCount = components * bytesPerFormat[format];
Packit 78deda
Packit 78deda
    if (byteCount > 4){
Packit 78deda
        unsigned const offsetVal = get32u(&dirEntry[8], byteOrder);
Packit 78deda
        /* If its bigger than 4 bytes, the dir entry contains an offset.*/
Packit 78deda
        if (offsetVal + byteCount > exifLength){
Packit 78deda
            /* Bogus pointer offset and / or bytecount value */
Packit 78deda
            pm_message("Illegal pointer offset value in EXIF "
Packit 78deda
                       "for tag %04x.  "
Packit 78deda
                       "Offset %d bytes %d ExifLen %d\n",
Packit 78deda
                       tag, offsetVal, byteCount, exifLength);
Packit 78deda
            return;
Packit 78deda
        }
Packit 78deda
        valuePtr = &exifData[offsetVal];
Packit 78deda
    } else {
Packit 78deda
        /* 4 bytes or less and value is in the dir entry itself */
Packit 78deda
        valuePtr = &dirEntry[8];
Packit 78deda
    }
Packit 78deda
Packit 78deda
    if (*lastExifRefdP < valuePtr + byteCount){
Packit 78deda
        /* Keep track of last byte in the exif header that was actually
Packit 78deda
           referenced.  That way, we know where the discardable thumbnail data
Packit 78deda
           begins.
Packit 78deda
        */
Packit 78deda
        *lastExifRefdP = valuePtr + byteCount;
Packit 78deda
    }
Packit 78deda
Packit 78deda
    if (wantTagTrace)
Packit 78deda
        traceTag(tag, format, valuePtr, byteCount, byteOrder);
Packit 78deda
Packit 78deda
    *haveThumbnailP = (tag == TAG_THUMBNAIL_OFFSET);
Packit 78deda
Packit 78deda
    /* Extract useful components of tag */
Packit 78deda
    switch (tag){
Packit 78deda
Packit 78deda
    case TAG_MAKE:
Packit 78deda
        STRSCPY(imageInfoP->CameraMake, (const char*)valuePtr);
Packit 78deda
        break;
Packit 78deda
Packit 78deda
    case TAG_MODEL:
Packit 78deda
        STRSCPY(imageInfoP->CameraModel, (const char*)valuePtr);
Packit 78deda
        break;
Packit 78deda
Packit 78deda
    case TAG_XRESOLUTION:
Packit 78deda
        imageInfoP->XResolution = 
Packit 78deda
            convertAnyFormat(valuePtr, format, byteOrder);
Packit 78deda
        break;
Packit 78deda
Packit 78deda
    case TAG_YRESOLUTION:
Packit 78deda
        imageInfoP->YResolution = 
Packit 78deda
            convertAnyFormat(valuePtr, format, byteOrder);
Packit 78deda
        break;
Packit 78deda
Packit 78deda
    case TAG_DATETIME_ORIGINAL:
Packit 78deda
        STRSCPY(imageInfoP->DateTime, (const char*)valuePtr);
Packit 78deda
        imageInfoP->DatePointer = (const char*)valuePtr;
Packit 78deda
        break;
Packit 78deda
Packit 78deda
    case TAG_USERCOMMENT: {
Packit 78deda
        /* Olympus has this padded with trailing spaces.  We stop the copy
Packit 78deda
           where those start.
Packit 78deda
        */
Packit 78deda
        const char * const value = (const char *)valuePtr;
Packit 78deda
Packit 78deda
        unsigned int cursor;
Packit 78deda
        unsigned int outCursor;
Packit 78deda
        unsigned int end;
Packit 78deda
Packit 78deda
        for (end = byteCount; end > 0 && value[end] == ' '; --end);
Packit 78deda
Packit 78deda
        /* Skip "ASCII" if it is there */
Packit 78deda
        if (end >= 5 && MEMEQ(value, "ASCII", 5))
Packit 78deda
            cursor = 5;
Packit 78deda
        else
Packit 78deda
            cursor = 0;
Packit 78deda
Packit 78deda
        /* Skip consecutive blanks and NULs */
Packit 78deda
Packit 78deda
        for (;
Packit 78deda
             cursor < byteCount && 
Packit 78deda
                 (value[cursor] == '\0' || value[cursor] == ' ');
Packit 78deda
             ++cursor);
Packit 78deda
Packit 78deda
        /* Copy the rest as the comment */
Packit 78deda
Packit 78deda
        for (outCursor = 0;
Packit 78deda
             cursor < end && outCursor < MAX_COMMENT-1;
Packit 78deda
             ++cursor)
Packit 78deda
            imageInfoP->Comments[outCursor++] = value[cursor];
Packit 78deda
Packit 78deda
        imageInfoP->Comments[outCursor++] = '\0';
Packit 78deda
    } break;
Packit 78deda
Packit 78deda
    case TAG_FNUMBER:
Packit 78deda
        /* Simplest way of expressing aperture, so I trust it the most.
Packit 78deda
           (overwrite previously computd value if there is one)
Packit 78deda
        */
Packit 78deda
        imageInfoP->ApertureFNumber = 
Packit 78deda
            (float)convertAnyFormat(valuePtr, format, byteOrder);
Packit 78deda
        break;
Packit 78deda
Packit 78deda
    case TAG_APERTURE:
Packit 78deda
    case TAG_MAXAPERTURE:
Packit 78deda
        /* More relevant info always comes earlier, so only use this field if
Packit 78deda
           we don't have appropriate aperture information yet.
Packit 78deda
        */
Packit 78deda
        if (imageInfoP->ApertureFNumber == 0){
Packit 78deda
            imageInfoP->ApertureFNumber = (float)
Packit 78deda
                exp(convertAnyFormat(valuePtr, format, byteOrder)
Packit 78deda
                    * log(2) * 0.5);
Packit 78deda
        }
Packit 78deda
        break;
Packit 78deda
Packit 78deda
    case TAG_FOCALLENGTH:
Packit 78deda
        /* Nice digital cameras actually save the focal length
Packit 78deda
           as a function of how farthey are zoomed in. 
Packit 78deda
        */
Packit 78deda
Packit 78deda
        imageInfoP->FocalLength = 
Packit 78deda
            (float)convertAnyFormat(valuePtr, format, byteOrder);
Packit 78deda
        break;
Packit 78deda
Packit 78deda
    case TAG_SUBJECT_DISTANCE:
Packit 78deda
        /* Inidcates the distacne the autofocus camera is focused to.
Packit 78deda
           Tends to be less accurate as distance increases.
Packit 78deda
        */
Packit 78deda
        imageInfoP->Distance = 
Packit 78deda
            (float)convertAnyFormat(valuePtr, format, byteOrder);
Packit 78deda
        break;
Packit 78deda
Packit 78deda
    case TAG_EXPOSURETIME:
Packit 78deda
        /* Simplest way of expressing exposure time, so I
Packit 78deda
           trust it most.  (overwrite previously computd value
Packit 78deda
           if there is one) 
Packit 78deda
        */
Packit 78deda
        imageInfoP->ExposureTime = 
Packit 78deda
            (float)convertAnyFormat(valuePtr, format, byteOrder);
Packit 78deda
        break;
Packit 78deda
Packit 78deda
    case TAG_SHUTTERSPEED:
Packit 78deda
        /* More complicated way of expressing exposure time,
Packit 78deda
           so only use this value if we don't already have it
Packit 78deda
           from somewhere else.  
Packit 78deda
        */
Packit 78deda
        if (imageInfoP->ExposureTime == 0){
Packit 78deda
            imageInfoP->ExposureTime = (float)
Packit 78deda
                (1/exp(convertAnyFormat(valuePtr, format, byteOrder)
Packit 78deda
                       * log(2)));
Packit 78deda
        }
Packit 78deda
        break;
Packit 78deda
Packit 78deda
    case TAG_FLASH:
Packit 78deda
        if ((int)convertAnyFormat(valuePtr, format, byteOrder) & 0x7){
Packit 78deda
            imageInfoP->FlashUsed = TRUE;
Packit 78deda
        }else{
Packit 78deda
            imageInfoP->FlashUsed = FALSE;
Packit 78deda
        }
Packit 78deda
        break;
Packit 78deda
Packit 78deda
    case TAG_ORIENTATION:
Packit 78deda
        imageInfoP->Orientation = 
Packit 78deda
            (int)convertAnyFormat(valuePtr, format, byteOrder);
Packit 78deda
        if (imageInfoP->Orientation < 1 || 
Packit 78deda
            imageInfoP->Orientation > 8){
Packit 78deda
            pm_message("Undefined rotation value %d",
Packit 78deda
                       imageInfoP->Orientation);
Packit 78deda
            imageInfoP->Orientation = 0;
Packit 78deda
        }
Packit 78deda
        break;
Packit 78deda
Packit 78deda
    case TAG_EXIF_IMAGELENGTH:
Packit 78deda
    case TAG_EXIF_IMAGEWIDTH:
Packit 78deda
        /* Use largest of height and width to deal with images
Packit 78deda
           that have been rotated to portrait format.  
Packit 78deda
        */
Packit 78deda
        ExifImageWidth =
Packit 78deda
            MIN(ExifImageWidth,
Packit 78deda
                (int)convertAnyFormat(valuePtr, format, byteOrder));
Packit 78deda
        break;
Packit 78deda
Packit 78deda
    case TAG_FOCALPLANEXRES:
Packit 78deda
        HaveXRes = TRUE;
Packit 78deda
        FocalplaneXRes = convertAnyFormat(valuePtr, format, byteOrder);
Packit 78deda
        break;
Packit 78deda
Packit 78deda
    case TAG_FOCALPLANEUNITS:
Packit 78deda
        switch((int)convertAnyFormat(valuePtr, format, byteOrder)){
Packit 78deda
        case 1: FocalplaneUnits = 25.4; break; /* 1 inch */
Packit 78deda
        case 2: 
Packit 78deda
            /* According to the information I was using, 2
Packit 78deda
               means meters.  But looking at the Cannon
Packit 78deda
               powershot's files, inches is the only
Packit 78deda
               sensible value.  
Packit 78deda
            */
Packit 78deda
            FocalplaneUnits = 25.4;
Packit 78deda
            break;
Packit 78deda
Packit 78deda
        case 3: FocalplaneUnits = 10;   break;  /* 1 centimeter*/
Packit 78deda
        case 4: FocalplaneUnits = 1;    break;  /* 1 millimeter*/
Packit 78deda
        case 5: FocalplaneUnits = .001; break;  /* 1 micrometer*/
Packit 78deda
        }
Packit 78deda
        break;
Packit 78deda
Packit 78deda
        /* Remaining cases contributed by: Volker C. Schoech
Packit 78deda
           (schoech@gmx.de)
Packit 78deda
        */
Packit 78deda
Packit 78deda
    case TAG_EXPOSURE_BIAS:
Packit 78deda
        imageInfoP->ExposureBias = 
Packit 78deda
            (float) convertAnyFormat(valuePtr, format, byteOrder);
Packit 78deda
        break;
Packit 78deda
Packit 78deda
    case TAG_WHITEBALANCE:
Packit 78deda
        imageInfoP->Whitebalance = 
Packit 78deda
            (int)convertAnyFormat(valuePtr, format, byteOrder);
Packit 78deda
        break;
Packit 78deda
Packit 78deda
    case TAG_METERING_MODE:
Packit 78deda
        imageInfoP->MeteringMode = 
Packit 78deda
            (int)convertAnyFormat(valuePtr, format, byteOrder);
Packit 78deda
        break;
Packit 78deda
Packit 78deda
    case TAG_EXPOSURE_PROGRAM:
Packit 78deda
        imageInfoP->ExposureProgram = 
Packit 78deda
            (int)convertAnyFormat(valuePtr, format, byteOrder);
Packit 78deda
        break;
Packit 78deda
Packit 78deda
    case TAG_ISO_EQUIVALENT:
Packit 78deda
        imageInfoP->ISOequivalent = 
Packit 78deda
            (int)convertAnyFormat(valuePtr, format, byteOrder);
Packit 78deda
        if ( imageInfoP->ISOequivalent < 50 ) 
Packit 78deda
            imageInfoP->ISOequivalent *= 200;
Packit 78deda
        break;
Packit 78deda
Packit 78deda
    case TAG_COMPRESSION_LEVEL:
Packit 78deda
        imageInfoP->CompressionLevel = 
Packit 78deda
            (int)convertAnyFormat(valuePtr, format, byteOrder);
Packit 78deda
        break;
Packit 78deda
Packit 78deda
    case TAG_THUMBNAIL_OFFSET:
Packit 78deda
        *thumbnailOffsetP = (unsigned int)
Packit 78deda
            convertAnyFormat(valuePtr, format, byteOrder);
Packit 78deda
        break;
Packit 78deda
Packit 78deda
    case TAG_THUMBNAIL_LENGTH:
Packit 78deda
        *thumbnailSizeP = (unsigned int)
Packit 78deda
            convertAnyFormat(valuePtr, format, byteOrder);
Packit 78deda
        break;
Packit 78deda
Packit 78deda
    case TAG_EXIF_OFFSET:
Packit 78deda
    case TAG_INTEROP_OFFSET: {
Packit 78deda
        unsigned int const subdirOffset = get32u(valuePtr, byteOrder);
Packit 78deda
        if (subdirOffset >= exifLength)
Packit 78deda
            pm_message("Illegal exif or interop offset "
Packit 78deda
                       "directory link.  Offset is %u, "
Packit 78deda
                       "but Exif data is only %u bytes.",
Packit 78deda
                       subdirOffset, exifLength);
Packit 78deda
        else
Packit 78deda
            processExifDir(exifData, exifLength, subdirOffset, 
Packit 78deda
                           imageInfoP, byteOrder, wantTagTrace,
Packit 78deda
                           lastExifRefdP);
Packit 78deda
    } break;
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void 
Packit 78deda
processExifDir(const unsigned char *  const exifData, 
Packit 78deda
               unsigned int           const exifLength,
Packit 78deda
               unsigned int           const dirOffset,
Packit 78deda
               exif_ImageInfo *       const imageInfoP, 
Packit 78deda
               ByteOrder              const byteOrder,
Packit 78deda
               bool                   const wantTagTrace,
Packit 78deda
               const unsigned char ** const lastExifRefdP) {
Packit 78deda
/*--------------------------------------------------------------------------
Packit 78deda
   Process one of the nested EXIF directories.
Packit 78deda
--------------------------------------------------------------------------*/
Packit 78deda
    const unsigned char * const dirStart = exifData + dirOffset;
Packit 78deda
    unsigned int const numDirEntries = get16u(&dirStart[0], byteOrder);
Packit 78deda
    unsigned int de;
Packit 78deda
    bool haveThumbnail;
Packit 78deda
    unsigned int thumbnailOffset;
Packit 78deda
    unsigned int thumbnailSize;
Packit 78deda
Packit 78deda
    #define DIR_ENTRY_ADDR(Start, Entry) (Start+2+12*(Entry))
Packit 78deda
Packit 78deda
    {
Packit 78deda
        const unsigned char * const dirEnd =
Packit 78deda
            DIR_ENTRY_ADDR(dirStart, numDirEntries);
Packit 78deda
        if (dirEnd + 4 > (exifData + exifLength)){
Packit 78deda
            if (dirEnd + 2 == exifData + exifLength || 
Packit 78deda
                dirEnd == exifData + exifLength){
Packit 78deda
                /* Version 1.3 of jhead would truncate a bit too much.
Packit 78deda
                   This also caught later on as well.
Packit 78deda
                */
Packit 78deda
            }else{
Packit 78deda
                /* Note: Files that had thumbnails trimmed with jhead
Packit 78deda
                   1.3 or earlier might trigger this.
Packit 78deda
                */
Packit 78deda
                pm_message("Illegal directory entry size");
Packit 78deda
                return;
Packit 78deda
            }
Packit 78deda
        }
Packit 78deda
        *lastExifRefdP = MAX(*lastExifRefdP, dirEnd);
Packit 78deda
    }
Packit 78deda
Packit 78deda
    if (wantTagTrace)
Packit 78deda
        pm_message("Directory with %d entries", numDirEntries);
Packit 78deda
Packit 78deda
    haveThumbnail   = false;  /* initial value */
Packit 78deda
    thumbnailOffset = 0;      /* initial value */
Packit 78deda
    thumbnailSize   = 0;      /* initial value */
Packit 78deda
Packit 78deda
    for (de = 0; de < numDirEntries; ++de)
Packit 78deda
        processDirEntry(DIR_ENTRY_ADDR(dirStart, de), exifData, exifLength,
Packit 78deda
                        byteOrder, wantTagTrace, imageInfoP,
Packit 78deda
                        &thumbnailOffset, &thumbnailSize, &haveThumbnail,
Packit 78deda
                        lastExifRefdP);
Packit 78deda
Packit 78deda
    if (haveThumbnail)
Packit 78deda
        DirWithThumbnailPtrs = dirStart;
Packit 78deda
Packit 78deda
    {
Packit 78deda
        /* In addition to linking to subdirectories via exif tags,
Packit 78deda
           there's also a potential link to another directory at the end
Packit 78deda
           of each directory.  This has got to be the result of a
Packit 78deda
           committee!  
Packit 78deda
        */
Packit 78deda
        if (DIR_ENTRY_ADDR(dirStart, numDirEntries) + 4 <= 
Packit 78deda
            exifData + exifLength){
Packit 78deda
            unsigned int const subdirOffset =
Packit 78deda
                get32u(dirStart + 2 + 12*numDirEntries, byteOrder);
Packit 78deda
            if (subdirOffset){
Packit 78deda
                const unsigned char * const subdirStart =
Packit 78deda
                    exifData + subdirOffset;
Packit 78deda
                if (subdirStart > exifData + exifLength){
Packit 78deda
                    if (subdirStart < exifData + exifLength + 20){
Packit 78deda
                        /* Jhead 1.3 or earlier would crop the whole directory!
Packit 78deda
                           As Jhead produces this form of format incorrectness,
Packit 78deda
                           I'll just let it pass silently.
Packit 78deda
                        */
Packit 78deda
                        if (wantTagTrace) 
Packit 78deda
                            printf("Thumbnail removed with "
Packit 78deda
                                   "Jhead 1.3 or earlier\n");
Packit 78deda
                    }else{
Packit 78deda
                        pm_message("Illegal subdirectory link");
Packit 78deda
                    }
Packit 78deda
                }else{
Packit 78deda
                    if (subdirOffset <= exifLength)
Packit 78deda
                        processExifDir(exifData, exifLength, subdirOffset,
Packit 78deda
                                       imageInfoP, byteOrder, wantTagTrace,
Packit 78deda
                                       lastExifRefdP);
Packit 78deda
                }
Packit 78deda
            }
Packit 78deda
        }else{
Packit 78deda
            /* The exif header ends before the last next directory pointer. */
Packit 78deda
        }
Packit 78deda
    }
Packit 78deda
Packit 78deda
    if (thumbnailSize && thumbnailOffset){
Packit 78deda
        if (thumbnailSize + thumbnailOffset <= exifLength){
Packit 78deda
            /* The thumbnail pointer appears to be valid.  Store it. */
Packit 78deda
            imageInfoP->ThumbnailPointer = exifData + thumbnailOffset;
Packit 78deda
            imageInfoP->ThumbnailSize = thumbnailSize;
Packit 78deda
Packit 78deda
            if (wantTagTrace){
Packit 78deda
                fprintf(stderr, "Thumbnail size: %u bytes\n", thumbnailSize);
Packit 78deda
            }
Packit 78deda
        }
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
void 
Packit 78deda
exif_parse(const unsigned char * const exifData,
Packit 78deda
           unsigned int          const length,
Packit 78deda
           exif_ImageInfo *      const imageInfoP, 
Packit 78deda
           bool                  const wantTagTrace,
Packit 78deda
           const char **         const errorP) {
Packit 78deda
/*--------------------------------------------------------------------------
Packit 78deda
  Interpret an EXIF APP1 marker
Packit 78deda
Packit 78deda
  'exifData' is the actual Exif data; it does not include the
Packit 78deda
  "Exif" identifier and length field that often prefix Exif data.
Packit 78deda
Packit 78deda
  'length' is the length of the Exif section.
Packit 78deda
--------------------------------------------------------------------------*/
Packit 78deda
    ByteOrder byteOrder;
Packit 78deda
    int FirstOffset;
Packit 78deda
    const unsigned char * lastExifRefd;
Packit 78deda
Packit 78deda
    *errorP = NULL;  /* initial assumption */
Packit 78deda
Packit 78deda
    if (wantTagTrace)
Packit 78deda
        fprintf(stderr, "Exif header %d bytes long\n",length);
Packit 78deda
Packit 78deda
    if (MEMEQ(exifData + 0, "II" , 2)) {
Packit 78deda
        if (wantTagTrace) 
Packit 78deda
            fprintf(stderr, "Exif header in Intel order\n");
Packit 78deda
        byteOrder = NORMAL;
Packit 78deda
    } else {
Packit 78deda
        if (MEMEQ(exifData + 0, "MM", 2)) {
Packit 78deda
            if (wantTagTrace) 
Packit 78deda
                fprintf(stderr, "Exif header in Motorola order\n");
Packit 78deda
            byteOrder = MOTOROLA;
Packit 78deda
        } else {
Packit 78deda
            pm_asprintf(errorP, "Invalid alignment marker in Exif "
Packit 78deda
                        "data.  First two bytes are '%c%c' (0x%02x%02x) "
Packit 78deda
                        "instead of 'II' or 'MM'.", 
Packit 78deda
                        exifData[0], exifData[1], exifData[0], exifData[1]);
Packit 78deda
        }
Packit 78deda
    }
Packit 78deda
    if (!*errorP) {
Packit 78deda
        unsigned short const start = get16u(exifData + 2, byteOrder);
Packit 78deda
        /* Check the next value for correctness. */
Packit 78deda
        if (start != 0x002a){
Packit 78deda
            pm_asprintf(errorP, "Invalid Exif header start.  "
Packit 78deda
                        "two bytes after the alignment marker "
Packit 78deda
                        "should be 0x002a, but is 0x%04x",
Packit 78deda
                        start);
Packit 78deda
        }
Packit 78deda
    }
Packit 78deda
    if (!*errorP) {
Packit 78deda
        FirstOffset = get32u(exifData + 4, byteOrder);
Packit 78deda
        if (FirstOffset < 8 || FirstOffset > 16){
Packit 78deda
            /* I used to ensure this was set to 8 (website I used
Packit 78deda
               indicated its 8) but PENTAX Optio 230 has it set
Packit 78deda
               differently, and uses it as offset. (Sept 11 2002)
Packit 78deda
                */
Packit 78deda
            pm_message("Suspicious offset of first IFD value in Exif header");
Packit 78deda
        }
Packit 78deda
        
Packit 78deda
        imageInfoP->Comments[0] = '\0';  /* Initial value - null string */
Packit 78deda
        
Packit 78deda
        HaveXRes = FALSE;  /* Initial assumption */
Packit 78deda
        FocalplaneUnits = 0;
Packit 78deda
        ExifImageWidth = 0;
Packit 78deda
        
Packit 78deda
        lastExifRefd = exifData;
Packit 78deda
        DirWithThumbnailPtrs = NULL;
Packit 78deda
        
Packit 78deda
        processExifDir(exifData, length, FirstOffset, 
Packit 78deda
                       imageInfoP, byteOrder, wantTagTrace, &lastExifRefd);
Packit 78deda
        
Packit 78deda
        /* Compute the CCD width, in millimeters. */
Packit 78deda
        if (HaveXRes){
Packit 78deda
            imageInfoP->HaveCCDWidth = 1;
Packit 78deda
            imageInfoP->CCDWidth = 
Packit 78deda
                    (float)(ExifImageWidth * FocalplaneUnits / FocalplaneXRes);
Packit 78deda
        } else
Packit 78deda
            imageInfoP->HaveCCDWidth = 0;
Packit 78deda
            
Packit 78deda
        if (wantTagTrace){
Packit 78deda
            fprintf(stderr, 
Packit 78deda
                    "Non-settings part of Exif header: %lu bytes\n",
Packit 78deda
                    (unsigned long)(exifData + length - lastExifRefd));
Packit 78deda
        }
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
void 
Packit 78deda
exif_showImageInfo(const exif_ImageInfo * const imageInfoP,
Packit 78deda
                   FILE *                 const fileP) {
Packit 78deda
/*--------------------------------------------------------------------------
Packit 78deda
   Show the collected image info, displaying camera F-stop and shutter
Packit 78deda
   speed in a consistent and legible fashion.
Packit 78deda
--------------------------------------------------------------------------*/
Packit 78deda
    if (imageInfoP->CameraMake[0]) {
Packit 78deda
        fprintf(fileP, "Camera make  : %s\n", imageInfoP->CameraMake);
Packit 78deda
        fprintf(fileP, "Camera model : %s\n", imageInfoP->CameraModel);
Packit 78deda
    }
Packit 78deda
    if (imageInfoP->DateTime[0])
Packit 78deda
        fprintf(fileP, "Date/Time    : %s\n", imageInfoP->DateTime);
Packit 78deda
Packit 78deda
    fprintf(fileP, "Resolution   : %f x %f\n",
Packit 78deda
            imageInfoP->XResolution, imageInfoP->YResolution);
Packit 78deda
Packit 78deda
    if (imageInfoP->Orientation > 1) {
Packit 78deda
Packit 78deda
        /* Only print orientation if one was supplied, and if its not
Packit 78deda
           1 (normal orientation)
Packit 78deda
Packit 78deda
           1 - The 0th row is at the visual top of the image
Packit 78deda
               and the 0th column is the visual left-hand side.
Packit 78deda
           2 - The 0th row is at the visual top of the image
Packit 78deda
               and the 0th column is the visual right-hand side.
Packit 78deda
           3 - The 0th row is at the visual bottom of the image
Packit 78deda
               and the 0th column is the visual right-hand side.
Packit 78deda
           4 - The 0th row is at the visual bottom of the image
Packit 78deda
               and the 0th column is the visual left-hand side.
Packit 78deda
           5 - The 0th row is the visual left-hand side of of the image
Packit 78deda
               and the 0th column is the visual top.
Packit 78deda
           6 - The 0th row is the visual right-hand side of of the image
Packit 78deda
               and the 0th column is the visual top.
Packit 78deda
           7 - The 0th row is the visual right-hand side of of the image
Packit 78deda
               and the 0th column is the visual bottom.
Packit 78deda
           8 - The 0th row is the visual left-hand side of of the image
Packit 78deda
               and the 0th column is the visual bottom.
Packit 78deda
Packit 78deda
           Note: The descriptions here are the same as the name of the
Packit 78deda
           command line option to pass to jpegtran to right the image
Packit 78deda
        */
Packit 78deda
        static const char * OrientTab[9] = {
Packit 78deda
            "Undefined",
Packit 78deda
            "Normal",           /* 1 */
Packit 78deda
            "flip horizontal",  /* left right reversed mirror */
Packit 78deda
            "rotate 180",       /* 3 */
Packit 78deda
            "flip vertical",    /* upside down mirror */
Packit 78deda
            "transpose",    /* Flipped about top-left <--> bottom-right axis.*/
Packit 78deda
            "rotate 90",        /* rotate 90 cw to right it. */
Packit 78deda
            "transverse",   /* flipped about top-right <--> bottom-left axis */
Packit 78deda
            "rotate 270",       /* rotate 270 to right it. */
Packit 78deda
        };
Packit 78deda
Packit 78deda
        fprintf(fileP, "Orientation  : %s\n", 
Packit 78deda
                OrientTab[imageInfoP->Orientation]);
Packit 78deda
    }
Packit 78deda
Packit 78deda
    if (imageInfoP->IsColor == 0)
Packit 78deda
        fprintf(fileP, "Color/bw     : Black and white\n");
Packit 78deda
Packit 78deda
    if (imageInfoP->FlashUsed >= 0)
Packit 78deda
        fprintf(fileP, "Flash used   : %s\n",
Packit 78deda
                imageInfoP->FlashUsed ? "Yes" :"No");
Packit 78deda
Packit 78deda
    if (imageInfoP->FocalLength) {
Packit 78deda
        fprintf(fileP, "Focal length : %4.1fmm",
Packit 78deda
                (double)imageInfoP->FocalLength);
Packit 78deda
        if (imageInfoP->HaveCCDWidth){
Packit 78deda
            fprintf(fileP, "  (35mm equivalent: %dmm)",
Packit 78deda
                    (int)
Packit 78deda
                    (imageInfoP->FocalLength/imageInfoP->CCDWidth*36 + 0.5));
Packit 78deda
        }
Packit 78deda
        fprintf(fileP, "\n");
Packit 78deda
    }
Packit 78deda
Packit 78deda
    if (imageInfoP->HaveCCDWidth)
Packit 78deda
        fprintf(fileP, "CCD width    : %2.4fmm\n",
Packit 78deda
                (double)imageInfoP->CCDWidth);
Packit 78deda
Packit 78deda
    if (imageInfoP->ExposureTime) {
Packit 78deda
        if (imageInfoP->ExposureTime < 0.010){
Packit 78deda
            fprintf(fileP, 
Packit 78deda
                    "Exposure time: %6.4f s ",
Packit 78deda
                    (double)imageInfoP->ExposureTime);
Packit 78deda
        }else{
Packit 78deda
            fprintf(fileP, 
Packit 78deda
                    "Exposure time: %5.3f s ",
Packit 78deda
                    (double)imageInfoP->ExposureTime);
Packit 78deda
        }
Packit 78deda
        if (imageInfoP->ExposureTime <= 0.5){
Packit 78deda
            fprintf(fileP, " (1/%d)",(int)(0.5 + 1/imageInfoP->ExposureTime));
Packit 78deda
        }
Packit 78deda
        fprintf(fileP, "\n");
Packit 78deda
    }
Packit 78deda
    if (imageInfoP->ApertureFNumber){
Packit 78deda
        fprintf(fileP, "Aperture     : f/%3.1f\n",
Packit 78deda
                (double)imageInfoP->ApertureFNumber);
Packit 78deda
    }
Packit 78deda
    if (imageInfoP->Distance){
Packit 78deda
        if (imageInfoP->Distance < 0){
Packit 78deda
            fprintf(fileP, "Focus dist.  : Infinite\n");
Packit 78deda
        }else{
Packit 78deda
            fprintf(fileP, "Focus dist.  :%5.2fm\n",
Packit 78deda
                    (double)imageInfoP->Distance);
Packit 78deda
        }
Packit 78deda
    }
Packit 78deda
Packit 78deda
    if (imageInfoP->ISOequivalent){ /* 05-jan-2001 vcs */
Packit 78deda
        fprintf(fileP, "ISO equiv.   : %2d\n",(int)imageInfoP->ISOequivalent);
Packit 78deda
    }
Packit 78deda
    if (imageInfoP->ExposureBias){ /* 05-jan-2001 vcs */
Packit 78deda
        fprintf(fileP, "Exposure bias:%4.2f\n",
Packit 78deda
                (double)imageInfoP->ExposureBias);
Packit 78deda
    }
Packit 78deda
        
Packit 78deda
    if (imageInfoP->Whitebalance){ /* 05-jan-2001 vcs */
Packit 78deda
        switch(imageInfoP->Whitebalance) {
Packit 78deda
        case 1:
Packit 78deda
            fprintf(fileP, "Whitebalance : sunny\n");
Packit 78deda
            break;
Packit 78deda
        case 2:
Packit 78deda
            fprintf(fileP, "Whitebalance : fluorescent\n");
Packit 78deda
            break;
Packit 78deda
        case 3:
Packit 78deda
            fprintf(fileP, "Whitebalance : incandescent\n");
Packit 78deda
            break;
Packit 78deda
        default:
Packit 78deda
            fprintf(fileP, "Whitebalance : cloudy\n");
Packit 78deda
        }
Packit 78deda
    }
Packit 78deda
    if (imageInfoP->MeteringMode){ /* 05-jan-2001 vcs */
Packit 78deda
        switch(imageInfoP->MeteringMode) {
Packit 78deda
        case 2:
Packit 78deda
            fprintf(fileP, "Metering Mode: center weight\n");
Packit 78deda
            break;
Packit 78deda
        case 3:
Packit 78deda
            fprintf(fileP, "Metering Mode: spot\n");
Packit 78deda
            break;
Packit 78deda
        case 5:
Packit 78deda
            fprintf(fileP, "Metering Mode: matrix\n");
Packit 78deda
            break;
Packit 78deda
        }
Packit 78deda
    }
Packit 78deda
    if (imageInfoP->ExposureProgram){ /* 05-jan-2001 vcs */
Packit 78deda
        switch(imageInfoP->ExposureProgram) {
Packit 78deda
        case 2:
Packit 78deda
            fprintf(fileP, "Exposure     : program (auto)\n");
Packit 78deda
            break;
Packit 78deda
        case 3:
Packit 78deda
            fprintf(fileP, "Exposure     : aperture priority (semi-auto)\n");
Packit 78deda
            break;
Packit 78deda
        case 4:
Packit 78deda
            fprintf(fileP, "Exposure     : shutter priority (semi-auto)\n");
Packit 78deda
            break;
Packit 78deda
        }
Packit 78deda
    }
Packit 78deda
    if (imageInfoP->CompressionLevel){ /* 05-jan-2001 vcs */
Packit 78deda
        switch(imageInfoP->CompressionLevel) {
Packit 78deda
        case 1:
Packit 78deda
            fprintf(fileP, "Jpeg Quality  : basic\n");
Packit 78deda
            break;
Packit 78deda
        case 2:
Packit 78deda
            fprintf(fileP, "Jpeg Quality  : normal\n");
Packit 78deda
            break;
Packit 78deda
        case 4:
Packit 78deda
            fprintf(fileP, "Jpeg Quality  : fine\n");
Packit 78deda
            break;
Packit 78deda
       }
Packit 78deda
    }
Packit 78deda
Packit 78deda
    /* Print the comment. Print 'Comment:' for each new line of comment. */
Packit 78deda
    if (imageInfoP->Comments[0]) {
Packit 78deda
        unsigned int a;
Packit 78deda
Packit 78deda
        fprintf(fileP, "Comment      : ");
Packit 78deda
Packit 78deda
        for (a = 0; a < MAX_COMMENT && imageInfoP->Comments[a]; ++a) {
Packit 78deda
            char const c = imageInfoP->Comments[a];
Packit 78deda
            if (c == '\n'){
Packit 78deda
                /* Do not start a new line if the string ends with a cr */
Packit 78deda
                if (imageInfoP->Comments[a+1] != '\0')
Packit 78deda
                    fprintf(fileP, "\nComment      : ");
Packit 78deda
                else
Packit 78deda
                    fprintf(fileP, "\n");
Packit 78deda
            } else
Packit 78deda
                putc(c, fileP);
Packit 78deda
        }
Packit 78deda
        fprintf(fileP, "\n");
Packit 78deda
    }
Packit 78deda
Packit 78deda
    fprintf(fileP, "\n");
Packit 78deda
}
Packit 78deda
Packit 78deda