0
# Data Structures
1
2
Classes and objects for representing EXIF metadata, including individual tags, file headers, and specialized data types. These structures provide access to raw tag values, metadata, and processing capabilities.
3
4
## Capabilities
5
6
### IfdTag Class
7
8
Represents an individual EXIF tag with its value and associated metadata.
9
10
```python { .api }
11
class IfdTag:
12
"""
13
Represents a single EXIF tag with value and metadata.
14
15
This class encapsulates all information about an EXIF tag including
16
its printable representation, raw values, type information, and
17
file location details.
18
"""
19
20
def __init__(self, printable, tag, field_type, values, field_offset, field_length):
21
"""
22
Initialize an IfdTag instance.
23
24
Parameters:
25
- printable: str, human-readable representation of tag value
26
- tag: int, tag ID number (hex identifier)
27
- field_type: int, field type as index into FIELD_TYPES
28
- values: str or list, raw tag values (string or array of data items)
29
- field_offset: int, offset of field start in bytes from IFD beginning
30
- field_length: int, length of data field in bytes
31
"""
32
33
printable: str
34
"""Human-readable version of the tag value."""
35
36
tag: int
37
"""Tag ID number (hexadecimal identifier)."""
38
39
field_type: int
40
"""Field type as index into FIELD_TYPES tuple."""
41
42
values: Union[str, List[Any]]
43
"""Raw tag values - either string or array of data items."""
44
45
field_offset: int
46
"""Offset of field start in bytes from beginning of IFD."""
47
48
field_length: int
49
"""Length of data field in bytes."""
50
51
def __str__(self) -> str:
52
"""Return printable representation of tag value."""
53
54
def __repr__(self) -> str:
55
"""Return detailed string representation for debugging."""
56
```
57
58
### ExifHeader Class
59
60
Handles EXIF header parsing and processing for extracting metadata from image files.
61
62
```python { .api }
63
class ExifHeader:
64
"""
65
Handle EXIF header parsing and tag extraction.
66
67
This class manages the low-level details of reading EXIF data from
68
image files, parsing IFD structures, and extracting individual tags.
69
"""
70
71
def __init__(self, file_handle, endian, offset, fake_exif, strict, debug=False, detailed=True, truncate_tags=True):
72
"""
73
Initialize ExifHeader for processing.
74
75
Parameters:
76
- file_handle: file object, open binary file handle
77
- endian: str, byte order ('I' for Intel/little-endian, 'M' for Motorola/big-endian)
78
- offset: int, offset to start of EXIF data in file
79
- fake_exif: int, flag indicating synthetic EXIF header
80
- strict: bool, strict processing mode
81
- debug: bool, enable debug output
82
- detailed: bool, process MakerNotes and thumbnails
83
- truncate_tags: bool, truncate long tag values
84
"""
85
86
tags: Dict[str, IfdTag]
87
"""Dictionary of extracted EXIF tags."""
88
89
def s2n(self, offset, length, signed=False) -> int:
90
"""
91
Convert slice to integer based on endian and sign flags.
92
93
Parameters:
94
- offset: int, offset relative to EXIF data start
95
- length: int, number of bytes to read (1, 2, 4, or 8)
96
- signed: bool, whether to interpret as signed integer
97
98
Returns:
99
int: Converted integer value
100
"""
101
102
def n2s(self, offset, length) -> str:
103
"""
104
Convert integer offset to string representation.
105
106
Parameters:
107
- offset: int, integer value to convert
108
- length: int, number of bytes for output
109
110
Returns:
111
str: String representation
112
"""
113
114
def list_ifd(self) -> List[int]:
115
"""
116
Return list of IFD (Image File Directory) offsets in header.
117
118
Returns:
119
List[int]: List of IFD offset positions
120
"""
121
122
def dump_ifd(self, ifd, ifd_name, tag_dict=None, relative=0, stop_tag='UNDEF'):
123
"""
124
Process and extract entries from an IFD.
125
126
Parameters:
127
- ifd: int, IFD offset position
128
- ifd_name: str, name for this IFD (e.g., 'Image', 'EXIF', 'GPS')
129
- tag_dict: dict, tag definitions dictionary (defaults to EXIF_TAGS)
130
- relative: int, relative addressing flag for MakerNotes
131
- stop_tag: str, tag name to stop processing at
132
"""
133
134
def extract_tiff_thumbnail(self, thumb_ifd):
135
"""
136
Extract uncompressed TIFF thumbnail from thumbnail IFD.
137
138
Parameters:
139
- thumb_ifd: int, thumbnail IFD offset
140
"""
141
142
def extract_jpeg_thumbnail(self):
143
"""
144
Extract JPEG thumbnail data from EXIF.
145
146
Stores thumbnail data in tags['JPEGThumbnail'] if found.
147
"""
148
149
def decode_maker_note(self):
150
"""
151
Decode camera-specific MakerNote formats.
152
153
Supports MakerNotes from Canon, Nikon, Olympus, Fujifilm,
154
Casio, and Apple cameras with format-specific parsing.
155
"""
156
157
def parse_xmp(self, xmp_string):
158
"""
159
Parse Adobe XMP (Extensible Metadata Platform) data.
160
161
Parameters:
162
- xmp_string: bytes, XMP XML data
163
"""
164
```
165
166
### Ratio Class
167
168
Specialized numeric type for representing rational numbers commonly used in EXIF data.
169
170
```python { .api }
171
class Ratio:
172
"""
173
Ratio object for EXIF rational number values.
174
175
Inherits from fractions.Fraction to provide rational number
176
arithmetic with EXIF-specific display formatting.
177
"""
178
179
def __new__(cls, numerator=0, denominator=None):
180
"""
181
Create new Ratio instance.
182
183
Parameters:
184
- numerator: int, numerator value
185
- denominator: int, denominator value (None for whole numbers)
186
187
Returns:
188
Ratio: New ratio instance
189
"""
190
191
@property
192
def num(self) -> int:
193
"""Get numerator value."""
194
195
@property
196
def den(self) -> int:
197
"""Get denominator value."""
198
199
def decimal(self) -> float:
200
"""
201
Convert ratio to decimal floating point.
202
203
Returns:
204
float: Decimal representation of the ratio
205
"""
206
207
def __repr__(self) -> str:
208
"""Return string representation of ratio."""
209
```
210
211
## Usage Examples
212
213
### Working with IfdTag Objects
214
215
```python
216
import exifread
217
218
with open('photo.jpg', 'rb') as f:
219
tags = exifread.process_file(f)
220
221
# Access tag properties
222
if 'EXIF FocalLength' in tags:
223
focal_length_tag = tags['EXIF FocalLength']
224
225
print(f"Printable value: {focal_length_tag.printable}")
226
print(f"Raw values: {focal_length_tag.values}")
227
print(f"Tag ID: 0x{focal_length_tag.tag:04X}")
228
print(f"Field type: {focal_length_tag.field_type}")
229
print(f"File offset: {focal_length_tag.field_offset}")
230
print(f"Data length: {focal_length_tag.field_length}")
231
```
232
233
### Working with Ratio Values
234
235
```python
236
import exifread
237
238
with open('photo.jpg', 'rb') as f:
239
tags = exifread.process_file(f)
240
241
# Many EXIF values are ratios (fractions)
242
if 'EXIF ExposureTime' in tags:
243
exposure_tag = tags['EXIF ExposureTime']
244
245
# Access as Ratio object
246
if hasattr(exposure_tag.values[0], 'decimal'):
247
ratio_value = exposure_tag.values[0]
248
print(f"Exposure time: {ratio_value.num}/{ratio_value.den}")
249
print(f"Decimal seconds: {ratio_value.decimal()}")
250
```
251
252
### Accessing Thumbnail Data
253
254
```python
255
import exifread
256
257
with open('photo.jpg', 'rb') as f:
258
tags = exifread.process_file(f)
259
260
# Check for embedded thumbnails
261
if 'JPEGThumbnail' in tags:
262
thumbnail_data = tags['JPEGThumbnail']
263
print(f"JPEG thumbnail found, size: {len(thumbnail_data)} bytes")
264
265
# Save thumbnail to file
266
with open('thumbnail.jpg', 'wb') as thumb_file:
267
thumb_file.write(thumbnail_data)
268
269
if 'TIFFThumbnail' in tags:
270
tiff_thumbnail = tags['TIFFThumbnail']
271
print(f"TIFF thumbnail found, size: {len(tiff_thumbnail)} bytes")
272
```
273
274
### Iterating Through Tag Categories
275
276
```python
277
import exifread
278
279
with open('photo.jpg', 'rb') as f:
280
tags = exifread.process_file(f)
281
282
# Organize tags by category
283
image_tags = {}
284
exif_tags = {}
285
gps_tags = {}
286
makernote_tags = {}
287
288
for tag_name, tag_value in tags.items():
289
if tag_name.startswith('Image '):
290
image_tags[tag_name] = tag_value
291
elif tag_name.startswith('EXIF '):
292
exif_tags[tag_name] = tag_value
293
elif tag_name.startswith('GPS '):
294
gps_tags[tag_name] = tag_value
295
elif tag_name.startswith('MakerNote '):
296
makernote_tags[tag_name] = tag_value
297
298
print(f"Image tags: {len(image_tags)}")
299
print(f"EXIF tags: {len(exif_tags)}")
300
print(f"GPS tags: {len(gps_tags)}")
301
print(f"MakerNote tags: {len(makernote_tags)}")
302
```