diff --git a/exifread/classes.py b/exifread/classes.py index 7880dc9..36fcadf 100644 --- a/exifread/classes.py +++ b/exifread/classes.py @@ -2,7 +2,7 @@ import re from .exif_log import get_logger -from .utils import s2n_motorola, s2n_intel, Ratio +from .utils import Ratio from .tags import * logger = get_logger() @@ -65,7 +65,7 @@ def __init__(self, file, endian, offset, fake_exif, strict, self.detailed = detailed self.tags = {} - def s2n(self, offset, length, signed=0): + def s2n(self, offset, length, signed=False): """ Convert slice to integer, based on sign and endian flags. @@ -74,18 +74,28 @@ def s2n(self, offset, length, signed=0): For some cameras that use relative tags, this offset may be relative to some other starting point. """ + # Little-endian if Intel, big-endian if Motorola + fmt = '<' if self.endian == 'I' else '>' + # Construct a format string from the requested length and signedness; + # raise a ValueError if length is something silly like 3 + try: + fmt += { + (1, False): 'B', + (1, True): 'b', + (2, False): 'H', + (2, True): 'h', + (4, False): 'I', + (4, True): 'i', + (8, False): 'L', + (8, True): 'l', + }[(length, signed)] + except KeyError: + raise ValueError('unexpected unpacking length: %d' % length) self.file.seek(self.offset + offset) - sliced = self.file.read(length) - if self.endian == 'I': - val = s2n_intel(sliced) - else: - val = s2n_motorola(sliced) - # Sign extension? - if signed: - msb = 1 << (8 * length - 1) - if val & msb: - val -= (msb << 1) - return val + buf = self.file.read(length) + if buf: + return struct.unpack(fmt, buf)[0] + return 0 def n2s(self, offset, length): """Convert offset to string.""" @@ -215,6 +225,20 @@ def dump_ifd(self, ifd, ifd_name, tag_dict=EXIF_TAGS, relative=0, stop_tag=DEFAU # a ratio value = Ratio(self.s2n(offset, 4, signed), self.s2n(offset + 4, 4, signed)) + elif field_type in (11,12): + # a float or double + unpack_format = "" + if self.endian == 'I': + unpack_format += "<" + else: + unpack_format += ">" + if field_type == 11: + unpack_format += "f" + else: + unpack_format += "d" + self.file.seek(self.offset + offset) + byte_str = self.file.read(type_length) + value = struct.unpack(unpack_format,byte_str) else: value = self.s2n(offset, type_length, signed) values.append(value) diff --git a/exifread/tags/__init__.py b/exifread/tags/__init__.py index 174de2e..7f633f8 100644 --- a/exifread/tags/__init__.py +++ b/exifread/tags/__init__.py @@ -20,6 +20,8 @@ (2, 'SS', 'Signed Short'), (4, 'SL', 'Signed Long'), (8, 'SR', 'Signed Ratio'), + (4, 'F32', 'Single-Precision Floating Point (32-bit)'), + (8, 'F64', 'Double-Precision Floating Point (64-bit)'), ) # To ignore when quick processing diff --git a/exifread/utils.py b/exifread/utils.py index f5e0d7f..98d833f 100644 --- a/exifread/utils.py +++ b/exifread/utils.py @@ -38,24 +38,6 @@ def make_string_uc(seq): return make_string(seq) -def s2n_motorola(string): - """Extract multi-byte integer in Motorola format (little endian).""" - x = 0 - for c in string: - x = (x << 8) | ord_(c) - return x - - -def s2n_intel(string): - """Extract multi-byte integer in Intel format (big endian).""" - x = 0 - y = 0 - for c in string: - x = x | (ord_(c) << y) - y += + 8 - return x - - class Ratio: """ Ratio object that eventually will be able to reduce itself to lowest @@ -71,6 +53,9 @@ def __repr__(self): if self.den == 1: return str(self.num) return '%d/%d' % (self.num, self.den) + + def __float__(self): + return ((1.0*self.num)/self.den) def _gcd(self, a, b): if b == 0: