1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
use super::rational::*;
use std::result::Result;

/// Top-level structure that contains all parsed metadata inside an image
pub struct ExifData {
	/// MIME type of the parsed image. It may be "image/jpeg", "image/tiff", or empty if unrecognized.
	pub mime: String,
	/// Collection of EXIF entries found in the image
	pub entries: Vec<ExifEntry>,
}

/// Possible fatal errors that may happen when an image is parsed.
#[derive(Copy, Clone)]
pub enum ExifErrorKind {
	FileOpenError,
	FileSeekError,
	FileReadError,
	FileTypeUnknown,
	JpegWithoutExif,
	TiffTruncated,
	TiffBadPreamble,
	IfdTruncated,
	ExifIfdTruncated,
	ExifIfdEntryNotFound,
}

/// EXIF parsing error type
pub struct ExifError {
	/// The general kind of the error that aborted the parsing
	pub kind: ExifErrorKind,
	/// Extra context info about the error, when available
	pub extra: String
}

/// Structure that represents a parsed IFD entry of a TIFF image
#[derive(Clone)]
pub struct IfdEntry {
	/// Namespace of the entry. Standard is a tag found in normal TIFF IFD structure,
	/// other namespaces are entries found e.g. within MarkerNote blobs that are
	/// manufacturer-specific.
	pub namespace: Namespace,
	/// IFD tag value, may or not be an EXIF tag
	pub tag: u16,
	/// IFD data format
	pub format: IfdFormat,
	/// Number of items, each one in the data format specified by format
	pub count: u32,
	/// Raw data as a vector of bytes. Length is sizeof(format) * count.
	/// Depending on its size, it came from different parts of the image file.
	pub data: Vec<u8>,
	/// Raw data contained within the IFD structure. If count * sizeof(format) >= 4,
	/// this item contains the offset where the actual data can be found
	pub ifd_data: Vec<u8>,
	/// Raw data contained outside of the IFD structure and pointed by ifd_data,
	/// if data would not fit within the IFD structure
	pub ext_data: Vec<u8>,
	/// If true, integer and offset formats must be parsed from raw data as little-endian.
	/// If false, integer and offset formats must be parsed from raw data as big-endian.
	///
	/// It is important to have 'endianess' per IFD entry, because some manufacturer-specific
	/// entries may have fixed endianess (regardeless of TIFF container's general endianess).
	pub le: bool,
}

/// Enumeration that represent EXIF tag namespaces. Namespaces exist to
/// accomodate future parsing of the manufacturer-specific tags embedded within
/// the MarkerNote tag.
#[derive(Copy, Clone, PartialEq)]
pub enum Namespace {
	Standard = 0x0000,
	Nikon = 0x0001,
	Canon = 0x0002,
}

/// Enumeration that represents recognized EXIF tags found in TIFF IFDs.
///
/// Items can be cast to u32 in order to get the namespace (most significant word)
/// and tag code (least significant word). The tag code matches the Exif, or the
/// Makernote standard, depending on the namespace that the tag belongs to.
///
/// On the other hand, the namespace code is arbitrary, it only matches
/// the `Namespace` enumeration. The namespace is 0 for standard Exif tags.
/// The non-standard namespaces exist to accomodate future parsing of the
/// MarkerNote tag, that contains embedded manufacturer-specific tags.
#[derive(Copy, Clone, PartialEq)]
pub enum ExifTag {
	/// Tag not recognized are partially parsed. The client may still try to interpret
	/// the tag by reading into the IfdFormat structure.
	UnknownToMe = 0x0000ffff,
	ImageDescription = 0x0000010e,
	Make = 0x0000010f,
	Model = 0x00000110,
	Orientation = 0x00000112,
	XResolution = 0x0000011a,
	YResolution = 0x0000011b,
	ResolutionUnit = 0x00000128,
	Software = 0x00000131,
	DateTime = 0x00000132,
	HostComputer = 0x0000013c,
	WhitePoint = 0x0000013e,
	PrimaryChromaticities = 0x0000013f,
	YCbCrCoefficients = 0x00000211,
	ReferenceBlackWhite = 0x00000214,
	Copyright = 0x00008298,
	ExifOffset = 0x00008769,
	GPSOffset = 0x00008825,

