def _get_offset_endian_jpeg(f): ## it's a JPEG file logger.debug("JPEG format recognized data[0:2] == '0xFFD8'.") ## Determine the "base" from which to start reading f.seek(0) data = bytearray(f.read(12)) base = 2 while data[2] == 0xFF and data[6:10] in ('JFIF', 'JFXX', 'OLYM', 'Phot'): logger.debug(" data[2] == 0xFF data[3] == {:x} and data[6:10] = {}" "".format(data[3], data[6:10])) length = (data[4] * 256) + data[5] assert isinstance(length, int) logger.debug(" Length offset is {:d}".format(length)) f.read(length - 8) ## Fake an EXIF beginning of file ## I don't think this is used. --gd data = '\xFF\x00' + f.read(10) #fake_exif = 1 if base > 2: base += length + 2 else: base = length + 4 logger.debug(" Set segment base to {}".format(base)) del data # We're done with it!! b = mmapbytes(f) while True: logger.debug("Segment base 0x{:X}".format(base)) b.set_window(base) b1 = b[1] if b[0] == 0xFF: if b1 == 0xE1: if b[4:8] == "Exif": base -= 2 break if b1 == 0xDB: break _base_increment = (b[2] * 256) + b[3] + 2 logger.debug("Increment base by {}".format(_base_increment)) base += _base_increment ## Jump ahead after file headers b.set_window(base) _data_b2 = b[2] _data_b6t10 = b[6:10] _data_b6t11 = b[6:11] if _data_b2 == 0xFF: logger.debug("Exif header: {:x} {!r}".format(_data_b2, _data_b6t11)) if _data_b6t10 == 'Exif': ## detected EXIF header offset = f.tell() endian = f.read(1) return offset, endian #HACK TEST: endian = 'M' elif _data_b6t11 == 'Ducky': ## detected Ducky header. logger.debug("EXIF-like header (normally 0xFF and code): " "{:x} and {!r}".format(_data_b2, _data_b6t11)) offset = f.tell() endian = f.read(1) return offset, endian elif _data_b6t11 == 'Adobe': ## detected APP14 (Adobe) logger.debug("EXIF-like header (normally 0xFF and code): " "{:x} and {!r}".format(_data_b2, _data_b6t11)) offset = f.tell() endian = f.read(1) return offset, endian else: ## No EXIF information found -- error!! logger.debug( "No EXIF header found:\n" " Expected b[2]==0xFF and b[6:10]=='Exif'' (or 'Duck')\n" " Got: {:x} and {!r}".format(_data_b2, _data_b6t11)) raise NoExifData("No EXIF header found")
def test_mmapbytes(self): from StringIO import StringIO from exifpy.utils import mmapbytes message = "Hello, world; spam & eggs for everybody!" ## Test direct referencing of items ## The object should behave exactly like a bytearray test_indices = [0, 7, 14, -1, -10, -14] test_windows = [ (None, None), (0, 0), (0, 12), (None, 12), # (14, 0), ## Invalid!! (14, None), (14, 25), ] for winStart, winEnd in test_windows: _message = message[winStart:winEnd] mm = mmapbytes(StringIO(message), winStart, winEnd) for idx in test_indices: try: expected = ord(_message[idx]) except IndexError: # print "\n\n\nWe're running with window {},{}" \ # "".format(winStart, winEnd) # print "The string {!r} raised IndexError on index {}. "\ # "We expect the mmapbytes() object to do the same."\ # "".format(_message, idx) with self.assertRaises(IndexError): mm[idx] # print "RETURNED {!r}".format(mm[idx]) else: result = mm[idx] self.assertEqual( expected, result, "Test window {},{} index {} - Expected: {!r} got: {!r}" "".format(winStart, winEnd, idx, expected, result), ) ## Test some slicing. ## The object should behave exactly like a string. test_indices = [(0, 1), (0, 10), (0, None), (None, 0), (None, 10), (10, None), (None, None)] test_windows = [ (None, None), (0, 0), (0, 12), (None, 12), # (14, 0), # Invalid! (14, None), (14, 25), ] for winStart, winEnd in test_windows: _message = message[winStart:winEnd] mm = mmapbytes(StringIO(message), winStart, winEnd) for start, end in test_indices: expected = _message[start:end] result = mm[start:end] self.assertEqual( expected, result, "Test window {},{} slice {},{} - Expected: {!r} got: {!r}" "".format(winStart, winEnd, start, end, expected, result), )