Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use struct for unpack (and add support for floats) #60

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 37 additions & 13 deletions exifread/classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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.

Expand All @@ -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."""
Expand Down Expand Up @@ -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)
Expand Down
2 changes: 2 additions & 0 deletions exifread/tags/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
21 changes: 3 additions & 18 deletions exifread/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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:
Expand Down