Ticket #854: 0001-make-exif-a-submodule.patch

File 0001-make-exif-a-submodule.patch, 76.3 KB (added by alon, 10 years ago)

patch to fix bug

  • .gitmodules

    From dc1b04acaa35b333c9abe546bfda5e5e709ac284 Mon Sep 17 00:00:00 2001
    From: Alon Levy <alon@pobox.com>
    Date: Sat, 1 Mar 2014 21:52:14 +0200
    Subject: [PATCH] make exif a submodule
    
    Updates it to latest which includes a fix for a 100% hang on a jpg without any EXIF data:
    https://www.dropbox.com/s/m5l185qxq9w5mzk/raspberry-pi-gpio-cheat-sheet.jpg
    
    Signed-off-by: Alon Levy <alon@pobox.com>
    ---
     .gitmodules                      |    3 +
     extlib/exif                      |    1 +
     extlib/exif/EXIF.py              | 1896 --------------------------------------
     extlib/exif/LICENSE              |    1 -
     extlib/exif/changes.txt          |  131 ---
     mediagoblin/tools/exif.py        |    9 +-
     mediagoblin/tools/extlib/EXIF.py |    1 -
     7 files changed, 9 insertions(+), 2033 deletions(-)
     create mode 160000 extlib/exif
     delete mode 100755 extlib/exif/EXIF.py
     delete mode 100644 extlib/exif/LICENSE
     delete mode 100644 extlib/exif/changes.txt
     delete mode 120000 mediagoblin/tools/extlib/EXIF.py
    
    diff --git a/.gitmodules b/.gitmodules
    index 20fa20e..5d78d03 100644
    a b  
    77[submodule "extlib/skeleton"]
    88        path = extlib/skeleton
    99        url = git://github.com/dhg/Skeleton.git
     10[submodule "extlib/exif"]
     11        path = extlib/exif
     12        url = https://github.com/ianare/exif-py.git
  • new file extlib/exif

    diff --git a/extlib/exif b/extlib/exif
    new file mode 160000
    index 0000000..6ff9ed1
    - +  
     1Subproject commit 6ff9ed1410e72e1da19257b93976fa88fe4248bd
  • deleted file extlib/exif/EXIF.py

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

    diff --git a/extlib/exif/LICENSE b/extlib/exif/LICENSE
    deleted file mode 100644
    index 3bd7714..0000000
    + -  
    1 See top of EXIF.py for license and copyright.
  • deleted file extlib/exif/changes.txt

    diff --git a/extlib/exif/changes.txt b/extlib/exif/changes.txt
    deleted file mode 100644
    index d1b18e6..0000000
    + -  
    1 ï»¿~ EXIF.py Changelog ~
    2 
    3 2012-11-30 - Gregory Dudek (date of merge).
    4 Patches and changes:
    5   Overflow error fixes added (related to 2**31 size)
    6   GPS tags added.
    7 
    8 2012-09-26 - Ianaré Sévi
    9 Merge patches:
    10   Add GPS tags
    11   Add better endian debug info
    12 
    13 2012-06-13 - Ianaré Sévi
    14 Merge patches:
    15   Support malformed last IFD by fhats
    16   Light source, Flash and Metering mode dictionaries update by gryfik
    17 
    18 2008-07-31 - Ianaré Sévi
    19 Wikipedia Commons hunt for suitable test case images,
    20 testing new code additions.
    21 
    22 2008-07-09 - Stephen H. Olson
    23 Fix a problem with reading MakerNotes out of NEF files.
    24 Add some more Nikon MakerNote tags.
    25 
    26 2008-07-08 - Stephen H. Olson
    27 An error check for large tags totally borked MakerNotes.
    28   With Nikon anyway, valid MakerNotes can be pretty big.
    29 Add error check for a crash caused by nikon_ev_bias being
    30   called with the wrong args.
    31 Drop any garbage after a null character in string
    32   (patch from Andrew McNabb <amcnabb@google.com>).
    33 
    34 2008-02-12 - Ianaré Sévi
    35 Fix crash on invalid MakerNote
    36 Fix crash on huge Makernote (temp fix)
    37 Add printIM tag 0xC4A5, needs decoding info
    38 Add 0x9C9B-F range of tags
    39 Add a bunch of tag definitions from:
    40  http://owl.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html
    41 Add 'strict' variable and command line option
    42 
    43 2008-01-18 - Gunter Ohrner
    44 Add 'GPSDate' tag
    45 
    46 2007-12-12 - Ianaré Sévi
    47 Fix quick option on certain image types
    48 Add note on tag naming in documentation
    49 
    50 2007-11-30 - Ianaré Sévi
    51 Changed -s option to -t
    52 Put changelog into separate file
    53 
    54 2007-10-28 - Ianaré Sévi
    55 Merged changes from MoinMoin:ReimarBauer
    56 Added command line option for debug, stop
    57 processing on tag.
    58 
    59 2007-09-27 - Ianaré Sévi
    60 Add some Olympus Makernote tags.
    61 
    62 2007-09-26 - Stephen H. Olson
    63 Don't error out on invalid Olympus 'SpecialMode'.
    64 Add a few more Olympus/Minolta tags.
    65 
    66 2007-09-22 - Stephen H. Olson
    67 Don't error on invalid string
    68 Improved Nikon MakerNote support
    69 
    70 2007-05-03 - Martin Stone <mj_stone@users.sourceforge.net>
    71 Fix for inverted detailed flag and Photoshop header
    72 
    73 2007-03-24 - Ianaré Sévi
    74 Can now ignore MakerNotes Tags for faster processing.
    75 
    76 2007-01-18 - Ianaré Sévi <ianare@gmail.com>
    77 Fixed a couple errors and assuming maintenance of the library.
    78 
    79 2006-08-04 MoinMoin:ReimarBauer
    80 Added an optional parameter name to process_file and dump_IFD. Using this parameter the
    81 loop is breaked after that tag_name is processed.
    82 some PEP8 changes
    83 
    84 ---------------------------- original notices -------------------------
    85 
    86 Contains code from "exifdump.py" originally written by Thierry Bousch
    87 <bousch@topo.math.u-psud.fr> and released into the public domain.
    88 
    89 Updated and turned into general-purpose library by Gene Cash
    90 
    91 Patch Contributors:
    92 * Simon J. Gerraty <sjg@crufty.net>
    93 s2n fix & orientation decode
    94 * John T. Riedl <riedl@cs.umn.edu>
    95 Added support for newer Nikon type 3 Makernote format for D70 and some
    96 other Nikon cameras.
    97 * Joerg Schaefer <schaeferj@gmx.net>
    98 Fixed subtle bug when faking an EXIF header, which affected maker notes
    99 using relative offsets, and a fix for Nikon D100.
    100 
    101 1999-08-21 TB  Last update by Thierry Bousch to his code.
    102 
    103 2002-01-17 CEC Discovered code on web.
    104             Commented everything.
    105             Made small code improvements.
    106             Reformatted for readability.
    107            
    108 2002-01-19 CEC Added ability to read TIFFs and JFIF-format JPEGs.
    109             Added ability to extract JPEG formatted thumbnail.
    110             Added ability to read GPS IFD (not tested).
    111             Converted IFD data structure to dictionaries indexed by
    112             tag name.
    113             Factored into library returning dictionary of IFDs plus
    114             thumbnail, if any.
    115            
    116 2002-01-20 CEC Added MakerNote processing logic.
    117             Added Olympus MakerNote.
    118             Converted data structure to single-level dictionary, avoiding
    119             tag name collisions by prefixing with IFD name.  This makes
    120             it much easier to use.
    121 2002-01-23 CEC Trimmed nulls from end of string values.
    122 
    123 2002-01-25 CEC Discovered JPEG thumbnail in Olympus TIFF MakerNote.
    124 
    125 2002-01-26 CEC Added ability to extract TIFF thumbnails.
    126             Added Nikon, Fujifilm, Casio MakerNotes.
    127            
    128 2003-11-30 CEC Fixed problem with canon_decode_tag() not creating an
    129             IFD_Tag() object.
    130            
    131 2004-02-15 CEC Finally fixed bit shift warning by converting Y to 0L.
  • mediagoblin/tools/exif.py

    diff --git a/mediagoblin/tools/exif.py b/mediagoblin/tools/exif.py
    index 6b3639e..acbf317 100644
    a b  
    1414# You should have received a copy of the GNU Affero General Public License
    1515# along with this program.  If not, see <http://www.gnu.org/licenses/>.
    1616
    17 try:
    18     from EXIF import process_file, Ratio
    19 except ImportError:
    20     from mediagoblin.tools.extlib.EXIF import process_file, Ratio
     17import sys
     18sys.path.append('extlib/exif')
     19
     20from EXIF import process_file
     21from exifread.utils import Ratio
    2122
    2223from mediagoblin.processing import BadMediaFail
    2324from mediagoblin.tools.translate import pass_to_ugettext as _
  • deleted file mediagoblin/tools/extlib/EXIF.py

    diff --git a/mediagoblin/tools/extlib/EXIF.py b/mediagoblin/tools/extlib/EXIF.py
    deleted file mode 120000
    index 82a2fb3..0000000
    + -  
    1 ../../../extlib/exif/EXIF.py
    2  No newline at end of file