1use crate::bytes::{ByteOrder, ReadAt};
13use crate::{Error, Result};
14
15pub mod types {
17 pub const BYTE: u16 = 1;
18 pub const ASCII: u16 = 2;
19 pub const SHORT: u16 = 3;
20 pub const LONG: u16 = 4;
21 pub const RATIONAL: u16 = 5;
22 pub const SBYTE: u16 = 6;
23 pub const UNDEFINED: u16 = 7;
24 pub const SSHORT: u16 = 8;
25 pub const SLONG: u16 = 9;
26 pub const SRATIONAL: u16 = 10;
27 pub const FLOAT: u16 = 11;
28 pub const DOUBLE: u16 = 12;
29 pub const IFD: u16 = 13;
30 pub const LONG8: u16 = 16;
31}
32
33pub fn type_size(t: u16) -> usize {
35 match t {
36 1 | 2 | 6 | 7 => 1,
37 3 | 8 => 2,
38 4 | 9 | 11 | 13 => 4,
39 5 | 10 | 12 | 16 | 17 | 18 => 8,
40 _ => 0,
41 }
42}
43
44#[allow(missing_docs)]
46pub mod tags {
47 pub const NEW_SUBFILE_TYPE: u16 = 0x00FE;
48 pub const IMAGE_WIDTH: u16 = 0x0100;
49 pub const IMAGE_LENGTH: u16 = 0x0101;
50 pub const BITS_PER_SAMPLE: u16 = 0x0102;
51 pub const COMPRESSION: u16 = 0x0103;
52 pub const PHOTOMETRIC: u16 = 0x0106;
53 pub const FILL_ORDER: u16 = 0x010A;
54 pub const IMAGE_DESCRIPTION: u16 = 0x010E;
55 pub const MAKE: u16 = 0x010F;
56 pub const MODEL: u16 = 0x0110;
57 pub const STRIP_OFFSETS: u16 = 0x0111;
58 pub const ORIENTATION: u16 = 0x0112;
59 pub const SAMPLES_PER_PIXEL: u16 = 0x0115;
60 pub const ROWS_PER_STRIP: u16 = 0x0116;
61 pub const STRIP_BYTE_COUNTS: u16 = 0x0117;
62 pub const X_RESOLUTION: u16 = 0x011A;
63 pub const PLANAR_CONFIG: u16 = 0x011C;
64 pub const SOFTWARE: u16 = 0x0131;
65 pub const DATE_TIME: u16 = 0x0132;
66 pub const ARTIST: u16 = 0x013B;
67 pub const PREDICTOR: u16 = 0x013D;
68 pub const TILE_WIDTH: u16 = 0x0142;
69 pub const TILE_LENGTH: u16 = 0x0143;
70 pub const TILE_OFFSETS: u16 = 0x0144;
71 pub const TILE_BYTE_COUNTS: u16 = 0x0145;
72 pub const SUB_IFDS: u16 = 0x014A;
73 pub const SAMPLE_FORMAT: u16 = 0x0153;
74 pub const JPEG_INTERCHANGE_FORMAT: u16 = 0x0201;
75 pub const JPEG_INTERCHANGE_FORMAT_LENGTH: u16 = 0x0202;
76 pub const YCBCR_SUBSAMPLING: u16 = 0x0212;
77 pub const XMP: u16 = 0x02BC;
78 pub const CFA_REPEAT_PATTERN_DIM: u16 = 0x828D;
79 pub const CFA_PATTERN_EP: u16 = 0x828E;
80 pub const COPYRIGHT: u16 = 0x8298;
81 pub const EXPOSURE_TIME: u16 = 0x829A;
82 pub const F_NUMBER: u16 = 0x829D;
83 pub const EXIF_IFD: u16 = 0x8769;
84 pub const EXPOSURE_PROGRAM: u16 = 0x8822;
85 pub const GPS_IFD: u16 = 0x8825;
86 pub const ISO: u16 = 0x8827;
87 pub const DATE_TIME_ORIGINAL: u16 = 0x9003;
88 pub const OFFSET_TIME_ORIGINAL: u16 = 0x9011;
89 pub const EXPOSURE_BIAS: u16 = 0x9204;
90 pub const METERING_MODE: u16 = 0x9207;
91 pub const FLASH: u16 = 0x9209;
92 pub const FOCAL_LENGTH: u16 = 0x920A;
93 pub const MAKER_NOTE: u16 = 0x927C;
94 pub const SUBSEC_TIME_ORIGINAL: u16 = 0x9291;
95 pub const PIXEL_X_DIMENSION: u16 = 0xA002;
96 pub const PIXEL_Y_DIMENSION: u16 = 0xA003;
97 pub const CFA_PATTERN: u16 = 0xA302;
98 pub const FOCAL_LENGTH_35MM: u16 = 0xA405;
99 pub const BODY_SERIAL: u16 = 0xA431;
100 pub const LENS_SPEC: u16 = 0xA432;
101 pub const LENS_MAKE: u16 = 0xA433;
102 pub const LENS_MODEL: u16 = 0xA434;
103 pub const LENS_SERIAL: u16 = 0xA435;
104 pub const DNG_VERSION: u16 = 0xC612;
106 pub const UNIQUE_CAMERA_MODEL: u16 = 0xC614;
107 pub const CFA_PLANE_COLOR: u16 = 0xC616;
108 pub const CFA_LAYOUT: u16 = 0xC617;
109 pub const LINEARIZATION_TABLE: u16 = 0xC618;
110 pub const BLACK_LEVEL_REPEAT_DIM: u16 = 0xC619;
111 pub const BLACK_LEVEL: u16 = 0xC61A;
112 pub const BLACK_LEVEL_DELTA_H: u16 = 0xC61B;
113 pub const BLACK_LEVEL_DELTA_V: u16 = 0xC61C;
114 pub const WHITE_LEVEL: u16 = 0xC61D;
115 pub const DEFAULT_SCALE: u16 = 0xC61E;
116 pub const DEFAULT_CROP_ORIGIN: u16 = 0xC61F;
117 pub const DEFAULT_CROP_SIZE: u16 = 0xC620;
118 pub const COLOR_MATRIX1: u16 = 0xC621;
119 pub const COLOR_MATRIX2: u16 = 0xC622;
120 pub const CAMERA_CALIBRATION1: u16 = 0xC623;
121 pub const CAMERA_CALIBRATION2: u16 = 0xC624;
122 pub const ANALOG_BALANCE: u16 = 0xC627;
123 pub const AS_SHOT_NEUTRAL: u16 = 0xC628;
124 pub const AS_SHOT_WHITE_XY: u16 = 0xC629;
125 pub const BASELINE_EXPOSURE: u16 = 0xC62A;
126 pub const BASELINE_NOISE: u16 = 0xC62B;
127 pub const BASELINE_SHARPNESS: u16 = 0xC62C;
128 pub const LINEAR_RESPONSE_LIMIT: u16 = 0xC62E;
129 pub const PROFILE_NAME: u16 = 0xC6F8;
130 pub const PROFILE_HUE_SAT_MAP_DIMS: u16 = 0xC6F9;
131 pub const PROFILE_HUE_SAT_MAP_DATA1: u16 = 0xC6FA;
132 pub const PROFILE_HUE_SAT_MAP_DATA2: u16 = 0xC6FB;
133 pub const PROFILE_TONE_CURVE: u16 = 0xC6FC;
134 pub const PROFILE_EMBED_POLICY: u16 = 0xC6FD;
135 pub const PROFILE_LOOK_TABLE_DIMS: u16 = 0xC725;
136 pub const PROFILE_LOOK_TABLE_DATA: u16 = 0xC726;
137 pub const NOISE_PROFILE: u16 = 0xC761;
138 pub const BASELINE_EXPOSURE_OFFSET: u16 = 0xC7A5;
139 pub const DEFAULT_USER_CROP: u16 = 0xC7B5;
140 pub const PROFILE_HUE_SAT_MAP_DATA3: u16 = 0xCD39;
141 pub const DNG_PRIVATE_DATA: u16 = 0xC634;
142 pub const MAKER_NOTE_SAFETY: u16 = 0xC635;
143 pub const CALIBRATION_ILLUMINANT1: u16 = 0xC65A;
144 pub const CALIBRATION_ILLUMINANT2: u16 = 0xC65B;
145 pub const ACTIVE_AREA: u16 = 0xC68D;
146 pub const MASKED_AREAS: u16 = 0xC68E;
147 pub const FORWARD_MATRIX1: u16 = 0xC714;
148 pub const FORWARD_MATRIX2: u16 = 0xC715;
149 pub const OPCODE_LIST1: u16 = 0xC740;
150 pub const OPCODE_LIST2: u16 = 0xC741;
151 pub const OPCODE_LIST3: u16 = 0xC74E;
152 pub const NEW_RAW_IMAGE_DIGEST: u16 = 0xC7A7;
153 pub const CALIBRATION_ILLUMINANT3: u16 = 0xCD31;
154 pub const COLOR_MATRIX3: u16 = 0xCD33;
155 pub const FORWARD_MATRIX3: u16 = 0xCD34;
156 pub const CAMERA_CALIBRATION3: u16 = 0xCD32;
157 pub const ILLUMINANT_DATA1: u16 = 0xCD35;
158 pub const ILLUMINANT_DATA2: u16 = 0xCD36;
159 pub const ILLUMINANT_DATA3: u16 = 0xCD37;
160 pub const ROW_INTERLEAVE_FACTOR: u16 = 0xC71F;
161 pub const COLUMN_INTERLEAVE_FACTOR: u16 = 0xCD43;
162 pub const JXL_DISTANCE: u16 = 0xCD49;
163}
164
165#[derive(Debug, Clone, Copy)]
167pub struct TiffOptions {
168 pub follow_sub_ifds: bool,
170 pub max_depth: u32,
171 pub max_ifds: usize,
172 pub max_entries: usize,
173 pub lenient: bool,
175}
176
177impl Default for TiffOptions {
178 fn default() -> Self {
179 TiffOptions { follow_sub_ifds: true, max_depth: 6, max_ifds: 256, max_entries: 4096, lenient: true }
180 }
181}
182
183#[derive(Clone, Copy)]
185pub struct Entry<'a> {
186 pub tag: u16,
187 pub typ: u16,
188 pub count: u32,
189 pub order: ByteOrder,
190 pub data: &'a [u8],
192 pub value_field: u32,
194 pub value_pos: usize,
196}
197
198impl std::fmt::Debug for Entry<'_> {
199 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
200 write!(f, "Entry(tag={:#06x}, type={}, count={})", self.tag, self.typ, self.count)
201 }
202}
203
204impl<'a> Entry<'a> {
205 pub fn len(&self) -> usize {
207 let sz = type_size(self.typ);
208 self.data.len().checked_div(sz).map_or(self.data.len(), |n| n.min(self.count as usize))
209 }
210 pub fn is_empty(&self) -> bool {
211 self.len() == 0
212 }
213
214 pub fn u32(&self, i: usize) -> Option<u32> {
216 let d = self.data;
217 let o = self.order;
218 Some(match self.typ {
219 types::BYTE | types::UNDEFINED | types::ASCII => *d.get(i)? as u32,
220 types::SBYTE => *d.get(i)? as i8 as i32 as u32,
221 types::SHORT => d.u16_at(o, i * 2).ok()? as u32,
222 types::SSHORT => d.u16_at(o, i * 2).ok()? as i16 as i32 as u32,
223 types::LONG | types::SLONG | types::IFD => d.u32_at(o, i * 4).ok()?,
224 types::LONG8 => d.u64_at(o, i * 8).ok()? as u32,
225 types::RATIONAL | types::SRATIONAL | types::FLOAT | types::DOUBLE => {
226 let f = self.f64(i)?;
227 if f.is_finite() && f >= 0.0 { f as u32 } else { return None }
228 }
229 _ => return None,
230 })
231 }
232 pub fn u16(&self, i: usize) -> Option<u16> {
233 self.u32(i).map(|v| v as u16)
234 }
235 pub fn i32(&self, i: usize) -> Option<i32> {
237 match self.typ {
238 types::SBYTE | types::SSHORT | types::SLONG => self.u32(i).map(|v| v as i32),
239 types::SRATIONAL | types::RATIONAL | types::FLOAT | types::DOUBLE => self.f64(i).map(|v| v as i32),
240 _ => self.u32(i).map(|v| v as i32),
241 }
242 }
243 pub fn f64(&self, i: usize) -> Option<f64> {
245 let d = self.data;
246 let o = self.order;
247 Some(match self.typ {
248 types::RATIONAL => {
249 let n = d.u32_at(o, i * 8).ok()?;
250 let den = d.u32_at(o, i * 8 + 4).ok()?;
251 if den == 0 {
252 return None;
253 }
254 n as f64 / den as f64
255 }
256 types::SRATIONAL => {
257 let n = d.u32_at(o, i * 8).ok()? as i32;
258 let den = d.u32_at(o, i * 8 + 4).ok()? as i32;
259 if den == 0 {
260 return None;
261 }
262 n as f64 / den as f64
263 }
264 types::FLOAT => f32::from_bits(d.u32_at(o, i * 4).ok()?) as f64,
265 types::DOUBLE => f64::from_bits(d.u64_at(o, i * 8).ok()?),
266 types::SBYTE | types::SSHORT | types::SLONG => self.i32(i)? as f64,
267 _ => self.u32(i)? as f64,
268 })
269 }
270 pub fn f32(&self, i: usize) -> Option<f32> {
271 self.f64(i).map(|v| v as f32)
272 }
273 pub fn u32s(&self) -> Vec<u32> {
275 (0..self.len()).filter_map(|i| self.u32(i)).collect()
276 }
277 pub fn f64s(&self) -> Vec<f64> {
279 (0..self.len()).filter_map(|i| self.f64(i)).collect()
280 }
281 pub fn f32s(&self) -> Vec<f32> {
282 (0..self.len()).filter_map(|i| self.f32(i)).collect()
283 }
284 pub fn string(&self) -> String {
286 crate::bytes::ascii_field(self.data)
287 }
288 pub fn bytes(&self) -> &'a [u8] {
290 self.data
291 }
292}
293
294#[derive(Debug, Clone)]
296pub struct Ifd<'a> {
297 pub offset: u32,
299 pub order: ByteOrder,
300 pub entries: Vec<Entry<'a>>,
301 pub sub_ifds: Vec<Ifd<'a>>,
303 pub exif: Option<Box<Ifd<'a>>>,
304 pub gps: Option<Box<Ifd<'a>>>,
305 pub next: u32,
307}
308
309impl<'a> Ifd<'a> {
310 pub fn get(&self, tag: u16) -> Option<&Entry<'a>> {
311 self.entries.iter().find(|e| e.tag == tag)
312 }
313 pub fn has(&self, tag: u16) -> bool {
314 self.get(tag).is_some()
315 }
316 pub fn u32(&self, tag: u16) -> Option<u32> {
317 self.get(tag)?.u32(0)
318 }
319 pub fn f64(&self, tag: u16) -> Option<f64> {
320 self.get(tag)?.f64(0)
321 }
322 pub fn string(&self, tag: u16) -> Option<String> {
323 let s = self.get(tag)?.string();
324 (!s.is_empty()).then_some(s)
325 }
326 pub fn req_u32(&self, tag: u16) -> Result<u32> {
328 self.u32(tag).ok_or_else(|| Error::invalid_metadata(format!("missing TIFF tag {tag:#06x}")))
329 }
330 pub fn width(&self) -> u32 {
331 self.u32(tags::IMAGE_WIDTH).unwrap_or(0)
332 }
333 pub fn height(&self) -> u32 {
334 self.u32(tags::IMAGE_LENGTH).unwrap_or(0)
335 }
336 pub fn compression(&self) -> u32 {
337 self.u32(tags::COMPRESSION).unwrap_or(1)
338 }
339 pub fn bps(&self) -> u32 {
340 self.u32(tags::BITS_PER_SAMPLE).unwrap_or(0)
341 }
342 pub fn is_reduced_resolution(&self) -> bool {
343 self.u32(tags::NEW_SUBFILE_TYPE).unwrap_or(0) & 1 != 0
344 }
345
346 pub fn walk(&self) -> Vec<&Ifd<'a>> {
348 let mut out = vec![self];
349 for s in &self.sub_ifds {
350 out.extend(s.walk());
351 }
352 if let Some(e) = &self.exif {
353 out.extend(e.walk());
354 }
355 if let Some(g) = &self.gps {
356 out.push(g);
357 }
358 out
359 }
360}
361
362#[derive(Debug, Clone)]
364pub struct Tiff<'a> {
365 pub data: &'a [u8],
367 pub order: ByteOrder,
368 pub ifds: Vec<Ifd<'a>>,
370}
371
372struct ParseState {
373 visited: Vec<u32>,
374 count: usize,
375}
376
377impl<'a> Tiff<'a> {
378 pub fn parse(data: &'a [u8]) -> Result<Self> {
380 Self::parse_with(data, TiffOptions::default())
381 }
382
383 pub fn parse_with(data: &'a [u8], opts: TiffOptions) -> Result<Self> {
384 let order = match data.get(0..2) {
385 Some(b"II") => ByteOrder::Little,
386 Some(b"MM") => ByteOrder::Big,
387 _ => return Err(Error::UnsupportedFormat("not a TIFF header".into())),
388 };
389 let first = data.u32_at(order, 4)?;
391 Self::parse_from(data, order, first, opts)
392 }
393
394 pub fn parse_from(data: &'a [u8], order: ByteOrder, first_ifd: u32, opts: TiffOptions) -> Result<Self> {
396 let mut st = ParseState { visited: Vec::new(), count: 0 };
397 let mut ifds = Vec::new();
398 let mut next = first_ifd;
399 while next != 0 && ifds.len() < 64 {
400 match parse_ifd(data, order, next, 0, &opts, &mut st) {
401 Ok(ifd) => {
402 next = ifd.next;
403 ifds.push(ifd);
404 }
405 Err(e) => {
406 if ifds.is_empty() {
407 return Err(e);
408 }
409 break; }
411 }
412 }
413 if ifds.is_empty() {
414 return Err(Error::corrupt("TIFF without IFDs"));
415 }
416 Ok(Tiff { data, order, ifds })
417 }
418
419 pub fn all_ifds(&self) -> Vec<&Ifd<'a>> {
421 self.ifds.iter().flat_map(|i| i.walk()).collect()
422 }
423
424 pub fn find_ifd_with(&self, tag: u16) -> Option<&Ifd<'a>> {
426 self.all_ifds().into_iter().find(|i| i.has(tag))
427 }
428
429 pub fn find_entry(&self, tag: u16) -> Option<&Entry<'a>> {
431 self.all_ifds().into_iter().find_map(|i| i.get(tag))
432 }
433
434 pub fn sub_ifd_at(&self, offset: u32) -> Result<Ifd<'a>> {
436 let mut st = ParseState { visited: Vec::new(), count: 0 };
437 parse_ifd(self.data, self.order, offset, 0, &TiffOptions::default(), &mut st)
438 }
439}
440
441pub fn parse_ifd_at<'a>(data: &'a [u8], order: ByteOrder, offset: u32, opts: TiffOptions) -> Result<Ifd<'a>> {
443 let mut st = ParseState { visited: Vec::new(), count: 0 };
444 parse_ifd(data, order, offset, 0, &opts, &mut st)
445}
446
447fn parse_ifd<'a>(
448 data: &'a [u8],
449 order: ByteOrder,
450 offset: u32,
451 depth: u32,
452 opts: &TiffOptions,
453 st: &mut ParseState,
454) -> Result<Ifd<'a>> {
455 if depth > opts.max_depth {
456 return Err(Error::corrupt("IFD nesting too deep"));
457 }
458 if st.count >= opts.max_ifds {
459 return Err(Error::corrupt("too many IFDs"));
460 }
461 if st.visited.contains(&offset) {
462 return Err(Error::corrupt_at("recursive IFD reference", offset as u64));
463 }
464 st.visited.push(offset);
465 st.count += 1;
466 let base = offset as usize;
467 let n = data.u16_at(order, base)? as usize;
468 if n == 0 || n > opts.max_entries {
469 return Err(Error::corrupt_at(format!("invalid IFD with {n} entries"), offset as u64));
470 }
471 let mut entries = Vec::with_capacity(n);
472 for i in 0..n {
473 let p = base + 2 + i * 12;
474 if p + 12 > data.len() {
475 if opts.lenient && !entries.is_empty() {
476 break;
477 }
478 return Err(Error::corrupt_at("IFD entries beyond end of data", p as u64));
479 }
480 let tag = data.u16_at(order, p)?;
481 let typ = data.u16_at(order, p + 2)?;
482 let count = data.u32_at(order, p + 4)?;
483 let value_field = data.u32_at(order, p + 8)?;
484 let sz = type_size(typ);
485 if sz == 0 {
486 continue; }
488 let total = (count as u64) * sz as u64;
489 let (vpos, vbytes) = if total <= 4 {
490 (p + 8, &data[p + 8..p + 8 + total as usize])
491 } else {
492 let o = value_field as usize;
493 if o >= data.len() {
494 continue; }
496 let end = (o as u64 + total).min(data.len() as u64) as usize;
497 (o, &data[o..end])
498 };
499 entries.push(Entry { tag, typ, count, order, data: vbytes, value_field, value_pos: vpos });
500 }
501 let next_pos = base + 2 + n * 12;
502 let next = data.u32_at(order, next_pos).unwrap_or(0);
503 let mut ifd = Ifd { offset, order, entries, sub_ifds: Vec::new(), exif: None, gps: None, next };
504 if opts.follow_sub_ifds {
505 if let Some(e) = ifd.get(tags::SUB_IFDS).copied() {
506 for i in 0..e.len().min(32) {
507 if let Some(o) = e.u32(i) {
508 if let Ok(sub) = parse_ifd(data, order, o, depth + 1, opts, st) {
509 ifd.sub_ifds.push(sub);
510 }
511 }
512 }
513 }
514 if let Some(o) = ifd.u32(tags::EXIF_IFD) {
515 if let Ok(sub) = parse_ifd(data, order, o, depth + 1, opts, st) {
516 ifd.exif = Some(Box::new(sub));
517 }
518 }
519 if let Some(o) = ifd.u32(tags::GPS_IFD) {
520 if let Ok(sub) = parse_ifd(data, order, o, depth + 1, opts, st) {
521 ifd.gps = Some(Box::new(sub));
522 }
523 }
524 }
525 Ok(ifd)
526}
527
528#[cfg(test)]
529pub(crate) mod test_util {
530 pub struct TiffBuilder {
532 pub entries: Vec<(u16, u16, u32, Vec<u8>)>,
533 }
534 impl TiffBuilder {
535 pub fn new() -> Self {
536 TiffBuilder { entries: vec![] }
537 }
538 pub fn short(mut self, tag: u16, v: &[u16]) -> Self {
539 self.entries.push((tag, 3, v.len() as u32, v.iter().flat_map(|x| x.to_le_bytes()).collect()));
540 self
541 }
542 pub fn long(mut self, tag: u16, v: &[u32]) -> Self {
543 self.entries.push((tag, 4, v.len() as u32, v.iter().flat_map(|x| x.to_le_bytes()).collect()));
544 self
545 }
546 pub fn ascii(mut self, tag: u16, s: &str) -> Self {
547 let mut b = s.as_bytes().to_vec();
548 b.push(0);
549 self.entries.push((tag, 2, b.len() as u32, b));
550 self
551 }
552 pub fn build(mut self, next: u32) -> Vec<u8> {
553 self.entries.sort_by_key(|e| e.0);
554 let mut out = b"II*\0\x08\0\0\0".to_vec();
555 let n = self.entries.len();
556 let mut extra_off = 8 + 2 + n * 12 + 4;
557 let mut extra: Vec<u8> = vec![];
558 out.extend((n as u16).to_le_bytes());
559 for (tag, typ, count, data) in &self.entries {
560 out.extend(tag.to_le_bytes());
561 out.extend(typ.to_le_bytes());
562 out.extend(count.to_le_bytes());
563 if data.len() <= 4 {
564 let mut d = data.clone();
565 d.resize(4, 0);
566 out.extend(d);
567 } else {
568 out.extend((extra_off as u32).to_le_bytes());
569 extra.extend(data);
570 extra_off += data.len();
571 }
572 }
573 out.extend(next.to_le_bytes());
574 out.extend(extra);
575 out
576 }
577 }
578}
579
580#[cfg(test)]
581mod tests {
582 use super::test_util::TiffBuilder;
583 use super::*;
584
585 #[test]
586 fn parse_basic() {
587 let data = TiffBuilder::new()
588 .long(tags::IMAGE_WIDTH, &[640])
589 .short(tags::IMAGE_LENGTH, &[480])
590 .ascii(tags::MAKE, "FlashRaw Test Make")
591 .short(tags::BITS_PER_SAMPLE, &[12, 12, 12])
592 .build(0);
593 let t = Tiff::parse(&data).unwrap();
594 let ifd = &t.ifds[0];
595 assert_eq!(ifd.width(), 640);
596 assert_eq!(ifd.height(), 480);
597 assert_eq!(ifd.string(tags::MAKE).unwrap(), "FlashRaw Test Make");
598 assert_eq!(ifd.get(tags::BITS_PER_SAMPLE).unwrap().u32s(), vec![12, 12, 12]);
599 }
600
601 #[test]
602 fn recursive_chain_terminates() {
603 let data = TiffBuilder::new().long(tags::IMAGE_WIDTH, &[1]).build(8);
605 let t = Tiff::parse(&data).unwrap();
606 assert_eq!(t.ifds.len(), 1);
607 }
608
609 #[test]
610 fn self_referencing_subifd() {
611 let data = TiffBuilder::new().long(tags::SUB_IFDS, &[8]).long(tags::EXIF_IFD, &[8]).build(0);
612 let t = Tiff::parse(&data).unwrap();
613 assert!(t.ifds[0].sub_ifds.is_empty());
614 assert!(t.ifds[0].exif.is_none());
615 }
616
617 #[test]
618 fn garbage_does_not_panic() {
619 for len in 0..64 {
620 let mut d = vec![0u8; len];
621 if len >= 2 {
622 d[0] = b'I';
623 d[1] = b'I';
624 }
625 let _ = Tiff::parse(&d);
626 }
627 let mut d = TiffBuilder::new().long(tags::STRIP_OFFSETS, &[1, 2, 3, 4]).build(0);
628 d[8 + 2 + 4] = 0xFF;
630 d[8 + 2 + 5] = 0xFF;
631 d[8 + 2 + 6] = 0xFF;
632 let t = Tiff::parse(&d).unwrap();
633 let e = t.ifds[0].get(tags::STRIP_OFFSETS);
634 if let Some(e) = e {
635 let _ = e.u32s();
636 }
637 }
638}