1use byteorder::{BigEndian, ByteOrder, LittleEndian};
7
8use crate::error::{Error, Result};
9use crate::tag::{Tag, TagGroup, TagId};
10use crate::tags::exif as exif_tags;
11use crate::value::Value;
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum ByteOrderMark {
16 LittleEndian,
17 BigEndian,
18}
19
20#[derive(Debug)]
22pub struct TiffHeader {
23 pub byte_order: ByteOrderMark,
24 pub ifd0_offset: u32,
25}
26
27#[derive(Debug)]
29struct IfdEntry {
30 tag: u16,
31 data_type: u16,
32 count: u32,
33 value_offset: u32,
34 inline_data: [u8; 4],
36}
37
38fn type_size(data_type: u16) -> Option<usize> {
40 match data_type {
41 1 => Some(1), 2 => Some(1), 3 => Some(2), 4 => Some(4), 5 => Some(8), 6 => Some(1), 7 => Some(1), 8 => Some(2), 9 => Some(4), 10 => Some(8), 11 => Some(4), 12 => Some(8), 13 => Some(4), _ => None,
55 }
56}
57
58pub fn parse_tiff_header(data: &[u8]) -> Result<TiffHeader> {
60 if data.len() < 8 {
61 return Err(Error::InvalidTiffHeader);
62 }
63
64 let byte_order = match (data[0], data[1]) {
65 (b'I', b'I') => ByteOrderMark::LittleEndian,
66 (b'M', b'M') => ByteOrderMark::BigEndian,
67 _ => return Err(Error::InvalidTiffHeader),
68 };
69
70 let magic = match byte_order {
71 ByteOrderMark::LittleEndian => LittleEndian::read_u16(&data[2..4]),
72 ByteOrderMark::BigEndian => BigEndian::read_u16(&data[2..4]),
73 };
74
75 if magic != 42 {
76 return Err(Error::InvalidTiffHeader);
77 }
78
79 let ifd0_offset = match byte_order {
80 ByteOrderMark::LittleEndian => LittleEndian::read_u32(&data[4..8]),
81 ByteOrderMark::BigEndian => BigEndian::read_u32(&data[4..8]),
82 };
83
84 Ok(TiffHeader {
85 byte_order,
86 ifd0_offset,
87 })
88}
89
90pub struct ExifReader;
92
93impl ExifReader {
94 pub fn read(data: &[u8]) -> Result<Vec<Tag>> {
96 let header = parse_tiff_header(data)?;
97 let mut tags = Vec::new();
98
99 Self::read_ifd(data, &header, header.ifd0_offset, "IFD0", &mut tags)?;
101
102 let make = tags
104 .iter()
105 .find(|t| t.name == "Make")
106 .map(|t| t.print_value.clone())
107 .unwrap_or_default();
108
109 let model = tags
110 .iter()
111 .find(|t| t.name == "Model")
112 .map(|t| t.print_value.clone())
113 .unwrap_or_default();
114
115 let make_and_model = if model.is_empty() { make.clone() } else { model };
117
118 let mn_info: Option<(usize, usize)> = {
121 let mut result = None;
123 Self::find_makernote(data, &header, &mut result);
124 result
125 };
126
127 if let Some((mn_offset, mn_size)) = mn_info {
128 let mn_tags = crate::metadata::makernotes::parse_makernotes(
129 data, mn_offset, mn_size, &make, &make_and_model, header.byte_order,
130 );
131 tags.retain(|t| t.name != "MakerNote");
133 tags.extend(mn_tags);
134 }
135
136 Ok(tags)
137 }
138
139 fn find_makernote(data: &[u8], header: &TiffHeader, result: &mut Option<(usize, usize)>) {
141 let ifd0_offset = header.ifd0_offset as usize;
143 if ifd0_offset + 2 > data.len() {
144 return;
145 }
146 let entry_count = read_u16(data, ifd0_offset, header.byte_order) as usize;
147 let entries_start = ifd0_offset + 2;
148
149 for i in 0..entry_count {
150 let eoff = entries_start + i * 12;
151 if eoff + 12 > data.len() { break; }
152 let tag = read_u16(data, eoff, header.byte_order);
153 if tag == 0x8769 {
154 let exif_offset = read_u32(data, eoff + 8, header.byte_order) as usize;
156 Self::find_makernote_in_ifd(data, header, exif_offset, result);
157 break;
158 }
159 }
160 }
161
162 fn find_makernote_in_ifd(data: &[u8], header: &TiffHeader, ifd_offset: usize, result: &mut Option<(usize, usize)>) {
163 if ifd_offset + 2 > data.len() {
164 return;
165 }
166 let entry_count = read_u16(data, ifd_offset, header.byte_order) as usize;
167 let entries_start = ifd_offset + 2;
168
169 for i in 0..entry_count {
170 let eoff = entries_start + i * 12;
171 if eoff + 12 > data.len() { break; }
172 let tag = read_u16(data, eoff, header.byte_order);
173 if tag == 0x927C {
174 let data_type = read_u16(data, eoff + 2, header.byte_order);
175 let count = read_u32(data, eoff + 4, header.byte_order) as usize;
176 let type_size = match data_type { 1 | 2 | 6 | 7 => 1, 3 | 8 => 2, 4 | 9 | 11 | 13 => 4, 5 | 10 | 12 => 8, _ => 1 };
177 let total_size = type_size * count;
178
179 if total_size <= 4 {
180 break;
182 }
183 let offset = read_u32(data, eoff + 8, header.byte_order) as usize;
184 if offset + total_size <= data.len() {
185 *result = Some((offset, total_size));
186 }
187 break;
188 }
189 }
190 }
191
192 fn read_ifd(
194 data: &[u8],
195 header: &TiffHeader,
196 offset: u32,
197 ifd_name: &str,
198 tags: &mut Vec<Tag>,
199 ) -> Result<Option<u32>> {
200 let offset = offset as usize;
201 if offset + 2 > data.len() {
202 return Err(Error::InvalidExif(format!(
203 "{} offset {} beyond data length {}",
204 ifd_name,
205 offset,
206 data.len()
207 )));
208 }
209
210 let entry_count = read_u16(data, offset, header.byte_order) as usize;
211 let entries_start = offset + 2;
212 let _entries_end = entries_start + entry_count * 12;
213
214 if entries_start + 12 > data.len() && entry_count > 0 {
216 return Err(Error::InvalidExif(format!(
217 "{} entries extend beyond data (need {}, have {})",
218 ifd_name,
219 entries_start + 12,
220 data.len()
221 )));
222 }
223 let entry_count = entry_count.min((data.len().saturating_sub(entries_start)) / 12);
225 let entries_end = entries_start + entry_count * 12;
226
227 for i in 0..entry_count {
228 let entry_offset = entries_start + i * 12;
229 let entry = parse_ifd_entry(data, entry_offset, header.byte_order);
230
231 match entry.tag {
233 0x8769 => {
234 let sub_offset = entry.value_offset;
236 if (sub_offset as usize) < data.len() {
237 let _ = Self::read_ifd(data, header, sub_offset, "ExifIFD", tags);
238 }
239 continue;
240 }
241 0x8825 => {
242 let sub_offset = entry.value_offset;
244 if (sub_offset as usize) < data.len() {
245 let _ = Self::read_ifd(data, header, sub_offset, "GPS", tags);
246 }
247 continue;
248 }
249 0xA005 => {
250 let sub_offset = entry.value_offset;
252 if (sub_offset as usize) < data.len() {
253 let _ = Self::read_ifd(data, header, sub_offset, "InteropIFD", tags);
254 }
255 continue;
256 }
257 0xC4A5 => {
259 let total_size = match entry.data_type {
260 1 | 2 | 6 | 7 => entry.count as usize,
261 _ => 0,
262 };
263 if total_size > 11 {
264 let off = entry.value_offset as usize;
265 if off + 11 <= data.len() && &data[off..off+7] == b"PrintIM" {
266 let ver = String::from_utf8_lossy(&data[off+7..off+11]).to_string();
267 tags.push(Tag {
268 id: TagId::Text("PrintIMVersion".into()),
269 name: "PrintIMVersion".into(),
270 description: "PrintIM Version".into(),
271 group: TagGroup { family0: "PrintIM".into(), family1: "PrintIM".into(), family2: "Printing".into() },
272 raw_value: Value::String(ver.clone()),
273 print_value: ver,
274 priority: 0,
275 });
276 }
277 }
278 continue; }
280 0x0006 if ifd_name == "GPS" => {
282 if let Some(val) = read_ifd_value(data, &entry, header.byte_order) {
283 if let Value::URational(0, 0) = val {
284 continue;
285 }
286 }
287 }
288 _ => {}
289 }
290
291 if let Some(mut value) = read_ifd_value(data, &entry, header.byte_order) {
292 if ifd_name == "GPS" && entry.tag == 0x0007 {
295 if let Value::List(ref mut items) = value {
296 for item in items.iter_mut() {
297 if matches!(item, Value::URational(0, 0)) {
298 *item = Value::URational(0, 1);
299 }
300 }
301 }
302 }
303 let tag_info = exif_tags::lookup(ifd_name, entry.tag);
304 let (name, description, family2) = match tag_info {
305 Some(info) => (
306 info.name.to_string(),
307 info.description.to_string(),
308 info.family2.to_string(),
309 ),
310 None => {
311 match exif_tags::lookup_generated(entry.tag) {
313 Some((n, d)) => (n.to_string(), d.to_string(), "Other".to_string()),
314 None => (
315 format!("Tag0x{:04X}", entry.tag),
316 format!("Unknown tag 0x{:04X}", entry.tag),
317 "Other".to_string(),
318 ),
319 }
320 }
321 };
322
323 let print_value =
324 exif_tags::print_conv(ifd_name, entry.tag, &value)
325 .or_else(|| {
326 value.as_u64()
328 .and_then(|v| crate::tags::print_conv_generated::print_conv_by_name(&name, v as i64))
329 .map(|s| s.to_string())
330 })
331 .unwrap_or_else(|| value.to_display_string());
332
333 tags.push(Tag {
334 id: TagId::Numeric(entry.tag),
335 name,
336 description,
337 group: TagGroup {
338 family0: "EXIF".to_string(),
339 family1: ifd_name.to_string(),
340 family2,
341 },
342 raw_value: value,
343 print_value,
344 priority: 0,
345 });
346 }
347 }
348
349 let next_ifd_offset = if entries_end + 4 <= data.len() {
351 read_u32(data, entries_end, header.byte_order)
352 } else { 0 };
353 if next_ifd_offset != 0 && ifd_name == "IFD0" {
354 let _ = Self::read_ifd(data, header, next_ifd_offset, "IFD1", tags);
356
357 let thumb_offset = tags.iter()
359 .find(|t| t.name == "ThumbnailOffset" && t.group.family1 == "IFD1")
360 .and_then(|t| t.raw_value.as_u64());
361 let thumb_length = tags.iter()
362 .find(|t| t.name == "ThumbnailLength" && t.group.family1 == "IFD1")
363 .and_then(|t| t.raw_value.as_u64());
364
365 if let (Some(off), Some(len)) = (thumb_offset, thumb_length) {
366 let off = off as usize;
367 let len = len as usize;
368 if off + len <= data.len() && len > 0 {
369 tags.push(Tag {
370 id: TagId::Text("ThumbnailImage".into()),
371 name: "ThumbnailImage".into(),
372 description: "Thumbnail Image".into(),
373 group: TagGroup { family0: "EXIF".into(), family1: "IFD1".into(), family2: "Image".into() },
374 raw_value: Value::Binary(data[off..off+len].to_vec()),
375 print_value: format!("(Binary data {} bytes)", len),
376 priority: 0,
377 });
378 }
379 }
380 }
381
382 Ok(if next_ifd_offset != 0 {
383 Some(next_ifd_offset)
384 } else {
385 None
386 })
387 }
388}
389
390fn parse_ifd_entry(data: &[u8], offset: usize, byte_order: ByteOrderMark) -> IfdEntry {
391 let tag = read_u16(data, offset, byte_order);
392 let data_type = read_u16(data, offset + 2, byte_order);
393 let count = read_u32(data, offset + 4, byte_order);
394 let value_offset = read_u32(data, offset + 8, byte_order);
395 let mut inline_data = [0u8; 4];
396 inline_data.copy_from_slice(&data[offset + 8..offset + 12]);
397
398 IfdEntry {
399 tag,
400 data_type,
401 count,
402 value_offset,
403 inline_data,
404 }
405}
406
407fn read_ifd_value(data: &[u8], entry: &IfdEntry, byte_order: ByteOrderMark) -> Option<Value> {
408 let elem_size = type_size(entry.data_type)?;
409 let total_size = elem_size * entry.count as usize;
410
411 let value_data = if total_size <= 4 {
412 &entry.inline_data[..total_size]
413 } else {
414 let offset = entry.value_offset as usize;
415 if offset + total_size > data.len() {
416 return None;
417 }
418 &data[offset..offset + total_size]
419 };
420
421 match entry.data_type {
422 1 => {
424 if entry.count == 1 {
425 Some(Value::U8(value_data[0]))
426 } else {
427 Some(Value::List(value_data.iter().map(|&b| Value::U8(b)).collect()))
428 }
429 }
430 2 => {
432 let s = String::from_utf8_lossy(value_data);
433 Some(Value::String(s.trim_end_matches('\0').to_string()))
434 }
435 3 => {
437 if entry.count == 1 {
438 Some(Value::U16(read_u16(value_data, 0, byte_order)))
439 } else {
440 let vals: Vec<Value> = (0..entry.count as usize)
441 .map(|i| Value::U16(read_u16(value_data, i * 2, byte_order)))
442 .collect();
443 Some(Value::List(vals))
444 }
445 }
446 4 | 13 => {
448 if entry.count == 1 {
449 Some(Value::U32(read_u32(value_data, 0, byte_order)))
450 } else {
451 let vals: Vec<Value> = (0..entry.count as usize)
452 .map(|i| Value::U32(read_u32(value_data, i * 4, byte_order)))
453 .collect();
454 Some(Value::List(vals))
455 }
456 }
457 5 => {
459 if entry.count == 1 {
460 let n = read_u32(value_data, 0, byte_order);
461 let d = read_u32(value_data, 4, byte_order);
462 Some(Value::URational(n, d))
463 } else {
464 let vals: Vec<Value> = (0..entry.count as usize)
465 .map(|i| {
466 let n = read_u32(value_data, i * 8, byte_order);
467 let d = read_u32(value_data, i * 8 + 4, byte_order);
468 Value::URational(n, d)
469 })
470 .collect();
471 Some(Value::List(vals))
472 }
473 }
474 6 => {
476 if entry.count == 1 {
477 Some(Value::I16(value_data[0] as i8 as i16))
478 } else {
479 let vals: Vec<Value> = value_data
480 .iter()
481 .map(|&b| Value::I16(b as i8 as i16))
482 .collect();
483 Some(Value::List(vals))
484 }
485 }
486 7 => Some(Value::Undefined(value_data.to_vec())),
488 8 => {
490 if entry.count == 1 {
491 Some(Value::I16(read_i16(value_data, 0, byte_order)))
492 } else {
493 let vals: Vec<Value> = (0..entry.count as usize)
494 .map(|i| Value::I16(read_i16(value_data, i * 2, byte_order)))
495 .collect();
496 Some(Value::List(vals))
497 }
498 }
499 9 => {
501 if entry.count == 1 {
502 Some(Value::I32(read_i32(value_data, 0, byte_order)))
503 } else {
504 let vals: Vec<Value> = (0..entry.count as usize)
505 .map(|i| Value::I32(read_i32(value_data, i * 4, byte_order)))
506 .collect();
507 Some(Value::List(vals))
508 }
509 }
510 10 => {
512 if entry.count == 1 {
513 let n = read_i32(value_data, 0, byte_order);
514 let d = read_i32(value_data, 4, byte_order);
515 Some(Value::IRational(n, d))
516 } else {
517 let vals: Vec<Value> = (0..entry.count as usize)
518 .map(|i| {
519 let n = read_i32(value_data, i * 8, byte_order);
520 let d = read_i32(value_data, i * 8 + 4, byte_order);
521 Value::IRational(n, d)
522 })
523 .collect();
524 Some(Value::List(vals))
525 }
526 }
527 11 => {
529 if entry.count == 1 {
530 let bits = read_u32(value_data, 0, byte_order);
531 Some(Value::F32(f32::from_bits(bits)))
532 } else {
533 let vals: Vec<Value> = (0..entry.count as usize)
534 .map(|i| {
535 let bits = read_u32(value_data, i * 4, byte_order);
536 Value::F32(f32::from_bits(bits))
537 })
538 .collect();
539 Some(Value::List(vals))
540 }
541 }
542 12 => {
544 if entry.count == 1 {
545 let bits = read_u64(value_data, 0, byte_order);
546 Some(Value::F64(f64::from_bits(bits)))
547 } else {
548 let vals: Vec<Value> = (0..entry.count as usize)
549 .map(|i| {
550 let bits = read_u64(value_data, i * 8, byte_order);
551 Value::F64(f64::from_bits(bits))
552 })
553 .collect();
554 Some(Value::List(vals))
555 }
556 }
557 _ => None,
558 }
559}
560
561fn read_u16(data: &[u8], offset: usize, bo: ByteOrderMark) -> u16 {
563 match bo {
564 ByteOrderMark::LittleEndian => LittleEndian::read_u16(&data[offset..]),
565 ByteOrderMark::BigEndian => BigEndian::read_u16(&data[offset..]),
566 }
567}
568
569fn read_u32(data: &[u8], offset: usize, bo: ByteOrderMark) -> u32 {
570 match bo {
571 ByteOrderMark::LittleEndian => LittleEndian::read_u32(&data[offset..]),
572 ByteOrderMark::BigEndian => BigEndian::read_u32(&data[offset..]),
573 }
574}
575
576fn read_u64(data: &[u8], offset: usize, bo: ByteOrderMark) -> u64 {
577 match bo {
578 ByteOrderMark::LittleEndian => LittleEndian::read_u64(&data[offset..]),
579 ByteOrderMark::BigEndian => BigEndian::read_u64(&data[offset..]),
580 }
581}
582
583fn read_i16(data: &[u8], offset: usize, bo: ByteOrderMark) -> i16 {
584 match bo {
585 ByteOrderMark::LittleEndian => LittleEndian::read_i16(&data[offset..]),
586 ByteOrderMark::BigEndian => BigEndian::read_i16(&data[offset..]),
587 }
588}
589
590fn read_i32(data: &[u8], offset: usize, bo: ByteOrderMark) -> i32 {
591 match bo {
592 ByteOrderMark::LittleEndian => LittleEndian::read_i32(&data[offset..]),
593 ByteOrderMark::BigEndian => BigEndian::read_i32(&data[offset..]),
594 }
595}