	ExposureTime = 0x0000829a,
	FNumber = 0x0000829d,
	ExposureProgram = 0x00008822,
	SpectralSensitivity = 0x00008824,
	ISOSpeedRatings = 0x00008827,
	OECF = 0x00008828,
	ExifVersion = 0x00009000,
	DateTimeOriginal = 0x00009003,
	DateTimeDigitized = 0x00009004,
	ShutterSpeedValue = 0x00009201,
	ApertureValue = 0x00009202,
	BrightnessValue = 0x00009203,
	ExposureBiasValue = 0x00009204,
	MaxApertureValue = 0x00009205,
	SubjectDistance = 0x00009206,
	MeteringMode = 0x00009207,
	LightSource = 0x00009208,
	Flash = 0x00009209,
	FocalLength = 0x0000920a,
	SubjectArea = 0x00009214,
	MakerNote = 0x0000927c,
	UserComment = 0x00009286,
	FlashPixVersion = 0x0000a000,
	ColorSpace = 0x0000a001,
	RelatedSoundFile = 0x0000a004,
	FlashEnergy = 0x0000a20b,
	FocalPlaneXResolution = 0x0000a20e,
	FocalPlaneYResolution = 0x0000a20f,
	FocalPlaneResolutionUnit = 0x0000a210,
	SubjectLocation = 0x0000a214,
	ExposureIndex = 0x0000a215,
	SensingMethod = 0x0000a217,
	FileSource = 0x0000a300,
	SceneType = 0x0000a301,
	CFAPattern = 0x0000a302,
	CustomRendered = 0x0000a401,
	ExposureMode = 0x0000a402,
	WhiteBalanceMode = 0x0000a403,
	DigitalZoomRatio = 0x0000a404,
	FocalLengthIn35mmFilm = 0x0000a405,
	SceneCaptureType = 0x0000a406,
	GainControl = 0x0000a407,
	Contrast = 0x0000a408,
	Saturation = 0x0000a409,
	Sharpness = 0x0000a40a,
	DeviceSettingDescription = 0x0000a40b,
	SubjectDistanceRange = 0x0000a40c,
	ImageUniqueID = 0x0000a420,
	LensSpecification = 0x0000a432,
	LensMake = 0x0000a433,
	LensModel = 0x0000a434,
		
	GPSVersionID = 0x00000,
	GPSLatitudeRef = 0x00001,
	GPSLatitude = 0x00002,
	GPSLongitudeRef = 0x00003,
	GPSLongitude = 0x00004,
	GPSAltitudeRef = 0x00005,
	GPSAltitude = 0x00006,
	GPSTimeStamp = 0x00007,
	GPSSatellites = 0x00008,
	GPSStatus = 0x00009,
	GPSMeasureMode = 0x0000a,
	GPSDOP = 0x0000b,
	GPSSpeedRef = 0x0000c,
	GPSSpeed = 0x0000d,
	GPSTrackRef = 0x0000e,
	GPSTrack = 0x0000f,
	GPSImgDirectionRef = 0x000010,
	GPSImgDirection = 0x000011,
	GPSMapDatum = 0x000012,
	GPSDestLatitudeRef = 0x000013,
	GPSDestLatitude = 0x000014,
	GPSDestLongitudeRef = 0x000015,
	GPSDestLongitude = 0x000016,
	GPSDestBearingRef = 0x000017,
	GPSDestBearing = 0x000018,
	GPSDestDistanceRef = 0x000019,
	GPSDestDistance = 0x00001a,
	GPSProcessingMethod = 0x00001b,
	GPSAreaInformation = 0x00001c,
	GPSDateStamp = 0x00001d,
	GPSDifferential = 0x00001e,
}

/// Enumeration that represents the possible data formats of an IFD entry.
///
/// Any enumeration item can be cast to u16 to get the low-level format code
/// as defined by the TIFF format.
#[derive(Copy, Clone, PartialEq)]
pub enum IfdFormat {
	Unknown = 0,
	U8 = 1,
	Ascii = 2,
	U16 = 3,
	U32 = 4,
	URational = 5,
	I8 = 6,
	Undefined = 7, // u8
	I16 = 8,
	I32 = 9,
	IRational = 10,
	F32 = 11,
	F64 = 12,
}

