degal/lib/EXIF.py
changeset 54 cc007b6ab972
child 98 d7d98c4479ab
equal deleted inserted replaced
53:14d73f544764 54:cc007b6ab972
       
     1 #!/usr/bin/env python
       
     2 # -*- coding: utf-8 -*-
       
     3 #
       
     4 # Library to extract EXIF information from digital camera image files
       
     5 # http://sourceforge.net/projects/exif-py/
       
     6 #
       
     7 # VERSION 1.1.0
       
     8 #
       
     9 # To use this library call with:
       
    10 #    f = open(path_name, 'rb')
       
    11 #    tags = EXIF.process_file(f)
       
    12 #
       
    13 # To ignore MakerNote tags, pass the -q or --quick
       
    14 # command line arguments, or as
       
    15 #    tags = EXIF.process_file(f, details=False)
       
    16 #
       
    17 # To stop processing after a certain tag is retrieved,
       
    18 # pass the -t TAG or --stop-tag TAG argument, or as
       
    19 #    tags = EXIF.process_file(f, stop_tag='TAG')
       
    20 #
       
    21 # where TAG is a valid tag name, ex 'DateTimeOriginal'
       
    22 #
       
    23 # These 2 are useful when you are retrieving a large list of images
       
    24 #
       
    25 #
       
    26 # To return an error on invalid tags,
       
    27 # pass the -s or --strict argument, or as
       
    28 #    tags = EXIF.process_file(f, strict=True)
       
    29 #
       
    30 # Otherwise these tags will be ignored
       
    31 #
       
    32 # Returned tags will be a dictionary mapping names of EXIF tags to their
       
    33 # values in the file named by path_name.  You can process the tags
       
    34 # as you wish.  In particular, you can iterate through all the tags with:
       
    35 #     for tag in tags.keys():
       
    36 #         if tag not in ('JPEGThumbnail', 'TIFFThumbnail', 'Filename',
       
    37 #                        'EXIF MakerNote'):
       
    38 #             print "Key: %s, value %s" % (tag, tags[tag])
       
    39 # (This code uses the if statement to avoid printing out a few of the
       
    40 # tags that tend to be long or boring.)
       
    41 #
       
    42 # The tags dictionary will include keys for all of the usual EXIF
       
    43 # tags, and will also include keys for Makernotes used by some
       
    44 # cameras, for which we have a good specification.
       
    45 #
       
    46 # Note that the dictionary keys are the IFD name followed by the
       
    47 # tag name. For example:
       
    48 # 'EXIF DateTimeOriginal', 'Image Orientation', 'MakerNote FocusMode'
       
    49 #
       
    50 # Copyright (c) 2002-2007 Gene Cash All rights reserved
       
    51 # Copyright (c) 2007-2008 Ianaré Sévi All rights reserved
       
    52 #
       
    53 # Redistribution and use in source and binary forms, with or without
       
    54 # modification, are permitted provided that the following conditions
       
    55 # are met:
       
    56 #
       
    57 #  1. Redistributions of source code must retain the above copyright
       
    58 #     notice, this list of conditions and the following disclaimer.
       
    59 #
       
    60 #  2. Redistributions in binary form must reproduce the above
       
    61 #     copyright notice, this list of conditions and the following
       
    62 #     disclaimer in the documentation and/or other materials provided
       
    63 #     with the distribution.
       
    64 #
       
    65 #  3. Neither the name of the authors nor the names of its contributors
       
    66 #     may be used to endorse or promote products derived from this
       
    67 #     software without specific prior written permission.
       
    68 #
       
    69 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
       
    70 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
       
    71 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
       
    72 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
       
    73 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
       
    74 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
       
    75 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
       
    76 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
       
    77 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
       
    78 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
       
    79 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
       
    80 #
       
    81 #
       
    82 # ----- See 'changes.txt' file for all contributors and changes ----- #
       
    83 #
       
    84 
       
    85 
       
    86 # Don't throw an exception when given an out of range character.
       
    87 def make_string(seq):
       
    88     str = ''
       
    89     for c in seq:
       
    90         # Screen out non-printing characters
       
    91         if 32 <= c and c < 256:
       
    92             str += chr(c)
       
    93     # If no printing chars
       
    94     if not str:
       
    95         return seq
       
    96     return str
       
    97 
       
    98 # Special version to deal with the code in the first 8 bytes of a user comment.
       
    99 # First 8 bytes gives coding system e.g. ASCII vs. JIS vs Unicode
       
   100 def make_string_uc(seq):
       
   101     code = seq[0:8]
       
   102     seq = seq[8:]
       
   103     # Of course, this is only correct if ASCII, and the standard explicitly
       
   104     # allows JIS and Unicode.
       
   105     return make_string(seq)
       
   106 
       
   107 # field type descriptions as (length, abbreviation, full name) tuples
       
   108 FIELD_TYPES = (
       
   109     (0, 'X', 'Proprietary'), # no such type
       
   110     (1, 'B', 'Byte'),
       
   111     (1, 'A', 'ASCII'),
       
   112     (2, 'S', 'Short'),
       
   113     (4, 'L', 'Long'),
       
   114     (8, 'R', 'Ratio'),
       
   115     (1, 'SB', 'Signed Byte'),
       
   116     (1, 'U', 'Undefined'),
       
   117     (2, 'SS', 'Signed Short'),
       
   118     (4, 'SL', 'Signed Long'),
       
   119     (8, 'SR', 'Signed Ratio'),
       
   120     )
       
   121 
       
   122 # dictionary of main EXIF tag names
       
   123 # first element of tuple is tag name, optional second element is
       
   124 # another dictionary giving names to values
       
   125 EXIF_TAGS = {
       
   126     0x0100: ('ImageWidth', ),
       
   127     0x0101: ('ImageLength', ),
       
   128     0x0102: ('BitsPerSample', ),
       
   129     0x0103: ('Compression',
       
   130              {1: 'Uncompressed',
       
   131               2: 'CCITT 1D',
       
   132               3: 'T4/Group 3 Fax',
       
   133               4: 'T6/Group 4 Fax',
       
   134               5: 'LZW',
       
   135               6: 'JPEG (old-style)',
       
   136               7: 'JPEG',
       
   137               8: 'Adobe Deflate',
       
   138               9: 'JBIG B&W',
       
   139               10: 'JBIG Color',
       
   140               32766: 'Next',
       
   141               32769: 'Epson ERF Compressed',
       
   142               32771: 'CCIRLEW',
       
   143               32773: 'PackBits',
       
   144               32809: 'Thunderscan',
       
   145               32895: 'IT8CTPAD',
       
   146               32896: 'IT8LW',
       
   147               32897: 'IT8MP',
       
   148               32898: 'IT8BL',
       
   149               32908: 'PixarFilm',
       
   150               32909: 'PixarLog',
       
   151               32946: 'Deflate',
       
   152               32947: 'DCS',
       
   153               34661: 'JBIG',
       
   154               34676: 'SGILog',
       
   155               34677: 'SGILog24',
       
   156               34712: 'JPEG 2000',
       
   157               34713: 'Nikon NEF Compressed',
       
   158               65000: 'Kodak DCR Compressed',
       
   159               65535: 'Pentax PEF Compressed'}),
       
   160     0x0106: ('PhotometricInterpretation', ),
       
   161     0x0107: ('Thresholding', ),
       
   162     0x010A: ('FillOrder', ),
       
   163     0x010D: ('DocumentName', ),
       
   164     0x010E: ('ImageDescription', ),
       
   165     0x010F: ('Make', ),
       
   166     0x0110: ('Model', ),
       
   167     0x0111: ('StripOffsets', ),
       
   168     0x0112: ('Orientation',
       
   169              {1: 'Horizontal (normal)',
       
   170               2: 'Mirrored horizontal',
       
   171               3: 'Rotated 180',
       
   172               4: 'Mirrored vertical',
       
   173               5: 'Mirrored horizontal then rotated 90 CCW',
       
   174               6: 'Rotated 90 CW',
       
   175               7: 'Mirrored horizontal then rotated 90 CW',
       
   176               8: 'Rotated 90 CCW'}),
       
   177     0x0115: ('SamplesPerPixel', ),
       
   178     0x0116: ('RowsPerStrip', ),
       
   179     0x0117: ('StripByteCounts', ),
       
   180     0x011A: ('XResolution', ),
       
   181     0x011B: ('YResolution', ),
       
   182     0x011C: ('PlanarConfiguration', ),
       
   183     0x011D: ('PageName', make_string),
       
   184     0x0128: ('ResolutionUnit',
       
   185              {1: 'Not Absolute',
       
   186               2: 'Pixels/Inch',
       
   187               3: 'Pixels/Centimeter'}),
       
   188     0x012D: ('TransferFunction', ),
       
   189     0x0131: ('Software', ),
       
   190     0x0132: ('DateTime', ),
       
   191     0x013B: ('Artist', ),
       
   192     0x013E: ('WhitePoint', ),
       
   193     0x013F: ('PrimaryChromaticities', ),
       
   194     0x0156: ('TransferRange', ),
       
   195     0x0200: ('JPEGProc', ),
       
   196     0x0201: ('JPEGInterchangeFormat', ),
       
   197     0x0202: ('JPEGInterchangeFormatLength', ),
       
   198     0x0211: ('YCbCrCoefficients', ),
       
   199     0x0212: ('YCbCrSubSampling', ),
       
   200     0x0213: ('YCbCrPositioning',
       
   201              {1: 'Centered',
       
   202               2: 'Co-sited'}),
       
   203     0x0214: ('ReferenceBlackWhite', ),
       
   204     
       
   205     0x4746: ('Rating', ),
       
   206     
       
   207     0x828D: ('CFARepeatPatternDim', ),
       
   208     0x828E: ('CFAPattern', ),
       
   209     0x828F: ('BatteryLevel', ),
       
   210     0x8298: ('Copyright', ),
       
   211     0x829A: ('ExposureTime', ),
       
   212     0x829D: ('FNumber', ),
       
   213     0x83BB: ('IPTC/NAA', ),
       
   214     0x8769: ('ExifOffset', ),
       
   215     0x8773: ('InterColorProfile', ),
       
   216     0x8822: ('ExposureProgram',
       
   217              {0: 'Unidentified',
       
   218               1: 'Manual',
       
   219               2: 'Program Normal',
       
   220               3: 'Aperture Priority',
       
   221               4: 'Shutter Priority',
       
   222               5: 'Program Creative',
       
   223               6: 'Program Action',
       
   224               7: 'Portrait Mode',
       
   225               8: 'Landscape Mode'}),
       
   226     0x8824: ('SpectralSensitivity', ),
       
   227     0x8825: ('GPSInfo', ),
       
   228     0x8827: ('ISOSpeedRatings', ),
       
   229     0x8828: ('OECF', ),
       
   230     0x9000: ('ExifVersion', make_string),
       
   231     0x9003: ('DateTimeOriginal', ),
       
   232     0x9004: ('DateTimeDigitized', ),
       
   233     0x9101: ('ComponentsConfiguration',
       
   234              {0: '',
       
   235               1: 'Y',
       
   236               2: 'Cb',
       
   237               3: 'Cr',
       
   238               4: 'Red',
       
   239               5: 'Green',
       
   240               6: 'Blue'}),
       
   241     0x9102: ('CompressedBitsPerPixel', ),
       
   242     0x9201: ('ShutterSpeedValue', ),
       
   243     0x9202: ('ApertureValue', ),
       
   244     0x9203: ('BrightnessValue', ),
       
   245     0x9204: ('ExposureBiasValue', ),
       
   246     0x9205: ('MaxApertureValue', ),
       
   247     0x9206: ('SubjectDistance', ),
       
   248     0x9207: ('MeteringMode',
       
   249              {0: 'Unidentified',
       
   250               1: 'Average',
       
   251               2: 'CenterWeightedAverage',
       
   252               3: 'Spot',
       
   253               4: 'MultiSpot',
       
   254               5: 'Pattern'}),
       
   255     0x9208: ('LightSource',
       
   256              {0: 'Unknown',
       
   257               1: 'Daylight',
       
   258               2: 'Fluorescent',
       
   259               3: 'Tungsten',
       
   260               9: 'Fine Weather',
       
   261               10: 'Flash',
       
   262               11: 'Shade',
       
   263               12: 'Daylight Fluorescent',
       
   264               13: 'Day White Fluorescent',
       
   265               14: 'Cool White Fluorescent',
       
   266               15: 'White Fluorescent',
       
   267               17: 'Standard Light A',
       
   268               18: 'Standard Light B',
       
   269               19: 'Standard Light C',
       
   270               20: 'D55',
       
   271               21: 'D65',
       
   272               22: 'D75',
       
   273               255: 'Other'}),
       
   274     0x9209: ('Flash',
       
   275              {0: 'No',
       
   276               1: 'Fired',
       
   277               5: 'Fired (?)', # no return sensed
       
   278               7: 'Fired (!)', # return sensed
       
   279               9: 'Fill Fired',
       
   280               13: 'Fill Fired (?)',
       
   281               15: 'Fill Fired (!)',
       
   282               16: 'Off',
       
   283               24: 'Auto Off',
       
   284               25: 'Auto Fired',
       
   285               29: 'Auto Fired (?)',
       
   286               31: 'Auto Fired (!)',
       
   287               32: 'Not Available'}),
       
   288     0x920A: ('FocalLength', ),
       
   289     0x9214: ('SubjectArea', ),
       
   290     0x927C: ('MakerNote', ),
       
   291     0x9286: ('UserComment', make_string_uc),
       
   292     0x9290: ('SubSecTime', ),
       
   293     0x9291: ('SubSecTimeOriginal', ),
       
   294     0x9292: ('SubSecTimeDigitized', ),
       
   295     
       
   296     # used by Windows Explorer
       
   297     0x9C9B: ('XPTitle', ),
       
   298     0x9C9C: ('XPComment', ),
       
   299     0x9C9D: ('XPAuthor', ), #(ignored by Windows Explorer if Artist exists)
       
   300     0x9C9E: ('XPKeywords', ),
       
   301     0x9C9F: ('XPSubject', ),
       
   302 
       
   303     0xA000: ('FlashPixVersion', make_string),
       
   304     0xA001: ('ColorSpace',
       
   305              {1: 'sRGB',
       
   306               2: 'Adobe RGB',
       
   307               65535: 'Uncalibrated'}),
       
   308     0xA002: ('ExifImageWidth', ),
       
   309     0xA003: ('ExifImageLength', ),
       
   310     0xA005: ('InteroperabilityOffset', ),
       
   311     0xA20B: ('FlashEnergy', ),               # 0x920B in TIFF/EP
       
   312     0xA20C: ('SpatialFrequencyResponse', ),  # 0x920C
       
   313     0xA20E: ('FocalPlaneXResolution', ),     # 0x920E
       
   314     0xA20F: ('FocalPlaneYResolution', ),     # 0x920F
       
   315     0xA210: ('FocalPlaneResolutionUnit', ),  # 0x9210
       
   316     0xA214: ('SubjectLocation', ),           # 0x9214
       
   317     0xA215: ('ExposureIndex', ),             # 0x9215
       
   318     0xA217: ('SensingMethod',                # 0x9217
       
   319              {1: 'Not defined',
       
   320               2: 'One-chip color area',
       
   321               3: 'Two-chip color area',
       
   322               4: 'Three-chip color area',
       
   323               5: 'Color sequential area',
       
   324               7: 'Trilinear',
       
   325               8: 'Color sequential linear'}),             
       
   326     0xA300: ('FileSource',
       
   327              {1: 'Film Scanner',
       
   328               2: 'Reflection Print Scanner',
       
   329               3: 'Digital Camera'}),
       
   330     0xA301: ('SceneType',
       
   331              {1: 'Directly Photographed'}),
       
   332     0xA302: ('CVAPattern', ),
       
   333     0xA401: ('CustomRendered',
       
   334              {0: 'Normal',
       
   335               1: 'Custom'}),
       
   336     0xA402: ('ExposureMode',
       
   337              {0: 'Auto Exposure',
       
   338               1: 'Manual Exposure',
       
   339               2: 'Auto Bracket'}),
       
   340     0xA403: ('WhiteBalance',
       
   341              {0: 'Auto',
       
   342               1: 'Manual'}),
       
   343     0xA404: ('DigitalZoomRatio', ),
       
   344     0xA405: ('FocalLengthIn35mmFilm', ),
       
   345     0xA406: ('SceneCaptureType',
       
   346              {0: 'Standard',
       
   347               1: 'Landscape',
       
   348               2: 'Portrait',
       
   349               3: 'Night)'}),
       
   350     0xA407: ('GainControl',
       
   351              {0: 'None',
       
   352               1: 'Low gain up',
       
   353               2: 'High gain up',
       
   354               3: 'Low gain down',
       
   355               4: 'High gain down'}),
       
   356     0xA408: ('Contrast',
       
   357              {0: 'Normal',
       
   358               1: 'Soft',
       
   359               2: 'Hard'}),
       
   360     0xA409: ('Saturation',
       
   361              {0: 'Normal',
       
   362               1: 'Soft',
       
   363               2: 'Hard'}),
       
   364     0xA40A: ('Sharpness',
       
   365              {0: 'Normal',
       
   366               1: 'Soft',
       
   367               2: 'Hard'}),
       
   368     0xA40B: ('DeviceSettingDescription', ),
       
   369     0xA40C: ('SubjectDistanceRange', ),
       
   370     0xA500: ('Gamma', ),
       
   371     0xC4A5: ('PrintIM', ),
       
   372     0xEA1C:	('Padding', ),
       
   373     }
       
   374 
       
   375 # interoperability tags
       
   376 INTR_TAGS = {
       
   377     0x0001: ('InteroperabilityIndex', ),
       
   378     0x0002: ('InteroperabilityVersion', ),
       
   379     0x1000: ('RelatedImageFileFormat', ),
       
   380     0x1001: ('RelatedImageWidth', ),
       
   381     0x1002: ('RelatedImageLength', ),
       
   382     }
       
   383 
       
   384 # GPS tags (not used yet, haven't seen camera with GPS)
       
   385 GPS_TAGS = {
       
   386     0x0000: ('GPSVersionID', ),
       
   387     0x0001: ('GPSLatitudeRef', ),
       
   388     0x0002: ('GPSLatitude', ),
       
   389     0x0003: ('GPSLongitudeRef', ),
       
   390     0x0004: ('GPSLongitude', ),
       
   391     0x0005: ('GPSAltitudeRef', ),
       
   392     0x0006: ('GPSAltitude', ),
       
   393     0x0007: ('GPSTimeStamp', ),
       
   394     0x0008: ('GPSSatellites', ),
       
   395     0x0009: ('GPSStatus', ),
       
   396     0x000A: ('GPSMeasureMode', ),
       
   397     0x000B: ('GPSDOP', ),
       
   398     0x000C: ('GPSSpeedRef', ),
       
   399     0x000D: ('GPSSpeed', ),
       
   400     0x000E: ('GPSTrackRef', ),
       
   401     0x000F: ('GPSTrack', ),
       
   402     0x0010: ('GPSImgDirectionRef', ),
       
   403     0x0011: ('GPSImgDirection', ),
       
   404     0x0012: ('GPSMapDatum', ),
       
   405     0x0013: ('GPSDestLatitudeRef', ),
       
   406     0x0014: ('GPSDestLatitude', ),
       
   407     0x0015: ('GPSDestLongitudeRef', ),
       
   408     0x0016: ('GPSDestLongitude', ),
       
   409     0x0017: ('GPSDestBearingRef', ),
       
   410     0x0018: ('GPSDestBearing', ),
       
   411     0x0019: ('GPSDestDistanceRef', ),
       
   412     0x001A: ('GPSDestDistance', ),
       
   413     0x001D: ('GPSDate', ),
       
   414     }
       
   415 
       
   416 # Ignore these tags when quick processing
       
   417 # 0x927C is MakerNote Tags
       
   418 # 0x9286 is user comment
       
   419 IGNORE_TAGS=(0x9286, 0x927C)
       
   420 
       
   421 # http://tomtia.plala.jp/DigitalCamera/MakerNote/index.asp
       
   422 def nikon_ev_bias(seq):
       
   423     # First digit seems to be in steps of 1/6 EV.
       
   424     # Does the third value mean the step size?  It is usually 6,
       
   425     # but it is 12 for the ExposureDifference.
       
   426     #
       
   427     # Check for an error condition that could cause a crash.
       
   428     # This only happens if something has gone really wrong in
       
   429     # reading the Nikon MakerNote.
       
   430     if len( seq ) < 4 : return ""
       
   431     #
       
   432     if seq == [252, 1, 6, 0]:
       
   433         return "-2/3 EV"
       
   434     if seq == [253, 1, 6, 0]:
       
   435         return "-1/2 EV"
       
   436     if seq == [254, 1, 6, 0]:
       
   437         return "-1/3 EV"
       
   438     if seq == [0, 1, 6, 0]:
       
   439         return "0 EV"
       
   440     if seq == [2, 1, 6, 0]:
       
   441         return "+1/3 EV"
       
   442     if seq == [3, 1, 6, 0]:
       
   443         return "+1/2 EV"
       
   444     if seq == [4, 1, 6, 0]:
       
   445         return "+2/3 EV"
       
   446     # Handle combinations not in the table.
       
   447     a = seq[0]
       
   448     # Causes headaches for the +/- logic, so special case it.
       
   449     if a == 0:
       
   450         return "0 EV"
       
   451     if a > 127:
       
   452         a = 256 - a
       
   453         ret_str = "-"
       
   454     else:
       
   455         ret_str = "+"
       
   456     b = seq[2]	# Assume third value means the step size
       
   457     whole = a / b
       
   458     a = a % b
       
   459     if whole != 0:
       
   460         ret_str = ret_str + str(whole) + " "
       
   461     if a == 0:
       
   462         ret_str = ret_str + "EV"
       
   463     else:
       
   464         r = Ratio(a, b)
       
   465         ret_str = ret_str + r.__repr__() + " EV"
       
   466     return ret_str
       
   467 
       
   468 # Nikon E99x MakerNote Tags
       
   469 MAKERNOTE_NIKON_NEWER_TAGS={
       
   470     0x0001: ('MakernoteVersion', make_string),	# Sometimes binary
       
   471     0x0002: ('ISOSetting', make_string),
       
   472     0x0003: ('ColorMode', ),
       
   473     0x0004: ('Quality', ),
       
   474     0x0005: ('Whitebalance', ),
       
   475     0x0006: ('ImageSharpening', ),
       
   476     0x0007: ('FocusMode', ),
       
   477     0x0008: ('FlashSetting', ),
       
   478     0x0009: ('AutoFlashMode', ),
       
   479     0x000B: ('WhiteBalanceBias', ),
       
   480     0x000C: ('WhiteBalanceRBCoeff', ),
       
   481     0x000D: ('ProgramShift', nikon_ev_bias),
       
   482     # Nearly the same as the other EV vals, but step size is 1/12 EV (?)
       
   483     0x000E: ('ExposureDifference', nikon_ev_bias),
       
   484     0x000F: ('ISOSelection', ),
       
   485     0x0011: ('NikonPreview', ),
       
   486     0x0012: ('FlashCompensation', nikon_ev_bias),
       
   487     0x0013: ('ISOSpeedRequested', ),
       
   488     0x0016: ('PhotoCornerCoordinates', ),
       
   489     # 0x0017: Unknown, but most likely an EV value
       
   490     0x0018: ('FlashBracketCompensationApplied', nikon_ev_bias),
       
   491     0x0019: ('AEBracketCompensationApplied', ),
       
   492     0x001A: ('ImageProcessing', ),
       
   493     0x001B: ('CropHiSpeed', ),
       
   494     0x001D: ('SerialNumber', ),	# Conflict with 0x00A0 ?
       
   495     0x001E: ('ColorSpace', ),
       
   496     0x001F: ('VRInfo', ),
       
   497     0x0020: ('ImageAuthentication', ),
       
   498     0x0022: ('ActiveDLighting', ),
       
   499     0x0023: ('PictureControl', ),
       
   500     0x0024: ('WorldTime', ),
       
   501     0x0025: ('ISOInfo', ),
       
   502     0x0080: ('ImageAdjustment', ),
       
   503     0x0081: ('ToneCompensation', ),
       
   504     0x0082: ('AuxiliaryLens', ),
       
   505     0x0083: ('LensType', ),
       
   506     0x0084: ('LensMinMaxFocalMaxAperture', ),
       
   507     0x0085: ('ManualFocusDistance', ),
       
   508     0x0086: ('DigitalZoomFactor', ),
       
   509     0x0087: ('FlashMode',
       
   510              {0x00: 'Did Not Fire',
       
   511               0x01: 'Fired, Manual',
       
   512               0x07: 'Fired, External',
       
   513               0x08: 'Fired, Commander Mode ',
       
   514               0x09: 'Fired, TTL Mode'}),
       
   515     0x0088: ('AFFocusPosition',
       
   516              {0x0000: 'Center',
       
   517               0x0100: 'Top',
       
   518               0x0200: 'Bottom',
       
   519               0x0300: 'Left',
       
   520               0x0400: 'Right'}),
       
   521     0x0089: ('BracketingMode',
       
   522              {0x00: 'Single frame, no bracketing',
       
   523               0x01: 'Continuous, no bracketing',
       
   524               0x02: 'Timer, no bracketing',
       
   525               0x10: 'Single frame, exposure bracketing',
       
   526               0x11: 'Continuous, exposure bracketing',
       
   527               0x12: 'Timer, exposure bracketing',
       
   528               0x40: 'Single frame, white balance bracketing',
       
   529               0x41: 'Continuous, white balance bracketing',
       
   530               0x42: 'Timer, white balance bracketing'}),
       
   531     0x008A: ('AutoBracketRelease', ),
       
   532     0x008B: ('LensFStops', ),
       
   533     0x008C: ('NEFCurve1', ),	# ExifTool calls this 'ContrastCurve'
       
   534     0x008D: ('ColorMode', ),
       
   535     0x008F: ('SceneMode', ),
       
   536     0x0090: ('LightingType', ),
       
   537     0x0091: ('ShotInfo', ),	# First 4 bytes are a version number in ASCII
       
   538     0x0092: ('HueAdjustment', ),
       
   539     # ExifTool calls this 'NEFCompression', should be 1-4
       
   540     0x0093: ('Compression', ),
       
   541     0x0094: ('Saturation',
       
   542              {-3: 'B&W',
       
   543               -2: '-2',
       
   544               -1: '-1',
       
   545               0: '0',
       
   546               1: '1',
       
   547               2: '2'}),
       
   548     0x0095: ('NoiseReduction', ),
       
   549     0x0096: ('NEFCurve2', ),	# ExifTool calls this 'LinearizationTable'
       
   550     0x0097: ('ColorBalance', ),	# First 4 bytes are a version number in ASCII
       
   551     0x0098: ('LensData', ),	# First 4 bytes are a version number in ASCII
       
   552     0x0099: ('RawImageCenter', ),
       
   553     0x009A: ('SensorPixelSize', ),
       
   554     0x009C: ('Scene Assist', ),
       
   555     0x009E: ('RetouchHistory', ),
       
   556     0x00A0: ('SerialNumber', ),
       
   557     0x00A2: ('ImageDataSize', ),
       
   558     # 00A3: unknown - a single byte 0
       
   559     # 00A4: In NEF, looks like a 4 byte ASCII version number ('0200')
       
   560     0x00A5: ('ImageCount', ),
       
   561     0x00A6: ('DeletedImageCount', ),
       
   562     0x00A7: ('TotalShutterReleases', ),
       
   563     # First 4 bytes are a version number in ASCII, with version specific
       
   564     # info to follow.  Its hard to treat it as a string due to embedded nulls.
       
   565     0x00A8: ('FlashInfo', ),
       
   566     0x00A9: ('ImageOptimization', ),
       
   567     0x00AA: ('Saturation', ),
       
   568     0x00AB: ('DigitalVariProgram', ),
       
   569     0x00AC: ('ImageStabilization', ),
       
   570     0x00AD: ('Responsive AF', ),	# 'AFResponse'
       
   571     0x00B0: ('MultiExposure', ),
       
   572     0x00B1: ('HighISONoiseReduction', ),
       
   573     0x00B7: ('AFInfo', ),
       
   574     0x00B8: ('FileInfo', ),
       
   575     # 00B9: unknown
       
   576     0x0100: ('DigitalICE', ),
       
   577     0x0103: ('PreviewCompression',
       
   578              {1: 'Uncompressed',
       
   579               2: 'CCITT 1D',
       
   580               3: 'T4/Group 3 Fax',
       
   581               4: 'T6/Group 4 Fax',
       
   582               5: 'LZW',
       
   583               6: 'JPEG (old-style)',
       
   584               7: 'JPEG',
       
   585               8: 'Adobe Deflate',
       
   586               9: 'JBIG B&W',
       
   587               10: 'JBIG Color',
       
   588               32766: 'Next',
       
   589               32769: 'Epson ERF Compressed',
       
   590               32771: 'CCIRLEW',
       
   591               32773: 'PackBits',
       
   592               32809: 'Thunderscan',
       
   593               32895: 'IT8CTPAD',
       
   594               32896: 'IT8LW',
       
   595               32897: 'IT8MP',
       
   596               32898: 'IT8BL',
       
   597               32908: 'PixarFilm',
       
   598               32909: 'PixarLog',
       
   599               32946: 'Deflate',
       
   600               32947: 'DCS',
       
   601               34661: 'JBIG',
       
   602               34676: 'SGILog',
       
   603               34677: 'SGILog24',
       
   604               34712: 'JPEG 2000',
       
   605               34713: 'Nikon NEF Compressed',
       
   606               65000: 'Kodak DCR Compressed',
       
   607               65535: 'Pentax PEF Compressed',}),
       
   608     0x0201: ('PreviewImageStart', ),
       
   609     0x0202: ('PreviewImageLength', ),
       
   610     0x0213: ('PreviewYCbCrPositioning',
       
   611              {1: 'Centered',
       
   612               2: 'Co-sited'}), 
       
   613     0x0010: ('DataDump', ),
       
   614     }
       
   615 
       
   616 MAKERNOTE_NIKON_OLDER_TAGS = {
       
   617     0x0003: ('Quality',
       
   618              {1: 'VGA Basic',
       
   619               2: 'VGA Normal',
       
   620               3: 'VGA Fine',
       
   621               4: 'SXGA Basic',
       
   622               5: 'SXGA Normal',
       
   623               6: 'SXGA Fine'}),
       
   624     0x0004: ('ColorMode',
       
   625              {1: 'Color',
       
   626               2: 'Monochrome'}),
       
   627     0x0005: ('ImageAdjustment',
       
   628              {0: 'Normal',
       
   629               1: 'Bright+',
       
   630               2: 'Bright-',
       
   631               3: 'Contrast+',
       
   632               4: 'Contrast-'}),
       
   633     0x0006: ('CCDSpeed',
       
   634              {0: 'ISO 80',
       
   635               2: 'ISO 160',
       
   636               4: 'ISO 320',
       
   637               5: 'ISO 100'}),
       
   638     0x0007: ('WhiteBalance',
       
   639              {0: 'Auto',
       
   640               1: 'Preset',
       
   641               2: 'Daylight',
       
   642               3: 'Incandescent',
       
   643               4: 'Fluorescent',
       
   644               5: 'Cloudy',
       
   645               6: 'Speed Light'}),
       
   646     }
       
   647 
       
   648 # decode Olympus SpecialMode tag in MakerNote
       
   649 def olympus_special_mode(v):
       
   650     a={
       
   651         0: 'Normal',
       
   652         1: 'Unknown',
       
   653         2: 'Fast',
       
   654         3: 'Panorama'}
       
   655     b={
       
   656         0: 'Non-panoramic',
       
   657         1: 'Left to right',
       
   658         2: 'Right to left',
       
   659         3: 'Bottom to top',
       
   660         4: 'Top to bottom'}
       
   661     if v[0] not in a or v[2] not in b:
       
   662         return v
       
   663     return '%s - sequence %d - %s' % (a[v[0]], v[1], b[v[2]])
       
   664 
       
   665 MAKERNOTE_OLYMPUS_TAGS={
       
   666     # ah HAH! those sneeeeeaky bastids! this is how they get past the fact
       
   667     # that a JPEG thumbnail is not allowed in an uncompressed TIFF file
       
   668     0x0100: ('JPEGThumbnail', ),
       
   669     0x0200: ('SpecialMode', olympus_special_mode),
       
   670     0x0201: ('JPEGQual',
       
   671              {1: 'SQ',
       
   672               2: 'HQ',
       
   673               3: 'SHQ'}),
       
   674     0x0202: ('Macro',
       
   675              {0: 'Normal',
       
   676              1: 'Macro',
       
   677              2: 'SuperMacro'}),
       
   678     0x0203: ('BWMode',
       
   679              {0: 'Off',
       
   680              1: 'On'}),
       
   681     0x0204: ('DigitalZoom', ),
       
   682     0x0205: ('FocalPlaneDiagonal', ),
       
   683     0x0206: ('LensDistortionParams', ),
       
   684     0x0207: ('SoftwareRelease', ),
       
   685     0x0208: ('PictureInfo', ),
       
   686     0x0209: ('CameraID', make_string), # print as string
       
   687     0x0F00: ('DataDump', ),
       
   688     0x0300: ('PreCaptureFrames', ),
       
   689     0x0404: ('SerialNumber', ),
       
   690     0x1000: ('ShutterSpeedValue', ),
       
   691     0x1001: ('ISOValue', ),
       
   692     0x1002: ('ApertureValue', ),
       
   693     0x1003: ('BrightnessValue', ),
       
   694     0x1004: ('FlashMode', ),
       
   695     0x1004: ('FlashMode',
       
   696        {2: 'On',
       
   697         3: 'Off'}),
       
   698     0x1005: ('FlashDevice',
       
   699        {0: 'None',
       
   700         1: 'Internal',
       
   701         4: 'External',
       
   702         5: 'Internal + External'}),
       
   703     0x1006: ('ExposureCompensation', ),
       
   704     0x1007: ('SensorTemperature', ),
       
   705     0x1008: ('LensTemperature', ),
       
   706     0x100b: ('FocusMode',
       
   707        {0: 'Auto',
       
   708         1: 'Manual'}),
       
   709     0x1017: ('RedBalance', ),
       
   710     0x1018: ('BlueBalance', ),
       
   711     0x101a: ('SerialNumber', ),
       
   712     0x1023: ('FlashExposureComp', ),
       
   713     0x1026: ('ExternalFlashBounce',
       
   714        {0: 'No',
       
   715         1: 'Yes'}),
       
   716     0x1027: ('ExternalFlashZoom', ),
       
   717     0x1028: ('ExternalFlashMode', ),
       
   718     0x1029: ('Contrast 	int16u',
       
   719        {0: 'High',
       
   720         1: 'Normal',
       
   721         2: 'Low'}),
       
   722     0x102a: ('SharpnessFactor', ),
       
   723     0x102b: ('ColorControl', ),
       
   724     0x102c: ('ValidBits', ),
       
   725     0x102d: ('CoringFilter', ),
       
   726     0x102e: ('OlympusImageWidth', ),
       
   727     0x102f: ('OlympusImageHeight', ),
       
   728     0x1034: ('CompressionRatio', ),
       
   729     0x1035: ('PreviewImageValid',
       
   730        {0: 'No',
       
   731         1: 'Yes'}),
       
   732     0x1036: ('PreviewImageStart', ),
       
   733     0x1037: ('PreviewImageLength', ),
       
   734     0x1039: ('CCDScanMode',
       
   735        {0: 'Interlaced',
       
   736         1: 'Progressive'}),
       
   737     0x103a: ('NoiseReduction',
       
   738        {0: 'Off',
       
   739         1: 'On'}),
       
   740     0x103b: ('InfinityLensStep', ),
       
   741     0x103c: ('NearLensStep', ),
       
   742 
       
   743     # TODO - these need extra definitions
       
   744     # http://search.cpan.org/src/EXIFTOOL/Image-ExifTool-6.90/html/TagNames/Olympus.html
       
   745     0x2010: ('Equipment', ),
       
   746     0x2020: ('CameraSettings', ),
       
   747     0x2030: ('RawDevelopment', ),
       
   748     0x2040: ('ImageProcessing', ),
       
   749     0x2050: ('FocusInfo', ),
       
   750     0x3000: ('RawInfo ', ),
       
   751     }
       
   752 
       
   753 # 0x2020 CameraSettings
       
   754 MAKERNOTE_OLYMPUS_TAG_0x2020={
       
   755     0x0100: ('PreviewImageValid',
       
   756              {0: 'No',
       
   757               1: 'Yes'}),
       
   758     0x0101: ('PreviewImageStart', ),
       
   759     0x0102: ('PreviewImageLength', ),
       
   760     0x0200: ('ExposureMode',
       
   761              {1: 'Manual',
       
   762               2: 'Program',
       
   763               3: 'Aperture-priority AE',
       
   764               4: 'Shutter speed priority AE',
       
   765               5: 'Program-shift'}),
       
   766     0x0201: ('AELock',
       
   767              {0: 'Off',
       
   768               1: 'On'}),
       
   769     0x0202: ('MeteringMode',
       
   770              {2: 'Center Weighted',
       
   771               3: 'Spot',
       
   772               5: 'ESP',
       
   773               261: 'Pattern+AF',
       
   774               515: 'Spot+Highlight control',
       
   775               1027: 'Spot+Shadow control'}),
       
   776     0x0300: ('MacroMode',
       
   777              {0: 'Off',
       
   778               1: 'On'}),
       
   779     0x0301: ('FocusMode',
       
   780              {0: 'Single AF',
       
   781               1: 'Sequential shooting AF',
       
   782               2: 'Continuous AF',
       
   783               3: 'Multi AF',
       
   784               10: 'MF'}),
       
   785     0x0302: ('FocusProcess',
       
   786              {0: 'AF Not Used',
       
   787               1: 'AF Used'}),
       
   788     0x0303: ('AFSearch',
       
   789              {0: 'Not Ready',
       
   790               1: 'Ready'}),
       
   791     0x0304: ('AFAreas', ),
       
   792     0x0401: ('FlashExposureCompensation', ),
       
   793     0x0500: ('WhiteBalance2',
       
   794              {0: 'Auto',
       
   795              16: '7500K (Fine Weather with Shade)',
       
   796              17: '6000K (Cloudy)',
       
   797              18: '5300K (Fine Weather)',
       
   798              20: '3000K (Tungsten light)',
       
   799              21: '3600K (Tungsten light-like)',
       
   800              33: '6600K (Daylight fluorescent)',
       
   801              34: '4500K (Neutral white fluorescent)',
       
   802              35: '4000K (Cool white fluorescent)',
       
   803              48: '3600K (Tungsten light-like)',
       
   804              256: 'Custom WB 1',
       
   805              257: 'Custom WB 2',
       
   806              258: 'Custom WB 3',
       
   807              259: 'Custom WB 4',
       
   808              512: 'Custom WB 5400K',
       
   809              513: 'Custom WB 2900K',
       
   810              514: 'Custom WB 8000K', }),
       
   811     0x0501: ('WhiteBalanceTemperature', ),
       
   812     0x0502: ('WhiteBalanceBracket', ),
       
   813     0x0503: ('CustomSaturation', ), # (3 numbers: 1. CS Value, 2. Min, 3. Max)
       
   814     0x0504: ('ModifiedSaturation',
       
   815              {0: 'Off',
       
   816               1: 'CM1 (Red Enhance)',
       
   817               2: 'CM2 (Green Enhance)',
       
   818               3: 'CM3 (Blue Enhance)',
       
   819               4: 'CM4 (Skin Tones)'}),
       
   820     0x0505: ('ContrastSetting', ), # (3 numbers: 1. Contrast, 2. Min, 3. Max)
       
   821     0x0506: ('SharpnessSetting', ), # (3 numbers: 1. Sharpness, 2. Min, 3. Max)
       
   822     0x0507: ('ColorSpace',
       
   823              {0: 'sRGB',
       
   824               1: 'Adobe RGB',
       
   825               2: 'Pro Photo RGB'}),
       
   826     0x0509: ('SceneMode',
       
   827              {0: 'Standard',
       
   828               6: 'Auto',
       
   829               7: 'Sport',
       
   830               8: 'Portrait',
       
   831               9: 'Landscape+Portrait',
       
   832              10: 'Landscape',
       
   833              11: 'Night scene',
       
   834              13: 'Panorama',
       
   835              16: 'Landscape+Portrait',
       
   836              17: 'Night+Portrait',
       
   837              19: 'Fireworks',
       
   838              20: 'Sunset',
       
   839              22: 'Macro',
       
   840              25: 'Documents',
       
   841              26: 'Museum',
       
   842              28: 'Beach&Snow',
       
   843              30: 'Candle',
       
   844              35: 'Underwater Wide1',
       
   845              36: 'Underwater Macro',
       
   846              39: 'High Key',
       
   847              40: 'Digital Image Stabilization',
       
   848              44: 'Underwater Wide2',
       
   849              45: 'Low Key',
       
   850              46: 'Children',
       
   851              48: 'Nature Macro'}),
       
   852     0x050a: ('NoiseReduction',
       
   853              {0: 'Off',
       
   854               1: 'Noise Reduction',
       
   855               2: 'Noise Filter',
       
   856               3: 'Noise Reduction + Noise Filter',
       
   857               4: 'Noise Filter (ISO Boost)',
       
   858               5: 'Noise Reduction + Noise Filter (ISO Boost)'}),
       
   859     0x050b: ('DistortionCorrection',
       
   860              {0: 'Off',
       
   861               1: 'On'}),
       
   862     0x050c: ('ShadingCompensation',
       
   863              {0: 'Off',
       
   864               1: 'On'}),
       
   865     0x050d: ('CompressionFactor', ),
       
   866     0x050f: ('Gradation',
       
   867              {'-1 -1 1': 'Low Key',
       
   868               '0 -1 1': 'Normal',
       
   869               '1 -1 1': 'High Key'}),
       
   870     0x0520: ('PictureMode',
       
   871              {1: 'Vivid',
       
   872               2: 'Natural',
       
   873               3: 'Muted',
       
   874               256: 'Monotone',
       
   875               512: 'Sepia'}),
       
   876     0x0521: ('PictureModeSaturation', ),
       
   877     0x0522: ('PictureModeHue?', ),
       
   878     0x0523: ('PictureModeContrast', ),
       
   879     0x0524: ('PictureModeSharpness', ),
       
   880     0x0525: ('PictureModeBWFilter',
       
   881              {0: 'n/a',
       
   882               1: 'Neutral',
       
   883               2: 'Yellow',
       
   884               3: 'Orange',
       
   885               4: 'Red',
       
   886               5: 'Green'}),
       
   887     0x0526: ('PictureModeTone',
       
   888              {0: 'n/a',
       
   889               1: 'Neutral',
       
   890               2: 'Sepia',
       
   891               3: 'Blue',
       
   892               4: 'Purple',
       
   893               5: 'Green'}),
       
   894     0x0600: ('Sequence', ), # 2 or 3 numbers: 1. Mode, 2. Shot number, 3. Mode bits
       
   895     0x0601: ('PanoramaMode', ), # (2 numbers: 1. Mode, 2. Shot number)
       
   896     0x0603: ('ImageQuality2',
       
   897              {1: 'SQ',
       
   898               2: 'HQ',
       
   899               3: 'SHQ',
       
   900               4: 'RAW'}),
       
   901     0x0901: ('ManometerReading', ),
       
   902     }
       
   903 
       
   904 
       
   905 MAKERNOTE_CASIO_TAGS={
       
   906     0x0001: ('RecordingMode',
       
   907              {1: 'Single Shutter',
       
   908               2: 'Panorama',
       
   909               3: 'Night Scene',
       
   910               4: 'Portrait',
       
   911               5: 'Landscape'}),
       
   912     0x0002: ('Quality',
       
   913              {1: 'Economy',
       
   914               2: 'Normal',
       
   915               3: 'Fine'}),
       
   916     0x0003: ('FocusingMode',
       
   917              {2: 'Macro',
       
   918               3: 'Auto Focus',
       
   919               4: 'Manual Focus',
       
   920               5: 'Infinity'}),
       
   921     0x0004: ('FlashMode',
       
   922              {1: 'Auto',
       
   923               2: 'On',
       
   924               3: 'Off',
       
   925               4: 'Red Eye Reduction'}),
       
   926     0x0005: ('FlashIntensity',
       
   927              {11: 'Weak',
       
   928               13: 'Normal',
       
   929               15: 'Strong'}),
       
   930     0x0006: ('Object Distance', ),
       
   931     0x0007: ('WhiteBalance',
       
   932              {1: 'Auto',
       
   933               2: 'Tungsten',
       
   934               3: 'Daylight',
       
   935               4: 'Fluorescent',
       
   936               5: 'Shade',
       
   937               129: 'Manual'}),
       
   938     0x000B: ('Sharpness',
       
   939              {0: 'Normal',
       
   940               1: 'Soft',
       
   941               2: 'Hard'}),
       
   942     0x000C: ('Contrast',
       
   943              {0: 'Normal',
       
   944               1: 'Low',
       
   945               2: 'High'}),
       
   946     0x000D: ('Saturation',
       
   947              {0: 'Normal',
       
   948               1: 'Low',
       
   949               2: 'High'}),
       
   950     0x0014: ('CCDSpeed',
       
   951              {64: 'Normal',
       
   952               80: 'Normal',
       
   953               100: 'High',
       
   954               125: '+1.0',
       
   955               244: '+3.0',
       
   956               250: '+2.0'}),
       
   957     }
       
   958 
       
   959 MAKERNOTE_FUJIFILM_TAGS={
       
   960     0x0000: ('NoteVersion', make_string),
       
   961     0x1000: ('Quality', ),
       
   962     0x1001: ('Sharpness',
       
   963              {1: 'Soft',
       
   964               2: 'Soft',
       
   965               3: 'Normal',
       
   966               4: 'Hard',
       
   967               5: 'Hard'}),
       
   968     0x1002: ('WhiteBalance',
       
   969              {0: 'Auto',
       
   970               256: 'Daylight',
       
   971               512: 'Cloudy',
       
   972               768: 'DaylightColor-Fluorescent',
       
   973               769: 'DaywhiteColor-Fluorescent',
       
   974               770: 'White-Fluorescent',
       
   975               1024: 'Incandescent',
       
   976               3840: 'Custom'}),
       
   977     0x1003: ('Color',
       
   978              {0: 'Normal',
       
   979               256: 'High',
       
   980               512: 'Low'}),
       
   981     0x1004: ('Tone',
       
   982              {0: 'Normal',
       
   983               256: 'High',
       
   984               512: 'Low'}),
       
   985     0x1010: ('FlashMode',
       
   986              {0: 'Auto',
       
   987               1: 'On',
       
   988               2: 'Off',
       
   989               3: 'Red Eye Reduction'}),
       
   990     0x1011: ('FlashStrength', ),
       
   991     0x1020: ('Macro',
       
   992              {0: 'Off',
       
   993               1: 'On'}),
       
   994     0x1021: ('FocusMode',
       
   995              {0: 'Auto',
       
   996               1: 'Manual'}),
       
   997     0x1030: ('SlowSync',
       
   998              {0: 'Off',
       
   999               1: 'On'}),
       
  1000     0x1031: ('PictureMode',
       
  1001              {0: 'Auto',
       
  1002               1: 'Portrait',
       
  1003               2: 'Landscape',
       
  1004               4: 'Sports',
       
  1005               5: 'Night',
       
  1006               6: 'Program AE',
       
  1007               256: 'Aperture Priority AE',
       
  1008               512: 'Shutter Priority AE',
       
  1009               768: 'Manual Exposure'}),
       
  1010     0x1100: ('MotorOrBracket',
       
  1011              {0: 'Off',
       
  1012               1: 'On'}),
       
  1013     0x1300: ('BlurWarning',
       
  1014              {0: 'Off',
       
  1015               1: 'On'}),
       
  1016     0x1301: ('FocusWarning',
       
  1017              {0: 'Off',
       
  1018               1: 'On'}),
       
  1019     0x1302: ('AEWarning',
       
  1020              {0: 'Off',
       
  1021               1: 'On'}),
       
  1022     }
       
  1023 
       
  1024 MAKERNOTE_CANON_TAGS = {
       
  1025     0x0006: ('ImageType', ),
       
  1026     0x0007: ('FirmwareVersion', ),
       
  1027     0x0008: ('ImageNumber', ),
       
  1028     0x0009: ('OwnerName', ),
       
  1029     }
       
  1030 
       
  1031 # this is in element offset, name, optional value dictionary format
       
  1032 MAKERNOTE_CANON_TAG_0x001 = {
       
  1033     1: ('Macromode',
       
  1034         {1: 'Macro',
       
  1035          2: 'Normal'}),
       
  1036     2: ('SelfTimer', ),
       
  1037     3: ('Quality',
       
  1038         {2: 'Normal',
       
  1039          3: 'Fine',
       
  1040          5: 'Superfine'}),
       
  1041     4: ('FlashMode',
       
  1042         {0: 'Flash Not Fired',
       
  1043          1: 'Auto',
       
  1044          2: 'On',
       
  1045          3: 'Red-Eye Reduction',
       
  1046          4: 'Slow Synchro',
       
  1047          5: 'Auto + Red-Eye Reduction',
       
  1048          6: 'On + Red-Eye Reduction',
       
  1049          16: 'external flash'}),
       
  1050     5: ('ContinuousDriveMode',
       
  1051         {0: 'Single Or Timer',
       
  1052          1: 'Continuous'}),
       
  1053     7: ('FocusMode',
       
  1054         {0: 'One-Shot',
       
  1055          1: 'AI Servo',
       
  1056          2: 'AI Focus',
       
  1057          3: 'MF',
       
  1058          4: 'Single',
       
  1059          5: 'Continuous',
       
  1060          6: 'MF'}),
       
  1061     10: ('ImageSize',
       
  1062          {0: 'Large',
       
  1063           1: 'Medium',
       
  1064           2: 'Small'}),
       
  1065     11: ('EasyShootingMode',
       
  1066          {0: 'Full Auto',
       
  1067           1: 'Manual',
       
  1068           2: 'Landscape',
       
  1069           3: 'Fast Shutter',
       
  1070           4: 'Slow Shutter',
       
  1071           5: 'Night',
       
  1072           6: 'B&W',
       
  1073           7: 'Sepia',
       
  1074           8: 'Portrait',
       
  1075           9: 'Sports',
       
  1076           10: 'Macro/Close-Up',
       
  1077           11: 'Pan Focus'}),
       
  1078     12: ('DigitalZoom',
       
  1079          {0: 'None',
       
  1080           1: '2x',
       
  1081           2: '4x'}),
       
  1082     13: ('Contrast',
       
  1083          {0xFFFF: 'Low',
       
  1084           0: 'Normal',
       
  1085           1: 'High'}),
       
  1086     14: ('Saturation',
       
  1087          {0xFFFF: 'Low',
       
  1088           0: 'Normal',
       
  1089           1: 'High'}),
       
  1090     15: ('Sharpness',
       
  1091          {0xFFFF: 'Low',
       
  1092           0: 'Normal',
       
  1093           1: 'High'}),
       
  1094     16: ('ISO',
       
  1095          {0: 'See ISOSpeedRatings Tag',
       
  1096           15: 'Auto',
       
  1097           16: '50',
       
  1098           17: '100',
       
  1099           18: '200',
       
  1100           19: '400'}),
       
  1101     17: ('MeteringMode',
       
  1102          {3: 'Evaluative',
       
  1103           4: 'Partial',
       
  1104           5: 'Center-weighted'}),
       
  1105     18: ('FocusType',
       
  1106          {0: 'Manual',
       
  1107           1: 'Auto',
       
  1108           3: 'Close-Up (Macro)',
       
  1109           8: 'Locked (Pan Mode)'}),
       
  1110     19: ('AFPointSelected',
       
  1111          {0x3000: 'None (MF)',
       
  1112           0x3001: 'Auto-Selected',
       
  1113           0x3002: 'Right',
       
  1114           0x3003: 'Center',
       
  1115           0x3004: 'Left'}),
       
  1116     20: ('ExposureMode',
       
  1117          {0: 'Easy Shooting',
       
  1118           1: 'Program',
       
  1119           2: 'Tv-priority',
       
  1120           3: 'Av-priority',
       
  1121           4: 'Manual',
       
  1122           5: 'A-DEP'}),
       
  1123     23: ('LongFocalLengthOfLensInFocalUnits', ),
       
  1124     24: ('ShortFocalLengthOfLensInFocalUnits', ),
       
  1125     25: ('FocalUnitsPerMM', ),
       
  1126     28: ('FlashActivity',
       
  1127          {0: 'Did Not Fire',
       
  1128           1: 'Fired'}),
       
  1129     29: ('FlashDetails',
       
  1130          {14: 'External E-TTL',
       
  1131           13: 'Internal Flash',
       
  1132           11: 'FP Sync Used',
       
  1133           7: '2nd("Rear")-Curtain Sync Used',
       
  1134           4: 'FP Sync Enabled'}),
       
  1135     32: ('FocusMode',
       
  1136          {0: 'Single',
       
  1137           1: 'Continuous'}),
       
  1138     }
       
  1139 
       
  1140 MAKERNOTE_CANON_TAG_0x004 = {
       
  1141     7: ('WhiteBalance',
       
  1142         {0: 'Auto',
       
  1143          1: 'Sunny',
       
  1144          2: 'Cloudy',
       
  1145          3: 'Tungsten',
       
  1146          4: 'Fluorescent',
       
  1147          5: 'Flash',
       
  1148          6: 'Custom'}),
       
  1149     9: ('SequenceNumber', ),
       
  1150     14: ('AFPointUsed', ),
       
  1151     15: ('FlashBias',
       
  1152          {0xFFC0: '-2 EV',
       
  1153           0xFFCC: '-1.67 EV',
       
  1154           0xFFD0: '-1.50 EV',
       
  1155           0xFFD4: '-1.33 EV',
       
  1156           0xFFE0: '-1 EV',
       
  1157           0xFFEC: '-0.67 EV',
       
  1158           0xFFF0: '-0.50 EV',
       
  1159           0xFFF4: '-0.33 EV',
       
  1160           0x0000: '0 EV',
       
  1161           0x000C: '0.33 EV',
       
  1162           0x0010: '0.50 EV',
       
  1163           0x0014: '0.67 EV',
       
  1164           0x0020: '1 EV',
       
  1165           0x002C: '1.33 EV',
       
  1166           0x0030: '1.50 EV',
       
  1167           0x0034: '1.67 EV',
       
  1168           0x0040: '2 EV'}),
       
  1169     19: ('SubjectDistance', ),
       
  1170     }
       
  1171 
       
  1172 # extract multibyte integer in Motorola format (little endian)
       
  1173 def s2n_motorola(str):
       
  1174     x = 0
       
  1175     for c in str:
       
  1176         x = (x << 8) | ord(c)
       
  1177     return x
       
  1178 
       
  1179 # extract multibyte integer in Intel format (big endian)
       
  1180 def s2n_intel(str):
       
  1181     x = 0
       
  1182     y = 0L
       
  1183     for c in str:
       
  1184         x = x | (ord(c) << y)
       
  1185         y = y + 8
       
  1186     return x
       
  1187 
       
  1188 # ratio object that eventually will be able to reduce itself to lowest
       
  1189 # common denominator for printing
       
  1190 def gcd(a, b):
       
  1191     if b == 0:
       
  1192         return a
       
  1193     else:
       
  1194         return gcd(b, a % b)
       
  1195 
       
  1196 class Ratio:
       
  1197     def __init__(self, num, den):
       
  1198         self.num = num
       
  1199         self.den = den
       
  1200 
       
  1201     def __repr__(self):
       
  1202         self.reduce()
       
  1203         if self.den == 1:
       
  1204             return str(self.num)
       
  1205         return '%d/%d' % (self.num, self.den)
       
  1206 
       
  1207     def reduce(self):
       
  1208         div = gcd(self.num, self.den)
       
  1209         if div > 1:
       
  1210             self.num = self.num / div
       
  1211             self.den = self.den / div
       
  1212 
       
  1213 # for ease of dealing with tags
       
  1214 class IFD_Tag:
       
  1215     def __init__(self, printable, tag, field_type, values, field_offset,
       
  1216                  field_length):
       
  1217         # printable version of data
       
  1218         self.printable = printable
       
  1219         # tag ID number
       
  1220         self.tag = tag
       
  1221         # field type as index into FIELD_TYPES
       
  1222         self.field_type = field_type
       
  1223         # offset of start of field in bytes from beginning of IFD
       
  1224         self.field_offset = field_offset
       
  1225         # length of data field in bytes
       
  1226         self.field_length = field_length
       
  1227         # either a string or array of data items
       
  1228         self.values = values
       
  1229 
       
  1230     def __str__(self):
       
  1231         return self.printable
       
  1232 
       
  1233     def __repr__(self):
       
  1234         return '(0x%04X) %s=%s @ %d' % (self.tag,
       
  1235                                         FIELD_TYPES[self.field_type][2],
       
  1236                                         self.printable,
       
  1237                                         self.field_offset)
       
  1238 
       
  1239 # class that handles an EXIF header
       
  1240 class EXIF_header:
       
  1241     def __init__(self, file, endian, offset, fake_exif, strict, debug=0):
       
  1242         self.file = file
       
  1243         self.endian = endian
       
  1244         self.offset = offset
       
  1245         self.fake_exif = fake_exif
       
  1246         self.strict = strict
       
  1247         self.debug = debug
       
  1248         self.tags = {}
       
  1249 
       
  1250     # convert slice to integer, based on sign and endian flags
       
  1251     # usually this offset is assumed to be relative to the beginning of the
       
  1252     # start of the EXIF information.  For some cameras that use relative tags,
       
  1253     # this offset may be relative to some other starting point.
       
  1254     def s2n(self, offset, length, signed=0):
       
  1255         self.file.seek(self.offset+offset)
       
  1256         slice=self.file.read(length)
       
  1257         if self.endian == 'I':
       
  1258             val=s2n_intel(slice)
       
  1259         else:
       
  1260             val=s2n_motorola(slice)
       
  1261         # Sign extension ?
       
  1262         if signed:
       
  1263             msb=1L << (8*length-1)
       
  1264             if val & msb:
       
  1265                 val=val-(msb << 1)
       
  1266         return val
       
  1267 
       
  1268     # convert offset to string
       
  1269     def n2s(self, offset, length):
       
  1270         s = ''
       
  1271         for dummy in range(length):
       
  1272             if self.endian == 'I':
       
  1273                 s = s + chr(offset & 0xFF)
       
  1274             else:
       
  1275                 s = chr(offset & 0xFF) + s
       
  1276             offset = offset >> 8
       
  1277         return s
       
  1278 
       
  1279     # return first IFD
       
  1280     def first_IFD(self):
       
  1281         return self.s2n(4, 4)
       
  1282 
       
  1283     # return pointer to next IFD
       
  1284     def next_IFD(self, ifd):
       
  1285         entries=self.s2n(ifd, 2)
       
  1286         return self.s2n(ifd+2+12*entries, 4)
       
  1287 
       
  1288     # return list of IFDs in header
       
  1289     def list_IFDs(self):
       
  1290         i=self.first_IFD()
       
  1291         a=[]
       
  1292         while i:
       
  1293             a.append(i)
       
  1294             i=self.next_IFD(i)
       
  1295         return a
       
  1296 
       
  1297     # return list of entries in this IFD
       
  1298     def dump_IFD(self, ifd, ifd_name, dict=EXIF_TAGS, relative=0, stop_tag='UNDEF'):
       
  1299         entries=self.s2n(ifd, 2)
       
  1300         for i in range(entries):
       
  1301             # entry is index of start of this IFD in the file
       
  1302             entry = ifd + 2 + 12 * i
       
  1303             tag = self.s2n(entry, 2)
       
  1304 
       
  1305             # get tag name early to avoid errors, help debug
       
  1306             tag_entry = dict.get(tag)
       
  1307             if tag_entry:
       
  1308                 tag_name = tag_entry[0]
       
  1309             else:
       
  1310                 tag_name = 'Tag 0x%04X' % tag
       
  1311 
       
  1312             # ignore certain tags for faster processing
       
  1313             if not (not detailed and tag in IGNORE_TAGS):
       
  1314                 field_type = self.s2n(entry + 2, 2)
       
  1315                 
       
  1316                 # unknown field type
       
  1317                 if not 0 < field_type < len(FIELD_TYPES):
       
  1318                     if not self.strict:
       
  1319                         continue
       
  1320                     else:
       
  1321                         raise ValueError('unknown type %d in tag 0x%04X' % (field_type, tag))
       
  1322 
       
  1323                 typelen = FIELD_TYPES[field_type][0]
       
  1324                 count = self.s2n(entry + 4, 4)
       
  1325                 # Adjust for tag id/type/count (2+2+4 bytes)
       
  1326                 # Now we point at either the data or the 2nd level offset
       
  1327                 offset = entry + 8
       
  1328 
       
  1329                 # If the value fits in 4 bytes, it is inlined, else we
       
  1330                 # need to jump ahead again.
       
  1331                 if count * typelen > 4:
       
  1332                     # offset is not the value; it's a pointer to the value
       
  1333                     # if relative we set things up so s2n will seek to the right
       
  1334                     # place when it adds self.offset.  Note that this 'relative'
       
  1335                     # is for the Nikon type 3 makernote.  Other cameras may use
       
  1336                     # other relative offsets, which would have to be computed here
       
  1337                     # slightly differently.
       
  1338                     if relative:
       
  1339                         tmp_offset = self.s2n(offset, 4)
       
  1340                         offset = tmp_offset + ifd - 8
       
  1341                         if self.fake_exif:
       
  1342                             offset = offset + 18
       
  1343                     else:
       
  1344                         offset = self.s2n(offset, 4)
       
  1345 
       
  1346                 field_offset = offset
       
  1347                 if field_type == 2:
       
  1348                     # special case: null-terminated ASCII string
       
  1349                     # XXX investigate
       
  1350                     # sometimes gets too big to fit in int value
       
  1351                     if count != 0 and count < (2**31):
       
  1352                         self.file.seek(self.offset + offset)
       
  1353                         values = self.file.read(count)
       
  1354                         #print values
       
  1355                         # Drop any garbage after a null.
       
  1356                         values = values.split('\x00', 1)[0]
       
  1357                     else:
       
  1358                         values = ''
       
  1359                 else:
       
  1360                     values = []
       
  1361                     signed = (field_type in [6, 8, 9, 10])
       
  1362                     
       
  1363                     # XXX investigate
       
  1364                     # some entries get too big to handle could be malformed
       
  1365                     # file or problem with self.s2n
       
  1366                     if count < 1000:
       
  1367                         for dummy in range(count):
       
  1368                             if field_type in (5, 10):
       
  1369                                 # a ratio
       
  1370                                 value = Ratio(self.s2n(offset, 4, signed),
       
  1371                                               self.s2n(offset + 4, 4, signed))
       
  1372                             else:
       
  1373                                 value = self.s2n(offset, typelen, signed)
       
  1374                             values.append(value)
       
  1375                             offset = offset + typelen
       
  1376                     # The test above causes problems with tags that are 
       
  1377                     # supposed to have long values!  Fix up one important case.
       
  1378                     elif tag_name == 'MakerNote' :
       
  1379                         for dummy in range(count):
       
  1380                             value = self.s2n(offset, typelen, signed)
       
  1381                             values.append(value)
       
  1382                             offset = offset + typelen
       
  1383                     #else :
       
  1384                     #    print "Warning: dropping large tag:", tag, tag_name
       
  1385                 
       
  1386                 # now 'values' is either a string or an array
       
  1387                 if count == 1 and field_type != 2:
       
  1388                     printable=str(values[0])
       
  1389                 elif count > 50 and len(values) > 20 :
       
  1390                     printable=str( values[0:20] )[0:-1] + ", ... ]"
       
  1391                 else:
       
  1392                     printable=str(values)
       
  1393 
       
  1394                 # compute printable version of values
       
  1395                 if tag_entry:
       
  1396                     if len(tag_entry) != 1:
       
  1397                         # optional 2nd tag element is present
       
  1398                         if callable(tag_entry[1]):
       
  1399                             # call mapping function
       
  1400                             printable = tag_entry[1](values)
       
  1401                         else:
       
  1402                             printable = ''
       
  1403                             for i in values:
       
  1404                                 # use lookup table for this tag
       
  1405                                 printable += tag_entry[1].get(i, repr(i))
       
  1406 
       
  1407                 self.tags[ifd_name + ' ' + tag_name] = IFD_Tag(printable, tag,
       
  1408                                                           field_type,
       
  1409                                                           values, field_offset,
       
  1410                                                           count * typelen)
       
  1411                 if self.debug:
       
  1412                     print ' debug:   %s: %s' % (tag_name,
       
  1413                                                 repr(self.tags[ifd_name + ' ' + tag_name]))
       
  1414 
       
  1415             if tag_name == stop_tag:
       
  1416                 break
       
  1417 
       
  1418     # extract uncompressed TIFF thumbnail (like pulling teeth)
       
  1419     # we take advantage of the pre-existing layout in the thumbnail IFD as
       
  1420     # much as possible
       
  1421     def extract_TIFF_thumbnail(self, thumb_ifd):
       
  1422         entries = self.s2n(thumb_ifd, 2)
       
  1423         # this is header plus offset to IFD ...
       
  1424         if self.endian == 'M':
       
  1425             tiff = 'MM\x00*\x00\x00\x00\x08'
       
  1426         else:
       
  1427             tiff = 'II*\x00\x08\x00\x00\x00'
       
  1428         # ... plus thumbnail IFD data plus a null "next IFD" pointer
       
  1429         self.file.seek(self.offset+thumb_ifd)
       
  1430         tiff += self.file.read(entries*12+2)+'\x00\x00\x00\x00'
       
  1431 
       
  1432         # fix up large value offset pointers into data area
       
  1433         for i in range(entries):
       
  1434             entry = thumb_ifd + 2 + 12 * i
       
  1435             tag = self.s2n(entry, 2)
       
  1436             field_type = self.s2n(entry+2, 2)
       
  1437             typelen = FIELD_TYPES[field_type][0]
       
  1438             count = self.s2n(entry+4, 4)
       
  1439             oldoff = self.s2n(entry+8, 4)
       
  1440             # start of the 4-byte pointer area in entry
       
  1441             ptr = i * 12 + 18
       
  1442             # remember strip offsets location
       
  1443             if tag == 0x0111:
       
  1444                 strip_off = ptr
       
  1445                 strip_len = count * typelen
       
  1446             # is it in the data area?
       
  1447             if count * typelen > 4:
       
  1448                 # update offset pointer (nasty "strings are immutable" crap)
       
  1449                 # should be able to say "tiff[ptr:ptr+4]=newoff"
       
  1450                 newoff = len(tiff)
       
  1451                 tiff = tiff[:ptr] + self.n2s(newoff, 4) + tiff[ptr+4:]
       
  1452                 # remember strip offsets location
       
  1453                 if tag == 0x0111:
       
  1454                     strip_off = newoff
       
  1455                     strip_len = 4
       
  1456                 # get original data and store it
       
  1457                 self.file.seek(self.offset + oldoff)
       
  1458                 tiff += self.file.read(count * typelen)
       
  1459 
       
  1460         # add pixel strips and update strip offset info
       
  1461         old_offsets = self.tags['Thumbnail StripOffsets'].values
       
  1462         old_counts = self.tags['Thumbnail StripByteCounts'].values
       
  1463         for i in range(len(old_offsets)):
       
  1464             # update offset pointer (more nasty "strings are immutable" crap)
       
  1465             offset = self.n2s(len(tiff), strip_len)
       
  1466             tiff = tiff[:strip_off] + offset + tiff[strip_off + strip_len:]
       
  1467             strip_off += strip_len
       
  1468             # add pixel strip to end
       
  1469             self.file.seek(self.offset + old_offsets[i])
       
  1470             tiff += self.file.read(old_counts[i])
       
  1471 
       
  1472         self.tags['TIFFThumbnail'] = tiff
       
  1473 
       
  1474     # decode all the camera-specific MakerNote formats
       
  1475 
       
  1476     # Note is the data that comprises this MakerNote.  The MakerNote will
       
  1477     # likely have pointers in it that point to other parts of the file.  We'll
       
  1478     # use self.offset as the starting point for most of those pointers, since
       
  1479     # they are relative to the beginning of the file.
       
  1480     #
       
  1481     # If the MakerNote is in a newer format, it may use relative addressing
       
  1482     # within the MakerNote.  In that case we'll use relative addresses for the
       
  1483     # pointers.
       
  1484     #
       
  1485     # As an aside: it's not just to be annoying that the manufacturers use
       
  1486     # relative offsets.  It's so that if the makernote has to be moved by the
       
  1487     # picture software all of the offsets don't have to be adjusted.  Overall,
       
  1488     # this is probably the right strategy for makernotes, though the spec is
       
  1489     # ambiguous.  (The spec does not appear to imagine that makernotes would
       
  1490     # follow EXIF format internally.  Once they did, it's ambiguous whether
       
  1491     # the offsets should be from the header at the start of all the EXIF info,
       
  1492     # or from the header at the start of the makernote.)
       
  1493     def decode_maker_note(self):
       
  1494         note = self.tags['EXIF MakerNote']
       
  1495         
       
  1496         # Some apps use MakerNote tags but do not use a format for which we
       
  1497         # have a description, so just do a raw dump for these.
       
  1498         #if self.tags.has_key('Image Make'):
       
  1499         make = self.tags['Image Make'].printable
       
  1500         #else:
       
  1501         #    make = ''
       
  1502 
       
  1503         # model = self.tags['Image Model'].printable # unused
       
  1504 
       
  1505         # Nikon
       
  1506         # The maker note usually starts with the word Nikon, followed by the
       
  1507         # type of the makernote (1 or 2, as a short).  If the word Nikon is
       
  1508         # not at the start of the makernote, it's probably type 2, since some
       
  1509         # cameras work that way.
       
  1510         if 'NIKON' in make:
       
  1511             if note.values[0:7] == [78, 105, 107, 111, 110, 0, 1]:
       
  1512                 if self.debug:
       
  1513                     print "Looks like a type 1 Nikon MakerNote."
       
  1514                 self.dump_IFD(note.field_offset+8, 'MakerNote',
       
  1515                               dict=MAKERNOTE_NIKON_OLDER_TAGS)
       
  1516             elif note.values[0:7] == [78, 105, 107, 111, 110, 0, 2]:
       
  1517                 if self.debug:
       
  1518                     print "Looks like a labeled type 2 Nikon MakerNote"
       
  1519                 if note.values[12:14] != [0, 42] and note.values[12:14] != [42L, 0L]:
       
  1520                     raise ValueError("Missing marker tag '42' in MakerNote.")
       
  1521                 # skip the Makernote label and the TIFF header
       
  1522                 self.dump_IFD(note.field_offset+10+8, 'MakerNote',
       
  1523                               dict=MAKERNOTE_NIKON_NEWER_TAGS, relative=1)
       
  1524             else:
       
  1525                 # E99x or D1
       
  1526                 if self.debug:
       
  1527                     print "Looks like an unlabeled type 2 Nikon MakerNote"
       
  1528                 self.dump_IFD(note.field_offset, 'MakerNote',
       
  1529                               dict=MAKERNOTE_NIKON_NEWER_TAGS)
       
  1530             return
       
  1531 
       
  1532         # Olympus
       
  1533         if make.startswith('OLYMPUS'):
       
  1534             self.dump_IFD(note.field_offset+8, 'MakerNote',
       
  1535                           dict=MAKERNOTE_OLYMPUS_TAGS)
       
  1536             # XXX TODO
       
  1537             #for i in (('MakerNote Tag 0x2020', MAKERNOTE_OLYMPUS_TAG_0x2020),):
       
  1538             #    self.decode_olympus_tag(self.tags[i[0]].values, i[1])
       
  1539             #return
       
  1540 
       
  1541         # Casio
       
  1542         if 'CASIO' in make or 'Casio' in make:
       
  1543             self.dump_IFD(note.field_offset, 'MakerNote',
       
  1544                           dict=MAKERNOTE_CASIO_TAGS)
       
  1545             return
       
  1546 
       
  1547         # Fujifilm
       
  1548         if make == 'FUJIFILM':
       
  1549             # bug: everything else is "Motorola" endian, but the MakerNote
       
  1550             # is "Intel" endian
       
  1551             endian = self.endian
       
  1552             self.endian = 'I'
       
  1553             # bug: IFD offsets are from beginning of MakerNote, not
       
  1554             # beginning of file header
       
  1555             offset = self.offset
       
  1556             self.offset += note.field_offset
       
  1557             # process note with bogus values (note is actually at offset 12)
       
  1558             self.dump_IFD(12, 'MakerNote', dict=MAKERNOTE_FUJIFILM_TAGS)
       
  1559             # reset to correct values
       
  1560             self.endian = endian
       
  1561             self.offset = offset
       
  1562             return
       
  1563 
       
  1564         # Canon
       
  1565         if make == 'Canon':
       
  1566             self.dump_IFD(note.field_offset, 'MakerNote',
       
  1567                           dict=MAKERNOTE_CANON_TAGS)
       
  1568             for i in (('MakerNote Tag 0x0001', MAKERNOTE_CANON_TAG_0x001),
       
  1569                       ('MakerNote Tag 0x0004', MAKERNOTE_CANON_TAG_0x004)):
       
  1570                 self.canon_decode_tag(self.tags[i[0]].values, i[1])
       
  1571             return
       
  1572 
       
  1573 
       
  1574     # XXX TODO decode Olympus MakerNote tag based on offset within tag
       
  1575     def olympus_decode_tag(self, value, dict):
       
  1576         pass
       
  1577 
       
  1578     # decode Canon MakerNote tag based on offset within tag
       
  1579     # see http://www.burren.cx/david/canon.html by David Burren
       
  1580     def canon_decode_tag(self, value, dict):
       
  1581         for i in range(1, len(value)):
       
  1582             x=dict.get(i, ('Unknown', ))
       
  1583             if self.debug:
       
  1584                 print i, x
       
  1585             name=x[0]
       
  1586             if len(x) > 1:
       
  1587                 val=x[1].get(value[i], 'Unknown')
       
  1588             else:
       
  1589                 val=value[i]
       
  1590             # it's not a real IFD Tag but we fake one to make everybody
       
  1591             # happy. this will have a "proprietary" type
       
  1592             self.tags['MakerNote '+name]=IFD_Tag(str(val), None, 0, None,
       
  1593                                                  None, None)
       
  1594 
       
  1595 # process an image file (expects an open file object)
       
  1596 # this is the function that has to deal with all the arbitrary nasty bits
       
  1597 # of the EXIF standard
       
  1598 def process_file(f, stop_tag='UNDEF', details=True, strict=False, debug=False):
       
  1599     # yah it's cheesy...
       
  1600     global detailed
       
  1601     detailed = details
       
  1602 
       
  1603     # by default do not fake an EXIF beginning
       
  1604     fake_exif = 0
       
  1605 
       
  1606     # determine whether it's a JPEG or TIFF
       
  1607     data = f.read(12)
       
  1608     if data[0:4] in ['II*\x00', 'MM\x00*']:
       
  1609         # it's a TIFF file
       
  1610         f.seek(0)
       
  1611         endian = f.read(1)
       
  1612         f.read(1)
       
  1613         offset = 0
       
  1614     elif data[0:2] == '\xFF\xD8':
       
  1615         # it's a JPEG file
       
  1616         while data[2] == '\xFF' and data[6:10] in ('JFIF', 'JFXX', 'OLYM', 'Phot'):
       
  1617             length = ord(data[4])*256+ord(data[5])
       
  1618             f.read(length-8)
       
  1619             # fake an EXIF beginning of file
       
  1620             data = '\xFF\x00'+f.read(10)
       
  1621             fake_exif = 1
       
  1622         if data[2] == '\xFF' and data[6:10] == 'Exif':
       
  1623             # detected EXIF header
       
  1624             offset = f.tell()
       
  1625             endian = f.read(1)
       
  1626         else:
       
  1627             # no EXIF information
       
  1628             return {}
       
  1629     else:
       
  1630         # file format not recognized
       
  1631         return {}
       
  1632 
       
  1633     # deal with the EXIF info we found
       
  1634     if debug:
       
  1635         print {'I': 'Intel', 'M': 'Motorola'}[endian], 'format'
       
  1636     hdr = EXIF_header(f, endian, offset, fake_exif, strict, debug)
       
  1637     ifd_list = hdr.list_IFDs()
       
  1638     ctr = 0
       
  1639     for i in ifd_list:
       
  1640         if ctr == 0:
       
  1641             IFD_name = 'Image'
       
  1642         elif ctr == 1:
       
  1643             IFD_name = 'Thumbnail'
       
  1644             thumb_ifd = i
       
  1645         else:
       
  1646             IFD_name = 'IFD %d' % ctr
       
  1647         if debug:
       
  1648             print ' IFD %d (%s) at offset %d:' % (ctr, IFD_name, i)
       
  1649         hdr.dump_IFD(i, IFD_name, stop_tag=stop_tag)
       
  1650         # EXIF IFD
       
  1651         exif_off = hdr.tags.get(IFD_name+' ExifOffset')
       
  1652         if exif_off:
       
  1653             if debug:
       
  1654                 print ' EXIF SubIFD at offset %d:' % exif_off.values[0]
       
  1655             hdr.dump_IFD(exif_off.values[0], 'EXIF', stop_tag=stop_tag)
       
  1656             # Interoperability IFD contained in EXIF IFD
       
  1657             intr_off = hdr.tags.get('EXIF SubIFD InteroperabilityOffset')
       
  1658             if intr_off:
       
  1659                 if debug:
       
  1660                     print ' EXIF Interoperability SubSubIFD at offset %d:' \
       
  1661                           % intr_off.values[0]
       
  1662                 hdr.dump_IFD(intr_off.values[0], 'EXIF Interoperability',
       
  1663                              dict=INTR_TAGS, stop_tag=stop_tag)
       
  1664         # GPS IFD
       
  1665         gps_off = hdr.tags.get(IFD_name+' GPSInfo')
       
  1666         if gps_off:
       
  1667             if debug:
       
  1668                 print ' GPS SubIFD at offset %d:' % gps_off.values[0]
       
  1669             hdr.dump_IFD(gps_off.values[0], 'GPS', dict=GPS_TAGS, stop_tag=stop_tag)
       
  1670         ctr += 1
       
  1671 
       
  1672     # extract uncompressed TIFF thumbnail
       
  1673     thumb = hdr.tags.get('Thumbnail Compression')
       
  1674     if thumb and thumb.printable == 'Uncompressed TIFF':
       
  1675         hdr.extract_TIFF_thumbnail(thumb_ifd)
       
  1676 
       
  1677     # JPEG thumbnail (thankfully the JPEG data is stored as a unit)
       
  1678     thumb_off = hdr.tags.get('Thumbnail JPEGInterchangeFormat')
       
  1679     if thumb_off:
       
  1680         f.seek(offset+thumb_off.values[0])
       
  1681         size = hdr.tags['Thumbnail JPEGInterchangeFormatLength'].values[0]
       
  1682         hdr.tags['JPEGThumbnail'] = f.read(size)
       
  1683 
       
  1684     # deal with MakerNote contained in EXIF IFD
       
  1685     # (Some apps use MakerNote tags but do not use a format for which we
       
  1686     # have a description, do not process these).
       
  1687     if 'EXIF MakerNote' in hdr.tags and 'Image Make' in hdr.tags and detailed:
       
  1688         hdr.decode_maker_note()
       
  1689 
       
  1690     # Sometimes in a TIFF file, a JPEG thumbnail is hidden in the MakerNote
       
  1691     # since it's not allowed in a uncompressed TIFF IFD
       
  1692     if 'JPEGThumbnail' not in hdr.tags:
       
  1693         thumb_off=hdr.tags.get('MakerNote JPEGThumbnail')
       
  1694         if thumb_off:
       
  1695             f.seek(offset+thumb_off.values[0])
       
  1696             hdr.tags['JPEGThumbnail']=file.read(thumb_off.field_length)
       
  1697 
       
  1698     return hdr.tags
       
  1699 
       
  1700 
       
  1701 # show command line usage
       
  1702 def usage(exit_status):
       
  1703     msg = 'Usage: EXIF.py [OPTIONS] file1 [file2 ...]\n'
       
  1704     msg += 'Extract EXIF information from digital camera image files.\n\nOptions:\n'
       
  1705     msg += '-q --quick   Do not process MakerNotes.\n'
       
  1706     msg += '-t TAG --stop-tag TAG   Stop processing when this tag is retrieved.\n'
       
  1707     msg += '-s --strict   Run in strict mode (stop on errors).\n'
       
  1708     msg += '-d --debug   Run in debug mode (display extra info).\n'
       
  1709     print msg
       
  1710     sys.exit(exit_status)
       
  1711 
       
  1712 # library test/debug function (dump given files)
       
  1713 if __name__ == '__main__':
       
  1714     import sys
       
  1715     import getopt
       
  1716 
       
  1717     # parse command line options/arguments
       
  1718     try:
       
  1719         opts, args = getopt.getopt(sys.argv[1:], "hqsdt:v", ["help", "quick", "strict", "debug", "stop-tag="])
       
  1720     except getopt.GetoptError:
       
  1721         usage(2)
       
  1722     if args == []:
       
  1723         usage(2)
       
  1724     detailed = True
       
  1725     stop_tag = 'UNDEF'
       
  1726     debug = False
       
  1727     strict = False
       
  1728     for o, a in opts:
       
  1729         if o in ("-h", "--help"):
       
  1730             usage(0)
       
  1731         if o in ("-q", "--quick"):
       
  1732             detailed = False
       
  1733         if o in ("-t", "--stop-tag"):
       
  1734             stop_tag = a
       
  1735         if o in ("-s", "--strict"):
       
  1736             strict = True
       
  1737         if o in ("-d", "--debug"):
       
  1738             debug = True
       
  1739 
       
  1740     # output info for each file
       
  1741     for filename in args:
       
  1742         try:
       
  1743             file=open(filename, 'rb')
       
  1744         except:
       
  1745             print "'%s' is unreadable\n"%filename
       
  1746             continue
       
  1747         print filename + ':'
       
  1748         # get the tags
       
  1749         data = process_file(file, stop_tag=stop_tag, details=detailed, strict=strict, debug=debug)
       
  1750         if not data:
       
  1751             print 'No EXIF information found'
       
  1752             continue
       
  1753 
       
  1754         x=data.keys()
       
  1755         x.sort()
       
  1756         for i in x:
       
  1757             if i in ('JPEGThumbnail', 'TIFFThumbnail'):
       
  1758                 continue
       
  1759             try:
       
  1760                 print '   %s (%s): %s' % \
       
  1761                       (i, FIELD_TYPES[data[i].field_type][2], data[i].printable)
       
  1762             except:
       
  1763                 print 'error', i, '"', data[i], '"'
       
  1764         if 'JPEGThumbnail' in data:
       
  1765             print 'File has JPEG thumbnail'
       
  1766         print
       
  1767