xref: /aosp_15_r20/external/libpng/contrib/pngexif/exifinfo.py (revision a67afe4df73cf47866eedc69947994b8ff839aba)
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