/// Structure that represents a parsed EXIF tag.
#[derive(Clone)]
pub struct ExifEntry {
	/// Namespace of the tag. If Standard (0x0000), it is an EXIF tag defined in the
	/// official standard. Other namespaces accomodate manufacturer-specific tags that
	/// may be embedded in MarkerNote blob tag.
	pub namespace: Namespace,
	/// Low-level IFD entry that contains the EXIF tag. The client may look into this
	/// structure to get tag's raw data, or to parse the tag herself if `tag` is `UnknownToMe`.
	pub ifd: IfdEntry,
	/// EXIF tag type as an enumeration. If `UnknownToMe`, the crate did not know the
	/// tag in detail, and parsing will be incomplete. The client may read into
	/// `ifd` to discover more about the unparsed tag.
	pub tag: ExifTag,
	/// EXIF tag value as an enumeration. Behaves as a "variant" value
	pub value: TagValue,
	/// Unit of the value, if applicable. If tag is `UnknownToMe`, unit will be empty.
	/// If the tag has been parsed and it is indeed unitless, it will be `"none"`.
	///
	/// Note that
	/// unit refers to the contents of `value`, not to the readable string. For example,
	/// a GPS latitude is a triplet of rational values, so unit is D/M/S, even though
	/// `value_more_readable` contains a single string with all three parts
	/// combined.
	pub unit: String,
	/// Human-readable name of the `tag`, for debugging and listing purposes
	pub tag_readable: String,
	/// Human-readable, but simple, version of `value`.
	/// Enumerations or tuples are not interpreted nor combined. This member contains a
	/// correct data representation even if tag is `UnknownToMe`.
	pub value_readable: String,
	/// Human-readable and "pretty" version of `value`.
	/// Enumerations and tuples are interpreted and combined. If `value`
	/// has a unit, it is also added. 
	/// If tag is `UnknownToMe`,
	/// this member contains the same string as `value_readable`.
	pub value_more_readable: String,
}

/// Tag value enumeration. It works as a variant type. Each value is
/// actually a vector because many EXIF tags are collections of values.
/// Exif tags with single values are represented as single-item vectors.
#[derive(Clone)]
pub enum TagValue {
	/// Array of unsigned byte integers
	U8(Vec<u8>),
	/// ASCII string. (The standard specifies 7-bit ASCII, but this parser accepts UTF-8 strings.)
	Ascii(String),
	U16(Vec<u16>),
	U32(Vec<u32>),
	/// Array of `URational` structures (tuples with integer numerator and denominator)
	URational(Vec<URational>),
	I8(Vec<i8>),
	/// Array of bytes with opaque internal structure. Used by manufacturer-specific
	/// tags, SIG-specific tags, tags that contain Unicode (UCS-2) or Japanese (JIS)
	/// strings (i.e. strings that are not 7-bit-clean), tags that contain 
	/// dissimilar or variant types, etc.
	///
	/// This item has a "little endian"
	/// boolean parameter that reports the whole TIFF's endianness. 
	/// Any sort of internal structure that is sensitive to endianess
	/// should be interpreted accordignly to this parameter (true=LE, false=BE).
	Undefined(Vec<u8>, bool),
	I16(Vec<i16>),
	I32(Vec<i32>),
	/// Array of `IRational` structures (tuples with signed integer numerator and denominator)
	IRational(Vec<IRational>),
	/// Array of IEEE 754 floating-points
	F32(Vec<f32>),
	/// Array of IEEE 754 floating-points
	F64(Vec<f64>),
	/// Array of bytes with unknown internal structure.
	/// This is different from `Undefined` because `Undefined` is actually a specified
	/// format, while `Unknown` is an unexpected format type. A tag of `Unknown` format
	/// is most likely a corrupted tag.
	///
	/// This variant has a "little endian"
	/// boolean parameter that reports the whole TIFF's endianness. 
	/// Any sort of internal structure that is sensitive to endianess
	/// should be interpreted accordignly to this parameter (true=LE, false=BE).
	Unknown(Vec<u8>, bool),
	/// Type that could not be parsed due to some sort of error (e.g. buffer too
	/// short for the count and type size). Variant contains raw data, LE/BE,
	/// format (as u16) and count.
	Invalid(Vec<u8>, bool, u16, u32)
}

/// Type returned by image file parsing
pub type ExifResult = Result<ExifData, ExifError>;

/// Type resturned by lower-level parsing functions
pub type ExifEntryResult = Result<Vec<ExifEntry>, ExifError>;