1*a67afe4dSAndroid Build Coastguard Worker#!/usr/bin/env python 2*a67afe4dSAndroid Build Coastguard Worker 3*a67afe4dSAndroid Build Coastguard Worker""" 4*a67afe4dSAndroid Build Coastguard WorkerShow the EXIF information. 5*a67afe4dSAndroid Build Coastguard Worker 6*a67afe4dSAndroid Build Coastguard WorkerCopyright (C) 2017-2020 Cosmin Truta. 7*a67afe4dSAndroid Build Coastguard Worker 8*a67afe4dSAndroid Build Coastguard WorkerUse, modification and distribution are subject to the MIT License. 9*a67afe4dSAndroid Build Coastguard WorkerPlease see the accompanying file LICENSE_MIT.txt 10*a67afe4dSAndroid Build Coastguard Worker""" 11*a67afe4dSAndroid Build Coastguard Worker 12*a67afe4dSAndroid Build Coastguard Workerfrom __future__ import absolute_import, division, print_function 13*a67afe4dSAndroid Build Coastguard Worker 14*a67afe4dSAndroid Build Coastguard Workerimport sys 15*a67afe4dSAndroid Build Coastguard Worker 16*a67afe4dSAndroid Build Coastguard Workerfrom bytepack import (unpack_uint32be, 17*a67afe4dSAndroid Build Coastguard Worker unpack_uint32le, 18*a67afe4dSAndroid Build Coastguard Worker unpack_uint16be, 19*a67afe4dSAndroid Build Coastguard Worker unpack_uint16le, 20*a67afe4dSAndroid Build Coastguard Worker unpack_uint8) 21*a67afe4dSAndroid Build Coastguard Worker 22*a67afe4dSAndroid Build Coastguard Worker 23*a67afe4dSAndroid Build Coastguard Worker# Generously allow the TIFF file to occupy up to a quarter-gigabyte. 24*a67afe4dSAndroid Build Coastguard Worker# TODO: Reduce this limit to 64K and use file seeking for anything larger. 25*a67afe4dSAndroid Build Coastguard Worker_READ_DATA_SIZE_MAX = 256 * 1024 * 1024 26*a67afe4dSAndroid Build Coastguard Worker 27*a67afe4dSAndroid Build Coastguard Worker_TIFF_TAG_TYPES = { 28*a67afe4dSAndroid Build Coastguard Worker 1: "byte", 29*a67afe4dSAndroid Build Coastguard Worker 2: "ascii", 30*a67afe4dSAndroid Build Coastguard Worker 3: "short", 31*a67afe4dSAndroid Build Coastguard Worker 4: "long", 32*a67afe4dSAndroid Build Coastguard Worker 5: "rational", 33*a67afe4dSAndroid Build Coastguard Worker 6: "sbyte", 34*a67afe4dSAndroid Build Coastguard Worker 7: "undefined", 35*a67afe4dSAndroid Build Coastguard Worker 8: "sshort", 36*a67afe4dSAndroid Build Coastguard Worker 9: "slong", 37*a67afe4dSAndroid Build Coastguard Worker 10: "srational", 38*a67afe4dSAndroid Build Coastguard Worker 11: "float", 39*a67afe4dSAndroid Build Coastguard Worker 12: "double", 40*a67afe4dSAndroid Build Coastguard Worker} 41*a67afe4dSAndroid Build Coastguard Worker 42*a67afe4dSAndroid Build Coastguard Worker# See http://www.digitalpreservation.gov/formats/content/tiff_tags.shtml 43*a67afe4dSAndroid Build Coastguard Worker_TIFF_TAGS = { 44*a67afe4dSAndroid Build Coastguard Worker 0x00fe: "Subfile Type", 45*a67afe4dSAndroid Build Coastguard Worker 0x0100: "Width", 46*a67afe4dSAndroid Build Coastguard Worker 0x0101: "Height", 47*a67afe4dSAndroid Build Coastguard Worker 0x0102: "Bits per Sample", 48*a67afe4dSAndroid Build Coastguard Worker 0x0103: "Compression", 49*a67afe4dSAndroid Build Coastguard Worker 0x0106: "Photometric", 50*a67afe4dSAndroid Build Coastguard Worker 0x010d: "Document Name", 51*a67afe4dSAndroid Build Coastguard Worker 0x010e: "Image Description", 52*a67afe4dSAndroid Build Coastguard Worker 0x010f: "Make", 53*a67afe4dSAndroid Build Coastguard Worker 0x0110: "Model", 54*a67afe4dSAndroid Build Coastguard Worker 0x0111: "Strip Offsets", 55*a67afe4dSAndroid Build Coastguard Worker 0x0112: "Orientation", 56*a67afe4dSAndroid Build Coastguard Worker 0x0115: "Samples per Pixel", 57*a67afe4dSAndroid Build Coastguard Worker 0x0116: "Rows per Strip", 58*a67afe4dSAndroid Build Coastguard Worker 0x0117: "Strip Byte Counts", 59*a67afe4dSAndroid Build Coastguard Worker 0x0118: "Min Sample Value", 60*a67afe4dSAndroid Build Coastguard Worker 0x0119: "Max Sample Value", 61*a67afe4dSAndroid Build Coastguard Worker 0x011a: "X Resolution", 62*a67afe4dSAndroid Build Coastguard Worker 0x011b: "Y Resolution", 63*a67afe4dSAndroid Build Coastguard Worker 0x011c: "Planar Configuration", 64*a67afe4dSAndroid Build Coastguard Worker 0x011d: "Page Name", 65*a67afe4dSAndroid Build Coastguard Worker 0x011e: "X Position", 66*a67afe4dSAndroid Build Coastguard Worker 0x011f: "Y Position", 67*a67afe4dSAndroid Build Coastguard Worker 0x0128: "Resolution Unit", 68*a67afe4dSAndroid Build Coastguard Worker 0x0129: "Page Number", 69*a67afe4dSAndroid Build Coastguard Worker 0x0131: "Software", 70*a67afe4dSAndroid Build Coastguard Worker 0x0132: "Date Time", 71*a67afe4dSAndroid Build Coastguard Worker 0x013b: "Artist", 72*a67afe4dSAndroid Build Coastguard Worker 0x013c: "Host Computer", 73*a67afe4dSAndroid Build Coastguard Worker 0x013d: "Predictor", 74*a67afe4dSAndroid Build Coastguard Worker 0x013e: "White Point", 75*a67afe4dSAndroid Build Coastguard Worker 0x013f: "Primary Chromaticities", 76*a67afe4dSAndroid Build Coastguard Worker 0x0140: "Color Map", 77*a67afe4dSAndroid Build Coastguard Worker 0x0141: "Half-Tone Hints", 78*a67afe4dSAndroid Build Coastguard Worker 0x0142: "Tile Width", 79*a67afe4dSAndroid Build Coastguard Worker 0x0143: "Tile Length", 80*a67afe4dSAndroid Build Coastguard Worker 0x0144: "Tile Offsets", 81*a67afe4dSAndroid Build Coastguard Worker 0x0145: "Tile Byte Counts", 82*a67afe4dSAndroid Build Coastguard Worker 0x0211: "YCbCr Coefficients", 83*a67afe4dSAndroid Build Coastguard Worker 0x0212: "YCbCr Subsampling", 84*a67afe4dSAndroid Build Coastguard Worker 0x0213: "YCbCr Positioning", 85*a67afe4dSAndroid Build Coastguard Worker 0x0214: "Reference Black White", 86*a67afe4dSAndroid Build Coastguard Worker 0x022f: "Strip Row Counts", 87*a67afe4dSAndroid Build Coastguard Worker 0x02bc: "XMP", 88*a67afe4dSAndroid Build Coastguard Worker 0x8298: "Copyright", 89*a67afe4dSAndroid Build Coastguard Worker 0x83bb: "IPTC", 90*a67afe4dSAndroid Build Coastguard Worker 0x8769: "EXIF IFD", 91*a67afe4dSAndroid Build Coastguard Worker 0x8773: "ICC Profile", 92*a67afe4dSAndroid Build Coastguard Worker 0x8825: "GPS IFD", 93*a67afe4dSAndroid Build Coastguard Worker 0xa005: "Interoperability IFD", 94*a67afe4dSAndroid Build Coastguard Worker 0xc4a5: "Print IM", 95*a67afe4dSAndroid Build Coastguard Worker 96*a67afe4dSAndroid Build Coastguard Worker # EXIF IFD tags 97*a67afe4dSAndroid Build Coastguard Worker 0x829a: "Exposure Time", 98*a67afe4dSAndroid Build Coastguard Worker 0x829d: "F-Number", 99*a67afe4dSAndroid Build Coastguard Worker 0x8822: "Exposure Program", 100*a67afe4dSAndroid Build Coastguard Worker 0x8824: "Spectral Sensitivity", 101*a67afe4dSAndroid Build Coastguard Worker 0x8827: "ISO Speed Ratings", 102*a67afe4dSAndroid Build Coastguard Worker 0x8828: "OECF", 103*a67afe4dSAndroid Build Coastguard Worker 0x9000: "EXIF Version", 104*a67afe4dSAndroid Build Coastguard Worker 0x9003: "DateTime Original", 105*a67afe4dSAndroid Build Coastguard Worker 0x9004: "DateTime Digitized", 106*a67afe4dSAndroid Build Coastguard Worker 0x9101: "Components Configuration", 107*a67afe4dSAndroid Build Coastguard Worker 0x9102: "Compressed Bits Per Pixel", 108*a67afe4dSAndroid Build Coastguard Worker 0x9201: "Shutter Speed Value", 109*a67afe4dSAndroid Build Coastguard Worker 0x9202: "Aperture Value", 110*a67afe4dSAndroid Build Coastguard Worker 0x9203: "Brightness Value", 111*a67afe4dSAndroid Build Coastguard Worker 0x9204: "Exposure Bias Value", 112*a67afe4dSAndroid Build Coastguard Worker 0x9205: "Max Aperture Value", 113*a67afe4dSAndroid Build Coastguard Worker 0x9206: "Subject Distance", 114*a67afe4dSAndroid Build Coastguard Worker 0x9207: "Metering Mode", 115*a67afe4dSAndroid Build Coastguard Worker 0x9208: "Light Source", 116*a67afe4dSAndroid Build Coastguard Worker 0x9209: "Flash", 117*a67afe4dSAndroid Build Coastguard Worker 0x920a: "Focal Length", 118*a67afe4dSAndroid Build Coastguard Worker 0x9214: "Subject Area", 119*a67afe4dSAndroid Build Coastguard Worker 0x927c: "Maker Note", 120*a67afe4dSAndroid Build Coastguard Worker 0x9286: "User Comment", 121*a67afe4dSAndroid Build Coastguard Worker # ... TODO 122*a67afe4dSAndroid Build Coastguard Worker 0xa000: "Flashpix Version", 123*a67afe4dSAndroid Build Coastguard Worker 0xa001: "Color Space", 124*a67afe4dSAndroid Build Coastguard Worker 0xa002: "Pixel X Dimension", 125*a67afe4dSAndroid Build Coastguard Worker 0xa003: "Pixel Y Dimension", 126*a67afe4dSAndroid Build Coastguard Worker 0xa004: "Related Sound File", 127*a67afe4dSAndroid Build Coastguard Worker # ... TODO 128*a67afe4dSAndroid Build Coastguard Worker 129*a67afe4dSAndroid Build Coastguard Worker # GPS IFD tags 130*a67afe4dSAndroid Build Coastguard Worker # ... TODO 131*a67afe4dSAndroid Build Coastguard Worker} 132*a67afe4dSAndroid Build Coastguard Worker 133*a67afe4dSAndroid Build Coastguard Worker_TIFF_EXIF_IFD = 0x8769 134*a67afe4dSAndroid Build Coastguard Worker_GPS_IFD = 0x8825 135*a67afe4dSAndroid Build Coastguard Worker_INTEROPERABILITY_IFD = 0xa005 136*a67afe4dSAndroid Build Coastguard Worker 137*a67afe4dSAndroid Build Coastguard Worker 138*a67afe4dSAndroid Build Coastguard Workerclass ExifInfo: 139*a67afe4dSAndroid Build Coastguard Worker """EXIF reader and information lister.""" 140*a67afe4dSAndroid Build Coastguard Worker 141*a67afe4dSAndroid Build Coastguard Worker _endian = None 142*a67afe4dSAndroid Build Coastguard Worker _buffer = None 143*a67afe4dSAndroid Build Coastguard Worker _offset = 0 144*a67afe4dSAndroid Build Coastguard Worker _global_ifd_offset = 0 145*a67afe4dSAndroid Build Coastguard Worker _exif_ifd_offset = 0 146*a67afe4dSAndroid Build Coastguard Worker _gps_ifd_offset = 0 147*a67afe4dSAndroid Build Coastguard Worker _interoperability_ifd_offset = 0 148*a67afe4dSAndroid Build Coastguard Worker _hex = False 149*a67afe4dSAndroid Build Coastguard Worker 150*a67afe4dSAndroid Build Coastguard Worker def __init__(self, buffer, **kwargs): 151*a67afe4dSAndroid Build Coastguard Worker """Initialize the EXIF data reader.""" 152*a67afe4dSAndroid Build Coastguard Worker self._hex = kwargs.get("hex", False) 153*a67afe4dSAndroid Build Coastguard Worker self._verbose = kwargs.get("verbose", False) 154*a67afe4dSAndroid Build Coastguard Worker if not isinstance(buffer, bytes): 155*a67afe4dSAndroid Build Coastguard Worker raise RuntimeError("invalid EXIF data type") 156*a67afe4dSAndroid Build Coastguard Worker if buffer.startswith(b"MM\x00\x2a"): 157*a67afe4dSAndroid Build Coastguard Worker self._endian = "MM" 158*a67afe4dSAndroid Build Coastguard Worker elif buffer.startswith(b"II\x2a\x00"): 159*a67afe4dSAndroid Build Coastguard Worker self._endian = "II" 160*a67afe4dSAndroid Build Coastguard Worker else: 161*a67afe4dSAndroid Build Coastguard Worker raise RuntimeError("invalid EXIF header") 162*a67afe4dSAndroid Build Coastguard Worker self._buffer = buffer 163*a67afe4dSAndroid Build Coastguard Worker self._offset = 4 164*a67afe4dSAndroid Build Coastguard Worker self._global_ifd_offset = self._ui32() 165*a67afe4dSAndroid Build Coastguard Worker 166*a67afe4dSAndroid Build Coastguard Worker def endian(self): 167*a67afe4dSAndroid Build Coastguard Worker """Return the endianness of the EXIF data.""" 168*a67afe4dSAndroid Build Coastguard Worker return self._endian 169*a67afe4dSAndroid Build Coastguard Worker 170*a67afe4dSAndroid Build Coastguard Worker def _tags_for_ifd(self, ifd_offset): 171*a67afe4dSAndroid Build Coastguard Worker """Yield the tags found at the given TIFF IFD offset.""" 172*a67afe4dSAndroid Build Coastguard Worker if ifd_offset < 8: 173*a67afe4dSAndroid Build Coastguard Worker raise RuntimeError("invalid TIFF IFD offset") 174*a67afe4dSAndroid Build Coastguard Worker self._offset = ifd_offset 175*a67afe4dSAndroid Build Coastguard Worker ifd_size = self._ui16() 176*a67afe4dSAndroid Build Coastguard Worker for _ in range(0, ifd_size): 177*a67afe4dSAndroid Build Coastguard Worker tag_id = self._ui16() 178*a67afe4dSAndroid Build Coastguard Worker tag_type = self._ui16() 179*a67afe4dSAndroid Build Coastguard Worker count = self._ui32() 180*a67afe4dSAndroid Build Coastguard Worker value_or_offset = self._ui32() 181*a67afe4dSAndroid Build Coastguard Worker if self._endian == "MM": 182*a67afe4dSAndroid Build Coastguard Worker # FIXME: 183*a67afe4dSAndroid Build Coastguard Worker # value_or_offset requires a fixup under big-endian encoding. 184*a67afe4dSAndroid Build Coastguard Worker if tag_type == 2: 185*a67afe4dSAndroid Build Coastguard Worker # 2 --> "ascii" 186*a67afe4dSAndroid Build Coastguard Worker value_or_offset >>= 24 187*a67afe4dSAndroid Build Coastguard Worker elif tag_type == 3: 188*a67afe4dSAndroid Build Coastguard Worker # 3 --> "short" 189*a67afe4dSAndroid Build Coastguard Worker value_or_offset >>= 16 190*a67afe4dSAndroid Build Coastguard Worker else: 191*a67afe4dSAndroid Build Coastguard Worker # ... FIXME 192*a67afe4dSAndroid Build Coastguard Worker pass 193*a67afe4dSAndroid Build Coastguard Worker if count == 0: 194*a67afe4dSAndroid Build Coastguard Worker raise RuntimeError("unsupported count=0 in tag 0x%x" % tag_id) 195*a67afe4dSAndroid Build Coastguard Worker if tag_id == _TIFF_EXIF_IFD: 196*a67afe4dSAndroid Build Coastguard Worker if tag_type != 4: 197*a67afe4dSAndroid Build Coastguard Worker raise RuntimeError("incorrect tag type for EXIF IFD") 198*a67afe4dSAndroid Build Coastguard Worker self._exif_ifd_offset = value_or_offset 199*a67afe4dSAndroid Build Coastguard Worker elif tag_id == _GPS_IFD: 200*a67afe4dSAndroid Build Coastguard Worker if tag_type != 4: 201*a67afe4dSAndroid Build Coastguard Worker raise RuntimeError("incorrect tag type for GPS IFD") 202*a67afe4dSAndroid Build Coastguard Worker self._gps_ifd_offset = value_or_offset 203*a67afe4dSAndroid Build Coastguard Worker elif tag_id == _INTEROPERABILITY_IFD: 204*a67afe4dSAndroid Build Coastguard Worker if tag_type != 4: 205*a67afe4dSAndroid Build Coastguard Worker raise RuntimeError("incorrect tag type for Interop IFD") 206*a67afe4dSAndroid Build Coastguard Worker self._interoperability_ifd_offset = value_or_offset 207*a67afe4dSAndroid Build Coastguard Worker yield (tag_id, tag_type, count, value_or_offset) 208*a67afe4dSAndroid Build Coastguard Worker 209*a67afe4dSAndroid Build Coastguard Worker def tags(self): 210*a67afe4dSAndroid Build Coastguard Worker """Yield all TIFF/EXIF tags.""" 211*a67afe4dSAndroid Build Coastguard Worker if self._verbose: 212*a67afe4dSAndroid Build Coastguard Worker print("TIFF IFD : 0x%08x" % self._global_ifd_offset) 213*a67afe4dSAndroid Build Coastguard Worker for tag in self._tags_for_ifd(self._global_ifd_offset): 214*a67afe4dSAndroid Build Coastguard Worker yield tag 215*a67afe4dSAndroid Build Coastguard Worker if self._exif_ifd_offset > 0: 216*a67afe4dSAndroid Build Coastguard Worker if self._verbose: 217*a67afe4dSAndroid Build Coastguard Worker print("EXIF IFD : 0x%08x" % self._exif_ifd_offset) 218*a67afe4dSAndroid Build Coastguard Worker for tag in self._tags_for_ifd(self._exif_ifd_offset): 219*a67afe4dSAndroid Build Coastguard Worker yield tag 220*a67afe4dSAndroid Build Coastguard Worker if self._gps_ifd_offset > 0: 221*a67afe4dSAndroid Build Coastguard Worker if self._verbose: 222*a67afe4dSAndroid Build Coastguard Worker print("GPS IFD : 0x%08x" % self._gps_ifd_offset) 223*a67afe4dSAndroid Build Coastguard Worker for tag in self._tags_for_ifd(self._gps_ifd_offset): 224*a67afe4dSAndroid Build Coastguard Worker yield tag 225*a67afe4dSAndroid Build Coastguard Worker if self._interoperability_ifd_offset > 0: 226*a67afe4dSAndroid Build Coastguard Worker if self._verbose: 227*a67afe4dSAndroid Build Coastguard Worker print("Interoperability IFD : 0x%08x" % 228*a67afe4dSAndroid Build Coastguard Worker self._interoperability_ifd_offset) 229*a67afe4dSAndroid Build Coastguard Worker for tag in self._tags_for_ifd(self._interoperability_ifd_offset): 230*a67afe4dSAndroid Build Coastguard Worker yield tag 231*a67afe4dSAndroid Build Coastguard Worker 232*a67afe4dSAndroid Build Coastguard Worker def tagid2str(self, tag_id): 233*a67afe4dSAndroid Build Coastguard Worker """Return an informative string representation of a TIFF tag id.""" 234*a67afe4dSAndroid Build Coastguard Worker idstr = _TIFF_TAGS.get(tag_id, "[Unknown]") 235*a67afe4dSAndroid Build Coastguard Worker if self._hex: 236*a67afe4dSAndroid Build Coastguard Worker idnum = "0x%04x" % tag_id 237*a67afe4dSAndroid Build Coastguard Worker else: 238*a67afe4dSAndroid Build Coastguard Worker idnum = "%d" % tag_id 239*a67afe4dSAndroid Build Coastguard Worker return "%s (%s)" % (idstr, idnum) 240*a67afe4dSAndroid Build Coastguard Worker 241*a67afe4dSAndroid Build Coastguard Worker @staticmethod 242*a67afe4dSAndroid Build Coastguard Worker def tagtype2str(tag_type): 243*a67afe4dSAndroid Build Coastguard Worker """Return an informative string representation of a TIFF tag type.""" 244*a67afe4dSAndroid Build Coastguard Worker typestr = _TIFF_TAG_TYPES.get(tag_type, "[unknown]") 245*a67afe4dSAndroid Build Coastguard Worker return "%d:%s" % (tag_type, typestr) 246*a67afe4dSAndroid Build Coastguard Worker 247*a67afe4dSAndroid Build Coastguard Worker def tag2str(self, tag_id, tag_type, count, value_or_offset): 248*a67afe4dSAndroid Build Coastguard Worker """Return an informative string representation of a TIFF tag tuple.""" 249*a67afe4dSAndroid Build Coastguard Worker return "%s (type=%s) (count=%d) : 0x%08x" \ 250*a67afe4dSAndroid Build Coastguard Worker % (self.tagid2str(tag_id), self.tagtype2str(tag_type), count, 251*a67afe4dSAndroid Build Coastguard Worker value_or_offset) 252*a67afe4dSAndroid Build Coastguard Worker 253*a67afe4dSAndroid Build Coastguard Worker def _ui32(self): 254*a67afe4dSAndroid Build Coastguard Worker """Decode a 32-bit unsigned int found at the current offset; 255*a67afe4dSAndroid Build Coastguard Worker advance the offset by 4. 256*a67afe4dSAndroid Build Coastguard Worker """ 257*a67afe4dSAndroid Build Coastguard Worker if self._offset + 4 > len(self._buffer): 258*a67afe4dSAndroid Build Coastguard Worker raise RuntimeError("out-of-bounds uint32 access in EXIF") 259*a67afe4dSAndroid Build Coastguard Worker if self._endian == "MM": 260*a67afe4dSAndroid Build Coastguard Worker result = unpack_uint32be(self._buffer, self._offset) 261*a67afe4dSAndroid Build Coastguard Worker else: 262*a67afe4dSAndroid Build Coastguard Worker result = unpack_uint32le(self._buffer, self._offset) 263*a67afe4dSAndroid Build Coastguard Worker self._offset += 4 264*a67afe4dSAndroid Build Coastguard Worker return result 265*a67afe4dSAndroid Build Coastguard Worker 266*a67afe4dSAndroid Build Coastguard Worker def _ui16(self): 267*a67afe4dSAndroid Build Coastguard Worker """Decode a 16-bit unsigned int found at the current offset; 268*a67afe4dSAndroid Build Coastguard Worker advance the offset by 2. 269*a67afe4dSAndroid Build Coastguard Worker """ 270*a67afe4dSAndroid Build Coastguard Worker if self._offset + 2 > len(self._buffer): 271*a67afe4dSAndroid Build Coastguard Worker raise RuntimeError("out-of-bounds uint16 access in EXIF") 272*a67afe4dSAndroid Build Coastguard Worker if self._endian == "MM": 273*a67afe4dSAndroid Build Coastguard Worker result = unpack_uint16be(self._buffer, self._offset) 274*a67afe4dSAndroid Build Coastguard Worker else: 275*a67afe4dSAndroid Build Coastguard Worker result = unpack_uint16le(self._buffer, self._offset) 276*a67afe4dSAndroid Build Coastguard Worker self._offset += 2 277*a67afe4dSAndroid Build Coastguard Worker return result 278*a67afe4dSAndroid Build Coastguard Worker 279*a67afe4dSAndroid Build Coastguard Worker def _ui8(self): 280*a67afe4dSAndroid Build Coastguard Worker """Decode an 8-bit unsigned int found at the current offset; 281*a67afe4dSAndroid Build Coastguard Worker advance the offset by 1. 282*a67afe4dSAndroid Build Coastguard Worker """ 283*a67afe4dSAndroid Build Coastguard Worker if self._offset + 1 > len(self._buffer): 284*a67afe4dSAndroid Build Coastguard Worker raise RuntimeError("out-of-bounds uint8 access in EXIF") 285*a67afe4dSAndroid Build Coastguard Worker result = unpack_uint8(self._buffer, self._offset) 286*a67afe4dSAndroid Build Coastguard Worker self._offset += 1 287*a67afe4dSAndroid Build Coastguard Worker return result 288*a67afe4dSAndroid Build Coastguard Worker 289*a67afe4dSAndroid Build Coastguard Worker 290*a67afe4dSAndroid Build Coastguard Workerdef print_raw_exif_info(buffer, **kwargs): 291*a67afe4dSAndroid Build Coastguard Worker """Print the EXIF information found in a raw byte stream.""" 292*a67afe4dSAndroid Build Coastguard Worker lister = ExifInfo(buffer, **kwargs) 293*a67afe4dSAndroid Build Coastguard Worker print("EXIF (endian=%s)" % lister.endian()) 294*a67afe4dSAndroid Build Coastguard Worker for (tag_id, tag_type, count, value_or_offset) in lister.tags(): 295*a67afe4dSAndroid Build Coastguard Worker print(lister.tag2str(tag_id=tag_id, 296*a67afe4dSAndroid Build Coastguard Worker tag_type=tag_type, 297*a67afe4dSAndroid Build Coastguard Worker count=count, 298*a67afe4dSAndroid Build Coastguard Worker value_or_offset=value_or_offset)) 299*a67afe4dSAndroid Build Coastguard Worker 300*a67afe4dSAndroid Build Coastguard Worker 301*a67afe4dSAndroid Build Coastguard Workerif __name__ == "__main__": 302*a67afe4dSAndroid Build Coastguard Worker # For testing only. 303*a67afe4dSAndroid Build Coastguard Worker for arg in sys.argv[1:]: 304*a67afe4dSAndroid Build Coastguard Worker with open(arg, "rb") as test_stream: 305*a67afe4dSAndroid Build Coastguard Worker test_buffer = test_stream.read(_READ_DATA_SIZE_MAX) 306*a67afe4dSAndroid Build Coastguard Worker print_raw_exif_info(test_buffer, hex=True, verbose=True) 307